blob: f3ad40fdf60764117334c970f2764c8be056050b [file] [log] [blame]
// Copyright 2020 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.
package tcpip
import (
"sync/atomic"
"gvisor.dev/gvisor/pkg/sync"
)
// 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.
type SocketOptionsHandler interface {
// OnReuseAddressSet is invoked when SO_REUSEADDR is set for an endpoint.
OnReuseAddressSet(v bool)
// OnReusePortSet is invoked when SO_REUSEPORT is set for an endpoint.
OnReusePortSet(v bool)
// OnKeepAliveSet is invoked when SO_KEEPALIVE is set for an endpoint.
OnKeepAliveSet(v bool)
// OnDelayOptionSet is invoked when TCP_NODELAY is set for an endpoint.
// Note that v will be the inverse of TCP_NODELAY option.
OnDelayOptionSet(v bool)
// OnCorkOptionSet is invoked when TCP_CORK is set for an endpoint.
OnCorkOptionSet(v bool)
// LastError is invoked when SO_ERROR is read for an endpoint.
LastError() *Error
// UpdateLastError updates the endpoint specific last error field.
UpdateLastError(err *Error)
// HasNIC is invoked to check if the NIC is valid for SO_BINDTODEVICE.
HasNIC(v int32) bool
}
// DefaultSocketOptionsHandler is an embeddable type that implements no-op
// implementations for SocketOptionsHandler methods.
type DefaultSocketOptionsHandler struct{}
var _ SocketOptionsHandler = (*DefaultSocketOptionsHandler)(nil)
// OnReuseAddressSet implements SocketOptionsHandler.OnReuseAddressSet.
func (*DefaultSocketOptionsHandler) OnReuseAddressSet(bool) {}
// OnReusePortSet implements SocketOptionsHandler.OnReusePortSet.
func (*DefaultSocketOptionsHandler) OnReusePortSet(bool) {}
// OnKeepAliveSet implements SocketOptionsHandler.OnKeepAliveSet.
func (*DefaultSocketOptionsHandler) OnKeepAliveSet(bool) {}
// OnDelayOptionSet implements SocketOptionsHandler.OnDelayOptionSet.
func (*DefaultSocketOptionsHandler) OnDelayOptionSet(bool) {}
// OnCorkOptionSet implements SocketOptionsHandler.OnCorkOptionSet.
func (*DefaultSocketOptionsHandler) OnCorkOptionSet(bool) {}
// LastError implements SocketOptionsHandler.LastError.
func (*DefaultSocketOptionsHandler) LastError() *Error {
return nil
}
// UpdateLastError implements SocketOptionsHandler.UpdateLastError.
func (*DefaultSocketOptionsHandler) UpdateLastError(*Error) {}
// HasNIC implements SocketOptionsHandler.HasNIC.
func (*DefaultSocketOptionsHandler) HasNIC(int32) bool {
return false
}
// SocketOptions contains all the variables which store values for SOL_SOCKET,
// SOL_IP, SOL_IPV6 and SOL_TCP level options.
//
// +stateify savable
type SocketOptions struct {
handler SocketOptionsHandler
// These fields are accessed and modified using atomic operations.
// broadcastEnabled determines whether datagram sockets are allowed to
// send packets to a broadcast address.
broadcastEnabled uint32
// passCredEnabled determines whether SCM_CREDENTIALS socket control
// messages are enabled.
passCredEnabled uint32
// noChecksumEnabled determines whether UDP checksum is disabled while
// transmitting for this socket.
noChecksumEnabled uint32
// reuseAddressEnabled determines whether Bind() should allow reuse of
// local address.
reuseAddressEnabled uint32
// reusePortEnabled determines whether to permit multiple sockets to be
// bound to an identical socket address.
reusePortEnabled uint32
// keepAliveEnabled determines whether TCP keepalive is enabled for this
// socket.
keepAliveEnabled uint32
// multicastLoopEnabled determines whether multicast packets sent over a
// non-loopback interface will be looped back.
multicastLoopEnabled uint32
// receiveTOSEnabled is used to specify if the TOS ancillary message is
// passed with incoming packets.
receiveTOSEnabled uint32
// receiveTClassEnabled is used to specify if the IPV6_TCLASS ancillary
// message is passed with incoming packets.
receiveTClassEnabled uint32
// receivePacketInfoEnabled is used to specify if more inforamtion is
// provided with incoming packets such as interface index and address.
receivePacketInfoEnabled uint32
// hdrIncludeEnabled is used to indicate for a raw endpoint that all packets
// being written have an IP header and the endpoint should not attach an IP
// header.
hdrIncludedEnabled uint32
// v6OnlyEnabled is used to determine whether an IPv6 socket is to be
// restricted to sending and receiving IPv6 packets only.
v6OnlyEnabled uint32
// quickAckEnabled is used to represent the value of TCP_QUICKACK option.
// It currently does not have any effect on the TCP endpoint.
quickAckEnabled uint32
// delayOptionEnabled is used to specify if data should be sent out immediately
// by the transport protocol. For TCP, it determines if the Nagle algorithm
// is on or off.
delayOptionEnabled uint32
// corkOptionEnabled is used to specify if data should be held until segments
// are full by the TCP transport protocol.
corkOptionEnabled uint32
// receiveOriginalDstAddress is used to specify if the original destination of
// the incoming packet should be returned as an ancillary message.
receiveOriginalDstAddress uint32
// recvErrEnabled determines whether extended reliable error message passing
// is enabled.
recvErrEnabled uint32
// errQueue is the per-socket error queue. It is protected by errQueueMu.
errQueueMu sync.Mutex `state:"nosave"`
errQueue sockErrorList
// bindToDevice determines the device to which the socket is bound.
bindToDevice int32
// mu protects the access to the below fields.
mu sync.Mutex `state:"nosave"`
// linger determines the amount of time the socket should linger before
// close. We currently implement this option for TCP socket only.
linger LingerOption
}
// InitHandler initializes the handler. This must be called before using the
// socket options utility.
func (so *SocketOptions) InitHandler(handler SocketOptionsHandler) {
so.handler = handler
}
func storeAtomicBool(addr *uint32, v bool) {
var val uint32
if v {
val = 1
}
atomic.StoreUint32(addr, val)
}
// SetLastError sets the last error for a socket.
func (so *SocketOptions) SetLastError(err *Error) {
so.handler.UpdateLastError(err)
}
// GetBroadcast gets value for SO_BROADCAST option.
func (so *SocketOptions) GetBroadcast() bool {
return atomic.LoadUint32(&so.broadcastEnabled) != 0
}
// SetBroadcast sets value for SO_BROADCAST option.
func (so *SocketOptions) SetBroadcast(v bool) {
storeAtomicBool(&so.broadcastEnabled, v)
}
// GetPassCred gets value for SO_PASSCRED option.
func (so *SocketOptions) GetPassCred() bool {
return atomic.LoadUint32(&so.passCredEnabled) != 0
}
// SetPassCred sets value for SO_PASSCRED option.
func (so *SocketOptions) SetPassCred(v bool) {
storeAtomicBool(&so.passCredEnabled, v)
}
// GetNoChecksum gets value for SO_NO_CHECK option.
func (so *SocketOptions) GetNoChecksum() bool {
return atomic.LoadUint32(&so.noChecksumEnabled) != 0
}
// SetNoChecksum sets value for SO_NO_CHECK option.
func (so *SocketOptions) SetNoChecksum(v bool) {
storeAtomicBool(&so.noChecksumEnabled, v)
}
// GetReuseAddress gets value for SO_REUSEADDR option.
func (so *SocketOptions) GetReuseAddress() bool {
return atomic.LoadUint32(&so.reuseAddressEnabled) != 0
}
// SetReuseAddress sets value for SO_REUSEADDR option.
func (so *SocketOptions) SetReuseAddress(v bool) {
storeAtomicBool(&so.reuseAddressEnabled, v)
so.handler.OnReuseAddressSet(v)
}
// GetReusePort gets value for SO_REUSEPORT option.
func (so *SocketOptions) GetReusePort() bool {
return atomic.LoadUint32(&so.reusePortEnabled) != 0
}
// SetReusePort sets value for SO_REUSEPORT option.
func (so *SocketOptions) SetReusePort(v bool) {
storeAtomicBool(&so.reusePortEnabled, v)
so.handler.OnReusePortSet(v)
}
// GetKeepAlive gets value for SO_KEEPALIVE option.
func (so *SocketOptions) GetKeepAlive() bool {
return atomic.LoadUint32(&so.keepAliveEnabled) != 0
}
// SetKeepAlive sets value for SO_KEEPALIVE option.
func (so *SocketOptions) SetKeepAlive(v bool) {
storeAtomicBool(&so.keepAliveEnabled, v)
so.handler.OnKeepAliveSet(v)
}
// GetMulticastLoop gets value for IP_MULTICAST_LOOP option.
func (so *SocketOptions) GetMulticastLoop() bool {
return atomic.LoadUint32(&so.multicastLoopEnabled) != 0
}
// SetMulticastLoop sets value for IP_MULTICAST_LOOP option.
func (so *SocketOptions) SetMulticastLoop(v bool) {
storeAtomicBool(&so.multicastLoopEnabled, v)
}
// GetReceiveTOS gets value for IP_RECVTOS option.
func (so *SocketOptions) GetReceiveTOS() bool {
return atomic.LoadUint32(&so.receiveTOSEnabled) != 0
}
// SetReceiveTOS sets value for IP_RECVTOS option.
func (so *SocketOptions) SetReceiveTOS(v bool) {
storeAtomicBool(&so.receiveTOSEnabled, v)
}
// GetReceiveTClass gets value for IPV6_RECVTCLASS option.
func (so *SocketOptions) GetReceiveTClass() bool {
return atomic.LoadUint32(&so.receiveTClassEnabled) != 0
}
// SetReceiveTClass sets value for IPV6_RECVTCLASS option.
func (so *SocketOptions) SetReceiveTClass(v bool) {
storeAtomicBool(&so.receiveTClassEnabled, v)
}
// GetReceivePacketInfo gets value for IP_PKTINFO option.
func (so *SocketOptions) GetReceivePacketInfo() bool {
return atomic.LoadUint32(&so.receivePacketInfoEnabled) != 0
}
// SetReceivePacketInfo sets value for IP_PKTINFO option.
func (so *SocketOptions) SetReceivePacketInfo(v bool) {
storeAtomicBool(&so.receivePacketInfoEnabled, v)
}
// GetHeaderIncluded gets value for IP_HDRINCL option.
func (so *SocketOptions) GetHeaderIncluded() bool {
return atomic.LoadUint32(&so.hdrIncludedEnabled) != 0
}
// SetHeaderIncluded sets value for IP_HDRINCL option.
func (so *SocketOptions) SetHeaderIncluded(v bool) {
storeAtomicBool(&so.hdrIncludedEnabled, v)
}
// GetV6Only gets value for IPV6_V6ONLY option.
func (so *SocketOptions) GetV6Only() bool {
return atomic.LoadUint32(&so.v6OnlyEnabled) != 0
}
// SetV6Only sets value for IPV6_V6ONLY option.
//
// Preconditions: the backing TCP or UDP endpoint must be in initial state.
func (so *SocketOptions) SetV6Only(v bool) {
storeAtomicBool(&so.v6OnlyEnabled, v)
}
// GetQuickAck gets value for TCP_QUICKACK option.
func (so *SocketOptions) GetQuickAck() bool {
return atomic.LoadUint32(&so.quickAckEnabled) != 0
}
// SetQuickAck sets value for TCP_QUICKACK option.
func (so *SocketOptions) SetQuickAck(v bool) {
storeAtomicBool(&so.quickAckEnabled, v)
}
// GetDelayOption gets inverted value for TCP_NODELAY option.
func (so *SocketOptions) GetDelayOption() bool {
return atomic.LoadUint32(&so.delayOptionEnabled) != 0
}
// SetDelayOption sets inverted value for TCP_NODELAY option.
func (so *SocketOptions) SetDelayOption(v bool) {
storeAtomicBool(&so.delayOptionEnabled, v)
so.handler.OnDelayOptionSet(v)
}
// GetCorkOption gets value for TCP_CORK option.
func (so *SocketOptions) GetCorkOption() bool {
return atomic.LoadUint32(&so.corkOptionEnabled) != 0
}
// SetCorkOption sets value for TCP_CORK option.
func (so *SocketOptions) SetCorkOption(v bool) {
storeAtomicBool(&so.corkOptionEnabled, v)
so.handler.OnCorkOptionSet(v)
}
// GetReceiveOriginalDstAddress gets value for IP(V6)_RECVORIGDSTADDR option.
func (so *SocketOptions) GetReceiveOriginalDstAddress() bool {
return atomic.LoadUint32(&so.receiveOriginalDstAddress) != 0
}
// SetReceiveOriginalDstAddress sets value for IP(V6)_RECVORIGDSTADDR option.
func (so *SocketOptions) SetReceiveOriginalDstAddress(v bool) {
storeAtomicBool(&so.receiveOriginalDstAddress, v)
}
// GetRecvError gets value for IP*_RECVERR option.
func (so *SocketOptions) GetRecvError() bool {
return atomic.LoadUint32(&so.recvErrEnabled) != 0
}
// SetRecvError sets value for IP*_RECVERR option.
func (so *SocketOptions) SetRecvError(v bool) {
storeAtomicBool(&so.recvErrEnabled, v)
if !v {
so.pruneErrQueue()
}
}
// GetLastError gets value for SO_ERROR option.
func (so *SocketOptions) GetLastError() *Error {
return so.handler.LastError()
}
// GetOutOfBandInline gets value for SO_OOBINLINE option.
func (*SocketOptions) GetOutOfBandInline() bool {
return true
}
// SetOutOfBandInline sets value for SO_OOBINLINE option. We currently do not
// support disabling this option.
func (*SocketOptions) SetOutOfBandInline(bool) {}
// GetLinger gets value for SO_LINGER option.
func (so *SocketOptions) GetLinger() LingerOption {
so.mu.Lock()
linger := so.linger
so.mu.Unlock()
return linger
}
// SetLinger sets value for SO_LINGER option.
func (so *SocketOptions) SetLinger(linger LingerOption) {
so.mu.Lock()
so.linger = linger
so.mu.Unlock()
}
// SockErrOrigin represents the constants for error origin.
type SockErrOrigin uint8
const (
// SockExtErrorOriginNone represents an unknown error origin.
SockExtErrorOriginNone SockErrOrigin = iota
// SockExtErrorOriginLocal indicates a local error.
SockExtErrorOriginLocal
// SockExtErrorOriginICMP indicates an IPv4 ICMP error.
SockExtErrorOriginICMP
// SockExtErrorOriginICMP6 indicates an IPv6 ICMP error.
SockExtErrorOriginICMP6
)
// IsICMPErr indicates if the error originated from an ICMP error.
func (origin SockErrOrigin) IsICMPErr() bool {
return origin == SockExtErrorOriginICMP || origin == SockExtErrorOriginICMP6
}
// SockError represents a queue entry in the per-socket error queue.
//
// +stateify savable
type SockError struct {
sockErrorEntry
// Err is the error caused by the errant packet.
Err *Error
// ErrOrigin indicates the error origin.
ErrOrigin SockErrOrigin
// ErrType is the type in the ICMP header.
ErrType uint8
// ErrCode is the code in the ICMP header.
ErrCode uint8
// ErrInfo is additional info about the error.
ErrInfo uint32
// Payload is the errant packet's payload.
Payload []byte
// Dst is the original destination address of the errant packet.
Dst FullAddress
// Offender is the original sender address of the errant packet.
Offender FullAddress
// NetProto is the network protocol being used to transmit the packet.
NetProto NetworkProtocolNumber
}
// pruneErrQueue resets the queue.
func (so *SocketOptions) pruneErrQueue() {
so.errQueueMu.Lock()
so.errQueue.Reset()
so.errQueueMu.Unlock()
}
// DequeueErr dequeues a socket extended error from the error queue and returns
// it. Returns nil if queue is empty.
func (so *SocketOptions) DequeueErr() *SockError {
so.errQueueMu.Lock()
defer so.errQueueMu.Unlock()
err := so.errQueue.Front()
if err != nil {
so.errQueue.Remove(err)
}
return err
}
// PeekErr returns the error in the front of the error queue. Returns nil if
// the error queue is empty.
func (so *SocketOptions) PeekErr() *SockError {
so.errQueueMu.Lock()
defer so.errQueueMu.Unlock()
return so.errQueue.Front()
}
// QueueErr inserts the error at the back of the error queue.
//
// Preconditions: so.GetRecvError() == true.
func (so *SocketOptions) QueueErr(err *SockError) {
so.errQueueMu.Lock()
defer so.errQueueMu.Unlock()
so.errQueue.PushBack(err)
}
// QueueLocalErr queues a local error onto the local queue.
func (so *SocketOptions) QueueLocalErr(err *Error, net NetworkProtocolNumber, info uint32, dst FullAddress, payload []byte) {
so.QueueErr(&SockError{
Err: err,
ErrOrigin: SockExtErrorOriginLocal,
ErrInfo: info,
Payload: payload,
Dst: dst,
NetProto: net,
})
}
// GetBindToDevice gets value for SO_BINDTODEVICE option.
func (so *SocketOptions) GetBindToDevice() int32 {
return atomic.LoadInt32(&so.bindToDevice)
}
// SetBindToDevice sets value for SO_BINDTODEVICE option.
func (so *SocketOptions) SetBindToDevice(bindToDevice int32) *Error {
if !so.handler.HasNIC(bindToDevice) {
return ErrUnknownDevice
}
atomic.StoreInt32(&so.bindToDevice, bindToDevice)
return nil
}