mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
netstack: move SO_SNDBUF/RCVBUF clamping logic out of //pkg/tcpip
- Keeps Linux-specific behavior out of //pkg/tcpip - Makes it clearer that clamping is done only for setsockopt calls from users - Removes code duplication PiperOrigin-RevId: 384389809
This commit is contained in:
committed by
gVisor bot
parent
520795aaad
commit
e35d20f79c
@@ -1682,6 +1682,26 @@ func SetSockOpt(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, level int
|
||||
return nil
|
||||
}
|
||||
|
||||
func clampBufSize(newSz, min, max int64) int64 {
|
||||
// packetOverheadFactor is used to multiply the value provided by the user on
|
||||
// a setsockopt(2) for setting the send/receive buffer sizes sockets.
|
||||
const packetOverheadFactor = 2
|
||||
|
||||
if newSz > max {
|
||||
newSz = max
|
||||
}
|
||||
|
||||
if newSz < math.MaxInt32/packetOverheadFactor {
|
||||
newSz *= packetOverheadFactor
|
||||
if newSz < min {
|
||||
newSz = min
|
||||
}
|
||||
} else {
|
||||
newSz = math.MaxInt32
|
||||
}
|
||||
return newSz
|
||||
}
|
||||
|
||||
// setSockOptSocket implements SetSockOpt when level is SOL_SOCKET.
|
||||
func setSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name int, optVal []byte) *syserr.Error {
|
||||
switch name {
|
||||
@@ -1691,7 +1711,9 @@ func setSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, nam
|
||||
}
|
||||
|
||||
v := hostarch.ByteOrder.Uint32(optVal)
|
||||
ep.SocketOptions().SetSendBufferSize(int64(v), true /* notify */)
|
||||
min, max := ep.SocketOptions().SendBufferLimits()
|
||||
clamped := clampBufSize(int64(v), min, max)
|
||||
ep.SocketOptions().SetSendBufferSize(clamped, true /* notify */)
|
||||
return nil
|
||||
|
||||
case linux.SO_RCVBUF:
|
||||
@@ -1700,7 +1722,9 @@ func setSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, nam
|
||||
}
|
||||
|
||||
v := hostarch.ByteOrder.Uint32(optVal)
|
||||
ep.SocketOptions().SetReceiveBufferSize(int64(v), true /* notify */)
|
||||
min, max := ep.SocketOptions().ReceiveBufferLimits()
|
||||
clamped := clampBufSize(int64(v), min, max)
|
||||
ep.SocketOptions().SetReceiveBufferSize(clamped, true /* notify */)
|
||||
return nil
|
||||
|
||||
case linux.SO_REUSEADDR:
|
||||
|
||||
+23
-64
@@ -15,17 +15,12 @@
|
||||
package tcpip
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/atomicbitops"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
)
|
||||
|
||||
// PacketOverheadFactor is used to multiply the value provided by the user on a
|
||||
// SetSockOpt for setting the send/receive buffer sizes sockets.
|
||||
const PacketOverheadFactor = 2
|
||||
|
||||
// SocketOptionsHandler holds methods that help define endpoint specific
|
||||
// behavior for socket level socket options. These must be implemented by
|
||||
// endpoints to get notified when socket level options are set.
|
||||
@@ -617,39 +612,20 @@ func (so *SocketOptions) GetSendBufferSize() int64 {
|
||||
return so.sendBufferSize.Load()
|
||||
}
|
||||
|
||||
// SendBufferLimits returns the [min, max) range of allowable send buffer
|
||||
// sizes.
|
||||
func (so *SocketOptions) SendBufferLimits() (min, max int64) {
|
||||
limits := so.getSendBufferLimits(so.stackHandler)
|
||||
return int64(limits.Min), int64(limits.Max)
|
||||
}
|
||||
|
||||
// SetSendBufferSize sets value for SO_SNDBUF option. notify indicates if the
|
||||
// stack handler should be invoked to set the send buffer size.
|
||||
func (so *SocketOptions) SetSendBufferSize(sendBufferSize int64, notify bool) {
|
||||
v := sendBufferSize
|
||||
|
||||
if !notify {
|
||||
so.sendBufferSize.Store(v)
|
||||
return
|
||||
if notify {
|
||||
sendBufferSize = so.handler.OnSetSendBufferSize(sendBufferSize)
|
||||
}
|
||||
|
||||
// Make sure the send buffer size is within the min and max
|
||||
// allowed.
|
||||
ss := so.getSendBufferLimits(so.stackHandler)
|
||||
min := int64(ss.Min)
|
||||
max := int64(ss.Max)
|
||||
// Validate the send buffer size with min and max values.
|
||||
// Multiply it by factor of 2.
|
||||
if v > max {
|
||||
v = max
|
||||
}
|
||||
|
||||
if v < math.MaxInt32/PacketOverheadFactor {
|
||||
v *= PacketOverheadFactor
|
||||
if v < min {
|
||||
v = min
|
||||
}
|
||||
} else {
|
||||
v = math.MaxInt32
|
||||
}
|
||||
|
||||
// Notify endpoint about change in buffer size.
|
||||
newSz := so.handler.OnSetSendBufferSize(v)
|
||||
so.sendBufferSize.Store(newSz)
|
||||
so.sendBufferSize.Store(sendBufferSize)
|
||||
}
|
||||
|
||||
// GetReceiveBufferSize gets value for SO_RCVBUF option.
|
||||
@@ -657,36 +633,19 @@ func (so *SocketOptions) GetReceiveBufferSize() int64 {
|
||||
return so.receiveBufferSize.Load()
|
||||
}
|
||||
|
||||
// SetReceiveBufferSize sets value for SO_RCVBUF option.
|
||||
// ReceiveBufferLimits returns the [min, max) range of allowable receive buffer
|
||||
// sizes.
|
||||
func (so *SocketOptions) ReceiveBufferLimits() (min, max int64) {
|
||||
limits := so.getReceiveBufferLimits(so.stackHandler)
|
||||
return int64(limits.Min), int64(limits.Max)
|
||||
}
|
||||
|
||||
// SetReceiveBufferSize sets the value of the SO_RCVBUF option, optionally
|
||||
// notifying the owning endpoint.
|
||||
func (so *SocketOptions) SetReceiveBufferSize(receiveBufferSize int64, notify bool) {
|
||||
if !notify {
|
||||
so.receiveBufferSize.Store(receiveBufferSize)
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure the send buffer size is within the min and max
|
||||
// allowed.
|
||||
v := receiveBufferSize
|
||||
ss := so.getReceiveBufferLimits(so.stackHandler)
|
||||
min := int64(ss.Min)
|
||||
max := int64(ss.Max)
|
||||
// Validate the send buffer size with min and max values.
|
||||
if v > max {
|
||||
v = max
|
||||
if notify {
|
||||
oldSz := so.receiveBufferSize.Load()
|
||||
receiveBufferSize = so.handler.OnSetReceiveBufferSize(receiveBufferSize, oldSz)
|
||||
}
|
||||
|
||||
// Multiply it by factor of 2.
|
||||
if v < math.MaxInt32/PacketOverheadFactor {
|
||||
v *= PacketOverheadFactor
|
||||
if v < min {
|
||||
v = min
|
||||
}
|
||||
} else {
|
||||
v = math.MaxInt32
|
||||
}
|
||||
|
||||
oldSz := so.receiveBufferSize.Load()
|
||||
// Notify endpoint about change in buffer size.
|
||||
newSz := so.handler.OnSetReceiveBufferSize(v, oldSz)
|
||||
so.receiveBufferSize.Store(newSz)
|
||||
so.receiveBufferSize.Store(receiveBufferSize)
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ func newEndpoint(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProt
|
||||
// headers included. Because they're write-only, We don't need to
|
||||
// register with the stack.
|
||||
if !associated {
|
||||
e.ops.SetReceiveBufferSize(0, false)
|
||||
e.ops.SetReceiveBufferSize(0, false /* notify */)
|
||||
e.waiterQueue = nil
|
||||
return e, nil
|
||||
}
|
||||
|
||||
@@ -2147,7 +2147,7 @@ func TestSmallSegReceiveWindowAdvertisement(t *testing.T) {
|
||||
|
||||
// Bump up the receive buffer size such that, when the receive window grows,
|
||||
// the scaled window exceeds maxUint16.
|
||||
c.EP.SocketOptions().SetReceiveBufferSize(int64(opt.Max), true)
|
||||
c.EP.SocketOptions().SetReceiveBufferSize(int64(opt.Max)*2, true /* notify */)
|
||||
|
||||
// Keep the payload size < segment overhead and such that it is a multiple
|
||||
// of the window scaled value. This enables the test to perform equality
|
||||
@@ -2267,7 +2267,7 @@ func TestNoWindowShrinking(t *testing.T) {
|
||||
initialWnd := header.TCP(header.IPv4(pkt).Payload()).WindowSize() << c.RcvdWindowScale
|
||||
initialLastAcceptableSeq := iss.Add(seqnum.Size(initialWnd))
|
||||
// Now shrink the receive buffer to half its original size.
|
||||
c.EP.SocketOptions().SetReceiveBufferSize(int64(rcvBufSize/2), true)
|
||||
c.EP.SocketOptions().SetReceiveBufferSize(int64(rcvBufSize), true /* notify */)
|
||||
|
||||
data := generateRandomPayload(t, rcvBufSize)
|
||||
// Send a payload of half the size of rcvBufSize.
|
||||
@@ -2523,7 +2523,7 @@ func TestScaledWindowAccept(t *testing.T) {
|
||||
defer ep.Close()
|
||||
|
||||
// Set the window size greater than the maximum non-scaled window.
|
||||
ep.SocketOptions().SetReceiveBufferSize(65535*3, true)
|
||||
ep.SocketOptions().SetReceiveBufferSize(65535*6, true /* notify */)
|
||||
|
||||
if err := ep.Bind(tcpip.FullAddress{Port: context.StackPort}); err != nil {
|
||||
t.Fatalf("Bind failed: %s", err)
|
||||
@@ -2595,7 +2595,7 @@ func TestNonScaledWindowAccept(t *testing.T) {
|
||||
defer ep.Close()
|
||||
|
||||
// Set the window size greater than the maximum non-scaled window.
|
||||
ep.SocketOptions().SetReceiveBufferSize(65535*3, true)
|
||||
ep.SocketOptions().SetReceiveBufferSize(65535*6, true /* notify */)
|
||||
|
||||
if err := ep.Bind(tcpip.FullAddress{Port: context.StackPort}); err != nil {
|
||||
t.Fatalf("Bind failed: %s", err)
|
||||
@@ -3188,7 +3188,7 @@ func TestPassiveSendMSSLessThanMTU(t *testing.T) {
|
||||
// Set the buffer size to a deterministic size so that we can check the
|
||||
// window scaling option.
|
||||
const rcvBufferSize = 0x20000
|
||||
ep.SocketOptions().SetReceiveBufferSize(rcvBufferSize, true)
|
||||
ep.SocketOptions().SetReceiveBufferSize(rcvBufferSize*2, true /* notify */)
|
||||
|
||||
if err := ep.Bind(tcpip.FullAddress{Port: context.StackPort}); err != nil {
|
||||
t.Fatalf("Bind failed: %s", err)
|
||||
@@ -3327,7 +3327,7 @@ func TestSynOptionsOnActiveConnect(t *testing.T) {
|
||||
// window scaling option.
|
||||
const rcvBufferSize = 0x20000
|
||||
const wndScale = 3
|
||||
c.EP.SocketOptions().SetReceiveBufferSize(rcvBufferSize, true)
|
||||
c.EP.SocketOptions().SetReceiveBufferSize(rcvBufferSize*2, true /* notify */)
|
||||
|
||||
// Start connection attempt.
|
||||
we, ch := waiter.NewChannelEntry(nil)
|
||||
@@ -4669,52 +4669,6 @@ func TestDefaultBufferSizes(t *testing.T) {
|
||||
checkRecvBufferSize(t, ep, tcp.DefaultReceiveBufferSize*3)
|
||||
}
|
||||
|
||||
func TestMinMaxBufferSizes(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol},
|
||||
})
|
||||
|
||||
// Check the default values.
|
||||
ep, err := s.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &waiter.Queue{})
|
||||
if err != nil {
|
||||
t.Fatalf("NewEndpoint failed; %s", err)
|
||||
}
|
||||
defer ep.Close()
|
||||
|
||||
// Change the min/max values for send/receive
|
||||
{
|
||||
opt := tcpip.TCPReceiveBufferSizeRangeOption{Min: 200, Default: tcp.DefaultReceiveBufferSize * 2, Max: tcp.DefaultReceiveBufferSize * 20}
|
||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
||||
t.Fatalf("SetTransportProtocolOption(%d, &%#v): %s", tcp.ProtocolNumber, opt, err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
opt := tcpip.TCPSendBufferSizeRangeOption{Min: 300, Default: tcp.DefaultSendBufferSize * 3, Max: tcp.DefaultSendBufferSize * 30}
|
||||
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
|
||||
t.Fatalf("SetTransportProtocolOption(%d, &%#v): %s", tcp.ProtocolNumber, opt, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set values below the min/2.
|
||||
ep.SocketOptions().SetReceiveBufferSize(99, true)
|
||||
checkRecvBufferSize(t, ep, 200)
|
||||
|
||||
ep.SocketOptions().SetSendBufferSize(149, true)
|
||||
|
||||
checkSendBufferSize(t, ep, 300)
|
||||
|
||||
// Set values above the max.
|
||||
ep.SocketOptions().SetReceiveBufferSize(1+tcp.DefaultReceiveBufferSize*20, true)
|
||||
// Values above max are capped at max and then doubled.
|
||||
checkRecvBufferSize(t, ep, tcp.DefaultReceiveBufferSize*20*2)
|
||||
|
||||
ep.SocketOptions().SetSendBufferSize(1+tcp.DefaultSendBufferSize*30, true)
|
||||
// Values above max are capped at max and then doubled.
|
||||
checkSendBufferSize(t, ep, tcp.DefaultSendBufferSize*30*2)
|
||||
}
|
||||
|
||||
func TestBindToDeviceOption(t *testing.T) {
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol},
|
||||
@@ -7752,7 +7706,7 @@ func TestIncreaseWindowOnBufferResize(t *testing.T) {
|
||||
|
||||
// Increasing the buffer from should generate an ACK,
|
||||
// since window grew from small value to larger equal MSS
|
||||
c.EP.SocketOptions().SetReceiveBufferSize(rcvBuf*2, true)
|
||||
c.EP.SocketOptions().SetReceiveBufferSize(rcvBuf*4, true /* notify */)
|
||||
checker.IPv4(t, c.GetPacket(),
|
||||
checker.PayloadLen(header.TCPMinimumSize),
|
||||
checker.TCP(
|
||||
|
||||
@@ -757,7 +757,7 @@ func (c *Context) Create(epRcvBuf int) {
|
||||
}
|
||||
|
||||
if epRcvBuf != -1 {
|
||||
c.EP.SocketOptions().SetReceiveBufferSize(int64(epRcvBuf), true /* notify */)
|
||||
c.EP.SocketOptions().SetReceiveBufferSize(int64(epRcvBuf)*2, true /* notify */)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user