Stub out support for TCP_MAXSEG.

Adds support to set/get the TCP_MAXSEG value but does not
really change the segment sizes emitted by netstack or
alter the MSS advertised by the endpoint. This is currently
being added only to unblock iperf3 on gVisor. Plumbing
this correctly requires a bit more work which will come
in separate CLs.

PiperOrigin-RevId: 257859112
diff --git a/tcpip/header/ipv4.go b/tcpip/header/ipv4.go
index f7b04be..4de95f8 100644
--- a/tcpip/header/ipv4.go
+++ b/tcpip/header/ipv4.go
@@ -85,6 +85,10 @@
 	// units, the header cannot exceed 15*4 = 60 bytes.
 	IPv4MaximumHeaderSize = 60
 
+	// MinIPFragmentPayloadSize is the minimum number of payload bytes that
+	// the first fragment must carry when an IPv4 packet is fragmented.
+	MinIPFragmentPayloadSize = 8
+
 	// IPv4AddressSize is the size, in bytes, of an IPv4 address.
 	IPv4AddressSize = 4
 
diff --git a/tcpip/header/tcp.go b/tcpip/header/tcp.go
index f990ebe..01d92c4 100644
--- a/tcpip/header/tcp.go
+++ b/tcpip/header/tcp.go
@@ -176,6 +176,21 @@
 
 	// TCPProtocolNumber is TCP's transport protocol number.
 	TCPProtocolNumber tcpip.TransportProtocolNumber = 6
+
+	// TCPMinimumMSS is the minimum acceptable value for MSS. This is the
+	// same as the value TCP_MIN_MSS defined net/tcp.h.
+	TCPMinimumMSS = IPv4MaximumHeaderSize + TCPHeaderMaximumSize + MinIPFragmentPayloadSize - IPv4MinimumSize - TCPMinimumSize
+
+	// TCPMaximumMSS is the maximum acceptable value for MSS.
+	TCPMaximumMSS = 0xffff
+
+	// TCPDefaultMSS is the MSS value that should be used if an MSS option
+	// is not received from the peer. It's also the value returned by
+	// TCP_MAXSEG option for a socket in an unconnected state.
+	//
+	// Per RFC 1122, page 85: "If an MSS option is not received at
+	// connection setup, TCP MUST assume a default send MSS of 536."
+	TCPDefaultMSS = 536
 )
 
 // SourcePort returns the "source port" field of the tcp header.
@@ -306,7 +321,7 @@
 	synOpts := TCPSynOptions{
 		// Per RFC 1122, page 85: "If an MSS option is not received at
 		// connection setup, TCP MUST assume a default send MSS of 536."
-		MSS: 536,
+		MSS: TCPDefaultMSS,
 		// If no window scale option is specified, WS in options is
 		// returned as -1; this is because the absence of the option
 		// indicates that the we cannot use window scaling on the
diff --git a/tcpip/tcpip.go b/tcpip/tcpip.go
index 0fd3c2c..7479cfd 100644
--- a/tcpip/tcpip.go
+++ b/tcpip/tcpip.go
@@ -496,6 +496,10 @@
 // buffer moderation.
 type ModerateReceiveBufferOption bool
 
+// MaxSegOption is used by SetSockOpt/GetSockOpt to set/get the current
+// Maximum Segment Size(MSS) value as specified using the TCP_MAXSEG option.
+type MaxSegOption int
+
 // MulticastTTLOption is used by SetSockOpt/GetSockOpt to control the default
 // TTL value for multicast messages. The default is 1.
 type MulticastTTLOption uint8
diff --git a/tcpip/transport/tcp/endpoint.go b/tcpip/transport/tcp/endpoint.go
index 12e1edb..bb4aad7 100644
--- a/tcpip/transport/tcp/endpoint.go
+++ b/tcpip/transport/tcp/endpoint.go
@@ -117,6 +117,7 @@
 	notifyDrain
 	notifyReset
 	notifyKeepaliveChanged
+	notifyMSSChanged
 )
 
 // SACKInfo holds TCP SACK related information for a given endpoint.
@@ -218,8 +219,6 @@
 	mu sync.RWMutex
 	id stack.TransportEndpointID
 
-	// state             endpointState
-	// pState            ProtocolState
 	state EndpointState
 
 	isPortReserved    bool
@@ -313,6 +312,10 @@
 	// in SYN-RCVD state.
 	synRcvdCount int
 
+	// userMSS if non-zero is the MSS value explicitly set by the user
+	// for this endpoint using the TCP_MAXSEG setsockopt.
+	userMSS int
+
 	// The following fields are used to manage the send buffer. When
 	// segments are ready to be sent, they are added to sndQueue and the
 	// protocol goroutine is signaled via sndWaker.
@@ -917,6 +920,17 @@
 		}
 		return nil
 
+	case tcpip.MaxSegOption:
+		userMSS := v
+		if userMSS < header.TCPMinimumMSS || userMSS > header.TCPMaximumMSS {
+			return tcpip.ErrInvalidOptionValue
+		}
+		e.mu.Lock()
+		e.userMSS = int(userMSS)
+		e.mu.Unlock()
+		e.notifyProtocolGoroutine(notifyMSSChanged)
+		return nil
+
 	case tcpip.ReceiveBufferSizeOption:
 		// Make sure the receive buffer size is within the min and max
 		// allowed.
@@ -1096,6 +1110,14 @@
 		e.lastErrorMu.Unlock()
 		return err
 
+	case *tcpip.MaxSegOption:
+		// This is just stubbed out. Linux never returns the user_mss
+		// value as it either returns the defaultMSS or returns the
+		// actual current MSS. Netstack just returns the defaultMSS
+		// always for now.
+		*o = header.TCPDefaultMSS
+		return nil
+
 	case *tcpip.SendBufferSizeOption:
 		e.sndBufMu.Lock()
 		*o = tcpip.SendBufferSizeOption(e.sndBufSize)