Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+51
-23
@@ -128,26 +128,34 @@ func (l *TCPListener) Addr() net.Addr {
|
||||
}
|
||||
|
||||
type deadlineTimer struct {
|
||||
// mu protects the fields below.
|
||||
mu sync.Mutex
|
||||
|
||||
readTimer *time.Timer
|
||||
readCancelCh chan struct{}
|
||||
writeTimer *time.Timer
|
||||
// +checklocks:mu
|
||||
readTimer *time.Timer
|
||||
// +checklocks:mu
|
||||
readCancelCh chan struct{}
|
||||
// +checklocks:mu
|
||||
writeTimer *time.Timer
|
||||
// +checklocks:mu
|
||||
writeCancelCh chan struct{}
|
||||
}
|
||||
|
||||
func (d *deadlineTimer) init() {
|
||||
d.readCancelCh = make(chan struct{})
|
||||
d.writeCancelCh = make(chan struct{})
|
||||
func newDeadlineTimer() deadlineTimer {
|
||||
return deadlineTimer{
|
||||
readCancelCh: make(chan struct{}),
|
||||
writeCancelCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// +checklocksexclude:d.mu
|
||||
func (d *deadlineTimer) readCancel() <-chan struct{} {
|
||||
d.mu.Lock()
|
||||
c := d.readCancelCh
|
||||
d.mu.Unlock()
|
||||
return c
|
||||
}
|
||||
|
||||
// +checklocksexclude:d.mu
|
||||
func (d *deadlineTimer) writeCancel() <-chan struct{} {
|
||||
d.mu.Lock()
|
||||
c := d.writeCancelCh
|
||||
@@ -157,11 +165,11 @@ func (d *deadlineTimer) writeCancel() <-chan struct{} {
|
||||
|
||||
// setDeadline contains the shared logic for setting a deadline.
|
||||
//
|
||||
// cancelCh and timer must be pointers to deadlineTimer.readCancelCh and
|
||||
// deadlineTimer.readTimer or deadlineTimer.writeCancelCh and
|
||||
// deadlineTimer.writeTimer.
|
||||
// cancelCh and timer must point to d.readCancelCh and d.readTimer, or to
|
||||
// d.writeCancelCh and d.writeTimer. checklocks verifies that d.mu is held,
|
||||
// but cannot prove that the arguments identify the matching fields of d.
|
||||
//
|
||||
// setDeadline must only be called while holding d.mu.
|
||||
// +checklocks:d.mu
|
||||
func (d *deadlineTimer) setDeadline(cancelCh *chan struct{}, timer **time.Timer, t time.Time) {
|
||||
if *timer != nil && !(*timer).Stop() {
|
||||
*cancelCh = make(chan struct{})
|
||||
@@ -183,7 +191,7 @@ func (d *deadlineTimer) setDeadline(cancelCh *chan struct{}, timer **time.Timer,
|
||||
return
|
||||
}
|
||||
|
||||
timeout := t.Sub(time.Now())
|
||||
timeout := time.Until(t)
|
||||
if timeout <= 0 {
|
||||
close(*cancelCh)
|
||||
return
|
||||
@@ -201,6 +209,8 @@ func (d *deadlineTimer) setDeadline(cancelCh *chan struct{}, timer **time.Timer,
|
||||
|
||||
// SetReadDeadline implements net.Conn.SetReadDeadline and
|
||||
// net.PacketConn.SetReadDeadline.
|
||||
//
|
||||
// +checklocksexclude:d.mu
|
||||
func (d *deadlineTimer) SetReadDeadline(t time.Time) error {
|
||||
d.mu.Lock()
|
||||
d.setDeadline(&d.readCancelCh, &d.readTimer, t)
|
||||
@@ -210,6 +220,8 @@ func (d *deadlineTimer) SetReadDeadline(t time.Time) error {
|
||||
|
||||
// SetWriteDeadline implements net.Conn.SetWriteDeadline and
|
||||
// net.PacketConn.SetWriteDeadline.
|
||||
//
|
||||
// +checklocksexclude:d.mu
|
||||
func (d *deadlineTimer) SetWriteDeadline(t time.Time) error {
|
||||
d.mu.Lock()
|
||||
d.setDeadline(&d.writeCancelCh, &d.writeTimer, t)
|
||||
@@ -218,6 +230,8 @@ func (d *deadlineTimer) SetWriteDeadline(t time.Time) error {
|
||||
}
|
||||
|
||||
// SetDeadline implements net.Conn.SetDeadline and net.PacketConn.SetDeadline.
|
||||
//
|
||||
// +checklocksexclude:d.mu
|
||||
func (d *deadlineTimer) SetDeadline(t time.Time) error {
|
||||
d.mu.Lock()
|
||||
d.setDeadline(&d.readCancelCh, &d.readTimer, t)
|
||||
@@ -234,7 +248,7 @@ type TCPConn struct {
|
||||
wq *waiter.Queue
|
||||
ep tcpip.Endpoint
|
||||
|
||||
// readMu serializes reads and implicitly protects read.
|
||||
// readMu serializes reads.
|
||||
//
|
||||
// Lock ordering:
|
||||
// If both readMu and deadlineTimer.mu are to be used in a single
|
||||
@@ -243,17 +257,18 @@ type TCPConn struct {
|
||||
|
||||
// read contains bytes that have been read from the endpoint,
|
||||
// but haven't yet been returned.
|
||||
//
|
||||
// +checklocks:readMu
|
||||
read []byte
|
||||
}
|
||||
|
||||
// NewTCPConn creates a new TCPConn.
|
||||
func NewTCPConn(wq *waiter.Queue, ep tcpip.Endpoint) *TCPConn {
|
||||
c := &TCPConn{
|
||||
wq: wq,
|
||||
ep: ep,
|
||||
return &TCPConn{
|
||||
deadlineTimer: newDeadlineTimer(),
|
||||
wq: wq,
|
||||
ep: ep,
|
||||
}
|
||||
c.deadlineTimer.init()
|
||||
return c
|
||||
}
|
||||
|
||||
// Accept implements net.Conn.Accept.
|
||||
@@ -343,6 +358,9 @@ func commonRead(b []byte, ep tcpip.Endpoint, wq *waiter.Queue, deadline <-chan s
|
||||
}
|
||||
|
||||
// Read implements net.Conn.Read.
|
||||
//
|
||||
// +checklocksexclude:c.readMu
|
||||
// +checklocksexclude:c.deadlineTimer.mu
|
||||
func (c *TCPConn) Read(b []byte) (int, error) {
|
||||
c.readMu.Lock()
|
||||
defer c.readMu.Unlock()
|
||||
@@ -357,6 +375,8 @@ func (c *TCPConn) Read(b []byte) (int, error) {
|
||||
}
|
||||
|
||||
// Write implements net.Conn.Write.
|
||||
//
|
||||
// +checklocksexclude:c.deadlineTimer.mu
|
||||
func (c *TCPConn) Write(b []byte) (int, error) {
|
||||
deadline := c.writeCancel()
|
||||
|
||||
@@ -553,12 +573,11 @@ type UDPConn struct {
|
||||
|
||||
// NewUDPConn creates a new UDPConn.
|
||||
func NewUDPConn(wq *waiter.Queue, ep tcpip.Endpoint) *UDPConn {
|
||||
c := &UDPConn{
|
||||
ep: ep,
|
||||
wq: wq,
|
||||
return &UDPConn{
|
||||
deadlineTimer: newDeadlineTimer(),
|
||||
ep: ep,
|
||||
wq: wq,
|
||||
}
|
||||
c.deadlineTimer.init()
|
||||
return c
|
||||
}
|
||||
|
||||
// DialUDP creates a new UDPConn.
|
||||
@@ -626,12 +645,16 @@ func (c *UDPConn) RemoteAddr() net.Addr {
|
||||
}
|
||||
|
||||
// Read implements net.Conn.Read
|
||||
//
|
||||
// +checklocksexclude:c.deadlineTimer.mu
|
||||
func (c *UDPConn) Read(b []byte) (int, error) {
|
||||
bytesRead, _, err := c.ReadFrom(b)
|
||||
return bytesRead, err
|
||||
}
|
||||
|
||||
// ReadFrom implements net.PacketConn.ReadFrom.
|
||||
//
|
||||
// +checklocksexclude:c.deadlineTimer.mu
|
||||
func (c *UDPConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
deadline := c.readCancel()
|
||||
|
||||
@@ -643,11 +666,16 @@ func (c *UDPConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
return n, fullToUDPAddr(addr), nil
|
||||
}
|
||||
|
||||
// Write implements net.Conn.Write.
|
||||
//
|
||||
// +checklocksexclude:c.deadlineTimer.mu
|
||||
func (c *UDPConn) Write(b []byte) (int, error) {
|
||||
return c.WriteTo(b, nil)
|
||||
}
|
||||
|
||||
// WriteTo implements net.PacketConn.WriteTo.
|
||||
//
|
||||
// +checklocksexclude:c.deadlineTimer.mu
|
||||
func (c *UDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
deadline := c.writeCancel()
|
||||
|
||||
|
||||
+1
-1
@@ -170,7 +170,7 @@ var (
|
||||
// https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml.
|
||||
IPv4AllSystems = tcpip.AddrFrom4([4]byte{0xe0, 0x00, 0x00, 0x01})
|
||||
|
||||
// IPv4Broadcast is the broadcast address of the IPv4 procotol.
|
||||
// IPv4Broadcast is the broadcast address of the IPv4 protocol.
|
||||
IPv4Broadcast = tcpip.AddrFrom4([4]byte{0xff, 0xff, 0xff, 0xff})
|
||||
|
||||
// IPv4Any is the non-routable IPv4 "any" meta address.
|
||||
|
||||
@@ -205,10 +205,80 @@ func (b IPv6) NextHeader() uint8 {
|
||||
}
|
||||
|
||||
// TransportProtocol implements Network.TransportProtocol.
|
||||
//
|
||||
// Deprecated: Use TryParseTransportProtocol instead.
|
||||
// This function does not parse extension headers and returns the next header
|
||||
// field of the IPv6 header as the transport
|
||||
// protocol which may not be the actual transport protocol.
|
||||
// Use TryParseTransportProtocol to get the transport protocol correctly.
|
||||
func (b IPv6) TransportProtocol() tcpip.TransportProtocolNumber {
|
||||
return tcpip.TransportProtocolNumber(b.NextHeader())
|
||||
}
|
||||
|
||||
// IsExtensionHeader returns true if the next header is a known extension header.
|
||||
func IsExtensionHeader(nextHdr uint8) bool {
|
||||
extType := IPv6ExtensionHeaderIdentifier(nextHdr)
|
||||
switch extType {
|
||||
case IPv6HopByHopOptionsExtHdrIdentifier, IPv6RoutingExtHdrIdentifier, IPv6FragmentExtHdrIdentifier, IPv6DestinationOptionsExtHdrIdentifier, IPv6AuthenticationExtHdrIdentifier, IPv6NoNextHeaderIdentifier:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// TryParseTransportProtocol parses the IPv6 header and extension headers to get the
|
||||
// transport protocol.
|
||||
// Reference: net/ipv6/exthdrs_core.c:ipv6_skip_exthdr.
|
||||
// Returns the transport protocol and a boolean indicating if the transport
|
||||
// protocol parsing was successful.
|
||||
func (b IPv6) TryParseTransportProtocol() (tcpip.TransportProtocolNumber, bool) {
|
||||
if len(b) < IPv6MinimumSize {
|
||||
return 0, false
|
||||
}
|
||||
data := []byte(b[IPv6MinimumSize:])
|
||||
nxtHdr := b.NextHeader()
|
||||
maybeProto := tcpip.TransportProtocolNumber(nxtHdr)
|
||||
for IsExtensionHeader(nxtHdr) {
|
||||
dataLen := len(data)
|
||||
if dataLen < 2 {
|
||||
return maybeProto, false
|
||||
}
|
||||
currHdrLen := 0
|
||||
switch IPv6ExtensionHeaderIdentifier(nxtHdr) {
|
||||
case IPv6FragmentExtHdrIdentifier:
|
||||
// Fragment extension header is always 8 bytes long.
|
||||
if dataLen < 8 {
|
||||
return maybeProto, false
|
||||
}
|
||||
// Get the fragment offset from the fragment extension header.
|
||||
fragOffset := binary.BigEndian.Uint16(data[2:4]) & ^uint16(0x7)
|
||||
if fragOffset != 0 {
|
||||
return tcpip.TransportProtocolNumber(data[0]), false
|
||||
}
|
||||
currHdrLen = 8
|
||||
case IPv6HopByHopOptionsExtHdrIdentifier, IPv6RoutingExtHdrIdentifier, IPv6DestinationOptionsExtHdrIdentifier:
|
||||
currHdrLen = int(data[1]+1) * 8
|
||||
case IPv6AuthenticationExtHdrIdentifier:
|
||||
// Authentication extension header length calculation is different from
|
||||
// other extension headers.
|
||||
currHdrLen = int(data[1]+2) * 4
|
||||
default:
|
||||
// IPv6NoNextHeaderIdentifier or any unknown extension header.
|
||||
return maybeProto, false
|
||||
}
|
||||
if currHdrLen > len(data) {
|
||||
return maybeProto, false
|
||||
}
|
||||
nxtHdr = data[0]
|
||||
maybeProto = tcpip.TransportProtocolNumber(nxtHdr)
|
||||
data = data[currHdrLen:]
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return maybeProto, false
|
||||
}
|
||||
return maybeProto, true
|
||||
}
|
||||
|
||||
// Payload implements Network.Payload.
|
||||
func (b IPv6) Payload() []byte {
|
||||
return b[IPv6MinimumSize:][:b.PayloadLength()]
|
||||
|
||||
@@ -45,6 +45,11 @@ const (
|
||||
// Destination Options extension header, as per RFC 8200 section 4.6.
|
||||
IPv6DestinationOptionsExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 60
|
||||
|
||||
// IPv6AuthenticationExtHdrIdentifier is the header identifier of an
|
||||
// Authentication extension header, as per RFC 8200 section 4.1.
|
||||
// TODO: b/512233021 - Parse Authentication extension header correctly.
|
||||
IPv6AuthenticationExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 51
|
||||
|
||||
// IPv6NoNextHeaderIdentifier is the header identifier used to signify the end
|
||||
// of an IPv6 payload, as per RFC 8200 section 4.7.
|
||||
IPv6NoNextHeaderIdentifier IPv6ExtensionHeaderIdentifier = 59
|
||||
|
||||
+12
-2
@@ -158,9 +158,19 @@ traverseExtensions:
|
||||
//
|
||||
// Returns true if the header was successfully parsed.
|
||||
func UDP(pkt *stack.PacketBuffer) bool {
|
||||
_, ok := pkt.TransportHeader().Consume(header.UDPMinimumSize)
|
||||
hdr, ok := pkt.TransportHeader().Consume(header.UDPMinimumSize)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
pkt.TransportProtocolNumber = header.UDPProtocolNumber
|
||||
return ok
|
||||
// Validate the UDP payload length.
|
||||
length := int(header.UDP(hdr).Length()) - header.UDPMinimumSize
|
||||
if length < 0 || length > pkt.Data().Size() {
|
||||
return false
|
||||
}
|
||||
// Trim the payload to the length specified in the UDP header.
|
||||
pkt.Data().CapLength(length)
|
||||
return true
|
||||
}
|
||||
|
||||
// TCP parses a TCP packet found in pkt.Data and populates pkt's transport
|
||||
|
||||
+2
-9
@@ -17,7 +17,6 @@ package header
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/google/btree"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/checksum"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
|
||||
@@ -175,11 +174,6 @@ type SACKBlock struct {
|
||||
End seqnum.Value
|
||||
}
|
||||
|
||||
// Less returns true if r.Start < b.Start.
|
||||
func (r SACKBlock) Less(b btree.Item) bool {
|
||||
return r.Start.LessThan(b.(SACKBlock).Start)
|
||||
}
|
||||
|
||||
// Contains returns true if b is completely contained in r.
|
||||
func (r SACKBlock) Contains(b SACKBlock) bool {
|
||||
return r.Start.LessThanEq(b.Start) && b.End.LessThanEq(r.End)
|
||||
@@ -219,9 +213,8 @@ const (
|
||||
// TCPTotalHeaderMaximumSize is the maximum size of headers from all layers in
|
||||
// a TCP packet. It analogous to MAX_TCP_HEADER in Linux.
|
||||
//
|
||||
// TODO(b/319936470): Investigate why this needs to be at least 140 bytes. In
|
||||
// Linux this value is at least 160, but in theory we should be able to use
|
||||
// 138. In practice anything less than 140 starts to break GSO on gVNIC
|
||||
// Note: In Linux this value is at least 160, but in theory we should be able
|
||||
// to use 138. In practice anything less than 140 starts to break GSO on gVNIC
|
||||
// hardware.
|
||||
TCPTotalHeaderMaximumSize = 160
|
||||
|
||||
|
||||
+27
-3
@@ -134,20 +134,33 @@ func (b UDP) Encode(u *UDPFields) {
|
||||
|
||||
// SetSourcePortWithChecksumUpdate implements ChecksummableTransport.
|
||||
func (b UDP) SetSourcePortWithChecksumUpdate(new uint16) {
|
||||
if b.Checksum() == 0 {
|
||||
b.SetSourcePort(new)
|
||||
return
|
||||
}
|
||||
old := b.SourcePort()
|
||||
b.SetSourcePort(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
xsum := ^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new)
|
||||
b.SetChecksum(normalizeChecksum(xsum))
|
||||
}
|
||||
|
||||
// SetDestinationPortWithChecksumUpdate implements ChecksummableTransport.
|
||||
func (b UDP) SetDestinationPortWithChecksumUpdate(new uint16) {
|
||||
if b.Checksum() == 0 {
|
||||
b.SetDestinationPort(new)
|
||||
return
|
||||
}
|
||||
old := b.DestinationPort()
|
||||
b.SetDestinationPort(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
xsum := ^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new)
|
||||
b.SetChecksum(normalizeChecksum(xsum))
|
||||
}
|
||||
|
||||
// UpdateChecksumPseudoHeaderAddress implements ChecksummableTransport.
|
||||
func (b UDP) UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullChecksum bool) {
|
||||
if fullChecksum && b.Checksum() == 0 {
|
||||
return
|
||||
}
|
||||
xsum := b.Checksum()
|
||||
if fullChecksum {
|
||||
xsum = ^xsum
|
||||
@@ -155,7 +168,7 @@ func (b UDP) UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullCheck
|
||||
|
||||
xsum = checksumUpdate2ByteAlignedAddress(xsum, old, new)
|
||||
if fullChecksum {
|
||||
xsum = ^xsum
|
||||
xsum = normalizeChecksum(^xsum)
|
||||
}
|
||||
|
||||
b.SetChecksum(xsum)
|
||||
@@ -193,3 +206,14 @@ func UDPValid(hdr UDP, payloadChecksum func() uint16, payloadSize uint16, netPro
|
||||
|
||||
return true, hdr.IsChecksumValid(srcAddr, dstAddr, payloadChecksum())
|
||||
}
|
||||
|
||||
func normalizeChecksum(xsum uint16) uint16 {
|
||||
// RFC 768:
|
||||
// If the computed UDP checksum is zero, it is transmitted as all ones.
|
||||
// An all zero transmitted checksum value means that
|
||||
// the transmitter generated no checksum.
|
||||
if xsum == 0 {
|
||||
return 0xFFFF
|
||||
}
|
||||
return xsum
|
||||
}
|
||||
+4
-4
@@ -87,8 +87,8 @@ func (v VirtioNetHeader) CSumOffset() uint16 {
|
||||
func (v VirtioNetHeader) Encode(f *VirtioNetHeaderFields) {
|
||||
v[flags] = uint8(f.Flags)
|
||||
v[gsoType] = uint8(f.GSOType)
|
||||
binary.BigEndian.PutUint16(v[hdrLen:], f.HdrLen)
|
||||
binary.BigEndian.PutUint16(v[gsoSize:], f.GSOSize)
|
||||
binary.BigEndian.PutUint16(v[csumStart:], f.CSumStart)
|
||||
binary.BigEndian.PutUint16(v[csumOffset:], f.CSumOffset)
|
||||
binary.LittleEndian.PutUint16(v[hdrLen:], f.HdrLen)
|
||||
binary.LittleEndian.PutUint16(v[gsoSize:], f.GSOSize)
|
||||
binary.LittleEndian.PutUint16(v[csumStart:], f.CSumStart)
|
||||
binary.LittleEndian.PutUint16(v[csumOffset:], f.CSumOffset)
|
||||
}
|
||||
+52
-16
@@ -83,12 +83,19 @@ type FragmentID struct {
|
||||
//
|
||||
// +stateify savable
|
||||
type Fragmentation struct {
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
highLimit int
|
||||
lowLimit int
|
||||
reassemblers map[FragmentID]*reassembler
|
||||
rList reassemblerList
|
||||
memSize int
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
highLimit int
|
||||
lowLimit int
|
||||
|
||||
// +checklocks:mu
|
||||
reassemblers map[FragmentID]*reassembler
|
||||
|
||||
// +checklocks:mu
|
||||
rList reassemblerList
|
||||
|
||||
// +checklocks:mu
|
||||
memSize int
|
||||
|
||||
timeout time.Duration
|
||||
blockSize uint16
|
||||
clock tcpip.Clock
|
||||
@@ -101,6 +108,14 @@ type TimeoutHandler interface {
|
||||
// OnReassemblyTimeout will be called with the first fragment (or nil, if the
|
||||
// first fragment has not been received) of a packet whose reassembly has
|
||||
// timed out.
|
||||
//
|
||||
// The packet is borrowed for the duration of the call; a handler that
|
||||
// retains it must take its own reference.
|
||||
//
|
||||
// The call is synchronous with the owning Fragmentation's mutex held.
|
||||
// The handler must not reenter that Fragmentation's Process or Release.
|
||||
// checklocks cannot name that mutex because TimeoutHandler does not
|
||||
// expose the owning Fragmentation.
|
||||
OnReassemblyTimeout(pkt *stack.PacketBuffer)
|
||||
}
|
||||
|
||||
@@ -116,8 +131,7 @@ type TimeoutHandler interface {
|
||||
// fragments after reaching highMemoryLimit.
|
||||
//
|
||||
// reassemblingTimeout specifies the maximum time allowed to reassemble a packet.
|
||||
// Fragments are lazily evicted only when a new a packet with an
|
||||
// already existing fragmentation-id arrives after the timeout.
|
||||
// Incomplete reassemblies are released once this timeout elapses.
|
||||
func NewFragmentation(blockSize uint16, highMemoryLimit, lowMemoryLimit int, reassemblingTimeout time.Duration, clock tcpip.Clock, timeoutHandler TimeoutHandler) *Fragmentation {
|
||||
if lowMemoryLimit >= highMemoryLimit {
|
||||
lowMemoryLimit = highMemoryLimit
|
||||
@@ -140,6 +154,8 @@ func NewFragmentation(blockSize uint16, highMemoryLimit, lowMemoryLimit int, rea
|
||||
clock: clock,
|
||||
timeoutHandler: timeoutHandler,
|
||||
}
|
||||
// Job locks f.mu before invoking this callback; checklocks cannot carry
|
||||
// that lock through the stored function value.
|
||||
f.releaseJob = tcpip.NewJob(f.clock, &f.mu, f.releaseReassemblersLocked)
|
||||
|
||||
return f
|
||||
@@ -158,6 +174,8 @@ func NewFragmentation(blockSize uint16, highMemoryLimit, lowMemoryLimit int, rea
|
||||
// proto is the protocol number marked in the fragment being processed. It has
|
||||
// to be given here outside of the FragmentID struct because IPv6 should not use
|
||||
// the protocol to identify a fragment.
|
||||
//
|
||||
// +checklocksexclude:f.mu
|
||||
func (f *Fragmentation) Process(
|
||||
id FragmentID, first, last uint16, more bool, proto uint8, pkt *stack.PacketBuffer) (
|
||||
*stack.PacketBuffer, uint8, bool, error) {
|
||||
@@ -180,6 +198,7 @@ func (f *Fragmentation) Process(
|
||||
|
||||
f.mu.Lock()
|
||||
if f.reassemblers == nil {
|
||||
f.mu.Unlock()
|
||||
return nil, 0, false, fmt.Errorf("Release() called before fragmentation processing could finish")
|
||||
}
|
||||
|
||||
@@ -228,6 +247,8 @@ func (f *Fragmentation) Process(
|
||||
}
|
||||
|
||||
// Release releases all underlying resources.
|
||||
//
|
||||
// +checklocksexclude:f.mu
|
||||
func (f *Fragmentation) Release() {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
@@ -237,40 +258,52 @@ func (f *Fragmentation) Release() {
|
||||
f.reassemblers = nil
|
||||
}
|
||||
|
||||
// release releases r if it has not already been marked done.
|
||||
// r must belong to f.
|
||||
//
|
||||
// +checklocks:f.mu
|
||||
// +checklocksexclude:r.mu
|
||||
func (f *Fragmentation) release(r *reassembler, timedOut bool) {
|
||||
// Before releasing a fragment we need to check if r is already marked as done.
|
||||
// Otherwise, we would delete it twice.
|
||||
if r.checkDoneOrMark() {
|
||||
return
|
||||
}
|
||||
// checkDoneOrMark waited for active processing under r.mu. Since it
|
||||
// returned false, this call marked r done, so subsequent process calls
|
||||
// return before accessing r.memSize, r.pkt, or r.holes. This call now
|
||||
// owns those fields exclusively without r.mu. checklocks cannot model
|
||||
// this ownership transfer.
|
||||
|
||||
delete(f.reassemblers, r.id)
|
||||
f.rList.Remove(r)
|
||||
f.memSize -= r.memSize
|
||||
f.memSize -= r.memSize // +checklocksignore
|
||||
if f.memSize < 0 {
|
||||
log.Warningf("memory counter < 0 (%d), this is an accounting bug that requires investigation", f.memSize)
|
||||
f.memSize = 0
|
||||
}
|
||||
|
||||
if h := f.timeoutHandler; timedOut && h != nil {
|
||||
h.OnReassemblyTimeout(r.pkt)
|
||||
h.OnReassemblyTimeout(r.pkt) // +checklocksignore
|
||||
}
|
||||
if r.pkt != nil {
|
||||
r.pkt.DecRef()
|
||||
r.pkt = nil
|
||||
if r.pkt != nil { // +checklocksignore
|
||||
r.pkt.DecRef() // +checklocksignore
|
||||
r.pkt = nil // +checklocksignore
|
||||
}
|
||||
for _, h := range r.holes {
|
||||
for _, h := range r.holes { // +checklocksignore
|
||||
if h.pkt != nil {
|
||||
h.pkt.DecRef()
|
||||
h.pkt = nil
|
||||
}
|
||||
}
|
||||
r.holes = nil
|
||||
r.holes = nil // +checklocksignore
|
||||
}
|
||||
|
||||
// releaseReassemblersLocked releases already-expired reassemblers, then
|
||||
// schedules the job to call back itself for the remaining reassemblers if
|
||||
// any. This function must be called with f.mu locked.
|
||||
// any.
|
||||
//
|
||||
// +checklocks:f.mu
|
||||
func (f *Fragmentation) releaseReassemblersLocked() {
|
||||
now := f.clock.NowMonotonic()
|
||||
for {
|
||||
@@ -301,6 +334,7 @@ type PacketFragmenter struct {
|
||||
fragmentCount int
|
||||
currentFragment int
|
||||
fragmentOffset int
|
||||
mark uint32
|
||||
}
|
||||
|
||||
// MakePacketFragmenter prepares the struct needed for packet fragmentation.
|
||||
@@ -331,6 +365,7 @@ func MakePacketFragmenter(pkt *stack.PacketBuffer, fragmentPayloadLen uint32, re
|
||||
reserve: reserve,
|
||||
fragmentPayloadLen: int(fragmentPayloadLen),
|
||||
fragmentCount: int(fragmentCount),
|
||||
mark: pkt.Mark,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,6 +385,7 @@ func (pf *PacketFragmenter) BuildNextFragment() (*stack.PacketBuffer, int, int,
|
||||
|
||||
fragPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: pf.reserve,
|
||||
Mark: pf.mark,
|
||||
})
|
||||
|
||||
// Copy data for the fragment.
|
||||
|
||||
+48
-10
@@ -15,14 +15,21 @@
|
||||
package fragmentation
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"math"
|
||||
"sort"
|
||||
"slices"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// hole describes a missing or received range in a reassembler.
|
||||
//
|
||||
// Holes stored in reassembler.holes are protected by that reassembler's mu
|
||||
// until release takes exclusive ownership. A hole has no reference to its
|
||||
// reassembler, so checklocks cannot name the owning mutex.
|
||||
//
|
||||
// +stateify savable
|
||||
type hole struct {
|
||||
first uint16
|
||||
@@ -34,18 +41,34 @@ type hole struct {
|
||||
pkt *stack.PacketBuffer
|
||||
}
|
||||
|
||||
// reassembler holds a packet's fragments until reassembly or release.
|
||||
//
|
||||
// Fragmentation.release claims exclusive ownership of memSize, holes, and
|
||||
// pkt by marking reassembly done under mu. Those cleanup accesses need
|
||||
// exceptions because checklocks cannot model the ownership transfer.
|
||||
//
|
||||
// +stateify savable
|
||||
type reassembler struct {
|
||||
// reassemblerEntry is protected by the owning Fragmentation.mu. There
|
||||
// is no reference back to that Fragmentation, so checklocks cannot name
|
||||
// its mutex here.
|
||||
reassemblerEntry
|
||||
id FragmentID
|
||||
memSize int
|
||||
proto uint8
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
holes []hole
|
||||
filled int
|
||||
id FragmentID
|
||||
// +checklocks:mu
|
||||
memSize int
|
||||
// +checklocks:mu
|
||||
proto uint8
|
||||
// mu follows the owning Fragmentation.mu in lock order.
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
// +checklocks:mu
|
||||
holes []hole
|
||||
// +checklocks:mu
|
||||
filled int
|
||||
// +checklocks:mu
|
||||
done bool
|
||||
createdAt tcpip.MonotonicTime
|
||||
pkt *stack.PacketBuffer
|
||||
// +checklocks:mu
|
||||
pkt *stack.PacketBuffer
|
||||
}
|
||||
|
||||
func newReassembler(id FragmentID, clock tcpip.Clock) *reassembler {
|
||||
@@ -62,6 +85,7 @@ func newReassembler(id FragmentID, clock tcpip.Clock) *reassembler {
|
||||
return r
|
||||
}
|
||||
|
||||
// +checklocksexclude:r.mu
|
||||
func (r *reassembler) process(first, last uint16, more bool, proto uint8, pkt *stack.PacketBuffer) (*stack.PacketBuffer, uint8, bool, int, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
@@ -103,8 +127,21 @@ func (r *reassembler) process(first, last uint16, more bool, proto uint8, pkt *s
|
||||
}
|
||||
|
||||
holeFound = true
|
||||
// IPv6: rfc8200#section-4.5
|
||||
// Changed the text to require that IPv6 nodes must not create
|
||||
// overlapping fragments. Also, when reassembling an IPv6
|
||||
// datagram, if one or more its constituent fragments is
|
||||
// determined to be an overlapping fragment, the entire datagram
|
||||
// (and any constituent fragments) must be silently discarded.
|
||||
// Includes a clarification that no ICMP error message should be
|
||||
// sent if overlapping fragments are received.
|
||||
if currentHole.filled {
|
||||
// Incoming fragment is a subset of an existing fragment.
|
||||
if first != currentHole.first || last != currentHole.last {
|
||||
return nil, 0, false, 0, ErrFragmentOverlap
|
||||
}
|
||||
// Incoming fragment is a duplicate.
|
||||
// Not dropping packet in case of duplicates.
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -165,8 +202,8 @@ func (r *reassembler) process(first, last uint16, more bool, proto uint8, pkt *s
|
||||
return nil, 0, false, memConsumed, nil
|
||||
}
|
||||
|
||||
sort.Slice(r.holes, func(i, j int) bool {
|
||||
return r.holes[i].first < r.holes[j].first
|
||||
slices.SortFunc(r.holes, func(a, b hole) int {
|
||||
return cmp.Compare(a.first, b.first)
|
||||
})
|
||||
|
||||
resPkt := r.holes[0].pkt.Clone()
|
||||
@@ -176,6 +213,7 @@ func (r *reassembler) process(first, last uint16, more bool, proto uint8, pkt *s
|
||||
return resPkt, r.proto, true /* done */, memConsumed, nil
|
||||
}
|
||||
|
||||
// +checklocksexclude:r.mu
|
||||
func (r *reassembler) checkDoneOrMark() bool {
|
||||
r.mu.Lock()
|
||||
prev := r.done
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ type dadState struct {
|
||||
extendRequest extendRequest
|
||||
|
||||
done *bool
|
||||
timer tcpip.Timer
|
||||
timer tcpip.Timer `state:"nosave"`
|
||||
|
||||
completionHandlers []stack.DADCompletionHandler
|
||||
}
|
||||
|
||||
+3
-3
@@ -291,13 +291,13 @@ type GenericMulticastProtocolState struct {
|
||||
robustnessVariable uint8
|
||||
queryInterval time.Duration
|
||||
mode protocolMode
|
||||
modeTimer tcpip.Timer
|
||||
modeTimer tcpip.Timer `state:"nosave"`
|
||||
|
||||
generalQueryV2Timer tcpip.Timer
|
||||
generalQueryV2Timer tcpip.Timer `state:"nosave"`
|
||||
// TODO(b/341946753): Restore when netstack is savable.
|
||||
generalQueryV2TimerFiresAt time.Time `state:"nosave"`
|
||||
|
||||
stateChangedReportV2Timer tcpip.Timer
|
||||
stateChangedReportV2Timer tcpip.Timer `state:"nosave"`
|
||||
stateChangedReportV2TimerSet bool
|
||||
}
|
||||
|
||||
|
||||
+4
-16
@@ -17,7 +17,6 @@ func (d *dadState) StateFields() []string {
|
||||
"nonce",
|
||||
"extendRequest",
|
||||
"done",
|
||||
"timer",
|
||||
"completionHandlers",
|
||||
}
|
||||
}
|
||||
@@ -30,8 +29,7 @@ func (d *dadState) StateSave(stateSinkObject state.Sink) {
|
||||
stateSinkObject.Save(0, &d.nonce)
|
||||
stateSinkObject.Save(1, &d.extendRequest)
|
||||
stateSinkObject.Save(2, &d.done)
|
||||
stateSinkObject.Save(3, &d.timer)
|
||||
stateSinkObject.Save(4, &d.completionHandlers)
|
||||
stateSinkObject.Save(3, &d.completionHandlers)
|
||||
}
|
||||
|
||||
func (d *dadState) afterLoad(context.Context) {}
|
||||
@@ -41,8 +39,7 @@ func (d *dadState) StateLoad(ctx context.Context, stateSourceObject state.Source
|
||||
stateSourceObject.Load(0, &d.nonce)
|
||||
stateSourceObject.Load(1, &d.extendRequest)
|
||||
stateSourceObject.Load(2, &d.done)
|
||||
stateSourceObject.Load(3, &d.timer)
|
||||
stateSourceObject.Load(4, &d.completionHandlers)
|
||||
stateSourceObject.Load(3, &d.completionHandlers)
|
||||
}
|
||||
|
||||
func (d *DADOptions) StateTypeName() string {
|
||||
@@ -237,9 +234,6 @@ func (g *GenericMulticastProtocolState) StateFields() []string {
|
||||
"robustnessVariable",
|
||||
"queryInterval",
|
||||
"mode",
|
||||
"modeTimer",
|
||||
"generalQueryV2Timer",
|
||||
"stateChangedReportV2Timer",
|
||||
"stateChangedReportV2TimerSet",
|
||||
}
|
||||
}
|
||||
@@ -254,10 +248,7 @@ func (g *GenericMulticastProtocolState) StateSave(stateSinkObject state.Sink) {
|
||||
stateSinkObject.Save(2, &g.robustnessVariable)
|
||||
stateSinkObject.Save(3, &g.queryInterval)
|
||||
stateSinkObject.Save(4, &g.mode)
|
||||
stateSinkObject.Save(5, &g.modeTimer)
|
||||
stateSinkObject.Save(6, &g.generalQueryV2Timer)
|
||||
stateSinkObject.Save(7, &g.stateChangedReportV2Timer)
|
||||
stateSinkObject.Save(8, &g.stateChangedReportV2TimerSet)
|
||||
stateSinkObject.Save(5, &g.stateChangedReportV2TimerSet)
|
||||
}
|
||||
|
||||
func (g *GenericMulticastProtocolState) afterLoad(context.Context) {}
|
||||
@@ -269,10 +260,7 @@ func (g *GenericMulticastProtocolState) StateLoad(ctx context.Context, stateSour
|
||||
stateSourceObject.Load(2, &g.robustnessVariable)
|
||||
stateSourceObject.Load(3, &g.queryInterval)
|
||||
stateSourceObject.Load(4, &g.mode)
|
||||
stateSourceObject.Load(5, &g.modeTimer)
|
||||
stateSourceObject.Load(6, &g.generalQueryV2Timer)
|
||||
stateSourceObject.Load(7, &g.stateChangedReportV2Timer)
|
||||
stateSourceObject.Load(8, &g.stateChangedReportV2TimerSet)
|
||||
stateSourceObject.Load(5, &g.stateChangedReportV2TimerSet)
|
||||
}
|
||||
|
||||
func (m *MultiCounterIPForwardingStats) StateTypeName() string {
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
// Copyright 2026 The 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 ip
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// ipv6FragmentOffset returns the fragment offset of the IPv6 packet
|
||||
// if present.
|
||||
func ipv6FragmentOffset(pkt *stack.PacketBuffer, ipHdr header.IPv6) (uint16, bool) {
|
||||
if !header.IsExtensionHeader(ipHdr.NextHeader()) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
netHeaderSlice := pkt.NetworkHeader().Slice()
|
||||
if len(netHeaderSlice) <= header.IPv6MinimumSize {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Make an iterator to walk the extension headers.
|
||||
buf := buffer.MakeWithData(netHeaderSlice[header.IPv6MinimumSize:])
|
||||
it := header.MakeIPv6PayloadIterator(header.IPv6ExtensionHeaderIdentifier(ipHdr.NextHeader()), buf)
|
||||
defer it.Release()
|
||||
|
||||
for {
|
||||
extHdr, done, err := it.Next()
|
||||
if err != nil || done {
|
||||
break
|
||||
}
|
||||
switch extHdr := extHdr.(type) {
|
||||
case header.IPv6FragmentExtHdr:
|
||||
offset := extHdr.FragmentOffset()
|
||||
extHdr.Release()
|
||||
return offset, true
|
||||
default:
|
||||
extHdr.Release()
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// buildResetPayloadV4 builds an IPv4 + TCP Reset packet in a buffer.
|
||||
func buildResetPayloadV4(ttl uint8, src, dst tcpip.Address, tcpHdr header.TCP, seq, ack uint32, flags header.TCPFlags) *buffer.View {
|
||||
totalHdrLen := header.IPv4MinimumSize + header.TCPMinimumSize
|
||||
v := buffer.NewViewSize(totalHdrLen)
|
||||
buf := v.AsSlice()
|
||||
|
||||
rstIPHdr := header.IPv4(buf[:header.IPv4MinimumSize])
|
||||
rstIPHdr.Encode(&header.IPv4Fields{
|
||||
TotalLength: uint16(totalHdrLen),
|
||||
TTL: ttl,
|
||||
Protocol: uint8(header.TCPProtocolNumber),
|
||||
TOS: stack.DefaultTOS,
|
||||
Flags: header.IPv4FlagDontFragment,
|
||||
// Flip source and destination addresses.
|
||||
SrcAddr: dst,
|
||||
DstAddr: src,
|
||||
})
|
||||
|
||||
rstTCPHdr := header.TCP(buf[header.IPv4MinimumSize:])
|
||||
rstTCPHdr.Encode(&header.TCPFields{
|
||||
SrcPort: tcpHdr.DestinationPort(),
|
||||
DstPort: tcpHdr.SourcePort(),
|
||||
SeqNum: seq,
|
||||
AckNum: ack,
|
||||
DataOffset: header.TCPMinimumSize,
|
||||
Flags: flags,
|
||||
})
|
||||
|
||||
xsum := header.PseudoHeaderChecksum(header.TCPProtocolNumber, dst, src, header.TCPMinimumSize)
|
||||
rstTCPHdr.SetChecksum(0)
|
||||
rstTCPHdr.SetChecksum(^rstTCPHdr.CalculateChecksum(xsum))
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// buildResetPayloadV6 builds an IPv6 + TCP Reset packet in a buffer.
|
||||
func buildResetPayloadV6(hopLimit uint8, src, dst tcpip.Address, tcpHdr header.TCP, seq, ack uint32, flags header.TCPFlags) *buffer.View {
|
||||
totalHdrLen := header.IPv6MinimumSize + header.TCPMinimumSize
|
||||
v := buffer.NewViewSize(totalHdrLen)
|
||||
buf := v.AsSlice()
|
||||
|
||||
rstIPHdr := header.IPv6(buf[:header.IPv6MinimumSize])
|
||||
rstIPHdr.Encode(&header.IPv6Fields{
|
||||
PayloadLength: uint16(header.TCPMinimumSize),
|
||||
TransportProtocol: header.TCPProtocolNumber,
|
||||
HopLimit: hopLimit,
|
||||
// Flip source and destination addresses.
|
||||
SrcAddr: dst,
|
||||
DstAddr: src,
|
||||
})
|
||||
|
||||
rstTCPHdr := header.TCP(buf[header.IPv6MinimumSize:])
|
||||
rstTCPHdr.Encode(&header.TCPFields{
|
||||
SrcPort: tcpHdr.DestinationPort(),
|
||||
DstPort: tcpHdr.SourcePort(),
|
||||
SeqNum: seq,
|
||||
AckNum: ack,
|
||||
DataOffset: header.TCPMinimumSize,
|
||||
Flags: flags,
|
||||
})
|
||||
|
||||
// Compute TCP checksum.
|
||||
xsum := header.PseudoHeaderChecksum(header.TCPProtocolNumber, dst, src, header.TCPMinimumSize)
|
||||
rstTCPHdr.SetChecksum(0)
|
||||
rstTCPHdr.SetChecksum(^rstTCPHdr.CalculateChecksum(xsum))
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// RejectWithTCPReset sends a TCP reset in response to the packet.
|
||||
//
|
||||
// Ref: net/ipv[4|6]/netfilter/nf_reject_ipv[4|6].c:nf_send_reset[6]()
|
||||
func RejectWithTCPReset(pkt *stack.PacketBuffer, netProto tcpip.NetworkProtocolNumber, stk *stack.Stack, deliveredLocally bool) tcpip.Error {
|
||||
var src, dst tcpip.Address
|
||||
var ttl uint8
|
||||
isFragment := false
|
||||
|
||||
switch netProto {
|
||||
case header.IPv4ProtocolNumber:
|
||||
// Ref: net/ipv4/netfilter/nf_reject_ipv4.c:nf_reject_ip_tcphdr_get
|
||||
ipHdr := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
if len(ipHdr) < header.IPv4MinimumSize {
|
||||
return nil
|
||||
}
|
||||
if ipHdr.Protocol() != uint8(header.TCPProtocolNumber) {
|
||||
return nil
|
||||
}
|
||||
if ipHdr.FragmentOffset() != 0 {
|
||||
return nil
|
||||
}
|
||||
isFragment = ipHdr.More()
|
||||
src = ipHdr.SourceAddress()
|
||||
dst = ipHdr.DestinationAddress()
|
||||
|
||||
// Ref: net/ipv4/netfilter/nf_reject_ipv4.c:nf_send_reset
|
||||
if header.IsV4MulticastAddress(dst) ||
|
||||
header.IsV4MulticastAddress(src) ||
|
||||
pkt.NetworkPacketInfo.LocalAddressBroadcast ||
|
||||
pkt.PktType == tcpip.PacketBroadcast || pkt.PktType == tcpip.PacketMulticast ||
|
||||
src == header.IPv4Any || dst == header.IPv4Any {
|
||||
return nil
|
||||
}
|
||||
|
||||
case header.IPv6ProtocolNumber:
|
||||
// Ref: net/ipv6/netfilter/nf_reject_ipv6.c:nf_reject_ip6_tcphdr_get
|
||||
ipHdr := header.IPv6(pkt.NetworkHeader().Slice())
|
||||
if len(ipHdr) < header.IPv6MinimumSize {
|
||||
return nil
|
||||
}
|
||||
fragOffset, ok := ipv6FragmentOffset(pkt, ipHdr)
|
||||
if ok && fragOffset != 0 {
|
||||
return nil
|
||||
}
|
||||
isFragment = ok
|
||||
src = ipHdr.SourceAddress()
|
||||
dst = ipHdr.DestinationAddress()
|
||||
|
||||
// Ref: net/ipv6/netfilter/nf_reject_ipv6.c:nf_send_reset6
|
||||
if header.IsV6MulticastAddress(src) || header.IsV6MulticastAddress(dst) ||
|
||||
header.IsV4MappedAddress(src) || header.IsV4MappedAddress(dst) ||
|
||||
src == header.IPv6Any || dst == header.IPv6Any ||
|
||||
pkt.PktType == tcpip.PacketBroadcast || pkt.PktType == tcpip.PacketMulticast {
|
||||
return nil
|
||||
}
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
tcpHdr := func(pkt *stack.PacketBuffer) header.TCP {
|
||||
// If 0 < len(transportHdr) < header.TCPMinimumSize, then the TCP header is invalid.
|
||||
// Assuming a TCP packet,
|
||||
// if the TCP header was parsed, the
|
||||
// len should be >= header.TCPMinimumSize;
|
||||
// else the TCP header was not parsed and the len should be 0.
|
||||
transportHdr := pkt.TransportHeader().Slice()
|
||||
if len(transportHdr) >= header.TCPMinimumSize {
|
||||
return header.TCP(transportHdr)
|
||||
}
|
||||
if len(transportHdr) != 0 {
|
||||
return nil
|
||||
}
|
||||
// In the case of fragmented TCP packets, the TCP header may not be parsed.
|
||||
// Pull up the TCP header from the payload.
|
||||
b, ok := pkt.Data().PullUp(header.TCPMinimumSize)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
hdr := header.TCP(b)
|
||||
hdrLen := int(hdr.DataOffset())
|
||||
if hdrLen < header.TCPMinimumSize || pkt.Data().Size() < hdrLen {
|
||||
return nil
|
||||
}
|
||||
tcpHdr, ok := pkt.Data().Consume(hdrLen)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
pkt.TransportProtocolNumber = header.TCPProtocolNumber
|
||||
return header.TCP(tcpHdr)
|
||||
}(pkt)
|
||||
if tcpHdr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ref: net/ipv[4|6]/netfilter/nf_reject_ipv[4|6].c:nf_reject_ip[6]_tcphdr_get()
|
||||
// No RST for RST as this will cause a loop.
|
||||
if tcpHdr.Flags().Contains(header.TCPFlagRst) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check checksum integrity only for non-fragmented packets.
|
||||
// We don't support refragmentation(nf_defrag) before REJECT,
|
||||
// so we can't validate the checksum for fragmented packets.
|
||||
if !isFragment {
|
||||
// Check checksum integrity.
|
||||
if !pkt.RXChecksumValidated && !tcpHdr.IsChecksumValid(src, dst, pkt.Data().Checksum(), uint16(pkt.Data().Size())) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
localAddr := dst
|
||||
if !deliveredLocally {
|
||||
// If the packet wasn't delivered locally, do not use the packet's destination
|
||||
// address as the response's source address as we should not own the
|
||||
// destination address.
|
||||
localAddr = tcpip.Address{}
|
||||
}
|
||||
|
||||
route, err := stk.FindRoute(0 /*nicID*/, localAddr, src, netProto, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer route.Release()
|
||||
ttl = route.DefaultTTL()
|
||||
|
||||
var seq uint32
|
||||
var ack uint32
|
||||
payloadLen := uint32(pkt.Data().Size())
|
||||
flags := header.TCPFlagRst
|
||||
|
||||
// Ref: net/ipv[4|6]/netfilter/nf_reject_ipv[4|6].c:nf_reject_ip[6]_tcphdr_put()
|
||||
if tcpHdr.Flags()&header.TCPFlagAck != 0 {
|
||||
seq = tcpHdr.AckNumber()
|
||||
} else {
|
||||
flags |= header.TCPFlagAck
|
||||
ack = tcpHdr.SequenceNumber() + payloadLen
|
||||
if tcpHdr.Flags()&header.TCPFlagSyn != 0 {
|
||||
ack++
|
||||
}
|
||||
if tcpHdr.Flags()&header.TCPFlagFin != 0 {
|
||||
ack++
|
||||
}
|
||||
}
|
||||
|
||||
var v *buffer.View
|
||||
if netProto == header.IPv4ProtocolNumber {
|
||||
v = buildResetPayloadV4(ttl, src, dst, tcpHdr, seq, ack, flags)
|
||||
} else {
|
||||
v = buildResetPayloadV6(ttl, src, dst, tcpHdr, seq, ack, flags)
|
||||
}
|
||||
|
||||
rstPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(route.MaxHeaderLength()),
|
||||
Payload: buffer.MakeWithView(v),
|
||||
})
|
||||
rstPkt.TransportProtocolNumber = header.TCPProtocolNumber
|
||||
defer rstPkt.DecRef()
|
||||
|
||||
// TODO: b/521536712 - Add support for mark propagation.
|
||||
if err := route.WriteHeaderIncludedPacket(rstPkt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+4
-7
@@ -16,7 +16,6 @@ func (r *RouteTable) StateFields() []string {
|
||||
return []string{
|
||||
"installedRoutes",
|
||||
"pendingRoutes",
|
||||
"cleanupPendingRoutesTimer",
|
||||
"isCleanupRoutineRunning",
|
||||
"config",
|
||||
}
|
||||
@@ -29,9 +28,8 @@ func (r *RouteTable) StateSave(stateSinkObject state.Sink) {
|
||||
r.beforeSave()
|
||||
stateSinkObject.Save(0, &r.installedRoutes)
|
||||
stateSinkObject.Save(1, &r.pendingRoutes)
|
||||
stateSinkObject.Save(2, &r.cleanupPendingRoutesTimer)
|
||||
stateSinkObject.Save(3, &r.isCleanupRoutineRunning)
|
||||
stateSinkObject.Save(4, &r.config)
|
||||
stateSinkObject.Save(2, &r.isCleanupRoutineRunning)
|
||||
stateSinkObject.Save(3, &r.config)
|
||||
}
|
||||
|
||||
func (r *RouteTable) afterLoad(context.Context) {}
|
||||
@@ -40,9 +38,8 @@ func (r *RouteTable) afterLoad(context.Context) {}
|
||||
func (r *RouteTable) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &r.installedRoutes)
|
||||
stateSourceObject.Load(1, &r.pendingRoutes)
|
||||
stateSourceObject.Load(2, &r.cleanupPendingRoutesTimer)
|
||||
stateSourceObject.Load(3, &r.isCleanupRoutineRunning)
|
||||
stateSourceObject.Load(4, &r.config)
|
||||
stateSourceObject.Load(2, &r.isCleanupRoutineRunning)
|
||||
stateSourceObject.Load(3, &r.config)
|
||||
}
|
||||
|
||||
func (r *InstalledRoute) StateTypeName() string {
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ type RouteTable struct {
|
||||
// cleanupPendingRoutesTimer is a timer that triggers a routine to remove
|
||||
// pending routes that are expired.
|
||||
// +checklocks:pendingMu
|
||||
cleanupPendingRoutesTimer tcpip.Timer
|
||||
cleanupPendingRoutesTimer tcpip.Timer `state:"nosave"`
|
||||
// +checklocks:pendingMu
|
||||
isCleanupRoutineRunning bool
|
||||
|
||||
|
||||
+133
-102
@@ -16,6 +16,7 @@ package ipv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
@@ -355,102 +356,28 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
|
||||
// DeliverTransportPacket so that is is only done when needed.
|
||||
replyData := stack.PayloadSince(pkt.TransportHeader())
|
||||
defer replyData.Release()
|
||||
ipHdr := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
localAddressTemporary := pkt.NetworkPacketInfo.LocalAddressTemporary
|
||||
localAddressBroadcast := pkt.NetworkPacketInfo.LocalAddressBroadcast
|
||||
|
||||
// It's possible that a raw socket expects to receive this.
|
||||
e.dispatcher.DeliverTransportPacket(header.ICMPv4ProtocolNumber, pkt)
|
||||
// It's possible that a raw socket or per-stack default handler expects
|
||||
// to receive this packet.
|
||||
defaultHandlerHandled := false
|
||||
if dispatcher, ok := e.dispatcher.(stack.TransportDispatcherWithDefaultHandlerResult); ok {
|
||||
_, defaultHandlerHandled = dispatcher.DeliverTransportPacketWithDefaultHandlerResult(header.ICMPv4ProtocolNumber, pkt)
|
||||
} else {
|
||||
e.dispatcher.DeliverTransportPacket(header.ICMPv4ProtocolNumber, pkt)
|
||||
}
|
||||
pkt = nil
|
||||
|
||||
sent := e.stats.icmp.packetsSent
|
||||
if !e.protocol.allowICMPReply(header.ICMPv4EchoReply, header.ICMPv4UnusedCode) {
|
||||
sent.rateLimited.Increment()
|
||||
// Skip the built-in ICMP echo reply if the request was consumed by a
|
||||
// per-stack default handler. Also preserve the IPv4 behavior for
|
||||
// temporary local addresses: the packet is delivered above, but the
|
||||
// stack does not synthesize an echo reply for it.
|
||||
if defaultHandlerHandled || localAddressTemporary {
|
||||
return
|
||||
}
|
||||
|
||||
// As per RFC 1122 section 3.2.1.3, when a host sends any datagram, the IP
|
||||
// source address MUST be one of its own IP addresses (but not a broadcast
|
||||
// or multicast address).
|
||||
localAddr := ipHdr.DestinationAddress()
|
||||
if localAddressBroadcast || header.IsV4MulticastAddress(localAddr) {
|
||||
localAddr = tcpip.Address{}
|
||||
}
|
||||
|
||||
r, err := e.protocol.stack.FindRoute(e.nic.ID(), localAddr, ipHdr.SourceAddress(), ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
// If we cannot find a route to the destination, silently drop the packet.
|
||||
return
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
outgoingEP, ok := e.protocol.getEndpointForNIC(r.NICID())
|
||||
if !ok {
|
||||
// The outgoing NIC went away.
|
||||
sent.dropped.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// Because IP and ICMP are so closely intertwined, we need to handcraft our
|
||||
// IP header to be able to follow RFC 792. The wording on page 13 is as
|
||||
// follows:
|
||||
// IP Fields:
|
||||
// Addresses
|
||||
// The address of the source in an echo message will be the
|
||||
// destination of the echo reply message. To form an echo reply
|
||||
// message, the source and destination addresses are simply reversed,
|
||||
// the type code changed to 0, and the checksum recomputed.
|
||||
//
|
||||
// This was interpreted by early implementors to mean that all options must
|
||||
// be copied from the echo request IP header to the echo reply IP header
|
||||
// and this behaviour is still relied upon by some applications.
|
||||
//
|
||||
// Create a copy of the IP header we received, options and all, and change
|
||||
// The fields we need to alter.
|
||||
//
|
||||
// We need to produce the entire packet in the data segment in order to
|
||||
// use WriteHeaderIncludedPacket(). WriteHeaderIncludedPacket sets the
|
||||
// total length and the header checksum so we don't need to set those here.
|
||||
//
|
||||
// Take the base of the incoming request IP header but replace the options.
|
||||
replyHeaderLength := uint8(header.IPv4MinimumSize + len(newOptions))
|
||||
replyIPHdrView := buffer.NewView(int(replyHeaderLength))
|
||||
replyIPHdrView.Write(iph[:header.IPv4MinimumSize])
|
||||
replyIPHdrView.Write(newOptions)
|
||||
replyIPHdr := header.IPv4(replyIPHdrView.AsSlice())
|
||||
replyIPHdr.SetHeaderLength(replyHeaderLength)
|
||||
replyIPHdr.SetSourceAddress(r.LocalAddress())
|
||||
replyIPHdr.SetDestinationAddress(r.RemoteAddress())
|
||||
replyIPHdr.SetTTL(r.DefaultTTL())
|
||||
replyIPHdr.SetTotalLength(uint16(len(replyIPHdr) + len(replyData.AsSlice())))
|
||||
replyIPHdr.SetChecksum(0)
|
||||
replyIPHdr.SetChecksum(^replyIPHdr.CalculateChecksum())
|
||||
|
||||
replyICMPHdr := header.ICMPv4(replyData.AsSlice())
|
||||
replyICMPHdr.SetType(header.ICMPv4EchoReply)
|
||||
replyICMPHdr.SetChecksum(0)
|
||||
replyICMPHdr.SetChecksum(^checksum.Checksum(replyData.AsSlice(), 0))
|
||||
|
||||
replyBuf := buffer.MakeWithView(replyIPHdrView)
|
||||
replyBuf.Append(replyData.Clone())
|
||||
replyPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(r.MaxHeaderLength()),
|
||||
Payload: replyBuf,
|
||||
})
|
||||
defer replyPkt.DecRef()
|
||||
// Populate the network/transport headers in the packet buffer so the
|
||||
// ICMP packet goes through IPTables.
|
||||
if ok := parse.IPv4(replyPkt); !ok {
|
||||
panic("expected to parse IPv4 header we just created")
|
||||
}
|
||||
if ok := parse.ICMPv4(replyPkt); !ok {
|
||||
panic("expected to parse ICMPv4 header we just created")
|
||||
}
|
||||
|
||||
if err := outgoingEP.writePacket(r, replyPkt); err != nil {
|
||||
sent.dropped.Increment()
|
||||
return
|
||||
}
|
||||
sent.echoReply.Increment()
|
||||
e.sendICMPEchoReply(replyData, iph, newOptions, localAddressBroadcast)
|
||||
|
||||
case header.ICMPv4EchoReply:
|
||||
received.echoReply.Increment()
|
||||
@@ -523,6 +450,98 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *endpoint) sendICMPEchoReply(replyData *buffer.View, ipHdr header.IPv4, newOptions header.IPv4Options, localAddressBroadcast bool) {
|
||||
sent := e.stats.icmp.packetsSent
|
||||
if !e.protocol.allowICMPReply(header.ICMPv4EchoReply, header.ICMPv4UnusedCode) {
|
||||
sent.rateLimited.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// As per RFC 1122 section 3.2.1.3, when a host sends any datagram, the IP
|
||||
// source address MUST be one of its own IP addresses (but not a broadcast
|
||||
// or multicast address).
|
||||
localAddr := ipHdr.DestinationAddress()
|
||||
if localAddressBroadcast || header.IsV4MulticastAddress(localAddr) {
|
||||
localAddr = tcpip.Address{}
|
||||
}
|
||||
|
||||
r, err := e.protocol.stack.FindRoute(e.nic.ID(), localAddr, ipHdr.SourceAddress(), ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
// If we cannot find a route to the destination, silently drop the packet.
|
||||
return
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
outgoingEP, ok := e.protocol.getEndpointForNIC(r.NICID())
|
||||
if !ok {
|
||||
// The outgoing NIC went away.
|
||||
sent.dropped.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// Because IP and ICMP are so closely intertwined, we need to handcraft our
|
||||
// IP header to be able to follow RFC 792. The wording on page 13 is as
|
||||
// follows:
|
||||
// IP Fields:
|
||||
// Addresses
|
||||
// The address of the source in an echo message will be the
|
||||
// destination of the echo reply message. To form an echo reply
|
||||
// message, the source and destination addresses are simply reversed,
|
||||
// the type code changed to 0, and the checksum recomputed.
|
||||
//
|
||||
// This was interpreted by early implementers to mean that all options must
|
||||
// be copied from the echo request IP header to the echo reply IP header
|
||||
// and this behaviour is still relied upon by some applications.
|
||||
//
|
||||
// Create a copy of the IP header we received, options and all, and change
|
||||
// The fields we need to alter.
|
||||
//
|
||||
// We need to produce the entire packet in the data segment in order to
|
||||
// use WriteHeaderIncludedPacket(). WriteHeaderIncludedPacket sets the
|
||||
// total length and the header checksum so we don't need to set those here.
|
||||
//
|
||||
// Take the base of the incoming request IP header but replace the options.
|
||||
replyHeaderLength := uint8(header.IPv4MinimumSize + len(newOptions))
|
||||
replyIPHdrView := buffer.NewView(int(replyHeaderLength))
|
||||
replyIPHdrView.Write(ipHdr[:header.IPv4MinimumSize])
|
||||
replyIPHdrView.Write(newOptions)
|
||||
replyIPHdr := header.IPv4(replyIPHdrView.AsSlice())
|
||||
replyIPHdr.SetHeaderLength(replyHeaderLength)
|
||||
replyIPHdr.SetSourceAddress(r.LocalAddress())
|
||||
replyIPHdr.SetDestinationAddress(r.RemoteAddress())
|
||||
replyIPHdr.SetTTL(r.DefaultTTL())
|
||||
replyIPHdr.SetTotalLength(uint16(len(replyIPHdr) + len(replyData.AsSlice())))
|
||||
replyIPHdr.SetChecksum(0)
|
||||
replyIPHdr.SetChecksum(^replyIPHdr.CalculateChecksum())
|
||||
|
||||
replyICMPHdr := header.ICMPv4(replyData.AsSlice())
|
||||
replyICMPHdr.SetType(header.ICMPv4EchoReply)
|
||||
replyICMPHdr.SetChecksum(0)
|
||||
replyICMPHdr.SetChecksum(^checksum.Checksum(replyData.AsSlice(), 0))
|
||||
|
||||
replyBuf := buffer.MakeWithView(replyIPHdrView)
|
||||
replyBuf.Append(replyData.Clone())
|
||||
replyPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(r.MaxHeaderLength()),
|
||||
Payload: replyBuf,
|
||||
})
|
||||
defer replyPkt.DecRef()
|
||||
// Populate the network/transport headers in the packet buffer so the
|
||||
// ICMP packet goes through IPTables.
|
||||
if ok := parse.IPv4(replyPkt); !ok {
|
||||
panic("expected to parse IPv4 header we just created")
|
||||
}
|
||||
if ok := parse.ICMPv4(replyPkt); !ok {
|
||||
panic("expected to parse ICMPv4 header we just created")
|
||||
}
|
||||
|
||||
if err := outgoingEP.writePacket(r, replyPkt); err != nil {
|
||||
sent.dropped.Increment()
|
||||
return
|
||||
}
|
||||
sent.echoReply.Increment()
|
||||
}
|
||||
|
||||
// ======= ICMP Error packet generation =========
|
||||
|
||||
// icmpReason is a marker interface for IPv4 specific ICMP errors.
|
||||
@@ -591,7 +610,12 @@ func (*icmpReasonNetworkUnreachable) isICMPReason() {}
|
||||
// icmpReasonFragmentationNeeded is an error where a packet requires
|
||||
// fragmentation while also having the Don't Fragment flag set, as per RFC 792
|
||||
// page 3, Destination Unreachable Message.
|
||||
type icmpReasonFragmentationNeeded struct{}
|
||||
type icmpReasonFragmentationNeeded struct {
|
||||
// mtu is the MTU of the next-hop link. Per RFC 1191 §4, this value
|
||||
// must be included in the ICMP Fragmentation Needed message so the
|
||||
// sender can update its path MTU cache.
|
||||
mtu uint32
|
||||
}
|
||||
|
||||
func (*icmpReasonFragmentationNeeded) isICMPReason() {}
|
||||
|
||||
@@ -696,30 +720,36 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer, deliv
|
||||
}
|
||||
|
||||
sent := netEP.stats.icmp.packetsSent
|
||||
icmpType, icmpCode, counter, pointer := func() (header.ICMPv4Type, header.ICMPv4Code, tcpip.MultiCounterStat, byte) {
|
||||
icmpType, icmpCode, counter, pointer, nextHopMTU := func() (header.ICMPv4Type, header.ICMPv4Code, tcpip.MultiCounterStat, byte, uint16) {
|
||||
switch reason := reason.(type) {
|
||||
case *icmpReasonNetworkProhibited:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4NetProhibited, sent.dstUnreachable, 0
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4NetProhibited, sent.dstUnreachable, 0, 0
|
||||
case *icmpReasonHostProhibited:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4HostProhibited, sent.dstUnreachable, 0
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4HostProhibited, sent.dstUnreachable, 0, 0
|
||||
case *icmpReasonAdministrativelyProhibited:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4AdminProhibited, sent.dstUnreachable, 0
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4AdminProhibited, sent.dstUnreachable, 0, 0
|
||||
case *icmpReasonPortUnreachable:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4PortUnreachable, sent.dstUnreachable, 0
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4PortUnreachable, sent.dstUnreachable, 0, 0
|
||||
case *icmpReasonProtoUnreachable:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4ProtoUnreachable, sent.dstUnreachable, 0
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4ProtoUnreachable, sent.dstUnreachable, 0, 0
|
||||
case *icmpReasonNetworkUnreachable:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4NetUnreachable, sent.dstUnreachable, 0
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4NetUnreachable, sent.dstUnreachable, 0, 0
|
||||
case *icmpReasonHostUnreachable:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4HostUnreachable, sent.dstUnreachable, 0
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4HostUnreachable, sent.dstUnreachable, 0, 0
|
||||
case *icmpReasonFragmentationNeeded:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4FragmentationNeeded, sent.dstUnreachable, 0
|
||||
// Per RFC 1191 §4, include the next-hop MTU in the ICMP message.
|
||||
// Cap at MaxUint16 since the field is 16 bits wide.
|
||||
mtu := reason.mtu
|
||||
if mtu > math.MaxUint16 {
|
||||
mtu = math.MaxUint16
|
||||
}
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4FragmentationNeeded, sent.dstUnreachable, 0, uint16(mtu)
|
||||
case *icmpReasonTTLExceeded:
|
||||
return header.ICMPv4TimeExceeded, header.ICMPv4TTLExceeded, sent.timeExceeded, 0
|
||||
return header.ICMPv4TimeExceeded, header.ICMPv4TTLExceeded, sent.timeExceeded, 0, 0
|
||||
case *icmpReasonReassemblyTimeout:
|
||||
return header.ICMPv4TimeExceeded, header.ICMPv4ReassemblyTimeout, sent.timeExceeded, 0
|
||||
return header.ICMPv4TimeExceeded, header.ICMPv4ReassemblyTimeout, sent.timeExceeded, 0, 0
|
||||
case *icmpReasonParamProblem:
|
||||
return header.ICMPv4ParamProblem, header.ICMPv4UnusedCode, sent.paramProblem, reason.pointer
|
||||
return header.ICMPv4ParamProblem, header.ICMPv4UnusedCode, sent.paramProblem, reason.pointer, 0
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported ICMP type %T", reason))
|
||||
}
|
||||
@@ -788,6 +818,7 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer, deliv
|
||||
icmpHdr.SetCode(icmpCode)
|
||||
icmpHdr.SetType(icmpType)
|
||||
icmpHdr.SetPointer(pointer)
|
||||
icmpHdr.SetMTU(nextHopMTU)
|
||||
icmpHdr.SetChecksum(header.ICMPv4Checksum(icmpHdr, icmpPkt.Data().Checksum()))
|
||||
|
||||
if err := route.WritePacket(
|
||||
|
||||
+160
-41
@@ -26,6 +26,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/checksum"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header/parse"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/hash"
|
||||
@@ -82,6 +83,7 @@ var _ stack.AddressableEndpoint = (*endpoint)(nil)
|
||||
var _ stack.NetworkEndpoint = (*endpoint)(nil)
|
||||
var _ IGMPEndpoint = (*endpoint)(nil)
|
||||
|
||||
// +checklocksalias:igmp.ep.mu=mu
|
||||
// +stateify savable
|
||||
type endpoint struct {
|
||||
nic stack.NetworkInterface
|
||||
@@ -126,13 +128,11 @@ func (e *endpoint) GetIGMPVersion() IGMPVersion {
|
||||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.igmp.ep.mu=e.mu
|
||||
func (e *endpoint) setIGMPVersionLocked(v IGMPVersion) IGMPVersion {
|
||||
return e.igmp.setVersion(v)
|
||||
}
|
||||
|
||||
// +checklocksread:e.mu
|
||||
// +checklocksalias:e.igmp.ep.mu=e.mu
|
||||
func (e *endpoint) getIGMPVersionLocked() IGMPVersion {
|
||||
return e.igmp.getVersion()
|
||||
}
|
||||
@@ -291,7 +291,6 @@ func (e *endpoint) Enable() tcpip.Error {
|
||||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.igmp.ep.mu=e.mu
|
||||
func (e *endpoint) enableLocked() tcpip.Error {
|
||||
// If the NIC is not enabled, the endpoint can't do anything meaningful so
|
||||
// don't enable the endpoint.
|
||||
@@ -362,7 +361,6 @@ func (e *endpoint) Disable() {
|
||||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.igmp.ep.mu=e.mu
|
||||
func (e *endpoint) disableLocked() {
|
||||
if !e.isEnabled() {
|
||||
return
|
||||
@@ -426,6 +424,11 @@ func (e *endpoint) MTU() uint32 {
|
||||
return networkMTU
|
||||
}
|
||||
|
||||
// EndpointHeaderSize returns the size necessary for the IPv4 header.
|
||||
func (e *endpoint) EndpointHeaderSize() uint32 {
|
||||
return header.IPv4MinimumSize
|
||||
}
|
||||
|
||||
// MaxHeaderLength returns the maximum length needed by ipv4 headers (and
|
||||
// underlying protocols).
|
||||
func (e *endpoint) MaxHeaderLength() uint16 {
|
||||
@@ -517,6 +520,54 @@ func (e *endpoint) handleFragments(_ *stack.Route, networkMTU uint32, pkt *stack
|
||||
}
|
||||
}
|
||||
|
||||
// recalculateChecksum recalculates the checksum of a TCP packet.
|
||||
func recalculateChecksum(pkt *stack.PacketBuffer, r *stack.Route) tcpip.Error {
|
||||
// RXChecksumValidated indicates that checksum verification may be
|
||||
// safely skipped.
|
||||
if pkt.RXChecksumValidated {
|
||||
return nil
|
||||
}
|
||||
// NeedsCsum is set if the checksum offload is enabled, so no need to
|
||||
// calculate the checksum.
|
||||
if pkt.GSOOptions.Type != stack.GSONone && pkt.GSOOptions.NeedsCsum {
|
||||
return nil
|
||||
}
|
||||
transportHeader := pkt.TransportHeader().Slice()
|
||||
netHdr := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
switch pkt.TransportProtocolNumber {
|
||||
case header.TCPProtocolNumber:
|
||||
if len(transportHeader) < header.TCPMinimumSize {
|
||||
return &tcpip.ErrMalformedHeader{}
|
||||
}
|
||||
tcp := header.TCP(transportHeader)
|
||||
xsum := r.PseudoHeaderChecksum(header.TCPProtocolNumber, netHdr.PayloadLength())
|
||||
xsum = checksum.Combine(xsum, pkt.Data().Checksum())
|
||||
tcp.SetChecksum(0)
|
||||
tcp.SetChecksum(^tcp.CalculateChecksum(xsum))
|
||||
case header.UDPProtocolNumber:
|
||||
if len(transportHeader) < header.UDPMinimumSize {
|
||||
return &tcpip.ErrMalformedHeader{}
|
||||
}
|
||||
udp := header.UDP(transportHeader)
|
||||
xsum := r.PseudoHeaderChecksum(header.UDPProtocolNumber, netHdr.PayloadLength())
|
||||
xsum = checksum.Combine(xsum, pkt.Data().Checksum())
|
||||
udp.SetChecksum(0)
|
||||
csum := ^udp.CalculateChecksum(xsum)
|
||||
// RFC 768: If the computed checksum is zero, it is transmitted as all ones.
|
||||
if csum == 0 {
|
||||
csum = 0xFFFF
|
||||
}
|
||||
udp.SetChecksum(csum)
|
||||
case header.ICMPv4ProtocolNumber:
|
||||
if len(transportHeader) < header.ICMPv4MinimumSize {
|
||||
return &tcpip.ErrMalformedHeader{}
|
||||
}
|
||||
icmp := header.ICMPv4(transportHeader)
|
||||
icmp.SetChecksum(header.ICMPv4Checksum(icmp, pkt.Data().Checksum()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePacket writes a packet to the given destination address and protocol.
|
||||
func (e *endpoint) WritePacket(r *stack.Route, params stack.NetworkHeaderParams, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
if err := e.addIPHeader(r.LocalAddress(), r.RemoteAddress(), pkt, params, nil /* options */); err != nil {
|
||||
@@ -529,16 +580,23 @@ func (e *endpoint) WritePacket(r *stack.Route, params stack.NetworkHeaderParams,
|
||||
func (e *endpoint) writePacket(r *stack.Route, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
netHeader := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
dstAddr := netHeader.DestinationAddress()
|
||||
stk := e.protocol.stack
|
||||
|
||||
// iptables filtering. All packets that reach here are locally
|
||||
// generated.
|
||||
outNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
|
||||
if ok := e.protocol.stack.IPTables().CheckOutput(pkt, r, outNicName); !ok {
|
||||
// iptables filtering. All packets that reach here are locally generated.
|
||||
outNicName := stk.FindNICNameFromID(e.nic.ID())
|
||||
if ok := stk.IPTables().CheckOutput(pkt, r, outNicName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
e.stats.ip.IPTablesOutputDropped.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckOutput(pkt, r, stack.IP) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// If the packet is manipulated as per DNAT Output rules, handle packet
|
||||
// based on destination address and do not send the packet to link
|
||||
// layer.
|
||||
@@ -553,6 +611,39 @@ func (e *endpoint) writePacket(r *stack.Route, pkt *stack.PacketBuffer) tcpip.Er
|
||||
ep.handleLocalPacket(pkt, true /* canSkipRXChecksum */)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Similar to the `ip_route_me_harder` in the kernel,
|
||||
// we need to find a new route for the packet.
|
||||
// Implementation is similar to the func forwardUnicastPacket.
|
||||
newRoute, err := stk.FindRoute(0 /* nic id */, netHeader.SourceAddress(), newDstAddr, header.IPv4ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
e.stats.ip.OutgoingPacketErrors.Increment()
|
||||
return err // Drop the packet
|
||||
}
|
||||
// Release the new route on exit.
|
||||
defer newRoute.Release()
|
||||
|
||||
// Check if we need to recalculate the checksum.
|
||||
// If the original route did not require a checksum but the new one does,
|
||||
// we must calculate the full checksum; otherwise, NAT should have already
|
||||
// done it.
|
||||
if !r.RequiresTXTransportChecksum() && newRoute.RequiresTXTransportChecksum() {
|
||||
if err := recalculateChecksum(pkt, newRoute); err != nil {
|
||||
e.stats.ip.OutgoingPacketErrors.Increment()
|
||||
return err // Drop the packet
|
||||
}
|
||||
}
|
||||
|
||||
// Update the route to the new route.
|
||||
r = newRoute
|
||||
|
||||
// Use the new endpoint to write the packet.
|
||||
forwardToEp, ok := e.protocol.getEndpointForNIC(r.NICID())
|
||||
if !ok {
|
||||
e.stats.ip.OutgoingPacketErrors.Increment()
|
||||
return &tcpip.ErrUnknownNICID{}
|
||||
}
|
||||
return forwardToEp.writePacketPostRouting(r, pkt, true /* headerIncluded */)
|
||||
}
|
||||
|
||||
return e.writePacketPostRouting(r, pkt, false /* headerIncluded */)
|
||||
@@ -569,15 +660,23 @@ func (e *endpoint) writePacketPostRouting(r *stack.Route, pkt *stack.PacketBuffe
|
||||
return nil
|
||||
}
|
||||
|
||||
stk := e.protocol.stack
|
||||
// Postrouting NAT can only change the source address, and does not alter the
|
||||
// route or outgoing interface of the packet.
|
||||
outNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
|
||||
if ok := e.protocol.stack.IPTables().CheckPostrouting(pkt, r, e, outNicName); !ok {
|
||||
outNicName := stk.FindNICNameFromID(e.nic.ID())
|
||||
if ok := stk.IPTables().CheckPostrouting(pkt, r, e, outNicName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
e.stats.ip.IPTablesPostroutingDropped.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckPostrouting(pkt, r, stack.IP) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
stats := e.stats.ip
|
||||
|
||||
networkMTU, err := calculateNetworkMTU(e.nic.MTU(), uint32(len(pkt.NetworkHeader().Slice())))
|
||||
@@ -688,6 +787,13 @@ func (e *endpoint) forwardPacketWithRoute(route *stack.Route, pkt *stack.PacketB
|
||||
return nil
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckForward(pkt, route, stack.IP) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// We need to do a deep copy of the IP packet because
|
||||
// WriteHeaderIncludedPacket may modify the packet buffer, but we do
|
||||
// not own it.
|
||||
@@ -723,6 +829,10 @@ func (e *endpoint) forwardPacketWithRoute(route *stack.Route, pkt *stack.PacketB
|
||||
newHdr.SetChecksum(0)
|
||||
newHdr.SetChecksum(^newHdr.CalculateChecksum())
|
||||
|
||||
if route.RequiresTXTransportChecksum() {
|
||||
newPkt.CalculateTransportChecksum()
|
||||
}
|
||||
|
||||
switch err := forwardToEp.writePacketPostRouting(route, newPkt, true /* headerIncluded */); err.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
@@ -736,7 +846,9 @@ func (e *endpoint) forwardPacketWithRoute(route *stack.Route, pkt *stack.PacketB
|
||||
// WriteHeaderIncludedPacket checks for the presence of the Don't Fragment bit
|
||||
// while sending the packet and returns this error iff fragmentation is
|
||||
// necessary and the bit is also set.
|
||||
_ = e.protocol.returnError(&icmpReasonFragmentationNeeded{}, pkt, false /* deliveredLocally */)
|
||||
_ = e.protocol.returnError(&icmpReasonFragmentationNeeded{
|
||||
mtu: forwardToEp.nic.MTU(),
|
||||
}, pkt, false /* deliveredLocally */)
|
||||
return &ip.ErrMessageTooLong{}
|
||||
case *tcpip.ErrNoBufferSpace:
|
||||
return &ip.ErrOutgoingDeviceNoBufferSpace{}
|
||||
@@ -788,6 +900,13 @@ func (e *endpoint) forwardUnicastPacket(pkt *stack.PacketBuffer) ip.ForwardingEr
|
||||
return nil
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckForward(pkt, nil /* route */, stack.IP) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// The packet originally arrived on e so provide its NIC as the input NIC.
|
||||
ep.handleValidatedPacket(h, pkt, e.nic.Name() /* inNICName */)
|
||||
return nil
|
||||
@@ -796,9 +915,7 @@ func (e *endpoint) forwardUnicastPacket(pkt *stack.PacketBuffer) ip.ForwardingEr
|
||||
r, err := stk.FindRoute(0, tcpip.Address{}, dstAddr, ProtocolNumber, false /* multicastLoop */)
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
// TODO(https://gvisor.dev/issues/8105): We should not observe ErrHostUnreachable from route
|
||||
// lookups.
|
||||
case *tcpip.ErrHostUnreachable, *tcpip.ErrNetworkUnreachable:
|
||||
case *tcpip.ErrNetworkUnreachable:
|
||||
// We return the original error rather than the result of returning
|
||||
// the ICMP packet because the original error is more relevant to
|
||||
// the caller.
|
||||
@@ -857,7 +974,8 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
||||
}
|
||||
}
|
||||
|
||||
if e.protocol.stack.HandleLocal() {
|
||||
stk := e.protocol.stack
|
||||
if stk.HandleLocal() {
|
||||
addressEndpoint := e.AcquireAssignedAddress(header.IPv4(pkt.NetworkHeader().Slice()).SourceAddress(), e.nic.Promiscuous(), stack.CanBePrimaryEndpoint, true /* readOnly */)
|
||||
if addressEndpoint != nil {
|
||||
// The source address is one of our own, so we never should have gotten
|
||||
@@ -868,13 +986,22 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
||||
}
|
||||
}
|
||||
|
||||
nicID := e.nic.ID()
|
||||
// Loopback traffic skips the prerouting chain.
|
||||
inNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
|
||||
if ok := e.protocol.stack.IPTables().CheckPrerouting(pkt, e, inNicName); !ok {
|
||||
inNicName := stk.FindNICNameFromID(nicID)
|
||||
pkt.InputNICID = nicID
|
||||
if ok := stk.IPTables().CheckPrerouting(pkt, e, inNicName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
stats.IPTablesPreroutingDropped.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckPrerouting(pkt, nil /* route */, stack.IP) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// CheckPrerouting can modify the backing storage of the packet, so refresh
|
||||
// the header.
|
||||
@@ -934,22 +1061,6 @@ func validateAddressesForForwarding(h header.IPv4) ip.ForwardingError {
|
||||
return &ip.ErrInitializingSourceAddress{}
|
||||
}
|
||||
|
||||
// As per RFC 3927 section 7,
|
||||
//
|
||||
// A router MUST NOT forward a packet with an IPv4 Link-Local source or
|
||||
// destination address, irrespective of the router's default route
|
||||
// configuration or routes obtained from dynamic routing protocols.
|
||||
//
|
||||
// A router which receives a packet with an IPv4 Link-Local source or
|
||||
// destination address MUST NOT forward the packet. This prevents
|
||||
// forwarding of packets back onto the network segment from which they
|
||||
// originated, or to any other segment.
|
||||
if header.IsV4LinkLocalUnicastAddress(srcAddr) {
|
||||
return &ip.ErrLinkLocalSourceAddress{}
|
||||
}
|
||||
if dstAddr := h.DestinationAddress(); header.IsV4LinkLocalUnicastAddress(dstAddr) || header.IsV4LinkLocalMulticastAddress(dstAddr) {
|
||||
return &ip.ErrLinkLocalDestinationAddress{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1169,6 +1280,7 @@ func (e *endpoint) handleValidatedPacket(h header.IPv4, pkt *stack.PacketBuffer,
|
||||
// If the packet is destined for this device, then it should be delivered
|
||||
// locally. Otherwise, if forwarding is enabled, it should be forwarded.
|
||||
if addressEndpoint := e.AcquireAssignedAddress(dstAddr, e.nic.Promiscuous(), stack.CanBePrimaryEndpoint, true /* readOnly */); addressEndpoint != nil {
|
||||
pkt.NetworkPacketInfo.LocalAddressTemporary = addressEndpoint.Temporary()
|
||||
subnet := addressEndpoint.AddressWithPrefix().Subnet()
|
||||
pkt.NetworkPacketInfo.LocalAddressBroadcast = subnet.IsBroadcast(dstAddr) || dstAddr == header.IPv4Broadcast
|
||||
e.deliverPacketLocally(h, pkt, inNICName)
|
||||
@@ -1223,14 +1335,22 @@ func (e *endpoint) handleForwardingError(err ip.ForwardingError) {
|
||||
|
||||
func (e *endpoint) deliverPacketLocally(h header.IPv4, pkt *stack.PacketBuffer, inNICName string) {
|
||||
stats := e.stats
|
||||
stk := e.protocol.stack
|
||||
// iptables filtering. All packets that reach here are intended for
|
||||
// this machine and will not be forwarded.
|
||||
if ok := e.protocol.stack.IPTables().CheckInput(pkt, inNICName); !ok {
|
||||
if ok := stk.IPTables().CheckInput(pkt, inNICName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
stats.ip.IPTablesInputDropped.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckInput(pkt, nil /* route */, stack.IP) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if h.More() || h.FragmentOffset() != 0 {
|
||||
if pkt.Data().Size()+len(pkt.TransportHeader().Slice()) == 0 {
|
||||
// Drop the packet as it's marked as a fragment but has
|
||||
@@ -1342,7 +1462,7 @@ func (e *endpoint) deliverPacketLocally(h header.IPv4, pkt *stack.PacketBuffer,
|
||||
}
|
||||
if p == header.IGMPProtocolNumber {
|
||||
e.mu.Lock()
|
||||
e.igmp.handleIGMP(pkt, hasRouterAlertOption) // +checklocksforce: e == e.igmp.ep.
|
||||
e.igmp.handleIGMP(pkt, hasRouterAlertOption)
|
||||
e.mu.Unlock()
|
||||
return
|
||||
}
|
||||
@@ -1392,7 +1512,6 @@ func (e *endpoint) AddAndAcquirePermanentAddress(addr tcpip.AddressWithPrefix, p
|
||||
// sendQueuedReports sends queued igmp reports.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.igmp.ep.mu=e.mu
|
||||
func (e *endpoint) sendQueuedReports() {
|
||||
e.igmp.sendQueuedReports()
|
||||
}
|
||||
@@ -1478,7 +1597,6 @@ func (e *endpoint) JoinGroup(addr tcpip.Address) tcpip.Error {
|
||||
// joinGroupLocked is like JoinGroup but with locking requirements.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.igmp.ep.mu=e.mu
|
||||
func (e *endpoint) joinGroupLocked(addr tcpip.Address) tcpip.Error {
|
||||
if !header.IsV4MulticastAddress(addr) {
|
||||
return &tcpip.ErrBadAddress{}
|
||||
@@ -1498,7 +1616,6 @@ func (e *endpoint) LeaveGroup(addr tcpip.Address) tcpip.Error {
|
||||
// leaveGroupLocked is like LeaveGroup but with locking requirements.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.igmp.ep.mu=e.mu
|
||||
func (e *endpoint) leaveGroupLocked(addr tcpip.Address) tcpip.Error {
|
||||
return e.igmp.leaveGroup(addr)
|
||||
}
|
||||
@@ -1507,7 +1624,7 @@ func (e *endpoint) leaveGroupLocked(addr tcpip.Address) tcpip.Error {
|
||||
func (e *endpoint) IsInGroup(addr tcpip.Address) bool {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
return e.igmp.isInGroup(addr) // +checklocksforce: e.mu==e.igmp.ep.mu.
|
||||
return e.igmp.isInGroup(addr)
|
||||
}
|
||||
|
||||
// Stats implements stack.NetworkEndpoint.
|
||||
@@ -1615,11 +1732,11 @@ func (p *protocol) Close() {
|
||||
func (*protocol) Wait() {}
|
||||
|
||||
func (p *protocol) validateUnicastSourceAndMulticastDestination(addresses stack.UnicastSourceAndMulticastDestination) tcpip.Error {
|
||||
if !p.isUnicastAddress(addresses.Source) || header.IsV4LinkLocalUnicastAddress(addresses.Source) {
|
||||
if !p.isUnicastAddress(addresses.Source) {
|
||||
return &tcpip.ErrBadAddress{}
|
||||
}
|
||||
|
||||
if !header.IsV4MulticastAddress(addresses.Destination) || header.IsV4LinkLocalMulticastAddress(addresses.Destination) {
|
||||
if !header.IsV4MulticastAddress(addresses.Destination) {
|
||||
return &tcpip.ErrBadAddress{}
|
||||
}
|
||||
|
||||
@@ -1873,6 +1990,8 @@ func (p *protocol) SendRejectionError(pkt *stack.PacketBuffer, rejectWith stack.
|
||||
return p.returnError(&icmpReasonHostProhibited{}, pkt, inputHook)
|
||||
case stack.RejectIPv4WithICMPAdminProhibited:
|
||||
return p.returnError(&icmpReasonAdministrativelyProhibited{}, pkt, inputHook)
|
||||
case stack.RejectIPv4WithTCPReset:
|
||||
return ip.RejectWithTCPReset(pkt, ProtocolNumber, p.stack, inputHook)
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled %[1]T = %[1]d", rejectWith))
|
||||
}
|
||||
|
||||
+72
-43
@@ -654,55 +654,28 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
|
||||
case header.ICMPv6EchoRequest:
|
||||
received.echoRequest.Increment()
|
||||
// As per RFC 4291 section 2.7, multicast addresses must not be used as
|
||||
// source addresses in IPv6 packets.
|
||||
localAddr := dstAddr
|
||||
if header.IsV6MulticastAddress(dstAddr) {
|
||||
localAddr = tcpip.Address{}
|
||||
}
|
||||
replyPayload := pkt.Data().ToBuffer()
|
||||
replyHeader := make([]byte, header.ICMPv6EchoMinimumSize)
|
||||
copy(replyHeader, h[:header.ICMPv6EchoMinimumSize])
|
||||
|
||||
r, err := e.protocol.stack.FindRoute(e.nic.ID(), localAddr, srcAddr, ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
// If we cannot find a route to the destination, silently drop the packet.
|
||||
return
|
||||
// It's possible that a raw socket or per-stack default handler expects
|
||||
// to receive this packet.
|
||||
defaultHandlerHandled := false
|
||||
if dispatcher, ok := e.dispatcher.(stack.TransportDispatcherWithDefaultHandlerResult); ok {
|
||||
_, defaultHandlerHandled = dispatcher.DeliverTransportPacketWithDefaultHandlerResult(header.ICMPv6ProtocolNumber, pkt)
|
||||
} else {
|
||||
e.dispatcher.DeliverTransportPacket(header.ICMPv6ProtocolNumber, pkt)
|
||||
}
|
||||
defer r.Release()
|
||||
pkt = nil
|
||||
|
||||
if !e.protocol.allowICMPReply(header.ICMPv6EchoReply) {
|
||||
sent.rateLimited.Increment()
|
||||
// Skip the built-in ICMP echo reply if the request was consumed by a
|
||||
// per-stack default handler.
|
||||
if defaultHandlerHandled {
|
||||
replyPayload.Release()
|
||||
return
|
||||
}
|
||||
|
||||
replyPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(r.MaxHeaderLength()) + header.ICMPv6EchoMinimumSize,
|
||||
Payload: pkt.Data().ToBuffer(),
|
||||
})
|
||||
defer replyPkt.DecRef()
|
||||
icmp := header.ICMPv6(replyPkt.TransportHeader().Push(header.ICMPv6EchoMinimumSize))
|
||||
replyPkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
|
||||
copy(icmp, h)
|
||||
icmp.SetType(header.ICMPv6EchoReply)
|
||||
replyData := replyPkt.Data()
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmp,
|
||||
Src: r.LocalAddress(),
|
||||
Dst: r.RemoteAddress(),
|
||||
PayloadCsum: replyData.Checksum(),
|
||||
PayloadLen: replyData.Size(),
|
||||
}))
|
||||
replyTClass, _ := iph.TOS()
|
||||
if err := r.WritePacket(stack.NetworkHeaderParams{
|
||||
Protocol: header.ICMPv6ProtocolNumber,
|
||||
TTL: r.DefaultTTL(),
|
||||
// Even though RFC 4443 does not mention anything about it, Linux uses the
|
||||
// TrafficClass of the received echo request when replying.
|
||||
// https://github.com/torvalds/linux/blob/0280e3c58f9/net/ipv6/icmp.c#L797
|
||||
TOS: replyTClass,
|
||||
}, replyPkt); err != nil {
|
||||
sent.dropped.Increment()
|
||||
return
|
||||
}
|
||||
sent.echoReply.Increment()
|
||||
e.sendICMPEchoReply(replyPayload, replyHeader, srcAddr, dstAddr, iph)
|
||||
|
||||
case header.ICMPv6EchoReply:
|
||||
received.echoReply.Increment()
|
||||
@@ -898,6 +871,62 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
}
|
||||
}
|
||||
|
||||
func (e *endpoint) sendICMPEchoReply(replyPayload buffer.Buffer, replyHeader []byte, srcAddr, dstAddr tcpip.Address, ipHdr header.IPv6) {
|
||||
sent := e.stats.icmp.packetsSent
|
||||
|
||||
// As per RFC 4291 section 2.7, multicast addresses must not be used as
|
||||
// source addresses in IPv6 packets.
|
||||
localAddr := dstAddr
|
||||
if header.IsV6MulticastAddress(dstAddr) {
|
||||
localAddr = tcpip.Address{}
|
||||
}
|
||||
|
||||
r, err := e.protocol.stack.FindRoute(e.nic.ID(), localAddr, srcAddr, ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
// If we cannot find a route to the destination, silently drop the packet.
|
||||
replyPayload.Release()
|
||||
return
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
if !e.protocol.allowICMPReply(header.ICMPv6EchoReply) {
|
||||
sent.rateLimited.Increment()
|
||||
replyPayload.Release()
|
||||
return
|
||||
}
|
||||
|
||||
replyPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(r.MaxHeaderLength()) + header.ICMPv6EchoMinimumSize,
|
||||
Payload: replyPayload,
|
||||
})
|
||||
defer replyPkt.DecRef()
|
||||
icmp := header.ICMPv6(replyPkt.TransportHeader().Push(header.ICMPv6EchoMinimumSize))
|
||||
replyPkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
|
||||
copy(icmp, replyHeader)
|
||||
icmp.SetType(header.ICMPv6EchoReply)
|
||||
replyData := replyPkt.Data()
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmp,
|
||||
Src: r.LocalAddress(),
|
||||
Dst: r.RemoteAddress(),
|
||||
PayloadCsum: replyData.Checksum(),
|
||||
PayloadLen: replyData.Size(),
|
||||
}))
|
||||
replyTClass, _ := ipHdr.TOS()
|
||||
if err := r.WritePacket(stack.NetworkHeaderParams{
|
||||
Protocol: header.ICMPv6ProtocolNumber,
|
||||
TTL: r.DefaultTTL(),
|
||||
// Even though RFC 4443 does not mention anything about it, Linux uses the
|
||||
// TrafficClass of the received echo request when replying.
|
||||
// https://github.com/torvalds/linux/blob/0280e3c58f9/net/ipv6/icmp.c#L797
|
||||
TOS: replyTClass,
|
||||
}, replyPkt); err != nil {
|
||||
sent.dropped.Increment()
|
||||
return
|
||||
}
|
||||
sent.echoReply.Increment()
|
||||
}
|
||||
|
||||
// LinkAddressProtocol implements stack.LinkAddressResolver.
|
||||
func (*endpoint) LinkAddressProtocol() tcpip.NetworkProtocolNumber {
|
||||
return header.IPv6ProtocolNumber
|
||||
|
||||
+205
-39
@@ -26,6 +26,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/checksum"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header/parse"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/internal/fragmentation"
|
||||
@@ -184,15 +185,21 @@ var _ NDPEndpoint = (*endpoint)(nil)
|
||||
type endpointMu struct {
|
||||
sync.RWMutex `state:"nosave"`
|
||||
|
||||
// +checklocks:RWMutex
|
||||
addressableEndpointState stack.AddressableEndpointState
|
||||
ndp ndpState
|
||||
mld mldState
|
||||
|
||||
// +checklocks:RWMutex
|
||||
ndp ndpState
|
||||
|
||||
// +checklocks:RWMutex
|
||||
mld mldState
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
type dadMu struct {
|
||||
sync.Mutex `state:"nosave"`
|
||||
|
||||
// +checklocks:Mutex
|
||||
dad ip.DAD
|
||||
}
|
||||
|
||||
@@ -201,6 +208,11 @@ type endpointDAD struct {
|
||||
mu dadMu
|
||||
}
|
||||
|
||||
// NDP and MLD retain their containing endpoint after NewEndpoint initializes
|
||||
// them, so their lock paths refer to the same mutex.
|
||||
//
|
||||
// +checklocksalias:mu.ndp.ep.mu.RWMutex=mu.RWMutex
|
||||
// +checklocksalias:mu.mld.ep.mu.RWMutex=mu.RWMutex
|
||||
// +stateify savable
|
||||
type endpoint struct {
|
||||
nic stack.NetworkInterface
|
||||
@@ -316,7 +328,7 @@ func (e *endpoint) HandleLinkResolutionFailure(pkt *stack.PacketBuffer) {
|
||||
|
||||
// onAddressAssignedLocked handles an address being assigned.
|
||||
//
|
||||
// Precondition: e.mu must be exclusively locked.
|
||||
// +checklocks:e.mu.RWMutex
|
||||
func (e *endpoint) onAddressAssignedLocked(addr tcpip.Address) {
|
||||
// As per RFC 2710 section 3,
|
||||
//
|
||||
@@ -586,7 +598,7 @@ func (e *endpoint) Enable() tcpip.Error {
|
||||
addressEndpoint.SetKind(stack.PermanentTentative)
|
||||
fallthrough
|
||||
case stack.PermanentTentative:
|
||||
err = e.mu.ndp.startDuplicateAddressDetection(addr, addressEndpoint)
|
||||
err = e.mu.ndp.startDuplicateAddressDetection(addr, addressEndpoint) // +checklocksforce: ForEachEndpoint calls back synchronously with e.mu held.
|
||||
return err == nil
|
||||
case stack.Temporary, stack.PermanentExpired:
|
||||
return true
|
||||
@@ -671,8 +683,9 @@ func (e *endpoint) Disable() {
|
||||
e.disableLocked()
|
||||
}
|
||||
|
||||
// +checklocks:e.mu.RWMutex
|
||||
func (e *endpoint) disableLocked() {
|
||||
if !e.Enabled() {
|
||||
if !e.isEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -696,7 +709,7 @@ func (e *endpoint) disableLocked() {
|
||||
switch kind := addressEndpoint.GetKind(); kind {
|
||||
case stack.Permanent, stack.PermanentTentative:
|
||||
if header.IsV6UnicastAddress(addrWithPrefix.Address) {
|
||||
e.mu.ndp.stopDuplicateAddressDetection(addrWithPrefix.Address, &stack.DADAborted{})
|
||||
e.mu.ndp.stopDuplicateAddressDetection(addrWithPrefix.Address, &stack.DADAborted{}) // +checklocksforce: ForEachEndpoint calls back synchronously with e.mu held.
|
||||
}
|
||||
case stack.Temporary, stack.PermanentExpired:
|
||||
default:
|
||||
@@ -728,6 +741,11 @@ func (e *endpoint) MTU() uint32 {
|
||||
return networkMTU
|
||||
}
|
||||
|
||||
// EndpointHeaderSize returns the size necessary for the IPv6 header.
|
||||
func (e *endpoint) EndpointHeaderSize() uint32 {
|
||||
return header.IPv6MinimumSize
|
||||
}
|
||||
|
||||
// MaxHeaderLength returns the maximum length needed by ipv6 headers (and
|
||||
// underlying protocols).
|
||||
func (e *endpoint) MaxHeaderLength() uint16 {
|
||||
@@ -810,6 +828,64 @@ func (e *endpoint) handleFragments(r *stack.Route, networkMTU uint32, pkt *stack
|
||||
}
|
||||
}
|
||||
|
||||
// recalculateChecksum recalculates the checksum of a TCP or UDP packet.
|
||||
func recalculateChecksum(pkt *stack.PacketBuffer, r *stack.Route) tcpip.Error {
|
||||
// RXChecksumValidated indicates that checksum verification may be
|
||||
// safely skipped.
|
||||
if pkt.RXChecksumValidated {
|
||||
return nil
|
||||
}
|
||||
// NeedsCsum is set if the checksum offload is enabled, so no need to
|
||||
// calculate the checksum.
|
||||
if pkt.GSOOptions.Type != stack.GSONone && pkt.GSOOptions.NeedsCsum {
|
||||
return nil
|
||||
}
|
||||
transportHeader := pkt.TransportHeader().Slice()
|
||||
payload := len(transportHeader) + pkt.Data().Size()
|
||||
if payload > math.MaxUint16 {
|
||||
return &tcpip.ErrMessageTooLong{}
|
||||
}
|
||||
payloadLength := uint16(payload)
|
||||
switch pkt.TransportProtocolNumber {
|
||||
case header.TCPProtocolNumber:
|
||||
if len(transportHeader) < header.TCPMinimumSize {
|
||||
return &tcpip.ErrMalformedHeader{}
|
||||
}
|
||||
tcp := header.TCP(transportHeader)
|
||||
xsum := r.PseudoHeaderChecksum(header.TCPProtocolNumber, payloadLength)
|
||||
xsum = checksum.Combine(xsum, pkt.Data().Checksum())
|
||||
tcp.SetChecksum(0)
|
||||
tcp.SetChecksum(^tcp.CalculateChecksum(xsum))
|
||||
case header.UDPProtocolNumber:
|
||||
if len(transportHeader) < header.UDPMinimumSize {
|
||||
return &tcpip.ErrMalformedHeader{}
|
||||
}
|
||||
udp := header.UDP(transportHeader)
|
||||
xsum := r.PseudoHeaderChecksum(header.UDPProtocolNumber, payloadLength)
|
||||
xsum = checksum.Combine(xsum, pkt.Data().Checksum())
|
||||
udp.SetChecksum(0)
|
||||
csum := ^udp.CalculateChecksum(xsum)
|
||||
// RFC 768: If the computed checksum is zero, it is transmitted as all ones.
|
||||
if csum == 0 {
|
||||
csum = 0xFFFF
|
||||
}
|
||||
udp.SetChecksum(csum)
|
||||
case header.ICMPv6ProtocolNumber:
|
||||
if len(transportHeader) < header.ICMPv6MinimumSize {
|
||||
return &tcpip.ErrMalformedHeader{}
|
||||
}
|
||||
icmp := header.ICMPv6(transportHeader)
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmp,
|
||||
Src: r.LocalAddress(),
|
||||
Dst: r.RemoteAddress(),
|
||||
PayloadCsum: pkt.Data().Checksum(),
|
||||
PayloadLen: pkt.Data().Size(),
|
||||
}))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePacket writes a packet to the given destination address and protocol.
|
||||
func (e *endpoint) WritePacket(r *stack.Route, params stack.NetworkHeaderParams, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
dstAddr := r.RemoteAddress()
|
||||
@@ -817,15 +893,22 @@ func (e *endpoint) WritePacket(r *stack.Route, params stack.NetworkHeaderParams,
|
||||
return err
|
||||
}
|
||||
|
||||
// iptables filtering. All packets that reach here are locally
|
||||
// generated.
|
||||
outNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
|
||||
if ok := e.protocol.stack.IPTables().CheckOutput(pkt, r, outNicName); !ok {
|
||||
stk := e.protocol.stack
|
||||
// iptables filtering. All packets that reach here are locally generated.
|
||||
outNicName := stk.FindNICNameFromID(e.nic.ID())
|
||||
if ok := stk.IPTables().CheckOutput(pkt, r, outNicName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
e.stats.ip.IPTablesOutputDropped.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckOutput(pkt, r, stack.IP6) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// If the packet is manipulated as per DNAT Output rules, handle packet
|
||||
// based on destination address and do not send the packet to link
|
||||
// layer.
|
||||
@@ -834,12 +917,46 @@ func (e *endpoint) WritePacket(r *stack.Route, params stack.NetworkHeaderParams,
|
||||
// removing this check short circuits broadcasts before they are sent out to
|
||||
// other hosts.
|
||||
if netHeader := header.IPv6(pkt.NetworkHeader().Slice()); dstAddr != netHeader.DestinationAddress() {
|
||||
if ep := e.protocol.findEndpointWithAddress(netHeader.DestinationAddress()); ep != nil {
|
||||
newDstAddr := netHeader.DestinationAddress()
|
||||
if ep := e.protocol.findEndpointWithAddress(newDstAddr); ep != nil {
|
||||
// Since we rewrote the packet but it is being routed back to us, we
|
||||
// can safely assume the checksum is valid.
|
||||
ep.handleLocalPacket(pkt, true /* canSkipRXChecksum */)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Similar to the `ip_route_me_harder` in the kernel,
|
||||
// we need to find a new route for the packet.
|
||||
// Implementation is similar to the func forwardUnicastPacket.
|
||||
newRoute, err := stk.FindRoute(0 /* nic id */, netHeader.SourceAddress(), newDstAddr, ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
e.stats.ip.OutgoingPacketErrors.Increment()
|
||||
return err // Drop the packet
|
||||
}
|
||||
// Release the new route on exit.
|
||||
defer newRoute.Release()
|
||||
|
||||
// Check if we need to recalculate the checksum.
|
||||
// If the original route did not require a checksum but the new one does,
|
||||
// we must calculate the full checksum; otherwise, NAT should have already
|
||||
// done it.
|
||||
if !r.RequiresTXTransportChecksum() && newRoute.RequiresTXTransportChecksum() {
|
||||
if err := recalculateChecksum(pkt, newRoute); err != nil {
|
||||
e.stats.ip.OutgoingPacketErrors.Increment()
|
||||
return err // Drop the packet
|
||||
}
|
||||
}
|
||||
|
||||
// Update the route to the new route.
|
||||
r = newRoute
|
||||
|
||||
// Use the new endpoint to write the packet.
|
||||
forwardToEp, ok := e.protocol.getEndpointForNIC(r.NICID())
|
||||
if !ok {
|
||||
e.stats.ip.OutgoingPacketErrors.Increment()
|
||||
return &tcpip.ErrUnknownNICID{}
|
||||
}
|
||||
return forwardToEp.writePacket(r, pkt, params.Protocol, true /* headerIncluded */)
|
||||
}
|
||||
|
||||
return e.writePacket(r, pkt, params.Protocol, false /* headerIncluded */)
|
||||
@@ -856,15 +973,23 @@ func (e *endpoint) writePacket(r *stack.Route, pkt *stack.PacketBuffer, protocol
|
||||
return nil
|
||||
}
|
||||
|
||||
stk := e.protocol.stack
|
||||
// Postrouting NAT can only change the source address, and does not alter the
|
||||
// route or outgoing interface of the packet.
|
||||
outNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
|
||||
if ok := e.protocol.stack.IPTables().CheckPostrouting(pkt, r, e, outNicName); !ok {
|
||||
outNicName := stk.FindNICNameFromID(e.nic.ID())
|
||||
if ok := stk.IPTables().CheckPostrouting(pkt, r, e, outNicName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
e.stats.ip.IPTablesPostroutingDropped.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckPostrouting(pkt, r, stack.IP6) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
stats := e.stats.ip
|
||||
networkMTU, err := calculateNetworkMTU(e.nic.MTU(), uint32(len(pkt.NetworkHeader().Slice())))
|
||||
if err != nil {
|
||||
@@ -1005,6 +1130,14 @@ func (e *endpoint) forwardUnicastPacket(pkt *stack.PacketBuffer) ip.ForwardingEr
|
||||
return nil
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
|
||||
if !nft.CheckForward(pkt, nil, stack.IP6) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// The packet originally arrived on e so provide its NIC as the input NIC.
|
||||
ep.handleValidatedPacket(h, pkt, e.nic.Name() /* inNICName */)
|
||||
return nil
|
||||
@@ -1018,9 +1151,7 @@ func (e *endpoint) forwardUnicastPacket(pkt *stack.PacketBuffer) ip.ForwardingEr
|
||||
r, err := stk.FindRoute(0, tcpip.Address{}, dstAddr, ProtocolNumber, false /* multicastLoop */)
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
// TODO(https://gvisor.dev/issues/8105): We should not observe ErrHostUnreachable from route
|
||||
// lookups.
|
||||
case *tcpip.ErrHostUnreachable, *tcpip.ErrNetworkUnreachable:
|
||||
case *tcpip.ErrNetworkUnreachable:
|
||||
// We return the original error rather than the result of returning the
|
||||
// ICMP packet because the original error is more relevant to the caller.
|
||||
_ = e.protocol.returnError(&icmpReasonNetUnreachable{}, pkt, false /* deliveredLocally */)
|
||||
@@ -1048,6 +1179,13 @@ func (e *endpoint) forwardPacketWithRoute(route *stack.Route, pkt *stack.PacketB
|
||||
return nil
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckForward(pkt, route, stack.IP6) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
hopLimit := h.HopLimit()
|
||||
|
||||
// We need to do a deep copy of the IP packet because
|
||||
@@ -1063,6 +1201,10 @@ func (e *endpoint) forwardPacketWithRoute(route *stack.Route, pkt *stack.PacketB
|
||||
// each node that forwards the packet.
|
||||
newHdr.SetHopLimit(hopLimit - 1)
|
||||
|
||||
if route.RequiresTXTransportChecksum() {
|
||||
newPkt.CalculateTransportChecksum()
|
||||
}
|
||||
|
||||
forwardToEp, ok := e.protocol.getEndpointForNIC(route.NICID())
|
||||
if !ok {
|
||||
// The interface was removed after we obtained the route.
|
||||
@@ -1123,7 +1265,8 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
||||
}
|
||||
}
|
||||
|
||||
if e.protocol.stack.HandleLocal() {
|
||||
stk := e.protocol.stack
|
||||
if stk.HandleLocal() {
|
||||
addressEndpoint := e.AcquireAssignedAddress(header.IPv6(pkt.NetworkHeader().Slice()).SourceAddress(), e.nic.Promiscuous(), stack.CanBePrimaryEndpoint, true /* readOnly */)
|
||||
if addressEndpoint != nil {
|
||||
// The source address is one of our own, so we never should have gotten
|
||||
@@ -1134,13 +1277,22 @@ func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
||||
}
|
||||
}
|
||||
|
||||
nicID := e.nic.ID()
|
||||
// Loopback traffic skips the prerouting chain.
|
||||
inNicName := e.protocol.stack.FindNICNameFromID(e.nic.ID())
|
||||
if ok := e.protocol.stack.IPTables().CheckPrerouting(pkt, e, inNicName); !ok {
|
||||
inNicName := stk.FindNICNameFromID(nicID)
|
||||
pkt.InputNICID = nicID
|
||||
if ok := stk.IPTables().CheckPrerouting(pkt, e, inNicName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
stats.IPTablesPreroutingDropped.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckPrerouting(pkt, nil, stack.IP6) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckPrerouting can modify the backing storage of the packet, so refresh
|
||||
@@ -1386,15 +1538,20 @@ func (e *endpoint) handleValidatedPacket(h header.IPv6, pkt *stack.PacketBuffer,
|
||||
|
||||
func (e *endpoint) deliverPacketLocally(h header.IPv6, pkt *stack.PacketBuffer, inNICName string) {
|
||||
stats := e.stats.ip
|
||||
|
||||
// iptables filtering. All packets that reach here are intended for
|
||||
// this machine and need not be forwarded.
|
||||
if ok := e.protocol.stack.IPTables().CheckInput(pkt, inNICName); !ok {
|
||||
stk := e.protocol.stack
|
||||
if ok := stk.IPTables().CheckInput(pkt, inNICName); !ok {
|
||||
// iptables is telling us to drop the packet.
|
||||
stats.IPTablesInputDropped.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
if nft := stk.NFTables(); nft != nil && stk.IsNFTablesConfigured() {
|
||||
if !nft.CheckInput(pkt, nil, stack.IP6) {
|
||||
// nftables is telling us to drop the packet.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Any returned error is only useful for terminating execution early, but
|
||||
// we have nothing left to do, so we can drop it.
|
||||
_ = e.processExtensionHeaders(h, pkt, false /* forwarding */)
|
||||
@@ -1495,12 +1652,13 @@ func (e *endpoint) processExtensionHeaders(h header.IPv6, pkt *stack.PacketBuffe
|
||||
buf.Merge(&dataBuf)
|
||||
it := header.MakeIPv6PayloadIterator(header.IPv6ExtensionHeaderIdentifier(h.NextHeader()), buf)
|
||||
|
||||
// Add a reference to pkt because fragment header processing can replace this
|
||||
// packet with a new one that has an extra reference. Adding a reference here
|
||||
// keeps the two in parity so they can both be DecRef'd the same way.
|
||||
pkt.IncRef()
|
||||
// Clone the pkt here because fragment header processing can replace this
|
||||
// packet with a new one that has an extra reference. Cloning here
|
||||
// keeps it so this method owns the packet either way so the packet can always
|
||||
// be DecRef'd after processing is done.
|
||||
processingPkt := pkt.Clone()
|
||||
defer func() {
|
||||
pkt.DecRef()
|
||||
processingPkt.DecRef()
|
||||
it.Release()
|
||||
}()
|
||||
|
||||
@@ -1510,7 +1668,7 @@ func (e *endpoint) processExtensionHeaders(h header.IPv6, pkt *stack.PacketBuffe
|
||||
)
|
||||
for {
|
||||
h := header.IPv6(pkt.NetworkHeader().Slice())
|
||||
if done, err := e.processExtensionHeader(&it, &pkt, h, &routerAlert, &hasFragmentHeader, forwarding); err != nil || done {
|
||||
if done, err := e.processExtensionHeader(&it, &processingPkt, h, &routerAlert, &hasFragmentHeader, forwarding); err != nil || done {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1943,7 +2101,7 @@ func (e *endpoint) AddAndAcquirePermanentAddress(addr tcpip.AddressWithPrefix, p
|
||||
// addAndAcquirePermanentAddressLocked also joins the passed address's
|
||||
// solicited-node multicast group and start duplicate address detection.
|
||||
//
|
||||
// Precondition: e.mu must be write locked.
|
||||
// +checklocks:e.mu.RWMutex
|
||||
func (e *endpoint) addAndAcquirePermanentAddressLocked(addr tcpip.AddressWithPrefix, properties stack.AddressProperties) (stack.AddressEndpoint, tcpip.Error) {
|
||||
addressEndpoint, err := e.mu.addressableEndpointState.AddAndAcquireAddress(addr, properties, stack.PermanentTentative)
|
||||
if err != nil {
|
||||
@@ -1986,7 +2144,7 @@ func (e *endpoint) RemovePermanentAddress(addr tcpip.Address) tcpip.Error {
|
||||
// removePermanentEndpointLocked is like removePermanentAddressLocked except
|
||||
// it works with a stack.AddressEndpoint.
|
||||
//
|
||||
// Precondition: e.mu must be write locked.
|
||||
// +checklocks:e.mu.RWMutex
|
||||
func (e *endpoint) removePermanentEndpointLocked(addressEndpoint stack.AddressEndpoint, allowSLAACInvalidation bool, reason stack.AddressRemovalReason, dadResult stack.DADResult) tcpip.Error {
|
||||
addr := addressEndpoint.AddressWithPrefix()
|
||||
// If we are removing an address generated via SLAAC, cleanup
|
||||
@@ -2005,7 +2163,7 @@ func (e *endpoint) removePermanentEndpointLocked(addressEndpoint stack.AddressEn
|
||||
// removePermanentEndpointInnerLocked is like removePermanentEndpointLocked
|
||||
// except it does not cleanup SLAAC address state.
|
||||
//
|
||||
// Precondition: e.mu must be write locked.
|
||||
// +checklocks:e.mu.RWMutex
|
||||
func (e *endpoint) removePermanentEndpointInnerLocked(addressEndpoint stack.AddressEndpoint, reason stack.AddressRemovalReason, dadResult stack.DADResult) tcpip.Error {
|
||||
addr := addressEndpoint.AddressWithPrefix()
|
||||
e.mu.ndp.stopDuplicateAddressDetection(addr.Address, dadResult)
|
||||
@@ -2026,7 +2184,7 @@ func (e *endpoint) removePermanentEndpointInnerLocked(addressEndpoint stack.Addr
|
||||
// hasPermanentAddressLocked returns true if the endpoint has a permanent
|
||||
// address equal to the passed address.
|
||||
//
|
||||
// Precondition: e.mu must be read or write locked.
|
||||
// +checklocksread:e.mu.RWMutex
|
||||
func (e *endpoint) hasPermanentAddressRLocked(addr tcpip.Address) bool {
|
||||
addressEndpoint := e.getAddressRLocked(addr)
|
||||
if addressEndpoint == nil {
|
||||
@@ -2037,7 +2195,7 @@ func (e *endpoint) hasPermanentAddressRLocked(addr tcpip.Address) bool {
|
||||
|
||||
// getAddressRLocked returns the endpoint for the passed address.
|
||||
//
|
||||
// Precondition: e.mu must be read or write locked.
|
||||
// +checklocksread:e.mu.RWMutex
|
||||
func (e *endpoint) getAddressRLocked(localAddr tcpip.Address) stack.AddressEndpoint {
|
||||
return e.mu.addressableEndpointState.GetAddress(localAddr)
|
||||
}
|
||||
@@ -2073,7 +2231,7 @@ func (e *endpoint) AcquireAssignedAddress(localAddr tcpip.Address, allowTemp boo
|
||||
// acquireAddressOrCreateTempLocked is like AcquireAssignedAddress but with
|
||||
// locking requirements.
|
||||
//
|
||||
// Precondition: e.mu must be write locked.
|
||||
// +checklocksread:e.mu.RWMutex
|
||||
func (e *endpoint) acquireAddressOrCreateTempLocked(localAddr tcpip.Address, allowTemp bool, tempPEB stack.PrimaryEndpointBehavior, readOnly bool) stack.AddressEndpoint {
|
||||
return e.mu.addressableEndpointState.AcquireAssignedAddress(localAddr, allowTemp, tempPEB, readOnly)
|
||||
}
|
||||
@@ -2090,7 +2248,7 @@ func (e *endpoint) AcquireOutgoingPrimaryAddress(remoteAddr, srcHint tcpip.Addre
|
||||
//
|
||||
// See stack.PrimaryEndpointBehavior for more details about the primary list.
|
||||
//
|
||||
// Precondition: e.mu must be read locked.
|
||||
// +checklocksread:e.mu.RWMutex
|
||||
func (e *endpoint) getLinkLocalAddressRLocked() tcpip.Address {
|
||||
var linkLocalAddr tcpip.Address
|
||||
e.mu.addressableEndpointState.ForEachPrimaryEndpoint(func(addressEndpoint stack.AddressEndpoint) bool {
|
||||
@@ -2108,7 +2266,7 @@ func (e *endpoint) getLinkLocalAddressRLocked() tcpip.Address {
|
||||
// acquireOutgoingPrimaryAddressRLocked is like AcquireOutgoingPrimaryAddress
|
||||
// but with locking requirements.
|
||||
//
|
||||
// Precondition: e.mu must be read locked.
|
||||
// +checklocksread:e.mu.RWMutex
|
||||
func (e *endpoint) acquireOutgoingPrimaryAddressRLocked(remoteAddr, srcHint tcpip.Address, allowExpired bool) stack.AddressEndpoint {
|
||||
// TODO(b/309216156): Support IPv6 hints.
|
||||
|
||||
@@ -2254,7 +2412,7 @@ func (e *endpoint) JoinGroup(addr tcpip.Address) tcpip.Error {
|
||||
|
||||
// joinGroupLocked is like JoinGroup but with locking requirements.
|
||||
//
|
||||
// Precondition: e.mu must be locked.
|
||||
// +checklocks:e.mu.RWMutex
|
||||
func (e *endpoint) joinGroupLocked(addr tcpip.Address) tcpip.Error {
|
||||
if !header.IsV6MulticastAddress(addr) {
|
||||
return &tcpip.ErrBadAddress{}
|
||||
@@ -2273,7 +2431,7 @@ func (e *endpoint) LeaveGroup(addr tcpip.Address) tcpip.Error {
|
||||
|
||||
// leaveGroupLocked is like LeaveGroup but with locking requirements.
|
||||
//
|
||||
// Precondition: e.mu must be locked.
|
||||
// +checklocks:e.mu.RWMutex
|
||||
func (e *endpoint) leaveGroupLocked(addr tcpip.Address) tcpip.Error {
|
||||
return e.mu.mld.leaveGroup(addr)
|
||||
}
|
||||
@@ -2301,14 +2459,20 @@ type protocolMu struct {
|
||||
|
||||
// eps is keyed by NICID to allow protocol methods to retrieve an endpoint
|
||||
// when handling a packet, by looking at which NIC handled the packet.
|
||||
//
|
||||
// +checklocks:RWMutex
|
||||
eps map[tcpip.NICID]*endpoint
|
||||
|
||||
// ICMP types for which the stack's global rate limiting must apply.
|
||||
//
|
||||
// +checklocks:RWMutex
|
||||
icmpRateLimitedTypes map[header.ICMPv6Type]struct{}
|
||||
|
||||
// multicastForwardingDisp is the multicast forwarding event dispatcher that
|
||||
// an integrator can provide to receive multicast forwarding events. Note
|
||||
// that multicast packets will only be forwarded if this is non-nil.
|
||||
//
|
||||
// +checklocks:RWMutex
|
||||
multicastForwardingDisp stack.MulticastForwardingEventDispatcher
|
||||
}
|
||||
|
||||
@@ -2689,6 +2853,8 @@ func (p *protocol) SendRejectionError(pkt *stack.PacketBuffer, rejectWith stack.
|
||||
return p.returnError(&icmpReasonPortUnreachable{}, pkt, inputHook)
|
||||
case stack.RejectIPv6WithICMPAdminProhibited:
|
||||
return p.returnError(&icmpReasonAdministrativelyProhibited{}, pkt, inputHook)
|
||||
case stack.RejectIPv6WithTCPReset:
|
||||
return ip.RejectWithTCPReset(pkt, ProtocolNumber, p.stack, inputHook)
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled %[1]T = %[1]d", rejectWith))
|
||||
}
|
||||
|
||||
@@ -525,7 +525,6 @@ func (t *timer) StateTypeName() string {
|
||||
func (t *timer) StateFields() []string {
|
||||
return []string{
|
||||
"done",
|
||||
"timer",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,7 +534,6 @@ func (t *timer) beforeSave() {}
|
||||
func (t *timer) StateSave(stateSinkObject state.Sink) {
|
||||
t.beforeSave()
|
||||
stateSinkObject.Save(0, &t.done)
|
||||
stateSinkObject.Save(1, &t.timer)
|
||||
}
|
||||
|
||||
func (t *timer) afterLoad(context.Context) {}
|
||||
@@ -543,7 +541,6 @@ func (t *timer) afterLoad(context.Context) {}
|
||||
// +checklocksignore
|
||||
func (t *timer) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &t.done)
|
||||
stateSourceObject.Load(1, &t.timer)
|
||||
}
|
||||
|
||||
func (o *offLinkRoute) StateTypeName() string {
|
||||
|
||||
+17
-14
@@ -94,14 +94,14 @@ func (mld *mldState) Enabled() bool {
|
||||
|
||||
// SendReport implements ip.MulticastGroupProtocol.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be read locked.
|
||||
// +checklocksread:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) SendReport(groupAddress tcpip.Address) (bool, tcpip.Error) {
|
||||
return mld.writePacket(groupAddress, groupAddress, header.ICMPv6MulticastListenerReport)
|
||||
}
|
||||
|
||||
// SendLeave implements ip.MulticastGroupProtocol.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be read locked.
|
||||
// +checklocksread:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) SendLeave(groupAddress tcpip.Address) tcpip.Error {
|
||||
_, err := mld.writePacket(header.IPv6AllRoutersLinkLocalMulticastAddress, groupAddress, header.ICMPv6MulticastListenerDone)
|
||||
return err
|
||||
@@ -160,6 +160,8 @@ func (b *mldv2ReportBuilder) AddRecord(genericRecordType ip.MulticastGroupProtoc
|
||||
}
|
||||
|
||||
// Send implements ip.MulticastGroupProtocolV2ReportBuilder.
|
||||
//
|
||||
// +checklocksread:b.mld.ep.mu.RWMutex
|
||||
func (b *mldv2ReportBuilder) Send() (sent bool, err tcpip.Error) {
|
||||
if len(b.records) == 0 {
|
||||
return false, err
|
||||
@@ -243,14 +245,14 @@ func (mld *mldState) init(ep *endpoint) {
|
||||
|
||||
// handleMulticastListenerQuery handles a query message.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
// +checklocks:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) handleMulticastListenerQuery(mldHdr header.MLD) {
|
||||
mld.genericMulticastProtocol.HandleQueryLocked(mldHdr.MulticastAddress(), mldHdr.MaximumResponseDelay())
|
||||
}
|
||||
|
||||
// handleMulticastListenerQueryV2 handles a V2 query message.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
// +checklocks:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) handleMulticastListenerQueryV2(mldHdr header.MLDv2Query) {
|
||||
sources, ok := mldHdr.Sources()
|
||||
if !ok {
|
||||
@@ -268,7 +270,7 @@ func (mld *mldState) handleMulticastListenerQueryV2(mldHdr header.MLDv2Query) {
|
||||
|
||||
// handleMulticastListenerReport handles a report message.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
// +checklocks:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) handleMulticastListenerReport(mldHdr header.MLD) {
|
||||
mld.genericMulticastProtocol.HandleReportLocked(mldHdr.MulticastAddress())
|
||||
}
|
||||
@@ -278,14 +280,14 @@ func (mld *mldState) handleMulticastListenerReport(mldHdr header.MLD) {
|
||||
//
|
||||
// If the group is already joined, returns *tcpip.ErrDuplicateAddress.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
// +checklocks:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) joinGroup(groupAddress tcpip.Address) {
|
||||
mld.genericMulticastProtocol.JoinGroupLocked(groupAddress)
|
||||
}
|
||||
|
||||
// isInGroup returns true if the specified group has been joined locally.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be read locked.
|
||||
// +checklocksread:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) isInGroup(groupAddress tcpip.Address) bool {
|
||||
return mld.genericMulticastProtocol.IsLocallyJoinedRLocked(groupAddress)
|
||||
}
|
||||
@@ -294,7 +296,7 @@ func (mld *mldState) isInGroup(groupAddress tcpip.Address) bool {
|
||||
// delay timers associated with that group, and sends the Done message, if
|
||||
// required.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
// +checklocks:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) leaveGroup(groupAddress tcpip.Address) tcpip.Error {
|
||||
// LeaveGroup returns false only if the group was not joined.
|
||||
if mld.genericMulticastProtocol.LeaveGroupLocked(groupAddress) {
|
||||
@@ -307,7 +309,7 @@ func (mld *mldState) leaveGroup(groupAddress tcpip.Address) tcpip.Error {
|
||||
// softLeaveAll leaves all groups from the perspective of MLD, but remains
|
||||
// joined locally.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
// +checklocks:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) softLeaveAll() {
|
||||
mld.genericMulticastProtocol.MakeAllNonMemberLocked()
|
||||
}
|
||||
@@ -315,21 +317,21 @@ func (mld *mldState) softLeaveAll() {
|
||||
// initializeAll attempts to initialize the MLD state for each group that has
|
||||
// been joined locally.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
// +checklocks:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) initializeAll() {
|
||||
mld.genericMulticastProtocol.InitializeGroupsLocked()
|
||||
}
|
||||
|
||||
// sendQueuedReports attempts to send any reports that are queued for sending.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
// +checklocks:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) sendQueuedReports() {
|
||||
mld.genericMulticastProtocol.SendQueuedReportsLocked()
|
||||
}
|
||||
|
||||
// setVersion sets the MLD version.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
// +checklocks:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) setVersion(v MLDVersion) MLDVersion {
|
||||
var prev bool
|
||||
switch v {
|
||||
@@ -353,14 +355,14 @@ func toMLDVersion(v1Generic bool) MLDVersion {
|
||||
|
||||
// getVersion returns the MLD version.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be read locked.
|
||||
// +checklocksread:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) getVersion() MLDVersion {
|
||||
return toMLDVersion(mld.genericMulticastProtocol.GetV1ModeLocked())
|
||||
}
|
||||
|
||||
// writePacket assembles and sends an MLD packet.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be read locked.
|
||||
// +checklocksread:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) writePacket(destAddress, groupAddress tcpip.Address, mldType header.ICMPv6Type) (bool, tcpip.Error) {
|
||||
sentStats := mld.ep.stats.icmp.packetsSent
|
||||
var mldStat tcpip.MultiCounterStat
|
||||
@@ -392,6 +394,7 @@ func (mld *mldState) writePacket(destAddress, groupAddress tcpip.Address, mldTyp
|
||||
)
|
||||
}
|
||||
|
||||
// +checklocksread:mld.ep.mu.RWMutex
|
||||
func (mld *mldState) writePacketInner(buf *buffer.View, mldType header.ICMPv6Type, reportStat tcpip.MultiCounterStat, extensionHeaders header.IPv6ExtHdrSerializer, destAddress tcpip.Address) (bool, tcpip.Error) {
|
||||
icmp := header.ICMPv6(buf.AsSlice())
|
||||
icmp.SetType(mldType)
|
||||
|
||||
+38
-36
@@ -471,7 +471,7 @@ type timer struct {
|
||||
// done indicates to the timer that the timer was stopped.
|
||||
done *bool
|
||||
|
||||
timer tcpip.Timer
|
||||
timer tcpip.Timer `state:"nosave"`
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
@@ -640,7 +640,7 @@ type slaacPrefixState struct {
|
||||
// This function must only be called by IPv6 addresses that are currently
|
||||
// tentative.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) startDuplicateAddressDetection(addr tcpip.Address, addressEndpoint stack.AddressEndpoint) tcpip.Error {
|
||||
// addr must be a valid unicast IPv6 address.
|
||||
if !header.IsV6UnicastAddress(addr) {
|
||||
@@ -681,9 +681,9 @@ func (ndp *ndpState) startDuplicateAddressDetection(addr tcpip.Address, addressE
|
||||
if addressEndpoint.ConfigType() == stack.AddressConfigSlaac && !addressEndpoint.Temporary() {
|
||||
// Reset the generation attempts counter as we are starting the
|
||||
// generation of a new address for the SLAAC prefix.
|
||||
ndp.regenerateTempSLAACAddr(addressEndpoint.AddressWithPrefix().Subnet(), true /* resetGenAttempts */)
|
||||
ndp.regenerateTempSLAACAddr(addressEndpoint.AddressWithPrefix().Subnet(), true /* resetGenAttempts */) // +checklocksforce: DAD calls its handlers with ndp.ep.mu held.
|
||||
}
|
||||
ndp.ep.onAddressAssignedLocked(addr)
|
||||
ndp.ep.onAddressAssignedLocked(addr) // +checklocksforce: DAD calls its handlers with ndp.ep.mu held.
|
||||
}
|
||||
})
|
||||
|
||||
@@ -713,7 +713,7 @@ func (ndp *ndpState) startDuplicateAddressDetection(addr tcpip.Address, addressE
|
||||
// (implying another node is attempting to use addr)). It is up to the caller
|
||||
// of this function to handle such a scenario.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) stopDuplicateAddressDetection(addr tcpip.Address, reason stack.DADResult) {
|
||||
ndp.dad.StopLocked(addr, reason)
|
||||
}
|
||||
@@ -721,7 +721,7 @@ func (ndp *ndpState) stopDuplicateAddressDetection(addr tcpip.Address, reason st
|
||||
// handleRA handles a Router Advertisement message that arrived on the NIC
|
||||
// this ndp is for. Does nothing if the NIC is configured to not handle RAs.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) handleRA(ip tcpip.Address, ra header.NDPRouterAdvert) {
|
||||
// Is the IPv6 endpoint configured to handle RAs at all?
|
||||
//
|
||||
@@ -859,7 +859,7 @@ func (ndp *ndpState) handleRA(ip tcpip.Address, ra header.NDPRouterAdvert) {
|
||||
|
||||
// invalidateOffLinkRoute invalidates a discovered off-link route.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) invalidateOffLinkRoute(route offLinkRoute) {
|
||||
state, ok := ndp.offLinkRoutes[route]
|
||||
if !ok {
|
||||
@@ -877,7 +877,7 @@ func (ndp *ndpState) invalidateOffLinkRoute(route offLinkRoute) {
|
||||
|
||||
// handleOffLinkRouteDiscovery handles the discovery of an off-link route.
|
||||
//
|
||||
// Precondition: ndp.ep.mu must be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) handleOffLinkRouteDiscovery(route offLinkRoute, lifetime time.Duration, prf header.NDPRoutePreference) {
|
||||
ndpDisp := ndp.ep.protocol.options.NDPDisp
|
||||
if ndpDisp == nil {
|
||||
@@ -898,7 +898,7 @@ func (ndp *ndpState) handleOffLinkRouteDiscovery(route offLinkRoute, lifetime ti
|
||||
state := offLinkRouteState{
|
||||
prf: prf,
|
||||
invalidationJob: tcpip.NewJob(ndp.ep.protocol.stack.Clock(), &ndp.ep.mu, func() {
|
||||
ndp.invalidateOffLinkRoute(route)
|
||||
ndp.invalidateOffLinkRoute(route) // +checklocksforce: NewJob calls back with ndp.ep.mu held.
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -933,7 +933,7 @@ func (ndp *ndpState) handleOffLinkRouteDiscovery(route offLinkRoute, lifetime ti
|
||||
//
|
||||
// The prefix identified by prefix MUST NOT already be known.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) rememberOnLinkPrefix(prefix tcpip.Subnet, l time.Duration) {
|
||||
ndpDisp := ndp.ep.protocol.options.NDPDisp
|
||||
if ndpDisp == nil {
|
||||
@@ -945,7 +945,7 @@ func (ndp *ndpState) rememberOnLinkPrefix(prefix tcpip.Subnet, l time.Duration)
|
||||
|
||||
state := onLinkPrefixState{
|
||||
invalidationJob: tcpip.NewJob(ndp.ep.protocol.stack.Clock(), &ndp.ep.mu, func() {
|
||||
ndp.invalidateOnLinkPrefix(prefix)
|
||||
ndp.invalidateOnLinkPrefix(prefix) // +checklocksforce: NewJob calls back with ndp.ep.mu held.
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -958,7 +958,7 @@ func (ndp *ndpState) rememberOnLinkPrefix(prefix tcpip.Subnet, l time.Duration)
|
||||
|
||||
// invalidateOnLinkPrefix invalidates a discovered on-link prefix.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) invalidateOnLinkPrefix(prefix tcpip.Subnet) {
|
||||
s, ok := ndp.onLinkPrefixes[prefix]
|
||||
|
||||
@@ -983,7 +983,7 @@ func (ndp *ndpState) invalidateOnLinkPrefix(prefix tcpip.Subnet) {
|
||||
// handleOnLinkPrefixInformation assumes that the prefix this pi is for is
|
||||
// not the link-local prefix and the on-link flag is set.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) handleOnLinkPrefixInformation(pi header.NDPPrefixInformation) {
|
||||
prefix := pi.Subnet()
|
||||
prefixState, ok := ndp.onLinkPrefixes[prefix]
|
||||
@@ -1036,7 +1036,7 @@ func (ndp *ndpState) handleOnLinkPrefixInformation(pi header.NDPPrefixInformatio
|
||||
// handleAutonomousPrefixInformation assumes that the prefix this pi is for is
|
||||
// not the link-local prefix and the autonomous flag is set.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) handleAutonomousPrefixInformation(pi header.NDPPrefixInformation) {
|
||||
vl := pi.ValidLifetime()
|
||||
pl := pi.PreferredLifetime()
|
||||
@@ -1077,7 +1077,7 @@ func (ndp *ndpState) handleAutonomousPrefixInformation(pi header.NDPPrefixInform
|
||||
//
|
||||
// pl is the new preferred lifetime. vl is the new valid lifetime.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
|
||||
// If we do not already have an address for this prefix and the valid
|
||||
// lifetime is 0, no need to do anything further, as per RFC 4862
|
||||
@@ -1100,7 +1100,7 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
|
||||
panic(fmt.Sprintf("ndp: must have a slaacPrefixes entry for the deprecated SLAAC prefix %s", prefix))
|
||||
}
|
||||
|
||||
ndp.deprecateSLAACAddress(state.stableAddr.addressEndpoint)
|
||||
ndp.deprecateSLAACAddress(state.stableAddr.addressEndpoint) // +checklocksforce: NewJob calls back with ndp.ep.mu held.
|
||||
}),
|
||||
invalidationJob: tcpip.NewJob(ndp.ep.protocol.stack.Clock(), &ndp.ep.mu, func() {
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
@@ -1108,7 +1108,7 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
|
||||
panic(fmt.Sprintf("ndp: must have a slaacPrefixes entry for the invalidated SLAAC prefix %s", prefix))
|
||||
}
|
||||
|
||||
ndp.invalidateSLAACPrefix(prefix, state)
|
||||
ndp.invalidateSLAACPrefix(prefix, state) // +checklocksforce: NewJob calls back with ndp.ep.mu held.
|
||||
}),
|
||||
tempAddrs: make(map[tcpip.Address]tempSLAACAddrState),
|
||||
maxGenerationAttempts: ndp.configs.AutoGenAddressConflictRetries + 1,
|
||||
@@ -1158,7 +1158,7 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
|
||||
|
||||
// addAndAcquireSLAACAddr adds a SLAAC address to the IPv6 endpoint.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) addAndAcquireSLAACAddr(addr tcpip.AddressWithPrefix, temporary bool, lifetimes stack.AddressLifetimes) stack.AddressEndpoint {
|
||||
addressEndpoint, err := ndp.ep.addAndAcquirePermanentAddressLocked(addr, stack.AddressProperties{
|
||||
PEB: stack.FirstPrimaryEndpoint,
|
||||
@@ -1186,7 +1186,7 @@ func (ndp *ndpState) addAndAcquireSLAACAddr(addr tcpip.AddressWithPrefix, tempor
|
||||
//
|
||||
// Panics if the prefix is not a SLAAC prefix or it already has an address.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) generateSLAACAddr(prefix tcpip.Subnet, state *slaacPrefixState) bool {
|
||||
if addressEndpoint := state.stableAddr.addressEndpoint; addressEndpoint != nil {
|
||||
panic(fmt.Sprintf("ndp: SLAAC prefix %s already has a permanent address %s", prefix, addressEndpoint.AddressWithPrefix()))
|
||||
@@ -1283,7 +1283,7 @@ func (ndp *ndpState) generateSLAACAddr(prefix tcpip.Subnet, state *slaacPrefixSt
|
||||
//
|
||||
// If generating a new address for the prefix fails, the prefix is invalidated.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) regenerateSLAACAddr(prefix tcpip.Subnet) {
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok {
|
||||
@@ -1306,6 +1306,8 @@ func (ndp *ndpState) regenerateSLAACAddr(prefix tcpip.Subnet) {
|
||||
// If resetGenAttempts is true, the prefix's generation counter is reset.
|
||||
//
|
||||
// Returns true if a new address was generated.
|
||||
//
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *slaacPrefixState, resetGenAttempts bool) bool {
|
||||
// Are we configured to auto-generate new temporary global addresses for the
|
||||
// prefix?
|
||||
@@ -1403,7 +1405,7 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
|
||||
panic(fmt.Sprintf("ndp: must have a tempAddr entry to deprecate temporary address %s", generatedAddr))
|
||||
}
|
||||
|
||||
ndp.deprecateSLAACAddress(tempAddrState.addressEndpoint)
|
||||
ndp.deprecateSLAACAddress(tempAddrState.addressEndpoint) // +checklocksforce: NewJob calls back with ndp.ep.mu held.
|
||||
}),
|
||||
invalidationJob: tcpip.NewJob(ndp.ep.protocol.stack.Clock(), &ndp.ep.mu, func() {
|
||||
prefixState, ok := ndp.slaacPrefixes[prefix]
|
||||
@@ -1416,7 +1418,7 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
|
||||
panic(fmt.Sprintf("ndp: must have a tempAddr entry to invalidate temporary address %s", generatedAddr))
|
||||
}
|
||||
|
||||
ndp.invalidateTempSLAACAddr(prefixState.tempAddrs, generatedAddr.Address, tempAddrState)
|
||||
ndp.invalidateTempSLAACAddr(prefixState.tempAddrs, generatedAddr.Address, tempAddrState) // +checklocksforce: NewJob calls back with ndp.ep.mu held.
|
||||
}),
|
||||
regenJob: tcpip.NewJob(ndp.ep.protocol.stack.Clock(), &ndp.ep.mu, func() {
|
||||
prefixState, ok := ndp.slaacPrefixes[prefix]
|
||||
@@ -1437,7 +1439,7 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
|
||||
|
||||
// Reset the generation attempts counter as we are starting the generation
|
||||
// of a new address for the SLAAC prefix.
|
||||
tempAddrState.regenerated = ndp.generateTempSLAACAddr(prefix, &prefixState, true /* resetGenAttempts */)
|
||||
tempAddrState.regenerated = ndp.generateTempSLAACAddr(prefix, &prefixState, true /* resetGenAttempts */) // +checklocksforce: NewJob calls back with ndp.ep.mu held.
|
||||
prefixState.tempAddrs[generatedAddr.Address] = tempAddrState
|
||||
ndp.slaacPrefixes[prefix] = prefixState
|
||||
}),
|
||||
@@ -1457,7 +1459,7 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
|
||||
|
||||
// regenerateTempSLAACAddr regenerates a temporary address for a SLAAC prefix.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) regenerateTempSLAACAddr(prefix tcpip.Subnet, resetGenAttempts bool) {
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok {
|
||||
@@ -1472,7 +1474,7 @@ func (ndp *ndpState) regenerateTempSLAACAddr(prefix tcpip.Subnet, resetGenAttemp
|
||||
//
|
||||
// pl is the new preferred lifetime. vl is the new valid lifetime.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixState *slaacPrefixState, pl, vl time.Duration) {
|
||||
// If prefix was preferred for some finite lifetime before, cancel the
|
||||
// deprecation job so it can be reset.
|
||||
@@ -1658,7 +1660,7 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
|
||||
//
|
||||
// deprecateSLAACAddress does nothing if the address is already deprecated.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) deprecateSLAACAddress(addressEndpoint stack.AddressEndpoint) {
|
||||
if addressEndpoint.Deprecated() {
|
||||
return
|
||||
@@ -1672,7 +1674,7 @@ func (ndp *ndpState) deprecateSLAACAddress(addressEndpoint stack.AddressEndpoint
|
||||
|
||||
// invalidateSLAACPrefix invalidates a SLAAC prefix.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) invalidateSLAACPrefix(prefix tcpip.Subnet, state slaacPrefixState) {
|
||||
ndp.cleanupSLAACPrefixResources(prefix, state)
|
||||
|
||||
@@ -1690,7 +1692,7 @@ func (ndp *ndpState) invalidateSLAACPrefix(prefix tcpip.Subnet, state slaacPrefi
|
||||
// cleanupSLAACAddrResourcesAndNotify cleans up an invalidated SLAAC address's
|
||||
// resources.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) cleanupSLAACAddrResourcesAndNotify(addr tcpip.AddressWithPrefix, invalidatePrefix bool) {
|
||||
if ndpDisp := ndp.ep.protocol.options.NDPDisp; ndpDisp != nil {
|
||||
ndpDisp.OnAutoGenAddressInvalidated(ndp.ep.nic.ID(), addr)
|
||||
@@ -1718,7 +1720,7 @@ func (ndp *ndpState) cleanupSLAACAddrResourcesAndNotify(addr tcpip.AddressWithPr
|
||||
//
|
||||
// Panics if the SLAAC prefix is not known.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) cleanupSLAACPrefixResources(prefix tcpip.Subnet, state slaacPrefixState) {
|
||||
// Invalidate all temporary addresses.
|
||||
for tempAddr, tempAddrState := range state.tempAddrs {
|
||||
@@ -1736,7 +1738,7 @@ func (ndp *ndpState) cleanupSLAACPrefixResources(prefix tcpip.Subnet, state slaa
|
||||
|
||||
// invalidateTempSLAACAddr invalidates a temporary SLAAC address.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) invalidateTempSLAACAddr(tempAddrs map[tcpip.Address]tempSLAACAddrState, tempAddr tcpip.Address, tempAddrState tempSLAACAddrState) {
|
||||
ndp.cleanupTempSLAACAddrResourcesAndNotifyInner(tempAddrs, tempAddr, tempAddrState)
|
||||
|
||||
@@ -1749,7 +1751,7 @@ func (ndp *ndpState) invalidateTempSLAACAddr(tempAddrs map[tcpip.Address]tempSLA
|
||||
// SLAAC address's resources from ndp and notifies the NDP dispatcher that the
|
||||
// address was invalidated.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) cleanupTempSLAACAddrResourcesAndNotify(addr tcpip.AddressWithPrefix) {
|
||||
prefix := addr.Subnet()
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
@@ -1769,7 +1771,7 @@ func (ndp *ndpState) cleanupTempSLAACAddrResourcesAndNotify(addr tcpip.AddressWi
|
||||
// cleanupTempSLAACAddrResourcesAndNotify except it does not lookup the
|
||||
// temporary address's state in ndp - it assumes the passed state is valid.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) cleanupTempSLAACAddrResourcesAndNotifyInner(tempAddrs map[tcpip.Address]tempSLAACAddrState, tempAddr tcpip.Address, tempAddrState tempSLAACAddrState) {
|
||||
if ndpDisp := ndp.ep.protocol.options.NDPDisp; ndpDisp != nil {
|
||||
ndpDisp.OnAutoGenAddressInvalidated(ndp.ep.nic.ID(), tempAddrState.addressEndpoint.AddressWithPrefix())
|
||||
@@ -1788,7 +1790,7 @@ func (ndp *ndpState) cleanupTempSLAACAddrResourcesAndNotifyInner(tempAddrs map[t
|
||||
// This function invalidates all discovered on-link prefixes, discovered
|
||||
// routers, and auto-generated addresses.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) cleanupState() {
|
||||
for prefix, state := range ndp.slaacPrefixes {
|
||||
ndp.invalidateSLAACPrefix(prefix, state)
|
||||
@@ -1820,7 +1822,7 @@ func (ndp *ndpState) cleanupState() {
|
||||
// be solicited as there is no point soliciting routers if we don't handle their
|
||||
// advertisements.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) startSolicitingRouters() {
|
||||
if ndp.rtrSolicitTimer.timer != nil {
|
||||
// We are already soliciting routers.
|
||||
@@ -1937,7 +1939,7 @@ func (ndp *ndpState) startSolicitingRouters() {
|
||||
// router solicitation will be stopped if NDP is not configured to handle RAs
|
||||
// as a router.
|
||||
//
|
||||
// Precondition: ndp.ep.mu must be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) forwardingChanged(forwarding bool) {
|
||||
if forwarding {
|
||||
if ndp.configs.HandleRAs.enabled(forwarding) {
|
||||
@@ -1960,7 +1962,7 @@ func (ndp *ndpState) forwardingChanged(forwarding bool) {
|
||||
// stopSolicitingRouters stops soliciting routers. If routers are not currently
|
||||
// being solicited, this function does nothing.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
// +checklocks:ndp.ep.mu.RWMutex
|
||||
func (ndp *ndpState) stopSolicitingRouters() {
|
||||
if ndp.rtrSolicitTimer.timer == nil {
|
||||
// Nothing to do.
|
||||
|
||||
+43
-8
@@ -219,18 +219,27 @@ func (ad addrToDevice) isAvailable(res Reservation, portSpecified bool) bool {
|
||||
//
|
||||
// +stateify savable
|
||||
type PortManager struct {
|
||||
// mu protects allocatedPorts.
|
||||
// LOCK ORDERING: mu > ephemeralMu.
|
||||
mu sync.RWMutex `state:"nosave"`
|
||||
|
||||
// allocatedPorts is a nesting of maps that ultimately map Reservations
|
||||
// to FlagCounters describing whether the Reservation is valid and can
|
||||
// be reused.
|
||||
//
|
||||
// mu also protects nested maps reached through allocatedPorts. Their
|
||||
// helper receivers have no reference to this PortManager, so checklocks
|
||||
// cannot express those helpers' owner-lock precondition.
|
||||
//
|
||||
// +checklocks:mu
|
||||
allocatedPorts map[portDescriptor]addrToDevice
|
||||
|
||||
// ephemeralMu protects firstEphemeral and numEphemeral.
|
||||
ephemeralMu sync.RWMutex `state:"nosave"`
|
||||
ephemeralMu sync.RWMutex `state:"nosave"`
|
||||
|
||||
// +checklocks:ephemeralMu
|
||||
firstEphemeral uint16
|
||||
numEphemeral uint16
|
||||
|
||||
// +checklocks:ephemeralMu
|
||||
numEphemeral uint16
|
||||
}
|
||||
|
||||
// NewPortManager creates new PortManager.
|
||||
@@ -251,6 +260,11 @@ type PortTester func(port uint16) (good bool, err tcpip.Error)
|
||||
// possible ephemeral ports, allowing the caller to decide whether a given port
|
||||
// is suitable for its needs, and stopping when a port is found or an error
|
||||
// occurs.
|
||||
//
|
||||
// testPort is called synchronously without ephemeralMu held. Locks held by
|
||||
// the caller remain held during the callback.
|
||||
//
|
||||
// +checklocksexclude:pm.ephemeralMu
|
||||
func (pm *PortManager) PickEphemeralPort(rng rand.RNG, testPort PortTester) (port uint16, err tcpip.Error) {
|
||||
pm.ephemeralMu.RLock()
|
||||
firstEphemeral := pm.firstEphemeral
|
||||
@@ -289,6 +303,12 @@ func pickEphemeralPort(offset uint32, first, count uint16, testPort PortTester)
|
||||
// An optional PortTester can be passed in which if provided will be used to
|
||||
// test if the picked port can be used. The function should return true if the
|
||||
// port is safe to use, false otherwise.
|
||||
//
|
||||
// testPort is called synchronously with pm.mu held and must not call methods
|
||||
// that acquire the same pm.mu.
|
||||
//
|
||||
// +checklocksexclude:pm.mu
|
||||
// +checklocksexclude:pm.ephemeralMu
|
||||
func (pm *PortManager) ReservePort(rng rand.RNG, res Reservation, testPort PortTester) (reservedPort uint16, err tcpip.Error) {
|
||||
pm.mu.Lock()
|
||||
defer pm.mu.Unlock()
|
||||
@@ -313,20 +333,22 @@ func (pm *PortManager) ReservePort(rng rand.RNG, res Reservation, testPort PortT
|
||||
return res.Port, nil
|
||||
}
|
||||
|
||||
// A port wasn't specified, so try to find one.
|
||||
// A port wasn't specified, so try to find one. PickEphemeralPort calls
|
||||
// this callback synchronously with pm.mu still held, but checklocks
|
||||
// analyzes passed callbacks without the caller's lock state.
|
||||
return pm.PickEphemeralPort(rng, func(p uint16) (bool, tcpip.Error) {
|
||||
res.Port = p
|
||||
if !pm.reserveSpecificPortLocked(res, false /* portSpecified */) {
|
||||
if !pm.reserveSpecificPortLocked(res, false /* portSpecified */) { // +checklocksignore
|
||||
return false, nil
|
||||
}
|
||||
if testPort != nil {
|
||||
ok, err := testPort(p)
|
||||
if err != nil {
|
||||
pm.releasePortLocked(res)
|
||||
pm.releasePortLocked(res) // +checklocksignore
|
||||
return false, err
|
||||
}
|
||||
if !ok {
|
||||
pm.releasePortLocked(res)
|
||||
pm.releasePortLocked(res) // +checklocksignore
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
@@ -336,6 +358,8 @@ func (pm *PortManager) ReservePort(rng rand.RNG, res Reservation, testPort PortT
|
||||
|
||||
// reserveSpecificPortLocked tries to reserve the given port on all given
|
||||
// protocols.
|
||||
//
|
||||
// +checklocks:pm.mu
|
||||
func (pm *PortManager) reserveSpecificPortLocked(res Reservation, portSpecified bool) bool {
|
||||
// Make sure the port is available.
|
||||
for _, network := range res.Networks {
|
||||
@@ -376,6 +400,8 @@ func (pm *PortManager) reserveSpecificPortLocked(res Reservation, portSpecified
|
||||
}
|
||||
|
||||
// ReserveTuple adds a port reservation for the tuple on all given protocol.
|
||||
//
|
||||
// +checklocksexclude:pm.mu
|
||||
func (pm *PortManager) ReserveTuple(res Reservation) bool {
|
||||
flagBits := res.Flags.Bits()
|
||||
dst := res.dst()
|
||||
@@ -428,6 +454,8 @@ func (pm *PortManager) ReserveTuple(res Reservation) bool {
|
||||
|
||||
// ReleasePort releases the reservation on a port/IP combination so that it can
|
||||
// be reserved by other endpoints.
|
||||
//
|
||||
// +checklocksexclude:pm.mu
|
||||
func (pm *PortManager) ReleasePort(res Reservation) {
|
||||
pm.mu.Lock()
|
||||
defer pm.mu.Unlock()
|
||||
@@ -435,6 +463,9 @@ func (pm *PortManager) ReleasePort(res Reservation) {
|
||||
pm.releasePortLocked(res)
|
||||
}
|
||||
|
||||
// releasePortLocked releases a reservation without dropping the caller's lock.
|
||||
//
|
||||
// +checklocks:pm.mu
|
||||
func (pm *PortManager) releasePortLocked(res Reservation) {
|
||||
dst := res.dst()
|
||||
for _, network := range res.Networks {
|
||||
@@ -478,6 +509,8 @@ func (pm *PortManager) releasePortLocked(res Reservation) {
|
||||
|
||||
// PortRange returns the UDP and TCP inclusive range of ephemeral ports used in
|
||||
// both IPv4 and IPv6.
|
||||
//
|
||||
// +checklocksexclude:pm.ephemeralMu
|
||||
func (pm *PortManager) PortRange() (uint16, uint16) {
|
||||
pm.ephemeralMu.RLock()
|
||||
defer pm.ephemeralMu.RUnlock()
|
||||
@@ -486,6 +519,8 @@ func (pm *PortManager) PortRange() (uint16, uint16) {
|
||||
|
||||
// SetPortRange sets the UDP and TCP IPv4 and IPv6 ephemeral port range
|
||||
// (inclusive).
|
||||
//
|
||||
// +checklocksexclude:pm.ephemeralMu
|
||||
func (pm *PortManager) SetPortRange(start uint16, end uint16) tcpip.Error {
|
||||
if start > end {
|
||||
return &tcpip.ErrInvalidPortRange{}
|
||||
|
||||
+59
-10
@@ -142,7 +142,9 @@ type SocketOptions struct {
|
||||
// StackHandler is initialized at the creation time and will not change.
|
||||
stackHandler StackHandler `state:"manual"`
|
||||
|
||||
// These fields are accessed and modified using atomic operations.
|
||||
// The boolean option fields below are accessed atomically. Their setters
|
||||
// pass field addresses through storeAtomicBool; checklocks cannot verify
|
||||
// that helper's atomic operation at those call sites.
|
||||
|
||||
// broadcastEnabled determines whether datagram sockets are allowed to
|
||||
// send packets to a broadcast address.
|
||||
@@ -230,11 +232,16 @@ type SocketOptions struct {
|
||||
// passing is enabled for IPv6.
|
||||
ipv6RecvErrEnabled atomicbitops.Uint32
|
||||
|
||||
// errQueue is the per-socket error queue. It is protected by errQueueMu.
|
||||
errQueueMu sync.Mutex `state:"nosave"`
|
||||
errQueue sockErrorList
|
||||
|
||||
// errQueue is the per-socket error queue.
|
||||
//
|
||||
// +checklocks:errQueueMu
|
||||
errQueue sockErrorList
|
||||
|
||||
// bindToDevice determines the device to which the socket is bound.
|
||||
//
|
||||
// +checkatomic
|
||||
bindToDevice atomicbitops.Int32
|
||||
|
||||
// getSendBufferLimits provides the handler to get the min, default and max
|
||||
@@ -243,6 +250,8 @@ type SocketOptions struct {
|
||||
getSendBufferLimits GetSendBufferLimits `state:"manual"`
|
||||
|
||||
// sendBufferSize determines the send buffer size for this socket.
|
||||
//
|
||||
// +checkatomic
|
||||
sendBufferSize atomicbitops.Int64
|
||||
|
||||
// getReceiveBufferLimits provides the handler to get the min, default and
|
||||
@@ -251,22 +260,34 @@ type SocketOptions struct {
|
||||
getReceiveBufferLimits GetReceiveBufferLimits `state:"manual"`
|
||||
|
||||
// receiveBufferSize determines the receive buffer size for this socket.
|
||||
//
|
||||
// +checkatomic
|
||||
receiveBufferSize atomicbitops.Int64
|
||||
|
||||
// mu protects the access to the below fields.
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
|
||||
// linger determines the amount of time the socket should linger before
|
||||
// close. We currently implement this option for TCP socket only.
|
||||
linger LingerOption
|
||||
|
||||
// rcvlowat specifies the minimum number of bytes which should be
|
||||
// received to indicate the socket as readable.
|
||||
//
|
||||
// +checkatomic
|
||||
rcvlowat atomicbitops.Int32
|
||||
|
||||
// experimentOptionValue is the value set for the IP option experiment header
|
||||
// if it is not zero.
|
||||
//
|
||||
// +checkatomic
|
||||
experimentOptionValue atomicbitops.Uint32
|
||||
|
||||
// mark is the mark value set for the socket.
|
||||
//
|
||||
// +checkatomic
|
||||
mark atomicbitops.Uint32
|
||||
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
|
||||
// linger determines the amount of time the socket should linger before
|
||||
// close. We currently implement this option for TCP socket only.
|
||||
//
|
||||
// +checklocks:mu
|
||||
linger LingerOption
|
||||
}
|
||||
|
||||
// InitHandler initializes the handler. This must be called before using the
|
||||
@@ -494,6 +515,8 @@ func (so *SocketOptions) GetIPv4RecvError() bool {
|
||||
}
|
||||
|
||||
// SetIPv4RecvError sets value for IP_RECVERR option.
|
||||
//
|
||||
// +checklocksexclude:so.errQueueMu
|
||||
func (so *SocketOptions) SetIPv4RecvError(v bool) {
|
||||
storeAtomicBool(&so.ipv4RecvErrEnabled, v)
|
||||
if !v {
|
||||
@@ -507,6 +530,8 @@ func (so *SocketOptions) GetIPv6RecvError() bool {
|
||||
}
|
||||
|
||||
// SetIPv6RecvError sets value for IPV6_RECVERR option.
|
||||
//
|
||||
// +checklocksexclude:so.errQueueMu
|
||||
func (so *SocketOptions) SetIPv6RecvError(v bool) {
|
||||
storeAtomicBool(&so.ipv6RecvErrEnabled, v)
|
||||
if !v {
|
||||
@@ -529,6 +554,8 @@ func (*SocketOptions) GetOutOfBandInline() bool {
|
||||
func (*SocketOptions) SetOutOfBandInline(bool) {}
|
||||
|
||||
// GetLinger gets value for SO_LINGER option.
|
||||
//
|
||||
// +checklocksexclude:so.mu
|
||||
func (so *SocketOptions) GetLinger() LingerOption {
|
||||
so.mu.Lock()
|
||||
linger := so.linger
|
||||
@@ -537,6 +564,8 @@ func (so *SocketOptions) GetLinger() LingerOption {
|
||||
}
|
||||
|
||||
// SetLinger sets value for SO_LINGER option.
|
||||
//
|
||||
// +checklocksexclude:so.mu
|
||||
func (so *SocketOptions) SetLinger(linger LingerOption) {
|
||||
so.mu.Lock()
|
||||
so.linger = linger
|
||||
@@ -640,6 +669,8 @@ type SockError struct {
|
||||
}
|
||||
|
||||
// pruneErrQueue resets the queue.
|
||||
//
|
||||
// +checklocksexclude:so.errQueueMu
|
||||
func (so *SocketOptions) pruneErrQueue() {
|
||||
so.errQueueMu.Lock()
|
||||
so.errQueue.Reset()
|
||||
@@ -648,6 +679,8 @@ func (so *SocketOptions) pruneErrQueue() {
|
||||
|
||||
// DequeueErr dequeues a socket extended error from the error queue and returns
|
||||
// it. Returns nil if queue is empty.
|
||||
//
|
||||
// +checklocksexclude:so.errQueueMu
|
||||
func (so *SocketOptions) DequeueErr() *SockError {
|
||||
so.errQueueMu.Lock()
|
||||
defer so.errQueueMu.Unlock()
|
||||
@@ -661,6 +694,8 @@ func (so *SocketOptions) DequeueErr() *SockError {
|
||||
|
||||
// PeekErr returns the error in the front of the error queue. Returns nil if
|
||||
// the error queue is empty.
|
||||
//
|
||||
// +checklocksexclude:so.errQueueMu
|
||||
func (so *SocketOptions) PeekErr() *SockError {
|
||||
so.errQueueMu.Lock()
|
||||
defer so.errQueueMu.Unlock()
|
||||
@@ -670,6 +705,8 @@ func (so *SocketOptions) PeekErr() *SockError {
|
||||
// QueueErr inserts the error at the back of the error queue.
|
||||
//
|
||||
// Preconditions: so.GetIPv4RecvError() or so.GetIPv6RecvError() is true.
|
||||
//
|
||||
// +checklocksexclude:so.errQueueMu
|
||||
func (so *SocketOptions) QueueErr(err *SockError) {
|
||||
so.errQueueMu.Lock()
|
||||
defer so.errQueueMu.Unlock()
|
||||
@@ -677,6 +714,8 @@ func (so *SocketOptions) QueueErr(err *SockError) {
|
||||
}
|
||||
|
||||
// QueueLocalErr queues a local error onto the local queue.
|
||||
//
|
||||
// +checklocksexclude:so.errQueueMu
|
||||
func (so *SocketOptions) QueueLocalErr(err Error, net NetworkProtocolNumber, info uint32, dst FullAddress, payload *buffer.View) {
|
||||
so.QueueErr(&SockError{
|
||||
Err: err,
|
||||
@@ -771,3 +810,13 @@ func (so *SocketOptions) SetRcvlowat(rcvlowat int32) Error {
|
||||
func (so *SocketOptions) GetAcceptConn() bool {
|
||||
return so.handler.GetAcceptConn()
|
||||
}
|
||||
|
||||
// GetMark gets value for SO_MARK option.
|
||||
func (so *SocketOptions) GetMark() uint32 {
|
||||
return so.mark.Load()
|
||||
}
|
||||
|
||||
// SetMark sets value for SO_MARK option.
|
||||
func (so *SocketOptions) SetMark(v uint32) {
|
||||
so.mark.Store(v)
|
||||
}
|
||||
@@ -92,5 +92,5 @@ func addressStateinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
addressStateinitLockNames()
|
||||
addressStateprefixIndex = locking.NewMutexClass(reflect.TypeOf(addressStateRWMutex{}), addressStatelockNames)
|
||||
addressStateprefixIndex = locking.NewMutexClass(reflect.TypeFor[addressStateRWMutex](), addressStatelockNames)
|
||||
}
|
||||
@@ -602,7 +602,7 @@ func (a *AddressableEndpointState) AcquireAssignedAddressOrMatching(localAddr tc
|
||||
|
||||
// Proceed to add a new temporary endpoint.
|
||||
addr := localAddr.WithPrefix()
|
||||
ep, err := a.addAndAcquireAddressLocked(addr, AddressProperties{PEB: tempPEB}, Temporary)
|
||||
ep, err := a.addAndAcquireAddressLocked(addr, AddressProperties{PEB: tempPEB, Temporary: true}, Temporary)
|
||||
if err != nil {
|
||||
// addAndAcquireAddressLocked only returns an error if the address is
|
||||
// already assigned but we just checked above if the address exists so we
|
||||
@@ -750,9 +750,9 @@ type addressState struct {
|
||||
// addressState.mu
|
||||
mu addressStateRWMutex
|
||||
refs addressStateRefs
|
||||
// checklocks:mu
|
||||
// +checklocks:mu
|
||||
kind AddressKind
|
||||
// checklocks:mu
|
||||
// +checklocks:mu
|
||||
configType AddressConfigType
|
||||
// lifetimes holds this address' lifetimes.
|
||||
//
|
||||
@@ -760,12 +760,12 @@ type addressState struct {
|
||||
// must be the zero value. Note that the converse does not need to be
|
||||
// upheld!
|
||||
//
|
||||
// checklocks:mu
|
||||
// +checklocks:mu
|
||||
lifetimes AddressLifetimes
|
||||
// The enclosing mutex must be write-locked before calling methods on the
|
||||
// dispatcher.
|
||||
//
|
||||
// checklocks:mu
|
||||
// +checklocks:mu
|
||||
disp AddressDispatcher
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -92,5 +92,5 @@ func addressableEndpointStateinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
addressableEndpointStateinitLockNames()
|
||||
addressableEndpointStateprefixIndex = locking.NewMutexClass(reflect.TypeOf(addressableEndpointStateRWMutex{}), addressableEndpointStatelockNames)
|
||||
addressableEndpointStateprefixIndex = locking.NewMutexClass(reflect.TypeFor[addressableEndpointStateRWMutex](), addressableEndpointStatelockNames)
|
||||
}
|
||||
@@ -92,5 +92,5 @@ func bridgeinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
bridgeinitLockNames()
|
||||
bridgeprefixIndex = locking.NewMutexClass(reflect.TypeOf(bridgeRWMutex{}), bridgelockNames)
|
||||
bridgeprefixIndex = locking.NewMutexClass(reflect.TypeFor[bridgeRWMutex](), bridgelockNames)
|
||||
}
|
||||
@@ -94,5 +94,5 @@ func bucketinitLockNames() { bucketlockNames = []string{"otherTuple"} }
|
||||
|
||||
func init() {
|
||||
bucketinitLockNames()
|
||||
bucketprefixIndex = locking.NewMutexClass(reflect.TypeOf(bucketRWMutex{}), bucketlockNames)
|
||||
bucketprefixIndex = locking.NewMutexClass(reflect.TypeFor[bucketRWMutex](), bucketlockNames)
|
||||
}
|
||||
@@ -60,5 +60,5 @@ func cleanupEndpointsinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
cleanupEndpointsinitLockNames()
|
||||
cleanupEndpointsprefixIndex = locking.NewMutexClass(reflect.TypeOf(cleanupEndpointsMutex{}), cleanupEndpointslockNames)
|
||||
cleanupEndpointsprefixIndex = locking.NewMutexClass(reflect.TypeFor[cleanupEndpointsMutex](), cleanupEndpointslockNames)
|
||||
}
|
||||
+1
-1
@@ -92,5 +92,5 @@ func conninitLockNames() {}
|
||||
|
||||
func init() {
|
||||
conninitLockNames()
|
||||
connprefixIndex = locking.NewMutexClass(reflect.TypeOf(connRWMutex{}), connlockNames)
|
||||
connprefixIndex = locking.NewMutexClass(reflect.TypeFor[connRWMutex](), connlockNames)
|
||||
}
|
||||
@@ -92,5 +92,5 @@ func connTrackinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
connTrackinitLockNames()
|
||||
connTrackprefixIndex = locking.NewMutexClass(reflect.TypeOf(connTrackRWMutex{}), connTracklockNames)
|
||||
connTrackprefixIndex = locking.NewMutexClass(reflect.TypeFor[connTrackRWMutex](), connTracklockNames)
|
||||
}
|
||||
+244
-410
@@ -18,7 +18,6 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -45,6 +44,31 @@ const (
|
||||
unestablishedTimeout time.Duration = 120 * time.Second
|
||||
)
|
||||
|
||||
// ConnTrackState represents the state of a connection.
|
||||
type ConnTrackState int
|
||||
|
||||
const (
|
||||
// ConnTrackStateInvalid is the invalid connection tracking state.
|
||||
ConnTrackStateInvalid ConnTrackState = -1
|
||||
// ConnTrackStateEstablished represents an established connection.
|
||||
ConnTrackStateEstablished ConnTrackState = 0
|
||||
// ConnTrackStateNew represents a new connection.
|
||||
ConnTrackStateNew ConnTrackState = 2
|
||||
// ConnTrackStateEstablishedReply represents an established connection
|
||||
// in the reply direction.
|
||||
ConnTrackStateEstablishedReply ConnTrackState = 3
|
||||
)
|
||||
|
||||
// ConnTrackDirection represents the direction of a connection.
|
||||
type ConnTrackDirection uint8
|
||||
|
||||
const (
|
||||
// ConnTrackDirectionOriginal represents the original direction.
|
||||
ConnTrackDirectionOriginal ConnTrackDirection = 0
|
||||
// ConnTrackDirectionReply represents the reply direction.
|
||||
ConnTrackDirectionReply ConnTrackDirection = 1
|
||||
)
|
||||
|
||||
// tuple holds a connection's identifying and manipulating data in one
|
||||
// direction. It is immutable.
|
||||
//
|
||||
@@ -161,6 +185,10 @@ type conn struct {
|
||||
//
|
||||
// +checklocks:stateMu
|
||||
lastUsed tcpip.MonotonicTime
|
||||
// replySeen indicates whether a packet in the reply direction has been seen.
|
||||
//
|
||||
// +checklocks:stateMu
|
||||
replySeen bool
|
||||
}
|
||||
|
||||
// timedOut returns whether the connection timed out based on its state.
|
||||
@@ -177,6 +205,27 @@ func (cn *conn) timedOut(now tcpip.MonotonicTime) bool {
|
||||
return now.Sub(cn.lastUsed) > unestablishedTimeout
|
||||
}
|
||||
|
||||
// expiresIn returns the duration from now until the connection times out.
|
||||
func (cn *conn) expiresIn() time.Duration {
|
||||
var timeout time.Duration
|
||||
var lastUsed tcpip.MonotonicTime
|
||||
cn.stateMu.RLock()
|
||||
state := cn.tcb.State()
|
||||
lastUsed = cn.lastUsed
|
||||
cn.stateMu.RUnlock()
|
||||
if state == tcpconntrack.ResultAlive {
|
||||
timeout = establishedTimeout
|
||||
} else {
|
||||
timeout = unestablishedTimeout
|
||||
}
|
||||
now := cn.ct.clock.NowMonotonic()
|
||||
expires := timeout - now.Sub(lastUsed)
|
||||
if expires < 0 {
|
||||
return 0
|
||||
}
|
||||
return expires
|
||||
}
|
||||
|
||||
// update the connection tracking state.
|
||||
func (cn *conn) update(pkt *PacketBuffer, reply bool) {
|
||||
cn.stateMu.Lock()
|
||||
@@ -184,6 +233,9 @@ func (cn *conn) update(pkt *PacketBuffer, reply bool) {
|
||||
|
||||
// Mark the connection as having been used recently so it isn't reaped.
|
||||
cn.lastUsed = cn.ct.clock.NowMonotonic()
|
||||
if reply {
|
||||
cn.replySeen = true
|
||||
}
|
||||
|
||||
if pkt.TransportProtocolNumber != header.TCPProtocolNumber {
|
||||
return
|
||||
@@ -206,6 +258,10 @@ func (cn *conn) update(pkt *PacketBuffer, reply bool) {
|
||||
}
|
||||
}
|
||||
|
||||
type connTrackRNG interface {
|
||||
Uint32() uint32
|
||||
}
|
||||
|
||||
// ConnTrack tracks all connections created for NAT rules. Most users are
|
||||
// expected to only call handlePacket, insertRedirectConn, and maybeInsertNoop.
|
||||
//
|
||||
@@ -225,12 +281,26 @@ type ConnTrack struct {
|
||||
// seed is a one-time random value initialized at stack startup
|
||||
// and is used in the calculation of hash keys for the list of buckets.
|
||||
// It is immutable.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/4595): When Stack.tables becomes savable and
|
||||
// ConnTrack flows into checkpoint state, this seed must be redrawn
|
||||
// from secureRNG during restore AND the entries in buckets must be
|
||||
// rehashed under the new seed. bucket_index = jenkins.Sum32(seed) %
|
||||
// len(buckets) couples the seed value to bucket layout; redrawing the
|
||||
// seed without rehashing leaves restored entries unreachable by
|
||||
// Lookup. Persisting the pre-checkpoint seed extends the brute-force
|
||||
// window across save boundaries.
|
||||
seed uint32
|
||||
|
||||
// nftIDSeed is a one-time random value initialized at stack startup
|
||||
// and is used in the calculation of tuple IDs for nftables.
|
||||
// It is immutable.
|
||||
nftIDSeed uint32
|
||||
|
||||
// clock provides timing used to determine conntrack reapings.
|
||||
clock tcpip.Clock
|
||||
// TODO(b/341946753): Restore when netstack is savable.
|
||||
rand *rand.Rand `state:"nosave"`
|
||||
rng connTrackRNG `state:"nosave"`
|
||||
|
||||
mu connTrackRWMutex `state:"nosave"`
|
||||
// mu protects the buckets slice, but not buckets' contents. Only take
|
||||
@@ -271,99 +341,6 @@ func v6NetAndTransHdr(icmpPayload []byte, minTransHdrLen int) (header.Network, [
|
||||
return netHdr, transHdr[:minTransHdrLen]
|
||||
}
|
||||
|
||||
func getEmbeddedNetAndTransHeaders(pkt *PacketBuffer, netHdrLength int, getNetAndTransHdr netAndTransHeadersFunc, transProto tcpip.TransportProtocolNumber) (header.Network, header.ChecksummableTransport, bool) {
|
||||
switch transProto {
|
||||
case header.TCPProtocolNumber:
|
||||
if netAndTransHeader, ok := pkt.Data().PullUp(netHdrLength + header.TCPMinimumSize); ok {
|
||||
netHeader, transHeaderBytes := getNetAndTransHdr(netAndTransHeader, header.TCPMinimumSize)
|
||||
return netHeader, header.TCP(transHeaderBytes), true
|
||||
}
|
||||
case header.UDPProtocolNumber:
|
||||
if netAndTransHeader, ok := pkt.Data().PullUp(netHdrLength + header.UDPMinimumSize); ok {
|
||||
netHeader, transHeaderBytes := getNetAndTransHdr(netAndTransHeader, header.UDPMinimumSize)
|
||||
return netHeader, header.UDP(transHeaderBytes), true
|
||||
}
|
||||
}
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
func getHeaders(pkt *PacketBuffer) (netHdr header.Network, transHdr header.Transport, isICMPError bool, ok bool) {
|
||||
switch pkt.TransportProtocolNumber {
|
||||
case header.TCPProtocolNumber:
|
||||
if tcpHeader := header.TCP(pkt.TransportHeader().Slice()); len(tcpHeader) >= header.TCPMinimumSize {
|
||||
return pkt.Network(), tcpHeader, false, true
|
||||
}
|
||||
return nil, nil, false, false
|
||||
case header.UDPProtocolNumber:
|
||||
if udpHeader := header.UDP(pkt.TransportHeader().Slice()); len(udpHeader) >= header.UDPMinimumSize {
|
||||
return pkt.Network(), udpHeader, false, true
|
||||
}
|
||||
return nil, nil, false, false
|
||||
case header.ICMPv4ProtocolNumber:
|
||||
icmpHeader := header.ICMPv4(pkt.TransportHeader().Slice())
|
||||
if len(icmpHeader) < header.ICMPv4MinimumSize {
|
||||
return nil, nil, false, false
|
||||
}
|
||||
|
||||
switch icmpType := icmpHeader.Type(); icmpType {
|
||||
case header.ICMPv4Echo, header.ICMPv4EchoReply:
|
||||
return pkt.Network(), icmpHeader, false, true
|
||||
case header.ICMPv4DstUnreachable, header.ICMPv4TimeExceeded, header.ICMPv4ParamProblem:
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected ICMPv4 type = %d", icmpType))
|
||||
}
|
||||
|
||||
h, ok := pkt.Data().PullUp(header.IPv4MinimumSize)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("should have a valid IPv4 packet; only have %d bytes, want at least %d bytes", pkt.Data().Size(), header.IPv4MinimumSize))
|
||||
}
|
||||
|
||||
if header.IPv4(h).HeaderLength() > header.IPv4MinimumSize {
|
||||
// TODO(https://gvisor.dev/issue/6765): Handle IPv4 options.
|
||||
panic("should have dropped packets with IPv4 options")
|
||||
}
|
||||
|
||||
if netHdr, transHdr, ok := getEmbeddedNetAndTransHeaders(pkt, header.IPv4MinimumSize, v4NetAndTransHdr, pkt.tuple.tupleID.transProto); ok {
|
||||
return netHdr, transHdr, true, true
|
||||
}
|
||||
return nil, nil, false, false
|
||||
case header.ICMPv6ProtocolNumber:
|
||||
icmpHeader := header.ICMPv6(pkt.TransportHeader().Slice())
|
||||
if len(icmpHeader) < header.ICMPv6MinimumSize {
|
||||
return nil, nil, false, false
|
||||
}
|
||||
|
||||
switch icmpType := icmpHeader.Type(); icmpType {
|
||||
case header.ICMPv6EchoRequest, header.ICMPv6EchoReply:
|
||||
return pkt.Network(), icmpHeader, false, true
|
||||
case header.ICMPv6DstUnreachable, header.ICMPv6PacketTooBig, header.ICMPv6TimeExceeded, header.ICMPv6ParamProblem:
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected ICMPv6 type = %d", icmpType))
|
||||
}
|
||||
|
||||
h, ok := pkt.Data().PullUp(header.IPv6MinimumSize)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("should have a valid IPv6 packet; only have %d bytes, want at least %d bytes", pkt.Data().Size(), header.IPv6MinimumSize))
|
||||
}
|
||||
|
||||
// We do not support extension headers in ICMP errors so the next header
|
||||
// in the IPv6 packet should be a tracked protocol if we reach this point.
|
||||
//
|
||||
// TODO(https://gvisor.dev/issue/6789): Support extension headers.
|
||||
transProto := pkt.tuple.tupleID.transProto
|
||||
if got := header.IPv6(h).TransportProtocol(); got != transProto {
|
||||
panic(fmt.Sprintf("got TransportProtocol() = %d, want = %d", got, transProto))
|
||||
}
|
||||
|
||||
if netHdr, transHdr, ok := getEmbeddedNetAndTransHeaders(pkt, header.IPv6MinimumSize, v6NetAndTransHdr, transProto); ok {
|
||||
return netHdr, transHdr, true, true
|
||||
}
|
||||
return nil, nil, false, false
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected transport protocol = %d", pkt.TransportProtocolNumber))
|
||||
}
|
||||
}
|
||||
|
||||
func getTupleIDForRegularPacket(netHdr header.Network, netProto tcpip.NetworkProtocolNumber, transHdr header.Transport, transProto tcpip.TransportProtocolNumber) tupleID {
|
||||
return tupleID{
|
||||
srcAddr: netHdr.SourceAddress(),
|
||||
@@ -376,7 +353,7 @@ func getTupleIDForRegularPacket(netHdr header.Network, netProto tcpip.NetworkPro
|
||||
}
|
||||
|
||||
func getTupleIDForPacketInICMPError(pkt *PacketBuffer, getNetAndTransHdr netAndTransHeadersFunc, netProto tcpip.NetworkProtocolNumber, netLen int, transProto tcpip.TransportProtocolNumber) (tupleID, bool) {
|
||||
if netHdr, transHdr, ok := getEmbeddedNetAndTransHeaders(pkt, netLen, getNetAndTransHdr, transProto); ok {
|
||||
if netHdr, transHdr, ok := pkt.GetEmbeddedNetAndTransHeaders(netLen, getNetAndTransHdr, transProto); ok {
|
||||
return tupleID{
|
||||
srcAddr: netHdr.DestinationAddress(),
|
||||
srcPortOrEchoRequestIdent: transHdr.DestinationPort(),
|
||||
@@ -602,6 +579,11 @@ func (ct *ConnTrack) getConnAndUpdate(pkt *PacketBuffer, skipChecksumValidation
|
||||
return t
|
||||
}
|
||||
|
||||
// GetConnAndUpdatePkt gets the connection for the packet and also sets the packet's tuple.
|
||||
func (ct *ConnTrack) GetConnAndUpdatePkt(pkt *PacketBuffer, skipChecksumValidation bool) {
|
||||
pkt.tuple = ct.getConnAndUpdate(pkt, skipChecksumValidation)
|
||||
}
|
||||
|
||||
func (ct *ConnTrack) connForTID(tid tupleID) *tuple {
|
||||
ct.mu.RLock()
|
||||
bkt := &ct.buckets[ct.bucket(tid)]
|
||||
@@ -610,6 +592,114 @@ func (ct *ConnTrack) connForTID(tid tupleID) *tuple {
|
||||
return bkt.connForTID(tid, ct.clock.NowMonotonic())
|
||||
}
|
||||
|
||||
// ConnTrackInfo holds connection tracking information for a packet.
|
||||
type ConnTrackInfo struct {
|
||||
State ConnTrackState
|
||||
Direction ConnTrackDirection
|
||||
SrcAddr tcpip.Address
|
||||
DstAddr tcpip.Address
|
||||
SrcPort uint16
|
||||
DstPort uint16
|
||||
NetProto tcpip.NetworkProtocolNumber
|
||||
TransProto tcpip.TransportProtocolNumber
|
||||
Expiration time.Duration
|
||||
PseudoID uint32
|
||||
Bytes uint64
|
||||
Packets uint64
|
||||
}
|
||||
|
||||
// ConnTrackInfoOpts holds options for GetConnTrackInfo.
|
||||
type ConnTrackInfoOpts struct {
|
||||
FillState bool
|
||||
UseReplyDir bool
|
||||
FillPseudoID bool
|
||||
FillExpiration bool
|
||||
}
|
||||
|
||||
// getTCPConnTrackState converts the TCB state to ConnTrackState.
|
||||
func (cn *conn) getTCPConnTrackState(useReplyDir bool) ConnTrackState {
|
||||
state := ConnTrackStateInvalid
|
||||
cn.stateMu.RLock()
|
||||
tcbState := cn.tcb.State()
|
||||
cn.stateMu.RUnlock()
|
||||
switch tcbState {
|
||||
case tcpconntrack.ResultConnecting:
|
||||
state = ConnTrackStateNew
|
||||
|
||||
case tcpconntrack.ResultAlive, tcpconntrack.ResultReset,
|
||||
tcpconntrack.ResultClosedByOriginator, tcpconntrack.ResultClosedByResponder:
|
||||
|
||||
if useReplyDir {
|
||||
state = ConnTrackStateEstablishedReply
|
||||
} else {
|
||||
state = ConnTrackStateEstablished
|
||||
}
|
||||
case tcpconntrack.ResultDrop:
|
||||
state = ConnTrackStateInvalid
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// getConnTrackState returns the connection tracking state for the connection.
|
||||
func (cn *conn) getConnTrackState(useReplyDir bool) ConnTrackState {
|
||||
state := ConnTrackStateInvalid
|
||||
// TCP connections have their own state machine in the TCB.
|
||||
if cn.original.tupleID.transProto == header.TCPProtocolNumber {
|
||||
return cn.getTCPConnTrackState(useReplyDir)
|
||||
}
|
||||
// For non-TCP connections, fill the info based on the reply.
|
||||
cn.stateMu.RLock()
|
||||
replySeen := cn.replySeen
|
||||
cn.stateMu.RUnlock()
|
||||
if useReplyDir {
|
||||
state = ConnTrackStateEstablishedReply
|
||||
} else if replySeen {
|
||||
state = ConnTrackStateEstablished
|
||||
} else {
|
||||
state = ConnTrackStateNew
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
// FillConnTrackInfo fills connection tracking information for the connection.
|
||||
func (cn *conn) FillConnTrackInfo(opts ConnTrackInfoOpts, info *ConnTrackInfo) bool {
|
||||
state := ConnTrackStateInvalid
|
||||
if opts.FillState {
|
||||
state = cn.getConnTrackState(opts.UseReplyDir)
|
||||
}
|
||||
|
||||
dir := ConnTrackDirectionOriginal
|
||||
t := &cn.original
|
||||
if opts.UseReplyDir {
|
||||
t = &cn.reply
|
||||
dir = ConnTrackDirectionReply
|
||||
}
|
||||
tID := t.tupleID
|
||||
|
||||
pID := uint32(0)
|
||||
if opts.FillPseudoID {
|
||||
// Generate a pseudo-ID similar to Linux nf_ct_get_id
|
||||
pID = tupleHash(cn.original.tupleID, cn.ct.nftIDSeed)
|
||||
}
|
||||
|
||||
var expires time.Duration
|
||||
if opts.FillExpiration {
|
||||
expires = cn.expiresIn()
|
||||
}
|
||||
|
||||
info.State = state
|
||||
info.Direction = dir
|
||||
info.SrcAddr = tID.srcAddr
|
||||
info.DstAddr = tID.dstAddr
|
||||
info.SrcPort = tID.srcPortOrEchoRequestIdent
|
||||
info.DstPort = tID.dstPortOrEchoReplyIdent
|
||||
info.NetProto = tID.netProto
|
||||
info.TransProto = tID.transProto
|
||||
info.Expiration = expires
|
||||
info.PseudoID = pID
|
||||
return true
|
||||
}
|
||||
|
||||
func (bkt *bucket) connForTID(tid tupleID, now tcpip.MonotonicTime) *tuple {
|
||||
bkt.mu.RLock()
|
||||
defer bkt.mu.RUnlock()
|
||||
@@ -697,325 +787,14 @@ func (cn *conn) finalize() bool {
|
||||
}
|
||||
}
|
||||
|
||||
// If NAT has not been configured for this connection, either mark the
|
||||
// connection as configured for "no-op NAT", in the case of DNAT, or, in the
|
||||
// case of SNAT, perform source port remapping so that source ports used by
|
||||
// locally-generated traffic do not conflict with ports occupied by existing NAT
|
||||
// bindings.
|
||||
//
|
||||
// Note that in the typical case this is also a no-op, because `snatAction`
|
||||
// will do nothing if the original tuple is already unique.
|
||||
func (cn *conn) maybePerformNoopNAT(pkt *PacketBuffer, hook Hook, r *Route, dnat bool) {
|
||||
cn.mu.Lock()
|
||||
var manip *manipType
|
||||
if dnat {
|
||||
manip = &cn.destinationManip
|
||||
} else {
|
||||
manip = &cn.sourceManip
|
||||
}
|
||||
if *manip != manipNotPerformed {
|
||||
cn.mu.Unlock()
|
||||
_ = cn.handlePacket(pkt, hook, r)
|
||||
return
|
||||
}
|
||||
if dnat {
|
||||
*manip = manipPerformedNoop
|
||||
cn.mu.Unlock()
|
||||
_ = cn.handlePacket(pkt, hook, r)
|
||||
return
|
||||
}
|
||||
cn.mu.Unlock()
|
||||
|
||||
// At this point, we know that NAT has not yet been performed on this
|
||||
// connection, and the DNAT case has been handled with a no-op. For SNAT, we
|
||||
// simply perform source port remapping to ensure that source ports for
|
||||
// locally generated traffic do not clash with ports used by existing NAT
|
||||
// bindings.
|
||||
_, _ = snatAction(pkt, hook, r, 0, tcpip.Address{}, true /* changePort */, false /* changeAddress */)
|
||||
}
|
||||
|
||||
type portOrIdentRange struct {
|
||||
start uint16
|
||||
size uint32
|
||||
}
|
||||
|
||||
// performNAT setups up the connection for the specified NAT and rewrites the
|
||||
// packet.
|
||||
//
|
||||
// If NAT has already been performed on the connection, then the packet will
|
||||
// be rewritten with the NAT performed on the connection, ignoring the passed
|
||||
// address and port range.
|
||||
//
|
||||
// Generally, only the first packet of a connection reaches this method; other
|
||||
// packets will be manipulated without needing to modify the connection.
|
||||
func (cn *conn) performNAT(pkt *PacketBuffer, hook Hook, r *Route, portsOrIdents portOrIdentRange, natAddress tcpip.Address, dnat, changePort, changeAddress bool) {
|
||||
lastPortOrIdent := func() uint16 {
|
||||
lastPortOrIdent := uint32(portsOrIdents.start) + portsOrIdents.size - 1
|
||||
if lastPortOrIdent > math.MaxUint16 {
|
||||
panic(fmt.Sprintf("got lastPortOrIdent = %d, want <= MaxUint16(=%d); portsOrIdents=%#v", lastPortOrIdent, math.MaxUint16, portsOrIdents))
|
||||
}
|
||||
return uint16(lastPortOrIdent)
|
||||
}()
|
||||
|
||||
// Make sure the packet is re-written after performing NAT.
|
||||
defer func() {
|
||||
// handlePacket returns true if the packet may skip the NAT table as the
|
||||
// connection is already NATed, but if we reach this point we must be in the
|
||||
// NAT table, so the return value is useless for us.
|
||||
_ = cn.handlePacket(pkt, hook, r)
|
||||
}()
|
||||
|
||||
cn.mu.Lock()
|
||||
defer cn.mu.Unlock()
|
||||
|
||||
var manip *manipType
|
||||
var address *tcpip.Address
|
||||
var portOrIdent *uint16
|
||||
if dnat {
|
||||
manip = &cn.destinationManip
|
||||
address = &cn.reply.tupleID.srcAddr
|
||||
portOrIdent = &cn.reply.tupleID.srcPortOrEchoRequestIdent
|
||||
} else {
|
||||
manip = &cn.sourceManip
|
||||
address = &cn.reply.tupleID.dstAddr
|
||||
portOrIdent = &cn.reply.tupleID.dstPortOrEchoReplyIdent
|
||||
}
|
||||
|
||||
if *manip != manipNotPerformed {
|
||||
return
|
||||
}
|
||||
*manip = manipPerformed
|
||||
if changeAddress {
|
||||
*address = natAddress
|
||||
}
|
||||
|
||||
// Everything below here is port-fiddling.
|
||||
if !changePort {
|
||||
return
|
||||
}
|
||||
|
||||
// Does the current port/ident fit in the range?
|
||||
if portsOrIdents.start <= *portOrIdent && *portOrIdent <= lastPortOrIdent {
|
||||
// Yes, is the current reply tuple unique?
|
||||
//
|
||||
// Or, does the reply tuple refer to the same connection as the current one that
|
||||
// we are NATing? This would apply, for example, to a self-connected socket,
|
||||
// where the original and reply tuples are identical.
|
||||
other := cn.ct.connForTID(cn.reply.tupleID)
|
||||
if other == nil || other.conn == cn {
|
||||
// Yes! No need to change the port.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Try our best to find a port/ident that results in a unique reply tuple.
|
||||
//
|
||||
// We limit the number of attempts to find a unique tuple to not waste a lot
|
||||
// of time looking for a unique tuple.
|
||||
//
|
||||
// Matches linux behaviour introduced in
|
||||
// https://github.com/torvalds/linux/commit/a504b703bb1da526a01593da0e4be2af9d9f5fa8.
|
||||
const maxAttemptsForInitialRound uint32 = 128
|
||||
const minAttemptsToContinue = 16
|
||||
|
||||
allowedInitialAttempts := maxAttemptsForInitialRound
|
||||
if allowedInitialAttempts > portsOrIdents.size {
|
||||
allowedInitialAttempts = portsOrIdents.size
|
||||
}
|
||||
|
||||
for maxAttempts := allowedInitialAttempts; ; maxAttempts /= 2 {
|
||||
// Start reach round with a random initial port/ident offset.
|
||||
randOffset := cn.ct.rand.Uint32()
|
||||
|
||||
for i := uint32(0); i < maxAttempts; i++ {
|
||||
newPortOrIdentU32 := uint32(portsOrIdents.start) + (randOffset+i)%portsOrIdents.size
|
||||
if newPortOrIdentU32 > math.MaxUint16 {
|
||||
panic(fmt.Sprintf("got newPortOrIdentU32 = %d, want <= MaxUint16(=%d); portsOrIdents=%#v, randOffset=%d", newPortOrIdentU32, math.MaxUint16, portsOrIdents, randOffset))
|
||||
}
|
||||
|
||||
*portOrIdent = uint16(newPortOrIdentU32)
|
||||
|
||||
if other := cn.ct.connForTID(cn.reply.tupleID); other == nil {
|
||||
// We found a unique tuple!
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if maxAttempts == portsOrIdents.size {
|
||||
// We already tried all the ports/idents in the range so no need to keep
|
||||
// trying.
|
||||
return
|
||||
}
|
||||
|
||||
if maxAttempts < minAttemptsToContinue {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// We did not find a unique tuple, use the last used port anyways.
|
||||
// TODO(https://gvisor.dev/issue/6850): Handle not finding a unique tuple
|
||||
// better (e.g. remove the connection and drop the packet).
|
||||
}
|
||||
|
||||
// handlePacket attempts to handle a packet and perform NAT if the connection
|
||||
// has had NAT performed on it.
|
||||
//
|
||||
// Returns true if the packet can skip the NAT table.
|
||||
func (cn *conn) handlePacket(pkt *PacketBuffer, hook Hook, rt *Route) bool {
|
||||
netHdr, transHdr, isICMPError, ok := getHeaders(pkt)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
fullChecksum := false
|
||||
updatePseudoHeader := false
|
||||
natDone := &pkt.snatDone
|
||||
dnat := false
|
||||
switch hook {
|
||||
case Prerouting:
|
||||
// Packet came from outside the stack so it must have a checksum set
|
||||
// already.
|
||||
fullChecksum = true
|
||||
updatePseudoHeader = true
|
||||
|
||||
natDone = &pkt.dnatDone
|
||||
dnat = true
|
||||
case Input:
|
||||
case Forward:
|
||||
panic("should not handle packet in the forwarding hook")
|
||||
case Output:
|
||||
natDone = &pkt.dnatDone
|
||||
dnat = true
|
||||
fallthrough
|
||||
case Postrouting:
|
||||
if pkt.TransportProtocolNumber == header.TCPProtocolNumber && pkt.GSOOptions.Type != GSONone && pkt.GSOOptions.NeedsCsum {
|
||||
updatePseudoHeader = true
|
||||
} else if rt.RequiresTXTransportChecksum() {
|
||||
fullChecksum = true
|
||||
updatePseudoHeader = true
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized hook = %d", hook))
|
||||
}
|
||||
|
||||
if *natDone {
|
||||
panic(fmt.Sprintf("packet already had NAT(dnat=%t) performed at hook=%s; pkt=%#v", dnat, hook, pkt))
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/5748): TCP checksums on inbound packets should be
|
||||
// validated if checksum offloading is off. It may require IP defrag if the
|
||||
// packets are fragmented.
|
||||
|
||||
reply := pkt.tuple.reply
|
||||
|
||||
tid, manip := func() (tupleID, manipType) {
|
||||
cn.mu.RLock()
|
||||
defer cn.mu.RUnlock()
|
||||
|
||||
if reply {
|
||||
tid := cn.original.tupleID
|
||||
|
||||
if dnat {
|
||||
return tid, cn.sourceManip
|
||||
}
|
||||
return tid, cn.destinationManip
|
||||
}
|
||||
|
||||
tid := cn.reply.tupleID
|
||||
if dnat {
|
||||
return tid, cn.destinationManip
|
||||
}
|
||||
return tid, cn.sourceManip
|
||||
}()
|
||||
switch manip {
|
||||
case manipNotPerformed:
|
||||
return false
|
||||
case manipPerformedNoop:
|
||||
*natDone = true
|
||||
return true
|
||||
case manipPerformed:
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled manip = %d", manip))
|
||||
}
|
||||
|
||||
newPort := tid.dstPortOrEchoReplyIdent
|
||||
newAddr := tid.dstAddr
|
||||
if dnat {
|
||||
newPort = tid.srcPortOrEchoRequestIdent
|
||||
newAddr = tid.srcAddr
|
||||
}
|
||||
|
||||
rewritePacket(
|
||||
netHdr,
|
||||
transHdr,
|
||||
!dnat != isICMPError,
|
||||
fullChecksum,
|
||||
updatePseudoHeader,
|
||||
newPort,
|
||||
newAddr,
|
||||
)
|
||||
|
||||
*natDone = true
|
||||
|
||||
if !isICMPError {
|
||||
return true
|
||||
}
|
||||
|
||||
// We performed NAT on (erroneous) packet that triggered an ICMP response, but
|
||||
// not the ICMP packet itself.
|
||||
switch pkt.TransportProtocolNumber {
|
||||
case header.ICMPv4ProtocolNumber:
|
||||
icmp := header.ICMPv4(pkt.TransportHeader().Slice())
|
||||
// TODO(https://gvisor.dev/issue/6788): Incrementally update ICMP checksum.
|
||||
icmp.SetChecksum(0)
|
||||
icmp.SetChecksum(header.ICMPv4Checksum(icmp, pkt.Data().Checksum()))
|
||||
|
||||
network := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
if dnat {
|
||||
network.SetDestinationAddressWithChecksumUpdate(tid.srcAddr)
|
||||
} else {
|
||||
network.SetSourceAddressWithChecksumUpdate(tid.dstAddr)
|
||||
}
|
||||
case header.ICMPv6ProtocolNumber:
|
||||
network := header.IPv6(pkt.NetworkHeader().Slice())
|
||||
srcAddr := network.SourceAddress()
|
||||
dstAddr := network.DestinationAddress()
|
||||
if dnat {
|
||||
dstAddr = tid.srcAddr
|
||||
} else {
|
||||
srcAddr = tid.dstAddr
|
||||
}
|
||||
|
||||
icmp := header.ICMPv6(pkt.TransportHeader().Slice())
|
||||
// TODO(https://gvisor.dev/issue/6788): Incrementally update ICMP checksum.
|
||||
icmp.SetChecksum(0)
|
||||
payload := pkt.Data()
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmp,
|
||||
Src: srcAddr,
|
||||
Dst: dstAddr,
|
||||
PayloadCsum: payload.Checksum(),
|
||||
PayloadLen: payload.Size(),
|
||||
}))
|
||||
|
||||
if dnat {
|
||||
network.SetDestinationAddress(dstAddr)
|
||||
} else {
|
||||
network.SetSourceAddress(srcAddr)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// bucket gets the conntrack bucket for a tupleID.
|
||||
// +checklocksread:ct.mu
|
||||
func (ct *ConnTrack) bucket(id tupleID) int {
|
||||
return ct.bucketWithTableLength(id, len(ct.buckets))
|
||||
}
|
||||
|
||||
func (ct *ConnTrack) bucketWithTableLength(id tupleID, tableLength int) int {
|
||||
h := jenkins.Sum32(ct.seed)
|
||||
func tupleHash(id tupleID, seed uint32) uint32 {
|
||||
h := jenkins.Sum32(seed)
|
||||
h.Write(id.srcAddr.AsSlice())
|
||||
h.Write(id.dstAddr.AsSlice())
|
||||
shortBuf := make([]byte, 2)
|
||||
@@ -1027,7 +806,12 @@ func (ct *ConnTrack) bucketWithTableLength(id tupleID, tableLength int) int {
|
||||
h.Write([]byte(shortBuf))
|
||||
binary.LittleEndian.PutUint16(shortBuf, uint16(id.netProto))
|
||||
h.Write([]byte(shortBuf))
|
||||
return int(h.Sum32()) % tableLength
|
||||
return h.Sum32()
|
||||
}
|
||||
|
||||
func (ct *ConnTrack) bucketWithTableLength(id tupleID, tableLength int) int {
|
||||
h := tupleHash(id, ct.seed)
|
||||
return int(h) % tableLength
|
||||
}
|
||||
|
||||
// reapUnused deletes timed out entries from the conntrack map. The rules for
|
||||
@@ -1167,3 +951,53 @@ func (ct *ConnTrack) originalDst(epID TransportEndpointID, netProto tcpip.Networ
|
||||
id := t.conn.original.tupleID
|
||||
return id.dstAddr, id.dstPortOrEchoReplyIdent, nil
|
||||
}
|
||||
|
||||
// NewConnTrack creates and initializes a new ConnTrack object.
|
||||
func NewConnTrack(clock tcpip.Clock, rng connTrackRNG, seed *uint32) *ConnTrack {
|
||||
if seed == nil {
|
||||
r := rng.Uint32()
|
||||
seed = &r
|
||||
}
|
||||
ct := &ConnTrack{
|
||||
clock: clock,
|
||||
rng: rng,
|
||||
seed: *seed,
|
||||
nftIDSeed: rng.Uint32(),
|
||||
}
|
||||
ct.init()
|
||||
return ct
|
||||
}
|
||||
|
||||
// NewConnTrackWithReaper creates and initializes a new ConnTrack and reaper.
|
||||
// Reaper garbage collects unused connections.
|
||||
func NewConnTrackWithReaper(clock tcpip.Clock, rng connTrackRNG, seed *uint32) (*ConnTrack, tcpip.Timer) {
|
||||
ct := NewConnTrack(clock, rng, seed)
|
||||
var reaper tcpip.Timer
|
||||
bucket := 0
|
||||
interval := 1 * time.Second
|
||||
reaper = ct.clock.AfterFunc(interval, func() {
|
||||
bucket, interval = ct.reapUnused(bucket, interval)
|
||||
reaper.Reset(interval)
|
||||
})
|
||||
return ct, reaper
|
||||
}
|
||||
|
||||
// NfConnTrackPriority returns the priority of the conntrack hook.
|
||||
// Check `ipv4/ipv6_conntrack_ops` in nf_conntrack_proto.c.
|
||||
func NfConnTrackPriority(hook NFHook) (int, bool) {
|
||||
switch hook {
|
||||
case NFPrerouting:
|
||||
// NF_IP_PRI_CONNTRACK
|
||||
return -200, true
|
||||
case NFInput:
|
||||
// NF_IP_PRI_CONNTRACK_CONFIRM
|
||||
return math.MaxInt32, true
|
||||
case NFPostrouting:
|
||||
// NF_IP_PRI_CONNTRACK_CONFIRM
|
||||
return math.MaxInt32, true
|
||||
case NFOutput:
|
||||
// NF_IP_PRI_CONNTRACK
|
||||
return -200, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
@@ -92,5 +92,5 @@ func endpointsByNICinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
endpointsByNICinitLockNames()
|
||||
endpointsByNICprefixIndex = locking.NewMutexClass(reflect.TypeOf(endpointsByNICRWMutex{}), endpointsByNIClockNames)
|
||||
endpointsByNICprefixIndex = locking.NewMutexClass(reflect.TypeFor[endpointsByNICRWMutex](), endpointsByNIClockNames)
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func _() {
|
||||
|
||||
const _headerType_name = "virtioNetHeaderlinkHeadernetworkHeadertransportHeadernumHeaderType"
|
||||
|
||||
var _headerType_index = [...]uint8{0, 10, 23, 38, 51}
|
||||
var _headerType_index = [...]uint8{0, 15, 25, 38, 53, 66}
|
||||
|
||||
func (i headerType) String() string {
|
||||
if i < 0 || i >= headerType(len(_headerType_index)-1) {
|
||||
|
||||
+13
-1
@@ -15,6 +15,8 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
)
|
||||
@@ -34,9 +36,15 @@ const (
|
||||
//
|
||||
// +stateify savable
|
||||
type ICMPRateLimiter struct {
|
||||
// TODO(b/341946753): Restore when netstack is savable.
|
||||
limiter *rate.Limiter `state:"nosave"`
|
||||
clock tcpip.Clock
|
||||
limit rate.Limit
|
||||
burst int
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (l *ICMPRateLimiter) afterLoad(context.Context) {
|
||||
l.limiter = rate.NewLimiter(l.limit, l.burst)
|
||||
}
|
||||
|
||||
// NewICMPRateLimiter returns a global rate limiter for controlling the rate
|
||||
@@ -46,11 +54,14 @@ func NewICMPRateLimiter(clock tcpip.Clock) *ICMPRateLimiter {
|
||||
return &ICMPRateLimiter{
|
||||
clock: clock,
|
||||
limiter: rate.NewLimiter(icmpLimit, icmpBurst),
|
||||
limit: icmpLimit,
|
||||
burst: icmpBurst,
|
||||
}
|
||||
}
|
||||
|
||||
// SetLimit sets a new Limit for the limiter.
|
||||
func (l *ICMPRateLimiter) SetLimit(limit rate.Limit) {
|
||||
l.limit = limit
|
||||
l.limiter.SetLimitAt(l.clock.Now(), limit)
|
||||
}
|
||||
|
||||
@@ -61,6 +72,7 @@ func (l *ICMPRateLimiter) Limit() rate.Limit {
|
||||
|
||||
// SetBurst sets a new burst size for the limiter.
|
||||
func (l *ICMPRateLimiter) SetBurst(burst int) {
|
||||
l.burst = burst
|
||||
l.limiter.SetBurstAt(l.clock.Now(), burst)
|
||||
}
|
||||
|
||||
|
||||
+85
-6
@@ -21,6 +21,7 @@ import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
@@ -33,6 +34,7 @@ const (
|
||||
NATID TableID = iota
|
||||
MangleID
|
||||
FilterID
|
||||
RawID
|
||||
NumTables
|
||||
)
|
||||
|
||||
@@ -111,6 +113,27 @@ func DefaultTables(clock tcpip.Clock, rand *rand.Rand) *IPTables {
|
||||
Postrouting: HookUnset,
|
||||
},
|
||||
},
|
||||
RawID: {
|
||||
Rules: []Rule{
|
||||
{Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
|
||||
{Filter: EmptyFilter4(), Target: &AcceptTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
|
||||
{Filter: EmptyFilter4(), Target: &ErrorTarget{NetworkProtocol: header.IPv4ProtocolNumber}},
|
||||
},
|
||||
BuiltinChains: [NumHooks]int{
|
||||
Prerouting: 0,
|
||||
Input: HookUnset,
|
||||
Forward: HookUnset,
|
||||
Output: 1,
|
||||
Postrouting: HookUnset,
|
||||
},
|
||||
Underflows: [NumHooks]int{
|
||||
Prerouting: 0,
|
||||
Input: HookUnset,
|
||||
Forward: HookUnset,
|
||||
Output: 1,
|
||||
Postrouting: HookUnset,
|
||||
},
|
||||
},
|
||||
},
|
||||
v6Tables: [NumTables]Table{
|
||||
NATID: {
|
||||
@@ -176,11 +199,32 @@ func DefaultTables(clock tcpip.Clock, rand *rand.Rand) *IPTables {
|
||||
Postrouting: HookUnset,
|
||||
},
|
||||
},
|
||||
RawID: {
|
||||
Rules: []Rule{
|
||||
{Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
|
||||
{Filter: EmptyFilter6(), Target: &AcceptTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
|
||||
{Filter: EmptyFilter6(), Target: &ErrorTarget{NetworkProtocol: header.IPv6ProtocolNumber}},
|
||||
},
|
||||
BuiltinChains: [NumHooks]int{
|
||||
Prerouting: 0,
|
||||
Input: HookUnset,
|
||||
Forward: HookUnset,
|
||||
Output: 1,
|
||||
Postrouting: HookUnset,
|
||||
},
|
||||
Underflows: [NumHooks]int{
|
||||
Prerouting: 0,
|
||||
Input: HookUnset,
|
||||
Forward: HookUnset,
|
||||
Output: 1,
|
||||
Postrouting: HookUnset,
|
||||
},
|
||||
},
|
||||
},
|
||||
connections: ConnTrack{
|
||||
seed: rand.Uint32(),
|
||||
clock: clock,
|
||||
rand: rand,
|
||||
rng: rand,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -215,6 +259,24 @@ func EmptyNATTable() Table {
|
||||
}
|
||||
}
|
||||
|
||||
// EmptyRawTable returns a Table with no rules and only the Prerouting and
|
||||
// Output hooks set, matching the Linux raw table's valid hooks.
|
||||
func EmptyRawTable() Table {
|
||||
return Table{
|
||||
Rules: []Rule{},
|
||||
BuiltinChains: [NumHooks]int{
|
||||
Input: HookUnset,
|
||||
Forward: HookUnset,
|
||||
Postrouting: HookUnset,
|
||||
},
|
||||
Underflows: [NumHooks]int{
|
||||
Input: HookUnset,
|
||||
Forward: HookUnset,
|
||||
Postrouting: HookUnset,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetTable returns a table with the given id and IP version. It panics when an
|
||||
// invalid id is provided.
|
||||
func (it *IPTables) GetTable(id TableID, ipv6 bool) Table {
|
||||
@@ -338,6 +400,10 @@ func (it *IPTables) shouldSkipOrPopulateTables(tables []checkTable, pkt *PacketB
|
||||
// +checkescape
|
||||
func (it *IPTables) CheckPrerouting(pkt *PacketBuffer, addressEP AddressableEndpoint, inNicName string) bool {
|
||||
tables := [...]checkTable{ // escapes: on arm this causes an allocation.
|
||||
{
|
||||
fn: check,
|
||||
tableID: RawID,
|
||||
},
|
||||
{
|
||||
fn: check,
|
||||
tableID: MangleID,
|
||||
@@ -448,6 +514,10 @@ func (it *IPTables) CheckForward(pkt *PacketBuffer, inNicName, outNicName string
|
||||
// +checkescape
|
||||
func (it *IPTables) CheckOutput(pkt *PacketBuffer, r *Route, outNicName string) bool {
|
||||
tables := [...]checkTable{ // escapes: on arm this causes an allocation.
|
||||
{
|
||||
fn: check,
|
||||
tableID: RawID,
|
||||
},
|
||||
{
|
||||
fn: check,
|
||||
tableID: MangleID,
|
||||
@@ -532,7 +602,7 @@ func checkNAT(it *IPTables, table Table, hook Hook, pkt *PacketBuffer, r *Route,
|
||||
// See check.
|
||||
func (it *IPTables) checkNAT(table Table, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool {
|
||||
t := pkt.tuple
|
||||
if t != nil && t.conn.handlePacket(pkt, hook, r) {
|
||||
if t != nil && IPTHandlePacket(pkt, hook, r) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -561,7 +631,7 @@ func (it *IPTables) checkNAT(table Table, hook Hook, pkt *PacketBuffer, r *Route
|
||||
//
|
||||
// If the packet was already NATed, the connection must be NATed.
|
||||
if !natDone {
|
||||
t.conn.maybePerformNoopNAT(pkt, hook, r, dnat)
|
||||
IPTMaybePerformNoopNAT(pkt, hook, r, dnat)
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -578,7 +648,7 @@ func check(it *IPTables, table Table, hook Hook, pkt *PacketBuffer, r *Route, ad
|
||||
// Precondition: The packet's network and transport header must be set.
|
||||
func (it *IPTables) check(table Table, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool {
|
||||
ruleIdx := table.BuiltinChains[hook]
|
||||
switch verdict := it.checkChain(hook, pkt, table, ruleIdx, r, addressEP, inNicName, outNicName); verdict {
|
||||
switch verdict := it.checkChain(hook, pkt, table, ruleIdx, r, addressEP, inNicName, outNicName, 0 /* depth */); verdict {
|
||||
// If the table returns Accept, move on to the next table.
|
||||
case chainAccept:
|
||||
return true
|
||||
@@ -626,10 +696,19 @@ func (it *IPTables) startReaper(interval time.Duration) {
|
||||
})
|
||||
}
|
||||
|
||||
// maxJumps is the maximum allowed depth of recursion when jumping between
|
||||
// chains in iptables. This prevents stack overflows from jump loops or deep
|
||||
// recursion. Linux kernel uses XT_JUMP_STACK_MAX = 16.
|
||||
const maxJumps = 16
|
||||
|
||||
// Preconditions:
|
||||
// - pkt is a IPv4 packet of at least length header.IPv4MinimumSize.
|
||||
// - pkt.NetworkHeader is not nil.
|
||||
func (it *IPTables) checkChain(hook Hook, pkt *PacketBuffer, table Table, ruleIdx int, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) chainVerdict {
|
||||
func (it *IPTables) checkChain(hook Hook, pkt *PacketBuffer, table Table, ruleIdx int, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string, depth int) chainVerdict {
|
||||
if depth >= maxJumps {
|
||||
log.Warningf("iptables: jump depth exceeded max %d", maxJumps)
|
||||
return chainDrop
|
||||
}
|
||||
// Start from ruleIdx and walk the list of rules until a rule gives us
|
||||
// a verdict.
|
||||
for ruleIdx < len(table.Rules) {
|
||||
@@ -650,7 +729,7 @@ func (it *IPTables) checkChain(hook Hook, pkt *PacketBuffer, table Table, ruleId
|
||||
ruleIdx++
|
||||
continue
|
||||
}
|
||||
switch verdict := it.checkChain(hook, pkt, table, jumpTo, r, addressEP, inNicName, outNicName); verdict {
|
||||
switch verdict := it.checkChain(hook, pkt, table, jumpTo, r, addressEP, inNicName, outNicName, depth+1); verdict {
|
||||
case chainAccept:
|
||||
return chainAccept
|
||||
case chainDrop:
|
||||
|
||||
@@ -92,5 +92,5 @@ func ipTablesinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
ipTablesinitLockNames()
|
||||
ipTablesprefixIndex = locking.NewMutexClass(reflect.TypeOf(ipTablesRWMutex{}), ipTableslockNames)
|
||||
ipTablesprefixIndex = locking.NewMutexClass(reflect.TypeFor[ipTablesRWMutex](), ipTableslockNames)
|
||||
}
|
||||
+36
-86
@@ -64,8 +64,11 @@ const (
|
||||
RejectIPv4WithICMPNetUnreachable
|
||||
RejectIPv4WithICMPHostUnreachable
|
||||
RejectIPv4WithICMPPortUnreachable
|
||||
RejectIPv4WithICMPProtUnreachable
|
||||
RejectIPv4WithICMPEchoReply
|
||||
RejectIPv4WithICMPNetProhibited
|
||||
RejectIPv4WithICMPHostProhibited
|
||||
RejectIPv4WithTCPReset
|
||||
RejectIPv4WithICMPAdminProhibited
|
||||
)
|
||||
|
||||
@@ -106,9 +109,14 @@ type RejectIPv6WithICMPType int
|
||||
const (
|
||||
_ RejectIPv6WithICMPType = iota
|
||||
RejectIPv6WithICMPNoRoute
|
||||
RejectIPv6WithICMPAdminProhibited
|
||||
RejectIPv6WithICMPNotNeighbour
|
||||
RejectIPv6WithICMPAddrUnreachable
|
||||
RejectIPv6WithICMPPortUnreachable
|
||||
RejectIPv6WithICMPAdminProhibited
|
||||
RejectIPv6WithICMPEchoReply
|
||||
RejectIPv6WithTCPReset
|
||||
RejectIPv6WithICMPPolicyFail
|
||||
RejectIPv6WithICMPRejectRoute
|
||||
)
|
||||
|
||||
// RejectIPv6Target drops packets and sends back an error packet in response to the
|
||||
@@ -163,7 +171,8 @@ type UserChainTarget struct {
|
||||
|
||||
// Action implements Target.Action.
|
||||
func (*UserChainTarget) Action(*PacketBuffer, Hook, *Route, AddressableEndpoint) (RuleVerdict, int) {
|
||||
panic("UserChainTarget should never be called.")
|
||||
log.BugTracebackOnce(fmt.Errorf("UserChainTarget should never be called"))
|
||||
return RuleDrop, 0
|
||||
}
|
||||
|
||||
// ReturnTarget returns from the current chain. If the chain is a built-in, the
|
||||
@@ -298,10 +307,10 @@ type SNATTarget struct {
|
||||
}
|
||||
|
||||
func dnatAction(pkt *PacketBuffer, hook Hook, r *Route, port uint16, address tcpip.Address, changePort, changeAddress bool) (RuleVerdict, int) {
|
||||
return natAction(pkt, hook, r, portOrIdentRange{start: port, size: 1}, address, true /* dnat */, changePort, changeAddress)
|
||||
return natAction(pkt, hook, r, PortOrIdentRange{Start: port, Size: 1}, address, true /* dnat */, changePort, changeAddress)
|
||||
}
|
||||
|
||||
func targetPortRangeForTCPAndUDP(originalSrcPort uint16) portOrIdentRange {
|
||||
func targetPortRangeForTCPAndUDP(originalSrcPort uint16) PortOrIdentRange {
|
||||
// As per iptables(8),
|
||||
//
|
||||
// If no port range is specified, then source ports below 512 will be
|
||||
@@ -310,16 +319,16 @@ func targetPortRangeForTCPAndUDP(originalSrcPort uint16) portOrIdentRange {
|
||||
// 1024 or above.
|
||||
switch {
|
||||
case originalSrcPort < 512:
|
||||
return portOrIdentRange{start: 1, size: 511}
|
||||
return PortOrIdentRange{Start: 1, Size: 511}
|
||||
case originalSrcPort < 1024:
|
||||
return portOrIdentRange{start: 1, size: 1023}
|
||||
return PortOrIdentRange{Start: 1, Size: 1023}
|
||||
default:
|
||||
return portOrIdentRange{start: 1024, size: math.MaxUint16 - 1023}
|
||||
return PortOrIdentRange{Start: 1024, Size: math.MaxUint16 - 1023}
|
||||
}
|
||||
}
|
||||
|
||||
func snatAction(pkt *PacketBuffer, hook Hook, r *Route, port uint16, address tcpip.Address, changePort, changeAddress bool) (RuleVerdict, int) {
|
||||
portsOrIdents := portOrIdentRange{start: port, size: 1}
|
||||
portsOrIdents := PortOrIdentRange{Start: port, Size: 1}
|
||||
|
||||
switch pkt.TransportProtocolNumber {
|
||||
case header.UDPProtocolNumber:
|
||||
@@ -335,20 +344,20 @@ func snatAction(pkt *PacketBuffer, hook Hook, r *Route, port uint16, address tcp
|
||||
// behaviour.
|
||||
//
|
||||
// https://github.com/torvalds/linux/blob/58e1100fdc5990b0cc0d4beaf2562a92e621ac7d/net/netfilter/nf_nat_core.c#L391
|
||||
portsOrIdents = portOrIdentRange{start: 0, size: math.MaxUint16 + 1}
|
||||
portsOrIdents = PortOrIdentRange{Start: 0, Size: math.MaxUint16 + 1}
|
||||
}
|
||||
|
||||
return natAction(pkt, hook, r, portsOrIdents, address, false /* dnat */, changePort, changeAddress)
|
||||
}
|
||||
|
||||
func natAction(pkt *PacketBuffer, hook Hook, r *Route, portsOrIdents portOrIdentRange, address tcpip.Address, dnat, changePort, changeAddress bool) (RuleVerdict, int) {
|
||||
func natAction(pkt *PacketBuffer, hook Hook, r *Route, portsOrIdents PortOrIdentRange, address tcpip.Address, dnat, changePort, changeAddress bool) (RuleVerdict, int) {
|
||||
// Drop the packet if network and transport header are not set.
|
||||
if len(pkt.NetworkHeader().Slice()) == 0 || len(pkt.TransportHeader().Slice()) == 0 {
|
||||
return RuleDrop, 0
|
||||
}
|
||||
|
||||
if t := pkt.tuple; t != nil {
|
||||
t.conn.performNAT(pkt, hook, r, portsOrIdents, address, dnat, changePort, changeAddress)
|
||||
IPTPerformNAT(pkt, hook, r, portsOrIdents, address, dnat, changePort, changeAddress)
|
||||
return RuleAccept, 0
|
||||
}
|
||||
|
||||
@@ -413,81 +422,22 @@ func (mt *MasqueradeTarget) Action(pkt *PacketBuffer, hook Hook, r *Route, addre
|
||||
return snatAction(pkt, hook, r, 0 /* port */, address, true /* changePort */, true /* changeAddress */)
|
||||
}
|
||||
|
||||
func rewritePacket(n header.Network, t header.Transport, updateSRCFields, fullChecksum, updatePseudoHeader bool, newPortOrIdent uint16, newAddr tcpip.Address) {
|
||||
switch t := t.(type) {
|
||||
case header.ChecksummableTransport:
|
||||
if updateSRCFields {
|
||||
if fullChecksum {
|
||||
t.SetSourcePortWithChecksumUpdate(newPortOrIdent)
|
||||
} else {
|
||||
t.SetSourcePort(newPortOrIdent)
|
||||
}
|
||||
} else {
|
||||
if fullChecksum {
|
||||
t.SetDestinationPortWithChecksumUpdate(newPortOrIdent)
|
||||
} else {
|
||||
t.SetDestinationPort(newPortOrIdent)
|
||||
}
|
||||
}
|
||||
// CTTarget is a no-op implementation of the CT (conntrack) target used in the
|
||||
// raw table. In Linux, CT --zone sets conntrack zones for connection tracking
|
||||
// isolation. gVisor's conntrack does not support zones, so this target simply
|
||||
// accepts the packet, allowing iptables-restore to load rulesets that reference
|
||||
// CT targets (e.g. Istio with DNS capture enabled).
|
||||
//
|
||||
// +stateify savable
|
||||
type CTTarget struct {
|
||||
// NetworkProtocol is the network protocol the target is used with.
|
||||
NetworkProtocol tcpip.NetworkProtocolNumber
|
||||
|
||||
if updatePseudoHeader {
|
||||
var oldAddr tcpip.Address
|
||||
if updateSRCFields {
|
||||
oldAddr = n.SourceAddress()
|
||||
} else {
|
||||
oldAddr = n.DestinationAddress()
|
||||
}
|
||||
// Zone is the conntrack zone ID. Stored but not acted upon.
|
||||
Zone uint16
|
||||
}
|
||||
|
||||
t.UpdateChecksumPseudoHeaderAddress(oldAddr, newAddr, fullChecksum)
|
||||
}
|
||||
case header.ICMPv4:
|
||||
switch icmpType := t.Type(); icmpType {
|
||||
case header.ICMPv4Echo:
|
||||
if updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPortOrIdent)
|
||||
}
|
||||
case header.ICMPv4EchoReply:
|
||||
if !updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPortOrIdent)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected ICMPv4 type = %d", icmpType))
|
||||
}
|
||||
case header.ICMPv6:
|
||||
switch icmpType := t.Type(); icmpType {
|
||||
case header.ICMPv6EchoRequest:
|
||||
if updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPortOrIdent)
|
||||
}
|
||||
case header.ICMPv6EchoReply:
|
||||
if !updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPortOrIdent)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected ICMPv4 type = %d", icmpType))
|
||||
}
|
||||
|
||||
var oldAddr tcpip.Address
|
||||
if updateSRCFields {
|
||||
oldAddr = n.SourceAddress()
|
||||
} else {
|
||||
oldAddr = n.DestinationAddress()
|
||||
}
|
||||
|
||||
t.UpdateChecksumPseudoHeaderAddress(oldAddr, newAddr)
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled transport = %#v", t))
|
||||
}
|
||||
|
||||
if checksummableNetHeader, ok := n.(header.ChecksummableNetwork); ok {
|
||||
if updateSRCFields {
|
||||
checksummableNetHeader.SetSourceAddressWithChecksumUpdate(newAddr)
|
||||
} else {
|
||||
checksummableNetHeader.SetDestinationAddressWithChecksumUpdate(newAddr)
|
||||
}
|
||||
} else if updateSRCFields {
|
||||
n.SetSourceAddress(newAddr)
|
||||
} else {
|
||||
n.SetDestinationAddress(newAddr)
|
||||
}
|
||||
// Action implements Target.Action. It is a no-op that accepts the packet.
|
||||
func (*CTTarget) Action(*PacketBuffer, Hook, *Route, AddressableEndpoint) (RuleVerdict, int) {
|
||||
return RuleAccept, 0
|
||||
}
|
||||
@@ -82,7 +82,7 @@ const (
|
||||
type IPTables struct {
|
||||
connections ConnTrack
|
||||
|
||||
reaper tcpip.Timer
|
||||
reaper tcpip.Timer `state:"nosave"`
|
||||
|
||||
mu ipTablesRWMutex `state:"nosave"`
|
||||
// v4Tables and v6tables map tableIDs to tables. They hold builtin
|
||||
@@ -283,7 +283,9 @@ func (fl IPHeaderFilter) match(pkt *PacketBuffer, hook Hook, inNicName, outNicNa
|
||||
|
||||
case header.IPv6ProtocolNumber:
|
||||
hdr := header.IPv6(pkt.NetworkHeader().Slice())
|
||||
transProto = hdr.TransportProtocol()
|
||||
// The transport protocol may be preceded by IPv6 extension headers, so
|
||||
// use the protocol from parsing (see IPv6.TransportProtocol).
|
||||
transProto = pkt.TransportProtocolNumber
|
||||
dstAddr = hdr.DestinationAddress()
|
||||
srcAddr = hdr.SourceAddress()
|
||||
|
||||
@@ -305,7 +307,7 @@ func (fl IPHeaderFilter) match(pkt *PacketBuffer, hook Hook, inNicName, outNicNa
|
||||
switch hook {
|
||||
case Prerouting, Input:
|
||||
return matchIfName(inNicName, fl.InputInterface, fl.InputInterfaceInvert)
|
||||
case Output:
|
||||
case Postrouting, Output:
|
||||
return matchIfName(outNicName, fl.OutputInterface, fl.OutputInterfaceInvert)
|
||||
case Forward:
|
||||
if !matchIfName(inNicName, fl.InputInterface, fl.InputInterfaceInvert) {
|
||||
@@ -316,8 +318,6 @@ func (fl IPHeaderFilter) match(pkt *PacketBuffer, hook Hook, inNicName, outNicNa
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
case Postrouting:
|
||||
return true
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown hook: %d", hook))
|
||||
|
||||
@@ -92,5 +92,5 @@ func multiPortEndpointinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
multiPortEndpointinitLockNames()
|
||||
multiPortEndpointprefixIndex = locking.NewMutexClass(reflect.TypeOf(multiPortEndpointRWMutex{}), multiPortEndpointlockNames)
|
||||
multiPortEndpointprefixIndex = locking.NewMutexClass(reflect.TypeFor[multiPortEndpointRWMutex](), multiPortEndpointlockNames)
|
||||
}
|
||||
+584
@@ -0,0 +1,584 @@
|
||||
// Copyright 2020 The 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 stack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
|
||||
// NATType represents the type of NAT.
|
||||
type NATType int
|
||||
|
||||
const (
|
||||
// SNAT is source NAT.
|
||||
SNAT NATType = iota
|
||||
// DNAT is destination NAT.
|
||||
DNAT
|
||||
// NATUnknown is unknown NAT type.
|
||||
NATUnknown
|
||||
)
|
||||
|
||||
// ToNATType converts a uint8 to a NATType.
|
||||
func ToNATType(t uint8) NATType {
|
||||
switch t {
|
||||
case 0:
|
||||
return SNAT
|
||||
case 1:
|
||||
return DNAT
|
||||
}
|
||||
return NATUnknown
|
||||
}
|
||||
|
||||
func (natType NATType) String() string {
|
||||
switch natType {
|
||||
case SNAT:
|
||||
return "SNAT"
|
||||
case DNAT:
|
||||
return "DNAT"
|
||||
default:
|
||||
return "NATUnknown"
|
||||
}
|
||||
}
|
||||
|
||||
// NfNATPriority returns the priority of the NAT hook.
|
||||
// Check `ipv4/ipv6_nat_ops` in nf_nat_proto.c.
|
||||
func NfNATPriority(hook NFHook) (int, bool) {
|
||||
switch hook {
|
||||
case NFPrerouting:
|
||||
// NF_IP_PRI_NAT_DST
|
||||
return -100, true
|
||||
case NFPostrouting:
|
||||
// NF_IP_PRI_NAT_SRC
|
||||
return 100, true
|
||||
case NFOutput:
|
||||
// NF_IP_PRI_NAT_DST
|
||||
return -100, true
|
||||
case NFInput:
|
||||
// NF_IP_PRI_NAT_SRC
|
||||
return 100, true
|
||||
}
|
||||
// NAT is not supported for other hooks.
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// NfHookToNATType returns the applicable NAT type
|
||||
// for the given netfilter hook.
|
||||
func NfHookToNATType(hook NFHook) NATType {
|
||||
switch hook {
|
||||
case NFPrerouting, NFOutput:
|
||||
return DNAT
|
||||
case NFInput, NFPostrouting:
|
||||
return SNAT
|
||||
}
|
||||
return NATUnknown
|
||||
}
|
||||
|
||||
// handlePacketOpts contains the options for handlePacket.
|
||||
type handlePacketOpts struct {
|
||||
fullChecksum bool
|
||||
updatePseudoHeader bool
|
||||
natType NATType
|
||||
}
|
||||
|
||||
// handlePacket attempts to handle a packet and perform NAT if the connection
|
||||
// has had NAT performed on it.
|
||||
//
|
||||
// Returns true if the packet can skip the NAT table.
|
||||
func handlePacket(pkt *PacketBuffer, opts *handlePacketOpts) bool {
|
||||
if opts == nil || opts.natType == NATUnknown {
|
||||
return false
|
||||
}
|
||||
netHdr, transHdr, isICMPError, ok := pkt.GetHeaders()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
natDone := &pkt.snatDone
|
||||
dnat := false
|
||||
if opts.natType == DNAT {
|
||||
natDone = &pkt.dnatDone
|
||||
dnat = true
|
||||
}
|
||||
|
||||
if *natDone {
|
||||
panic(fmt.Sprintf("packet already had NAT: %s performed; pkt=%#v", opts.natType, pkt))
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/5748): TCP checksums on inbound packets should be
|
||||
// validated if checksum offloading is off. It may require IP defrag if the
|
||||
// packets are fragmented.
|
||||
|
||||
reply := pkt.tuple.reply
|
||||
cn := pkt.tuple.conn
|
||||
|
||||
tid, manip := func() (tupleID, manipType) {
|
||||
cn.mu.RLock()
|
||||
defer cn.mu.RUnlock()
|
||||
|
||||
if reply {
|
||||
tid := cn.original.tupleID
|
||||
|
||||
if dnat {
|
||||
return tid, cn.sourceManip
|
||||
}
|
||||
return tid, cn.destinationManip
|
||||
}
|
||||
|
||||
tid := cn.reply.tupleID
|
||||
if dnat {
|
||||
return tid, cn.destinationManip
|
||||
}
|
||||
return tid, cn.sourceManip
|
||||
}()
|
||||
switch manip {
|
||||
case manipNotPerformed:
|
||||
return false
|
||||
case manipPerformedNoop:
|
||||
*natDone = true
|
||||
return true
|
||||
case manipPerformed:
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled manip = %d", manip))
|
||||
}
|
||||
|
||||
newPort := tid.dstPortOrEchoReplyIdent
|
||||
newAddr := tid.dstAddr
|
||||
if dnat {
|
||||
newPort = tid.srcPortOrEchoRequestIdent
|
||||
newAddr = tid.srcAddr
|
||||
}
|
||||
|
||||
UpdateHeaders(
|
||||
netHdr,
|
||||
transHdr,
|
||||
!dnat != isICMPError,
|
||||
opts.fullChecksum,
|
||||
opts.updatePseudoHeader,
|
||||
newPort,
|
||||
newAddr,
|
||||
)
|
||||
|
||||
*natDone = true
|
||||
|
||||
if !isICMPError {
|
||||
return true
|
||||
}
|
||||
|
||||
// We performed NAT on (erroneous) packet that triggered an ICMP response, but
|
||||
// not the ICMP packet itself.
|
||||
switch pkt.TransportProtocolNumber {
|
||||
case header.ICMPv4ProtocolNumber:
|
||||
icmp := header.ICMPv4(pkt.TransportHeader().Slice())
|
||||
// TODO(https://gvisor.dev/issue/6788): Incrementally update ICMP checksum.
|
||||
icmp.SetChecksum(0)
|
||||
icmp.SetChecksum(header.ICMPv4Checksum(icmp, pkt.Data().Checksum()))
|
||||
|
||||
network := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
if dnat {
|
||||
network.SetDestinationAddressWithChecksumUpdate(tid.srcAddr)
|
||||
} else {
|
||||
network.SetSourceAddressWithChecksumUpdate(tid.dstAddr)
|
||||
}
|
||||
case header.ICMPv6ProtocolNumber:
|
||||
network := header.IPv6(pkt.NetworkHeader().Slice())
|
||||
srcAddr := network.SourceAddress()
|
||||
dstAddr := network.DestinationAddress()
|
||||
if dnat {
|
||||
dstAddr = tid.srcAddr
|
||||
} else {
|
||||
srcAddr = tid.dstAddr
|
||||
}
|
||||
|
||||
icmp := header.ICMPv6(pkt.TransportHeader().Slice())
|
||||
// TODO(https://gvisor.dev/issue/6788): Incrementally update ICMP checksum.
|
||||
icmp.SetChecksum(0)
|
||||
payload := pkt.Data()
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmp,
|
||||
Src: srcAddr,
|
||||
Dst: dstAddr,
|
||||
PayloadCsum: payload.Checksum(),
|
||||
PayloadLen: payload.Size(),
|
||||
}))
|
||||
|
||||
if dnat {
|
||||
network.SetDestinationAddress(dstAddr)
|
||||
} else {
|
||||
network.SetSourceAddress(srcAddr)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IPTHandlePacket handles and applies NAT to the packet if required.
|
||||
func IPTHandlePacket(pkt *PacketBuffer, hook Hook, r *Route) bool {
|
||||
opts := handlePacketOpts{
|
||||
fullChecksum: false,
|
||||
updatePseudoHeader: false,
|
||||
natType: SNAT,
|
||||
}
|
||||
requiresTXTransportChecksum := false
|
||||
if r != nil {
|
||||
requiresTXTransportChecksum = r.RequiresTXTransportChecksum()
|
||||
}
|
||||
switch hook {
|
||||
case Prerouting:
|
||||
opts.fullChecksum = true
|
||||
opts.updatePseudoHeader = true
|
||||
opts.natType = DNAT
|
||||
case Input:
|
||||
case Forward:
|
||||
panic("should not handle packet in the forwarding hook")
|
||||
case Output:
|
||||
opts.natType = DNAT
|
||||
fallthrough
|
||||
case Postrouting:
|
||||
if pkt.TransportProtocolNumber == header.TCPProtocolNumber && pkt.GSOOptions.Type != GSONone && pkt.GSOOptions.NeedsCsum {
|
||||
opts.updatePseudoHeader = true
|
||||
} else if requiresTXTransportChecksum {
|
||||
opts.fullChecksum = true
|
||||
opts.updatePseudoHeader = true
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized hook = %d", hook))
|
||||
}
|
||||
|
||||
return handlePacket(pkt, &opts)
|
||||
}
|
||||
|
||||
// PortOrIdentRange represents a range of ports or idents
|
||||
// range to use for NAT.
|
||||
type PortOrIdentRange struct {
|
||||
Start uint16
|
||||
Size uint32
|
||||
}
|
||||
|
||||
// ConfigureNAT setups up the connection for the specified NAT and rewrites the
|
||||
// packet.
|
||||
//
|
||||
// If NAT has already been performed on the connection, then the packet will
|
||||
// be rewritten with the NAT performed on the connection, ignoring the passed
|
||||
// address and port range.
|
||||
//
|
||||
// Generally, only the first packet of a connection reaches this method; other
|
||||
// packets will be manipulated without needing to modify the connection.
|
||||
//
|
||||
// Returns whether the NAT was configured or not.
|
||||
func (cn *conn) ConfigureNAT(portsOrIdents PortOrIdentRange, natAddress tcpip.Address, natType NATType, changePort, changeAddress bool) bool {
|
||||
lastPortOrIdentU32 := uint32(portsOrIdents.Start) + portsOrIdents.Size - 1
|
||||
if lastPortOrIdentU32 > math.MaxUint16 {
|
||||
log.Warningf("got lastPortOrIdent = %d, want <= MaxUint16(=%d); portsOrIdents=%#v", lastPortOrIdentU32, math.MaxUint16, portsOrIdents)
|
||||
return false
|
||||
}
|
||||
lastPortOrIdent := uint16(lastPortOrIdentU32)
|
||||
|
||||
cn.mu.Lock()
|
||||
defer cn.mu.Unlock()
|
||||
|
||||
var manip *manipType
|
||||
var address *tcpip.Address
|
||||
var portOrIdent *uint16
|
||||
if natType == DNAT {
|
||||
manip = &cn.destinationManip
|
||||
address = &cn.reply.tupleID.srcAddr
|
||||
portOrIdent = &cn.reply.tupleID.srcPortOrEchoRequestIdent
|
||||
} else {
|
||||
manip = &cn.sourceManip
|
||||
address = &cn.reply.tupleID.dstAddr
|
||||
portOrIdent = &cn.reply.tupleID.dstPortOrEchoReplyIdent
|
||||
}
|
||||
|
||||
if *manip != manipNotPerformed {
|
||||
return true
|
||||
}
|
||||
*manip = manipPerformed
|
||||
if changeAddress {
|
||||
*address = natAddress
|
||||
}
|
||||
|
||||
// Everything below here is port-fiddling.
|
||||
// TODO: b/486197011 - Handle reply tuple collisions when changePort
|
||||
// is false. Check for tuple uniqueness or find a unique tuple.
|
||||
// Ref: net/netfilter/nf_nat_core.c:nf_nat_l4proto_unique_tuple.
|
||||
if !changePort {
|
||||
return true
|
||||
}
|
||||
|
||||
// Does the current port/ident fit in the range?
|
||||
if portsOrIdents.Start <= *portOrIdent && *portOrIdent <= lastPortOrIdent {
|
||||
// Yes, is the current reply tuple unique?
|
||||
//
|
||||
// Or, does the reply tuple refer to the same connection as the current one that
|
||||
// we are NATing? This would apply, for example, to a self-connected socket,
|
||||
// where the original and reply tuples are identical.
|
||||
other := cn.ct.connForTID(cn.reply.tupleID)
|
||||
if other == nil || other.conn == cn {
|
||||
// Yes! No need to change the port.
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Try our best to find a port/ident that results in a unique reply tuple.
|
||||
//
|
||||
// We limit the number of attempts to find a unique tuple to not waste a lot
|
||||
// of time looking for a unique tuple.
|
||||
//
|
||||
// Matches linux behaviour introduced in
|
||||
// https://github.com/torvalds/linux/commit/a504b703bb1da526a01593da0e4be2af9d9f5fa8.
|
||||
const maxAttemptsForInitialRound uint32 = 128
|
||||
const minAttemptsToContinue = 16
|
||||
|
||||
allowedInitialAttempts := maxAttemptsForInitialRound
|
||||
if allowedInitialAttempts > portsOrIdents.Size {
|
||||
allowedInitialAttempts = portsOrIdents.Size
|
||||
}
|
||||
|
||||
for maxAttempts := allowedInitialAttempts; ; maxAttempts /= 2 {
|
||||
// Start reach round with a random initial port/ident offset.
|
||||
randOffset := cn.ct.rng.Uint32()
|
||||
|
||||
for i := uint32(0); i < maxAttempts; i++ {
|
||||
newPortOrIdentU32 := uint32(portsOrIdents.Start) + (randOffset+i)%portsOrIdents.Size
|
||||
if newPortOrIdentU32 > math.MaxUint16 {
|
||||
log.Warningf("got newPortOrIdentU32 = %d, want <= MaxUint16(=%d); portsOrIdents=%#v", newPortOrIdentU32, math.MaxUint16, portsOrIdents)
|
||||
continue
|
||||
}
|
||||
|
||||
*portOrIdent = uint16(newPortOrIdentU32)
|
||||
|
||||
if other := cn.ct.connForTID(cn.reply.tupleID); other == nil {
|
||||
// We found a unique tuple!
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if maxAttempts == portsOrIdents.Size {
|
||||
// We already tried all the ports/idents in the range so no need to keep
|
||||
// trying.
|
||||
return false
|
||||
}
|
||||
|
||||
if maxAttempts < minAttemptsToContinue {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// We did not find a unique tuple, use the last used port anyways.
|
||||
// TODO(https://gvisor.dev/issue/6850): Handle not finding a unique tuple
|
||||
// better (e.g. remove the connection and drop the packet).
|
||||
}
|
||||
|
||||
// IPTPerformNAT performs NAT on the packet and updates the connection.
|
||||
// Used by IPTables.
|
||||
func IPTPerformNAT(pkt *PacketBuffer, hook Hook, r *Route, portsOrIdents PortOrIdentRange, natAddress tcpip.Address, dnat, changePort, changeAddress bool) {
|
||||
// Make sure the packet is re-written after performing NAT.
|
||||
defer func() {
|
||||
// handlePacket returns true if the packet may skip the NAT table as the
|
||||
// connection is already NATed, but if we reach this point we must be in the
|
||||
// NAT table, so the return value is useless for us.
|
||||
_ = IPTHandlePacket(pkt, hook, r)
|
||||
}()
|
||||
cn := pkt.tuple.conn
|
||||
natType := SNAT
|
||||
if dnat {
|
||||
natType = DNAT
|
||||
}
|
||||
_ = cn.ConfigureNAT(portsOrIdents, natAddress, natType, changePort, changeAddress)
|
||||
}
|
||||
|
||||
// IPTMaybePerformNoopNAT can apply NAT or configure a no-op NAT.
|
||||
// If NAT has not been configured for this connection, either mark the
|
||||
// connection as configured for "no-op NAT", in the case of DNAT, or, in the
|
||||
// case of SNAT, perform source port remapping so that source ports used by
|
||||
// locally-generated traffic do not conflict with ports occupied by existing NAT
|
||||
// bindings.
|
||||
//
|
||||
// Note that in the typical case this is also a no-op, because `snatAction`
|
||||
// will do nothing if the original tuple is already unique.
|
||||
func IPTMaybePerformNoopNAT(pkt *PacketBuffer, hook Hook, r *Route, dnat bool) {
|
||||
cn := pkt.tuple.conn
|
||||
cn.mu.Lock()
|
||||
var manip *manipType
|
||||
if dnat {
|
||||
manip = &cn.destinationManip
|
||||
} else {
|
||||
manip = &cn.sourceManip
|
||||
}
|
||||
if *manip != manipNotPerformed {
|
||||
cn.mu.Unlock()
|
||||
_ = IPTHandlePacket(pkt, hook, r)
|
||||
return
|
||||
}
|
||||
if dnat {
|
||||
*manip = manipPerformedNoop
|
||||
cn.mu.Unlock()
|
||||
_ = IPTHandlePacket(pkt, hook, r)
|
||||
return
|
||||
}
|
||||
cn.mu.Unlock()
|
||||
|
||||
// At this point, we know that NAT has not yet been performed on this
|
||||
// connection, and the DNAT case has been handled with a no-op. For SNAT, we
|
||||
// simply perform source port remapping to ensure that source ports for
|
||||
// locally generated traffic do not clash with ports used by existing NAT
|
||||
// bindings.
|
||||
_, _ = snatAction(pkt, hook, r, 0, tcpip.Address{}, true /* changePort */, false /* changeAddress */)
|
||||
}
|
||||
|
||||
// NFTApplyNAT applies NAT to the packet and updates the connection.
|
||||
// Similar to IPTHandlePacket but for NFTables hooks.
|
||||
func NFTApplyNAT(pkt *PacketBuffer, hook NFHook, rt *Route) bool {
|
||||
requiresTXTransportChecksum := false
|
||||
if rt != nil {
|
||||
requiresTXTransportChecksum = rt.RequiresTXTransportChecksum()
|
||||
}
|
||||
opts := handlePacketOpts{
|
||||
fullChecksum: false,
|
||||
updatePseudoHeader: false,
|
||||
natType: SNAT,
|
||||
}
|
||||
switch hook {
|
||||
case NFPrerouting:
|
||||
opts.fullChecksum = true
|
||||
opts.updatePseudoHeader = true
|
||||
opts.natType = DNAT
|
||||
case NFInput:
|
||||
case NFForward:
|
||||
panic("should not handle packet in the forwarding hook")
|
||||
case NFOutput:
|
||||
opts.natType = DNAT
|
||||
fallthrough
|
||||
case NFPostrouting:
|
||||
if pkt.TransportProtocolNumber == header.TCPProtocolNumber && pkt.GSOOptions.Type != GSONone && pkt.GSOOptions.NeedsCsum {
|
||||
opts.updatePseudoHeader = true
|
||||
} else if requiresTXTransportChecksum {
|
||||
opts.fullChecksum = true
|
||||
opts.updatePseudoHeader = true
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized hook = %d", hook))
|
||||
}
|
||||
|
||||
return handlePacket(pkt, &opts)
|
||||
}
|
||||
|
||||
// IsNATConfigured returns whether NAT has been configured for the given NAT type.
|
||||
func (cn *conn) IsNATConfigured(natType NATType) bool {
|
||||
cn.mu.RLock()
|
||||
defer cn.mu.RUnlock()
|
||||
switch natType {
|
||||
case SNAT:
|
||||
return cn.sourceManip != manipNotPerformed
|
||||
case DNAT:
|
||||
return cn.destinationManip != manipNotPerformed
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ConfigureNoopNAT configures the connection for no-op NAT.
|
||||
// Similar to the func `IPTMaybePerformNoopNAT` except that this one only configures NO-OP NAT and is independent of IPTables.
|
||||
func (cn *conn) ConfigureNoopNAT(pkt *PacketBuffer, natType NATType) bool {
|
||||
cn.mu.Lock()
|
||||
var manip *manipType
|
||||
if natType == DNAT {
|
||||
manip = &cn.destinationManip
|
||||
} else {
|
||||
manip = &cn.sourceManip
|
||||
}
|
||||
|
||||
if *manip != manipNotPerformed {
|
||||
cn.mu.Unlock()
|
||||
return true
|
||||
}
|
||||
|
||||
if natType == DNAT {
|
||||
*manip = manipPerformedNoop
|
||||
cn.mu.Unlock()
|
||||
return true
|
||||
}
|
||||
cn.mu.Unlock()
|
||||
|
||||
// At this point, we know that NAT has not yet been performed on this
|
||||
// connection, and the DNAT case has been handled with a no-op. For SNAT, we
|
||||
// simply perform source port remapping to ensure that source ports for
|
||||
// locally generated traffic do not clash with ports used by existing NAT
|
||||
// bindings.
|
||||
|
||||
portsOrIdents := PortOrIdentRange{Start: 0, Size: math.MaxUint16 + 1}
|
||||
|
||||
// However, we need to extract the port from packet.
|
||||
var port uint16
|
||||
switch pkt.TransportProtocolNumber {
|
||||
case header.UDPProtocolNumber:
|
||||
port = header.UDP(pkt.TransportHeader().Slice()).SourcePort()
|
||||
case header.TCPProtocolNumber:
|
||||
port = header.TCP(pkt.TransportHeader().Slice()).SourcePort()
|
||||
}
|
||||
|
||||
if port != 0 {
|
||||
portsOrIdents = targetPortRangeForTCPAndUDP(port)
|
||||
}
|
||||
|
||||
return cn.ConfigureNAT(portsOrIdents, tcpip.Address{}, natType, true /* changePort */, false /* changeAddress */)
|
||||
}
|
||||
|
||||
// ConfigureMasquerade configures the connection for masquerade.
|
||||
func (cn *conn) configureMasquerade(pkt *PacketBuffer, route *Route, stk *Stack, ports PortOrIdentRange, changePort bool) bool {
|
||||
srcAddr := pkt.Network().SourceAddress()
|
||||
if srcAddr == header.IPv4Any || srcAddr == header.IPv6Any {
|
||||
return false
|
||||
}
|
||||
// Masquerade is only supported for postrouting.
|
||||
if route == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Get the network endpoint for the outgoing interface to find its primary address.
|
||||
netEP, err := stk.GetNetworkEndpoint(route.NICID(), route.NetProto())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
addressEP, ok := netEP.(AddressableEndpoint)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Ref: net/netfilter/nf_nat_masquerade.c:nf_nat_masquerade_ipv[4|6]()
|
||||
// Use the next hop address as the destination address if it is set.
|
||||
nh := route.NextHop()
|
||||
if nh.Len() == 0 {
|
||||
nh = pkt.Network().DestinationAddress()
|
||||
}
|
||||
|
||||
// addressEP is expected to be set for the postrouting hook.
|
||||
// Find the outgoing primary address for the destination address.
|
||||
ep := addressEP.AcquireOutgoingPrimaryAddress(nh, tcpip.Address{} /* srcHint */, false /* allowExpired */)
|
||||
if ep == nil {
|
||||
// No address exists that we can use as a source address.
|
||||
return false
|
||||
}
|
||||
address := ep.AddressWithPrefix().Address
|
||||
ep.DecRef()
|
||||
|
||||
// Configure NAT for the packet to change the source address.
|
||||
return cn.ConfigureNAT(ports, address, SNAT, changePort, true /* changeAddress */)
|
||||
}
|
||||
@@ -45,7 +45,9 @@ type dynamicCacheEntry struct {
|
||||
type neighborCacheMu struct {
|
||||
neighborCacheRWMutex `state:"nosave"`
|
||||
|
||||
cache map[tcpip.Address]*neighborEntry
|
||||
// +checklocks:neighborCacheRWMutex
|
||||
cache map[tcpip.Address]*neighborEntry
|
||||
// +checklocks:neighborCacheRWMutex
|
||||
dynamic dynamicCacheEntry
|
||||
}
|
||||
|
||||
|
||||
@@ -92,5 +92,5 @@ func neighborCacheinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
neighborCacheinitLockNames()
|
||||
neighborCacheprefixIndex = locking.NewMutexClass(reflect.TypeOf(neighborCacheRWMutex{}), neighborCachelockNames)
|
||||
neighborCacheprefixIndex = locking.NewMutexClass(reflect.TypeFor[neighborCacheRWMutex](), neighborCachelockNames)
|
||||
}
|
||||
+17
-12
@@ -90,17 +90,22 @@ type timer struct {
|
||||
type neighborEntryMu struct {
|
||||
neighborEntryRWMutex `state:"nosave"`
|
||||
|
||||
// +checklocks:neighborEntryRWMutex
|
||||
neigh NeighborEntry
|
||||
|
||||
// done is closed when address resolution is complete. It is nil iff s is
|
||||
// incomplete and resolution is not yet in progress.
|
||||
// +checklocks:neighborEntryRWMutex
|
||||
done chan struct{} `state:"nosave"`
|
||||
|
||||
// onResolve is called with the result of address resolution.
|
||||
// +checklocks:neighborEntryRWMutex
|
||||
onResolve []func(LinkResolutionResult) `state:"nosave"`
|
||||
|
||||
// +checklocks:neighborEntryRWMutex
|
||||
isRouter bool
|
||||
|
||||
// +checklocks:neighborEntryRWMutex
|
||||
timer timer
|
||||
}
|
||||
|
||||
@@ -163,7 +168,7 @@ func newStaticNeighborEntry(cache *neighborCache, addr tcpip.Address, linkAddr t
|
||||
// notifyCompletionLocked notifies those waiting for address resolution, with
|
||||
// the link address if resolution completed successfully.
|
||||
//
|
||||
// Precondition: e.mu MUST be locked.
|
||||
// +checklocks:e.mu.neighborEntryRWMutex
|
||||
func (e *neighborEntry) notifyCompletionLocked(err tcpip.Error) {
|
||||
res := LinkResolutionResult{LinkAddress: e.mu.neigh.LinkAddr, Err: err}
|
||||
for _, callback := range e.mu.onResolve {
|
||||
@@ -185,7 +190,7 @@ func (e *neighborEntry) notifyCompletionLocked(err tcpip.Error) {
|
||||
// keyword but allows tests that use manual clocks to deterministically
|
||||
// wait for this work to complete.
|
||||
e.cache.nic.stack.clock.AfterFunc(0, func() {
|
||||
e.cache.nic.linkResQueue.dequeue(ch, e.mu.neigh.LinkAddr, err)
|
||||
e.cache.nic.linkResQueue.dequeue(ch, res.LinkAddress, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -193,7 +198,7 @@ func (e *neighborEntry) notifyCompletionLocked(err tcpip.Error) {
|
||||
// dispatchAddEventLocked signals to stack's NUD Dispatcher that the entry has
|
||||
// been added.
|
||||
//
|
||||
// Precondition: e.mu MUST be locked.
|
||||
// +checklocks:e.mu.neighborEntryRWMutex
|
||||
func (e *neighborEntry) dispatchAddEventLocked() {
|
||||
if nudDisp := e.cache.nic.stack.nudDisp; nudDisp != nil {
|
||||
nudDisp.OnNeighborAdded(e.cache.nic.id, e.mu.neigh)
|
||||
@@ -203,7 +208,7 @@ func (e *neighborEntry) dispatchAddEventLocked() {
|
||||
// dispatchChangeEventLocked signals to stack's NUD Dispatcher that the entry
|
||||
// has changed state or link-layer address.
|
||||
//
|
||||
// Precondition: e.mu MUST be locked.
|
||||
// +checklocks:e.mu.neighborEntryRWMutex
|
||||
func (e *neighborEntry) dispatchChangeEventLocked() {
|
||||
if nudDisp := e.cache.nic.stack.nudDisp; nudDisp != nil {
|
||||
nudDisp.OnNeighborChanged(e.cache.nic.id, e.mu.neigh)
|
||||
@@ -213,7 +218,7 @@ func (e *neighborEntry) dispatchChangeEventLocked() {
|
||||
// dispatchRemoveEventLocked signals to stack's NUD Dispatcher that the entry
|
||||
// has been removed.
|
||||
//
|
||||
// Precondition: e.mu MUST be locked.
|
||||
// +checklocks:e.mu.neighborEntryRWMutex
|
||||
func (e *neighborEntry) dispatchRemoveEventLocked() {
|
||||
if nudDisp := e.cache.nic.stack.nudDisp; nudDisp != nil {
|
||||
nudDisp.OnNeighborRemoved(e.cache.nic.id, e.mu.neigh)
|
||||
@@ -223,7 +228,7 @@ func (e *neighborEntry) dispatchRemoveEventLocked() {
|
||||
// cancelTimerLocked cancels the currently scheduled action, if there is one.
|
||||
// Entries in Unknown, Stale, or Static state do not have a scheduled action.
|
||||
//
|
||||
// Precondition: e.mu MUST be locked.
|
||||
// +checklocks:e.mu.neighborEntryRWMutex
|
||||
func (e *neighborEntry) cancelTimerLocked() {
|
||||
if e.mu.timer.timer != nil {
|
||||
e.mu.timer.timer.Stop()
|
||||
@@ -235,7 +240,7 @@ func (e *neighborEntry) cancelTimerLocked() {
|
||||
|
||||
// removeLocked prepares the entry for removal.
|
||||
//
|
||||
// Precondition: e.mu MUST be locked.
|
||||
// +checklocks:e.mu.neighborEntryRWMutex
|
||||
func (e *neighborEntry) removeLocked() {
|
||||
e.mu.neigh.UpdatedAt = e.cache.nic.stack.clock.NowMonotonic()
|
||||
e.dispatchRemoveEventLocked()
|
||||
@@ -255,7 +260,7 @@ func (e *neighborEntry) removeLocked() {
|
||||
//
|
||||
// Follows the logic defined in RFC 4861 section 7.3.3.
|
||||
//
|
||||
// Precondition: e.mu MUST be locked.
|
||||
// +checklocks:e.mu.neighborEntryRWMutex
|
||||
func (e *neighborEntry) setStateLocked(next NeighborState) {
|
||||
e.cancelTimerLocked()
|
||||
|
||||
@@ -363,7 +368,7 @@ func (e *neighborEntry) setStateLocked(next NeighborState) {
|
||||
//
|
||||
// Follows the logic defined in RFC 4861 section 7.3.3.
|
||||
//
|
||||
// Precondition: e.mu MUST be locked.
|
||||
// +checklocks:e.mu.neighborEntryRWMutex
|
||||
func (e *neighborEntry) handlePacketQueuedLocked(localAddr tcpip.Address) {
|
||||
switch e.mu.neigh.State {
|
||||
case Unknown, Unreachable:
|
||||
@@ -442,7 +447,7 @@ func (e *neighborEntry) handlePacketQueuedLocked(localAddr tcpip.Address) {
|
||||
//
|
||||
// Follows the logic defined in RFC 4861 section 7.2.3.
|
||||
//
|
||||
// Precondition: e.mu MUST be locked.
|
||||
// +checklocks:e.mu.neighborEntryRWMutex
|
||||
func (e *neighborEntry) handleProbeLocked(remoteLinkAddr tcpip.LinkAddress) {
|
||||
// Probes MUST be silently discarded if the target address is tentative, does
|
||||
// not exist, or not bound to the NIC as per RFC 4861 section 7.2.3. These
|
||||
@@ -505,7 +510,7 @@ func (e *neighborEntry) handleProbeLocked(remoteLinkAddr tcpip.LinkAddress) {
|
||||
// Generated Addresses (CGA), as defined in RFC 3972. This ensures that the
|
||||
// claimed source of an NDP message is the owner of the claimed address.
|
||||
//
|
||||
// Precondition: e.mu MUST be locked.
|
||||
// +checklocks:e.mu.neighborEntryRWMutex
|
||||
func (e *neighborEntry) handleConfirmationLocked(linkAddr tcpip.LinkAddress, flags ReachabilityConfirmationFlags) {
|
||||
switch e.mu.neigh.State {
|
||||
case Incomplete:
|
||||
@@ -579,7 +584,7 @@ func (e *neighborEntry) handleConfirmationLocked(linkAddr tcpip.LinkAddress, fla
|
||||
// here.
|
||||
ep := e.cache.nic.getNetworkEndpoint(header.IPv6ProtocolNumber)
|
||||
if ep == nil {
|
||||
panic(fmt.Sprintf("have a neighbor entry for an IPv6 router but no IPv6 network endpoint"))
|
||||
panic("have a neighbor entry for an IPv6 router but no IPv6 network endpoint")
|
||||
}
|
||||
|
||||
if ndpEP, ok := ep.(NDPEndpoint); ok {
|
||||
|
||||
@@ -92,5 +92,5 @@ func neighborEntryinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
neighborEntryinitLockNames()
|
||||
neighborEntryprefixIndex = locking.NewMutexClass(reflect.TypeOf(neighborEntryRWMutex{}), neighborEntrylockNames)
|
||||
neighborEntryprefixIndex = locking.NewMutexClass(reflect.TypeFor[neighborEntryRWMutex](), neighborEntrylockNames)
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
// Copyright 2025 The 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 stack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NFTablesInterface is an interface for evaluating chains.
|
||||
type NFTablesInterface interface {
|
||||
CheckPrerouting(pkt *PacketBuffer, route *Route, af AddressFamily) bool
|
||||
CheckInput(pkt *PacketBuffer, route *Route, af AddressFamily) bool
|
||||
CheckForward(pkt *PacketBuffer, route *Route, af AddressFamily) bool
|
||||
CheckOutput(pkt *PacketBuffer, route *Route, af AddressFamily) bool
|
||||
CheckPostrouting(pkt *PacketBuffer, route *Route, af AddressFamily) bool
|
||||
CheckIngress(pkt *PacketBuffer, route *Route, af AddressFamily) bool
|
||||
CheckEgress(pkt *PacketBuffer, route *Route, af AddressFamily) bool
|
||||
}
|
||||
|
||||
// NFHook describes specific points in the pipeline where chains can be attached.
|
||||
// Each address family has its own set of hooks (defined in supportedHooks).
|
||||
// For IPv4/IPv6/Inet and Bridge, there are two possible pipelines:
|
||||
// 1. Prerouting -> Input -> ~Local Process~ -> Output -> Postrouting
|
||||
// 2. Prerouting -> Forward -> Postrouting
|
||||
type NFHook uint16
|
||||
|
||||
const (
|
||||
// NFPrerouting Hook is supported by IPv4/IPv6/Inet, Bridge Families.
|
||||
// Prerouting is evaluated before a packet is routed to applications or forwarded.
|
||||
NFPrerouting NFHook = iota
|
||||
|
||||
// NFInput Hook is supported by IPv4/IPv6/Inet, Bridge, ARP Families.
|
||||
// Input is evaluated before a packet reaches an application.
|
||||
NFInput
|
||||
|
||||
// NFForward Hook is supported by IPv4/IPv6/Inet, Bridge Families.
|
||||
// Forward is evaluated once it's decided that a packet should be forwarded to another host.
|
||||
NFForward
|
||||
|
||||
// NFOutput Hook is supported by IPv4/IPv6/Inet, Bridge, ARP Families.
|
||||
// Output is evaluated after a packet is written by an application to be sent out.
|
||||
NFOutput
|
||||
|
||||
// NFPostrouting Hook is supported by IPv4/IPv6/Inet, Bridge Families.
|
||||
// Postrouting is evaluated just before a packet goes out on the wire.
|
||||
NFPostrouting
|
||||
|
||||
// NFIngress Hook is supported by IPv4/IPv6/Inet, Bridge, Netdev Families.
|
||||
// Ingress is the first hook evaluated, even before prerouting.
|
||||
NFIngress
|
||||
|
||||
// NFEgress Hook is supported by Netdev Family only.
|
||||
// Egress is the last hook evaluated, after the packet has been processed by the
|
||||
// application and is being prepared for transmission out of the network interface.
|
||||
NFEgress
|
||||
|
||||
// NFNumHooks is the number of hooks supported by nftables.
|
||||
NFNumHooks
|
||||
)
|
||||
|
||||
// hookStrings maps hooks to their string representation.
|
||||
var hookStrings = map[NFHook]string{
|
||||
NFPrerouting: "Prerouting",
|
||||
NFInput: "Input",
|
||||
NFForward: "Forward",
|
||||
NFOutput: "Output",
|
||||
NFPostrouting: "Postrouting",
|
||||
NFIngress: "Ingress",
|
||||
NFEgress: "Egress",
|
||||
}
|
||||
|
||||
// String for Hook returns the name of the hook.
|
||||
func (h NFHook) String() string {
|
||||
if hook, ok := hookStrings[h]; ok {
|
||||
return hook
|
||||
}
|
||||
panic(fmt.Sprintf("invalid NFHook: %d", int(h)))
|
||||
}
|
||||
|
||||
// AddressFamily describes the 6 address families supported by nftables.
|
||||
// The address family determines the type of packets processed, and each family
|
||||
// contains hooks at specific stages of the packet processing pipeline.
|
||||
type AddressFamily int
|
||||
|
||||
const (
|
||||
// Unspec represents an unspecified address family.
|
||||
Unspec AddressFamily = iota
|
||||
|
||||
// IP represents IPv4 Family.
|
||||
IP
|
||||
|
||||
// IP6 represents IPv6 Family.
|
||||
IP6
|
||||
|
||||
// Inet represents Internet Family for hybrid IPv4/IPv6 rules.
|
||||
Inet
|
||||
|
||||
// Arp represents ARP Family for IPv4 ARP packets.
|
||||
Arp
|
||||
|
||||
// Bridge represents Bridge Family for Ethernet packets across bridge devices.
|
||||
Bridge
|
||||
|
||||
// Netdev represents Netdev Family for packets on ingress and egress.
|
||||
Netdev
|
||||
|
||||
// NumAFs is the number of address families supported by nftables.
|
||||
NumAFs
|
||||
)
|
||||
|
||||
// AddressFamilyStrings maps address families to their string representation.
|
||||
var AddressFamilyStrings = map[AddressFamily]string{
|
||||
Unspec: "UNSPEC",
|
||||
IP: "IPv4",
|
||||
IP6: "IPv6",
|
||||
Inet: "Internet (Both IPv4/IPv6)",
|
||||
Arp: "ARP",
|
||||
Bridge: "Bridge",
|
||||
Netdev: "Netdev",
|
||||
}
|
||||
|
||||
// ValidateAddressFamily ensures the family address is valid (within bounds).
|
||||
// Unspecified address family is not valid. It is only used to reference all address families.
|
||||
func ValidateAddressFamily(family AddressFamily) error {
|
||||
if family < 1 || family >= NumAFs {
|
||||
return fmt.Errorf("invalid address family: %d", int(family))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// String for AddressFamily returns the name of the address family.
|
||||
func (f AddressFamily) String() string {
|
||||
if af, ok := AddressFamilyStrings[f]; ok {
|
||||
return af
|
||||
}
|
||||
panic(fmt.Sprintf("invalid address family: %d", int(f)))
|
||||
}
|
||||
+38
-18
@@ -17,6 +17,7 @@ package stack
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/atomicbitops"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
@@ -43,6 +44,7 @@ type nic struct {
|
||||
stack *Stack
|
||||
id tcpip.NICID
|
||||
name string
|
||||
kind string
|
||||
context NICContext
|
||||
|
||||
stats sharedStats
|
||||
@@ -71,7 +73,7 @@ type nic struct {
|
||||
// complete.
|
||||
linkResQueue packetsPendingLinkResolution
|
||||
|
||||
// packetEPsMu protects annotated fields below.
|
||||
// packetEPsMu protects packetEPs.
|
||||
packetEPsMu packetEPsRWMutex `state:"nosave"`
|
||||
|
||||
// eps is protected by the mutex, but the values contained in it are not.
|
||||
@@ -190,17 +192,14 @@ func newNIC(stack *Stack, id tcpip.NICID, ep LinkEndpoint, opts NICOptions) *nic
|
||||
networkEndpoints: make(map[tcpip.NetworkProtocolNumber]NetworkEndpoint),
|
||||
linkAddrResolvers: make(map[tcpip.NetworkProtocolNumber]*linkResolver),
|
||||
duplicateAddressDetectors: make(map[tcpip.NetworkProtocolNumber]DuplicateAddressDetector),
|
||||
packetEPs: make(map[tcpip.NetworkProtocolNumber]*packetEndpointList),
|
||||
qDisc: qDisc,
|
||||
deliverLinkPackets: opts.DeliverLinkPackets,
|
||||
experimentIPOptionEnabled: opts.EnableExperimentIPOption,
|
||||
kind: opts.Kind,
|
||||
}
|
||||
nic.linkResQueue.init(nic)
|
||||
|
||||
nic.packetEPsMu.Lock()
|
||||
defer nic.packetEPsMu.Unlock()
|
||||
|
||||
nic.packetEPs = make(map[tcpip.NetworkProtocolNumber]*packetEndpointList)
|
||||
|
||||
resolutionRequired := ep.Capabilities()&CapabilityResolutionRequired != 0
|
||||
|
||||
for _, netProto := range stack.networkProtocols {
|
||||
@@ -317,7 +316,7 @@ func (n *nic) enable() tcpip.Error {
|
||||
// resources. This guarantees no packets between this NIC and the network
|
||||
// stack.
|
||||
//
|
||||
// It returns an action that has to be excuted after releasing the Stack lock
|
||||
// It returns an action that has to be executed after releasing the Stack lock
|
||||
// and any error encountered.
|
||||
func (n *nic) remove(closeLinkEndpoint bool) (func(), tcpip.Error) {
|
||||
n.enableDisableMu.Lock()
|
||||
@@ -574,12 +573,22 @@ func (n *nic) allPermanentAddresses() []tcpip.ProtocolAddress {
|
||||
// primaryAddresses returns the primary addresses associated with this NIC.
|
||||
func (n *nic) primaryAddresses() []tcpip.ProtocolAddress {
|
||||
var addrs []tcpip.ProtocolAddress
|
||||
for p, ep := range n.networkEndpoints {
|
||||
addressableEndpoint, ok := ep.(AddressableEndpoint)
|
||||
|
||||
protocolNumbers := make([]tcpip.NetworkProtocolNumber, 0, len(n.networkEndpoints))
|
||||
for p := range n.networkEndpoints {
|
||||
protocolNumbers = append(protocolNumbers, p)
|
||||
}
|
||||
// Sort the network protocol numbers so that IPv4 address is always
|
||||
// added to the list before IPv6 address.
|
||||
sort.Slice(protocolNumbers, func(i, j int) bool {
|
||||
return protocolNumbers[i] < protocolNumbers[j]
|
||||
})
|
||||
|
||||
for _, p := range protocolNumbers {
|
||||
addressableEndpoint, ok := n.networkEndpoints[p].(AddressableEndpoint)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, a := range addressableEndpoint.PrimaryAddresses() {
|
||||
addrs = append(addrs, tcpip.ProtocolAddress{Protocol: p, AddressWithPrefix: a})
|
||||
}
|
||||
@@ -834,23 +843,34 @@ func (n *nic) DeliverLinkPacket(protocol tcpip.NetworkProtocolNumber, pkt *Packe
|
||||
// DeliverTransportPacket delivers the packets to the appropriate transport
|
||||
// protocol endpoint.
|
||||
func (n *nic) DeliverTransportPacket(protocol tcpip.TransportProtocolNumber, pkt *PacketBuffer) TransportPacketDisposition {
|
||||
res, _ := n.deliverTransportPacket(protocol, pkt)
|
||||
return res
|
||||
}
|
||||
|
||||
// DeliverTransportPacketWithDefaultHandlerResult implements
|
||||
// TransportDispatcherWithDefaultHandlerResult.DeliverTransportPacketWithDefaultHandlerResult.
|
||||
func (n *nic) DeliverTransportPacketWithDefaultHandlerResult(protocol tcpip.TransportProtocolNumber, pkt *PacketBuffer) (TransportPacketDisposition, bool) {
|
||||
return n.deliverTransportPacket(protocol, pkt)
|
||||
}
|
||||
|
||||
func (n *nic) deliverTransportPacket(protocol tcpip.TransportProtocolNumber, pkt *PacketBuffer) (TransportPacketDisposition, bool) {
|
||||
state, ok := n.stack.transportProtocols[protocol]
|
||||
if !ok {
|
||||
n.stats.unknownL4ProtocolRcvdPacketCounts.Increment(uint64(protocol))
|
||||
return TransportPacketProtocolUnreachable
|
||||
return TransportPacketProtocolUnreachable, false
|
||||
}
|
||||
|
||||
transProto := state.proto
|
||||
|
||||
if len(pkt.TransportHeader().Slice()) == 0 {
|
||||
n.stats.malformedL4RcvdPackets.Increment()
|
||||
return TransportPacketHandled
|
||||
return TransportPacketHandled, false
|
||||
}
|
||||
|
||||
srcPort, dstPort, err := transProto.ParsePorts(pkt.TransportHeader().Slice())
|
||||
if err != nil {
|
||||
n.stats.malformedL4RcvdPackets.Increment()
|
||||
return TransportPacketHandled
|
||||
return TransportPacketHandled, false
|
||||
}
|
||||
|
||||
netProto, ok := n.stack.networkProtocols[pkt.NetworkProtocolNumber]
|
||||
@@ -866,13 +886,13 @@ func (n *nic) DeliverTransportPacket(protocol tcpip.TransportProtocolNumber, pkt
|
||||
RemoteAddress: src,
|
||||
}
|
||||
if n.stack.demux.deliverPacket(protocol, pkt, id) {
|
||||
return TransportPacketHandled
|
||||
return TransportPacketHandled, false
|
||||
}
|
||||
|
||||
// Try to deliver to per-stack default handler.
|
||||
if state.defaultHandler != nil {
|
||||
if state.defaultHandler(id, pkt) {
|
||||
return TransportPacketHandled
|
||||
return TransportPacketHandled, true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,11 +902,11 @@ func (n *nic) DeliverTransportPacket(protocol tcpip.TransportProtocolNumber, pkt
|
||||
switch res := transProto.HandleUnknownDestinationPacket(id, pkt); res {
|
||||
case UnknownDestinationPacketMalformed:
|
||||
n.stats.malformedL4RcvdPackets.Increment()
|
||||
return TransportPacketHandled
|
||||
return TransportPacketHandled, false
|
||||
case UnknownDestinationPacketUnhandled:
|
||||
return TransportPacketDestinationPortUnreachable
|
||||
return TransportPacketDestinationPortUnreachable, false
|
||||
case UnknownDestinationPacketHandled:
|
||||
return TransportPacketHandled
|
||||
return TransportPacketHandled, false
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized result from HandleUnknownDestinationPacket = %d", res))
|
||||
}
|
||||
|
||||
+1
-1
@@ -92,5 +92,5 @@ func nicinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
nicinitLockNames()
|
||||
nicprefixIndex = locking.NewMutexClass(reflect.TypeOf(nicRWMutex{}), niclockNames)
|
||||
nicprefixIndex = locking.NewMutexClass(reflect.TypeFor[nicRWMutex](), niclockNames)
|
||||
}
|
||||
+11
-4
@@ -320,17 +320,24 @@ func calcMaxRandomFactor(minRandomFactor float32) float32 {
|
||||
type nudStateMu struct {
|
||||
sync.RWMutex `state:"nosave"`
|
||||
|
||||
// +checklocks:RWMutex
|
||||
config NUDConfigurations
|
||||
|
||||
// reachableTime is the duration to wait for a REACHABLE entry to
|
||||
// transition into STALE after inactivity. This value is calculated with
|
||||
// the algorithm defined in RFC 4861 section 6.3.2.
|
||||
//
|
||||
// +checklocks:RWMutex
|
||||
reachableTime time.Duration
|
||||
|
||||
expiration tcpip.MonotonicTime
|
||||
// +checklocks:RWMutex
|
||||
expiration tcpip.MonotonicTime
|
||||
// +checklocks:RWMutex
|
||||
prevBaseReachableTime time.Duration
|
||||
prevMinRandomFactor float32
|
||||
prevMaxRandomFactor float32
|
||||
// +checklocks:RWMutex
|
||||
prevMinRandomFactor float32
|
||||
// +checklocks:RWMutex
|
||||
prevMaxRandomFactor float32
|
||||
}
|
||||
|
||||
// NUDState stores states needed for calculating reachable time.
|
||||
@@ -405,7 +412,7 @@ func (s *NUDState) ReachableTime() time.Duration {
|
||||
// changes. In such cases, an implementation SHOULD ensure that a new
|
||||
// random value gets re-computed at least once every few hours.
|
||||
//
|
||||
// s.mu MUST be locked for writing.
|
||||
// +checklocks:s.mu.RWMutex
|
||||
func (s *NUDState) recomputeReachableTimeLocked() {
|
||||
s.mu.prevBaseReachableTime = s.mu.config.BaseReachableTime
|
||||
s.mu.prevMinRandomFactor = s.mu.config.MinRandomFactor
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/checksum"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
|
||||
@@ -56,6 +57,9 @@ type PacketBufferOptions struct {
|
||||
// OnRelease is a function to be run when the packet buffer is no longer
|
||||
// referenced (released back to the pool).
|
||||
OnRelease func()
|
||||
|
||||
// Mark is the mark value of this packet.
|
||||
Mark uint32
|
||||
}
|
||||
|
||||
// A PacketBuffer contains all the data of a network packet.
|
||||
@@ -154,6 +158,10 @@ type PacketBuffer struct {
|
||||
// NICID is the ID of the last interface the network packet was handled at.
|
||||
NICID tcpip.NICID
|
||||
|
||||
// InputNICID is the ID of the interface that the network packet
|
||||
// was received on.
|
||||
InputNICID tcpip.NICID
|
||||
|
||||
// RXChecksumValidated indicates that checksum verification may be
|
||||
// safely skipped.
|
||||
RXChecksumValidated bool
|
||||
@@ -161,6 +169,9 @@ type PacketBuffer struct {
|
||||
// NetworkPacketInfo holds an incoming packet's network-layer information.
|
||||
NetworkPacketInfo NetworkPacketInfo
|
||||
|
||||
// Mark is the mark value of this packet.
|
||||
Mark uint32
|
||||
|
||||
tuple *tuple
|
||||
|
||||
// onRelease is a function to be run when the packet buffer is no longer
|
||||
@@ -182,6 +193,7 @@ func NewPacketBuffer(opts PacketBufferOptions) *PacketBuffer {
|
||||
}
|
||||
pk.NetworkPacketInfo.IsForwardedPacket = opts.IsForwardedPacket
|
||||
pk.onRelease = opts.OnRelease
|
||||
pk.Mark = opts.Mark
|
||||
pk.InitRefs()
|
||||
return pk
|
||||
}
|
||||
@@ -380,6 +392,7 @@ func (pk *PacketBuffer) Clone() *PacketBuffer {
|
||||
newPk.headers = pk.headers
|
||||
newPk.Hash = pk.Hash
|
||||
newPk.Owner = pk.Owner
|
||||
newPk.Mark = pk.Mark
|
||||
newPk.GSOOptions = pk.GSOOptions
|
||||
newPk.EgressRoute = pk.EgressRoute
|
||||
newPk.NetworkProtocolNumber = pk.NetworkProtocolNumber
|
||||
@@ -388,6 +401,7 @@ func (pk *PacketBuffer) Clone() *PacketBuffer {
|
||||
newPk.TransportProtocolNumber = pk.TransportProtocolNumber
|
||||
newPk.PktType = pk.PktType
|
||||
newPk.NICID = pk.NICID
|
||||
newPk.InputNICID = pk.InputNICID
|
||||
newPk.RXChecksumValidated = pk.RXChecksumValidated
|
||||
newPk.NetworkPacketInfo = pk.NetworkPacketInfo
|
||||
newPk.tuple = pk.tuple
|
||||
@@ -431,6 +445,7 @@ func (pk *PacketBuffer) CloneToInbound() *PacketBuffer {
|
||||
newPk.InitRefs()
|
||||
// Treat unfilled header portion as reserved.
|
||||
newPk.reserved = pk.AvailableHeaderBytes()
|
||||
newPk.Mark = pk.Mark
|
||||
newPk.tuple = pk.tuple
|
||||
return newPk
|
||||
}
|
||||
@@ -466,10 +481,78 @@ func (pk *PacketBuffer) DeepCopyForForwarding(reservedHeaderBytes int) *PacketBu
|
||||
}
|
||||
|
||||
newPk.tuple = pk.tuple
|
||||
newPk.Mark = pk.Mark
|
||||
newPk.InputNICID = pk.InputNICID
|
||||
|
||||
return newPk
|
||||
}
|
||||
|
||||
// IsConnTrackConfigured returns whether connection tracking is configured for this packet.
|
||||
func (pk *PacketBuffer) IsConnTrackConfigured() bool {
|
||||
return pk.tuple != nil && pk.tuple.conn != nil
|
||||
}
|
||||
|
||||
// FillConnTrackInfo fills connection tracking information for the packet.
|
||||
func (pk *PacketBuffer) FillConnTrackInfo(opts ConnTrackInfoOpts, info *ConnTrackInfo) bool {
|
||||
t := pk.tuple
|
||||
if t == nil || t.conn == nil {
|
||||
return false
|
||||
}
|
||||
return t.conn.FillConnTrackInfo(opts, info)
|
||||
}
|
||||
|
||||
// IsReplyPacket returns whether the packet is a reply packet.
|
||||
func (pk *PacketBuffer) IsReplyPacket() bool {
|
||||
t := pk.tuple
|
||||
if t == nil {
|
||||
return false
|
||||
}
|
||||
return t.reply
|
||||
}
|
||||
|
||||
// IsNATConfigured returns whether NAT is configured for this packet.
|
||||
func (pk *PacketBuffer) IsNATConfigured(nt NATType) bool {
|
||||
if !pk.IsConnTrackConfigured() {
|
||||
return false
|
||||
}
|
||||
return pk.tuple.conn.IsNATConfigured(nt)
|
||||
}
|
||||
|
||||
// ConfigureNoopNAT configures a no-op NAT for the packet.
|
||||
// Called if no NAT rules are configured for this packet.
|
||||
func (pk *PacketBuffer) ConfigureNoopNAT(natType NATType) bool {
|
||||
if !pk.IsConnTrackConfigured() {
|
||||
return false
|
||||
}
|
||||
return pk.tuple.conn.ConfigureNoopNAT(pk, natType)
|
||||
}
|
||||
|
||||
// ConfigureNAT configures NAT for the packet.
|
||||
// Called if NAT rules are configured for this packet.
|
||||
// Returns whether NAT was configured or not.
|
||||
func (pk *PacketBuffer) ConfigureNAT(portsOrIdents PortOrIdentRange, natAddress tcpip.Address, natType NATType, changePort, changeAddress bool) bool {
|
||||
if !pk.IsConnTrackConfigured() {
|
||||
return false
|
||||
}
|
||||
return pk.tuple.conn.ConfigureNAT(portsOrIdents, natAddress, natType, changePort, changeAddress)
|
||||
}
|
||||
|
||||
// ConfigureMasquerade configures NAT masquerade for the packet.
|
||||
func (pk *PacketBuffer) ConfigureMasquerade(portsOrIdents PortOrIdentRange, route *Route, stk *Stack, changePort bool) bool {
|
||||
if !pk.IsConnTrackConfigured() {
|
||||
return false
|
||||
}
|
||||
return pk.tuple.conn.configureMasquerade(pk, route, stk, portsOrIdents, changePort)
|
||||
}
|
||||
|
||||
// FinalizeConnTrack finalizes the connection tracking state for the packet.
|
||||
func (pk *PacketBuffer) FinalizeConnTrack() bool {
|
||||
if pk.tuple == nil || pk.tuple.conn == nil {
|
||||
return true
|
||||
}
|
||||
return pk.tuple.conn.finalize()
|
||||
}
|
||||
|
||||
// headerInfo stores metadata about a header in a packet.
|
||||
//
|
||||
// +stateify savable
|
||||
@@ -768,3 +851,312 @@ func BufferSince(h PacketHeader) buffer.Buffer {
|
||||
clone.TrimFront(int64(offset))
|
||||
return clone
|
||||
}
|
||||
|
||||
// ExperimentOptionValue returns the experiment option value from the packet
|
||||
// and a bool indicating whether an experiment option value was found.
|
||||
func (pk *PacketBuffer) ExperimentOptionValue() (uint16, bool) {
|
||||
switch pk.NetworkProtocolNumber {
|
||||
case header.IPv4ProtocolNumber:
|
||||
h := header.IPv4(pk.NetworkHeader().Slice())
|
||||
opts := h.Options()
|
||||
iter := opts.MakeIterator()
|
||||
for {
|
||||
opt, done, err := iter.Next()
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
if done {
|
||||
return 0, false
|
||||
}
|
||||
if opt.Type() == header.IPv4OptionExperimentType {
|
||||
return opt.(*header.IPv4OptionExperiment).Value(), true
|
||||
}
|
||||
}
|
||||
case header.IPv6ProtocolNumber:
|
||||
h := header.IPv6(pk.NetworkHeader().Slice())
|
||||
v := pk.NetworkHeader().View()
|
||||
if v != nil {
|
||||
v.TrimFront(header.IPv6MinimumSize)
|
||||
}
|
||||
buf := buffer.MakeWithView(v)
|
||||
buf.Append(pk.TransportHeader().View())
|
||||
dataBuf := pk.Data().ToBuffer()
|
||||
buf.Merge(&dataBuf)
|
||||
it := header.MakeIPv6PayloadIterator(header.IPv6ExtensionHeaderIdentifier(h.NextHeader()), buf)
|
||||
|
||||
for {
|
||||
hdr, done, err := it.Next()
|
||||
if done || err != nil {
|
||||
break
|
||||
}
|
||||
if h, ok := hdr.(header.IPv6ExperimentExtHdr); ok {
|
||||
hdr.Release()
|
||||
return h.Value, true
|
||||
}
|
||||
hdr.Release()
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Unexpected network protocol number %d", pk.NetworkProtocolNumber))
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// GetEmbeddedNetAndTransHeaders returns the network and transport headers of the
|
||||
// packet.
|
||||
func (pk *PacketBuffer) GetEmbeddedNetAndTransHeaders(netHdrLength int, getNetAndTransHdr netAndTransHeadersFunc, transProto tcpip.TransportProtocolNumber) (header.Network, header.ChecksummableTransport, bool) {
|
||||
switch transProto {
|
||||
case header.TCPProtocolNumber:
|
||||
if netAndTransHeader, ok := pk.Data().PullUp(netHdrLength + header.TCPMinimumSize); ok {
|
||||
netHeader, transHeaderBytes := getNetAndTransHdr(netAndTransHeader, header.TCPMinimumSize)
|
||||
return netHeader, header.TCP(transHeaderBytes), true
|
||||
}
|
||||
case header.UDPProtocolNumber:
|
||||
if netAndTransHeader, ok := pk.Data().PullUp(netHdrLength + header.UDPMinimumSize); ok {
|
||||
netHeader, transHeaderBytes := getNetAndTransHdr(netAndTransHeader, header.UDPMinimumSize)
|
||||
return netHeader, header.UDP(transHeaderBytes), true
|
||||
}
|
||||
}
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
// GetHeaders returns the network and transport headers of the packet.
|
||||
func (pk *PacketBuffer) GetHeaders() (netHdr header.Network, transHdr header.Transport, isICMPError bool, ok bool) {
|
||||
switch pk.TransportProtocolNumber {
|
||||
case header.TCPProtocolNumber:
|
||||
if tcpHeader := header.TCP(pk.TransportHeader().Slice()); len(tcpHeader) >= header.TCPMinimumSize {
|
||||
return pk.Network(), tcpHeader, false, true
|
||||
}
|
||||
return nil, nil, false, false
|
||||
case header.UDPProtocolNumber:
|
||||
if udpHeader := header.UDP(pk.TransportHeader().Slice()); len(udpHeader) >= header.UDPMinimumSize {
|
||||
return pk.Network(), udpHeader, false, true
|
||||
}
|
||||
return nil, nil, false, false
|
||||
case header.ICMPv4ProtocolNumber:
|
||||
icmpHeader := header.ICMPv4(pk.TransportHeader().Slice())
|
||||
if len(icmpHeader) < header.ICMPv4MinimumSize {
|
||||
return nil, nil, false, false
|
||||
}
|
||||
|
||||
switch icmpType := icmpHeader.Type(); icmpType {
|
||||
case header.ICMPv4Echo, header.ICMPv4EchoReply:
|
||||
return pk.Network(), icmpHeader, false, true
|
||||
case header.ICMPv4DstUnreachable, header.ICMPv4TimeExceeded, header.ICMPv4ParamProblem:
|
||||
default:
|
||||
return nil, nil, false, false
|
||||
}
|
||||
|
||||
h, ok := pk.Data().PullUp(header.IPv4MinimumSize)
|
||||
if !ok {
|
||||
return nil, nil, false, false
|
||||
}
|
||||
|
||||
hdrLength := int(header.IPv4(h).HeaderLength())
|
||||
// Pull up the full IPv4 header which might include options.
|
||||
if hdrLength > header.IPv4MinimumSize {
|
||||
// TODO(https://gvisor.dev/issue/6765): Handle IPv4
|
||||
// options.
|
||||
h, ok = pk.Data().PullUp(hdrLength)
|
||||
if !ok {
|
||||
return nil, nil, false, false
|
||||
}
|
||||
}
|
||||
|
||||
if netHdr, transHdr, ok := pk.GetEmbeddedNetAndTransHeaders(hdrLength, v4NetAndTransHdr, tcpip.TransportProtocolNumber(header.IPv4(h).Protocol())); ok {
|
||||
return netHdr, transHdr, true, true
|
||||
}
|
||||
return nil, nil, false, false
|
||||
case header.ICMPv6ProtocolNumber:
|
||||
icmpHeader := header.ICMPv6(pk.TransportHeader().Slice())
|
||||
if len(icmpHeader) < header.ICMPv6MinimumSize {
|
||||
return nil, nil, false, false
|
||||
}
|
||||
|
||||
switch icmpType := icmpHeader.Type(); icmpType {
|
||||
case header.ICMPv6EchoRequest, header.ICMPv6EchoReply:
|
||||
return pk.Network(), icmpHeader, false, true
|
||||
case header.ICMPv6DstUnreachable, header.ICMPv6PacketTooBig, header.ICMPv6TimeExceeded, header.ICMPv6ParamProblem:
|
||||
default:
|
||||
return nil, nil, false, false
|
||||
}
|
||||
|
||||
h, ok := pk.Data().PullUp(header.IPv6MinimumSize)
|
||||
if !ok {
|
||||
return nil, nil, false, false
|
||||
}
|
||||
|
||||
// We do not support extension headers in ICMP errors so the next header
|
||||
// in the IPv6 packet should be a tracked protocol if we reach this point.
|
||||
//
|
||||
// TODO(https://gvisor.dev/issue/6789): Support extension headers.
|
||||
transProto, _ := header.IPv6(h).TryParseTransportProtocol()
|
||||
if netHdr, transHdr, ok := pk.GetEmbeddedNetAndTransHeaders(header.IPv6MinimumSize, v6NetAndTransHdr, transProto); ok {
|
||||
return netHdr, transHdr, true, true
|
||||
}
|
||||
return nil, nil, false, false
|
||||
default:
|
||||
return nil, nil, false, false
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateHeaders updates the headers of the packet with the new port and address.
|
||||
func UpdateHeaders(n header.Network, t header.Transport, updateSRCFields, fullChecksum, updatePseudoHeader bool, newPortOrIdent uint16, newAddr tcpip.Address) {
|
||||
switch t := t.(type) {
|
||||
case header.ChecksummableTransport:
|
||||
if updateSRCFields {
|
||||
if fullChecksum {
|
||||
t.SetSourcePortWithChecksumUpdate(newPortOrIdent)
|
||||
} else {
|
||||
t.SetSourcePort(newPortOrIdent)
|
||||
}
|
||||
} else {
|
||||
if fullChecksum {
|
||||
t.SetDestinationPortWithChecksumUpdate(newPortOrIdent)
|
||||
} else {
|
||||
t.SetDestinationPort(newPortOrIdent)
|
||||
}
|
||||
}
|
||||
|
||||
if updatePseudoHeader {
|
||||
var oldAddr tcpip.Address
|
||||
if updateSRCFields {
|
||||
oldAddr = n.SourceAddress()
|
||||
} else {
|
||||
oldAddr = n.DestinationAddress()
|
||||
}
|
||||
|
||||
t.UpdateChecksumPseudoHeaderAddress(oldAddr, newAddr, fullChecksum)
|
||||
}
|
||||
case header.ICMPv4:
|
||||
switch icmpType := t.Type(); icmpType {
|
||||
case header.ICMPv4Echo:
|
||||
if updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPortOrIdent)
|
||||
}
|
||||
case header.ICMPv4EchoReply:
|
||||
if !updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPortOrIdent)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected ICMPv4 type = %d", icmpType))
|
||||
}
|
||||
case header.ICMPv6:
|
||||
switch icmpType := t.Type(); icmpType {
|
||||
case header.ICMPv6EchoRequest:
|
||||
if updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPortOrIdent)
|
||||
}
|
||||
case header.ICMPv6EchoReply:
|
||||
if !updateSRCFields {
|
||||
t.SetIdentWithChecksumUpdate(newPortOrIdent)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected ICMPv6 type = %d", icmpType))
|
||||
}
|
||||
|
||||
var oldAddr tcpip.Address
|
||||
if updateSRCFields {
|
||||
oldAddr = n.SourceAddress()
|
||||
} else {
|
||||
oldAddr = n.DestinationAddress()
|
||||
}
|
||||
|
||||
t.UpdateChecksumPseudoHeaderAddress(oldAddr, newAddr)
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled transport = %#v", t))
|
||||
}
|
||||
|
||||
if checksummableNetHeader, ok := n.(header.ChecksummableNetwork); ok {
|
||||
if updateSRCFields {
|
||||
checksummableNetHeader.SetSourceAddressWithChecksumUpdate(newAddr)
|
||||
} else {
|
||||
checksummableNetHeader.SetDestinationAddressWithChecksumUpdate(newAddr)
|
||||
}
|
||||
} else if updateSRCFields {
|
||||
n.SetSourceAddress(newAddr)
|
||||
} else {
|
||||
n.SetDestinationAddress(newAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// CalculateTransportChecksum calculates the transport-layer checksum of the
|
||||
// packet.
|
||||
// TODO: b/521901282 - Verify with GSO.
|
||||
func (pk *PacketBuffer) CalculateTransportChecksum() {
|
||||
netHdr, transHdr, isICMPError, ok := pk.GetHeaders()
|
||||
if isICMPError {
|
||||
// Skip ICMP errors because GetHeaders() returns inner headers, but pk.Data()
|
||||
// contains the outer payload (including inner IP header), which would
|
||||
// corrupt the checksum calculation if used as the transport payload.
|
||||
// Inner headers are already incrementally updated by NAT if needed.
|
||||
// This aligns with Linux, which also relies on incremental updates for
|
||||
// inner headers and does not perform full recalculation from scratch.
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
// Try to parse headers from Data if not set (e.g., forwarded packet).
|
||||
if pk.NetworkProtocolNumber == 0 {
|
||||
return
|
||||
}
|
||||
netHdr = pk.Network()
|
||||
transProto := netHdr.TransportProtocol()
|
||||
|
||||
var headerSize int
|
||||
switch transProto {
|
||||
case header.TCPProtocolNumber:
|
||||
// Peek at minimum TCP header to find data offset (which includes options).
|
||||
b, ok := pk.Data().PullUp(header.TCPMinimumSize)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
tcp := header.TCP(b)
|
||||
headerSize = int(tcp.DataOffset())
|
||||
if headerSize < header.TCPMinimumSize {
|
||||
return
|
||||
}
|
||||
case header.UDPProtocolNumber:
|
||||
headerSize = header.UDPMinimumSize
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
// Consume the transport header.
|
||||
if _, ok := pk.TransportHeader().Consume(headerSize); !ok {
|
||||
return
|
||||
}
|
||||
pk.TransportProtocolNumber = transProto
|
||||
|
||||
// Refresh headers.
|
||||
netHdr, transHdr, isICMPError, ok = pk.GetHeaders()
|
||||
if !ok || isICMPError {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var xsum uint16
|
||||
switch t := transHdr.(type) {
|
||||
case header.TCP:
|
||||
src := netHdr.SourceAddress()
|
||||
dst := netHdr.DestinationAddress()
|
||||
proto := netHdr.TransportProtocol()
|
||||
totalLen := uint16(len(t) + pk.Data().Size())
|
||||
xsum = header.PseudoHeaderChecksum(proto, src, dst, totalLen)
|
||||
xsum = checksum.Combine(xsum, pk.Data().Checksum())
|
||||
t.SetChecksum(0)
|
||||
t.SetChecksum(^t.CalculateChecksum(xsum))
|
||||
case header.UDP:
|
||||
src := netHdr.SourceAddress()
|
||||
dst := netHdr.DestinationAddress()
|
||||
proto := netHdr.TransportProtocol()
|
||||
totalLen := uint16(len(t) + pk.Data().Size())
|
||||
xsum = header.PseudoHeaderChecksum(proto, src, dst, totalLen)
|
||||
xsum = checksum.Combine(xsum, pk.Data().Checksum())
|
||||
t.SetChecksum(0)
|
||||
csum := ^t.CalculateChecksum(xsum)
|
||||
// udp csum RFC 768.
|
||||
if csum == 0 {
|
||||
csum = 0xFFFF
|
||||
}
|
||||
t.SetChecksum(csum)
|
||||
}
|
||||
}
|
||||
@@ -92,5 +92,5 @@ func packetEndpointListinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
packetEndpointListinitLockNames()
|
||||
packetEndpointListprefixIndex = locking.NewMutexClass(reflect.TypeOf(packetEndpointListRWMutex{}), packetEndpointListlockNames)
|
||||
packetEndpointListprefixIndex = locking.NewMutexClass(reflect.TypeFor[packetEndpointListRWMutex](), packetEndpointListlockNames)
|
||||
}
|
||||
@@ -92,5 +92,5 @@ func packetEPsinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
packetEPsinitLockNames()
|
||||
packetEPsprefixIndex = locking.NewMutexClass(reflect.TypeOf(packetEPsRWMutex{}), packetEPslockNames)
|
||||
packetEPsprefixIndex = locking.NewMutexClass(reflect.TypeFor[packetEPsRWMutex](), packetEPslockNames)
|
||||
}
|
||||
+1
-1
@@ -60,5 +60,5 @@ func packetsPendingLinkResolutioninitLockNames() {}
|
||||
|
||||
func init() {
|
||||
packetsPendingLinkResolutioninitLockNames()
|
||||
packetsPendingLinkResolutionprefixIndex = locking.NewMutexClass(reflect.TypeOf(packetsPendingLinkResolutionMutex{}), packetsPendingLinkResolutionlockNames)
|
||||
packetsPendingLinkResolutionprefixIndex = locking.NewMutexClass(reflect.TypeFor[packetsPendingLinkResolutionMutex](), packetsPendingLinkResolutionlockNames)
|
||||
}
|
||||
@@ -39,12 +39,16 @@ type packetsPendingLinkResolutionMu struct {
|
||||
// The packets to send once the resolver completes.
|
||||
//
|
||||
// The link resolution channel is used as the key for this map.
|
||||
//
|
||||
// +checklocks:packetsPendingLinkResolutionMutex
|
||||
packets map[<-chan struct{}][]pendingPacket
|
||||
|
||||
// FIFO of channels used to cancel the oldest goroutine waiting for
|
||||
// link-address resolution.
|
||||
//
|
||||
// cancelChans holds the same channels that are used as keys to packets.
|
||||
//
|
||||
// +checklocks:packetsPendingLinkResolutionMutex
|
||||
cancelChans []<-chan struct{}
|
||||
}
|
||||
|
||||
@@ -183,6 +187,8 @@ func (f *packetsPendingLinkResolution) enqueue(r *Route, pkt *PacketBuffer) tcpi
|
||||
// newCancelChannelLocked appends the link resolution channel to a FIFO. If the
|
||||
// maximum number of pending resolutions is reached, the oldest channel will be
|
||||
// removed and its associated pending packets will be returned.
|
||||
//
|
||||
// +checklocks:f.mu.packetsPendingLinkResolutionMutex
|
||||
func (f *packetsPendingLinkResolution) newCancelChannelLocked(newCH <-chan struct{}) []pendingPacket {
|
||||
f.mu.cancelChans = append(f.mu.cancelChans, newCH)
|
||||
if len(f.mu.cancelChans) <= maxPendingResolutions {
|
||||
|
||||
+5
-1
@@ -22,10 +22,13 @@ import (
|
||||
|
||||
// lockedRandomSource provides a threadsafe rand.Source.
|
||||
type lockedRandomSource struct {
|
||||
mu sync.Mutex
|
||||
mu sync.Mutex
|
||||
|
||||
// +checklocks:mu
|
||||
src rand.Source
|
||||
}
|
||||
|
||||
// +checklocksexclude:r.mu
|
||||
func (r *lockedRandomSource) Int63() (n int64) {
|
||||
r.mu.Lock()
|
||||
n = r.src.Int63()
|
||||
@@ -33,6 +36,7 @@ func (r *lockedRandomSource) Int63() (n int64) {
|
||||
return n
|
||||
}
|
||||
|
||||
// +checklocksexclude:r.mu
|
||||
func (r *lockedRandomSource) Seed(seed int64) {
|
||||
r.mu.Lock()
|
||||
r.src.Seed(seed)
|
||||
|
||||
+31
-6
@@ -58,6 +58,10 @@ type NetworkPacketInfo struct {
|
||||
// address.
|
||||
LocalAddressBroadcast bool
|
||||
|
||||
// LocalAddressTemporary is true if the packet's local address is a temporary
|
||||
// address.
|
||||
LocalAddressTemporary bool
|
||||
|
||||
// IsForwardedPacket is true if the packet is being forwarded.
|
||||
IsForwardedPacket bool
|
||||
}
|
||||
@@ -203,18 +207,18 @@ type PacketMMapOpts struct {
|
||||
IsRx bool
|
||||
Cooked bool
|
||||
Stack *Stack
|
||||
Stats *tcpip.TransportEndpointStats
|
||||
Wq *waiter.Queue
|
||||
NICID tcpip.NICID
|
||||
NetProto tcpip.NetworkProtocolNumber
|
||||
PacketEndpoint MappablePacketEndpoint
|
||||
Version int
|
||||
Reserve uint32
|
||||
}
|
||||
|
||||
// PacketMMapEndpoint is the interface implemented by endpoints to handle memory
|
||||
// mapped packets over the packet transport protocol (PACKET_MMAP).
|
||||
type PacketMMapEndpoint interface {
|
||||
// HandlePacket is called by the stack when new packets arrive that
|
||||
// match the endpoint.
|
||||
// match the endpoint. It returns true if the packet was handled by the
|
||||
// endpoint and false otherwise.
|
||||
//
|
||||
// Implementers should treat packet as immutable and should copy it
|
||||
// before modification.
|
||||
@@ -223,13 +227,17 @@ type PacketMMapEndpoint interface {
|
||||
// should construct its own ethernet header for applications.
|
||||
//
|
||||
// HandlePacket may modify pkt.
|
||||
HandlePacket(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, pkt *PacketBuffer)
|
||||
HandlePacket(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, pkt *PacketBuffer) bool
|
||||
|
||||
// Close releases any resources associated with the endpoint.
|
||||
Close()
|
||||
|
||||
// Readiness returns the events that the endpoint is ready for.
|
||||
Readiness(mask waiter.EventMask) waiter.EventMask
|
||||
|
||||
// Stats returns the statistics for the endpoint that can be used for
|
||||
// getsockopt(PACKET_STATISTICS).
|
||||
Stats() tcpip.TpacketStats
|
||||
}
|
||||
|
||||
// UnknownDestinationPacketDisposition enumerates the possible return values from
|
||||
@@ -357,6 +365,21 @@ type TransportDispatcher interface {
|
||||
DeliverRawPacket(tcpip.TransportProtocolNumber, *PacketBuffer)
|
||||
}
|
||||
|
||||
// TransportDispatcherWithDefaultHandlerResult extends TransportDispatcher with
|
||||
// default-handler-specific delivery metadata.
|
||||
type TransportDispatcherWithDefaultHandlerResult interface {
|
||||
TransportDispatcher
|
||||
|
||||
// DeliverTransportPacketWithDefaultHandlerResult delivers packets to the
|
||||
// appropriate transport protocol endpoint and reports whether the packet was
|
||||
// specifically handled by the per-stack default transport protocol handler.
|
||||
//
|
||||
// pkt.NetworkHeader must be set before calling this method.
|
||||
//
|
||||
// DeliverTransportPacketWithDefaultHandlerResult may modify the packet.
|
||||
DeliverTransportPacketWithDefaultHandlerResult(tcpip.TransportProtocolNumber, *PacketBuffer) (TransportPacketDisposition, bool)
|
||||
}
|
||||
|
||||
// PacketLooping specifies where an outbound packet should be sent.
|
||||
type PacketLooping byte
|
||||
|
||||
@@ -864,6 +887,9 @@ type NetworkEndpoint interface {
|
||||
// minus the network endpoint max header length.
|
||||
MTU() uint32
|
||||
|
||||
// EndpointHeaderSize returns the size of this endpoint header.
|
||||
EndpointHeaderSize() uint32
|
||||
|
||||
// MaxHeaderLength returns the maximum size the network (and lower
|
||||
// level layers combined) headers can have. Higher levels use this
|
||||
// information to reserve space in the front of the packets they're
|
||||
@@ -1127,7 +1153,6 @@ const (
|
||||
CapabilityRXChecksumOffload
|
||||
CapabilityResolutionRequired
|
||||
CapabilitySaveRestore
|
||||
CapabilityDisconnectOk
|
||||
CapabilityLoopback
|
||||
)
|
||||
|
||||
|
||||
+10
-6
@@ -180,7 +180,7 @@ func constructAndValidateRoute(netProto tcpip.NetworkProtocolNumber, addressEndp
|
||||
// AssignableAddressEndpoint.
|
||||
func makeRoute(netProto tcpip.NetworkProtocolNumber, gateway, localAddr, remoteAddr tcpip.Address, outgoingNIC, localAddressNIC *nic, localAddressEndpoint AssignableAddressEndpoint, handleLocal, multicastLoop bool, mtu uint32) *Route {
|
||||
if localAddressNIC.stack != outgoingNIC.stack {
|
||||
panic(fmt.Sprintf("cannot create a route with NICs from different stacks"))
|
||||
panic("cannot create a route with NICs from different stacks")
|
||||
}
|
||||
|
||||
if localAddr.BitLen() == 0 {
|
||||
@@ -245,6 +245,14 @@ func makeRoute(netProto tcpip.NetworkProtocolNumber, gateway, localAddr, remoteA
|
||||
}
|
||||
|
||||
func makeRouteInner(netProto tcpip.NetworkProtocolNumber, localAddr, remoteAddr tcpip.Address, outgoingNIC, localAddressNIC *nic, localAddressEndpoint AssignableAddressEndpoint, loop PacketLooping, mtu uint32) *Route {
|
||||
if mtu != 0 {
|
||||
adjusted := mtu - outgoingNIC.getNetworkEndpoint(netProto).EndpointHeaderSize()
|
||||
if adjusted > mtu {
|
||||
mtu = 0
|
||||
} else {
|
||||
mtu = adjusted
|
||||
}
|
||||
}
|
||||
r := &Route{
|
||||
routeInfo: routeInfo{
|
||||
NetProto: netProto,
|
||||
@@ -339,11 +347,6 @@ func (r *Route) HasSaveRestoreCapability() bool {
|
||||
return r.outgoingNIC.NetworkLinkEndpoint.Capabilities()&CapabilitySaveRestore != 0
|
||||
}
|
||||
|
||||
// HasDisconnectOkCapability returns true if the route supports disconnecting.
|
||||
func (r *Route) HasDisconnectOkCapability() bool {
|
||||
return r.outgoingNIC.NetworkLinkEndpoint.Capabilities()&CapabilityDisconnectOk != 0
|
||||
}
|
||||
|
||||
// GSOMaxSize returns the maximum GSO packet size.
|
||||
func (r *Route) GSOMaxSize() uint32 {
|
||||
if gso, ok := r.outgoingNIC.NetworkLinkEndpoint.(GSOEndpoint); ok {
|
||||
@@ -525,6 +528,7 @@ func (r *Route) DefaultTTL() uint8 {
|
||||
// MTU returns the MTU of the route if present, otherwise the MTU of the underlying network endpoint.
|
||||
func (r *Route) MTU() uint32 {
|
||||
if r.mtu > 0 {
|
||||
// r.mtu is already adjusted to account for IP headers. See makeRouteInner.
|
||||
return r.mtu
|
||||
}
|
||||
return r.outgoingNIC.getNetworkEndpoint(r.NetProto()).MTU()
|
||||
|
||||
+1
-1
@@ -92,5 +92,5 @@ func routeinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
routeinitLockNames()
|
||||
routeprefixIndex = locking.NewMutexClass(reflect.TypeOf(routeRWMutex{}), routelockNames)
|
||||
routeprefixIndex = locking.NewMutexClass(reflect.TypeFor[routeRWMutex](), routelockNames)
|
||||
}
|
||||
@@ -92,5 +92,5 @@ func routeStackinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
routeStackinitLockNames()
|
||||
routeStackprefixIndex = locking.NewMutexClass(reflect.TypeOf(routeStackRWMutex{}), routeStacklockNames)
|
||||
routeStackprefixIndex = locking.NewMutexClass(reflect.TypeFor[routeStackRWMutex](), routeStacklockNames)
|
||||
}
|
||||
@@ -22,6 +22,31 @@ import (
|
||||
cryptorand "gvisor.dev/gvisor/pkg/rand"
|
||||
)
|
||||
|
||||
// beforeSave is invoked by stateify.
|
||||
func (s *Stack) beforeSave() {
|
||||
// removeConf will be set only in case of save/restore.
|
||||
s.mu.Lock()
|
||||
if !s.removeConf {
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Remove all the NICs and routes from the stack as they will be
|
||||
// created again during restore based on the new network config.
|
||||
deferActs := make([]func(), 0)
|
||||
for id := range s.nics {
|
||||
act, _ := s.removeNICLocked(id, true /* closeLinkEndpoint */)
|
||||
if act != nil {
|
||||
deferActs = append(deferActs, act)
|
||||
}
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
for _, act := range deferActs {
|
||||
act()
|
||||
}
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (s *Stack) afterLoad(context.Context) {
|
||||
s.insecureRNG = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
+373
-103
@@ -20,11 +20,11 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
cryptorand "gvisor.dev/gvisor/pkg/rand"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/ports"
|
||||
@@ -96,6 +97,8 @@ type Stack struct {
|
||||
// +checklocks:mu
|
||||
nics map[tcpip.NICID]*nic `state:"nosave"`
|
||||
// +checklocks:mu
|
||||
loopbackNIC *nic `state:"nosave"`
|
||||
// +checklocks:mu
|
||||
defaultForwardingEnabled map[tcpip.NetworkProtocolNumber]struct{}
|
||||
|
||||
// nicIDGen is used to generate NIC IDs.
|
||||
@@ -111,6 +114,10 @@ type Stack struct {
|
||||
// clock is used to generate user-visible times.
|
||||
clock tcpip.Clock
|
||||
|
||||
// clockResolution is an upper bound on the quantization interval of
|
||||
// monotonic timestamps returned by clock.
|
||||
clockResolution time.Duration
|
||||
|
||||
// handleLocal allows non-loopback interfaces to loop packets.
|
||||
handleLocal bool
|
||||
|
||||
@@ -118,6 +125,17 @@ type Stack struct {
|
||||
// TODO(gvisor.dev/issue/4595): S/R this field.
|
||||
tables *IPTables `state:"nosave"`
|
||||
|
||||
// nftables is the nftables interface for packet filtering and manipulation rules.
|
||||
// Using atomic.Pointer for RCU lock-free reads.
|
||||
nftables atomic.Pointer[NFTablesInterface] `state:"nosave"`
|
||||
|
||||
// nftablesUpdateMu serializes concurrent netlink batch modifications to nftables.
|
||||
nftablesUpdateMu sync.Mutex `state:"nosave"`
|
||||
|
||||
// nftablesConfigured indicates whether NFTables is configured with at
|
||||
// least one rule on a chain at a network hook.
|
||||
nftablesConfigured atomicbitops.Bool
|
||||
|
||||
// restoredEndpoints is a list of endpoints that need to be restored if the
|
||||
// stack is being restored.
|
||||
restoredEndpoints []RestoredEndpoint
|
||||
@@ -174,8 +192,23 @@ type Stack struct {
|
||||
// initialized at stack startup.
|
||||
tsOffsetSecret uint32
|
||||
|
||||
// saveRestoreEnabled indicates whether the stack is saved and restored.
|
||||
saveRestoreEnabled bool
|
||||
// removeConf indicates whether to remove NICs and routes and terminate
|
||||
// active connections before saving. This flag will be set to true only
|
||||
// when resume is false.
|
||||
removeConf bool `state:"nosave"`
|
||||
|
||||
// allowLiveTCPMigration allows TCP connection state to be migrated.
|
||||
// If false, any connected TCP endpoints will be terminated
|
||||
// during save/restore.
|
||||
allowLiveTCPMigration bool `state:"nosave"`
|
||||
|
||||
// externalNetworkingDisabled indicates whether external networking is
|
||||
// disabled. This means all non-loopback NICs are disabled.
|
||||
externalNetworkingDisabled bool
|
||||
|
||||
// allowConnectedOnSave indicates whether connections should be
|
||||
// allowed to remain connected during save.
|
||||
allowConnectedOnSave bool
|
||||
}
|
||||
|
||||
// NetworkProtocolFactory instantiates a network protocol.
|
||||
@@ -203,6 +236,17 @@ type Options struct {
|
||||
// If Clock is nil, tcpip.NewStdClock() will be used.
|
||||
Clock tcpip.Clock
|
||||
|
||||
// ClockResolution is an upper bound on the quantization interval of
|
||||
// timestamps returned by Clock.NowMonotonic. It should be set when the
|
||||
// clock advances in discrete steps large enough to affect TCP loss detection;
|
||||
// for example, some Windows monotonic clocks commonly update every 500 us.
|
||||
// TCP RACK uses this value to avoid declaring loss based on timestamp
|
||||
// quantization alone. It does not describe timer wake-up precision.
|
||||
//
|
||||
// A value of zero means that no quantization allowance is needed or known.
|
||||
// Values less than zero are clamped to zero.
|
||||
ClockResolution time.Duration
|
||||
|
||||
// Stats are optional statistic counters.
|
||||
Stats tcpip.Stats
|
||||
|
||||
@@ -226,6 +270,11 @@ type Options struct {
|
||||
// operations.
|
||||
AllowPacketEndpointWrite bool
|
||||
|
||||
// AllowLiveTCPMigration allows TCP connection state to be migrated.
|
||||
// If false, any connected TCP endpoints will be terminated
|
||||
// during save/restore.
|
||||
AllowLiveTCPMigration bool
|
||||
|
||||
// RandSource is an optional source to use to generate random
|
||||
// numbers. If omitted it defaults to a Source seeded by the data
|
||||
// returned by the stack secure RNG.
|
||||
@@ -238,6 +287,9 @@ type Options struct {
|
||||
// all traffic.
|
||||
IPTables *IPTables
|
||||
|
||||
// NFTables is the nftables interface for packet filtering and manipulation rules.
|
||||
NFTables NFTablesInterface
|
||||
|
||||
// DefaultIPTables is an optional iptables rules constructor that is called
|
||||
// if IPTables is nil. If both fields are nil, iptables will allow all
|
||||
// traffic.
|
||||
@@ -378,6 +430,11 @@ func New(opts Options) *Stack {
|
||||
|
||||
opts.NUDConfigs.resetInvalidFields()
|
||||
|
||||
clockResolution := opts.ClockResolution
|
||||
if clockResolution < 0 {
|
||||
clockResolution = 0
|
||||
}
|
||||
|
||||
s := &Stack{
|
||||
transportProtocols: make(map[tcpip.TransportProtocolNumber]*transportProtocolState),
|
||||
networkProtocols: make(map[tcpip.NetworkProtocolNumber]NetworkProtocol),
|
||||
@@ -387,6 +444,7 @@ func New(opts Options) *Stack {
|
||||
cleanupEndpoints: make(map[TransportEndpoint]struct{}),
|
||||
PortManager: ports.NewPortManager(),
|
||||
clock: clock,
|
||||
clockResolution: clockResolution,
|
||||
stats: opts.Stats.FillIn(),
|
||||
handleLocal: opts.HandleLocal,
|
||||
tables: opts.IPTables,
|
||||
@@ -406,9 +464,11 @@ func New(opts Options) *Stack {
|
||||
Default: DefaultBufferSize,
|
||||
Max: DefaultMaxBufferSize,
|
||||
},
|
||||
tcpInvalidRateLimit: defaultTCPInvalidRateLimit,
|
||||
tsOffsetSecret: secureRNG.Uint32(),
|
||||
tcpInvalidRateLimit: defaultTCPInvalidRateLimit,
|
||||
tsOffsetSecret: secureRNG.Uint32(),
|
||||
allowLiveTCPMigration: opts.AllowLiveTCPMigration,
|
||||
}
|
||||
s.SetNFTables(opts.NFTables)
|
||||
|
||||
// Add specified network protocols.
|
||||
for _, netProtoFactory := range opts.NetworkProtocols {
|
||||
@@ -527,6 +587,16 @@ func (s *Stack) Clock() tcpip.Clock {
|
||||
return s.clock
|
||||
}
|
||||
|
||||
// ClockResolution returns an upper bound on the quantization interval of
|
||||
// monotonic timestamps returned by the Stack's clock.
|
||||
//
|
||||
// A zero value means to trust the clock advances between reads at a precision
|
||||
// close to the precision of the value it returns; consuming code should need no
|
||||
// corrections for clock resolution.
|
||||
func (s *Stack) ClockResolution() time.Duration {
|
||||
return s.clockResolution
|
||||
}
|
||||
|
||||
// Stats returns a mutable copy of the current stats.
|
||||
//
|
||||
// This is not generally exported via the public interface, but is available
|
||||
@@ -721,12 +791,16 @@ func (s *Stack) NICMulticastForwarding(id tcpip.NICID, protocol tcpip.NetworkPro
|
||||
|
||||
// PortRange returns the UDP and TCP inclusive range of ephemeral ports used in
|
||||
// both IPv4 and IPv6.
|
||||
//
|
||||
// +checklocksexclude:s.PortManager.ephemeralMu
|
||||
func (s *Stack) PortRange() (uint16, uint16) {
|
||||
return s.PortManager.PortRange()
|
||||
}
|
||||
|
||||
// SetPortRange sets the UDP and TCP IPv4 and IPv6 ephemeral port range
|
||||
// (inclusive).
|
||||
//
|
||||
// +checklocksexclude:s.PortManager.ephemeralMu
|
||||
func (s *Stack) SetPortRange(start uint16, end uint16) tcpip.Error {
|
||||
return s.PortManager.SetPortRange(start, end)
|
||||
}
|
||||
@@ -798,7 +872,7 @@ func (s *Stack) removeRoutesLocked(match func(tcpip.Route) bool) int {
|
||||
return count
|
||||
}
|
||||
|
||||
// ReplaceRoute replaces the route in the routing table which matchse
|
||||
// ReplaceRoute replaces the route in the routing table which matches
|
||||
// the lookup key for the routing table. If there is no match, the given
|
||||
// route will still be added to the routing table.
|
||||
// The lookup key consists of destination, ToS, scope and output interface.
|
||||
@@ -882,12 +956,15 @@ type NICOptions struct {
|
||||
// EnableExperimentIPOption specifies whether the NIC is responsible for
|
||||
// passing the experiment IP option.
|
||||
EnableExperimentIPOption bool
|
||||
|
||||
// Kind specifies the link kind of the NIC (e.g. "veth", "bridge").
|
||||
Kind string
|
||||
}
|
||||
|
||||
// GetNICByID return a network device associated with the specified ID.
|
||||
func (s *Stack) GetNICByID(id tcpip.NICID) (*nic, tcpip.Error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
n, ok := s.nics[id]
|
||||
if !ok {
|
||||
@@ -929,6 +1006,9 @@ func (s *Stack) CreateNICWithOptions(id tcpip.NICID, ep LinkEndpoint, opts NICOp
|
||||
}
|
||||
}
|
||||
s.nics[id] = n
|
||||
if n.IsLoopback() {
|
||||
s.loopbackNIC = n
|
||||
}
|
||||
ep.SetOnCloseAction(func() {
|
||||
s.RemoveNIC(id)
|
||||
})
|
||||
@@ -1005,7 +1085,7 @@ func (s *Stack) CheckNIC(id tcpip.NICID) bool {
|
||||
// RemoveNIC removes NIC and all related routes from the network stack.
|
||||
func (s *Stack) RemoveNIC(id tcpip.NICID) tcpip.Error {
|
||||
s.mu.Lock()
|
||||
deferAct, err := s.removeNICLocked(id)
|
||||
deferAct, err := s.removeNICLocked(id, true /* closeLinkEndpoint */)
|
||||
s.mu.Unlock()
|
||||
if deferAct != nil {
|
||||
deferAct()
|
||||
@@ -1016,7 +1096,7 @@ func (s *Stack) RemoveNIC(id tcpip.NICID) tcpip.Error {
|
||||
// removeNICLocked removes NIC and all related routes from the network stack.
|
||||
//
|
||||
// +checklocks:s.mu
|
||||
func (s *Stack) removeNICLocked(id tcpip.NICID) (func(), tcpip.Error) {
|
||||
func (s *Stack) removeNICLocked(id tcpip.NICID, closeLinkEndpoint bool) (func(), tcpip.Error) {
|
||||
nic, ok := s.nics[id]
|
||||
if !ok {
|
||||
return nil, &tcpip.ErrUnknownNICID{}
|
||||
@@ -1041,7 +1121,22 @@ func (s *Stack) removeNICLocked(id tcpip.NICID) (func(), tcpip.Error) {
|
||||
}
|
||||
s.routeMu.Unlock()
|
||||
|
||||
return nic.remove(true /* closeLinkEndpoint */)
|
||||
if s.loopbackNIC == nic {
|
||||
s.loopbackNIC = nil
|
||||
}
|
||||
return nic.remove(closeLinkEndpoint)
|
||||
}
|
||||
|
||||
// GetNICCoordinatorID returns the ID of the coordinator device of a NIC.
|
||||
func (s *Stack) GetNICCoordinatorID(id tcpip.NICID) (tcpip.NICID, bool) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
if nic, ok := s.nics[id]; ok {
|
||||
if nic.Primary != nil {
|
||||
return nic.Primary.id, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// SetNICCoordinator sets a coordinator device.
|
||||
@@ -1144,6 +1239,12 @@ type NICInfo struct {
|
||||
// MulticastForwarding holds the forwarding status for each network endpoint
|
||||
// that supports multicast forwarding.
|
||||
MulticastForwarding map[tcpip.NetworkProtocolNumber]bool
|
||||
|
||||
// Primary is the index of the main controlling interface in a bonded setup.
|
||||
Primary tcpip.NICID
|
||||
|
||||
// Kind specifies the link kind of the NIC (e.g. "veth", "bridge").
|
||||
Kind string
|
||||
}
|
||||
|
||||
// HasNIC returns true if the NICID is defined in the stack.
|
||||
@@ -1154,65 +1255,88 @@ func (s *Stack) HasNIC(id tcpip.NICID) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
type forwardingFn func(tcpip.NetworkProtocolNumber) (bool, tcpip.Error)
|
||||
|
||||
func forwardingValue(forwardingFn forwardingFn, proto tcpip.NetworkProtocolNumber, nicID tcpip.NICID, fnName string) (forward bool, ok bool) {
|
||||
switch forwarding, err := forwardingFn(proto); err.(type) {
|
||||
case nil:
|
||||
return forwarding, true
|
||||
case *tcpip.ErrUnknownProtocol:
|
||||
panic(fmt.Sprintf("expected network protocol %d to be available on NIC %d", proto, nicID))
|
||||
case *tcpip.ErrNotSupported:
|
||||
// Not all network protocols support forwarding.
|
||||
default:
|
||||
panic(fmt.Sprintf("nic(id=%d).%s(%d): %s", nicID, fnName, proto, err))
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
// precondition: s.mu is held.
|
||||
func (s *Stack) nicInfo(nic *nic, id tcpip.NICID) *NICInfo {
|
||||
flags := NICStateFlags{
|
||||
Up: true, // Netstack interfaces are always up.
|
||||
Running: nic.Enabled(),
|
||||
Promiscuous: nic.Promiscuous(),
|
||||
Loopback: nic.IsLoopback(),
|
||||
}
|
||||
|
||||
netStats := make(map[tcpip.NetworkProtocolNumber]NetworkEndpointStats)
|
||||
for proto, netEP := range nic.networkEndpoints {
|
||||
netStats[proto] = netEP.Stats()
|
||||
}
|
||||
|
||||
info := NICInfo{
|
||||
Name: nic.name,
|
||||
LinkAddress: nic.NetworkLinkEndpoint.LinkAddress(),
|
||||
ProtocolAddresses: nic.primaryAddresses(),
|
||||
Flags: flags,
|
||||
MTU: nic.NetworkLinkEndpoint.MTU(),
|
||||
Stats: nic.stats.local,
|
||||
NetworkStats: netStats,
|
||||
Context: nic.context,
|
||||
ARPHardwareType: nic.NetworkLinkEndpoint.ARPHardwareType(),
|
||||
Forwarding: make(map[tcpip.NetworkProtocolNumber]bool),
|
||||
MulticastForwarding: make(map[tcpip.NetworkProtocolNumber]bool),
|
||||
Kind: nic.kind,
|
||||
}
|
||||
|
||||
for proto := range s.networkProtocols {
|
||||
if forwarding, ok := forwardingValue(nic.forwarding, proto, id, "forwarding"); ok {
|
||||
info.Forwarding[proto] = forwarding
|
||||
}
|
||||
|
||||
if multicastForwarding, ok := forwardingValue(nic.multicastForwarding, proto, id, "multicastForwarding"); ok {
|
||||
info.MulticastForwarding[proto] = multicastForwarding
|
||||
}
|
||||
}
|
||||
|
||||
if nic.Primary != nil {
|
||||
info.Primary = nic.Primary.id
|
||||
}
|
||||
|
||||
return &info
|
||||
}
|
||||
|
||||
// SingleNICInfo returns the NICInfo for the given NICID.
|
||||
func (s *Stack) SingleNICInfo(id tcpip.NICID) (*NICInfo, bool) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
if nic, ok := s.nics[id]; !ok {
|
||||
return nil, false
|
||||
} else {
|
||||
return s.nicInfo(nic, id), true
|
||||
}
|
||||
}
|
||||
|
||||
// NICInfo returns a map of NICIDs to their associated information.
|
||||
func (s *Stack) NICInfo() map[tcpip.NICID]NICInfo {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
type forwardingFn func(tcpip.NetworkProtocolNumber) (bool, tcpip.Error)
|
||||
forwardingValue := func(forwardingFn forwardingFn, proto tcpip.NetworkProtocolNumber, nicID tcpip.NICID, fnName string) (forward bool, ok bool) {
|
||||
switch forwarding, err := forwardingFn(proto); err.(type) {
|
||||
case nil:
|
||||
return forwarding, true
|
||||
case *tcpip.ErrUnknownProtocol:
|
||||
panic(fmt.Sprintf("expected network protocol %d to be available on NIC %d", proto, nicID))
|
||||
case *tcpip.ErrNotSupported:
|
||||
// Not all network protocols support forwarding.
|
||||
default:
|
||||
panic(fmt.Sprintf("nic(id=%d).%s(%d): %s", nicID, fnName, proto, err))
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
nics := make(map[tcpip.NICID]NICInfo)
|
||||
for id, nic := range s.nics {
|
||||
flags := NICStateFlags{
|
||||
Up: true, // Netstack interfaces are always up.
|
||||
Running: nic.Enabled(),
|
||||
Promiscuous: nic.Promiscuous(),
|
||||
Loopback: nic.IsLoopback(),
|
||||
}
|
||||
|
||||
netStats := make(map[tcpip.NetworkProtocolNumber]NetworkEndpointStats)
|
||||
for proto, netEP := range nic.networkEndpoints {
|
||||
netStats[proto] = netEP.Stats()
|
||||
}
|
||||
|
||||
info := NICInfo{
|
||||
Name: nic.name,
|
||||
LinkAddress: nic.NetworkLinkEndpoint.LinkAddress(),
|
||||
ProtocolAddresses: nic.primaryAddresses(),
|
||||
Flags: flags,
|
||||
MTU: nic.NetworkLinkEndpoint.MTU(),
|
||||
Stats: nic.stats.local,
|
||||
NetworkStats: netStats,
|
||||
Context: nic.context,
|
||||
ARPHardwareType: nic.NetworkLinkEndpoint.ARPHardwareType(),
|
||||
Forwarding: make(map[tcpip.NetworkProtocolNumber]bool),
|
||||
MulticastForwarding: make(map[tcpip.NetworkProtocolNumber]bool),
|
||||
}
|
||||
|
||||
for proto := range s.networkProtocols {
|
||||
if forwarding, ok := forwardingValue(nic.forwarding, proto, id, "forwarding"); ok {
|
||||
info.Forwarding[proto] = forwarding
|
||||
}
|
||||
|
||||
if multicastForwarding, ok := forwardingValue(nic.multicastForwarding, proto, id, "multicastForwarding"); ok {
|
||||
info.MulticastForwarding[proto] = multicastForwarding
|
||||
}
|
||||
}
|
||||
|
||||
nics[id] = info
|
||||
nics[id] = *s.nicInfo(nic, id)
|
||||
}
|
||||
return nics
|
||||
}
|
||||
@@ -1378,6 +1502,29 @@ func (s *Stack) findLocalRouteFromNICRLocked(localAddressNIC *nic, localAddr, re
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *Stack) loopbackLocalRoute(localAddressNIC *nic, localAddr, remoteAddr tcpip.Address, netProto tcpip.NetworkProtocolNumber) *Route {
|
||||
localAddressEndpoint := localAddressNIC.getAddressOrCreateTempInner(netProto, localAddr, true /* createTemp */, NeverPrimaryEndpoint)
|
||||
if localAddressEndpoint == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
r := makeLocalRoute(
|
||||
netProto,
|
||||
localAddr,
|
||||
remoteAddr,
|
||||
localAddressNIC,
|
||||
localAddressNIC,
|
||||
localAddressEndpoint,
|
||||
)
|
||||
|
||||
if r.IsOutboundBroadcast() {
|
||||
r.Release()
|
||||
return nil
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// findLocalRouteRLocked returns a local route.
|
||||
//
|
||||
// A local route is a route to some remote address which the stack owns. That
|
||||
@@ -1390,6 +1537,22 @@ func (s *Stack) findLocalRouteRLocked(localAddressNICID tcpip.NICID, localAddr,
|
||||
}
|
||||
|
||||
if localAddressNICID == 0 {
|
||||
if s.loopbackNIC != nil {
|
||||
// Send all packets directed to local ip addresses through the loopback device.
|
||||
for _, nic := range s.nics {
|
||||
if !nic.hasAddress(netProto, remoteAddr) {
|
||||
continue
|
||||
}
|
||||
if isSubnetBroadcastOnNIC(nic, netProto, remoteAddr) {
|
||||
break
|
||||
}
|
||||
if r := s.loopbackLocalRoute(s.loopbackNIC, localAddr, remoteAddr, netProto); r != nil {
|
||||
return r
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, localAddressNIC := range s.nics {
|
||||
if r := s.findLocalRouteFromNICRLocked(localAddressNIC, localAddr, remoteAddr, netProto); r != nil {
|
||||
return r
|
||||
@@ -1591,8 +1754,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(https://gvisor.dev/issues/8105): This should be ErrNetworkUnreachable.
|
||||
return nil, &tcpip.ErrHostUnreachable{}
|
||||
return nil, &tcpip.ErrNetworkUnreachable{}
|
||||
}
|
||||
|
||||
if id == 0 {
|
||||
@@ -1605,13 +1767,11 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
|
||||
}
|
||||
|
||||
if needRoute {
|
||||
// TODO(https://gvisor.dev/issues/8105): This should be ErrNetworkUnreachable.
|
||||
return nil, &tcpip.ErrHostUnreachable{}
|
||||
return nil, &tcpip.ErrNetworkUnreachable{}
|
||||
}
|
||||
if header.IsV6LoopbackAddress(remoteAddr) {
|
||||
return nil, &tcpip.ErrBadLocalAddress{}
|
||||
}
|
||||
// TODO(https://gvisor.dev/issues/8105): This should be ErrNetworkUnreachable.
|
||||
return nil, &tcpip.ErrNetworkUnreachable{}
|
||||
}
|
||||
|
||||
@@ -1940,7 +2100,7 @@ func (s *Stack) Wait() {
|
||||
for id, n := range s.nics {
|
||||
// Remove NIC to ensure that qDisc goroutines are correctly
|
||||
// terminated on stack teardown.
|
||||
act, _ := s.removeNICLocked(id)
|
||||
act, _ := s.removeNICLocked(id, true /* closeLinkEndpoint */)
|
||||
n.NetworkLinkEndpoint.Wait()
|
||||
if act != nil {
|
||||
deferActs = append(deferActs, act)
|
||||
@@ -1974,26 +2134,43 @@ func (s *Stack) getNICs() map[tcpip.NICID]*nic {
|
||||
return nics
|
||||
}
|
||||
|
||||
// ResetConfig resets the stack's NICs and ID generator.
|
||||
func (s *Stack) ResetConfig() {
|
||||
nics := make(map[tcpip.NICID]*nic)
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.nics = nics
|
||||
s.loopbackNIC = nil
|
||||
s.nicIDGen.Store(0)
|
||||
}
|
||||
|
||||
// ReplaceConfig replaces config in the loaded stack.
|
||||
func (s *Stack) ReplaceConfig(st *Stack) {
|
||||
if st == nil {
|
||||
panic("stack.Stack cannot be nil when netstack s/r is enabled")
|
||||
panic("stack.Stack cannot be nil when replacing config")
|
||||
}
|
||||
|
||||
// Update route table.
|
||||
s.SetRouteTable(st.GetRouteTable())
|
||||
|
||||
// Update NICs.
|
||||
nics := st.getNICs()
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.nics = make(map[tcpip.NICID]*nic)
|
||||
|
||||
// Update iptables and nftables.
|
||||
s.tables = st.IPTables()
|
||||
s.SetNFTables(st.NFTables())
|
||||
for id, nic := range nics {
|
||||
nic.stack = s
|
||||
s.nics[id] = nic
|
||||
if nic.IsLoopback() {
|
||||
s.loopbackNIC = nic
|
||||
} else if s.externalNetworkingDisabled {
|
||||
nic.disable()
|
||||
}
|
||||
_ = s.NextNICID()
|
||||
}
|
||||
s.tables = st.tables
|
||||
}
|
||||
|
||||
// Restore restarts the stack after a restore. This must be called after the
|
||||
@@ -2004,18 +2181,18 @@ func (s *Stack) Restore() {
|
||||
s.mu.Lock()
|
||||
eps := s.restoredEndpoints
|
||||
s.restoredEndpoints = nil
|
||||
saveRestoreEnabled := s.saveRestoreEnabled
|
||||
s.mu.Unlock()
|
||||
for _, e := range eps {
|
||||
e.Restore(s)
|
||||
}
|
||||
// Now resume any protocol level background workers.
|
||||
|
||||
// Make sure all the endpoints are loaded correctly before resuming the
|
||||
// protocol level background workers.
|
||||
tcpip.AsyncLoading.Wait()
|
||||
|
||||
// Now restore any protocol level background workers.
|
||||
for _, p := range s.transportProtocols {
|
||||
if saveRestoreEnabled {
|
||||
p.proto.Restore()
|
||||
} else {
|
||||
p.proto.Resume()
|
||||
}
|
||||
p.proto.Restore()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2091,6 +2268,12 @@ func (s *Stack) unregisterPacketEndpointLocked(nicID tcpip.NICID, netProto tcpip
|
||||
// WritePacketToRemote writes a payload on the specified NIC using the provided
|
||||
// network protocol and remote link address.
|
||||
func (s *Stack) WritePacketToRemote(nicID tcpip.NICID, remote tcpip.LinkAddress, netProto tcpip.NetworkProtocolNumber, payload buffer.Buffer) tcpip.Error {
|
||||
return s.WritePacketToRemoteWithMark(nicID, remote, netProto, payload, 0)
|
||||
}
|
||||
|
||||
// WritePacketToRemoteWithMark writes a payload on the specified NIC using the
|
||||
// provided network protocol, remote link address, and packet mark.
|
||||
func (s *Stack) WritePacketToRemoteWithMark(nicID tcpip.NICID, remote tcpip.LinkAddress, netProto tcpip.NetworkProtocolNumber, payload buffer.Buffer, mark uint32) tcpip.Error {
|
||||
s.mu.Lock()
|
||||
nic, ok := s.nics[nicID]
|
||||
s.mu.Unlock()
|
||||
@@ -2100,6 +2283,7 @@ func (s *Stack) WritePacketToRemote(nicID tcpip.NICID, remote tcpip.LinkAddress,
|
||||
pkt := NewPacketBuffer(PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(nic.MaxHeaderLength()),
|
||||
Payload: payload,
|
||||
Mark: mark,
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
pkt.NetworkProtocolNumber = netProto
|
||||
@@ -2109,6 +2293,12 @@ func (s *Stack) WritePacketToRemote(nicID tcpip.NICID, remote tcpip.LinkAddress,
|
||||
// WriteRawPacket writes data directly to the specified NIC without adding any
|
||||
// headers.
|
||||
func (s *Stack) WriteRawPacket(nicID tcpip.NICID, proto tcpip.NetworkProtocolNumber, payload buffer.Buffer) tcpip.Error {
|
||||
return s.WriteRawPacketWithMark(nicID, proto, payload, 0)
|
||||
}
|
||||
|
||||
// WriteRawPacketWithMark writes data directly to the specified NIC without adding any
|
||||
// headers, setting the specified packet mark.
|
||||
func (s *Stack) WriteRawPacketWithMark(nicID tcpip.NICID, proto tcpip.NetworkProtocolNumber, payload buffer.Buffer, mark uint32) tcpip.Error {
|
||||
s.mu.RLock()
|
||||
nic, ok := s.nics[nicID]
|
||||
s.mu.RUnlock()
|
||||
@@ -2118,6 +2308,7 @@ func (s *Stack) WriteRawPacket(nicID tcpip.NICID, proto tcpip.NetworkProtocolNum
|
||||
|
||||
pkt := NewPacketBuffer(PacketBufferOptions{
|
||||
Payload: payload,
|
||||
Mark: mark,
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
pkt.NetworkProtocolNumber = proto
|
||||
@@ -2183,6 +2374,49 @@ func (s *Stack) IPTables() *IPTables {
|
||||
return s.tables
|
||||
}
|
||||
|
||||
// SetIPTables sets the stack's iptables.
|
||||
func (s *Stack) SetIPTables(tables *IPTables) {
|
||||
s.tables = tables
|
||||
}
|
||||
|
||||
// NFTables returns the stack's nftables.
|
||||
func (s *Stack) NFTables() NFTablesInterface {
|
||||
val := s.nftables.Load()
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
return *val
|
||||
}
|
||||
|
||||
// SetNFTables sets the stack's nftables.
|
||||
func (s *Stack) SetNFTables(nft NFTablesInterface) {
|
||||
if nft == nil {
|
||||
s.nftables.Store(nil)
|
||||
} else {
|
||||
s.nftables.Store(&nft)
|
||||
}
|
||||
}
|
||||
|
||||
// LockNFTablesUpdate locks the stack's nftables update mutex for netlink batch modification.
|
||||
func (s *Stack) LockNFTablesUpdate() {
|
||||
s.nftablesUpdateMu.Lock()
|
||||
}
|
||||
|
||||
// UnlockNFTablesUpdate unlocks the stack's nftables update mutex.
|
||||
func (s *Stack) UnlockNFTablesUpdate() {
|
||||
s.nftablesUpdateMu.Unlock()
|
||||
}
|
||||
|
||||
// IsNFTablesConfigured returns true if the stack has nftables configured.
|
||||
func (s *Stack) IsNFTablesConfigured() bool {
|
||||
return s.nftablesConfigured.Load()
|
||||
}
|
||||
|
||||
// SetNFTablesConfigured sets whether the stack has nftables configured.
|
||||
func (s *Stack) SetNFTablesConfigured(configured bool) {
|
||||
s.nftablesConfigured.Store(configured)
|
||||
}
|
||||
|
||||
// ICMPLimit returns the maximum number of ICMP messages that can be sent
|
||||
// in one second.
|
||||
func (s *Stack) ICMPLimit() rate.Limit {
|
||||
@@ -2389,12 +2623,11 @@ func (s *Stack) SetNICStack(id tcpip.NICID, peer *Stack) (tcpip.NICID, tcpip.Err
|
||||
s.mu.Unlock()
|
||||
return id, nil
|
||||
}
|
||||
delete(s.nics, id)
|
||||
|
||||
// Remove routes in-place. n tracks the number of routes written.
|
||||
s.RemoveRoutes(func(r tcpip.Route) bool { return r.NIC == id })
|
||||
ne := nic.NetworkLinkEndpoint.(LinkEndpoint)
|
||||
deferAct, err := nic.remove(false /* closeLinkEndpoint */)
|
||||
linkEp := nic.NetworkLinkEndpoint.(LinkEndpoint)
|
||||
name := nic.Name()
|
||||
|
||||
deferAct, err := s.removeNICLocked(id, false /* closeLinkEndpoint */)
|
||||
s.mu.Unlock()
|
||||
if deferAct != nil {
|
||||
deferAct()
|
||||
@@ -2404,34 +2637,71 @@ func (s *Stack) SetNICStack(id tcpip.NICID, peer *Stack) (tcpip.NICID, tcpip.Err
|
||||
}
|
||||
|
||||
id = tcpip.NICID(peer.NextNICID())
|
||||
return id, peer.CreateNICWithOptions(id, ne, NICOptions{Name: nic.Name()})
|
||||
return id, peer.CreateNICWithOptions(id, linkEp, NICOptions{Name: name})
|
||||
}
|
||||
|
||||
// EnableSaveRestore marks the saveRestoreEnabled to true.
|
||||
func (s *Stack) EnableSaveRestore() {
|
||||
// SetRemoveConf sets the removeConf in stack to the given value.
|
||||
func (s *Stack) SetRemoveConf(removeConf bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.saveRestoreEnabled = true
|
||||
s.removeConf = removeConf
|
||||
}
|
||||
|
||||
// IsSaveRestoreEnabled returns true if save restore is enabled for the stack.
|
||||
func (s *Stack) IsSaveRestoreEnabled() bool {
|
||||
// GetRemoveConf gets the removeConf from stack.
|
||||
func (s *Stack) GetRemoveConf() bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.removeConf
|
||||
}
|
||||
|
||||
// SetAllowConnectedOnSave sets allowConnectedOnSave in stack with the given value.
|
||||
func (s *Stack) SetAllowConnectedOnSave(allowConnectedOnSave bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return s.saveRestoreEnabled
|
||||
s.allowConnectedOnSave = allowConnectedOnSave
|
||||
}
|
||||
|
||||
// contextID is this package's type for context.Context.Value keys.
|
||||
type contextID int
|
||||
// GetAllowConnectedOnSave gets the allowConnectedOnSave from stack.
|
||||
func (s *Stack) GetAllowConnectedOnSave() bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.allowConnectedOnSave
|
||||
}
|
||||
|
||||
const (
|
||||
// CtxRestoreStack is a Context.Value key for the stack to be used in restore.
|
||||
CtxRestoreStack contextID = iota
|
||||
)
|
||||
// AllowLiveTCPMigration returns if TCP connections can be migrated.
|
||||
func (s *Stack) AllowLiveTCPMigration() bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.allowLiveTCPMigration
|
||||
}
|
||||
|
||||
// RestoreStackFromContext returns the stack to be used during restore.
|
||||
func RestoreStackFromContext(ctx context.Context) *Stack {
|
||||
return ctx.Value(CtxRestoreStack).(*Stack)
|
||||
// SetAllowLiveTCPMigration sets if TCP connections can be migrated.
|
||||
func (s *Stack) SetAllowLiveTCPMigration(allow bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.allowLiveTCPMigration = allow
|
||||
}
|
||||
|
||||
// DisableAllNonLoopbackNICs disables all non-loopback NICs in the stack.
|
||||
func (s *Stack) DisableAllNonLoopbackNICs() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.externalNetworkingDisabled = true
|
||||
for _, nic := range s.nics {
|
||||
if !nic.IsLoopback() {
|
||||
nic.disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EnableAllNonLoopbackNICs enables all non-loopback NICs in the stack.
|
||||
func (s *Stack) EnableAllNonLoopbackNICs() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.externalNetworkingDisabled = false
|
||||
for _, nic := range s.nics {
|
||||
if !nic.IsLoopback() {
|
||||
nic.enable()
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -92,5 +92,5 @@ func stackinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
stackinitLockNames()
|
||||
stackprefixIndex = locking.NewMutexClass(reflect.TypeOf(stackRWMutex{}), stacklockNames)
|
||||
stackprefixIndex = locking.NewMutexClass(reflect.TypeFor[stackRWMutex](), stacklockNames)
|
||||
}
|
||||
@@ -37,14 +37,6 @@ const (
|
||||
defaultTCPInvalidRateLimit = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
// ReceiveBufferSizeOption is used by stack.(Stack*).Option/SetOption to
|
||||
// get/set the default, min and max receive buffer sizes.
|
||||
type ReceiveBufferSizeOption struct {
|
||||
Min int
|
||||
Default int
|
||||
Max int
|
||||
}
|
||||
|
||||
// TCPInvalidRateLimitOption is used by stack.(Stack*).Option/SetOption to get/set
|
||||
// stack.tcpInvalidRateLimit.
|
||||
type TCPInvalidRateLimitOption time.Duration
|
||||
|
||||
+144
-91
@@ -244,6 +244,7 @@ func (cn *conn) StateFields() []string {
|
||||
"destinationManip",
|
||||
"tcb",
|
||||
"lastUsed",
|
||||
"replySeen",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,6 +261,7 @@ func (cn *conn) StateSave(stateSinkObject state.Sink) {
|
||||
stateSinkObject.Save(5, &cn.destinationManip)
|
||||
stateSinkObject.Save(6, &cn.tcb)
|
||||
stateSinkObject.Save(7, &cn.lastUsed)
|
||||
stateSinkObject.Save(8, &cn.replySeen)
|
||||
}
|
||||
|
||||
func (cn *conn) afterLoad(context.Context) {}
|
||||
@@ -274,6 +276,7 @@ func (cn *conn) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(5, &cn.destinationManip)
|
||||
stateSourceObject.Load(6, &cn.tcb)
|
||||
stateSourceObject.Load(7, &cn.lastUsed)
|
||||
stateSourceObject.Load(8, &cn.replySeen)
|
||||
}
|
||||
|
||||
func (ct *ConnTrack) StateTypeName() string {
|
||||
@@ -283,6 +286,7 @@ func (ct *ConnTrack) StateTypeName() string {
|
||||
func (ct *ConnTrack) StateFields() []string {
|
||||
return []string{
|
||||
"seed",
|
||||
"nftIDSeed",
|
||||
"clock",
|
||||
"buckets",
|
||||
}
|
||||
@@ -294,8 +298,9 @@ func (ct *ConnTrack) beforeSave() {}
|
||||
func (ct *ConnTrack) StateSave(stateSinkObject state.Sink) {
|
||||
ct.beforeSave()
|
||||
stateSinkObject.Save(0, &ct.seed)
|
||||
stateSinkObject.Save(1, &ct.clock)
|
||||
stateSinkObject.Save(2, &ct.buckets)
|
||||
stateSinkObject.Save(1, &ct.nftIDSeed)
|
||||
stateSinkObject.Save(2, &ct.clock)
|
||||
stateSinkObject.Save(3, &ct.buckets)
|
||||
}
|
||||
|
||||
func (ct *ConnTrack) afterLoad(context.Context) {}
|
||||
@@ -303,8 +308,9 @@ func (ct *ConnTrack) afterLoad(context.Context) {}
|
||||
// +checklocksignore
|
||||
func (ct *ConnTrack) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &ct.seed)
|
||||
stateSourceObject.Load(1, &ct.clock)
|
||||
stateSourceObject.Load(2, &ct.buckets)
|
||||
stateSourceObject.Load(1, &ct.nftIDSeed)
|
||||
stateSourceObject.Load(2, &ct.clock)
|
||||
stateSourceObject.Load(3, &ct.buckets)
|
||||
}
|
||||
|
||||
func (bkt *bucket) StateTypeName() string {
|
||||
@@ -339,6 +345,8 @@ func (l *ICMPRateLimiter) StateTypeName() string {
|
||||
func (l *ICMPRateLimiter) StateFields() []string {
|
||||
return []string{
|
||||
"clock",
|
||||
"limit",
|
||||
"burst",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,13 +356,16 @@ func (l *ICMPRateLimiter) beforeSave() {}
|
||||
func (l *ICMPRateLimiter) StateSave(stateSinkObject state.Sink) {
|
||||
l.beforeSave()
|
||||
stateSinkObject.Save(0, &l.clock)
|
||||
stateSinkObject.Save(1, &l.limit)
|
||||
stateSinkObject.Save(2, &l.burst)
|
||||
}
|
||||
|
||||
func (l *ICMPRateLimiter) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *ICMPRateLimiter) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &l.clock)
|
||||
stateSourceObject.Load(1, &l.limit)
|
||||
stateSourceObject.Load(2, &l.burst)
|
||||
stateSourceObject.AfterLoad(func() { l.afterLoad(ctx) })
|
||||
}
|
||||
|
||||
func (a *AcceptTarget) StateTypeName() string {
|
||||
@@ -668,6 +679,34 @@ func (mt *MasqueradeTarget) StateLoad(ctx context.Context, stateSourceObject sta
|
||||
stateSourceObject.Load(0, &mt.NetworkProtocol)
|
||||
}
|
||||
|
||||
func (c *CTTarget) StateTypeName() string {
|
||||
return "pkg/tcpip/stack.CTTarget"
|
||||
}
|
||||
|
||||
func (c *CTTarget) StateFields() []string {
|
||||
return []string{
|
||||
"NetworkProtocol",
|
||||
"Zone",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CTTarget) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (c *CTTarget) StateSave(stateSinkObject state.Sink) {
|
||||
c.beforeSave()
|
||||
stateSinkObject.Save(0, &c.NetworkProtocol)
|
||||
stateSinkObject.Save(1, &c.Zone)
|
||||
}
|
||||
|
||||
func (c *CTTarget) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (c *CTTarget) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &c.NetworkProtocol)
|
||||
stateSourceObject.Load(1, &c.Zone)
|
||||
}
|
||||
|
||||
func (it *IPTables) StateTypeName() string {
|
||||
return "pkg/tcpip/stack.IPTables"
|
||||
}
|
||||
@@ -675,7 +714,6 @@ func (it *IPTables) StateTypeName() string {
|
||||
func (it *IPTables) StateFields() []string {
|
||||
return []string{
|
||||
"connections",
|
||||
"reaper",
|
||||
"v4Tables",
|
||||
"v6Tables",
|
||||
"modified",
|
||||
@@ -686,19 +724,17 @@ func (it *IPTables) StateFields() []string {
|
||||
func (it *IPTables) StateSave(stateSinkObject state.Sink) {
|
||||
it.beforeSave()
|
||||
stateSinkObject.Save(0, &it.connections)
|
||||
stateSinkObject.Save(1, &it.reaper)
|
||||
stateSinkObject.Save(2, &it.v4Tables)
|
||||
stateSinkObject.Save(3, &it.v6Tables)
|
||||
stateSinkObject.Save(4, &it.modified)
|
||||
stateSinkObject.Save(1, &it.v4Tables)
|
||||
stateSinkObject.Save(2, &it.v6Tables)
|
||||
stateSinkObject.Save(3, &it.modified)
|
||||
}
|
||||
|
||||
// +checklocksignore
|
||||
func (it *IPTables) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &it.connections)
|
||||
stateSourceObject.Load(1, &it.reaper)
|
||||
stateSourceObject.Load(2, &it.v4Tables)
|
||||
stateSourceObject.Load(3, &it.v6Tables)
|
||||
stateSourceObject.Load(4, &it.modified)
|
||||
stateSourceObject.Load(1, &it.v4Tables)
|
||||
stateSourceObject.Load(2, &it.v6Tables)
|
||||
stateSourceObject.Load(3, &it.modified)
|
||||
stateSourceObject.AfterLoad(func() { it.afterLoad(ctx) })
|
||||
}
|
||||
|
||||
@@ -1136,6 +1172,7 @@ func (n *nic) StateFields() []string {
|
||||
"stack",
|
||||
"id",
|
||||
"name",
|
||||
"kind",
|
||||
"context",
|
||||
"stats",
|
||||
"networkEndpoints",
|
||||
@@ -1162,20 +1199,21 @@ func (n *nic) StateSave(stateSinkObject state.Sink) {
|
||||
stateSinkObject.Save(1, &n.stack)
|
||||
stateSinkObject.Save(2, &n.id)
|
||||
stateSinkObject.Save(3, &n.name)
|
||||
stateSinkObject.Save(4, &n.context)
|
||||
stateSinkObject.Save(5, &n.stats)
|
||||
stateSinkObject.Save(6, &n.networkEndpoints)
|
||||
stateSinkObject.Save(7, &n.linkAddrResolvers)
|
||||
stateSinkObject.Save(8, &n.duplicateAddressDetectors)
|
||||
stateSinkObject.Save(9, &n.enabled)
|
||||
stateSinkObject.Save(10, &n.spoofing)
|
||||
stateSinkObject.Save(11, &n.promiscuous)
|
||||
stateSinkObject.Save(12, &n.linkResQueue)
|
||||
stateSinkObject.Save(13, &n.packetEPs)
|
||||
stateSinkObject.Save(14, &n.qDisc)
|
||||
stateSinkObject.Save(15, &n.deliverLinkPackets)
|
||||
stateSinkObject.Save(16, &n.Primary)
|
||||
stateSinkObject.Save(17, &n.experimentIPOptionEnabled)
|
||||
stateSinkObject.Save(4, &n.kind)
|
||||
stateSinkObject.Save(5, &n.context)
|
||||
stateSinkObject.Save(6, &n.stats)
|
||||
stateSinkObject.Save(7, &n.networkEndpoints)
|
||||
stateSinkObject.Save(8, &n.linkAddrResolvers)
|
||||
stateSinkObject.Save(9, &n.duplicateAddressDetectors)
|
||||
stateSinkObject.Save(10, &n.enabled)
|
||||
stateSinkObject.Save(11, &n.spoofing)
|
||||
stateSinkObject.Save(12, &n.promiscuous)
|
||||
stateSinkObject.Save(13, &n.linkResQueue)
|
||||
stateSinkObject.Save(14, &n.packetEPs)
|
||||
stateSinkObject.Save(15, &n.qDisc)
|
||||
stateSinkObject.Save(16, &n.deliverLinkPackets)
|
||||
stateSinkObject.Save(17, &n.Primary)
|
||||
stateSinkObject.Save(18, &n.experimentIPOptionEnabled)
|
||||
}
|
||||
|
||||
func (n *nic) afterLoad(context.Context) {}
|
||||
@@ -1186,20 +1224,21 @@ func (n *nic) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(1, &n.stack)
|
||||
stateSourceObject.Load(2, &n.id)
|
||||
stateSourceObject.Load(3, &n.name)
|
||||
stateSourceObject.Load(4, &n.context)
|
||||
stateSourceObject.Load(5, &n.stats)
|
||||
stateSourceObject.Load(6, &n.networkEndpoints)
|
||||
stateSourceObject.Load(7, &n.linkAddrResolvers)
|
||||
stateSourceObject.Load(8, &n.duplicateAddressDetectors)
|
||||
stateSourceObject.Load(9, &n.enabled)
|
||||
stateSourceObject.Load(10, &n.spoofing)
|
||||
stateSourceObject.Load(11, &n.promiscuous)
|
||||
stateSourceObject.Load(12, &n.linkResQueue)
|
||||
stateSourceObject.Load(13, &n.packetEPs)
|
||||
stateSourceObject.Load(14, &n.qDisc)
|
||||
stateSourceObject.Load(15, &n.deliverLinkPackets)
|
||||
stateSourceObject.Load(16, &n.Primary)
|
||||
stateSourceObject.Load(17, &n.experimentIPOptionEnabled)
|
||||
stateSourceObject.Load(4, &n.kind)
|
||||
stateSourceObject.Load(5, &n.context)
|
||||
stateSourceObject.Load(6, &n.stats)
|
||||
stateSourceObject.Load(7, &n.networkEndpoints)
|
||||
stateSourceObject.Load(8, &n.linkAddrResolvers)
|
||||
stateSourceObject.Load(9, &n.duplicateAddressDetectors)
|
||||
stateSourceObject.Load(10, &n.enabled)
|
||||
stateSourceObject.Load(11, &n.spoofing)
|
||||
stateSourceObject.Load(12, &n.promiscuous)
|
||||
stateSourceObject.Load(13, &n.linkResQueue)
|
||||
stateSourceObject.Load(14, &n.packetEPs)
|
||||
stateSourceObject.Load(15, &n.qDisc)
|
||||
stateSourceObject.Load(16, &n.deliverLinkPackets)
|
||||
stateSourceObject.Load(17, &n.Primary)
|
||||
stateSourceObject.Load(18, &n.experimentIPOptionEnabled)
|
||||
}
|
||||
|
||||
func (p *packetEndpointList) StateTypeName() string {
|
||||
@@ -1530,8 +1569,10 @@ func (pk *PacketBuffer) StateFields() []string {
|
||||
"dnatDone",
|
||||
"PktType",
|
||||
"NICID",
|
||||
"InputNICID",
|
||||
"RXChecksumValidated",
|
||||
"NetworkPacketInfo",
|
||||
"Mark",
|
||||
"tuple",
|
||||
}
|
||||
}
|
||||
@@ -1557,9 +1598,11 @@ func (pk *PacketBuffer) StateSave(stateSinkObject state.Sink) {
|
||||
stateSinkObject.Save(13, &pk.dnatDone)
|
||||
stateSinkObject.Save(14, &pk.PktType)
|
||||
stateSinkObject.Save(15, &pk.NICID)
|
||||
stateSinkObject.Save(16, &pk.RXChecksumValidated)
|
||||
stateSinkObject.Save(17, &pk.NetworkPacketInfo)
|
||||
stateSinkObject.Save(18, &pk.tuple)
|
||||
stateSinkObject.Save(16, &pk.InputNICID)
|
||||
stateSinkObject.Save(17, &pk.RXChecksumValidated)
|
||||
stateSinkObject.Save(18, &pk.NetworkPacketInfo)
|
||||
stateSinkObject.Save(19, &pk.Mark)
|
||||
stateSinkObject.Save(20, &pk.tuple)
|
||||
}
|
||||
|
||||
func (pk *PacketBuffer) afterLoad(context.Context) {}
|
||||
@@ -1582,9 +1625,11 @@ func (pk *PacketBuffer) StateLoad(ctx context.Context, stateSourceObject state.S
|
||||
stateSourceObject.Load(13, &pk.dnatDone)
|
||||
stateSourceObject.Load(14, &pk.PktType)
|
||||
stateSourceObject.Load(15, &pk.NICID)
|
||||
stateSourceObject.Load(16, &pk.RXChecksumValidated)
|
||||
stateSourceObject.Load(17, &pk.NetworkPacketInfo)
|
||||
stateSourceObject.Load(18, &pk.tuple)
|
||||
stateSourceObject.Load(16, &pk.InputNICID)
|
||||
stateSourceObject.Load(17, &pk.RXChecksumValidated)
|
||||
stateSourceObject.Load(18, &pk.NetworkPacketInfo)
|
||||
stateSourceObject.Load(19, &pk.Mark)
|
||||
stateSourceObject.Load(20, &pk.tuple)
|
||||
}
|
||||
|
||||
func (h *headerInfo) StateTypeName() string {
|
||||
@@ -1783,6 +1828,7 @@ func (n *NetworkPacketInfo) StateTypeName() string {
|
||||
func (n *NetworkPacketInfo) StateFields() []string {
|
||||
return []string{
|
||||
"LocalAddressBroadcast",
|
||||
"LocalAddressTemporary",
|
||||
"IsForwardedPacket",
|
||||
}
|
||||
}
|
||||
@@ -1793,7 +1839,8 @@ func (n *NetworkPacketInfo) beforeSave() {}
|
||||
func (n *NetworkPacketInfo) StateSave(stateSinkObject state.Sink) {
|
||||
n.beforeSave()
|
||||
stateSinkObject.Save(0, &n.LocalAddressBroadcast)
|
||||
stateSinkObject.Save(1, &n.IsForwardedPacket)
|
||||
stateSinkObject.Save(1, &n.LocalAddressTemporary)
|
||||
stateSinkObject.Save(2, &n.IsForwardedPacket)
|
||||
}
|
||||
|
||||
func (n *NetworkPacketInfo) afterLoad(context.Context) {}
|
||||
@@ -1801,7 +1848,8 @@ func (n *NetworkPacketInfo) afterLoad(context.Context) {}
|
||||
// +checklocksignore
|
||||
func (n *NetworkPacketInfo) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &n.LocalAddressBroadcast)
|
||||
stateSourceObject.Load(1, &n.IsForwardedPacket)
|
||||
stateSourceObject.Load(1, &n.LocalAddressTemporary)
|
||||
stateSourceObject.Load(2, &n.IsForwardedPacket)
|
||||
}
|
||||
|
||||
func (p *PacketMMapOpts) StateTypeName() string {
|
||||
@@ -1814,11 +1862,10 @@ func (p *PacketMMapOpts) StateFields() []string {
|
||||
"IsRx",
|
||||
"Cooked",
|
||||
"Stack",
|
||||
"Stats",
|
||||
"Wq",
|
||||
"NICID",
|
||||
"NetProto",
|
||||
"PacketEndpoint",
|
||||
"Version",
|
||||
"Reserve",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1831,11 +1878,10 @@ func (p *PacketMMapOpts) StateSave(stateSinkObject state.Sink) {
|
||||
stateSinkObject.Save(1, &p.IsRx)
|
||||
stateSinkObject.Save(2, &p.Cooked)
|
||||
stateSinkObject.Save(3, &p.Stack)
|
||||
stateSinkObject.Save(4, &p.Stats)
|
||||
stateSinkObject.Save(5, &p.Wq)
|
||||
stateSinkObject.Save(6, &p.NICID)
|
||||
stateSinkObject.Save(7, &p.NetProto)
|
||||
stateSinkObject.Save(8, &p.PacketEndpoint)
|
||||
stateSinkObject.Save(4, &p.Wq)
|
||||
stateSinkObject.Save(5, &p.PacketEndpoint)
|
||||
stateSinkObject.Save(6, &p.Version)
|
||||
stateSinkObject.Save(7, &p.Reserve)
|
||||
}
|
||||
|
||||
func (p *PacketMMapOpts) afterLoad(context.Context) {}
|
||||
@@ -1846,11 +1892,10 @@ func (p *PacketMMapOpts) StateLoad(ctx context.Context, stateSourceObject state.
|
||||
stateSourceObject.Load(1, &p.IsRx)
|
||||
stateSourceObject.Load(2, &p.Cooked)
|
||||
stateSourceObject.Load(3, &p.Stack)
|
||||
stateSourceObject.Load(4, &p.Stats)
|
||||
stateSourceObject.Load(5, &p.Wq)
|
||||
stateSourceObject.Load(6, &p.NICID)
|
||||
stateSourceObject.Load(7, &p.NetProto)
|
||||
stateSourceObject.Load(8, &p.PacketEndpoint)
|
||||
stateSourceObject.Load(4, &p.Wq)
|
||||
stateSourceObject.Load(5, &p.PacketEndpoint)
|
||||
stateSourceObject.Load(6, &p.Version)
|
||||
stateSourceObject.Load(7, &p.Reserve)
|
||||
}
|
||||
|
||||
func (lifetimes *AddressLifetimes) StateTypeName() string {
|
||||
@@ -2097,7 +2142,9 @@ func (s *Stack) StateFields() []string {
|
||||
"cleanupEndpoints",
|
||||
"PortManager",
|
||||
"clock",
|
||||
"clockResolution",
|
||||
"handleLocal",
|
||||
"nftablesConfigured",
|
||||
"restoredEndpoints",
|
||||
"resumableEndpoints",
|
||||
"icmpRateLimiter",
|
||||
@@ -2108,12 +2155,11 @@ func (s *Stack) StateFields() []string {
|
||||
"receiveBufferSize",
|
||||
"tcpInvalidRateLimit",
|
||||
"tsOffsetSecret",
|
||||
"saveRestoreEnabled",
|
||||
"externalNetworkingDisabled",
|
||||
"allowConnectedOnSave",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stack) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *Stack) StateSave(stateSinkObject state.Sink) {
|
||||
s.beforeSave()
|
||||
@@ -2127,18 +2173,21 @@ func (s *Stack) StateSave(stateSinkObject state.Sink) {
|
||||
stateSinkObject.Save(7, &s.cleanupEndpoints)
|
||||
stateSinkObject.Save(8, &s.PortManager)
|
||||
stateSinkObject.Save(9, &s.clock)
|
||||
stateSinkObject.Save(10, &s.handleLocal)
|
||||
stateSinkObject.Save(11, &s.restoredEndpoints)
|
||||
stateSinkObject.Save(12, &s.resumableEndpoints)
|
||||
stateSinkObject.Save(13, &s.icmpRateLimiter)
|
||||
stateSinkObject.Save(14, &s.seed)
|
||||
stateSinkObject.Save(15, &s.nudConfigs)
|
||||
stateSinkObject.Save(16, &s.nudDisp)
|
||||
stateSinkObject.Save(17, &s.sendBufferSize)
|
||||
stateSinkObject.Save(18, &s.receiveBufferSize)
|
||||
stateSinkObject.Save(19, &s.tcpInvalidRateLimit)
|
||||
stateSinkObject.Save(20, &s.tsOffsetSecret)
|
||||
stateSinkObject.Save(21, &s.saveRestoreEnabled)
|
||||
stateSinkObject.Save(10, &s.clockResolution)
|
||||
stateSinkObject.Save(11, &s.handleLocal)
|
||||
stateSinkObject.Save(12, &s.nftablesConfigured)
|
||||
stateSinkObject.Save(13, &s.restoredEndpoints)
|
||||
stateSinkObject.Save(14, &s.resumableEndpoints)
|
||||
stateSinkObject.Save(15, &s.icmpRateLimiter)
|
||||
stateSinkObject.Save(16, &s.seed)
|
||||
stateSinkObject.Save(17, &s.nudConfigs)
|
||||
stateSinkObject.Save(18, &s.nudDisp)
|
||||
stateSinkObject.Save(19, &s.sendBufferSize)
|
||||
stateSinkObject.Save(20, &s.receiveBufferSize)
|
||||
stateSinkObject.Save(21, &s.tcpInvalidRateLimit)
|
||||
stateSinkObject.Save(22, &s.tsOffsetSecret)
|
||||
stateSinkObject.Save(23, &s.externalNetworkingDisabled)
|
||||
stateSinkObject.Save(24, &s.allowConnectedOnSave)
|
||||
}
|
||||
|
||||
// +checklocksignore
|
||||
@@ -2153,18 +2202,21 @@ func (s *Stack) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(7, &s.cleanupEndpoints)
|
||||
stateSourceObject.Load(8, &s.PortManager)
|
||||
stateSourceObject.Load(9, &s.clock)
|
||||
stateSourceObject.Load(10, &s.handleLocal)
|
||||
stateSourceObject.Load(11, &s.restoredEndpoints)
|
||||
stateSourceObject.Load(12, &s.resumableEndpoints)
|
||||
stateSourceObject.Load(13, &s.icmpRateLimiter)
|
||||
stateSourceObject.Load(14, &s.seed)
|
||||
stateSourceObject.Load(15, &s.nudConfigs)
|
||||
stateSourceObject.Load(16, &s.nudDisp)
|
||||
stateSourceObject.Load(17, &s.sendBufferSize)
|
||||
stateSourceObject.Load(18, &s.receiveBufferSize)
|
||||
stateSourceObject.Load(19, &s.tcpInvalidRateLimit)
|
||||
stateSourceObject.Load(20, &s.tsOffsetSecret)
|
||||
stateSourceObject.Load(21, &s.saveRestoreEnabled)
|
||||
stateSourceObject.Load(10, &s.clockResolution)
|
||||
stateSourceObject.Load(11, &s.handleLocal)
|
||||
stateSourceObject.Load(12, &s.nftablesConfigured)
|
||||
stateSourceObject.Load(13, &s.restoredEndpoints)
|
||||
stateSourceObject.Load(14, &s.resumableEndpoints)
|
||||
stateSourceObject.Load(15, &s.icmpRateLimiter)
|
||||
stateSourceObject.Load(16, &s.seed)
|
||||
stateSourceObject.Load(17, &s.nudConfigs)
|
||||
stateSourceObject.Load(18, &s.nudDisp)
|
||||
stateSourceObject.Load(19, &s.sendBufferSize)
|
||||
stateSourceObject.Load(20, &s.receiveBufferSize)
|
||||
stateSourceObject.Load(21, &s.tcpInvalidRateLimit)
|
||||
stateSourceObject.Load(22, &s.tsOffsetSecret)
|
||||
stateSourceObject.Load(23, &s.externalNetworkingDisabled)
|
||||
stateSourceObject.Load(24, &s.allowConnectedOnSave)
|
||||
stateSourceObject.AfterLoad(func() { s.afterLoad(ctx) })
|
||||
}
|
||||
|
||||
@@ -2439,6 +2491,7 @@ func init() {
|
||||
state.Register((*RedirectTarget)(nil))
|
||||
state.Register((*SNATTarget)(nil))
|
||||
state.Register((*MasqueradeTarget)(nil))
|
||||
state.Register((*CTTarget)(nil))
|
||||
state.Register((*IPTables)(nil))
|
||||
state.Register((*Table)(nil))
|
||||
state.Register((*Rule)(nil))
|
||||
|
||||
@@ -92,5 +92,5 @@ func stateConninitLockNames() {}
|
||||
|
||||
func init() {
|
||||
stateConninitLockNames()
|
||||
stateConnprefixIndex = locking.NewMutexClass(reflect.TypeOf(stateConnRWMutex{}), stateConnlockNames)
|
||||
stateConnprefixIndex = locking.NewMutexClass(reflect.TypeFor[stateConnRWMutex](), stateConnlockNames)
|
||||
}
|
||||
@@ -92,5 +92,5 @@ func transportEndpointsinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
transportEndpointsinitLockNames()
|
||||
transportEndpointsprefixIndex = locking.NewMutexClass(reflect.TypeOf(transportEndpointsRWMutex{}), transportEndpointslockNames)
|
||||
transportEndpointsprefixIndex = locking.NewMutexClass(reflect.TypeFor[transportEndpointsRWMutex](), transportEndpointslockNames)
|
||||
}
|
||||
@@ -91,7 +91,6 @@ func (*stdClock) AfterFunc(d time.Duration, f func()) Timer {
|
||||
}
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
type stdTimer struct {
|
||||
t *time.Timer
|
||||
}
|
||||
|
||||
+40
-9
@@ -49,10 +49,8 @@ import (
|
||||
|
||||
// Using the header package here would cause an import cycle.
|
||||
const (
|
||||
ipv4AddressSize = 4
|
||||
ipv4ProtocolNumber = 0x0800
|
||||
ipv6AddressSize = 16
|
||||
ipv6ProtocolNumber = 0x86dd
|
||||
ipv4AddressSize = 4
|
||||
ipv6AddressSize = 16
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -712,6 +710,10 @@ type ReadOptions struct {
|
||||
// NeedLinkPacketInfo indicates whether to return the link-layer information,
|
||||
// if supported.
|
||||
NeedLinkPacketInfo bool
|
||||
|
||||
// NeedRecvdExperimentOption indicates whether to return the experiment
|
||||
// option value from the last received packet, if supported.
|
||||
NeedReceivedExperimentOption bool
|
||||
}
|
||||
|
||||
// ReadResult represents result for a successful Endpoint.Read.
|
||||
@@ -732,6 +734,10 @@ type ReadResult struct {
|
||||
// LinkPacketInfo is the link-layer information of the received packet if
|
||||
// ReadOptions.NeedLinkPacketInfo is true.
|
||||
LinkPacketInfo LinkPacketInfo
|
||||
|
||||
// ReceivedExperimentOption is the experiment option value from the last
|
||||
// received packet if ReadOptions.NeedReceivedExperimentOption is true.
|
||||
ReceivedExperimentOption uint16
|
||||
}
|
||||
|
||||
// Endpoint is the interface implemented by transport protocols (e.g., tcp, udp)
|
||||
@@ -844,7 +850,7 @@ type Endpoint interface {
|
||||
// protocol-specific and is primarily used for diagnostics.
|
||||
State() uint32
|
||||
|
||||
// ModerateRecvBuf should be called everytime data is copied to the user
|
||||
// ModerateRecvBuf should be called every time data is copied to the user
|
||||
// space. This allows for dynamic tuning of recv buffer space for a
|
||||
// given socket.
|
||||
//
|
||||
@@ -949,9 +955,8 @@ const (
|
||||
|
||||
// MTUDiscoverOption is used to set/get the path MTU discovery setting.
|
||||
//
|
||||
// NOTE: Setting this option to any other value than PMTUDiscoveryDont
|
||||
// is not supported and will fail as such, and getting this option will
|
||||
// always return PMTUDiscoveryDont.
|
||||
// The value controls whether the Don't Fragment (DF) bit is set on
|
||||
// outgoing IPv4 packets.
|
||||
MTUDiscoverOption
|
||||
|
||||
// MulticastTTLOption is used by SetSockOptInt/GetSockOptInt to control
|
||||
@@ -996,6 +1001,17 @@ const (
|
||||
// IPv6Checksum is used to request the stack to populate and validate the IPv6
|
||||
// checksum for transport level headers.
|
||||
IPv6Checksum
|
||||
|
||||
// PacketMMapVersionOption is used to set the packet mmap version.
|
||||
PacketMMapVersionOption
|
||||
|
||||
// PacketMMapReserveOption is used to set the packet mmap reserved space
|
||||
// between the aligned header and the payload.
|
||||
PacketMMapReserveOption
|
||||
|
||||
// IPv6MulticastInterfaceOption is used to set/get the NIC used for
|
||||
// IPv6 multicast Tx.
|
||||
IPv6MulticastInterfaceOption
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -1198,6 +1214,17 @@ type TpacketReq struct {
|
||||
|
||||
func (*TpacketReq) isSettableSocketOption() {}
|
||||
|
||||
// TpacketStats is the statistics for a packet_mmap ring buffer from
|
||||
// <linux/if_packet.h>.
|
||||
//
|
||||
// +stateify savable
|
||||
type TpacketStats struct {
|
||||
Packets uint32
|
||||
Dropped uint32
|
||||
}
|
||||
|
||||
func (*TpacketStats) isGettableSocketOption() {}
|
||||
|
||||
// EndpointState represents the state of an endpoint.
|
||||
type EndpointState uint8
|
||||
|
||||
@@ -2830,10 +2857,14 @@ type ProtocolAddress struct {
|
||||
}
|
||||
|
||||
var (
|
||||
// danglingEndpointsMu protects access to danglingEndpoints.
|
||||
// Public dangling-endpoint helpers acquire this mutex themselves. Their
|
||||
// caller exclusions cannot be exported reliably by checklocks because
|
||||
// the guard is rooted in a private package global.
|
||||
danglingEndpointsMu sync.Mutex
|
||||
|
||||
// danglingEndpoints tracks all dangling endpoints no longer owned by the app.
|
||||
//
|
||||
// +checklocks:danglingEndpointsMu
|
||||
danglingEndpoints = make(map[Endpoint]struct{})
|
||||
)
|
||||
|
||||
|
||||
+43
-40
@@ -1097,9 +1097,10 @@ func (so *SocketOptions) StateFields() []string {
|
||||
"bindToDevice",
|
||||
"sendBufferSize",
|
||||
"receiveBufferSize",
|
||||
"linger",
|
||||
"rcvlowat",
|
||||
"experimentOptionValue",
|
||||
"mark",
|
||||
"linger",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1134,9 +1135,10 @@ func (so *SocketOptions) StateSave(stateSinkObject state.Sink) {
|
||||
stateSinkObject.Save(23, &so.bindToDevice)
|
||||
stateSinkObject.Save(24, &so.sendBufferSize)
|
||||
stateSinkObject.Save(25, &so.receiveBufferSize)
|
||||
stateSinkObject.Save(26, &so.linger)
|
||||
stateSinkObject.Save(27, &so.rcvlowat)
|
||||
stateSinkObject.Save(28, &so.experimentOptionValue)
|
||||
stateSinkObject.Save(26, &so.rcvlowat)
|
||||
stateSinkObject.Save(27, &so.experimentOptionValue)
|
||||
stateSinkObject.Save(28, &so.mark)
|
||||
stateSinkObject.Save(29, &so.linger)
|
||||
}
|
||||
|
||||
func (so *SocketOptions) afterLoad(context.Context) {}
|
||||
@@ -1169,9 +1171,10 @@ func (so *SocketOptions) StateLoad(ctx context.Context, stateSourceObject state.
|
||||
stateSourceObject.Load(23, &so.bindToDevice)
|
||||
stateSourceObject.Load(24, &so.sendBufferSize)
|
||||
stateSourceObject.Load(25, &so.receiveBufferSize)
|
||||
stateSourceObject.Load(26, &so.linger)
|
||||
stateSourceObject.Load(27, &so.rcvlowat)
|
||||
stateSourceObject.Load(28, &so.experimentOptionValue)
|
||||
stateSourceObject.Load(26, &so.rcvlowat)
|
||||
stateSourceObject.Load(27, &so.experimentOptionValue)
|
||||
stateSourceObject.Load(28, &so.mark)
|
||||
stateSourceObject.Load(29, &so.linger)
|
||||
}
|
||||
|
||||
func (l *LocalSockError) StateTypeName() string {
|
||||
@@ -1264,31 +1267,6 @@ func (s *stdClock) StateLoad(ctx context.Context, stateSourceObject state.Source
|
||||
stateSourceObject.AfterLoad(func() { s.afterLoad(ctx) })
|
||||
}
|
||||
|
||||
func (st *stdTimer) StateTypeName() string {
|
||||
return "pkg/tcpip.stdTimer"
|
||||
}
|
||||
|
||||
func (st *stdTimer) StateFields() []string {
|
||||
return []string{
|
||||
"t",
|
||||
}
|
||||
}
|
||||
|
||||
func (st *stdTimer) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (st *stdTimer) StateSave(stateSinkObject state.Sink) {
|
||||
st.beforeSave()
|
||||
stateSinkObject.Save(0, &st.t)
|
||||
}
|
||||
|
||||
func (st *stdTimer) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (st *stdTimer) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &st.t)
|
||||
}
|
||||
|
||||
func (mt *MonotonicTime) StateTypeName() string {
|
||||
return "pkg/tcpip.MonotonicTime"
|
||||
}
|
||||
@@ -1505,8 +1483,8 @@ func (c *ReceivableControlMessages) beforeSave() {}
|
||||
// +checklocksignore
|
||||
func (c *ReceivableControlMessages) StateSave(stateSinkObject state.Sink) {
|
||||
c.beforeSave()
|
||||
var TimestampValue int64
|
||||
TimestampValue = c.saveTimestamp()
|
||||
TimestampValue := c.saveTimestamp()
|
||||
_ = (int64)(TimestampValue)
|
||||
stateSinkObject.SaveValue(0, TimestampValue)
|
||||
stateSinkObject.Save(1, &c.HasInq)
|
||||
stateSinkObject.Save(2, &c.Inq)
|
||||
@@ -1702,6 +1680,34 @@ func (t *TpacketReq) StateLoad(ctx context.Context, stateSourceObject state.Sour
|
||||
stateSourceObject.Load(3, &t.TpFrameNr)
|
||||
}
|
||||
|
||||
func (t *TpacketStats) StateTypeName() string {
|
||||
return "pkg/tcpip.TpacketStats"
|
||||
}
|
||||
|
||||
func (t *TpacketStats) StateFields() []string {
|
||||
return []string{
|
||||
"Packets",
|
||||
"Dropped",
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TpacketStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (t *TpacketStats) StateSave(stateSinkObject state.Sink) {
|
||||
t.beforeSave()
|
||||
stateSinkObject.Save(0, &t.Packets)
|
||||
stateSinkObject.Save(1, &t.Dropped)
|
||||
}
|
||||
|
||||
func (t *TpacketStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (t *TpacketStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &t.Packets)
|
||||
stateSourceObject.Load(1, &t.Dropped)
|
||||
}
|
||||
|
||||
func (l *LingerOption) StateTypeName() string {
|
||||
return "pkg/tcpip.LingerOption"
|
||||
}
|
||||
@@ -3196,7 +3202,6 @@ func (j *jobInstance) StateTypeName() string {
|
||||
|
||||
func (j *jobInstance) StateFields() []string {
|
||||
return []string{
|
||||
"timer",
|
||||
"earlyReturn",
|
||||
}
|
||||
}
|
||||
@@ -3206,16 +3211,14 @@ func (j *jobInstance) beforeSave() {}
|
||||
// +checklocksignore
|
||||
func (j *jobInstance) StateSave(stateSinkObject state.Sink) {
|
||||
j.beforeSave()
|
||||
stateSinkObject.Save(0, &j.timer)
|
||||
stateSinkObject.Save(1, &j.earlyReturn)
|
||||
stateSinkObject.Save(0, &j.earlyReturn)
|
||||
}
|
||||
|
||||
func (j *jobInstance) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (j *jobInstance) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &j.timer)
|
||||
stateSourceObject.Load(1, &j.earlyReturn)
|
||||
stateSourceObject.Load(0, &j.earlyReturn)
|
||||
}
|
||||
|
||||
func (j *Job) StateTypeName() string {
|
||||
@@ -3300,7 +3303,6 @@ func init() {
|
||||
state.Register((*LocalSockError)(nil))
|
||||
state.Register((*SockError)(nil))
|
||||
state.Register((*stdClock)(nil))
|
||||
state.Register((*stdTimer)(nil))
|
||||
state.Register((*MonotonicTime)(nil))
|
||||
state.Register((*Address)(nil))
|
||||
state.Register((*AddressMask)(nil))
|
||||
@@ -3313,6 +3315,7 @@ func init() {
|
||||
state.Register((*TCPReceiveBufferSizeRangeOption)(nil))
|
||||
state.Register((*ICMPv6Filter)(nil))
|
||||
state.Register((*TpacketReq)(nil))
|
||||
state.Register((*TpacketStats)(nil))
|
||||
state.Register((*LingerOption)(nil))
|
||||
state.Register((*IPPacketInfo)(nil))
|
||||
state.Register((*IPv6PacketInfo)(nil))
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ import (
|
||||
//
|
||||
// +stateify savable
|
||||
type jobInstance struct {
|
||||
timer Timer
|
||||
timer Timer `state:"nosave"`
|
||||
|
||||
// Used to inform the timer to early return when it gets stopped while the
|
||||
// lock the timer tries to obtain when fired is held (T1 is a goroutine that
|
||||
|
||||
+31
-21
@@ -64,20 +64,23 @@ type endpoint struct {
|
||||
stats tcpip.TransportEndpointStats
|
||||
ops tcpip.SocketOptions
|
||||
|
||||
// The following fields are used to manage the receive queue, and are
|
||||
// protected by rcvMu.
|
||||
rcvMu sync.Mutex `state:"nosave"`
|
||||
rcvReady bool
|
||||
rcvList icmpPacketList
|
||||
// The following fields are used to manage the receive queue.
|
||||
rcvMu sync.Mutex `state:"nosave"`
|
||||
// +checklocks:rcvMu
|
||||
rcvReady bool
|
||||
// +checklocks:rcvMu
|
||||
rcvList icmpPacketList
|
||||
// +checklocks:rcvMu
|
||||
rcvBufSize int
|
||||
rcvClosed bool
|
||||
|
||||
// The following fields are protected by the mu mutex.
|
||||
mu sync.RWMutex `state:"nosave"`
|
||||
// frozen indicates if the packets should be delivered to the endpoint
|
||||
// during restore.
|
||||
// +checklocks:rcvMu
|
||||
rcvClosed bool
|
||||
// frozen prevents packet delivery during save/restore.
|
||||
// +checklocks:rcvMu
|
||||
frozen bool
|
||||
ident uint16
|
||||
|
||||
mu sync.RWMutex `state:"nosave"`
|
||||
// +checklocks:mu
|
||||
ident uint16
|
||||
}
|
||||
|
||||
func newEndpoint(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
@@ -176,11 +179,13 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
|
||||
}
|
||||
|
||||
p := e.rcvList.Front()
|
||||
if !opts.Peek {
|
||||
if opts.Peek {
|
||||
p.data.IncRef()
|
||||
} else {
|
||||
e.rcvList.Remove(p)
|
||||
defer p.data.DecRef()
|
||||
e.rcvBufSize -= p.data.Data().Size()
|
||||
}
|
||||
defer p.data.DecRef()
|
||||
|
||||
e.rcvMu.Unlock()
|
||||
|
||||
@@ -497,20 +502,22 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) tcpip.Error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
ident := e.ident
|
||||
err := e.net.ConnectAndThen(addr, func(netProto tcpip.NetworkProtocolNumber, previousID, nextID stack.TransportEndpointID) tcpip.Error {
|
||||
nextID.LocalPort = e.ident
|
||||
nextID.LocalPort = ident
|
||||
|
||||
nextID, err := e.registerWithStack(netProto, nextID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.ident = nextID.LocalPort
|
||||
ident = nextID.LocalPort
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.ident = ident
|
||||
|
||||
e.rcvMu.Lock()
|
||||
e.rcvReady = true
|
||||
@@ -593,6 +600,7 @@ func (e *endpoint) registerWithStack(netProto tcpip.NetworkProtocolNumber, id st
|
||||
return id, err
|
||||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
func (e *endpoint) bindLocked(addr tcpip.FullAddress) tcpip.Error {
|
||||
// Don't allow binding once endpoint is not in the initial state
|
||||
// anymore.
|
||||
@@ -600,6 +608,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) tcpip.Error {
|
||||
return &tcpip.ErrInvalidEndpointState{}
|
||||
}
|
||||
|
||||
var ident uint16
|
||||
err := e.net.BindAndThen(addr, func(boundNetProto tcpip.NetworkProtocolNumber, boundAddr tcpip.Address) tcpip.Error {
|
||||
id := stack.TransportEndpointID{
|
||||
LocalPort: addr.Port,
|
||||
@@ -610,12 +619,13 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) tcpip.Error {
|
||||
return err
|
||||
}
|
||||
|
||||
e.ident = id.LocalPort
|
||||
ident = id.LocalPort
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.ident = ident
|
||||
|
||||
e.rcvMu.Lock()
|
||||
e.rcvReady = true
|
||||
@@ -814,15 +824,15 @@ func (e *endpoint) SocketOptions() *tcpip.SocketOptions {
|
||||
|
||||
// freeze prevents any more packets from being delivered to the endpoint.
|
||||
func (e *endpoint) freeze() {
|
||||
e.mu.Lock()
|
||||
e.rcvMu.Lock()
|
||||
e.frozen = true
|
||||
e.mu.Unlock()
|
||||
e.rcvMu.Unlock()
|
||||
}
|
||||
|
||||
// thaw unfreezes a previously frozen endpoint using endpoint.freeze() allows
|
||||
// new packets to be delivered again.
|
||||
func (e *endpoint) thaw() {
|
||||
e.mu.Lock()
|
||||
e.rcvMu.Lock()
|
||||
e.frozen = false
|
||||
e.mu.Unlock()
|
||||
e.rcvMu.Unlock()
|
||||
}
|
||||
@@ -16,12 +16,11 @@ package icmp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport"
|
||||
)
|
||||
|
||||
// saveReceivedAt is invoked by stateify.
|
||||
@@ -36,11 +35,7 @@ func (p *icmpPacket) loadReceivedAt(_ context.Context, nsec int64) {
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (e *endpoint) afterLoad(ctx context.Context) {
|
||||
if e.stack.IsSaveRestoreEnabled() {
|
||||
e.stack.RegisterRestoredEndpoint(e)
|
||||
} else {
|
||||
stack.RestoreStackFromContext(ctx).RegisterRestoredEndpoint(e)
|
||||
}
|
||||
e.stack.RegisterRestoredEndpoint(e)
|
||||
}
|
||||
|
||||
// beforeSave is invoked by stateify.
|
||||
@@ -51,31 +46,14 @@ func (e *endpoint) beforeSave() {
|
||||
|
||||
// Restore implements tcpip.RestoredEndpoint.Restore.
|
||||
func (e *endpoint) Restore(s *stack.Stack) {
|
||||
e.thaw()
|
||||
|
||||
e.net.Resume(s)
|
||||
if e.stack.IsSaveRestoreEnabled() {
|
||||
e.ops.InitHandler(e, e.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
if err := e.net.Resume(s); err != nil {
|
||||
log.Warningf("Closing the ICMP endpoint as it cannot be restored, err: %v", err)
|
||||
e.Close()
|
||||
return
|
||||
}
|
||||
|
||||
e.stack = s
|
||||
e.thaw()
|
||||
e.ops.InitHandler(e, e.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
|
||||
switch state := e.net.State(); state {
|
||||
case transport.DatagramEndpointStateInitial, transport.DatagramEndpointStateClosed:
|
||||
case transport.DatagramEndpointStateBound, transport.DatagramEndpointStateConnected:
|
||||
var err tcpip.Error
|
||||
info := e.net.Info()
|
||||
info.ID.LocalPort = e.ident
|
||||
info.ID, err = e.registerWithStack(info.NetProto, info.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("e.registerWithStack(%d, %#v): %s", info.NetProto, info.ID, err))
|
||||
}
|
||||
e.ident = info.ID.LocalPort
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled state = %s", state))
|
||||
}
|
||||
}
|
||||
|
||||
// Resume implements tcpip.ResumableEndpoint.Resume.
|
||||
|
||||
@@ -29,8 +29,8 @@ func (p *icmpPacket) beforeSave() {}
|
||||
// +checklocksignore
|
||||
func (p *icmpPacket) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
var receivedAtValue int64
|
||||
receivedAtValue = p.saveReceivedAt()
|
||||
receivedAtValue := p.saveReceivedAt()
|
||||
_ = (int64)(receivedAtValue)
|
||||
stateSinkObject.SaveValue(4, receivedAtValue)
|
||||
stateSinkObject.Save(0, &p.icmpPacketEntry)
|
||||
stateSinkObject.Save(1, &p.senderAddress)
|
||||
|
||||
+106
-31
@@ -66,13 +66,16 @@ type Endpoint struct {
|
||||
// TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6.
|
||||
// +checklocks:mu
|
||||
multicastAddr tcpip.Address
|
||||
// TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6.
|
||||
// +checklocks:mu
|
||||
multicastNICID tcpip.NICID
|
||||
// +checklocks:mu
|
||||
ipv6MulticastNICID tcpip.NICID
|
||||
// +checklocks:mu
|
||||
ipv4TOS uint8
|
||||
// +checklocks:mu
|
||||
ipv6TClass uint8
|
||||
// +checklocks:mu
|
||||
pmtud tcpip.PMTUDStrategy
|
||||
|
||||
// Lock ordering: mu > infoMu.
|
||||
infoMu sync.RWMutex `state:"nosave"`
|
||||
@@ -181,7 +184,11 @@ func (e *Endpoint) Close() {
|
||||
}
|
||||
|
||||
for mem := range e.multicastMemberships {
|
||||
e.stack.LeaveGroup(e.netProto, mem.nicID, mem.multicastAddr)
|
||||
proto, err := e.multicastNetProto(mem.multicastAddr)
|
||||
if err != nil {
|
||||
panic("non multicast address in an existing membership")
|
||||
}
|
||||
e.stack.LeaveGroup(proto, mem.nicID, mem.multicastAddr)
|
||||
}
|
||||
e.multicastMemberships = nil
|
||||
|
||||
@@ -229,6 +236,7 @@ type WriteContext struct {
|
||||
route *stack.Route
|
||||
ttl uint8
|
||||
tos uint8
|
||||
df bool
|
||||
}
|
||||
|
||||
func (c *WriteContext) MTU() uint32 {
|
||||
@@ -274,33 +282,33 @@ func (c *WriteContext) TryNewPacketBuffer(reserveHdrBytes int, data buffer.Buffe
|
||||
if !e.hasSendSpaceRLocked() {
|
||||
return nil
|
||||
}
|
||||
return c.newPacketBufferLocked(reserveHdrBytes, data)
|
||||
|
||||
mark := e.ops.GetMark()
|
||||
return c.newPacketBufferLocked(reserveHdrBytes, data, mark)
|
||||
}
|
||||
|
||||
// TryNewPacketBufferFromPayloader returns a new packet buffer iff the endpoint's send buffer
|
||||
// TryNewPacketBufferFromPayloader returns a new packet buffer if the endpoint's send buffer
|
||||
// is not full. Otherwise, data from `payloader` isn't read.
|
||||
//
|
||||
// If this method returns nil, the caller should wait for the endpoint to become
|
||||
// writable.
|
||||
func (c *WriteContext) TryNewPacketBufferFromPayloader(reserveHdrBytes int, payloader tcpip.Payloader) *stack.PacketBuffer {
|
||||
func (c *WriteContext) TryNewPacketBufferFromPayloader(reserveHdrBytes int, payloader tcpip.Payloader) (*stack.PacketBuffer, tcpip.Error) {
|
||||
e := c.e
|
||||
|
||||
e.sendBufferSizeInUseMu.Lock()
|
||||
defer e.sendBufferSizeInUseMu.Unlock()
|
||||
|
||||
if !e.hasSendSpaceRLocked() {
|
||||
return nil
|
||||
return nil, &tcpip.ErrWouldBlock{}
|
||||
}
|
||||
var data buffer.Buffer
|
||||
if _, err := data.WriteFromReader(payloader, int64(payloader.Len())); err != nil {
|
||||
data.Release()
|
||||
return nil
|
||||
return nil, &tcpip.ErrBadBuffer{}
|
||||
}
|
||||
return c.newPacketBufferLocked(reserveHdrBytes, data)
|
||||
mark := e.ops.GetMark()
|
||||
return c.newPacketBufferLocked(reserveHdrBytes, data, mark), nil
|
||||
}
|
||||
|
||||
// +checklocks:c.e.sendBufferSizeInUseMu
|
||||
func (c *WriteContext) newPacketBufferLocked(reserveHdrBytes int, data buffer.Buffer) *stack.PacketBuffer {
|
||||
func (c *WriteContext) newPacketBufferLocked(reserveHdrBytes int, data buffer.Buffer, mark uint32) *stack.PacketBuffer {
|
||||
e := c.e
|
||||
// Note that we allow oversubscription - if there is any space at all in the
|
||||
// send buffer, we accept the full packet which may be larger than the space
|
||||
@@ -323,6 +331,7 @@ func (c *WriteContext) newPacketBufferLocked(reserveHdrBytes int, data buffer.Bu
|
||||
return stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: reserveHdrBytes,
|
||||
Payload: data,
|
||||
Mark: mark,
|
||||
OnRelease: func() {
|
||||
e.sendBufferSizeInUseMu.Lock()
|
||||
if got := e.sendBufferSizeInUse; got < pktSize {
|
||||
@@ -360,6 +369,7 @@ func (c *WriteContext) WritePacket(pkt *stack.PacketBuffer, headerIncluded bool)
|
||||
Protocol: c.e.transProto,
|
||||
TTL: c.ttl,
|
||||
TOS: c.tos,
|
||||
DF: c.df,
|
||||
ExperimentOptionValue: expOptVal,
|
||||
}, pkt)
|
||||
|
||||
@@ -433,8 +443,8 @@ func (e *Endpoint) AcquireContextForWrite(opts tcpip.WriteOptions) (WriteContext
|
||||
route := e.connectedRoute
|
||||
to := opts.To
|
||||
info := e.Info()
|
||||
switch {
|
||||
case to == nil:
|
||||
switch to {
|
||||
case nil:
|
||||
// If the user doesn't specify a destination, they should have
|
||||
// connected to another address.
|
||||
if e.State() != transport.DatagramEndpointStateConnected {
|
||||
@@ -566,11 +576,28 @@ func (e *Endpoint) AcquireContextForWrite(opts tcpip.WriteOptions) (WriteContext
|
||||
panic(fmt.Sprintf("invalid protocol number = %d", netProto))
|
||||
}
|
||||
|
||||
// Set the DF (Don't Fragment) bit based on the PMTUD strategy,
|
||||
// matching TCP behavior in connect.go.
|
||||
// Note: In gVisor, WANT and DO are treated identically (both set DF).
|
||||
// Linux kernel differentiates them (WANT allows local fragmentation,
|
||||
// DO returns EMSGSIZE), but gVisor's IPv4 layer always allows local
|
||||
// fragmentation for locally-generated packets regardless of DF
|
||||
// (see gvisor.dev/issue/5919).
|
||||
//
|
||||
// PROBE also sets DF, matching Linux ip_dont_fragment(). In Linux,
|
||||
// PROBE differs from DO only in that it ignores incoming ICMP
|
||||
// "Fragmentation Needed" messages (i.e. does not update the cached
|
||||
// route PMTU). Since gVisor does not implement ICMP-based PMTU
|
||||
// feedback for transport sockets, PROBE and DO are functionally
|
||||
// equivalent here.
|
||||
df := e.pmtud == tcpip.PMTUDiscoveryWant || e.pmtud == tcpip.PMTUDiscoveryDo || e.pmtud == tcpip.PMTUDiscoveryProbe
|
||||
|
||||
return WriteContext{
|
||||
e: e,
|
||||
route: route,
|
||||
ttl: ttl,
|
||||
tos: tos,
|
||||
df: df,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -613,7 +640,7 @@ func (e *Endpoint) connectRouteRLocked(nicID tcpip.NICID, localAddr tcpip.Addres
|
||||
localAddr = tcpip.Address{}
|
||||
}
|
||||
|
||||
if header.IsV4MulticastAddress(addr.Addr) || header.IsV6MulticastAddress(addr.Addr) {
|
||||
if header.IsV4MulticastAddress(addr.Addr) {
|
||||
if nicID == 0 {
|
||||
nicID = e.multicastNICID
|
||||
}
|
||||
@@ -621,6 +648,9 @@ func (e *Endpoint) connectRouteRLocked(nicID tcpip.NICID, localAddr tcpip.Addres
|
||||
localAddr = e.multicastAddr
|
||||
}
|
||||
}
|
||||
if header.IsV6MulticastAddress(addr.Addr) && nicID == 0 {
|
||||
nicID = e.ipv6MulticastNICID
|
||||
}
|
||||
}
|
||||
|
||||
// Find a route to the desired destination.
|
||||
@@ -835,9 +865,18 @@ func (e *Endpoint) GetRemoteAddress() (tcpip.FullAddress, bool) {
|
||||
func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
|
||||
switch opt {
|
||||
case tcpip.MTUDiscoverOption:
|
||||
// Return not supported if the value is not disabling path
|
||||
// MTU discovery.
|
||||
if tcpip.PMTUDStrategy(v) != tcpip.PMTUDiscoveryDont {
|
||||
// Store PMTU discovery settings. The DF bit on outgoing
|
||||
// packets is set accordingly in AcquireContextForWrite.
|
||||
// PROBE is accepted alongside DO/WANT/DONT. In Linux,
|
||||
// PROBE sets DF but ignores ICMP-based PMTU updates;
|
||||
// since gVisor lacks ICMP PMTU feedback, it behaves
|
||||
// identically to DO.
|
||||
switch tcpip.PMTUDStrategy(v) {
|
||||
case tcpip.PMTUDiscoveryWant, tcpip.PMTUDiscoveryDont, tcpip.PMTUDiscoveryDo, tcpip.PMTUDiscoveryProbe:
|
||||
e.mu.Lock()
|
||||
e.pmtud = tcpip.PMTUDStrategy(v)
|
||||
e.mu.Unlock()
|
||||
default:
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
@@ -865,6 +904,18 @@ func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
|
||||
e.mu.Lock()
|
||||
e.ipv6TClass = uint8(v)
|
||||
e.mu.Unlock()
|
||||
|
||||
case tcpip.IPv6MulticastInterfaceOption:
|
||||
if v != 0 && !e.stack.CheckNIC(tcpip.NICID(v)) {
|
||||
return &tcpip.ErrUnknownNICID{}
|
||||
}
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
nic := tcpip.NICID(v)
|
||||
if info := e.Info(); info.BindNICID != 0 && info.BindNICID != nic {
|
||||
return &tcpip.ErrInvalidEndpointState{}
|
||||
}
|
||||
e.ipv6MulticastNICID = nic
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -874,8 +925,10 @@ func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
|
||||
func (e *Endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
|
||||
switch opt {
|
||||
case tcpip.MTUDiscoverOption:
|
||||
// The only supported setting is path MTU discovery disabled.
|
||||
return int(tcpip.PMTUDiscoveryDont), nil
|
||||
e.mu.Lock()
|
||||
v := int(e.pmtud)
|
||||
e.mu.Unlock()
|
||||
return v, nil
|
||||
|
||||
case tcpip.MulticastTTLOption:
|
||||
e.mu.Lock()
|
||||
@@ -907,11 +960,30 @@ func (e *Endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
|
||||
e.mu.RUnlock()
|
||||
return v, nil
|
||||
|
||||
case tcpip.IPv6MulticastInterfaceOption:
|
||||
e.mu.RLock()
|
||||
v := int(e.ipv6MulticastNICID)
|
||||
e.mu.RUnlock()
|
||||
return v, nil
|
||||
|
||||
default:
|
||||
return -1, &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
}
|
||||
|
||||
// multicastNetProto returns the network protocol of a given multicast address.
|
||||
// Returns an error if the address is not a multicast address.
|
||||
func (e *Endpoint) multicastNetProto(addr tcpip.Address) (tcpip.NetworkProtocolNumber, tcpip.Error) {
|
||||
switch {
|
||||
case header.IsV4MulticastAddress(addr):
|
||||
return header.IPv4ProtocolNumber, nil
|
||||
case header.IsV6MulticastAddress(addr):
|
||||
return header.IPv6ProtocolNumber, nil
|
||||
default:
|
||||
return 0, &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
}
|
||||
|
||||
// SetSockOpt sets the socket option.
|
||||
func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
|
||||
switch v := opt.(type) {
|
||||
@@ -952,21 +1024,23 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
|
||||
e.multicastAddr = addr
|
||||
|
||||
case *tcpip.AddMembershipOption:
|
||||
if !(header.IsV4MulticastAddress(v.MulticastAddr) && e.netProto == header.IPv4ProtocolNumber) && !(header.IsV6MulticastAddress(v.MulticastAddr) && e.netProto == header.IPv6ProtocolNumber) {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
// Allowing IP_ADD_MEMBERSHIP on an ipv6 socket matches Linux behavior:
|
||||
// https://github.com/torvalds/linux/blob/cec1e6e5d1a/net/ipv6/ipv6_sockglue.c#L964
|
||||
proto, err := e.multicastNetProto(v.MulticastAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nicID := v.NIC
|
||||
|
||||
if v.InterfaceAddr.Unspecified() {
|
||||
if nicID == 0 {
|
||||
if r, err := e.stack.FindRoute(0, tcpip.Address{}, v.MulticastAddr, e.netProto, false /* multicastLoop */); err == nil {
|
||||
if r, err := e.stack.FindRoute(0, tcpip.Address{}, v.MulticastAddr, proto, false /* multicastLoop */); err == nil {
|
||||
nicID = r.NICID()
|
||||
r.Release()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr)
|
||||
nicID = e.stack.CheckLocalAddress(nicID, proto, v.InterfaceAddr)
|
||||
}
|
||||
if nicID == 0 {
|
||||
return &tcpip.ErrUnknownDevice{}
|
||||
@@ -981,27 +1055,28 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
|
||||
return &tcpip.ErrPortInUse{}
|
||||
}
|
||||
|
||||
if err := e.stack.JoinGroup(e.netProto, nicID, v.MulticastAddr); err != nil {
|
||||
if err := e.stack.JoinGroup(proto, nicID, v.MulticastAddr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.multicastMemberships[memToInsert] = struct{}{}
|
||||
|
||||
case *tcpip.RemoveMembershipOption:
|
||||
if !(header.IsV4MulticastAddress(v.MulticastAddr) && e.netProto == header.IPv4ProtocolNumber) && !(header.IsV6MulticastAddress(v.MulticastAddr) && e.netProto == header.IPv6ProtocolNumber) {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
proto, err := e.multicastNetProto(v.MulticastAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nicID := v.NIC
|
||||
if v.InterfaceAddr.Unspecified() {
|
||||
if nicID == 0 {
|
||||
if r, err := e.stack.FindRoute(0, tcpip.Address{}, v.MulticastAddr, e.netProto, false /* multicastLoop */); err == nil {
|
||||
if r, err := e.stack.FindRoute(0, tcpip.Address{}, v.MulticastAddr, proto, false /* multicastLoop */); err == nil {
|
||||
nicID = r.NICID()
|
||||
r.Release()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr)
|
||||
nicID = e.stack.CheckLocalAddress(nicID, proto, v.InterfaceAddr)
|
||||
}
|
||||
if nicID == 0 {
|
||||
return &tcpip.ErrUnknownDevice{}
|
||||
@@ -1016,7 +1091,7 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
|
||||
return &tcpip.ErrBadLocalAddress{}
|
||||
}
|
||||
|
||||
if err := e.stack.LeaveGroup(e.netProto, nicID, v.MulticastAddr); err != nil {
|
||||
if err := e.stack.LeaveGroup(proto, nicID, v.MulticastAddr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
+14
-6
@@ -23,15 +23,18 @@ import (
|
||||
)
|
||||
|
||||
// Resume implements tcpip.ResumableEndpoint.Resume.
|
||||
func (e *Endpoint) Resume(s *stack.Stack) {
|
||||
func (e *Endpoint) Resume(s *stack.Stack) error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
e.stack = s
|
||||
|
||||
for m := range e.multicastMemberships {
|
||||
if err := e.stack.JoinGroup(e.netProto, m.nicID, m.multicastAddr); err != nil {
|
||||
panic(fmt.Sprintf("e.stack.JoinGroup(%d, %d, %s): %s", e.netProto, m.nicID, m.multicastAddr, err))
|
||||
proto, err := e.multicastNetProto(m.multicastAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("non multicast address in an existing membership during Resume: %s", err)
|
||||
}
|
||||
if err := e.stack.JoinGroup(proto, m.nicID, m.multicastAddr); err != nil {
|
||||
return fmt.Errorf("e.stack.JoinGroup(%d, %d, %s): %s", proto, m.nicID, m.multicastAddr, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,17 +45,22 @@ func (e *Endpoint) Resume(s *stack.Stack) {
|
||||
case transport.DatagramEndpointStateBound:
|
||||
if info.ID.LocalAddress.BitLen() != 0 && !e.isBroadcastOrMulticast(info.RegisterNICID, e.effectiveNetProto, info.ID.LocalAddress) {
|
||||
if e.stack.CheckLocalAddress(info.RegisterNICID, e.effectiveNetProto, info.ID.LocalAddress) == 0 {
|
||||
panic(fmt.Sprintf("got e.stack.CheckLocalAddress(%d, %d, %s) = 0, want != 0", info.RegisterNICID, e.effectiveNetProto, info.ID.LocalAddress))
|
||||
return fmt.Errorf("got e.stack.CheckLocalAddress(%d, %d, %s) = 0, want != 0", info.RegisterNICID, e.effectiveNetProto, info.ID.LocalAddress)
|
||||
}
|
||||
}
|
||||
case transport.DatagramEndpointStateConnected:
|
||||
var err tcpip.Error
|
||||
multicastLoop := e.ops.GetMulticastLoop()
|
||||
// Release the connectedRoute if present.
|
||||
if e.connectedRoute != nil {
|
||||
e.connectedRoute.Release()
|
||||
}
|
||||
e.connectedRoute, err = e.stack.FindRoute(info.RegisterNICID, info.ID.LocalAddress, info.ID.RemoteAddress, e.effectiveNetProto, multicastLoop)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("e.stack.FindRoute(%d, %s, %s, %d, %t): %s", info.RegisterNICID, info.ID.LocalAddress, info.ID.RemoteAddress, e.effectiveNetProto, multicastLoop, err))
|
||||
return fmt.Errorf("e.stack.FindRoute(%d, %s, %s, %d, %t): %s", info.RegisterNICID, info.ID.LocalAddress, info.ID.RemoteAddress, e.effectiveNetProto, multicastLoop, err)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled state = %s", state))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+14
-8
@@ -29,8 +29,10 @@ func (e *Endpoint) StateFields() []string {
|
||||
"multicastTTL",
|
||||
"multicastAddr",
|
||||
"multicastNICID",
|
||||
"ipv6MulticastNICID",
|
||||
"ipv4TOS",
|
||||
"ipv6TClass",
|
||||
"pmtud",
|
||||
"info",
|
||||
"state",
|
||||
}
|
||||
@@ -56,10 +58,12 @@ func (e *Endpoint) StateSave(stateSinkObject state.Sink) {
|
||||
stateSinkObject.Save(12, &e.multicastTTL)
|
||||
stateSinkObject.Save(13, &e.multicastAddr)
|
||||
stateSinkObject.Save(14, &e.multicastNICID)
|
||||
stateSinkObject.Save(15, &e.ipv4TOS)
|
||||
stateSinkObject.Save(16, &e.ipv6TClass)
|
||||
stateSinkObject.Save(17, &e.info)
|
||||
stateSinkObject.Save(18, &e.state)
|
||||
stateSinkObject.Save(15, &e.ipv6MulticastNICID)
|
||||
stateSinkObject.Save(16, &e.ipv4TOS)
|
||||
stateSinkObject.Save(17, &e.ipv6TClass)
|
||||
stateSinkObject.Save(18, &e.pmtud)
|
||||
stateSinkObject.Save(19, &e.info)
|
||||
stateSinkObject.Save(20, &e.state)
|
||||
}
|
||||
|
||||
func (e *Endpoint) afterLoad(context.Context) {}
|
||||
@@ -81,10 +85,12 @@ func (e *Endpoint) StateLoad(ctx context.Context, stateSourceObject state.Source
|
||||
stateSourceObject.Load(12, &e.multicastTTL)
|
||||
stateSourceObject.Load(13, &e.multicastAddr)
|
||||
stateSourceObject.Load(14, &e.multicastNICID)
|
||||
stateSourceObject.Load(15, &e.ipv4TOS)
|
||||
stateSourceObject.Load(16, &e.ipv6TClass)
|
||||
stateSourceObject.Load(17, &e.info)
|
||||
stateSourceObject.Load(18, &e.state)
|
||||
stateSourceObject.Load(15, &e.ipv6MulticastNICID)
|
||||
stateSourceObject.Load(16, &e.ipv4TOS)
|
||||
stateSourceObject.Load(17, &e.ipv6TClass)
|
||||
stateSourceObject.Load(18, &e.pmtud)
|
||||
stateSourceObject.Load(19, &e.info)
|
||||
stateSourceObject.Load(20, &e.state)
|
||||
}
|
||||
|
||||
func (m *multicastMembership) StateTypeName() string {
|
||||
|
||||
+155
-28
@@ -26,16 +26,25 @@ package packet
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
type tpacketVersion int
|
||||
|
||||
const (
|
||||
tpacketVersion1 tpacketVersion = iota
|
||||
tpacketVersion2
|
||||
)
|
||||
|
||||
var _ stack.MappablePacketEndpoint = (*endpoint)(nil)
|
||||
|
||||
// +stateify savable
|
||||
type packet struct {
|
||||
packetEntry
|
||||
@@ -54,8 +63,9 @@ type packet struct {
|
||||
//
|
||||
// Lock order:
|
||||
//
|
||||
// endpoint.mu
|
||||
// endpoint.rcvMu
|
||||
// endpoint.mu
|
||||
// endpoint.rcvMu
|
||||
// endpoint.packetMmapMu
|
||||
//
|
||||
// +stateify savable
|
||||
type endpoint struct {
|
||||
@@ -70,7 +80,7 @@ type endpoint struct {
|
||||
stats tcpip.TransportEndpointStats
|
||||
|
||||
// The following fields are used to manage the receive queue.
|
||||
rcvMu sync.Mutex `state:"nosave"`
|
||||
rcvMu rcvMutex `state:"nosave"`
|
||||
// +checklocks:rcvMu
|
||||
rcvList packetList
|
||||
// +checklocks:rcvMu
|
||||
@@ -80,7 +90,7 @@ type endpoint struct {
|
||||
// +checklocks:rcvMu
|
||||
rcvDisabled bool
|
||||
|
||||
mu sync.RWMutex `state:"nosave"`
|
||||
mu endpointRWMutex `state:"nosave"`
|
||||
// +checklocks:mu
|
||||
closed bool
|
||||
// +checklocks:mu
|
||||
@@ -88,9 +98,17 @@ type endpoint struct {
|
||||
// +checklocks:mu
|
||||
boundNIC tcpip.NICID
|
||||
|
||||
lastErrorMu sync.Mutex `state:"nosave"`
|
||||
lastErrorMu lastErrorMutex `state:"nosave"`
|
||||
// +checklocks:lastErrorMu
|
||||
lastError tcpip.Error
|
||||
|
||||
packetMmapMu packetMmapRWMutex `state:"nosave"`
|
||||
// +checklocks:packetMmapMu
|
||||
packetMMapVersion tpacketVersion
|
||||
// +checklocks:packetMmapMu
|
||||
packetMMapReserve int
|
||||
// +checklocks:packetMmapMu
|
||||
packetMMapEp stack.PacketMMapEndpoint
|
||||
}
|
||||
|
||||
// NewEndpoint returns a new packet endpoint.
|
||||
@@ -129,13 +147,18 @@ func (ep *endpoint) Abort() {
|
||||
func (ep *endpoint) Close() {
|
||||
ep.mu.Lock()
|
||||
defer ep.mu.Unlock()
|
||||
|
||||
if ep.closed {
|
||||
return
|
||||
}
|
||||
|
||||
ep.stack.UnregisterPacketEndpoint(ep.boundNIC, ep.boundNetProto, ep)
|
||||
|
||||
ep.packetMmapMu.Lock()
|
||||
if ep.packetMMapEp != nil {
|
||||
ep.packetMMapEp.Close()
|
||||
ep.packetMMapEp = nil
|
||||
}
|
||||
ep.packetMmapMu.Unlock()
|
||||
|
||||
ep.rcvMu.Lock()
|
||||
defer ep.rcvMu.Unlock()
|
||||
|
||||
@@ -172,11 +195,13 @@ func (ep *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResul
|
||||
}
|
||||
|
||||
packet := ep.rcvList.Front()
|
||||
if !opts.Peek {
|
||||
if opts.Peek {
|
||||
packet.data.IncRef()
|
||||
} else {
|
||||
ep.rcvList.Remove(packet)
|
||||
defer packet.data.DecRef()
|
||||
ep.rcvBufSize -= packet.data.Size()
|
||||
}
|
||||
defer packet.data.DecRef()
|
||||
|
||||
ep.rcvMu.Unlock()
|
||||
|
||||
@@ -244,11 +269,12 @@ func (ep *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tc
|
||||
}
|
||||
payloadSz := payload.Size()
|
||||
|
||||
mark := ep.ops.GetMark()
|
||||
if err := func() tcpip.Error {
|
||||
if ep.cooked {
|
||||
return ep.stack.WritePacketToRemote(nicID, remote, proto, payload)
|
||||
return ep.stack.WritePacketToRemoteWithMark(nicID, remote, proto, payload, mark)
|
||||
}
|
||||
return ep.stack.WriteRawPacket(nicID, proto, payload)
|
||||
return ep.stack.WriteRawPacketWithMark(nicID, proto, payload, mark)
|
||||
}(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -348,6 +374,11 @@ func (ep *endpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
|
||||
// Determine whether the endpoint is readable.
|
||||
if (mask & waiter.ReadableEvents) != 0 {
|
||||
ep.packetMmapMu.RLock()
|
||||
if ep.packetMMapEp != nil {
|
||||
result |= ep.packetMMapEp.Readiness(mask)
|
||||
}
|
||||
ep.packetMmapMu.RUnlock()
|
||||
ep.rcvMu.Lock()
|
||||
if !ep.rcvList.Empty() || ep.rcvClosed {
|
||||
result |= waiter.ReadableEvents
|
||||
@@ -358,13 +389,18 @@ func (ep *endpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
|
||||
return result
|
||||
}
|
||||
|
||||
// SetSockOpt implements tcpip.Endpoint.SetSockOpt. Packet sockets cannot be
|
||||
// used with SetSockOpt, and this function always returns
|
||||
// *tcpip.ErrNotSupported.
|
||||
// SetSockOpt implements tcpip.Endpoint.SetSockOpt.
|
||||
func (ep *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
|
||||
switch opt.(type) {
|
||||
case *tcpip.SocketDetachFilterOption:
|
||||
return nil
|
||||
case *tcpip.TpacketReq:
|
||||
ep.rcvMu.Lock()
|
||||
defer ep.rcvMu.Unlock()
|
||||
if !ep.rcvList.Empty() {
|
||||
return &tcpip.ErrWouldBlock{}
|
||||
}
|
||||
return nil
|
||||
|
||||
default:
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
@@ -372,8 +408,37 @@ func (ep *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
|
||||
}
|
||||
|
||||
// SetSockOptInt implements tcpip.Endpoint.SetSockOptInt.
|
||||
func (*endpoint) SetSockOptInt(tcpip.SockOptInt, int) tcpip.Error {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
func (ep *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
|
||||
switch opt {
|
||||
case tcpip.PacketMMapVersionOption:
|
||||
ep.packetMmapMu.Lock()
|
||||
defer ep.packetMmapMu.Unlock()
|
||||
// We support up to TPACKET_V2.
|
||||
version := tpacketVersion(v)
|
||||
switch version {
|
||||
case tpacketVersion1, tpacketVersion2:
|
||||
if ep.packetMMapEp != nil {
|
||||
return &tcpip.ErrEndpointBusy{}
|
||||
}
|
||||
ep.packetMMapVersion = version
|
||||
return nil
|
||||
default:
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
case tcpip.PacketMMapReserveOption:
|
||||
ep.packetMmapMu.Lock()
|
||||
defer ep.packetMmapMu.Unlock()
|
||||
if ep.packetMMapEp != nil {
|
||||
return &tcpip.ErrEndpointBusy{}
|
||||
}
|
||||
if uint32(v) > uint32(math.MaxInt32) {
|
||||
return &tcpip.ErrInvalidOptionValue{}
|
||||
}
|
||||
ep.packetMMapReserve = v
|
||||
return nil
|
||||
default:
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
}
|
||||
|
||||
func (ep *endpoint) LastError() tcpip.Error {
|
||||
@@ -393,8 +458,19 @@ func (ep *endpoint) UpdateLastError(err tcpip.Error) {
|
||||
}
|
||||
|
||||
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
|
||||
func (*endpoint) GetSockOpt(tcpip.GettableSocketOption) tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
func (ep *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
|
||||
switch opt := opt.(type) {
|
||||
case *tcpip.TpacketStats:
|
||||
ep.packetMmapMu.RLock()
|
||||
defer ep.packetMmapMu.RUnlock()
|
||||
if ep.packetMMapEp == nil {
|
||||
return nil
|
||||
}
|
||||
*opt = ep.packetMMapEp.Stats()
|
||||
return nil
|
||||
default:
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
}
|
||||
|
||||
// GetSockOptInt implements tcpip.Endpoint.GetSockOptInt.
|
||||
@@ -415,8 +491,31 @@ func (ep *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
|
||||
}
|
||||
}
|
||||
|
||||
// HandlePacket implements stack.PacketEndpoint.HandlePacket.
|
||||
// handlePacket implements stack.PacketEndpoint.HandlePacket
|
||||
func (ep *endpoint) HandlePacket(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
|
||||
ep.packetMmapMu.RLock()
|
||||
if ep.packetMMapEp != nil {
|
||||
if handled := ep.packetMMapEp.HandlePacket(nicID, netProto, pkt); handled {
|
||||
ep.packetMmapMu.RUnlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
ep.packetMmapMu.RUnlock()
|
||||
|
||||
wasEmpty := ep.handlePacketInner(nicID, netProto, pkt)
|
||||
|
||||
ep.stats.PacketsReceived.Increment()
|
||||
// Notify waiters that there's data to be read.
|
||||
if wasEmpty {
|
||||
ep.waiterQueue.Notify(waiter.ReadableEvents)
|
||||
}
|
||||
}
|
||||
|
||||
func (ep *endpoint) HandlePacketMMapCopy(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
|
||||
_ = ep.handlePacketInner(nicID, netProto, pkt)
|
||||
}
|
||||
|
||||
func (ep *endpoint) handlePacketInner(nicID tcpip.NICID, netProto tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) bool {
|
||||
ep.rcvMu.Lock()
|
||||
|
||||
// Drop the packet if our buffer is currently full.
|
||||
@@ -424,7 +523,7 @@ func (ep *endpoint) HandlePacket(nicID tcpip.NICID, netProto tcpip.NetworkProtoc
|
||||
ep.rcvMu.Unlock()
|
||||
ep.stack.Stats().DroppedPackets.Increment()
|
||||
ep.stats.ReceiveErrors.ClosedReceiver.Increment()
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
rcvBufSize := ep.ops.GetReceiveBufferSize()
|
||||
@@ -432,7 +531,7 @@ func (ep *endpoint) HandlePacket(nicID tcpip.NICID, netProto tcpip.NetworkProtoc
|
||||
ep.rcvMu.Unlock()
|
||||
ep.stack.Stats().DroppedPackets.Increment()
|
||||
ep.stats.ReceiveErrors.ReceiveBufferOverflow.Increment()
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
wasEmpty := ep.rcvBufSize == 0
|
||||
@@ -464,13 +563,8 @@ func (ep *endpoint) HandlePacket(nicID tcpip.NICID, netProto tcpip.NetworkProtoc
|
||||
|
||||
ep.rcvList.PushBack(&rcvdPkt)
|
||||
ep.rcvBufSize += rcvdPkt.data.Size()
|
||||
|
||||
ep.rcvMu.Unlock()
|
||||
ep.stats.PacketsReceived.Increment()
|
||||
// Notify waiters that there's data to be read.
|
||||
if wasEmpty {
|
||||
ep.waiterQueue.Notify(waiter.ReadableEvents)
|
||||
}
|
||||
return wasEmpty
|
||||
}
|
||||
|
||||
// State implements socket.Socket.State.
|
||||
@@ -497,3 +591,36 @@ func (*endpoint) SetOwner(tcpip.PacketOwner) {}
|
||||
func (ep *endpoint) SocketOptions() *tcpip.SocketOptions {
|
||||
return &ep.ops
|
||||
}
|
||||
|
||||
// GetPacketMMapOpts implements stack.MappablePacketEndpoint.GetPacketMMapOpts.
|
||||
func (ep *endpoint) GetPacketMMapOpts(req *tcpip.TpacketReq, isRx bool) stack.PacketMMapOpts {
|
||||
ep.packetMmapMu.Lock()
|
||||
defer ep.packetMmapMu.Unlock()
|
||||
|
||||
return stack.PacketMMapOpts{
|
||||
Req: req,
|
||||
IsRx: isRx,
|
||||
Cooked: ep.cooked,
|
||||
Stack: ep.stack,
|
||||
Wq: ep.waiterQueue,
|
||||
PacketEndpoint: ep,
|
||||
Version: int(ep.packetMMapVersion),
|
||||
Reserve: uint32(ep.packetMMapReserve),
|
||||
}
|
||||
}
|
||||
|
||||
// SetPacketMMapEndpoint implements
|
||||
// stack.MappablePacketEndpoint.SetPacketMMapEndpoint.
|
||||
func (ep *endpoint) SetPacketMMapEndpoint(m stack.PacketMMapEndpoint) {
|
||||
ep.packetMmapMu.Lock()
|
||||
defer ep.packetMmapMu.Unlock()
|
||||
ep.packetMMapEp = m
|
||||
}
|
||||
|
||||
// GetPacketMMapEndpoint implements
|
||||
// stack.MappablePacketEndpoint.GetPacketMMapEndpoint.
|
||||
func (ep *endpoint) GetPacketMMapEndpoint() stack.PacketMMapEndpoint {
|
||||
ep.packetMmapMu.RLock()
|
||||
defer ep.packetMmapMu.RUnlock()
|
||||
return ep.packetMMapEp
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// RWMutex is sync.RWMutex with the correctness validator.
|
||||
type endpointRWMutex struct {
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var endpointlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type endpointlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) Lock() {
|
||||
locking.AddGLock(endpointprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) NestedLock(i endpointlockNameIndex) {
|
||||
locking.AddGLock(endpointprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) Unlock() {
|
||||
m.mu.Unlock()
|
||||
locking.DelGLock(endpointprefixIndex, -1)
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) NestedUnlock(i endpointlockNameIndex) {
|
||||
m.mu.Unlock()
|
||||
locking.DelGLock(endpointprefixIndex, int(i))
|
||||
}
|
||||
|
||||
// RLock locks m for reading.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) RLock() {
|
||||
locking.AddGLock(endpointprefixIndex, -1)
|
||||
m.mu.RLock()
|
||||
}
|
||||
|
||||
// RUnlock undoes a single RLock call.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) RUnlock() {
|
||||
m.mu.RUnlock()
|
||||
locking.DelGLock(endpointprefixIndex, -1)
|
||||
}
|
||||
|
||||
// RLockBypass locks m for reading without executing the validator.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) RLockBypass() {
|
||||
m.mu.RLock()
|
||||
}
|
||||
|
||||
// RUnlockBypass undoes a single RLockBypass call.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) RUnlockBypass() {
|
||||
m.mu.RUnlock()
|
||||
}
|
||||
|
||||
// DowngradeLock atomically unlocks rw for writing and locks it for reading.
|
||||
// +checklocksignore
|
||||
func (m *endpointRWMutex) DowngradeLock() {
|
||||
m.mu.DowngradeLock()
|
||||
}
|
||||
|
||||
var endpointprefixIndex *locking.MutexClass
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func endpointinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
endpointinitLockNames()
|
||||
endpointprefixIndex = locking.NewMutexClass(reflect.TypeFor[endpointRWMutex](), endpointlockNames)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type rcvMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var rcvprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var rcvlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type rcvlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *rcvMutex) Lock() {
|
||||
locking.AddGLock(rcvprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *rcvMutex) NestedLock(i rcvlockNameIndex) {
|
||||
locking.AddGLock(rcvprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *rcvMutex) Unlock() {
|
||||
locking.DelGLock(rcvprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *rcvMutex) NestedUnlock(i rcvlockNameIndex) {
|
||||
locking.DelGLock(rcvprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func rcvinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
rcvinitLockNames()
|
||||
rcvprefixIndex = locking.NewMutexClass(reflect.TypeFor[rcvMutex](), rcvlockNames)
|
||||
}
|
||||
@@ -16,9 +16,9 @@ package packet
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
@@ -36,18 +36,13 @@ func (p *packet) loadReceivedAt(_ context.Context, nsec int64) {
|
||||
// beforeSave is invoked by stateify.
|
||||
func (ep *endpoint) beforeSave() {
|
||||
ep.rcvMu.Lock()
|
||||
defer ep.rcvMu.Unlock()
|
||||
ep.rcvDisabled = true
|
||||
ep.rcvMu.Unlock()
|
||||
ep.stack.RegisterResumableEndpoint(ep)
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (ep *endpoint) afterLoad(ctx context.Context) {
|
||||
if !ep.stack.IsSaveRestoreEnabled() {
|
||||
ep.mu.Lock()
|
||||
ep.stack = stack.RestoreStackFromContext(ctx)
|
||||
ep.mu.Unlock()
|
||||
}
|
||||
ep.stack.RegisterRestoredEndpoint(ep)
|
||||
}
|
||||
|
||||
@@ -58,7 +53,8 @@ func (ep *endpoint) Restore(_ *stack.Stack) {
|
||||
|
||||
ep.ops.InitHandler(ep, ep.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
if err := ep.stack.RegisterPacketEndpoint(ep.boundNIC, ep.boundNetProto, ep); err != nil {
|
||||
panic(fmt.Sprintf("RegisterPacketEndpoint(%d, %d, _): %s", ep.boundNIC, ep.boundNetProto, err))
|
||||
log.Warningf("RegisterPacketEndpoint(%d, %d, _) failed during restore with error: %s", ep.boundNIC, ep.boundNetProto, err)
|
||||
return
|
||||
}
|
||||
|
||||
ep.rcvMu.Lock()
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type lastErrorMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var lastErrorprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var lastErrorlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type lastErrorlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *lastErrorMutex) Lock() {
|
||||
locking.AddGLock(lastErrorprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *lastErrorMutex) NestedLock(i lastErrorlockNameIndex) {
|
||||
locking.AddGLock(lastErrorprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *lastErrorMutex) Unlock() {
|
||||
locking.DelGLock(lastErrorprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *lastErrorMutex) NestedUnlock(i lastErrorlockNameIndex) {
|
||||
locking.DelGLock(lastErrorprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func lastErrorinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
lastErrorinitLockNames()
|
||||
lastErrorprefixIndex = locking.NewMutexClass(reflect.TypeFor[lastErrorMutex](), lastErrorlockNames)
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// RWMutex is sync.RWMutex with the correctness validator.
|
||||
type packetMmapRWMutex struct {
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var packetMmaplockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type packetMmaplockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) Lock() {
|
||||
locking.AddGLock(packetMmapprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) NestedLock(i packetMmaplockNameIndex) {
|
||||
locking.AddGLock(packetMmapprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) Unlock() {
|
||||
m.mu.Unlock()
|
||||
locking.DelGLock(packetMmapprefixIndex, -1)
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) NestedUnlock(i packetMmaplockNameIndex) {
|
||||
m.mu.Unlock()
|
||||
locking.DelGLock(packetMmapprefixIndex, int(i))
|
||||
}
|
||||
|
||||
// RLock locks m for reading.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) RLock() {
|
||||
locking.AddGLock(packetMmapprefixIndex, -1)
|
||||
m.mu.RLock()
|
||||
}
|
||||
|
||||
// RUnlock undoes a single RLock call.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) RUnlock() {
|
||||
m.mu.RUnlock()
|
||||
locking.DelGLock(packetMmapprefixIndex, -1)
|
||||
}
|
||||
|
||||
// RLockBypass locks m for reading without executing the validator.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) RLockBypass() {
|
||||
m.mu.RLock()
|
||||
}
|
||||
|
||||
// RUnlockBypass undoes a single RLockBypass call.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) RUnlockBypass() {
|
||||
m.mu.RUnlock()
|
||||
}
|
||||
|
||||
// DowngradeLock atomically unlocks rw for writing and locks it for reading.
|
||||
// +checklocksignore
|
||||
func (m *packetMmapRWMutex) DowngradeLock() {
|
||||
m.mu.DowngradeLock()
|
||||
}
|
||||
|
||||
var packetMmapprefixIndex *locking.MutexClass
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func packetMmapinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
packetMmapinitLockNames()
|
||||
packetMmapprefixIndex = locking.NewMutexClass(reflect.TypeFor[packetMmapRWMutex](), packetMmaplockNames)
|
||||
}
|
||||
+11
-2
@@ -27,8 +27,8 @@ func (p *packet) beforeSave() {}
|
||||
// +checklocksignore
|
||||
func (p *packet) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
var receivedAtValue int64
|
||||
receivedAtValue = p.saveReceivedAt()
|
||||
receivedAtValue := p.saveReceivedAt()
|
||||
_ = (int64)(receivedAtValue)
|
||||
stateSinkObject.SaveValue(2, receivedAtValue)
|
||||
stateSinkObject.Save(0, &p.packetEntry)
|
||||
stateSinkObject.Save(1, &p.data)
|
||||
@@ -67,6 +67,9 @@ func (ep *endpoint) StateFields() []string {
|
||||
"boundNetProto",
|
||||
"boundNIC",
|
||||
"lastError",
|
||||
"packetMMapVersion",
|
||||
"packetMMapReserve",
|
||||
"packetMMapEp",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +90,9 @@ func (ep *endpoint) StateSave(stateSinkObject state.Sink) {
|
||||
stateSinkObject.Save(11, &ep.boundNetProto)
|
||||
stateSinkObject.Save(12, &ep.boundNIC)
|
||||
stateSinkObject.Save(13, &ep.lastError)
|
||||
stateSinkObject.Save(14, &ep.packetMMapVersion)
|
||||
stateSinkObject.Save(15, &ep.packetMMapReserve)
|
||||
stateSinkObject.Save(16, &ep.packetMMapEp)
|
||||
}
|
||||
|
||||
// +checklocksignore
|
||||
@@ -105,6 +111,9 @@ func (ep *endpoint) StateLoad(ctx context.Context, stateSourceObject state.Sourc
|
||||
stateSourceObject.Load(11, &ep.boundNetProto)
|
||||
stateSourceObject.Load(12, &ep.boundNIC)
|
||||
stateSourceObject.Load(13, &ep.lastError)
|
||||
stateSourceObject.Load(14, &ep.packetMMapVersion)
|
||||
stateSourceObject.Load(15, &ep.packetMMapReserve)
|
||||
stateSourceObject.Load(16, &ep.packetMMapEp)
|
||||
stateSourceObject.AfterLoad(func() { ep.afterLoad(ctx) })
|
||||
}
|
||||
|
||||
|
||||
@@ -236,11 +236,13 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
|
||||
}
|
||||
|
||||
pkt := e.rcvList.Front()
|
||||
if !opts.Peek {
|
||||
if opts.Peek {
|
||||
pkt.data.IncRef()
|
||||
} else {
|
||||
e.rcvList.Remove(pkt)
|
||||
defer pkt.data.DecRef()
|
||||
e.rcvBufSize -= pkt.data.Data().Size()
|
||||
}
|
||||
defer pkt.data.DecRef()
|
||||
|
||||
e.rcvMu.Unlock()
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
@@ -34,11 +35,7 @@ func (p *rawPacket) loadReceivedAt(_ context.Context, nsec int64) {
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (e *endpoint) afterLoad(ctx context.Context) {
|
||||
if e.stack.IsSaveRestoreEnabled() {
|
||||
e.stack.RegisterRestoredEndpoint(e)
|
||||
} else {
|
||||
stack.RestoreStackFromContext(ctx).RegisterRestoredEndpoint(e)
|
||||
}
|
||||
e.stack.RegisterRestoredEndpoint(e)
|
||||
}
|
||||
|
||||
// beforeSave is invoked by stateify.
|
||||
@@ -49,22 +46,13 @@ func (e *endpoint) beforeSave() {
|
||||
|
||||
// Restore implements tcpip.RestoredEndpoint.Restore.
|
||||
func (e *endpoint) Restore(s *stack.Stack) {
|
||||
e.setReceiveDisabled(false)
|
||||
e.net.Resume(s)
|
||||
if e.stack.IsSaveRestoreEnabled() {
|
||||
e.ops.InitHandler(e, e.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
if err := e.net.Resume(s); err != nil {
|
||||
log.Warningf("Closing the raw endpoint as it cannot be restored, err: %v", err)
|
||||
e.Close()
|
||||
return
|
||||
}
|
||||
|
||||
e.stack = s
|
||||
e.setReceiveDisabled(false)
|
||||
e.ops.InitHandler(e, e.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
|
||||
|
||||
if e.associated {
|
||||
netProto := e.net.NetProto()
|
||||
if err := e.stack.RegisterRawTransportEndpoint(netProto, e.transProto, e); err != nil {
|
||||
panic("RegisterRawTransportEndpoint failed during restore")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resume implements tcpip.ResumableEndpoint.Resume.
|
||||
|
||||
@@ -29,8 +29,8 @@ func (p *rawPacket) beforeSave() {}
|
||||
// +checklocksignore
|
||||
func (p *rawPacket) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
var receivedAtValue int64
|
||||
receivedAtValue = p.saveReceivedAt()
|
||||
receivedAtValue := p.saveReceivedAt()
|
||||
_ = (int64)(receivedAtValue)
|
||||
stateSinkObject.SaveValue(2, receivedAtValue)
|
||||
stateSinkObject.Save(0, &p.rawPacketEntry)
|
||||
stateSinkObject.Save(1, &p.data)
|
||||
|
||||
+14
-4
@@ -23,7 +23,6 @@ import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/ports"
|
||||
@@ -87,9 +86,11 @@ type listenContext struct {
|
||||
// this context. Can be nil if the context is created by the forwarder.
|
||||
listenEP *Endpoint
|
||||
|
||||
// hasherMu protects hasher.
|
||||
hasherMu sync.Mutex
|
||||
hasherMu hasherMutex
|
||||
|
||||
// hasher is the hash function used to generate a SYN cookie.
|
||||
//
|
||||
// +checklocks:hasherMu
|
||||
hasher hash.Hash
|
||||
|
||||
// v6Only is true if listenEP is a dual stack socket and has the
|
||||
@@ -129,6 +130,8 @@ func newListenContext(stk *stack.Stack, protocol *protocol, listenEP *Endpoint,
|
||||
|
||||
// cookieHash calculates the cookieHash for the given id, timestamp and nonce
|
||||
// index. The hash is used to create and validate cookies.
|
||||
//
|
||||
// +checklocksexclude:l.hasherMu
|
||||
func (l *listenContext) cookieHash(id stack.TransportEndpointID, ts uint32, nonceIndex int) uint32 {
|
||||
|
||||
// Initialize block with fixed-size data: local ports and v.
|
||||
@@ -158,6 +161,8 @@ func (l *listenContext) cookieHash(id stack.TransportEndpointID, ts uint32, nonc
|
||||
|
||||
// createCookie creates a SYN cookie for the given id and incoming sequence
|
||||
// number.
|
||||
//
|
||||
// +checklocksexclude:l.hasherMu
|
||||
func (l *listenContext) createCookie(id stack.TransportEndpointID, seq seqnum.Value, data uint32) seqnum.Value {
|
||||
ts := timeStamp(l.stack.Clock())
|
||||
v := l.cookieHash(id, 0, 0) + uint32(seq) + (ts << tsOffset)
|
||||
@@ -168,6 +173,8 @@ func (l *listenContext) createCookie(id stack.TransportEndpointID, seq seqnum.Va
|
||||
// isCookieValid checks if the supplied cookie is valid for the given id and
|
||||
// sequence number. If it is, it also returns the data originally encoded in the
|
||||
// cookie when createCookie was called.
|
||||
//
|
||||
// +checklocksexclude:l.hasherMu
|
||||
func (l *listenContext) isCookieValid(id stack.TransportEndpointID, cookie seqnum.Value, seq seqnum.Value) (uint32, bool) {
|
||||
ts := timeStamp(l.stack.Clock())
|
||||
v := uint32(cookie) - l.cookieHash(id, 0, 0) - uint32(seq)
|
||||
@@ -259,7 +266,7 @@ func (l *listenContext) startHandshake(s *segment, opts header.TCPSynOptions, qu
|
||||
|
||||
// Propagate any inheritable options from the listening endpoint
|
||||
// to the newly created endpoint.
|
||||
l.listenEP.propagateInheritableOptionsLocked(ep) // +checklocksforce
|
||||
l.listenEP.propagateInheritableOptionsLocked(ep) // +checklocksforce:ep.mu
|
||||
|
||||
if !ep.reserveTupleLocked() {
|
||||
ep.mu.Unlock()
|
||||
@@ -363,6 +370,7 @@ func (e *Endpoint) propagateInheritableOptionsLocked(n *Endpoint) {
|
||||
n.boundBindToDevice = e.boundBindToDevice
|
||||
n.boundPortFlags = e.boundPortFlags
|
||||
n.userMSS = e.userMSS
|
||||
n.ops.SetMark(e.ops.GetMark())
|
||||
}
|
||||
|
||||
// reserveTupleLocked reserves an accepted endpoint's tuple.
|
||||
@@ -434,6 +442,7 @@ func (a *acceptQueue) isFull() bool {
|
||||
// and needs to handle it.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksexclude:ctx.hasherMu
|
||||
func (e *Endpoint) handleListenSegment(ctx *listenContext, s *segment) tcpip.Error {
|
||||
e.rcvQueueMu.Lock()
|
||||
rcvClosed := e.RcvClosed
|
||||
@@ -533,6 +542,7 @@ func (e *Endpoint) handleListenSegment(ctx *listenContext, s *segment) tcpip.Err
|
||||
seq: cookie,
|
||||
ack: s.sequenceNumber + 1,
|
||||
rcvWnd: ctx.rcvWnd,
|
||||
df: e.pmtud == tcpip.PMTUDiscoveryWant || e.pmtud == tcpip.PMTUDiscoveryDo || e.pmtud == tcpip.PMTUDiscoveryProbe,
|
||||
expOptVal: e.getExperimentOptionValue(route),
|
||||
}
|
||||
if err := e.sendSynTCP(route, fields, synOpts); err != nil {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type acceptMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var acceptprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var acceptlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type acceptlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *acceptMutex) Lock() {
|
||||
locking.AddGLock(acceptprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *acceptMutex) NestedLock(i acceptlockNameIndex) {
|
||||
locking.AddGLock(acceptprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *acceptMutex) Unlock() {
|
||||
locking.DelGLock(acceptprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *acceptMutex) NestedUnlock(i acceptlockNameIndex) {
|
||||
locking.DelGLock(acceptprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func acceptinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
acceptinitLockNames()
|
||||
acceptprefixIndex = locking.NewMutexClass(reflect.TypeFor[acceptMutex](), acceptlockNames)
|
||||
}
|
||||
+139
-70
@@ -129,6 +129,9 @@ type handshake struct {
|
||||
// processor if there are pending segments that need to be processed.
|
||||
//
|
||||
// NOTE: e.mu is held for the duration of the call to f().
|
||||
// The wrapper does not acquire a sender RTT mutex before calling f. checklocks
|
||||
// cannot recover f's captured sender to express that exclusion; the returned
|
||||
// callback runs later, so its lock state is not a precondition on timerHandler.
|
||||
func timerHandler(e *Endpoint, f func() tcpip.Error) func() {
|
||||
return func() {
|
||||
e.mu.Lock()
|
||||
@@ -371,6 +374,7 @@ func (h *handshake) synSentState(s *segment) tcpip.Error {
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
df: h.ep.pmtud == tcpip.PMTUDiscoveryWant || h.ep.pmtud == tcpip.PMTUDiscoveryDo || h.ep.pmtud == tcpip.PMTUDiscoveryProbe,
|
||||
expOptVal: h.ep.getExperimentOptionValue(h.ep.route),
|
||||
}, synOpts)
|
||||
return nil
|
||||
@@ -379,6 +383,9 @@ func (h *handshake) synSentState(s *segment) tcpip.Error {
|
||||
// synRcvdState handles a segment received when the TCP 3-way handshake is in
|
||||
// the SYN-RCVD state.
|
||||
// +checklocks:h.ep.mu
|
||||
// +checklocksexclude:h.ep.segmentQueue.mu
|
||||
// +checklocksexclude:h.ep.pendingProcessingMu
|
||||
// +checklocksexclude:h.listenEP.listenCtx.hasherMu
|
||||
func (h *handshake) synRcvdState(s *segment) tcpip.Error {
|
||||
if s.flags.Contains(header.TCPFlagRst) {
|
||||
// RFC 793, page 37, states that in the SYN-RCVD state, a reset
|
||||
@@ -458,6 +465,7 @@ func (h *handshake) synRcvdState(s *segment) tcpip.Error {
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
df: h.ep.pmtud == tcpip.PMTUDiscoveryWant || h.ep.pmtud == tcpip.PMTUDiscoveryDo || h.ep.pmtud == tcpip.PMTUDiscoveryProbe,
|
||||
expOptVal: h.ep.getExperimentOptionValue(h.ep.route),
|
||||
}, synOpts)
|
||||
return nil
|
||||
@@ -512,6 +520,9 @@ func (h *handshake) synRcvdState(s *segment) tcpip.Error {
|
||||
}
|
||||
|
||||
// +checklocks:h.ep.mu
|
||||
// +checklocksexclude:h.ep.segmentQueue.mu
|
||||
// +checklocksexclude:h.ep.pendingProcessingMu
|
||||
// +checklocksexclude:h.listenEP.listenCtx.hasherMu
|
||||
func (h *handshake) handleSegment(s *segment) tcpip.Error {
|
||||
h.sndWnd = s.window
|
||||
if !s.flags.Contains(header.TCPFlagSyn) && h.sndWndScale > 0 {
|
||||
@@ -530,6 +541,9 @@ func (h *handshake) handleSegment(s *segment) tcpip.Error {
|
||||
// processSegments goes through the segment queue and processes up to
|
||||
// maxSegmentsPerWake (if they're available).
|
||||
// +checklocks:h.ep.mu
|
||||
// +checklocksexclude:h.ep.segmentQueue.mu
|
||||
// +checklocksexclude:h.ep.pendingProcessingMu
|
||||
// +checklocksexclude:h.listenEP.listenCtx.hasherMu
|
||||
func (h *handshake) processSegments() tcpip.Error {
|
||||
for i := 0; i < maxSegmentsPerWake; i++ {
|
||||
s := h.ep.segmentQueue.dequeue()
|
||||
@@ -556,6 +570,7 @@ func (h *handshake) processSegments() tcpip.Error {
|
||||
|
||||
// start sends the first SYN/SYN-ACK. It does not block, even if link address
|
||||
// resolution is required.
|
||||
// +checklocks:h.ep.mu
|
||||
func (h *handshake) start() {
|
||||
h.startTime = h.ep.stack.Clock().NowMonotonic()
|
||||
h.ep.amss = calculateAdvertisedMSS(h.ep.userMSS, h.ep.route)
|
||||
@@ -596,6 +611,7 @@ func (h *handshake) start() {
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
df: h.ep.pmtud == tcpip.PMTUDiscoveryWant || h.ep.pmtud == tcpip.PMTUDiscoveryDo || h.ep.pmtud == tcpip.PMTUDiscoveryProbe,
|
||||
expOptVal: h.ep.getExperimentOptionValue(h.ep.route),
|
||||
}, synOpts)
|
||||
}
|
||||
@@ -633,6 +649,7 @@ func (h *handshake) retransmitHandlerLocked() tcpip.Error {
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
df: h.ep.pmtud == tcpip.PMTUDiscoveryWant || h.ep.pmtud == tcpip.PMTUDiscoveryDo || h.ep.pmtud == tcpip.PMTUDiscoveryProbe,
|
||||
expOptVal: e.getExperimentOptionValue(e.route),
|
||||
}, h.sendSYNOpts)
|
||||
// If we have ever retransmitted the SYN-ACK or
|
||||
@@ -643,11 +660,10 @@ func (h *handshake) retransmitHandlerLocked() tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// transitionToStateEstablisedLocked transitions the endpoint of the handshake
|
||||
// transitionToStateEstablishedLocked transitions the endpoint of the handshake
|
||||
// to an established state given the last segment received from peer. It also
|
||||
// initializes sender/receiver.
|
||||
// +checklocks:h.ep.mu
|
||||
// +checklocksalias:h.ep.snd.ep.mu=h.ep.mu
|
||||
func (h *handshake) transitionToStateEstablishedLocked(s *segment) {
|
||||
// Stop the SYN retransmissions now that handshake is complete.
|
||||
if h.retransmitTimer != nil {
|
||||
@@ -657,16 +673,20 @@ func (h *handshake) transitionToStateEstablishedLocked(s *segment) {
|
||||
// Transfer handshake state to TCP connection. We disable
|
||||
// receive window scaling if the peer doesn't support it
|
||||
// (indicated by a negative send window scale).
|
||||
h.ep.snd = newSender(h.ep, h.iss, h.ackNum-1, h.sndWnd, h.mss, h.sndWndScale)
|
||||
initSender(h.ep, h.iss, h.ackNum-1, h.sndWnd, h.mss, h.sndWndScale)
|
||||
|
||||
now := h.ep.stack.Clock().NowMonotonic()
|
||||
// Use the final handshake ACK's ingress time (s.rcvdTime) rather than the
|
||||
// current clock to seed the initial RTT/RTO. If the ACK was delayed inside
|
||||
// the stack before processing, the processing-time clock would inflate the
|
||||
// initial RTO, which then persists for several RTTs.
|
||||
rcvd := s.rcvdTime
|
||||
|
||||
var rtt time.Duration
|
||||
if h.ep.SendTSOk && s.parsedOptions.TSEcr != 0 {
|
||||
rtt = h.ep.elapsed(now, s.parsedOptions.TSEcr)
|
||||
rtt = h.ep.elapsed(rcvd, s.parsedOptions.TSEcr)
|
||||
}
|
||||
if !h.sampleRTTWithTSOnly && rtt == 0 {
|
||||
rtt = now.Sub(h.startTime)
|
||||
rtt = rcvd.Sub(h.startTime)
|
||||
}
|
||||
|
||||
if rtt > 0 {
|
||||
@@ -825,7 +845,10 @@ func (e *Endpoint) sendSynTCP(r *stack.Route, tf tcpFields, opts header.TCPSynOp
|
||||
if r.NetProto() == header.IPv6ProtocolNumber && tf.expOptVal != 0 {
|
||||
hdrSize += header.IPv6ExperimentHdrLength
|
||||
}
|
||||
p := stack.NewPacketBuffer(stack.PacketBufferOptions{ReserveHeaderBytes: hdrSize})
|
||||
p := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: hdrSize,
|
||||
Mark: e.ops.GetMark(),
|
||||
})
|
||||
defer p.DecRef()
|
||||
if err := e.sendTCP(r, tf, p, stack.GSO{}); err != nil {
|
||||
e.stats.SendErrors.SynSendToNetworkFailed.Increment()
|
||||
@@ -901,7 +924,10 @@ func sendTCPBatch(r *stack.Route, tf tcpFields, pkt *stack.PacketBuffer, gso sta
|
||||
// Reserve extra bytes for the experiment option.
|
||||
hdrSize += header.IPv6ExperimentHdrLength
|
||||
}
|
||||
splitPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ReserveHeaderBytes: hdrSize})
|
||||
splitPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: hdrSize,
|
||||
Mark: pkt.Mark,
|
||||
})
|
||||
splitPkt.Data().ReadFromPacketData(pkt.Data(), packetSize)
|
||||
pkt = splitPkt
|
||||
}
|
||||
@@ -967,6 +993,8 @@ func sendTCP(r *stack.Route, tf tcpFields, pkt *stack.PacketBuffer, gso stack.GS
|
||||
}
|
||||
|
||||
// makeOptions makes an options slice.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
func (e *Endpoint) makeOptions(sackBlocks []header.SACKBlock) []byte {
|
||||
options := getOptions()
|
||||
offset := 0
|
||||
@@ -1008,9 +1036,10 @@ func (e *Endpoint) makeOptions(sackBlocks []header.SACKBlock) []byte {
|
||||
// sendEmptyRaw sends a TCP segment with no payload to the endpoint's peer.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) sendEmptyRaw(flags header.TCPFlags, seq, ack seqnum.Value, rcvWnd seqnum.Size) tcpip.Error {
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{})
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Mark: e.ops.GetMark(),
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
return e.sendRaw(pkt, flags, seq, ack, rcvWnd)
|
||||
}
|
||||
@@ -1019,7 +1048,6 @@ func (e *Endpoint) sendEmptyRaw(flags header.TCPFlags, seq, ack seqnum.Value, rc
|
||||
// ownership of pkt. pkt must not have any headers set.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) sendRaw(pkt *stack.PacketBuffer, flags header.TCPFlags, seq, ack seqnum.Value, rcvWnd seqnum.Size) tcpip.Error {
|
||||
var sackBlocks []header.SACKBlock
|
||||
if e.EndpointState() == StateEstablished && e.rcv.pendingRcvdSegments.Len() > 0 && (flags&header.TCPFlagAck != 0) {
|
||||
@@ -1034,21 +1062,22 @@ func (e *Endpoint) sendRaw(pkt *stack.PacketBuffer, flags header.TCPFlags, seq,
|
||||
}
|
||||
pkt.ReserveHeaderBytes(hdrSize)
|
||||
return e.sendTCP(e.route, tcpFields{
|
||||
id: e.TransportEndpointInfo.ID,
|
||||
ttl: calculateTTL(e.route, e.ipv4TTL, e.ipv6HopLimit),
|
||||
tos: e.sendTOS,
|
||||
flags: flags,
|
||||
seq: seq,
|
||||
ack: ack,
|
||||
rcvWnd: rcvWnd,
|
||||
opts: options,
|
||||
df: e.pmtud == tcpip.PMTUDiscoveryWant || e.pmtud == tcpip.PMTUDiscoveryDo,
|
||||
id: e.TransportEndpointInfo.ID,
|
||||
ttl: calculateTTL(e.route, e.ipv4TTL, e.ipv6HopLimit),
|
||||
tos: e.sendTOS,
|
||||
flags: flags,
|
||||
seq: seq,
|
||||
ack: ack,
|
||||
rcvWnd: rcvWnd,
|
||||
opts: options,
|
||||
// PROBE sets DF like DO; see network/endpoint.go for details.
|
||||
df: e.pmtud == tcpip.PMTUDiscoveryWant || e.pmtud == tcpip.PMTUDiscoveryDo || e.pmtud == tcpip.PMTUDiscoveryProbe,
|
||||
expOptVal: expOptVal,
|
||||
}, pkt, e.gso)
|
||||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
// +checklocksexclude:e.snd.rtt.rttMutex
|
||||
func (e *Endpoint) sendData(next *segment) {
|
||||
// Initialize the next segment to write if it's currently nil.
|
||||
if e.snd.writeNext == nil {
|
||||
@@ -1066,7 +1095,6 @@ func (e *Endpoint) sendData(next *segment) {
|
||||
// error code and sends a RST if and only if the error is not ErrConnectionReset
|
||||
// indicating that the connection is being reset due to receiving a RST.
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) resetConnectionLocked(err tcpip.Error) {
|
||||
// Only send a reset if the connection is being aborted for a reason
|
||||
// other than receiving a reset.
|
||||
@@ -1081,12 +1109,26 @@ func (e *Endpoint) resetConnectionLocked(err tcpip.Error) {
|
||||
//
|
||||
// See: https://www.snellman.net/blog/archive/2016-02-01-tcp-rst/ for more
|
||||
// information.
|
||||
sndWndEnd := e.snd.SndUna.Add(e.snd.SndWnd)
|
||||
resetSeqNum := sndWndEnd
|
||||
if !sndWndEnd.LessThan(e.snd.SndNxt) || e.snd.SndNxt.Size(sndWndEnd) < (1<<e.snd.SndWndScale) {
|
||||
resetSeqNum = e.snd.SndNxt
|
||||
//
|
||||
// e.snd and e.rcv may be nil if the endpoint is in a handshake
|
||||
// state (e.g. SynSent) where the sender and receiver have not
|
||||
// yet been initialized. Per Linux behavior, use a sequence
|
||||
// number of zero when no ACK has been received (snd is nil),
|
||||
// and a receive window of zero when rcv is nil since the
|
||||
// connection will be immediately terminated.
|
||||
var resetSeqNum seqnum.Value
|
||||
var ackNum seqnum.Value
|
||||
if e.snd != nil {
|
||||
sndWndEnd := e.snd.SndUna.Add(e.snd.SndWnd)
|
||||
resetSeqNum = sndWndEnd
|
||||
if !sndWndEnd.LessThan(e.snd.SndNxt) || e.snd.SndNxt.Size(sndWndEnd) < (1<<e.snd.SndWndScale) {
|
||||
resetSeqNum = e.snd.SndNxt
|
||||
}
|
||||
}
|
||||
e.sendEmptyRaw(header.TCPFlagAck|header.TCPFlagRst, resetSeqNum, e.rcv.RcvNxt, 0)
|
||||
if e.rcv != nil {
|
||||
ackNum = e.rcv.RcvNxt
|
||||
}
|
||||
e.sendEmptyRaw(header.TCPFlagAck|header.TCPFlagRst, resetSeqNum, ackNum, 0)
|
||||
}
|
||||
// Don't purge read queues here. If there's buffered data, it's still allowed
|
||||
// to be read.
|
||||
@@ -1151,6 +1193,8 @@ func (e *Endpoint) tryDeliverSegmentFromClosedEndpoint(s *segment) {
|
||||
// Drain segment queue from the endpoint and try to re-match the segment to a
|
||||
// different endpoint. This is used when the current endpoint is transitioned to
|
||||
// StateClose and has been unregistered from the transport demuxer.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
func (e *Endpoint) drainClosingSegmentQueue() {
|
||||
for {
|
||||
s := e.segmentQueue.dequeue()
|
||||
@@ -1163,52 +1207,68 @@ func (e *Endpoint) drainClosingSegmentQueue() {
|
||||
}
|
||||
}
|
||||
|
||||
// handleReset processes an inbound segment carrying the RST flag.
|
||||
//
|
||||
// Acceptance follows RFC 5961 section 3.2:
|
||||
// - If the segment sequence number is out of window, the segment is
|
||||
// silently dropped.
|
||||
// - If the segment sequence number is in window but not exactly equal
|
||||
// to RCV.NXT, the implementation sends a challenge ACK and drops
|
||||
// the segment.
|
||||
// - Only an exact match against RCV.NXT causes the connection to be
|
||||
// reset.
|
||||
//
|
||||
// This is stricter than RFC 793 page 37, which accepted any in-window RST.
|
||||
// The strict-match rule defends against off-path blind RST injection.
|
||||
// Linux has implemented it since version 3.6 (2012); see
|
||||
// net/ipv4/tcp_input.c tcp_validate_incoming().
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
func (e *Endpoint) handleReset(s *segment) (ok bool, err tcpip.Error) {
|
||||
if e.rcv.acceptable(s.sequenceNumber, 0) {
|
||||
// RFC 793, page 37 states that "in all states
|
||||
// except SYN-SENT, all reset (RST) segments are
|
||||
// validated by checking their SEQ-fields." So
|
||||
// we only process it if it's acceptable.
|
||||
switch e.EndpointState() {
|
||||
// In case of a RST in CLOSE-WAIT linux moves
|
||||
// the socket to closed state with an error set
|
||||
// to indicate EPIPE.
|
||||
//
|
||||
// Technically this seems to be at odds w/ RFC.
|
||||
// As per https://tools.ietf.org/html/rfc793#section-2.7
|
||||
// page 69 the behavior for a segment arriving
|
||||
// w/ RST bit set in CLOSE-WAIT is inlined below.
|
||||
//
|
||||
// ESTABLISHED
|
||||
// FIN-WAIT-1
|
||||
// FIN-WAIT-2
|
||||
// CLOSE-WAIT
|
||||
if !e.rcv.acceptable(s.sequenceNumber, 0) {
|
||||
// Out of window. Silent drop.
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// If the RST bit is set then, any outstanding RECEIVEs and
|
||||
// SEND should receive "reset" responses. All segment queues
|
||||
// should be flushed. Users should also receive an unsolicited
|
||||
// general "connection reset" signal. Enter the CLOSED state,
|
||||
// delete the TCB, and return.
|
||||
case StateCloseWait:
|
||||
e.transitionToStateCloseLocked()
|
||||
e.hardError = &tcpip.ErrAborted{}
|
||||
return false, nil
|
||||
default:
|
||||
// RFC 793, page 37 states that "in all states
|
||||
// except SYN-SENT, all reset (RST) segments are
|
||||
// validated by checking their SEQ-fields." So
|
||||
// we only process it if it's acceptable.
|
||||
return false, &tcpip.ErrConnectionReset{}
|
||||
}
|
||||
if s.sequenceNumber != e.rcv.RcvNxt {
|
||||
// In window but not an exact match. Send a challenge ACK and drop the
|
||||
// segment per RFC 5961 section 3.2. The challenge ACK helper rate-limits
|
||||
// challenge transmission per RFC 5961 section 7.
|
||||
e.snd.maybeSendOutOfWindowAck(s)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
switch e.EndpointState() {
|
||||
// In case of a RST in CLOSE-WAIT linux moves the socket to closed state
|
||||
// with an error set to indicate EPIPE.
|
||||
//
|
||||
// As per https://tools.ietf.org/html/rfc793#section-2.7 page 69 the
|
||||
// behavior for a segment arriving w/ RST bit set in CLOSE-WAIT is
|
||||
// inlined below.
|
||||
//
|
||||
// ESTABLISHED
|
||||
// FIN-WAIT-1
|
||||
// FIN-WAIT-2
|
||||
// CLOSE-WAIT
|
||||
//
|
||||
// If the RST bit is set then, any outstanding RECEIVEs and SEND should
|
||||
// receive "reset" responses. All segment queues should be flushed.
|
||||
// Users should also receive an unsolicited general "connection reset"
|
||||
// signal. Enter the CLOSED state, delete the TCB, and return.
|
||||
case StateCloseWait:
|
||||
e.transitionToStateCloseLocked()
|
||||
e.hardError = &tcpip.ErrAborted{}
|
||||
return false, nil
|
||||
default:
|
||||
return false, &tcpip.ErrConnectionReset{}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// handleSegments processes all inbound segments.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.snd.rtt.rttMutex
|
||||
func (e *Endpoint) handleSegmentsLocked() tcpip.Error {
|
||||
sndUna := e.snd.SndUna
|
||||
for i := 0; i < maxSegmentsPerWake; i++ {
|
||||
@@ -1251,6 +1311,7 @@ func (e *Endpoint) handleSegmentsLocked() tcpip.Error {
|
||||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
// +checklocksexclude:e.snd.rtt.rttMutex
|
||||
func (e *Endpoint) probeSegmentLocked() {
|
||||
if fn := e.probe; fn != nil {
|
||||
var state TCPEndpointState
|
||||
@@ -1263,8 +1324,7 @@ func (e *Endpoint) probeSegmentLocked() {
|
||||
// if the connection should be terminated.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.rcv.ep.mu=e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
// +checklocksexclude:e.snd.rtt.rttMutex
|
||||
func (e *Endpoint) handleSegmentLocked(s *segment) (cont bool, err tcpip.Error) {
|
||||
// Invoke the tcp probe if installed. The tcp probe function will update
|
||||
// the TCPEndpointState after the segment is processed.
|
||||
@@ -1317,8 +1377,12 @@ func (e *Endpoint) handleSegmentLocked(s *segment) (cont bool, err tcpip.Error)
|
||||
// Now check if the received segment has caused us to transition
|
||||
// to a CLOSED state, if yes then terminate processing and do
|
||||
// not invoke the sender.
|
||||
// It is also possible that the sender has sent a RST before
|
||||
// which got lost and didn't reach the other side. At that time,
|
||||
// we can still receive ACKs after the sender has purged the
|
||||
// write list. Do not process such ACKs and return immediately.
|
||||
state := e.EndpointState()
|
||||
if state == StateClose {
|
||||
if state == StateClose || state == StateError {
|
||||
// When we get into StateClose while processing from the queue,
|
||||
// return immediately and let the TCP processors handle it.
|
||||
return false, nil
|
||||
@@ -1334,7 +1398,6 @@ func (e *Endpoint) handleSegmentLocked(s *segment) (cont bool, err tcpip.Error)
|
||||
// keepalive packets periodically when the connection is idle. If we don't hear
|
||||
// from the other side after a number of tries, we terminate the connection.
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) keepaliveTimerExpired() tcpip.Error {
|
||||
userTimeout := e.userTimeout
|
||||
|
||||
@@ -1376,7 +1439,6 @@ func (e *Endpoint) keepaliveTimerExpired() tcpip.Error {
|
||||
// whether it is enabled for this endpoint.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) resetKeepaliveTimer(receivedData bool) {
|
||||
e.keepalive.Lock()
|
||||
defer e.keepalive.Unlock()
|
||||
@@ -1403,6 +1465,8 @@ func (e *Endpoint) resetKeepaliveTimer(receivedData bool) {
|
||||
}
|
||||
|
||||
// disableKeepaliveTimer stops the keepalive timer.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
func (e *Endpoint) disableKeepaliveTimer() {
|
||||
e.keepalive.Lock()
|
||||
e.keepalive.timer.disable()
|
||||
@@ -1439,7 +1503,7 @@ func (e *Endpoint) handshakeFailed(err tcpip.Error) {
|
||||
// handleTimeWaitSegments processes segments received during TIME_WAIT
|
||||
// state.
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.rcv.ep.mu=e.mu
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
func (e *Endpoint) handleTimeWaitSegments() (extendTimeWait bool, reuseTW func()) {
|
||||
for i := 0; i < maxSegmentsPerWake; i++ {
|
||||
s := e.segmentQueue.dequeue()
|
||||
@@ -1513,6 +1577,11 @@ func (e *Endpoint) timeWaitTimerExpired() {
|
||||
}
|
||||
|
||||
// notifyProcessor queues this endpoint for processing to its TCP processor.
|
||||
//
|
||||
// The caller must not hold the selected processor's queue mutex. checklocks
|
||||
// cannot name that mutex through the dispatcher's indexed processor selection.
|
||||
//
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) notifyProcessor() {
|
||||
// We use TryLock here to avoid deadlocks in cases where a listening endpoint that is being
|
||||
// closed tries to abort half completed connections which in turn try to queue any segments
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -26,5 +25,5 @@ import (
|
||||
func optionsToArray(options []byte) *[maxOptionSize]byte {
|
||||
// Reslice to full capacity.
|
||||
options = options[0:maxOptionSize]
|
||||
return (*[maxOptionSize]byte)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&options)).Data))
|
||||
return (*[maxOptionSize]byte)(unsafe.Pointer(&options[0]))
|
||||
}
|
||||
+25
-7
@@ -79,9 +79,14 @@ func newCubicCC(s *sender) *cubicState {
|
||||
C: 0.4,
|
||||
// By this point, the sender has initialized it's initial sequence
|
||||
// number.
|
||||
EndSeq: s.SndNxt,
|
||||
LastRTT: effectivelyInfinity,
|
||||
CurrRTT: effectivelyInfinity,
|
||||
EndSeq: s.SndNxt,
|
||||
LastRTT: effectivelyInfinity,
|
||||
CurrRTT: effectivelyInfinity,
|
||||
// LastAck/RoundStart are seeded here from processing time, but the
|
||||
// HyStart ACK-train comparator that reads them is gated on
|
||||
// LastRTT < effectivelyInfinity, which only becomes true after the
|
||||
// first beginHyStartRound re-seeds both from an ACK's ingress time.
|
||||
// So this initial processing-time seed is never compared.
|
||||
LastAck: now,
|
||||
RoundStart: now,
|
||||
},
|
||||
@@ -122,12 +127,19 @@ func (c *cubicState) enterCongestionAvoidance() {
|
||||
// here.
|
||||
//
|
||||
// +checklocks:c.s.ep.mu
|
||||
func (c *cubicState) updateHyStart(rtt time.Duration) {
|
||||
func (c *cubicState) updateHyStart(rtt time.Duration, ackTime tcpip.MonotonicTime) {
|
||||
if rtt < 0 {
|
||||
// negative indicates unknown
|
||||
return
|
||||
}
|
||||
now := c.s.ep.stack.Clock().NowMonotonic()
|
||||
// Use the ACK's ingress time (ackTime) rather than the current clock for
|
||||
// HyStart's ACK-train timing. The ACK-train detector compares inter-ACK
|
||||
// spacing against ackDelta (2ms) and the round duration against LastRTT/2;
|
||||
// if ACKs are delayed and processed in a burst inside the stack (e.g.
|
||||
// queued while the application held the endpoint lock during a Write), the
|
||||
// processing clock would make distinct ACKs appear to arrive together,
|
||||
// distorting both comparisons and potentially exiting slow start early.
|
||||
now := ackTime
|
||||
if c.EndSeq.LessThan(c.s.SndUna) {
|
||||
c.beginHyStartRound(now)
|
||||
}
|
||||
@@ -197,9 +209,10 @@ func (c *cubicState) updateSlowStart(packetsAcked int) int {
|
||||
// Refer: https://tools.ietf.org/html/rfc8312#section-4
|
||||
//
|
||||
// +checklocks:c.s.ep.mu
|
||||
func (c *cubicState) Update(packetsAcked int, rtt time.Duration) {
|
||||
// +checklocksexclude:c.s.rtt.rttMutex
|
||||
func (c *cubicState) Update(packetsAcked int, rtt time.Duration, ackTime tcpip.MonotonicTime) {
|
||||
if c.s.Ssthresh == InitialSsthresh && c.s.SndCwnd < c.s.Ssthresh {
|
||||
c.updateHyStart(rtt)
|
||||
c.updateHyStart(rtt, ackTime)
|
||||
}
|
||||
if c.s.SndCwnd < c.s.Ssthresh {
|
||||
packetsAcked = c.updateSlowStart(packetsAcked)
|
||||
@@ -248,6 +261,11 @@ func (c *cubicState) getCwnd(packetsAcked, sndCwnd int, srtt time.Duration) int
|
||||
// As per 4.3 for each received ACK cwnd must be incremented
|
||||
// by (w_cubic(t+RTT) - cwnd/cwnd.
|
||||
cwnd := float64(sndCwnd)
|
||||
// RFC 9438 section 4.2 bounds the target at cwnd so an ACK does not
|
||||
// reduce the congestion window.
|
||||
if wtRtt < cwnd {
|
||||
wtRtt = cwnd
|
||||
}
|
||||
for i := 0; i < packetsAcked; i++ {
|
||||
// Concave/Convex regions of cubic have the same formulas.
|
||||
// See: https://tools.ietf.org/html/rfc8312#section-4.3
|
||||
|
||||
@@ -18,7 +18,10 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sleep"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
@@ -32,11 +35,16 @@ import (
|
||||
//
|
||||
// +stateify savable
|
||||
type epQueue struct {
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
list endpointList
|
||||
mu epQueueMutex `state:"nosave"`
|
||||
|
||||
// +checklocks:mu
|
||||
list endpointList `state:"nosave"`
|
||||
}
|
||||
|
||||
// enqueue adds e to the queue if the endpoint is not already on the queue.
|
||||
//
|
||||
// +checklocksexclude:q.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (q *epQueue) enqueue(e *Endpoint) {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
@@ -52,6 +60,11 @@ func (q *epQueue) enqueue(e *Endpoint) {
|
||||
|
||||
// dequeue removes and returns the first element from the queue if available,
|
||||
// returns nil otherwise.
|
||||
//
|
||||
// The caller must not hold a queued endpoint's pendingProcessingMu. checklocks
|
||||
// cannot name that mutex through the endpoint obtained from the queue.
|
||||
//
|
||||
// +checklocksexclude:q.mu
|
||||
func (q *epQueue) dequeue() *Endpoint {
|
||||
q.mu.Lock()
|
||||
if e := q.list.Front(); e != nil {
|
||||
@@ -67,6 +80,8 @@ func (q *epQueue) dequeue() *Endpoint {
|
||||
}
|
||||
|
||||
// empty returns true if the queue is empty, false otherwise.
|
||||
//
|
||||
// +checklocksexclude:q.mu
|
||||
func (q *epQueue) empty() bool {
|
||||
q.mu.Lock()
|
||||
v := q.list.Empty()
|
||||
@@ -91,6 +106,8 @@ func (p *processor) close() {
|
||||
p.closeWaker.Assert()
|
||||
}
|
||||
|
||||
// +checklocksexclude:p.epQ.mu
|
||||
// +checklocksexclude:ep.pendingProcessingMu
|
||||
func (p *processor) queueEndpoint(ep *Endpoint) {
|
||||
// Queue an endpoint for processing by the processor goroutine.
|
||||
p.epQ.enqueue(ep)
|
||||
@@ -133,6 +150,9 @@ func deliverAccepted(ep *Endpoint) bool {
|
||||
|
||||
// handleConnecting is responsible for TCP processing for an endpoint in one of
|
||||
// the connecting states.
|
||||
//
|
||||
// +checklocksexclude:ep.segmentQueue.mu
|
||||
// +checklocksexclude:ep.pendingProcessingMu
|
||||
func handleConnecting(ep *Endpoint) {
|
||||
if !ep.TryLock() {
|
||||
return
|
||||
@@ -149,7 +169,7 @@ func handleConnecting(ep *Endpoint) {
|
||||
ep.mu.Unlock()
|
||||
return
|
||||
}
|
||||
if err := ep.h.processSegments(); err != nil { // +checklocksforce:ep.h.ep.mu
|
||||
if err := ep.h.processSegments(); err != nil {
|
||||
// handshake failed. clean up the tcp endpoint and handshake
|
||||
// state.
|
||||
if lEP := ep.h.listenEP; lEP != nil {
|
||||
@@ -176,6 +196,8 @@ func handleConnecting(ep *Endpoint) {
|
||||
|
||||
// handleConnected is responsible for TCP processing for an endpoint in one of
|
||||
// the connected states(StateEstablished, StateFinWait1 etc.)
|
||||
//
|
||||
// +checklocksexclude:ep.segmentQueue.mu
|
||||
func handleConnected(ep *Endpoint) {
|
||||
if !ep.TryLock() {
|
||||
return
|
||||
@@ -225,6 +247,8 @@ func startTimeWait(ep *Endpoint) {
|
||||
|
||||
// handleTimeWait is responsible for TCP processing for an endpoint in TIME-WAIT
|
||||
// state.
|
||||
//
|
||||
// +checklocksexclude:ep.segmentQueue.mu
|
||||
func handleTimeWait(ep *Endpoint) {
|
||||
if !ep.TryLock() {
|
||||
return
|
||||
@@ -253,8 +277,12 @@ func handleTimeWait(ep *Endpoint) {
|
||||
ep.mu.Unlock()
|
||||
}
|
||||
|
||||
var warnRateLimiter = rate.NewLimiter(rate.Every(time.Second), 1)
|
||||
|
||||
// handleListen is responsible for TCP processing for an endpoint in LISTEN
|
||||
// state.
|
||||
//
|
||||
// +checklocksexclude:ep.segmentQueue.mu
|
||||
func handleListen(ep *Endpoint) {
|
||||
if !ep.TryLock() {
|
||||
return
|
||||
@@ -274,9 +302,11 @@ func handleListen(ep *Endpoint) {
|
||||
break
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/4690): Better handle errors instead of
|
||||
// silently dropping.
|
||||
_ = ep.handleListenSegment(ep.listenCtx, s)
|
||||
if err := ep.handleListenSegment(ep.listenCtx, s); err != nil {
|
||||
if warnRateLimiter.Allow() {
|
||||
log.Warningf("tcp.Endpoint.handleListenSegment() failed for packet [nic=%d, source=%s, dest=%s, protocol=%d]: %v", s.pkt.NICID, s.pkt.Network().SourceAddress(), s.pkt.Network().DestinationAddress(), s.pkt.NetworkProtocolNumber, err)
|
||||
}
|
||||
}
|
||||
s.DecRef()
|
||||
}
|
||||
}
|
||||
@@ -365,7 +395,7 @@ type dispatcher struct {
|
||||
processors []processor
|
||||
wg sync.WaitGroup `state:"nosave"`
|
||||
hasher jenkinsHasher
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
mu dispatcherMutex `state:"nosave"`
|
||||
// +checklocks:mu
|
||||
paused bool
|
||||
// +checklocks:mu
|
||||
@@ -431,6 +461,11 @@ func (d *dispatcher) wait() {
|
||||
|
||||
// queuePacket queues an incoming packet to the matching tcp endpoint and
|
||||
// also queues the endpoint to a processor queue for processing.
|
||||
//
|
||||
// The caller must not hold the target endpoint's segmentQueue.mu or
|
||||
// pendingProcessingMu, or the selected processor's queue mutex. checklocks
|
||||
// cannot name these locks through stackEP's interface type and the indexed
|
||||
// processor selection.
|
||||
func (d *dispatcher) queuePacket(stackEP stack.TransportEndpoint, id stack.TransportEndpointID, clock tcpip.Clock, pkt *stack.PacketBuffer) {
|
||||
d.mu.Lock()
|
||||
closed := d.closed
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type dispatcherMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var dispatcherprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var dispatcherlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type dispatcherlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *dispatcherMutex) Lock() {
|
||||
locking.AddGLock(dispatcherprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *dispatcherMutex) NestedLock(i dispatcherlockNameIndex) {
|
||||
locking.AddGLock(dispatcherprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *dispatcherMutex) Unlock() {
|
||||
locking.DelGLock(dispatcherprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *dispatcherMutex) NestedUnlock(i dispatcherlockNameIndex) {
|
||||
locking.DelGLock(dispatcherprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func dispatcherinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
dispatcherinitLockNames()
|
||||
dispatcherprefixIndex = locking.NewMutexClass(reflect.TypeFor[dispatcherMutex](), dispatcherlockNames)
|
||||
}
|
||||
+216
-65
@@ -285,18 +285,24 @@ func (*Stats) IsEndpointStats() {}
|
||||
//
|
||||
// +stateify savable
|
||||
type sndQueueInfo struct {
|
||||
sndQueueMu sync.Mutex `state:"nosave"`
|
||||
sndQueueMu sndQueueMutex `state:"nosave"`
|
||||
|
||||
// +checklocks:sndQueueMu
|
||||
TCPSndBufState
|
||||
}
|
||||
|
||||
// CloneState clones sq into other. It is not thread safe
|
||||
// CloneState clones sq into other. The caller must exclusively own other.
|
||||
// other is a standalone snapshot without an owning mutex; the source-lock
|
||||
// contract does not establish the caller's exclusive ownership of other.
|
||||
//
|
||||
// +checklocks:sq.sndQueueMu
|
||||
func (sq *sndQueueInfo) CloneState(other *TCPSndBufState) {
|
||||
other.SndBufSize = sq.SndBufSize
|
||||
other.SndBufUsed = sq.SndBufUsed
|
||||
other.SndClosed = sq.SndClosed
|
||||
other.PacketTooBigCount = sq.PacketTooBigCount
|
||||
other.SndMTU = sq.SndMTU
|
||||
other.AutoTuneSndBufDisabled = atomicbitops.FromUint32(sq.AutoTuneSndBufDisabled.RacyLoad())
|
||||
other.AutoTuneSndBufDisabled.Store(sq.AutoTuneSndBufDisabled.Load())
|
||||
}
|
||||
|
||||
// Endpoint represents a TCP endpoint. This struct serves as the interface
|
||||
@@ -333,7 +339,7 @@ func (sq *sndQueueInfo) CloneState(other *TCPSndBufState) {
|
||||
// lock from say a syscall goroutine we can implement a bit of spinning if we
|
||||
// know that the lock is not held by another syscall goroutine. Background
|
||||
// processors should never hold the lock for long and we can avoid an expensive
|
||||
// sleep/wakeup by spinning for a shortwhile.
|
||||
// sleep/wakeup by spinning for a short while.
|
||||
//
|
||||
// For more details please see the detailed documentation on
|
||||
// e.LockUser/e.UnlockUser methods.
|
||||
@@ -341,6 +347,9 @@ func (sq *sndQueueInfo) CloneState(other *TCPSndBufState) {
|
||||
// TODO(b/339664055): Checklocks should be used more extensively here. Coverage
|
||||
// is currently sparse.
|
||||
//
|
||||
// +checklocksalias:snd.ep.mu=mu
|
||||
// +checklocksalias:rcv.ep.mu=mu
|
||||
// +checklocksalias:h.ep.mu=mu
|
||||
// +stateify savable
|
||||
type Endpoint struct {
|
||||
TCPEndpointStateInner
|
||||
@@ -354,7 +363,7 @@ type Endpoint struct {
|
||||
endpointEntry `state:"nosave"`
|
||||
|
||||
// pendingProcessingMu protects pendingProcessing.
|
||||
pendingProcessingMu sync.Mutex `state:"nosave"`
|
||||
pendingProcessingMu pendingProcessingMutex `state:"nosave"`
|
||||
|
||||
// pendingProcessing is true if this endpoint is queued for processing
|
||||
// to a TCP processor.
|
||||
@@ -374,10 +383,10 @@ type Endpoint struct {
|
||||
|
||||
// lastError represents the last error that the endpoint reported;
|
||||
// access to it is protected by the following mutex.
|
||||
lastErrorMu sync.Mutex `state:"nosave"`
|
||||
lastErrorMu lastErrorMutex `state:"nosave"`
|
||||
lastError tcpip.Error
|
||||
|
||||
rcvQueueMu sync.Mutex `state:"nosave"`
|
||||
rcvQueueMu rcvQueueMutex `state:"nosave"`
|
||||
|
||||
// +checklocks:rcvQueueMu
|
||||
TCPRcvBufState
|
||||
@@ -406,8 +415,9 @@ type Endpoint struct {
|
||||
// methods.
|
||||
state atomicbitops.Uint32 `state:".(EndpointState)"`
|
||||
|
||||
// connectionDirectionState holds current state of send and receive,
|
||||
// accessed atomically
|
||||
// connectionDirectionState records whether sending and receiving are closed.
|
||||
//
|
||||
// +checkatomic
|
||||
connectionDirectionState atomicbitops.Uint32
|
||||
|
||||
// origEndpointState is only used during a restore phase to save the
|
||||
@@ -591,6 +601,8 @@ type Endpoint struct {
|
||||
|
||||
// listenCtx is used by listening endpoints to store state used while listening for
|
||||
// connections. Nil otherwise.
|
||||
// The context remains fixed while child handshakes run. Restore reinstalls
|
||||
// it before releasing the listenLoading barrier and resuming handshakes.
|
||||
listenCtx *listenContext `state:"nosave"`
|
||||
|
||||
// limRdr is reused to avoid allocations.
|
||||
@@ -602,6 +614,21 @@ type Endpoint struct {
|
||||
//
|
||||
// +checklocks:mu
|
||||
pmtud tcpip.PMTUDStrategy
|
||||
|
||||
// alsoBindToV4 indicates if `any` address was used to bind a port.
|
||||
//
|
||||
// +checklocks:mu
|
||||
alsoBindToV4 bool
|
||||
|
||||
// terminateAtRestore indicates whether the endpoint must be terminated
|
||||
// upon restore. This applies specifically when the snapshots are taken
|
||||
// with the "save-resume" flag, ensuring that if such a snapshot is
|
||||
// restored later, this endpoint is cleaned up. This flag is only set
|
||||
// to true in beforeSave for external endpoints which do not have
|
||||
// save-restore capability.
|
||||
//
|
||||
// +checklocks:mu
|
||||
terminateAtRestore bool
|
||||
}
|
||||
|
||||
// calculateAdvertisedMSS calculates the MSS to advertise.
|
||||
@@ -636,7 +663,6 @@ func (e *Endpoint) isOwnedByUser() bool {
|
||||
// should not be holding the lock for long and spinning reduces latency as we
|
||||
// avoid an expensive sleep/wakeup of the syscall goroutine).
|
||||
// +checklocksacquire:e.mu
|
||||
// +checklocksacquire:e.snd.ep.mu
|
||||
func (e *Endpoint) LockUser() {
|
||||
const iterations = 5
|
||||
for i := 0; i < iterations; i++ {
|
||||
@@ -649,14 +675,14 @@ func (e *Endpoint) LockUser() {
|
||||
if e.ownedByUser.Load() == 1 {
|
||||
e.mu.Lock()
|
||||
e.ownedByUser.Store(1)
|
||||
return // +checklocksforce: this locks e.snd.ep.mu
|
||||
return
|
||||
}
|
||||
// Spin but don't yield the processor since the lower half
|
||||
// should yield the lock soon.
|
||||
continue
|
||||
}
|
||||
e.ownedByUser.Store(1)
|
||||
return // +checklocksforce: this locks e.snd.ep.mu
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < iterations; i++ {
|
||||
@@ -669,7 +695,7 @@ func (e *Endpoint) LockUser() {
|
||||
if e.ownedByUser.Load() == 1 {
|
||||
e.mu.Lock()
|
||||
e.ownedByUser.Store(1)
|
||||
return // +checklocksforce: this locks e.snd.ep.mu
|
||||
return
|
||||
}
|
||||
// Spin but yield the processor since the lower half
|
||||
// should yield the lock soon.
|
||||
@@ -677,7 +703,7 @@ func (e *Endpoint) LockUser() {
|
||||
continue
|
||||
}
|
||||
e.ownedByUser.Store(1)
|
||||
return // +checklocksforce: this locks e.snd.ep.mu
|
||||
return
|
||||
}
|
||||
|
||||
// Finally just give up and wait for the Lock.
|
||||
@@ -692,6 +718,8 @@ func (e *Endpoint) LockUser() {
|
||||
//
|
||||
// Precondition: e.LockUser() must have been called before calling e.UnlockUser()
|
||||
// +checklocksrelease:e.mu
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) UnlockUser() {
|
||||
// Lock segment queue before checking so that we avoid a race where
|
||||
// segments can be queued between the time we check if queue is empty
|
||||
@@ -720,7 +748,6 @@ func (e *Endpoint) UnlockUser() {
|
||||
// processor goroutine starts running before we release the lock here
|
||||
// then it will fail to process as TryLock() will fail.
|
||||
processor.queueEndpoint(e)
|
||||
return
|
||||
}
|
||||
|
||||
// StopWork halts packet processing. Only to be used in tests.
|
||||
@@ -744,7 +771,7 @@ func (e *Endpoint) ResumeWork() {
|
||||
// variable locks.
|
||||
// +checklocks:locked.mu
|
||||
// +checklocksacquire:e.mu
|
||||
func (e *Endpoint) AssertLockHeld(locked *Endpoint) {
|
||||
func (e *Endpoint) AssertLockHeld(locked *Endpoint) { // +checklocksforce: e.mu is held because e == locked.
|
||||
if e != locked {
|
||||
panic("AssertLockHeld failed: locked endpoint != asserting endpoint")
|
||||
}
|
||||
@@ -755,10 +782,7 @@ func (e *Endpoint) AssertLockHeld(locked *Endpoint) {
|
||||
// TODO(b/226403629): Remove this once checklocks understands TryLock.
|
||||
// +checklocksacquire:e.mu
|
||||
func (e *Endpoint) TryLock() bool {
|
||||
if e.mu.TryLock() {
|
||||
return true // +checklocksforce
|
||||
}
|
||||
return false // +checklocksignore
|
||||
return e.mu.TryLock() // +checklocksforce: TryLock.
|
||||
}
|
||||
|
||||
// setEndpointState updates the state of the endpoint to state atomically. This
|
||||
@@ -829,11 +853,11 @@ func calculateTTL(route *stack.Route, ipv4TTL uint8, ipv6HopLimit int16) uint8 {
|
||||
//
|
||||
// +stateify savable
|
||||
type keepalive struct {
|
||||
sync.Mutex `state:"nosave"`
|
||||
idle time.Duration
|
||||
interval time.Duration
|
||||
count int
|
||||
unacked int
|
||||
keepaliveMutex `state:"nosave"`
|
||||
idle time.Duration
|
||||
interval time.Duration
|
||||
count int
|
||||
unacked int
|
||||
// should never be a zero timer if the endpoint is not closed.
|
||||
timer timer `state:"nosave"`
|
||||
waker sleep.Waker `state:"nosave"`
|
||||
@@ -1010,7 +1034,6 @@ func (e *Endpoint) purgeReadQueue() {
|
||||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) purgeWriteQueue() {
|
||||
if e.snd != nil {
|
||||
e.sndQueueInfo.sndQueueMu.Lock()
|
||||
@@ -1027,6 +1050,9 @@ func (e *Endpoint) purgeWriteQueue() {
|
||||
}
|
||||
|
||||
// Abort implements stack.TransportEndpoint.Abort.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) Abort() {
|
||||
defer e.drainClosingSegmentQueue()
|
||||
e.LockUser()
|
||||
@@ -1045,6 +1071,9 @@ func (e *Endpoint) Abort() {
|
||||
// Close puts the endpoint in a closed state and frees all resources associated
|
||||
// with it. It must be called only once and with no other concurrent calls to
|
||||
// the endpoint.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) Close() {
|
||||
e.LockUser()
|
||||
if e.closed {
|
||||
@@ -1193,6 +1222,14 @@ func (e *Endpoint) cleanupLocked() {
|
||||
e.timeWaitTimer.Stop()
|
||||
}
|
||||
|
||||
// Remove current EP from its lEP acceptQueue.pendingEndpoint if exists.
|
||||
if e.h != nil && e.h.listenEP != nil {
|
||||
lEP := e.h.listenEP
|
||||
lEP.acceptMu.Lock()
|
||||
delete(lEP.acceptQueue.pendingEndpoints, e)
|
||||
lEP.acceptMu.Unlock()
|
||||
}
|
||||
|
||||
// Close all endpoints that might have been accepted by TCP but not by
|
||||
// the client.
|
||||
e.closePendingAcceptableConnectionsLocked()
|
||||
@@ -1272,6 +1309,9 @@ func (e *Endpoint) initialReceiveWindow() int {
|
||||
|
||||
// ModerateRecvBuf adjusts the receive buffer and the advertised window
|
||||
// based on the number of bytes copied to userspace.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) ModerateRecvBuf(copied int) {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
@@ -1345,7 +1385,7 @@ func (e *Endpoint) ModerateRecvBuf(copied int) {
|
||||
// Send the update after unlocking rcvQueueMu as sending a segment acquires
|
||||
// the lock to calculate the window to be sent.
|
||||
if e.EndpointState().connected() && sendNonZeroWindowUpdate {
|
||||
e.rcv.nonZeroWindow() // +checklocksforce:e.rcv.ep.mu
|
||||
e.rcv.nonZeroWindow()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1371,6 +1411,9 @@ func (e *Endpoint) lastErrorLocked() tcpip.Error {
|
||||
}
|
||||
|
||||
// LastError implements tcpip.Endpoint.LastError.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) LastError() tcpip.Error {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
@@ -1388,6 +1431,9 @@ func (e *Endpoint) LastErrorLocked() tcpip.Error {
|
||||
}
|
||||
|
||||
// UpdateLastError implements tcpip.SocketOptionsHandler.UpdateLastError.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) UpdateLastError(err tcpip.Error) {
|
||||
e.LockUser()
|
||||
e.lastErrorMu.Lock()
|
||||
@@ -1397,6 +1443,9 @@ func (e *Endpoint) UpdateLastError(err tcpip.Error) {
|
||||
}
|
||||
|
||||
// Read implements tcpip.Endpoint.Read.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, tcpip.Error) {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
@@ -1452,7 +1501,7 @@ func (e *Endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
|
||||
e.rcvQueueMu.Unlock()
|
||||
|
||||
if e.EndpointState().connected() && sendNonZeroWindowUpdate {
|
||||
e.rcv.nonZeroWindow() // +checklocksforce:e.rcv.ep.mu
|
||||
e.rcv.nonZeroWindow()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1551,6 +1600,8 @@ func (e *Endpoint) isEndpointWritableLocked() (int, tcpip.Error) {
|
||||
// readFromPayloader reads a slice from the Payloader.
|
||||
// +checklocks:e.mu
|
||||
// +checklocks:e.sndQueueInfo.sndQueueMu
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) readFromPayloader(p tcpip.Payloader, opts tcpip.WriteOptions, avail int) (buffer.Buffer, tcpip.Error) {
|
||||
// We can release locks while copying data.
|
||||
//
|
||||
@@ -1588,7 +1639,8 @@ func (e *Endpoint) readFromPayloader(p tcpip.Payloader, opts tcpip.WriteOptions,
|
||||
|
||||
// queueSegment reads data from the payloader and returns a segment to be sent.
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) queueSegment(p tcpip.Payloader, opts tcpip.WriteOptions) (*segment, int, tcpip.Error) {
|
||||
e.sndQueueInfo.sndQueueMu.Lock()
|
||||
defer e.sndQueueInfo.sndQueueMu.Unlock()
|
||||
@@ -1629,7 +1681,7 @@ func (e *Endpoint) queueSegment(p tcpip.Payloader, opts tcpip.WriteOptions) (*se
|
||||
|
||||
// Add data to the send queue.
|
||||
size := int(buf.Size())
|
||||
s := newOutgoingSegment(e.TransportEndpointInfo.ID, e.stack.Clock(), buf)
|
||||
s := newOutgoingSegment(e.TransportEndpointInfo.ID, e.stack.Clock(), buf, e.ops.GetMark())
|
||||
e.sndQueueInfo.SndBufUsed += size
|
||||
e.snd.writeList.PushBack(s)
|
||||
|
||||
@@ -1637,6 +1689,9 @@ func (e *Endpoint) queueSegment(p tcpip.Payloader, opts tcpip.WriteOptions) (*se
|
||||
}
|
||||
|
||||
// Write writes data to the endpoint's peer.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
|
||||
// Linux completely ignores any address passed to sendto(2) for TCP sockets
|
||||
// (without the MSG_FASTOPEN flag). Corking is unimplemented, so opts.More
|
||||
@@ -1730,6 +1785,9 @@ func (e *Endpoint) windowCrossedACKThresholdLocked(deltaBefore int, rcvBufSize i
|
||||
}
|
||||
|
||||
// OnReuseAddressSet implements tcpip.SocketOptionsHandler.OnReuseAddressSet.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) OnReuseAddressSet(v bool) {
|
||||
e.LockUser()
|
||||
e.portFlags.TupleOnly = v
|
||||
@@ -1737,6 +1795,9 @@ func (e *Endpoint) OnReuseAddressSet(v bool) {
|
||||
}
|
||||
|
||||
// OnReusePortSet implements tcpip.SocketOptionsHandler.OnReusePortSet.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) OnReusePortSet(v bool) {
|
||||
e.LockUser()
|
||||
e.portFlags.LoadBalanced = v
|
||||
@@ -1744,6 +1805,9 @@ func (e *Endpoint) OnReusePortSet(v bool) {
|
||||
}
|
||||
|
||||
// OnKeepAliveSet implements tcpip.SocketOptionsHandler.OnKeepAliveSet.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) OnKeepAliveSet(bool) {
|
||||
e.LockUser()
|
||||
e.resetKeepaliveTimer(true /* receivedData */)
|
||||
@@ -1751,6 +1815,9 @@ func (e *Endpoint) OnKeepAliveSet(bool) {
|
||||
}
|
||||
|
||||
// OnDelayOptionSet implements tcpip.SocketOptionsHandler.OnDelayOptionSet.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) OnDelayOptionSet(v bool) {
|
||||
if !v {
|
||||
e.LockUser()
|
||||
@@ -1763,6 +1830,9 @@ func (e *Endpoint) OnDelayOptionSet(v bool) {
|
||||
}
|
||||
|
||||
// OnCorkOptionSet implements tcpip.SocketOptionsHandler.OnCorkOptionSet.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) OnCorkOptionSet(v bool) {
|
||||
if !v {
|
||||
e.LockUser()
|
||||
@@ -1782,6 +1852,9 @@ func (e *Endpoint) getSendBufferSize() int {
|
||||
}
|
||||
|
||||
// OnSetReceiveBufferSize implements tcpip.SocketOptionsHandler.OnSetReceiveBufferSize.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) OnSetReceiveBufferSize(rcvBufSz, oldSz int64) (newSz int64, postSet func()) {
|
||||
e.LockUser()
|
||||
|
||||
@@ -1815,7 +1888,7 @@ func (e *Endpoint) OnSetReceiveBufferSize(rcvBufSz, oldSz int64) (newSz int64, p
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
if e.EndpointState().connected() && sendNonZeroWindowUpdate {
|
||||
e.rcv.nonZeroWindow() // +checklocksforce:e.rcv.ep.mu
|
||||
e.rcv.nonZeroWindow()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1825,11 +1898,17 @@ func (e *Endpoint) OnSetReceiveBufferSize(rcvBufSz, oldSz int64) (newSz int64, p
|
||||
|
||||
// OnSetSendBufferSize implements tcpip.SocketOptionsHandler.OnSetSendBufferSize.
|
||||
func (e *Endpoint) OnSetSendBufferSize(sz int64) int64 {
|
||||
e.sndQueueInfo.TCPSndBufState.AutoTuneSndBufDisabled.Store(1)
|
||||
// Only this atomic flag is accessed without sndQueueMu; checklocks
|
||||
// cannot express that leaf exception to the enclosing state guard.
|
||||
disabled := &e.sndQueueInfo.TCPSndBufState.AutoTuneSndBufDisabled // +checklocksignore
|
||||
disabled.Store(1)
|
||||
return sz
|
||||
}
|
||||
|
||||
// WakeupWriters implements tcpip.SocketOptionsHandler.WakeupWriters.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) WakeupWriters() {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
@@ -1845,6 +1924,9 @@ func (e *Endpoint) WakeupWriters() {
|
||||
}
|
||||
|
||||
// SetSockOptInt sets a socket option.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
|
||||
// Lower 2 bits represents ECN bits. RFC 3168, section 23.1
|
||||
const inetECNMask = 3
|
||||
@@ -1882,15 +1964,15 @@ func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
|
||||
e.UnlockUser()
|
||||
|
||||
case tcpip.MTUDiscoverOption:
|
||||
// PROBE is accepted alongside DO/WANT/DONT. In Linux,
|
||||
// PROBE sets DF but ignores ICMP-based PMTU updates;
|
||||
// since gVisor lacks ICMP PMTU feedback, it behaves
|
||||
// identically to DO.
|
||||
switch v := tcpip.PMTUDStrategy(v); v {
|
||||
case tcpip.PMTUDiscoveryWant, tcpip.PMTUDiscoveryDont, tcpip.PMTUDiscoveryDo:
|
||||
case tcpip.PMTUDiscoveryWant, tcpip.PMTUDiscoveryDont, tcpip.PMTUDiscoveryDo, tcpip.PMTUDiscoveryProbe:
|
||||
e.LockUser()
|
||||
e.pmtud = v
|
||||
e.UnlockUser()
|
||||
case tcpip.PMTUDiscoveryProbe:
|
||||
// We don't support a way to ignore MTU updates; it's
|
||||
// either on or it's off.
|
||||
return &tcpip.ErrNotSupported{}
|
||||
default:
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
@@ -1945,6 +2027,9 @@ func (e *Endpoint) HasNIC(id int32) bool {
|
||||
}
|
||||
|
||||
// SetSockOpt sets a socket option.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
|
||||
switch v := opt.(type) {
|
||||
case *tcpip.KeepaliveIdleOption:
|
||||
@@ -2038,6 +2123,9 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
|
||||
}
|
||||
|
||||
// readyReceiveSize returns the number of bytes ready to be received.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) readyReceiveSize() (int, tcpip.Error) {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
@@ -2054,6 +2142,9 @@ func (e *Endpoint) readyReceiveSize() (int, tcpip.Error) {
|
||||
}
|
||||
|
||||
// GetSockOptInt implements tcpip.Endpoint.GetSockOptInt.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
|
||||
switch opt {
|
||||
case tcpip.KeepaliveCountOption:
|
||||
@@ -2128,6 +2219,14 @@ func (e *Endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
|
||||
}
|
||||
}
|
||||
|
||||
// getTCPInfo snapshots endpoint state and the selected sender's RTT state.
|
||||
//
|
||||
// The caller must not hold that sender's RTT mutex. The sender is selected
|
||||
// after locking e.mu, so checklocks cannot name it in an entry exclusion
|
||||
// through the unlocked e.snd association.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) getTCPInfo() tcpip.TCPInfoOption {
|
||||
info := tcpip.TCPInfoOption{}
|
||||
e.LockUser()
|
||||
@@ -2157,6 +2256,9 @@ func (e *Endpoint) getTCPInfo() tcpip.TCPInfoOption {
|
||||
}
|
||||
|
||||
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
|
||||
switch o := opt.(type) {
|
||||
case *tcpip.TCPInfoOption:
|
||||
@@ -2228,6 +2330,9 @@ func (*Endpoint) Disconnect() tcpip.Error {
|
||||
}
|
||||
|
||||
// Connect connects the endpoint to its peer.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) Connect(addr tcpip.FullAddress) tcpip.Error {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
@@ -2342,9 +2447,12 @@ func (e *Endpoint) registerEndpoint(addr tcpip.FullAddress, netProto tcpip.Netwo
|
||||
}
|
||||
}
|
||||
|
||||
id := e.TransportEndpointInfo.ID
|
||||
id.LocalPort = p
|
||||
if err := e.stack.RegisterTransportEndpoint(netProtos, ProtocolNumber, id, e, e.portFlags, bindToDevice); err != nil {
|
||||
// Initialize the ID before publishing the endpoint: ICMP error
|
||||
// delivery reads it without acquiring e.mu.
|
||||
oldID := e.TransportEndpointInfo.ID
|
||||
e.TransportEndpointInfo.ID.LocalPort = p
|
||||
if err := e.stack.RegisterTransportEndpoint(netProtos, ProtocolNumber, e.TransportEndpointInfo.ID, e, e.portFlags, bindToDevice); err != nil {
|
||||
e.TransportEndpointInfo.ID = oldID
|
||||
portRes := ports.Reservation{
|
||||
Networks: netProtos,
|
||||
Transport: ProtocolNumber,
|
||||
@@ -2363,7 +2471,6 @@ func (e *Endpoint) registerEndpoint(addr tcpip.FullAddress, netProto tcpip.Netwo
|
||||
|
||||
// Port picking successful. Save the details of
|
||||
// the selected port.
|
||||
e.TransportEndpointInfo.ID = id
|
||||
e.isPortReserved = true
|
||||
e.boundBindToDevice = bindToDevice
|
||||
e.boundPortFlags = e.portFlags
|
||||
@@ -2379,7 +2486,7 @@ func (e *Endpoint) registerEndpoint(addr tcpip.FullAddress, netProto tcpip.Netwo
|
||||
|
||||
// connect connects the endpoint to its peer.
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
func (e *Endpoint) connect(addr tcpip.FullAddress, handshake bool) tcpip.Error {
|
||||
connectingAddr := addr.Addr
|
||||
|
||||
@@ -2461,6 +2568,19 @@ func (e *Endpoint) connect(addr tcpip.FullAddress, handshake bool) tcpip.Error {
|
||||
e.effectiveNetProtos = []tcpip.NetworkProtocolNumber{netProto}
|
||||
e.connectingAddress = connectingAddr
|
||||
|
||||
if e.alsoBindToV4 {
|
||||
// If the endpoint was bound to `any` address the port will be
|
||||
// reserved for both IPv4 and IPv6 addresses. Release the port
|
||||
// reservation for the IPv4 address here so that the future bind
|
||||
// for IPv4 socket will not fail.
|
||||
portRes := ports.Reservation{
|
||||
Networks: []tcpip.NetworkProtocolNumber{header.IPv4ProtocolNumber},
|
||||
Transport: ProtocolNumber,
|
||||
Port: e.TransportEndpointInfo.ID.LocalPort,
|
||||
}
|
||||
e.stack.ReleasePort(portRes)
|
||||
}
|
||||
|
||||
e.initGSO()
|
||||
|
||||
// Connect in the restore phase does not perform handshake. Restore its
|
||||
@@ -2497,6 +2617,9 @@ func (*Endpoint) ConnectEndpoint(tcpip.Endpoint) tcpip.Error {
|
||||
|
||||
// Shutdown closes the read and/or write end of the endpoint connection to its
|
||||
// peer.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) Shutdown(flags tcpip.ShutdownFlags) tcpip.Error {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
@@ -2515,7 +2638,7 @@ func (e *Endpoint) Shutdown(flags tcpip.ShutdownFlags) tcpip.Error {
|
||||
}
|
||||
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
// +checklocksexclude:e.snd.rtt.rttMutex
|
||||
func (e *Endpoint) shutdownLocked(flags tcpip.ShutdownFlags) tcpip.Error {
|
||||
e.shutdownFlags |= flags
|
||||
switch {
|
||||
@@ -2556,9 +2679,18 @@ func (e *Endpoint) shutdownLocked(flags tcpip.ShutdownFlags) tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Queue fin segment.
|
||||
s := newOutgoingSegment(e.TransportEndpointInfo.ID, e.stack.Clock(), buffer.Buffer{})
|
||||
// Queue FIN and transition to the closing state immediately,
|
||||
// matching Linux tcp_close_state(): the FIN may be queued but
|
||||
// not yet transmitted when the write queue is blocked.
|
||||
s := newOutgoingSegment(e.TransportEndpointInfo.ID, e.stack.Clock(), buffer.Buffer{}, e.ops.GetMark())
|
||||
e.snd.writeList.PushBack(s)
|
||||
e.updateConnDirectionState(connDirectionStateSndClosed)
|
||||
switch e.EndpointState() {
|
||||
case StateCloseWait:
|
||||
e.setEndpointState(StateLastAck)
|
||||
default:
|
||||
e.setEndpointState(StateFinWait1)
|
||||
}
|
||||
// Mark endpoint as closed.
|
||||
e.sndQueueInfo.SndClosed = true
|
||||
e.sndQueueInfo.sndQueueMu.Unlock()
|
||||
@@ -2598,6 +2730,9 @@ func (e *Endpoint) shutdownLocked(flags tcpip.ShutdownFlags) tcpip.Error {
|
||||
|
||||
// Listen puts the endpoint in "listen" mode, which allows it to accept
|
||||
// new connections.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) Listen(backlog int) tcpip.Error {
|
||||
if err := e.listen(backlog); err != nil {
|
||||
if !err.IgnoreStats() {
|
||||
@@ -2609,6 +2744,8 @@ func (e *Endpoint) Listen(backlog int) tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) listen(backlog int) tcpip.Error {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
@@ -2688,6 +2825,9 @@ func (e *Endpoint) listen(backlog int) tcpip.Error {
|
||||
// to an endpoint previously set to listen mode.
|
||||
//
|
||||
// addr if not-nil will contain the peer address of the returned endpoint.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) Accept(peerAddr *tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, tcpip.Error) {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
@@ -2717,6 +2857,9 @@ func (e *Endpoint) Accept(peerAddr *tcpip.FullAddress) (tcpip.Endpoint, *waiter.
|
||||
}
|
||||
|
||||
// Bind binds the endpoint to a specific local port and optionally address.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) Bind(addr tcpip.FullAddress) (err tcpip.Error) {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
@@ -2749,6 +2892,7 @@ func (e *Endpoint) bindLocked(addr tcpip.FullAddress) (err tcpip.Error) {
|
||||
alsoBindToV4 := !e.ops.GetV6Only() && addr.Addr == tcpip.Address{} && stackHasV4
|
||||
if alsoBindToV4 {
|
||||
netProtos = append(netProtos, header.IPv4ProtocolNumber)
|
||||
e.alsoBindToV4 = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2809,6 +2953,9 @@ func (e *Endpoint) bindLocked(addr tcpip.FullAddress) (err tcpip.Error) {
|
||||
}
|
||||
|
||||
// GetLocalAddress returns the address to which the endpoint is bound.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
@@ -2821,6 +2968,9 @@ func (e *Endpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
}
|
||||
|
||||
// GetRemoteAddress returns the address to which the endpoint is connected.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) GetRemoteAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
e.LockUser()
|
||||
defer e.UnlockUser()
|
||||
@@ -2848,6 +2998,7 @@ func (*Endpoint) HandlePacket(stack.TransportEndpointID, *stack.PacketBuffer) {
|
||||
// based on the state of the endpoint.
|
||||
}
|
||||
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
func (e *Endpoint) enqueueSegment(s *segment) bool {
|
||||
// Send packet to worker goroutine.
|
||||
if !e.segmentQueue.enqueue(s) {
|
||||
@@ -2898,7 +3049,8 @@ func (e *Endpoint) onICMPError(err tcpip.Error, transErr stack.TransportError, p
|
||||
|
||||
if e.EndpointState().connecting() {
|
||||
e.mu.Lock()
|
||||
if lEP := e.h.listenEP; lEP != nil {
|
||||
if e.h != nil && e.h.listenEP != nil {
|
||||
lEP := e.h.listenEP
|
||||
// Remove from listening endpoints pending list.
|
||||
lEP.acceptMu.Lock()
|
||||
delete(lEP.acceptQueue.pendingEndpoints, e)
|
||||
@@ -2930,7 +3082,7 @@ func (e *Endpoint) HandleError(transErr stack.TransportError, pkt *stack.PacketB
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
if e.snd != nil {
|
||||
e.snd.updateMaxPayloadSize(newMTU, 1 /* count */) // +checklocksforce:e.snd.ep.mu
|
||||
e.snd.updateMaxPayloadSize(newMTU, 1 /* count */)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2960,7 +3112,6 @@ func (e *Endpoint) HandleError(transErr stack.TransportError, pkt *stack.PacketB
|
||||
// number of newly available bytes is v.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) updateSndBufferUsage(v int) {
|
||||
sendBufferSize := e.getSendBufferSize()
|
||||
e.sndQueueInfo.sndQueueMu.Lock()
|
||||
@@ -3055,14 +3206,15 @@ func (e *Endpoint) maxReceiveBufferSize() int {
|
||||
return rs.Max
|
||||
}
|
||||
|
||||
// directionState returns the close state of send and receive part of the endpoint
|
||||
// connDirectionState returns the send and receive close state of the endpoint.
|
||||
func (e *Endpoint) connDirectionState() connDirectionState {
|
||||
return connDirectionState(e.connectionDirectionState.Load())
|
||||
}
|
||||
|
||||
// updateDirectionState updates the close state of send and receive part of the endpoint
|
||||
func (e *Endpoint) updateConnDirectionState(state connDirectionState) connDirectionState {
|
||||
return connDirectionState(e.connectionDirectionState.Swap(uint32(e.connDirectionState() | state)))
|
||||
// updateConnDirectionState adds closed directions to the endpoint's state.
|
||||
// Passing connDirectionStateOpen leaves the state unchanged.
|
||||
func (e *Endpoint) updateConnDirectionState(state connDirectionState) {
|
||||
atomicbitops.OrUint32(&e.connectionDirectionState, uint32(state))
|
||||
}
|
||||
|
||||
// rcvWndScaleForHandshake computes the receive window scale to offer to the
|
||||
@@ -3129,6 +3281,8 @@ func (e *Endpoint) maybeEnableSACKPermitted(synOpts header.TCPSynOptions) {
|
||||
}
|
||||
|
||||
// maxOptionSize return the maximum size of TCP options.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
func (e *Endpoint) maxOptionSize() (size int) {
|
||||
var maxSackBlocks [header.TCPMaxSACKBlocks]header.SACKBlock
|
||||
options := e.makeOptions(maxSackBlocks[:])
|
||||
@@ -3142,7 +3296,7 @@ func (e *Endpoint) maxOptionSize() (size int) {
|
||||
// used before invoking the probe.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
// +checklocksexclude:e.snd.rtt.rttMutex
|
||||
func (e *Endpoint) completeStateLocked(s *TCPEndpointState) {
|
||||
s.TCPEndpointStateInner = e.TCPEndpointStateInner
|
||||
s.ID = TCPEndpointID(e.TransportEndpointInfo.ID)
|
||||
@@ -3216,6 +3370,9 @@ func (e *Endpoint) State() uint32 {
|
||||
}
|
||||
|
||||
// Info returns a copy of the endpoint info.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) Info() tcpip.EndpointInfo {
|
||||
e.LockUser()
|
||||
// Make a copy of the endpoint info.
|
||||
@@ -3251,11 +3408,7 @@ func GetTCPSendBufferLimits(sh tcpip.StackHandler) tcpip.SendBufferSizeOption {
|
||||
// This type assertion is safe because only the TCP stack calls this
|
||||
// function.
|
||||
ss := sh.(*stack.Stack).TCPSendBufferLimits()
|
||||
return tcpip.SendBufferSizeOption{
|
||||
Min: ss.Min,
|
||||
Default: ss.Default,
|
||||
Max: ss.Max,
|
||||
}
|
||||
return tcpip.SendBufferSizeOption(ss)
|
||||
}
|
||||
|
||||
// allowOutOfWindowAck returns true if an out-of-window ACK can be sent now.
|
||||
@@ -3283,24 +3436,22 @@ func GetTCPReceiveBufferLimits(s tcpip.StackHandler) tcpip.ReceiveBufferSizeOpti
|
||||
panic(fmt.Sprintf("s.TransportProtocolOption(%d, %#v) = %s", header.TCPProtocolNumber, ss, err))
|
||||
}
|
||||
|
||||
return tcpip.ReceiveBufferSizeOption{
|
||||
Min: ss.Min,
|
||||
Default: ss.Default,
|
||||
Max: ss.Max,
|
||||
}
|
||||
return tcpip.ReceiveBufferSizeOption(ss)
|
||||
}
|
||||
|
||||
// computeTCPSendBufferSize implements auto tuning of send buffer size and
|
||||
// returns the new send buffer size.
|
||||
//
|
||||
// +checklocks:e.mu
|
||||
// +checklocksalias:e.snd.ep.mu=e.mu
|
||||
func (e *Endpoint) computeTCPSendBufferSize() int64 {
|
||||
curSndBufSz := int64(e.getSendBufferSize())
|
||||
|
||||
// Auto tuning is disabled when the user explicitly sets the send
|
||||
// buffer size with SO_SNDBUF option.
|
||||
if disabled := e.sndQueueInfo.TCPSndBufState.AutoTuneSndBufDisabled.Load(); disabled == 1 {
|
||||
// Only this atomic flag is accessed without sndQueueMu; checklocks
|
||||
// cannot express that leaf exception to the enclosing state guard.
|
||||
disabled := &e.sndQueueInfo.TCPSndBufState.AutoTuneSndBufDisabled // +checklocksignore
|
||||
if disabled.Load() == 1 {
|
||||
return curSndBufSz
|
||||
}
|
||||
|
||||
|
||||
+134
-80
@@ -23,7 +23,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/ports"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
@@ -33,11 +32,14 @@ var logDisconnectOnce sync.Once
|
||||
|
||||
func logDisconnect() {
|
||||
logDisconnectOnce.Do(func() {
|
||||
log.Infof("One or more TCP connections terminated during save")
|
||||
log.Infof("One or more TCP connections terminated during save restore")
|
||||
})
|
||||
}
|
||||
|
||||
// beforeSave is invoked by stateify.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) beforeSave() {
|
||||
// Stop incoming packets.
|
||||
e.segmentQueue.freeze()
|
||||
@@ -49,17 +51,24 @@ func (e *Endpoint) beforeSave() {
|
||||
switch {
|
||||
case epState == StateInitial || epState == StateBound:
|
||||
case epState.connected() || epState.handshake():
|
||||
if !e.route.HasSaveRestoreCapability() {
|
||||
if !e.route.HasDisconnectOkCapability() {
|
||||
panic(&tcpip.ErrSaveRejection{
|
||||
Err: fmt.Errorf("endpoint cannot be saved in connected state: local %s:%d, remote %s:%d", e.TransportEndpointInfo.ID.LocalAddress, e.TransportEndpointInfo.ID.LocalPort, e.TransportEndpointInfo.ID.RemoteAddress, e.TransportEndpointInfo.ID.RemotePort),
|
||||
})
|
||||
// Terminate valid connections only for restore.
|
||||
if !e.stack.GetAllowConnectedOnSave() && !e.route.HasSaveRestoreCapability() {
|
||||
if e.stack.GetRemoveConf() {
|
||||
// Terminate the endpoint when resume=false.
|
||||
e.terminateAtRestore = false
|
||||
if !e.stack.AllowLiveTCPMigration() {
|
||||
logDisconnect()
|
||||
e.resetConnectionLocked(&tcpip.ErrConnectionAborted{})
|
||||
e.mu.Unlock()
|
||||
e.Close()
|
||||
e.mu.Lock()
|
||||
}
|
||||
} else {
|
||||
// This is set only when resume=true, the termination
|
||||
// of this endpoint will happen during restore of the
|
||||
// saved snapshot.
|
||||
e.terminateAtRestore = true
|
||||
}
|
||||
logDisconnect()
|
||||
e.resetConnectionLocked(&tcpip.ErrConnectionAborted{})
|
||||
e.mu.Unlock()
|
||||
e.Close()
|
||||
e.mu.Lock()
|
||||
}
|
||||
fallthrough
|
||||
case epState == StateListen:
|
||||
@@ -131,14 +140,45 @@ func (e *Endpoint) afterLoad(ctx context.Context) {
|
||||
// Restore the endpoint to InitialState as it will be moved to
|
||||
// its origEndpointState during Restore.
|
||||
e.state = atomicbitops.FromUint32(uint32(StateInitial))
|
||||
if e.stack.IsSaveRestoreEnabled() {
|
||||
e.stack.RegisterRestoredEndpoint(e)
|
||||
} else {
|
||||
stack.RestoreStackFromContext(ctx).RegisterRestoredEndpoint(e)
|
||||
e.stack.RegisterRestoredEndpoint(e)
|
||||
}
|
||||
|
||||
// Close the endpoint during restore if terminateAtRestore was set for the endpoint.
|
||||
func (e *Endpoint) closeEndpointAtRestore() {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
epState := EndpointState(e.origEndpointState)
|
||||
if !epState.connected() && !epState.connecting() {
|
||||
log.Debugf("endpoint was marked to terminate at restore in a wrong state, ID: %+v state: %v", e.ID, epState)
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("terminating TCP connection during restore, ID: %+v state: %v", e.ID, epState)
|
||||
|
||||
// Put the endpoint in the error state and do cleanup. Do not
|
||||
// attempt to send RST as route will be nil.
|
||||
e.purgeReadQueue()
|
||||
if epState.connected() {
|
||||
e.purgeWriteQueue()
|
||||
e.purgePendingRcvQueue()
|
||||
e.cleanupLocked()
|
||||
}
|
||||
e.state.Store(uint32(StateError))
|
||||
e.closeNoShutdownLocked()
|
||||
tcpip.DeleteDanglingEndpoint(e)
|
||||
|
||||
if epState.connected() {
|
||||
connectedLoading.Done()
|
||||
} else if epState.connecting() {
|
||||
connectingLoading.Done()
|
||||
}
|
||||
}
|
||||
|
||||
// Restore implements tcpip.RestoredEndpoint.Restore.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) Restore(s *stack.Stack) {
|
||||
if !e.EndpointState().closed() {
|
||||
e.keepalive.timer.init(s.Clock(), timerHandler(e, e.keepaliveTimerExpired))
|
||||
@@ -149,44 +189,52 @@ func (e *Endpoint) Restore(s *stack.Stack) {
|
||||
snd.probeTimer.init(s.Clock(), timerHandler(e, e.snd.probeTimerExpired))
|
||||
snd.corkTimer.init(s.Clock(), timerHandler(e, e.snd.corkTimerExpired))
|
||||
}
|
||||
saveRestoreEnabled := e.stack.IsSaveRestoreEnabled()
|
||||
if !saveRestoreEnabled {
|
||||
e.stack = s
|
||||
e.protocol = protocolFromStack(s)
|
||||
}
|
||||
e.ops.InitHandler(e, e.stack, GetTCPSendBufferLimits, GetTCPReceiveBufferLimits)
|
||||
e.segmentQueue.thaw()
|
||||
|
||||
e.mu.Lock()
|
||||
id := e.ID
|
||||
terminateAtRestore := e.terminateAtRestore
|
||||
e.mu.Unlock()
|
||||
|
||||
bind := func() {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
if !saveRestoreEnabled {
|
||||
addr, _, err := e.checkV4MappedLocked(tcpip.FullAddress{Addr: e.BindAddr, Port: e.TransportEndpointInfo.ID.LocalPort}, true /* bind */)
|
||||
if err != nil {
|
||||
panic("unable to parse BindAddr: " + err.String())
|
||||
}
|
||||
portRes := ports.Reservation{
|
||||
Networks: e.effectiveNetProtos,
|
||||
Transport: ProtocolNumber,
|
||||
Addr: addr.Addr,
|
||||
Port: addr.Port,
|
||||
Flags: e.boundPortFlags,
|
||||
BindToDevice: e.boundBindToDevice,
|
||||
Dest: e.boundDest,
|
||||
}
|
||||
if ok := e.stack.ReserveTuple(portRes); !ok {
|
||||
panic(fmt.Sprintf("unable to re-reserve tuple (%v, %q, %d, %+v, %d, %v)", e.effectiveNetProtos, addr.Addr, addr.Port, e.boundPortFlags, e.boundBindToDevice, e.boundDest))
|
||||
}
|
||||
}
|
||||
e.isPortReserved = true
|
||||
|
||||
// Mark endpoint as bound.
|
||||
e.setEndpointState(StateBound)
|
||||
}
|
||||
|
||||
if terminateAtRestore && !e.stack.AllowLiveTCPMigration() {
|
||||
e.closeEndpointAtRestore()
|
||||
return
|
||||
}
|
||||
|
||||
epState := EndpointState(e.origEndpointState)
|
||||
switch {
|
||||
case epState.connected():
|
||||
if e.stack.AllowLiveTCPMigration() {
|
||||
// Handle dual stack addresses.
|
||||
netProto := e.NetProto
|
||||
switch e.TransportEndpointInfo.ID.LocalAddress.BitLen() {
|
||||
case header.IPv4AddressSizeBits:
|
||||
netProto = header.IPv4ProtocolNumber
|
||||
case header.IPv6AddressSizeBits:
|
||||
netProto = header.IPv6ProtocolNumber
|
||||
}
|
||||
// Get the new local NIC for source IP and do a FindRoute here to
|
||||
// identify if the network config is same. Then only attempt restore,
|
||||
// else close the connection on our end.
|
||||
r, err := e.stack.FindRoute(0, e.TransportEndpointInfo.ID.LocalAddress, e.TransportEndpointInfo.ID.RemoteAddress, netProto, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
e.closeEndpointAtRestore()
|
||||
log.Infof("Cannot find the route %+v", e.TransportEndpointInfo.ID)
|
||||
return
|
||||
}
|
||||
e.boundNICID = r.NICID()
|
||||
r.Release()
|
||||
}
|
||||
bind()
|
||||
if e.connectingAddress.BitLen() == 0 {
|
||||
e.connectingAddress = e.TransportEndpointInfo.ID.RemoteAddress
|
||||
@@ -204,16 +252,19 @@ func (e *Endpoint) Restore(s *stack.Stack) {
|
||||
// Reset the scoreboard to reinitialize the sack information as
|
||||
// we do not restore SACK information.
|
||||
e.scoreboard.Reset()
|
||||
if saveRestoreEnabled {
|
||||
// Unregister the endpoint before registering again during Connect.
|
||||
e.stack.UnregisterTransportEndpoint(e.effectiveNetProtos, header.TCPProtocolNumber, e.TransportEndpointInfo.ID, e, e.boundPortFlags, e.boundBindToDevice)
|
||||
}
|
||||
// Unregister the endpoint before registering again during Connect.
|
||||
e.stack.UnregisterTransportEndpoint(e.effectiveNetProtos, header.TCPProtocolNumber, e.TransportEndpointInfo.ID, e, e.boundPortFlags, e.boundBindToDevice)
|
||||
e.mu.Lock()
|
||||
err := e.connect(tcpip.FullAddress{NIC: e.boundNICID, Addr: e.connectingAddress, Port: e.TransportEndpointInfo.ID.RemotePort}, false /* handshake */)
|
||||
if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
|
||||
panic("endpoint connecting failed: " + err.String())
|
||||
log.Warningf("TCP endpoint connect failed for connected endpoint with ID: %+v err: %v", id, err)
|
||||
e.mu.Unlock()
|
||||
e.Close()
|
||||
connectedLoading.Done()
|
||||
return
|
||||
}
|
||||
e.state.Store(e.origEndpointState)
|
||||
log.Infof("connect success: %+v", e.TransportEndpointInfo.ID)
|
||||
// For FIN-WAIT-2 and TIME-WAIT we need to start the appropriate timers so
|
||||
// that the socket is closed correctly.
|
||||
switch epState {
|
||||
@@ -229,42 +280,24 @@ func (e *Endpoint) Restore(s *stack.Stack) {
|
||||
e.snd.corkTimer.enable(MinRTO)
|
||||
}
|
||||
e.mu.Unlock()
|
||||
e.requeueOnRestore()
|
||||
connectedLoading.Done()
|
||||
case epState == StateListen:
|
||||
tcpip.AsyncLoading.Add(1)
|
||||
if !saveRestoreEnabled {
|
||||
go func() {
|
||||
connectedLoading.Wait()
|
||||
bind()
|
||||
e.acceptMu.Lock()
|
||||
backlog := e.acceptQueue.capacity
|
||||
e.acceptMu.Unlock()
|
||||
if err := e.Listen(backlog); err != nil {
|
||||
panic("endpoint listening failed: " + err.String())
|
||||
}
|
||||
e.LockUser()
|
||||
if e.shutdownFlags != 0 {
|
||||
e.shutdownLocked(e.shutdownFlags)
|
||||
}
|
||||
e.UnlockUser()
|
||||
listenLoading.Done()
|
||||
tcpip.AsyncLoading.Done()
|
||||
}()
|
||||
} else {
|
||||
go func() {
|
||||
connectedLoading.Wait()
|
||||
e.LockUser()
|
||||
// All endpoints will be moved to initial state after
|
||||
// restore. Set endpoint to its originial listen state.
|
||||
e.setEndpointState(StateListen)
|
||||
// Initialize the listening context.
|
||||
rcvWnd := seqnum.Size(e.receiveBufferAvailable())
|
||||
e.listenCtx = newListenContext(e.stack, e.protocol, e, rcvWnd, e.ops.GetV6Only(), e.NetProto)
|
||||
e.UnlockUser()
|
||||
listenLoading.Done()
|
||||
tcpip.AsyncLoading.Done()
|
||||
}()
|
||||
}
|
||||
go func() {
|
||||
connectedLoading.Wait()
|
||||
e.LockUser()
|
||||
// All endpoints will be moved to initial state after
|
||||
// restore. Set endpoint to its original listen state.
|
||||
e.setEndpointState(StateListen)
|
||||
// Initialize the listening context.
|
||||
rcvWnd := seqnum.Size(e.receiveBufferAvailable())
|
||||
e.listenCtx = newListenContext(e.stack, e.protocol, e, rcvWnd, e.ops.GetV6Only(), e.NetProto)
|
||||
e.UnlockUser()
|
||||
e.requeueOnRestore()
|
||||
listenLoading.Done()
|
||||
tcpip.AsyncLoading.Done()
|
||||
}()
|
||||
case epState == StateConnecting:
|
||||
// Initial SYN hasn't been sent yet so initiate a connect.
|
||||
tcpip.AsyncLoading.Add(1)
|
||||
@@ -274,7 +307,8 @@ func (e *Endpoint) Restore(s *stack.Stack) {
|
||||
bind()
|
||||
err := e.Connect(tcpip.FullAddress{NIC: e.boundNICID, Addr: e.connectingAddress, Port: e.TransportEndpointInfo.ID.RemotePort})
|
||||
if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
|
||||
panic("endpoint connecting failed: " + err.String())
|
||||
log.Warningf("TCP endpoint connect failed for connecting endpoint with ID: %+v err: %v", id, err)
|
||||
e.Close()
|
||||
}
|
||||
connectingLoading.Done()
|
||||
tcpip.AsyncLoading.Done()
|
||||
@@ -289,11 +323,15 @@ func (e *Endpoint) Restore(s *stack.Stack) {
|
||||
// naturally complete the connection.
|
||||
bind()
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.setEndpointState(epState)
|
||||
r, err := e.stack.FindRoute(e.boundNICID, e.TransportEndpointInfo.ID.LocalAddress, e.TransportEndpointInfo.ID.RemoteAddress, e.effectiveNetProtos[0], false /* multicastLoop */)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("FindRoute failed when restoring endpoint w/ ID: %+v", e.ID))
|
||||
e.mu.Unlock()
|
||||
log.Warningf("FindRoute failed when restoring endpoint w/ ID: %+v err: %v", id, err)
|
||||
e.Close()
|
||||
connectingLoading.Done()
|
||||
tcpip.AsyncLoading.Done()
|
||||
return
|
||||
}
|
||||
e.route = r
|
||||
timer, err := newBackoffTimer(e.stack.Clock(), InitialRTO, MaxRTO, timerHandler(e, e.h.retransmitHandlerLocked))
|
||||
@@ -303,6 +341,8 @@ func (e *Endpoint) Restore(s *stack.Stack) {
|
||||
e.h.retransmitTimer = timer
|
||||
connectingLoading.Done()
|
||||
tcpip.AsyncLoading.Done()
|
||||
e.mu.Unlock()
|
||||
e.requeueOnRestore()
|
||||
}()
|
||||
case epState == StateBound:
|
||||
tcpip.AsyncLoading.Add(1)
|
||||
@@ -326,6 +366,20 @@ func (e *Endpoint) Restore(s *stack.Stack) {
|
||||
}
|
||||
|
||||
// Resume implements tcpip.ResumableEndpoint.Resume.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
func (e *Endpoint) Resume() {
|
||||
e.segmentQueue.thaw()
|
||||
}
|
||||
|
||||
// requeueOnRestore re-adds the endpoint to its processor's run-queue if it has
|
||||
// queued segments. The run-queue is not saved across checkpoint/restore.
|
||||
//
|
||||
// +checklocksexclude:e.segmentQueue.mu
|
||||
// +checklocksexclude:e.pendingProcessingMu
|
||||
func (e *Endpoint) requeueOnRestore() {
|
||||
if e.segmentQueue.empty() || e.isOwnedByUser() {
|
||||
return
|
||||
}
|
||||
e.protocol.dispatcher.selectProcessor(e.TransportEndpointInfo.ID).queueEndpoint(e)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type epQueueMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var epQueueprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var epQueuelockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type epQueuelockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *epQueueMutex) Lock() {
|
||||
locking.AddGLock(epQueueprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *epQueueMutex) NestedLock(i epQueuelockNameIndex) {
|
||||
locking.AddGLock(epQueueprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *epQueueMutex) Unlock() {
|
||||
locking.DelGLock(epQueueprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *epQueueMutex) NestedUnlock(i epQueuelockNameIndex) {
|
||||
locking.DelGLock(epQueueprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func epQueueinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
epQueueinitLockNames()
|
||||
epQueueprefixIndex = locking.NewMutexClass(reflect.TypeFor[epQueueMutex](), epQueuelockNames)
|
||||
}
|
||||
@@ -15,10 +15,6 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
|
||||
@@ -38,7 +34,7 @@ type Forwarder struct {
|
||||
maxInFlight int
|
||||
handler func(*ForwarderRequest)
|
||||
|
||||
mu sync.Mutex
|
||||
mu forwarderMutex
|
||||
inFlight map[stack.TransportEndpointID]struct{}
|
||||
listen *listenContext
|
||||
}
|
||||
@@ -111,7 +107,7 @@ func (f *Forwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.Packet
|
||||
// and passed to the client. Clients must eventually call Complete() on it, and
|
||||
// may optionally create an endpoint to represent it via CreateEndpoint.
|
||||
type ForwarderRequest struct {
|
||||
mu sync.Mutex
|
||||
mu forwarderRequestMutex
|
||||
forwarder *Forwarder
|
||||
segment *segment
|
||||
synOptions header.TCPSynOptions
|
||||
@@ -180,49 +176,5 @@ func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint,
|
||||
func (r *ForwarderRequest) ForwardedPacketExperimentOption() (uint16, bool) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
switch r.segment.pkt.NetworkProtocolNumber {
|
||||
case header.IPv4ProtocolNumber:
|
||||
h := header.IPv4(r.segment.pkt.NetworkHeader().Slice())
|
||||
opts := h.Options()
|
||||
iter := opts.MakeIterator()
|
||||
for {
|
||||
opt, done, err := iter.Next()
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
if done {
|
||||
return 0, false
|
||||
}
|
||||
if opt.Type() == header.IPv4OptionExperimentType {
|
||||
return opt.(*header.IPv4OptionExperiment).Value(), true
|
||||
}
|
||||
}
|
||||
case header.IPv6ProtocolNumber:
|
||||
h := header.IPv6(r.segment.pkt.NetworkHeader().Slice())
|
||||
v := r.segment.pkt.NetworkHeader().View()
|
||||
if v != nil {
|
||||
v.TrimFront(header.IPv6MinimumSize)
|
||||
}
|
||||
buf := buffer.MakeWithView(v)
|
||||
buf.Append(r.segment.pkt.TransportHeader().View())
|
||||
dataBuf := r.segment.pkt.Data().ToBuffer()
|
||||
buf.Merge(&dataBuf)
|
||||
it := header.MakeIPv6PayloadIterator(header.IPv6ExtensionHeaderIdentifier(h.NextHeader()), buf)
|
||||
|
||||
for {
|
||||
hdr, done, err := it.Next()
|
||||
if done || err != nil {
|
||||
break
|
||||
}
|
||||
if h, ok := hdr.(header.IPv6ExperimentExtHdr); ok {
|
||||
hdr.Release()
|
||||
return h.Value, true
|
||||
}
|
||||
hdr.Release()
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Unexpected network protocol number %d", r.segment.pkt.NetworkProtocolNumber))
|
||||
}
|
||||
return 0, false
|
||||
return r.segment.pkt.ExperimentOptionValue()
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/sync/locking"
|
||||
)
|
||||
|
||||
// Mutex is sync.Mutex with the correctness validator.
|
||||
type forwarderMutex struct {
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var forwarderprefixIndex *locking.MutexClass
|
||||
|
||||
// lockNames is a list of user-friendly lock names.
|
||||
// Populated in init.
|
||||
var forwarderlockNames []string
|
||||
|
||||
// lockNameIndex is used as an index passed to NestedLock and NestedUnlock,
|
||||
// referring to an index within lockNames.
|
||||
// Values are specified using the "consts" field of go_template_instance.
|
||||
type forwarderlockNameIndex int
|
||||
|
||||
// DO NOT REMOVE: The following function automatically replaced with lock index constants.
|
||||
// LOCK_NAME_INDEX_CONSTANTS
|
||||
const ()
|
||||
|
||||
// Lock locks m.
|
||||
// +checklocksignore
|
||||
func (m *forwarderMutex) Lock() {
|
||||
locking.AddGLock(forwarderprefixIndex, -1)
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// NestedLock locks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *forwarderMutex) NestedLock(i forwarderlockNameIndex) {
|
||||
locking.AddGLock(forwarderprefixIndex, int(i))
|
||||
m.mu.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks m.
|
||||
// +checklocksignore
|
||||
func (m *forwarderMutex) Unlock() {
|
||||
locking.DelGLock(forwarderprefixIndex, -1)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// NestedUnlock unlocks m knowing that another lock of the same type is held.
|
||||
// +checklocksignore
|
||||
func (m *forwarderMutex) NestedUnlock(i forwarderlockNameIndex) {
|
||||
locking.DelGLock(forwarderprefixIndex, int(i))
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// DO NOT REMOVE: The following function is automatically replaced.
|
||||
func forwarderinitLockNames() {}
|
||||
|
||||
func init() {
|
||||
forwarderinitLockNames()
|
||||
forwarderprefixIndex = locking.NewMutexClass(reflect.TypeFor[forwarderMutex](), forwarderlockNames)
|
||||
}
|
||||
Loaded 100 of 2146 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user