mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Merge branch 'master' into ipt-udp-matchers
This commit is contained in:
@@ -17,6 +17,7 @@ package tmpfs
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
@@ -444,10 +445,15 @@ func (rw *fileReadWriter) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error)
|
||||
defer rw.f.dataMu.Unlock()
|
||||
|
||||
// Compute the range to write.
|
||||
end := fs.WriteEndOffset(rw.offset, int64(srcs.NumBytes()))
|
||||
if end == rw.offset { // srcs.NumBytes() == 0?
|
||||
if srcs.NumBytes() == 0 {
|
||||
// Nothing to do.
|
||||
return 0, nil
|
||||
}
|
||||
end := fs.WriteEndOffset(rw.offset, int64(srcs.NumBytes()))
|
||||
if end == math.MaxInt64 {
|
||||
// Overflow.
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
|
||||
// Check if seals prevent either file growth or all writes.
|
||||
switch {
|
||||
|
||||
@@ -88,14 +88,14 @@ const (
|
||||
El0Sync_undef
|
||||
El0Sync_dbg
|
||||
El0Sync_inv
|
||||
VirtualizationException
|
||||
_NR_INTERRUPTS
|
||||
)
|
||||
|
||||
// System call vectors.
|
||||
const (
|
||||
Syscall Vector = El0Sync_svc
|
||||
PageFault Vector = El0Sync_da
|
||||
Syscall Vector = El0Sync_svc
|
||||
PageFault Vector = El0Sync_da
|
||||
VirtualizationException Vector = El0Error
|
||||
)
|
||||
|
||||
// VirtualAddressBits returns the number bits available for virtual addresses.
|
||||
|
||||
@@ -601,7 +601,19 @@ TEXT ·El0_fiq(SB),NOSPLIT,$0
|
||||
B ·Shutdown(SB)
|
||||
|
||||
TEXT ·El0_error(SB),NOSPLIT,$0
|
||||
B ·Shutdown(SB)
|
||||
KERNEL_ENTRY_FROM_EL0
|
||||
WORD $0xd538d092 //MRS TPIDR_EL1, R18
|
||||
WORD $0xd538601a //MRS FAR_EL1, R26
|
||||
|
||||
MOVD R26, CPU_FAULT_ADDR(RSV_REG)
|
||||
|
||||
MOVD $1, R3
|
||||
MOVD R3, CPU_ERROR_TYPE(RSV_REG) // Set error type to user.
|
||||
|
||||
MOVD $VirtualizationException, R3
|
||||
MOVD R3, CPU_VECTOR_CODE(RSV_REG)
|
||||
|
||||
B ·Halt(SB)
|
||||
|
||||
TEXT ·El0_sync_invalid(SB),NOSPLIT,$0
|
||||
B ·Shutdown(SB)
|
||||
|
||||
@@ -85,6 +85,7 @@ func Emit(w io.Writer) {
|
||||
|
||||
fmt.Fprintf(w, "#define PageFault 0x%02x\n", PageFault)
|
||||
fmt.Fprintf(w, "#define Syscall 0x%02x\n", Syscall)
|
||||
fmt.Fprintf(w, "#define VirtualizationException 0x%02x\n", VirtualizationException)
|
||||
|
||||
p := &syscall.PtraceRegs{}
|
||||
fmt.Fprintf(w, "\n// Ptrace registers.\n")
|
||||
|
||||
@@ -1260,6 +1260,18 @@ func getSockOptTCP(t *kernel.Task, ep commonEndpoint, name, outLen int) (interfa
|
||||
|
||||
return int32(time.Duration(v) / time.Second), nil
|
||||
|
||||
case linux.TCP_DEFER_ACCEPT:
|
||||
if outLen < sizeOfInt32 {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
var v tcpip.TCPDeferAcceptOption
|
||||
if err := ep.GetSockOpt(&v); err != nil {
|
||||
return nil, syserr.TranslateNetstackError(err)
|
||||
}
|
||||
|
||||
return int32(time.Duration(v) / time.Second), nil
|
||||
|
||||
default:
|
||||
emitUnimplementedEventTCP(t, name)
|
||||
}
|
||||
@@ -1713,6 +1725,16 @@ func setSockOptTCP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *
|
||||
v := usermem.ByteOrder.Uint32(optVal)
|
||||
return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.TCPLingerTimeoutOption(time.Second * time.Duration(v))))
|
||||
|
||||
case linux.TCP_DEFER_ACCEPT:
|
||||
if len(optVal) < sizeOfInt32 {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
v := int32(usermem.ByteOrder.Uint32(optVal))
|
||||
if v < 0 {
|
||||
v = 0
|
||||
}
|
||||
return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.TCPDeferAcceptOption(time.Second * time.Duration(v))))
|
||||
|
||||
case linux.TCP_REPAIR_OPTIONS:
|
||||
t.Kernel().EmitUnimplementedEvent(t)
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@ import (
|
||||
|
||||
// doSplice implements a blocking splice operation.
|
||||
func doSplice(t *kernel.Task, outFile, inFile *fs.File, opts fs.SpliceOpts, nonBlocking bool) (int64, error) {
|
||||
if opts.Length < 0 || opts.SrcStart < 0 || opts.DstStart < 0 {
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
|
||||
var (
|
||||
total int64
|
||||
n int64
|
||||
@@ -82,11 +86,6 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
offsetAddr := args[2].Pointer()
|
||||
count := int64(args[3].SizeT())
|
||||
|
||||
// Don't send a negative number of bytes.
|
||||
if count < 0 {
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
|
||||
// Get files.
|
||||
inFile := t.GetFile(inFD)
|
||||
if inFile == nil {
|
||||
@@ -136,11 +135,6 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// The offset must be valid.
|
||||
if offset < 0 {
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
|
||||
// Do the splice.
|
||||
n, err = doSplice(t, outFile, inFile, fs.SpliceOpts{
|
||||
Length: count,
|
||||
@@ -227,6 +221,7 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
if _, err := t.CopyIn(outOffset, &offset); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// Use the destination offset.
|
||||
opts.DstOffset = true
|
||||
opts.DstStart = offset
|
||||
@@ -244,6 +239,7 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
if _, err := t.CopyIn(inOffset, &offset); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// Use the source offset.
|
||||
opts.SrcOffset = true
|
||||
opts.SrcStart = offset
|
||||
|
||||
+6
-24
@@ -167,8 +167,8 @@ type NDPDispatcher interface {
|
||||
// reason, such as the address being removed). If an error occured
|
||||
// during DAD, err will be set and resolved must be ignored.
|
||||
//
|
||||
// This function is permitted to block indefinitely without interfering
|
||||
// with the stack's operation.
|
||||
// This function is not permitted to block indefinitely. This function
|
||||
// is also not permitted to call into the stack.
|
||||
OnDuplicateAddressDetectionStatus(nicID tcpip.NICID, addr tcpip.Address, resolved bool, err *tcpip.Error)
|
||||
|
||||
// OnDefaultRouterDiscovered will be called when a new default router is
|
||||
@@ -538,29 +538,11 @@ func (ndp *ndpState) sendDADPacket(addr tcpip.Address) *tcpip.Error {
|
||||
r := makeRoute(header.IPv6ProtocolNumber, header.IPv6Any, snmc, ndp.nic.linkEP.LinkAddress(), ref, false, false)
|
||||
defer r.Release()
|
||||
|
||||
linkAddr := ndp.nic.linkEP.LinkAddress()
|
||||
isValidLinkAddr := header.IsValidUnicastEthernetAddress(linkAddr)
|
||||
ndpNSSize := header.ICMPv6NeighborSolicitMinimumSize
|
||||
if isValidLinkAddr {
|
||||
// Only include a Source Link Layer Address option if the NIC has a valid
|
||||
// link layer address.
|
||||
//
|
||||
// TODO(b/141011931): Validate a LinkEndpoint's link address (provided by
|
||||
// LinkEndpoint.LinkAddress) before reaching this point.
|
||||
ndpNSSize += header.NDPLinkLayerAddressSize
|
||||
}
|
||||
|
||||
hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + ndpNSSize)
|
||||
pkt := header.ICMPv6(hdr.Prepend(ndpNSSize))
|
||||
hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.ICMPv6NeighborSolicitMinimumSize)
|
||||
pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6NeighborSolicitMinimumSize))
|
||||
pkt.SetType(header.ICMPv6NeighborSolicit)
|
||||
ns := header.NDPNeighborSolicit(pkt.NDPPayload())
|
||||
ns.SetTargetAddress(addr)
|
||||
|
||||
if isValidLinkAddr {
|
||||
ns.Options().Serialize(header.NDPOptionsSerializer{
|
||||
header.NDPSourceLinkLayerAddressOption(linkAddr),
|
||||
})
|
||||
}
|
||||
pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
|
||||
|
||||
sent := r.Stats().ICMP.V6PacketsSent
|
||||
@@ -607,8 +589,8 @@ func (ndp *ndpState) stopDuplicateAddressDetection(addr tcpip.Address) {
|
||||
delete(ndp.dad, addr)
|
||||
|
||||
// Let the integrator know DAD did not resolve.
|
||||
if ndp.nic.stack.ndpDisp != nil {
|
||||
go ndp.nic.stack.ndpDisp.OnDuplicateAddressDetectionStatus(ndp.nic.ID(), addr, false, nil)
|
||||
if ndpDisp := ndp.nic.stack.ndpDisp; ndpDisp != nil {
|
||||
ndpDisp.OnDuplicateAddressDetectionStatus(ndp.nic.ID(), addr, false, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -413,14 +413,18 @@ func TestDADResolve(t *testing.T) {
|
||||
t.Fatalf("got Proto = %d, want = %d", p.Proto, header.IPv6ProtocolNumber)
|
||||
}
|
||||
|
||||
// Check NDP packet.
|
||||
// Check NDP NS packet.
|
||||
//
|
||||
// As per RFC 4861 section 4.3, a possible option is the Source Link
|
||||
// Layer option, but this option MUST NOT be included when the source
|
||||
// address of the packet is the unspecified address.
|
||||
checker.IPv6(t, p.Pkt.Header.View().ToVectorisedView().First(),
|
||||
checker.SrcAddr(header.IPv6Any),
|
||||
checker.DstAddr(header.SolicitedNodeAddr(addr1)),
|
||||
checker.TTL(header.NDPHopLimit),
|
||||
checker.NDPNS(
|
||||
checker.NDPNSTargetAddress(addr1),
|
||||
checker.NDPNSOptions([]header.NDPOption{
|
||||
header.NDPSourceLinkLayerAddressOption(linkAddr1),
|
||||
}),
|
||||
checker.NDPNSOptions(nil),
|
||||
))
|
||||
}
|
||||
})
|
||||
@@ -497,7 +501,7 @@ func TestDADFail(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ndpDisp := ndpDispatcher{
|
||||
dadC: make(chan ndpDADEvent),
|
||||
dadC: make(chan ndpDADEvent, 1),
|
||||
}
|
||||
ndpConfigs := stack.DefaultNDPConfigurations()
|
||||
opts := stack.Options{
|
||||
@@ -576,7 +580,7 @@ func TestDADFail(t *testing.T) {
|
||||
// removed.
|
||||
func TestDADStop(t *testing.T) {
|
||||
ndpDisp := ndpDispatcher{
|
||||
dadC: make(chan ndpDADEvent),
|
||||
dadC: make(chan ndpDADEvent, 1),
|
||||
}
|
||||
ndpConfigs := stack.NDPConfigurations{
|
||||
RetransmitTimer: time.Second,
|
||||
|
||||
@@ -626,6 +626,12 @@ type TCPLingerTimeoutOption time.Duration
|
||||
// before being marked closed.
|
||||
type TCPTimeWaitTimeoutOption time.Duration
|
||||
|
||||
// TCPDeferAcceptOption is used by SetSockOpt/GetSockOpt to allow a
|
||||
// accept to return a completed connection only when there is data to be
|
||||
// read. This usually means the listening socket will drop the final ACK
|
||||
// for a handshake till the specified timeout until a segment with data arrives.
|
||||
type TCPDeferAcceptOption time.Duration
|
||||
|
||||
// MulticastTTLOption is used by SetSockOpt/GetSockOpt to control the default
|
||||
// TTL value for multicast messages. The default is 1.
|
||||
type MulticastTTLOption uint8
|
||||
|
||||
@@ -57,6 +57,7 @@ go_library(
|
||||
imports = ["gvisor.dev/gvisor/pkg/tcpip/buffer"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/log",
|
||||
"//pkg/rand",
|
||||
"//pkg/sleep",
|
||||
"//pkg/sync",
|
||||
@@ -90,6 +91,7 @@ go_test(
|
||||
tags = ["flaky"],
|
||||
deps = [
|
||||
":tcp",
|
||||
"//pkg/sync",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/checker",
|
||||
|
||||
@@ -222,13 +222,13 @@ func (l *listenContext) isCookieValid(id stack.TransportEndpointID, cookie seqnu
|
||||
|
||||
// createConnectingEndpoint creates a new endpoint in a connecting state, with
|
||||
// the connection parameters given by the arguments.
|
||||
func (l *listenContext) createConnectingEndpoint(s *segment, iss seqnum.Value, irs seqnum.Value, rcvdSynOpts *header.TCPSynOptions) (*endpoint, *tcpip.Error) {
|
||||
func (l *listenContext) createConnectingEndpoint(s *segment, iss seqnum.Value, irs seqnum.Value, rcvdSynOpts *header.TCPSynOptions, queue *waiter.Queue) (*endpoint, *tcpip.Error) {
|
||||
// Create a new endpoint.
|
||||
netProto := l.netProto
|
||||
if netProto == 0 {
|
||||
netProto = s.route.NetProto
|
||||
}
|
||||
n := newEndpoint(l.stack, netProto, nil)
|
||||
n := newEndpoint(l.stack, netProto, queue)
|
||||
n.v6only = l.v6only
|
||||
n.ID = s.id
|
||||
n.boundNICID = s.route.NICID()
|
||||
@@ -273,16 +273,17 @@ func (l *listenContext) createConnectingEndpoint(s *segment, iss seqnum.Value, i
|
||||
|
||||
// createEndpoint creates a new endpoint in connected state and then performs
|
||||
// the TCP 3-way handshake.
|
||||
func (l *listenContext) createEndpointAndPerformHandshake(s *segment, opts *header.TCPSynOptions) (*endpoint, *tcpip.Error) {
|
||||
func (l *listenContext) createEndpointAndPerformHandshake(s *segment, opts *header.TCPSynOptions, queue *waiter.Queue) (*endpoint, *tcpip.Error) {
|
||||
// Create new endpoint.
|
||||
irs := s.sequenceNumber
|
||||
isn := generateSecureISN(s.id, l.stack.Seed())
|
||||
ep, err := l.createConnectingEndpoint(s, isn, irs, opts)
|
||||
ep, err := l.createConnectingEndpoint(s, isn, irs, opts, queue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// listenEP is nil when listenContext is used by tcp.Forwarder.
|
||||
deferAccept := time.Duration(0)
|
||||
if l.listenEP != nil {
|
||||
l.listenEP.mu.Lock()
|
||||
if l.listenEP.EndpointState() != StateListen {
|
||||
@@ -290,13 +291,12 @@ func (l *listenContext) createEndpointAndPerformHandshake(s *segment, opts *head
|
||||
return nil, tcpip.ErrConnectionAborted
|
||||
}
|
||||
l.addPendingEndpoint(ep)
|
||||
deferAccept = l.listenEP.deferAccept
|
||||
l.listenEP.mu.Unlock()
|
||||
}
|
||||
|
||||
// Perform the 3-way handshake.
|
||||
h := newHandshake(ep, seqnum.Size(ep.initialReceiveWindow()))
|
||||
|
||||
h.resetToSynRcvd(isn, irs, opts)
|
||||
h := newPassiveHandshake(ep, seqnum.Size(ep.initialReceiveWindow()), isn, irs, opts, deferAccept)
|
||||
if err := h.execute(); err != nil {
|
||||
ep.Close()
|
||||
if l.listenEP != nil {
|
||||
@@ -377,16 +377,14 @@ func (e *endpoint) handleSynSegment(ctx *listenContext, s *segment, opts *header
|
||||
defer e.decSynRcvdCount()
|
||||
defer s.decRef()
|
||||
|
||||
n, err := ctx.createEndpointAndPerformHandshake(s, opts)
|
||||
n, err := ctx.createEndpointAndPerformHandshake(s, opts, &waiter.Queue{})
|
||||
if err != nil {
|
||||
e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
|
||||
e.stats.FailedConnectionAttempts.Increment()
|
||||
return
|
||||
}
|
||||
ctx.removePendingEndpoint(n)
|
||||
// Start the protocol goroutine.
|
||||
wq := &waiter.Queue{}
|
||||
n.startAcceptedLoop(wq)
|
||||
n.startAcceptedLoop()
|
||||
e.stack.Stats().TCP.PassiveConnectionOpenings.Increment()
|
||||
|
||||
e.deliverAccepted(n)
|
||||
@@ -546,7 +544,7 @@ func (e *endpoint) handleListenSegment(ctx *listenContext, s *segment) {
|
||||
rcvdSynOptions.TSEcr = s.parsedOptions.TSEcr
|
||||
}
|
||||
|
||||
n, err := ctx.createConnectingEndpoint(s, s.ackNumber-1, s.sequenceNumber-1, rcvdSynOptions)
|
||||
n, err := ctx.createConnectingEndpoint(s, s.ackNumber-1, s.sequenceNumber-1, rcvdSynOptions, &waiter.Queue{})
|
||||
if err != nil {
|
||||
e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
|
||||
e.stats.FailedConnectionAttempts.Increment()
|
||||
@@ -576,8 +574,7 @@ func (e *endpoint) handleListenSegment(ctx *listenContext, s *segment) {
|
||||
// space available in the backlog.
|
||||
|
||||
// Start the protocol goroutine.
|
||||
wq := &waiter.Queue{}
|
||||
n.startAcceptedLoop(wq)
|
||||
n.startAcceptedLoop()
|
||||
e.stack.Stats().TCP.PassiveConnectionOpenings.Increment()
|
||||
go e.deliverAccepted(n)
|
||||
}
|
||||
|
||||
@@ -86,6 +86,19 @@ type handshake struct {
|
||||
|
||||
// rcvWndScale is the receive window scale, as defined in RFC 1323.
|
||||
rcvWndScale int
|
||||
|
||||
// startTime is the time at which the first SYN/SYN-ACK was sent.
|
||||
startTime time.Time
|
||||
|
||||
// deferAccept if non-zero will drop the final ACK for a passive
|
||||
// handshake till an ACK segment with data is received or the timeout is
|
||||
// hit.
|
||||
deferAccept time.Duration
|
||||
|
||||
// acked is true if the the final ACK for a 3-way handshake has
|
||||
// been received. This is required to stop retransmitting the
|
||||
// original SYN-ACK when deferAccept is enabled.
|
||||
acked bool
|
||||
}
|
||||
|
||||
func newHandshake(ep *endpoint, rcvWnd seqnum.Size) handshake {
|
||||
@@ -112,6 +125,12 @@ func newHandshake(ep *endpoint, rcvWnd seqnum.Size) handshake {
|
||||
return h
|
||||
}
|
||||
|
||||
func newPassiveHandshake(ep *endpoint, rcvWnd seqnum.Size, isn, irs seqnum.Value, opts *header.TCPSynOptions, deferAccept time.Duration) handshake {
|
||||
h := newHandshake(ep, rcvWnd)
|
||||
h.resetToSynRcvd(isn, irs, opts, deferAccept)
|
||||
return h
|
||||
}
|
||||
|
||||
// FindWndScale determines the window scale to use for the given maximum window
|
||||
// size.
|
||||
func FindWndScale(wnd seqnum.Size) int {
|
||||
@@ -181,7 +200,7 @@ func (h *handshake) effectiveRcvWndScale() uint8 {
|
||||
|
||||
// resetToSynRcvd resets the state of the handshake object to the SYN-RCVD
|
||||
// state.
|
||||
func (h *handshake) resetToSynRcvd(iss seqnum.Value, irs seqnum.Value, opts *header.TCPSynOptions) {
|
||||
func (h *handshake) resetToSynRcvd(iss seqnum.Value, irs seqnum.Value, opts *header.TCPSynOptions, deferAccept time.Duration) {
|
||||
h.active = false
|
||||
h.state = handshakeSynRcvd
|
||||
h.flags = header.TCPFlagSyn | header.TCPFlagAck
|
||||
@@ -189,6 +208,7 @@ func (h *handshake) resetToSynRcvd(iss seqnum.Value, irs seqnum.Value, opts *hea
|
||||
h.ackNum = irs + 1
|
||||
h.mss = opts.MSS
|
||||
h.sndWndScale = opts.WS
|
||||
h.deferAccept = deferAccept
|
||||
h.ep.mu.Lock()
|
||||
h.ep.setEndpointState(StateSynRecv)
|
||||
h.ep.mu.Unlock()
|
||||
@@ -352,6 +372,14 @@ func (h *handshake) synRcvdState(s *segment) *tcpip.Error {
|
||||
// We have previously received (and acknowledged) the peer's SYN. If the
|
||||
// peer acknowledges our SYN, the handshake is completed.
|
||||
if s.flagIsSet(header.TCPFlagAck) {
|
||||
// If deferAccept is not zero and this is a bare ACK and the
|
||||
// timeout is not hit then drop the ACK.
|
||||
if h.deferAccept != 0 && s.data.Size() == 0 && time.Since(h.startTime) < h.deferAccept {
|
||||
h.acked = true
|
||||
h.ep.stack.Stats().DroppedPackets.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the timestamp option is negotiated and the segment does
|
||||
// not carry a timestamp option then the segment must be dropped
|
||||
// as per https://tools.ietf.org/html/rfc7323#section-3.2.
|
||||
@@ -365,10 +393,16 @@ func (h *handshake) synRcvdState(s *segment) *tcpip.Error {
|
||||
h.ep.updateRecentTimestamp(s.parsedOptions.TSVal, h.ackNum, s.sequenceNumber)
|
||||
}
|
||||
h.state = handshakeCompleted
|
||||
|
||||
h.ep.mu.Lock()
|
||||
h.ep.transitionToStateEstablishedLocked(h)
|
||||
// If the segment has data then requeue it for the receiver
|
||||
// to process it again once main loop is started.
|
||||
if s.data.Size() > 0 {
|
||||
s.incRef()
|
||||
h.ep.enqueueSegment(s)
|
||||
}
|
||||
h.ep.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -471,6 +505,7 @@ func (h *handshake) execute() *tcpip.Error {
|
||||
}
|
||||
}
|
||||
|
||||
h.startTime = time.Now()
|
||||
// Initialize the resend timer.
|
||||
resendWaker := sleep.Waker{}
|
||||
timeOut := time.Duration(time.Second)
|
||||
@@ -524,11 +559,21 @@ func (h *handshake) execute() *tcpip.Error {
|
||||
switch index, _ := s.Fetch(true); index {
|
||||
case wakerForResend:
|
||||
timeOut *= 2
|
||||
if timeOut > 60*time.Second {
|
||||
if timeOut > MaxRTO {
|
||||
return tcpip.ErrTimeout
|
||||
}
|
||||
rt.Reset(timeOut)
|
||||
h.ep.sendSynTCP(&h.ep.route, h.ep.ID, h.ep.ttl, h.ep.sendTOS, h.flags, h.iss, h.ackNum, h.rcvWnd, synOpts)
|
||||
// Resend the SYN/SYN-ACK only if the following conditions hold.
|
||||
// - It's an active handshake (deferAccept does not apply)
|
||||
// - It's a passive handshake and we have not yet got the final-ACK.
|
||||
// - It's a passive handshake and we got an ACK but deferAccept is
|
||||
// enabled and we are now past the deferAccept duration.
|
||||
// The last is required to provide a way for the peer to complete
|
||||
// the connection with another ACK or data (as ACKs are never
|
||||
// retransmitted on their own).
|
||||
if h.active || !h.acked || h.deferAccept != 0 && time.Since(h.startTime) > h.deferAccept {
|
||||
h.ep.sendSynTCP(&h.ep.route, h.ep.ID, h.ep.ttl, h.ep.sendTOS, h.flags, h.iss, h.ackNum, h.rcvWnd, synOpts)
|
||||
}
|
||||
|
||||
case wakerForNotification:
|
||||
n := h.ep.fetchNotifications()
|
||||
|
||||
@@ -498,6 +498,13 @@ type endpoint struct {
|
||||
// without any data being acked.
|
||||
userTimeout time.Duration
|
||||
|
||||
// deferAccept if non-zero specifies a user specified time during
|
||||
// which the final ACK of a handshake will be dropped provided the
|
||||
// ACK is a bare ACK and carries no data. If the timeout is crossed then
|
||||
// the bare ACK is accepted and the connection is delivered to the
|
||||
// listener.
|
||||
deferAccept time.Duration
|
||||
|
||||
// pendingAccepted is a synchronization primitive used to track number
|
||||
// of connections that are queued up to be delivered to the accepted
|
||||
// channel. We use this to ensure that all goroutines blocked on writing
|
||||
@@ -1574,6 +1581,15 @@ func (e *endpoint) SetSockOpt(opt interface{}) *tcpip.Error {
|
||||
e.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case tcpip.TCPDeferAcceptOption:
|
||||
e.mu.Lock()
|
||||
if time.Duration(v) > MaxRTO {
|
||||
v = tcpip.TCPDeferAcceptOption(MaxRTO)
|
||||
}
|
||||
e.deferAccept = time.Duration(v)
|
||||
e.mu.Unlock()
|
||||
return nil
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -1798,6 +1814,12 @@ func (e *endpoint) GetSockOpt(opt interface{}) *tcpip.Error {
|
||||
e.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.TCPDeferAcceptOption:
|
||||
e.mu.Lock()
|
||||
*o = tcpip.TCPDeferAcceptOption(e.deferAccept)
|
||||
e.mu.Unlock()
|
||||
return nil
|
||||
|
||||
default:
|
||||
return tcpip.ErrUnknownProtocolOption
|
||||
}
|
||||
@@ -2025,8 +2047,14 @@ func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) *tcpip.Error {
|
||||
// work mutex is available.
|
||||
if e.workMu.TryLock() {
|
||||
e.mu.Lock()
|
||||
e.resetConnectionLocked(tcpip.ErrConnectionAborted)
|
||||
e.notifyProtocolGoroutine(notifyTickleWorker)
|
||||
// We need to double check here to make
|
||||
// sure worker has not transitioned the
|
||||
// endpoint out of a connected state
|
||||
// before trying to send a reset.
|
||||
if e.EndpointState().connected() {
|
||||
e.resetConnectionLocked(tcpip.ErrConnectionAborted)
|
||||
e.notifyProtocolGoroutine(notifyTickleWorker)
|
||||
}
|
||||
e.mu.Unlock()
|
||||
e.workMu.Unlock()
|
||||
} else {
|
||||
@@ -2149,9 +2177,8 @@ func (e *endpoint) listen(backlog int) *tcpip.Error {
|
||||
|
||||
// startAcceptedLoop sets up required state and starts a goroutine with the
|
||||
// main loop for accepted connections.
|
||||
func (e *endpoint) startAcceptedLoop(waiterQueue *waiter.Queue) {
|
||||
func (e *endpoint) startAcceptedLoop() {
|
||||
e.mu.Lock()
|
||||
e.waiterQueue = waiterQueue
|
||||
e.workerRunning = true
|
||||
e.mu.Unlock()
|
||||
wakerInitDone := make(chan struct{})
|
||||
@@ -2177,7 +2204,6 @@ func (e *endpoint) Accept() (tcpip.Endpoint, *waiter.Queue, *tcpip.Error) {
|
||||
default:
|
||||
return nil, nil, tcpip.ErrWouldBlock
|
||||
}
|
||||
|
||||
return n, n.waiterQueue, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -157,13 +157,13 @@ func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint,
|
||||
TSVal: r.synOptions.TSVal,
|
||||
TSEcr: r.synOptions.TSEcr,
|
||||
SACKPermitted: r.synOptions.SACKPermitted,
|
||||
})
|
||||
}, queue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Start the protocol goroutine.
|
||||
ep.startAcceptedLoop(queue)
|
||||
ep.startAcceptedLoop()
|
||||
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/checker"
|
||||
@@ -6787,3 +6788,183 @@ func TestIncreaseWindowOnBufferResize(t *testing.T) {
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func TestTCPDeferAccept(t *testing.T) {
|
||||
c := context.New(t, defaultMTU)
|
||||
defer c.Cleanup()
|
||||
|
||||
c.Create(-1)
|
||||
|
||||
if err := c.EP.Bind(tcpip.FullAddress{Port: context.StackPort}); err != nil {
|
||||
t.Fatal("Bind failed:", err)
|
||||
}
|
||||
|
||||
if err := c.EP.Listen(10); err != nil {
|
||||
t.Fatal("Listen failed:", err)
|
||||
}
|
||||
|
||||
const tcpDeferAccept = 1 * time.Second
|
||||
if err := c.EP.SetSockOpt(tcpip.TCPDeferAcceptOption(tcpDeferAccept)); err != nil {
|
||||
t.Fatalf("c.EP.SetSockOpt(TCPDeferAcceptOption(%s) failed: %v", tcpDeferAccept, err)
|
||||
}
|
||||
|
||||
irs, iss := executeHandshake(t, c, context.TestPort, false /* synCookiesInUse */)
|
||||
|
||||
if _, _, err := c.EP.Accept(); err != tcpip.ErrWouldBlock {
|
||||
t.Fatalf("c.EP.Accept() returned unexpected error got: %v, want: %s", err, tcpip.ErrWouldBlock)
|
||||
}
|
||||
|
||||
// Send data. This should result in an acceptable endpoint.
|
||||
c.SendPacket([]byte{1, 2, 3, 4}, &context.Headers{
|
||||
SrcPort: context.TestPort,
|
||||
DstPort: context.StackPort,
|
||||
Flags: header.TCPFlagAck,
|
||||
SeqNum: irs + 1,
|
||||
AckNum: iss + 1,
|
||||
})
|
||||
|
||||
// Receive ACK for the data we sent.
|
||||
checker.IPv4(t, c.GetPacket(), checker.TCP(
|
||||
checker.DstPort(context.TestPort),
|
||||
checker.TCPFlags(header.TCPFlagAck),
|
||||
checker.SeqNum(uint32(iss+1)),
|
||||
checker.AckNum(uint32(irs+5))))
|
||||
|
||||
// Give a bit of time for the socket to be delivered to the accept queue.
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
aep, _, err := c.EP.Accept()
|
||||
if err != nil {
|
||||
t.Fatalf("c.EP.Accept() returned unexpected error got: %v, want: nil", err)
|
||||
}
|
||||
|
||||
aep.Close()
|
||||
// Closing aep without reading the data should trigger a RST.
|
||||
checker.IPv4(t, c.GetPacket(), checker.TCP(
|
||||
checker.DstPort(context.TestPort),
|
||||
checker.TCPFlags(header.TCPFlagRst|header.TCPFlagAck),
|
||||
checker.SeqNum(uint32(iss+1)),
|
||||
checker.AckNum(uint32(irs+5))))
|
||||
}
|
||||
|
||||
func TestTCPDeferAcceptTimeout(t *testing.T) {
|
||||
c := context.New(t, defaultMTU)
|
||||
defer c.Cleanup()
|
||||
|
||||
c.Create(-1)
|
||||
|
||||
if err := c.EP.Bind(tcpip.FullAddress{Port: context.StackPort}); err != nil {
|
||||
t.Fatal("Bind failed:", err)
|
||||
}
|
||||
|
||||
if err := c.EP.Listen(10); err != nil {
|
||||
t.Fatal("Listen failed:", err)
|
||||
}
|
||||
|
||||
const tcpDeferAccept = 1 * time.Second
|
||||
if err := c.EP.SetSockOpt(tcpip.TCPDeferAcceptOption(tcpDeferAccept)); err != nil {
|
||||
t.Fatalf("c.EP.SetSockOpt(TCPDeferAcceptOption(%s) failed: %v", tcpDeferAccept, err)
|
||||
}
|
||||
|
||||
irs, iss := executeHandshake(t, c, context.TestPort, false /* synCookiesInUse */)
|
||||
|
||||
if _, _, err := c.EP.Accept(); err != tcpip.ErrWouldBlock {
|
||||
t.Fatalf("c.EP.Accept() returned unexpected error got: %v, want: %s", err, tcpip.ErrWouldBlock)
|
||||
}
|
||||
|
||||
// Sleep for a little of the tcpDeferAccept timeout.
|
||||
time.Sleep(tcpDeferAccept + 100*time.Millisecond)
|
||||
|
||||
// On timeout expiry we should get a SYN-ACK retransmission.
|
||||
checker.IPv4(t, c.GetPacket(), checker.TCP(
|
||||
checker.SrcPort(context.StackPort),
|
||||
checker.DstPort(context.TestPort),
|
||||
checker.TCPFlags(header.TCPFlagAck|header.TCPFlagSyn),
|
||||
checker.AckNum(uint32(irs)+1)))
|
||||
|
||||
// Send data. This should result in an acceptable endpoint.
|
||||
c.SendPacket([]byte{1, 2, 3, 4}, &context.Headers{
|
||||
SrcPort: context.TestPort,
|
||||
DstPort: context.StackPort,
|
||||
Flags: header.TCPFlagAck,
|
||||
SeqNum: irs + 1,
|
||||
AckNum: iss + 1,
|
||||
})
|
||||
|
||||
// Receive ACK for the data we sent.
|
||||
checker.IPv4(t, c.GetPacket(), checker.TCP(
|
||||
checker.SrcPort(context.StackPort),
|
||||
checker.DstPort(context.TestPort),
|
||||
checker.TCPFlags(header.TCPFlagAck),
|
||||
checker.SeqNum(uint32(iss+1)),
|
||||
checker.AckNum(uint32(irs+5))))
|
||||
|
||||
// Give sometime for the endpoint to be delivered to the accept queue.
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
aep, _, err := c.EP.Accept()
|
||||
if err != nil {
|
||||
t.Fatalf("c.EP.Accept() returned unexpected error got: %v, want: nil", err)
|
||||
}
|
||||
|
||||
aep.Close()
|
||||
// Closing aep without reading the data should trigger a RST.
|
||||
checker.IPv4(t, c.GetPacket(), checker.TCP(
|
||||
checker.SrcPort(context.StackPort),
|
||||
checker.DstPort(context.TestPort),
|
||||
checker.TCPFlags(header.TCPFlagRst|header.TCPFlagAck),
|
||||
checker.SeqNum(uint32(iss+1)),
|
||||
checker.AckNum(uint32(irs+5))))
|
||||
}
|
||||
|
||||
func TestResetDuringClose(t *testing.T) {
|
||||
c := context.New(t, defaultMTU)
|
||||
defer c.Cleanup()
|
||||
|
||||
iss := seqnum.Value(789)
|
||||
c.CreateConnected(iss, 30000, -1 /* epRecvBuf */)
|
||||
// Send some data to make sure there is some unread
|
||||
// data to trigger a reset on c.Close.
|
||||
irs := c.IRS
|
||||
c.SendPacket([]byte{1, 2, 3, 4}, &context.Headers{
|
||||
SrcPort: context.TestPort,
|
||||
DstPort: c.Port,
|
||||
Flags: header.TCPFlagAck,
|
||||
SeqNum: iss.Add(1),
|
||||
AckNum: irs.Add(1),
|
||||
RcvWnd: 30000,
|
||||
})
|
||||
|
||||
// Receive ACK for the data we sent.
|
||||
checker.IPv4(t, c.GetPacket(), checker.TCP(
|
||||
checker.DstPort(context.TestPort),
|
||||
checker.TCPFlags(header.TCPFlagAck),
|
||||
checker.SeqNum(uint32(irs.Add(1))),
|
||||
checker.AckNum(uint32(iss.Add(5)))))
|
||||
|
||||
// Close in a separate goroutine so that we can trigger
|
||||
// a race with the RST we send below. This should not
|
||||
// panic due to the route being released depeding on
|
||||
// whether Close() sends an active RST or the RST sent
|
||||
// below is processed by the worker first.
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
c.SendPacket(nil, &context.Headers{
|
||||
SrcPort: context.TestPort,
|
||||
DstPort: c.Port,
|
||||
SeqNum: iss.Add(5),
|
||||
AckNum: c.IRS.Add(5),
|
||||
RcvWnd: 30000,
|
||||
Flags: header.TCPFlagRst,
|
||||
})
|
||||
}()
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
c.EP.Close()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ source $(dirname $0)/common.sh
|
||||
install_runsc_for_test iptables
|
||||
|
||||
# Build the docker image for the test.
|
||||
run //test/iptables/runner --norun
|
||||
run //test/iptables/runner-image --norun
|
||||
|
||||
# TODO(gvisor.dev/issue/170): Also test this on runsc once iptables are better
|
||||
# supported
|
||||
test //test/iptables:iptables_test "--test_arg=--runtime=runc" \
|
||||
"--test_arg=--image=bazel/test/iptables/runner:runner"
|
||||
"--test_arg=--image=bazel/test/iptables/runner:runner-image"
|
||||
|
||||
@@ -28,7 +28,7 @@ Your test is now runnable with bazel!
|
||||
Build the testing Docker container:
|
||||
|
||||
```bash
|
||||
$ bazel run //test/iptables/runner -- --norun
|
||||
$ bazel run //test/iptables/runner-image -- --norun
|
||||
```
|
||||
|
||||
Run an individual test via:
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
|
||||
const timeout = 18 * time.Second
|
||||
|
||||
var image = flag.String("image", "bazel/test/iptables/runner:runner", "image to run tests in")
|
||||
var image = flag.String("image", "bazel/test/iptables/runner:runner-image", "image to run tests in")
|
||||
|
||||
type result struct {
|
||||
output string
|
||||
|
||||
@@ -754,6 +754,7 @@ cc_binary(
|
||||
":socket_test_util",
|
||||
"//test/util:cleanup",
|
||||
"//test/util:eventfd_util",
|
||||
"//test/util:fs_util",
|
||||
"//test/util:multiprocess_util",
|
||||
"//test/util:posix_error",
|
||||
"//test/util:save_util",
|
||||
@@ -1391,6 +1392,7 @@ cc_binary(
|
||||
linkstatic = 1,
|
||||
deps = [
|
||||
"//test/util:file_descriptor",
|
||||
"//test/util:fs_util",
|
||||
"//test/util:posix_error",
|
||||
"//test/util:temp_path",
|
||||
"//test/util:test_main",
|
||||
@@ -2173,6 +2175,7 @@ cc_library(
|
||||
":socket_test_util",
|
||||
"//test/util:test_util",
|
||||
"//test/util:thread_util",
|
||||
"@com_google_absl//absl/memory",
|
||||
"@com_google_absl//absl/time",
|
||||
"@com_google_googletest//:gtest",
|
||||
],
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "test/syscalls/linux/socket_test_util.h"
|
||||
#include "test/util/cleanup.h"
|
||||
#include "test/util/eventfd_util.h"
|
||||
#include "test/util/fs_util.h"
|
||||
#include "test/util/multiprocess_util.h"
|
||||
#include "test/util/posix_error.h"
|
||||
#include "test/util/save_util.h"
|
||||
@@ -55,10 +56,6 @@ ABSL_FLAG(int32_t, socket_fd, -1,
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
// O_LARGEFILE as defined by Linux. glibc tries to be clever by setting it to 0
|
||||
// because "it isn't needed", even though Linux can return it via F_GETFL.
|
||||
constexpr int kOLargeFile = 00100000;
|
||||
|
||||
class FcntlLockTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user