mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
make PMTUD on by default and settable via sockopt
We've supported PMTUD for a long time and just never turned it on. Addresses #10344. PiperOrigin-RevId: 634003508
This commit is contained in:
committed by
gVisor bot
parent
2621d25b52
commit
e367e0b134
@@ -63,6 +63,7 @@ var SockOpts = []SockOpt{
|
||||
{linux.SOL_IP, linux.IP_MULTICAST_IF, 0 /* kernel allows multiple structures to be passed */, true, true},
|
||||
{linux.SOL_IP, linux.IP_MULTICAST_LOOP, 0 /* can be 32-bit int or 8-bit uint */, true, true},
|
||||
{linux.SOL_IP, linux.IP_MULTICAST_TTL, 0 /* can be 32-bit int or 8-bit uint */, true, true},
|
||||
{linux.SOL_IP, linux.IP_MTU_DISCOVER, 0 /* can be 32-bit int or 8-bit uint */, true, true},
|
||||
{linux.SOL_IP, linux.IP_PKTINFO, sizeofInt32, true, true},
|
||||
{linux.SOL_IP, linux.IP_RECVERR, sizeofInt32, true, true},
|
||||
{linux.SOL_IP, linux.IP_RECVORIGDSTADDR, sizeofInt32, true, true},
|
||||
|
||||
@@ -1762,6 +1762,30 @@ func getSockOptIP(t *kernel.Task, s socket.Socket, ep commonEndpoint, name int,
|
||||
return nil, err
|
||||
}
|
||||
return &ret, nil
|
||||
|
||||
case linux.IP_MTU_DISCOVER:
|
||||
if outLen < sizeOfInt32 {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
v, err := ep.GetSockOptInt(tcpip.MTUDiscoverOption)
|
||||
if err != nil {
|
||||
return nil, syserr.TranslateNetstackError(err)
|
||||
}
|
||||
switch tcpip.PMTUDStrategy(v) {
|
||||
case tcpip.PMTUDiscoveryWant:
|
||||
v = linux.IP_PMTUDISC_WANT
|
||||
case tcpip.PMTUDiscoveryDont:
|
||||
v = linux.IP_PMTUDISC_DONT
|
||||
case tcpip.PMTUDiscoveryDo:
|
||||
v = linux.IP_PMTUDISC_DO
|
||||
case tcpip.PMTUDiscoveryProbe:
|
||||
v = linux.IP_PMTUDISC_PROBE
|
||||
default:
|
||||
panic(fmt.Errorf("unknown PMTUD option: %d", v))
|
||||
}
|
||||
vP := primitive.Int32(v)
|
||||
return &vP, nil
|
||||
}
|
||||
return nil, syserr.ErrProtocolNotAvailable
|
||||
}
|
||||
@@ -2576,6 +2600,28 @@ func setSockOptIP(t *kernel.Task, s socket.Socket, ep commonEndpoint, name int,
|
||||
log.Infof("IPT_SO_SET_ADD_COUNTERS is not supported")
|
||||
return nil
|
||||
|
||||
case linux.IP_MTU_DISCOVER:
|
||||
if len(optVal) == 0 {
|
||||
return nil
|
||||
}
|
||||
v, err := parseIntOrChar(optVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch v {
|
||||
case linux.IP_PMTUDISC_DONT:
|
||||
v = int32(tcpip.PMTUDiscoveryDont)
|
||||
case linux.IP_PMTUDISC_WANT:
|
||||
v = int32(tcpip.PMTUDiscoveryWant)
|
||||
case linux.IP_PMTUDISC_DO:
|
||||
v = int32(tcpip.PMTUDiscoveryDo)
|
||||
case linux.IP_PMTUDISC_PROBE:
|
||||
v = int32(tcpip.PMTUDiscoveryProbe)
|
||||
default:
|
||||
return syserr.ErrNotSupported
|
||||
}
|
||||
return syserr.TranslateNetstackError(ep.SetSockOptInt(tcpip.MTUDiscoverOption, int(v)))
|
||||
|
||||
case linux.IP_ADD_SOURCE_MEMBERSHIP,
|
||||
linux.IP_BIND_ADDRESS_NO_PORT,
|
||||
linux.IP_BLOCK_SOURCE,
|
||||
@@ -2585,7 +2631,6 @@ func setSockOptIP(t *kernel.Task, s socket.Socket, ep commonEndpoint, name int,
|
||||
linux.IP_IPSEC_POLICY,
|
||||
linux.IP_MINTTL,
|
||||
linux.IP_MSFILTER,
|
||||
linux.IP_MTU_DISCOVER,
|
||||
linux.IP_MULTICAST_ALL,
|
||||
linux.IP_NODEFRAG,
|
||||
linux.IP_OPTIONS,
|
||||
|
||||
@@ -462,19 +462,26 @@ func (e *endpoint) addIPHeader(srcAddr, dstAddr tcpip.Address, pkt *stack.Packet
|
||||
if length > math.MaxUint16 {
|
||||
return &tcpip.ErrMessageTooLong{}
|
||||
}
|
||||
// RFC 6864 section 4.3 mandates uniqueness of ID values for non-atomic
|
||||
// datagrams. Since the DF bit is never being set here, all datagrams
|
||||
// are non-atomic and need an ID.
|
||||
ipH.Encode(&header.IPv4Fields{
|
||||
|
||||
fields := header.IPv4Fields{
|
||||
TotalLength: uint16(length),
|
||||
ID: e.getID(),
|
||||
TTL: params.TTL,
|
||||
TOS: params.TOS,
|
||||
Protocol: uint8(params.Protocol),
|
||||
SrcAddr: srcAddr,
|
||||
DstAddr: dstAddr,
|
||||
Options: options,
|
||||
})
|
||||
}
|
||||
if params.DF {
|
||||
// Treat want and do the same.
|
||||
fields.Flags = header.IPv4FlagDontFragment
|
||||
} else {
|
||||
// RFC 6864 section 4.3 mandates uniqueness of ID values for
|
||||
// non-atomic datagrams.
|
||||
fields.ID = e.getID()
|
||||
}
|
||||
ipH.Encode(&fields)
|
||||
|
||||
ipH.SetChecksum(^ipH.CalculateChecksum())
|
||||
pkt.NetworkProtocolNumber = ProtocolNumber
|
||||
return nil
|
||||
|
||||
@@ -319,6 +319,9 @@ type NetworkHeaderParams struct {
|
||||
|
||||
// TOS refers to TypeOfService or TrafficClass field of the IP-header.
|
||||
TOS uint8
|
||||
|
||||
// DF indicates whether the DF bit should be set.
|
||||
DF bool
|
||||
}
|
||||
|
||||
// GroupAddressableEndpoint is an endpoint that supports group addressing.
|
||||
|
||||
+4
-1
@@ -995,10 +995,13 @@ const (
|
||||
UseDefaultIPv6HopLimit = -1
|
||||
)
|
||||
|
||||
// PMTUDStrategy is the kind of PMTUD to perform.
|
||||
type PMTUDStrategy int
|
||||
|
||||
const (
|
||||
// PMTUDiscoveryWant is a setting of the MTUDiscoverOption to use
|
||||
// per-route settings.
|
||||
PMTUDiscoveryWant int = iota
|
||||
PMTUDiscoveryWant PMTUDStrategy = iota
|
||||
|
||||
// PMTUDiscoveryDont is a setting of the MTUDiscoverOption to disable
|
||||
// path MTU discovery.
|
||||
|
||||
@@ -824,7 +824,7 @@ func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
|
||||
case tcpip.MTUDiscoverOption:
|
||||
// Return not supported if the value is not disabling path
|
||||
// MTU discovery.
|
||||
if v != tcpip.PMTUDiscoveryDont {
|
||||
if tcpip.PMTUDStrategy(v) != tcpip.PMTUDiscoveryDont {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
@@ -862,7 +862,7 @@ 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 tcpip.PMTUDiscoveryDont, nil
|
||||
return int(tcpip.PMTUDiscoveryDont), nil
|
||||
|
||||
case tcpip.MulticastTTLOption:
|
||||
e.mu.Lock()
|
||||
|
||||
@@ -794,6 +794,7 @@ type tcpFields struct {
|
||||
rcvWnd seqnum.Size
|
||||
opts []byte
|
||||
txHash uint32
|
||||
df bool
|
||||
}
|
||||
|
||||
func (e *Endpoint) sendSynTCP(r *stack.Route, tf tcpFields, opts header.TCPSynOptions) tcpip.Error {
|
||||
@@ -881,7 +882,7 @@ func sendTCPBatch(r *stack.Route, tf tcpFields, pkt *stack.PacketBuffer, gso sta
|
||||
buildTCPHdr(r, tf, pkt, gso)
|
||||
tf.seq = tf.seq.Add(seqnum.Size(packetSize))
|
||||
pkt.GSOOptions = gso
|
||||
if err := r.WritePacket(stack.NetworkHeaderParams{Protocol: ProtocolNumber, TTL: tf.ttl, TOS: tf.tos}, pkt); err != nil {
|
||||
if err := r.WritePacket(stack.NetworkHeaderParams{Protocol: ProtocolNumber, TTL: tf.ttl, TOS: tf.tos, DF: tf.df}, pkt); err != nil {
|
||||
r.Stats().TCP.SegmentSendErrors.Increment()
|
||||
if shouldSplitPacket {
|
||||
pkt.DecRef()
|
||||
@@ -913,7 +914,7 @@ func sendTCP(r *stack.Route, tf tcpFields, pkt *stack.PacketBuffer, gso stack.GS
|
||||
pkt.Owner = owner
|
||||
buildTCPHdr(r, tf, pkt, gso)
|
||||
|
||||
if err := r.WritePacket(stack.NetworkHeaderParams{Protocol: ProtocolNumber, TTL: tf.ttl, TOS: tf.tos}, pkt); err != nil {
|
||||
if err := r.WritePacket(stack.NetworkHeaderParams{Protocol: ProtocolNumber, TTL: tf.ttl, TOS: tf.tos, DF: tf.df}, pkt); err != nil {
|
||||
r.Stats().TCP.SegmentSendErrors.Increment()
|
||||
return err
|
||||
}
|
||||
@@ -964,6 +965,9 @@ 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{})
|
||||
defer pkt.DecRef()
|
||||
@@ -972,6 +976,9 @@ func (e *Endpoint) sendEmptyRaw(flags header.TCPFlags, seq, ack seqnum.Value, rc
|
||||
|
||||
// sendRaw sends a TCP segment to the endpoint's peer. This method takes
|
||||
// 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) {
|
||||
@@ -989,6 +996,7 @@ func (e *Endpoint) sendRaw(pkt *stack.PacketBuffer, flags header.TCPFlags, seq,
|
||||
ack: ack,
|
||||
rcvWnd: rcvWnd,
|
||||
opts: options,
|
||||
df: e.pmtud == tcpip.PMTUDiscoveryWant || e.pmtud == tcpip.PMTUDiscoveryDo,
|
||||
}, pkt, e.gso)
|
||||
}
|
||||
|
||||
|
||||
@@ -600,6 +600,11 @@ type Endpoint struct {
|
||||
//
|
||||
// +checklocks:mu
|
||||
limRdr *io.LimitedReader `state:"nosave"`
|
||||
|
||||
// pmtud is the PMTUD strategy to use.
|
||||
//
|
||||
// +checklocks:mu
|
||||
pmtud tcpip.PMTUDStrategy
|
||||
}
|
||||
|
||||
// UniqueID implements stack.TransportEndpoint.UniqueID.
|
||||
@@ -1890,9 +1895,16 @@ func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
|
||||
e.UnlockUser()
|
||||
|
||||
case tcpip.MTUDiscoverOption:
|
||||
// Return not supported if attempting to set this option to
|
||||
// anything other than path MTU discovery disabled.
|
||||
if v != tcpip.PMTUDiscoveryDont {
|
||||
switch v := tcpip.PMTUDStrategy(v); v {
|
||||
case tcpip.PMTUDiscoveryWant, tcpip.PMTUDiscoveryDont, tcpip.PMTUDiscoveryDo:
|
||||
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{}
|
||||
}
|
||||
|
||||
@@ -2089,9 +2101,10 @@ func (e *Endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
|
||||
return v, nil
|
||||
|
||||
case tcpip.MTUDiscoverOption:
|
||||
// Always return the path MTU discovery disabled setting since
|
||||
// it's the only one supported.
|
||||
return tcpip.PMTUDiscoveryDont, nil
|
||||
e.LockUser()
|
||||
v := e.pmtud
|
||||
e.UnlockUser()
|
||||
return int(v), nil
|
||||
|
||||
case tcpip.ReceiveQueueSizeOption:
|
||||
return e.readyReceiveSize()
|
||||
|
||||
@@ -4245,7 +4245,7 @@ func TestRetransmitIPv4IDUniqueness(t *testing.T) {
|
||||
// have DF=0. This needs to be done because the IPv4 ID uniqueness
|
||||
// applies only to non-atomic IPv4 datagrams as defined in RFC 6864
|
||||
// Section 4, and datagrams with DF=0 are non-atomic.
|
||||
if err := c.EP.SetSockOptInt(tcpip.MTUDiscoverOption, tcpip.PMTUDiscoveryDont); err != nil {
|
||||
if err := c.EP.SetSockOptInt(tcpip.MTUDiscoverOption, int(tcpip.PMTUDiscoveryDont)); err != nil {
|
||||
t.Fatalf("disabling PMTU discovery via sockopt to force DF=0 failed: %s", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/filter.h>
|
||||
#include <sys/epoll.h>
|
||||
#endif // __linux__
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <poll.h>
|
||||
@@ -2327,6 +2328,49 @@ TEST_P(TcpSocketTest, GetSocketAcceptConnNonListener) {
|
||||
EXPECT_EQ(got, 0);
|
||||
}
|
||||
|
||||
TEST_P(TcpSocketTest, SetPMTUD) {
|
||||
// IP_PMTUDISC_WANT should be default.
|
||||
int got = -1;
|
||||
socklen_t length = sizeof(got);
|
||||
ASSERT_THAT(
|
||||
getsockopt(accepted_.get(), SOL_IP, IP_MTU_DISCOVER, &got, &length),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(got, IP_PMTUDISC_WANT);
|
||||
|
||||
int set = IP_PMTUDISC_DO;
|
||||
ASSERT_THAT(
|
||||
setsockopt(accepted_.get(), SOL_IP, IP_MTU_DISCOVER, &set, length),
|
||||
SyscallSucceeds());
|
||||
ASSERT_THAT(
|
||||
getsockopt(accepted_.get(), SOL_IP, IP_MTU_DISCOVER, &got, &length),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(got, IP_PMTUDISC_DO);
|
||||
set = IP_PMTUDISC_DONT;
|
||||
ASSERT_THAT(
|
||||
setsockopt(accepted_.get(), SOL_IP, IP_MTU_DISCOVER, &set, length),
|
||||
SyscallSucceeds());
|
||||
ASSERT_THAT(
|
||||
getsockopt(accepted_.get(), SOL_IP, IP_MTU_DISCOVER, &got, &length),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(got, IP_PMTUDISC_DONT);
|
||||
|
||||
// IP_PMTUDISC_PROBE is not supported by gVisor.
|
||||
set = IP_PMTUDISC_PROBE;
|
||||
if (IsRunningOnGvisor() && !IsRunningWithHostinet()) {
|
||||
ASSERT_THAT(
|
||||
setsockopt(accepted_.get(), SOL_IP, IP_MTU_DISCOVER, &set, length),
|
||||
SyscallFailsWithErrno(ENOTSUP));
|
||||
} else {
|
||||
ASSERT_THAT(
|
||||
setsockopt(accepted_.get(), SOL_IP, IP_MTU_DISCOVER, &set, length),
|
||||
SyscallSucceeds());
|
||||
ASSERT_THAT(
|
||||
getsockopt(accepted_.get(), SOL_IP, IP_MTU_DISCOVER, &got, &length),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(got, IP_PMTUDISC_PROBE);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(SimpleTcpSocketTest, GetSocketAcceptConnWithShutdown) {
|
||||
// TODO(b/171345701): Fix the TCP state for listening socket on shutdown.
|
||||
SKIP_IF(IsRunningOnGvisor());
|
||||
|
||||
Reference in New Issue
Block a user