mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add support for custom socket options and setting the experiment IP option.
PiperOrigin-RevId: 700011458
This commit is contained in:
committed by
gVisor bot
parent
e816d99736
commit
2267c24a41
@@ -12,6 +12,7 @@ go_library(
|
||||
"netstack_state.go",
|
||||
"provider.go",
|
||||
"save_restore.go",
|
||||
"socketopt_custom.go",
|
||||
"stack.go",
|
||||
"tun.go",
|
||||
],
|
||||
|
||||
@@ -1107,6 +1107,10 @@ func getSockOptSocket(t *kernel.Task, s socket.Socket, ep commonEndpoint, family
|
||||
|
||||
v := primitive.Int32(ep.SocketOptions().GetRcvlowat())
|
||||
return &v, nil
|
||||
default:
|
||||
if v, err, handled := getSockOptSocketCustom(t, s, ep, name, outLen); handled {
|
||||
return v, err
|
||||
}
|
||||
}
|
||||
return nil, syserr.ErrProtocolNotAvailable
|
||||
}
|
||||
@@ -2028,6 +2032,10 @@ func setSockOptSocket(t *kernel.Task, s socket.Socket, ep commonEndpoint, name i
|
||||
v := hostarch.ByteOrder.Uint32(optVal)
|
||||
ep.SocketOptions().SetRcvlowat(int32(v))
|
||||
return nil
|
||||
default:
|
||||
if err, handled := setSockOptSocketCustom(t, s, ep, name, optVal); handled {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
//go:build !false
|
||||
// +build !false
|
||||
|
||||
package netstack
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/marshal"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
)
|
||||
|
||||
// setSockOptSocketCustom handles SetSockOpt options not handled by setSockOptSocket.
|
||||
// It returns a bool indicating whether the option was handled in addition to
|
||||
// return values from setSockOptSocket.
|
||||
func setSockOptSocketCustom(t *kernel.Task, s socket.Socket, ep commonEndpoint, name int, optVal []byte) (*syserr.Error, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// getSockOptSocketCustom handles GetSockOpt options not handled by getSockOptSocket.
|
||||
// It returns a bool indicating whether the option was handled in addition to
|
||||
// return values from getSockOptSocket.
|
||||
func getSockOptSocketCustom(t *kernel.Task, s socket.Socket, ep commonEndpoint, name int, outLen int) (marshal.Marshallable, *syserr.Error, bool) {
|
||||
return nil, nil, false
|
||||
}
|
||||
@@ -263,6 +263,10 @@ type SocketOptions struct {
|
||||
// rcvlowat specifies the minimum number of bytes which should be
|
||||
// received to indicate the socket as readable.
|
||||
rcvlowat atomicbitops.Int32
|
||||
|
||||
// experimentOptionValue is the value set for the IP option experiment header
|
||||
// if it is not zero.
|
||||
experimentOptionValue atomicbitops.Uint32
|
||||
}
|
||||
|
||||
// InitHandler initializes the handler. This must be called before using the
|
||||
@@ -539,6 +543,17 @@ func (so *SocketOptions) SetLinger(linger LingerOption) {
|
||||
so.mu.Unlock()
|
||||
}
|
||||
|
||||
// GetExperimentOptionValue gets value for the experiment IP option header.
|
||||
func (so *SocketOptions) GetExperimentOptionValue() uint16 {
|
||||
v := so.experimentOptionValue.Load()
|
||||
return uint16(v)
|
||||
}
|
||||
|
||||
// SetExperimentOptionValue sets the value for the experiment IP option header.
|
||||
func (so *SocketOptions) SetExperimentOptionValue(v uint16) {
|
||||
so.experimentOptionValue.Store(uint32(v))
|
||||
}
|
||||
|
||||
// SockErrOrigin represents the constants for error origin.
|
||||
type SockErrOrigin uint8
|
||||
|
||||
|
||||
@@ -322,6 +322,10 @@ type NetworkHeaderParams struct {
|
||||
|
||||
// DF indicates whether the DF bit should be set.
|
||||
DF bool
|
||||
|
||||
// ExperimentOptionValue is a 16 bit value that is set for the IP experiment
|
||||
// option headers if it is not zero.
|
||||
ExperimentOptionValue uint16
|
||||
}
|
||||
|
||||
// GroupAddressableEndpoint is an endpoint that supports group addressing.
|
||||
|
||||
@@ -345,9 +345,10 @@ func (c *WriteContext) WritePacket(pkt *stack.PacketBuffer, headerIncluded bool)
|
||||
}
|
||||
|
||||
err := c.route.WritePacket(stack.NetworkHeaderParams{
|
||||
Protocol: c.e.transProto,
|
||||
TTL: c.ttl,
|
||||
TOS: c.tos,
|
||||
Protocol: c.e.transProto,
|
||||
TTL: c.ttl,
|
||||
TOS: c.tos,
|
||||
ExperimentOptionValue: c.e.ops.GetExperimentOptionValue(),
|
||||
}, pkt)
|
||||
|
||||
if _, ok := err.(*tcpip.ErrNoBufferSpace); ok {
|
||||
|
||||
@@ -526,13 +526,14 @@ func (e *Endpoint) handleListenSegment(ctx *listenContext, s *segment) tcpip.Err
|
||||
}
|
||||
cookie := ctx.createCookie(s.id, s.sequenceNumber, encodeMSS(opts.MSS))
|
||||
fields := tcpFields{
|
||||
id: s.id,
|
||||
ttl: calculateTTL(route, e.ipv4TTL, e.ipv6HopLimit),
|
||||
tos: e.sendTOS,
|
||||
flags: header.TCPFlagSyn | header.TCPFlagAck,
|
||||
seq: cookie,
|
||||
ack: s.sequenceNumber + 1,
|
||||
rcvWnd: ctx.rcvWnd,
|
||||
id: s.id,
|
||||
ttl: calculateTTL(route, e.ipv4TTL, e.ipv6HopLimit),
|
||||
tos: e.sendTOS,
|
||||
flags: header.TCPFlagSyn | header.TCPFlagAck,
|
||||
seq: cookie,
|
||||
ack: s.sequenceNumber + 1,
|
||||
rcvWnd: ctx.rcvWnd,
|
||||
expOptVal: e.SocketOptions().GetExperimentOptionValue(),
|
||||
}
|
||||
if err := e.sendSynTCP(route, fields, synOpts); err != nil {
|
||||
return err
|
||||
|
||||
@@ -364,13 +364,14 @@ func (h *handshake) synSentState(s *segment) tcpip.Error {
|
||||
ttl = h.ep.route.DefaultTTL()
|
||||
}
|
||||
h.ep.sendSynTCP(h.ep.route, tcpFields{
|
||||
id: h.ep.TransportEndpointInfo.ID,
|
||||
ttl: ttl,
|
||||
tos: h.ep.sendTOS,
|
||||
flags: h.flags,
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
id: h.ep.TransportEndpointInfo.ID,
|
||||
ttl: ttl,
|
||||
tos: h.ep.sendTOS,
|
||||
flags: h.flags,
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
expOptVal: h.ep.SocketOptions().GetExperimentOptionValue(),
|
||||
}, synOpts)
|
||||
return nil
|
||||
}
|
||||
@@ -450,13 +451,14 @@ func (h *handshake) synRcvdState(s *segment) tcpip.Error {
|
||||
MSS: h.ep.amss,
|
||||
}
|
||||
h.ep.sendSynTCP(h.ep.route, tcpFields{
|
||||
id: h.ep.TransportEndpointInfo.ID,
|
||||
ttl: calculateTTL(h.ep.route, h.ep.ipv4TTL, h.ep.ipv6HopLimit),
|
||||
tos: h.ep.sendTOS,
|
||||
flags: h.flags,
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
id: h.ep.TransportEndpointInfo.ID,
|
||||
ttl: calculateTTL(h.ep.route, h.ep.ipv4TTL, h.ep.ipv6HopLimit),
|
||||
tos: h.ep.sendTOS,
|
||||
flags: h.flags,
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
expOptVal: h.ep.SocketOptions().GetExperimentOptionValue(),
|
||||
}, synOpts)
|
||||
return nil
|
||||
}
|
||||
@@ -587,13 +589,14 @@ func (h *handshake) start() {
|
||||
|
||||
h.sendSYNOpts = synOpts
|
||||
h.ep.sendSynTCP(h.ep.route, tcpFields{
|
||||
id: h.ep.TransportEndpointInfo.ID,
|
||||
ttl: calculateTTL(h.ep.route, h.ep.ipv4TTL, h.ep.ipv6HopLimit),
|
||||
tos: h.ep.sendTOS,
|
||||
flags: h.flags,
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
id: h.ep.TransportEndpointInfo.ID,
|
||||
ttl: calculateTTL(h.ep.route, h.ep.ipv4TTL, h.ep.ipv6HopLimit),
|
||||
tos: h.ep.sendTOS,
|
||||
flags: h.flags,
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
expOptVal: h.ep.SocketOptions().GetExperimentOptionValue(),
|
||||
}, synOpts)
|
||||
}
|
||||
|
||||
@@ -623,13 +626,14 @@ func (h *handshake) retransmitHandlerLocked() tcpip.Error {
|
||||
// retransmitted on their own).
|
||||
if h.active || !h.acked || h.deferAccept != 0 && e.stack.Clock().NowMonotonic().Sub(h.startTime) > h.deferAccept {
|
||||
e.sendSynTCP(e.route, tcpFields{
|
||||
id: e.TransportEndpointInfo.ID,
|
||||
ttl: calculateTTL(e.route, e.ipv4TTL, e.ipv6HopLimit),
|
||||
tos: e.sendTOS,
|
||||
flags: h.flags,
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
id: e.TransportEndpointInfo.ID,
|
||||
ttl: calculateTTL(e.route, e.ipv4TTL, e.ipv6HopLimit),
|
||||
tos: e.sendTOS,
|
||||
flags: h.flags,
|
||||
seq: h.iss,
|
||||
ack: h.ackNum,
|
||||
rcvWnd: h.rcvWnd,
|
||||
expOptVal: e.SocketOptions().GetExperimentOptionValue(),
|
||||
}, h.sendSYNOpts)
|
||||
// If we have ever retransmitted the SYN-ACK or
|
||||
// SYN segment, we should only measure RTT if
|
||||
@@ -800,16 +804,17 @@ func makeSynOptions(opts header.TCPSynOptions) []byte {
|
||||
// tcpFields is a struct to carry different parameters required by the
|
||||
// send*TCP variant functions below.
|
||||
type tcpFields struct {
|
||||
id stack.TransportEndpointID
|
||||
ttl uint8
|
||||
tos uint8
|
||||
flags header.TCPFlags
|
||||
seq seqnum.Value
|
||||
ack seqnum.Value
|
||||
rcvWnd seqnum.Size
|
||||
opts []byte
|
||||
txHash uint32
|
||||
df bool
|
||||
id stack.TransportEndpointID
|
||||
ttl uint8
|
||||
tos uint8
|
||||
flags header.TCPFlags
|
||||
seq seqnum.Value
|
||||
ack seqnum.Value
|
||||
rcvWnd seqnum.Size
|
||||
opts []byte
|
||||
txHash uint32
|
||||
df bool
|
||||
expOptVal uint16
|
||||
}
|
||||
|
||||
func (e *Endpoint) sendSynTCP(r *stack.Route, tf tcpFields, opts header.TCPSynOptions) tcpip.Error {
|
||||
@@ -897,7 +902,13 @@ 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, DF: tf.df}, pkt); err != nil {
|
||||
if err := r.WritePacket(stack.NetworkHeaderParams{
|
||||
Protocol: ProtocolNumber,
|
||||
TTL: tf.ttl,
|
||||
TOS: tf.tos,
|
||||
DF: tf.df,
|
||||
ExperimentOptionValue: tf.expOptVal,
|
||||
}, pkt); err != nil {
|
||||
r.Stats().TCP.SegmentSendErrors.Increment()
|
||||
if shouldSplitPacket {
|
||||
pkt.DecRef()
|
||||
@@ -929,7 +940,13 @@ 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, DF: tf.df}, pkt); err != nil {
|
||||
if err := r.WritePacket(stack.NetworkHeaderParams{
|
||||
Protocol: ProtocolNumber,
|
||||
TTL: tf.ttl,
|
||||
TOS: tf.tos,
|
||||
DF: tf.df,
|
||||
ExperimentOptionValue: tf.expOptVal,
|
||||
}, pkt); err != nil {
|
||||
r.Stats().TCP.SegmentSendErrors.Increment()
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -235,14 +235,19 @@ func replyWithReset(st *stack.Stack, s *segment, tos, ipv4TTL uint8, ipv6HopLimi
|
||||
|
||||
p := stack.NewPacketBuffer(stack.PacketBufferOptions{ReserveHeaderBytes: header.TCPMinimumSize + int(route.MaxHeaderLength())})
|
||||
defer p.DecRef()
|
||||
var expOptVal uint16
|
||||
if s.ep != nil {
|
||||
expOptVal = s.ep.SocketOptions().GetExperimentOptionValue()
|
||||
}
|
||||
return sendTCP(route, tcpFields{
|
||||
id: s.id,
|
||||
ttl: ttl,
|
||||
tos: tos,
|
||||
flags: flags,
|
||||
seq: seq,
|
||||
ack: ack,
|
||||
rcvWnd: 0,
|
||||
id: s.id,
|
||||
ttl: ttl,
|
||||
tos: tos,
|
||||
flags: flags,
|
||||
seq: seq,
|
||||
ack: ack,
|
||||
rcvWnd: 0,
|
||||
expOptVal: expOptVal,
|
||||
}, p, stack.GSO{}, nil /* PacketOwner */)
|
||||
}
|
||||
|
||||
|
||||
@@ -1263,3 +1263,8 @@ syscall_test(
|
||||
save = False,
|
||||
test = "//test/syscalls/linux:close_range_test",
|
||||
)
|
||||
|
||||
syscall_test(
|
||||
save = False,
|
||||
test = "//test/syscalls/linux:socketopt_test",
|
||||
)
|
||||
|
||||
@@ -2557,6 +2557,22 @@ cc_library(
|
||||
alwayslink = 1,
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "socketopt_test",
|
||||
testonly = 1,
|
||||
srcs = ["socketopt_test.cc"],
|
||||
linkstatic = 1,
|
||||
deps = select_gtest() + [
|
||||
"//test/util:capability_util",
|
||||
"//test/util:file_descriptor",
|
||||
"//test/util:posix_error",
|
||||
"//test/util:socket_util",
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "socket_stress_test",
|
||||
testonly = 1,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
namespace {} // namespace
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
@@ -51,6 +51,7 @@ generic = [
|
||||
"_unsafe",
|
||||
"_opts",
|
||||
"_testonly",
|
||||
"_custom",
|
||||
]
|
||||
|
||||
# State explosion? Sure. This is approximately:
|
||||
|
||||
Reference in New Issue
Block a user