mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Move SO_BINDTODEVICE to socketops.
PiperOrigin-RevId: 348696094
This commit is contained in:
committed by
gVisor bot
parent
202e9fa369
commit
7c8ba72b02
@@ -1042,10 +1042,7 @@ func getSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, fam
|
||||
return &v, nil
|
||||
|
||||
case linux.SO_BINDTODEVICE:
|
||||
var v tcpip.BindToDeviceOption
|
||||
if err := ep.GetSockOpt(&v); err != nil {
|
||||
return nil, syserr.TranslateNetstackError(err)
|
||||
}
|
||||
v := ep.SocketOptions().GetBindToDevice()
|
||||
if v == 0 {
|
||||
var b primitive.ByteSlice
|
||||
return &b, nil
|
||||
@@ -1804,8 +1801,7 @@ func setSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, nam
|
||||
}
|
||||
name := string(optVal[:n])
|
||||
if name == "" {
|
||||
v := tcpip.BindToDeviceOption(0)
|
||||
return syserr.TranslateNetstackError(ep.SetSockOpt(&v))
|
||||
return syserr.TranslateNetstackError(ep.SocketOptions().SetBindToDevice(0))
|
||||
}
|
||||
s := t.NetworkContext()
|
||||
if s == nil {
|
||||
@@ -1813,8 +1809,7 @@ func setSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, nam
|
||||
}
|
||||
for nicID, nic := range s.Interfaces() {
|
||||
if nic.Name == name {
|
||||
v := tcpip.BindToDeviceOption(nicID)
|
||||
return syserr.TranslateNetstackError(ep.SetSockOpt(&v))
|
||||
return syserr.TranslateNetstackError(ep.SocketOptions().SetBindToDevice(nicID))
|
||||
}
|
||||
}
|
||||
return syserr.ErrUnknownDevice
|
||||
|
||||
@@ -45,6 +45,9 @@ type SocketOptionsHandler interface {
|
||||
|
||||
// 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
|
||||
@@ -76,6 +79,11 @@ func (*DefaultSocketOptionsHandler) LastError() *Error {
|
||||
// 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.
|
||||
//
|
||||
@@ -159,6 +167,9 @@ type SocketOptions struct {
|
||||
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"`
|
||||
|
||||
@@ -492,3 +503,18 @@ func (so *SocketOptions) QueueLocalErr(err *Error, net NetworkProtocolNumber, in
|
||||
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
|
||||
}
|
||||
|
||||
@@ -308,9 +308,8 @@ func TestBindToDeviceDistribution(t *testing.T) {
|
||||
|
||||
defer ep.Close()
|
||||
ep.SocketOptions().SetReusePort(endpoint.reuse)
|
||||
bindToDeviceOption := tcpip.BindToDeviceOption(endpoint.bindToDevice)
|
||||
if err := ep.SetSockOpt(&bindToDeviceOption); err != nil {
|
||||
t.Fatalf("SetSockOpt(&%T(%d)) on endpoint %d failed: %s", bindToDeviceOption, bindToDeviceOption, i, err)
|
||||
if err := ep.SocketOptions().SetBindToDevice(int32(endpoint.bindToDevice)); err != nil {
|
||||
t.Fatalf("SetSockOpt(&%T(%d)) on endpoint %d failed: %s", endpoint.bindToDevice, endpoint.bindToDevice, i, err)
|
||||
}
|
||||
|
||||
var dstAddr tcpip.Address
|
||||
|
||||
@@ -955,14 +955,6 @@ type SettableSocketOption interface {
|
||||
isSettableSocketOption()
|
||||
}
|
||||
|
||||
// BindToDeviceOption is used by SetSockOpt/GetSockOpt to specify that sockets
|
||||
// should bind only on a specific NIC.
|
||||
type BindToDeviceOption NICID
|
||||
|
||||
func (*BindToDeviceOption) isGettableSocketOption() {}
|
||||
|
||||
func (*BindToDeviceOption) isSettableSocketOption() {}
|
||||
|
||||
// TCPInfoOption is used by GetSockOpt to expose TCP statistics.
|
||||
//
|
||||
// TODO(b/64800844): Add and populate stat fields.
|
||||
|
||||
@@ -502,9 +502,6 @@ type endpoint struct {
|
||||
// sack holds TCP SACK related information for this endpoint.
|
||||
sack SACKInfo
|
||||
|
||||
// bindToDevice is set to the NIC on which to bind or disabled if 0.
|
||||
bindToDevice tcpip.NICID
|
||||
|
||||
// delay enables Nagle's algorithm.
|
||||
//
|
||||
// delay is a boolean (0 is false) and must be accessed atomically.
|
||||
@@ -1821,18 +1818,13 @@ func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpoint) HasNIC(id int32) bool {
|
||||
return id == 0 || e.stack.HasNIC(tcpip.NICID(id))
|
||||
}
|
||||
|
||||
// SetSockOpt sets a socket option.
|
||||
func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
|
||||
switch v := opt.(type) {
|
||||
case *tcpip.BindToDeviceOption:
|
||||
id := tcpip.NICID(*v)
|
||||
if id != 0 && !e.stack.HasNIC(id) {
|
||||
return tcpip.ErrUnknownDevice
|
||||
}
|
||||
e.LockUser()
|
||||
e.bindToDevice = id
|
||||
e.UnlockUser()
|
||||
|
||||
case *tcpip.KeepaliveIdleOption:
|
||||
e.keepalive.Lock()
|
||||
e.keepalive.idle = time.Duration(*v)
|
||||
@@ -2013,11 +2005,6 @@ func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
|
||||
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
|
||||
func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
|
||||
switch o := opt.(type) {
|
||||
case *tcpip.BindToDeviceOption:
|
||||
e.LockUser()
|
||||
*o = tcpip.BindToDeviceOption(e.bindToDevice)
|
||||
e.UnlockUser()
|
||||
|
||||
case *tcpip.TCPInfoOption:
|
||||
*o = tcpip.TCPInfoOption{}
|
||||
e.LockUser()
|
||||
@@ -2220,11 +2207,12 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
|
||||
}
|
||||
}
|
||||
|
||||
bindToDevice := tcpip.NICID(e.ops.GetBindToDevice())
|
||||
if _, err := e.stack.PickEphemeralPortStable(portOffset, func(p uint16) (bool, *tcpip.Error) {
|
||||
if sameAddr && p == e.ID.RemotePort {
|
||||
return false, nil
|
||||
}
|
||||
if _, err := e.stack.ReservePort(netProtos, ProtocolNumber, e.ID.LocalAddress, p, e.portFlags, e.bindToDevice, addr, nil /* testPort */); err != nil {
|
||||
if _, err := e.stack.ReservePort(netProtos, ProtocolNumber, e.ID.LocalAddress, p, e.portFlags, bindToDevice, addr, nil /* testPort */); err != nil {
|
||||
if err != tcpip.ErrPortInUse || !reuse {
|
||||
return false, nil
|
||||
}
|
||||
@@ -2262,15 +2250,15 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
|
||||
tcpEP.notifyProtocolGoroutine(notifyAbort)
|
||||
tcpEP.UnlockUser()
|
||||
// Now try and Reserve again if it fails then we skip.
|
||||
if _, err := e.stack.ReservePort(netProtos, ProtocolNumber, e.ID.LocalAddress, p, e.portFlags, e.bindToDevice, addr, nil /* testPort */); err != nil {
|
||||
if _, err := e.stack.ReservePort(netProtos, ProtocolNumber, e.ID.LocalAddress, p, e.portFlags, bindToDevice, addr, nil /* testPort */); err != nil {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
id := e.ID
|
||||
id.LocalPort = p
|
||||
if err := e.stack.RegisterTransportEndpoint(nicID, netProtos, ProtocolNumber, id, e, e.portFlags, e.bindToDevice); err != nil {
|
||||
e.stack.ReleasePort(netProtos, ProtocolNumber, e.ID.LocalAddress, p, e.portFlags, e.bindToDevice, addr)
|
||||
if err := e.stack.RegisterTransportEndpoint(nicID, netProtos, ProtocolNumber, id, e, e.portFlags, bindToDevice); err != nil {
|
||||
e.stack.ReleasePort(netProtos, ProtocolNumber, e.ID.LocalAddress, p, e.portFlags, bindToDevice, addr)
|
||||
if err == tcpip.ErrPortInUse {
|
||||
return false, nil
|
||||
}
|
||||
@@ -2281,7 +2269,7 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
|
||||
// the selected port.
|
||||
e.ID = id
|
||||
e.isPortReserved = true
|
||||
e.boundBindToDevice = e.bindToDevice
|
||||
e.boundBindToDevice = bindToDevice
|
||||
e.boundPortFlags = e.portFlags
|
||||
e.boundDest = addr
|
||||
return true, nil
|
||||
@@ -2634,7 +2622,8 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) (err *tcpip.Error) {
|
||||
e.ID.LocalAddress = addr.Addr
|
||||
}
|
||||
|
||||
port, err := e.stack.ReservePort(netProtos, ProtocolNumber, addr.Addr, addr.Port, e.portFlags, e.bindToDevice, tcpip.FullAddress{}, func(p uint16) bool {
|
||||
bindToDevice := tcpip.NICID(e.ops.GetBindToDevice())
|
||||
port, err := e.stack.ReservePort(netProtos, ProtocolNumber, addr.Addr, addr.Port, e.portFlags, bindToDevice, tcpip.FullAddress{}, func(p uint16) bool {
|
||||
id := e.ID
|
||||
id.LocalPort = p
|
||||
// CheckRegisterTransportEndpoint should only return an error if there is a
|
||||
@@ -2645,7 +2634,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) (err *tcpip.Error) {
|
||||
// demuxer. Further connected endpoints always have a remote
|
||||
// address/port. Hence this will only return an error if there is a matching
|
||||
// listening endpoint.
|
||||
if err := e.stack.CheckRegisterTransportEndpoint(nic, netProtos, ProtocolNumber, id, e.portFlags, e.bindToDevice); err != nil {
|
||||
if err := e.stack.CheckRegisterTransportEndpoint(nic, netProtos, ProtocolNumber, id, e.portFlags, bindToDevice); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -2654,7 +2643,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) (err *tcpip.Error) {
|
||||
return err
|
||||
}
|
||||
|
||||
e.boundBindToDevice = e.bindToDevice
|
||||
e.boundBindToDevice = bindToDevice
|
||||
e.boundPortFlags = e.portFlags
|
||||
// TODO(gvisor.dev/issue/3691): Add test to verify boundNICID is correct.
|
||||
e.boundNICID = nic
|
||||
|
||||
@@ -1380,9 +1380,8 @@ func TestConnectBindToDevice(t *testing.T) {
|
||||
defer c.Cleanup()
|
||||
|
||||
c.Create(-1)
|
||||
bindToDevice := tcpip.BindToDeviceOption(test.device)
|
||||
if err := c.EP.SetSockOpt(&bindToDevice); err != nil {
|
||||
t.Fatalf("c.EP.SetSockOpt(&%T(%d)): %s", bindToDevice, bindToDevice, err)
|
||||
if err := c.EP.SocketOptions().SetBindToDevice(int32(test.device)); err != nil {
|
||||
t.Fatalf("c.EP.SetSockOpt(&%T(%d)): %s", test.device, test.device, err)
|
||||
}
|
||||
// Start connection attempt.
|
||||
waitEntry, _ := waiter.NewChannelEntry(nil)
|
||||
@@ -4507,7 +4506,7 @@ func TestBindToDeviceOption(t *testing.T) {
|
||||
name string
|
||||
setBindToDevice *tcpip.NICID
|
||||
setBindToDeviceError *tcpip.Error
|
||||
getBindToDevice tcpip.BindToDeviceOption
|
||||
getBindToDevice int32
|
||||
}{
|
||||
{"GetDefaultValue", nil, nil, 0},
|
||||
{"BindToNonExistent", nicIDPtr(999), tcpip.ErrUnknownDevice, 0},
|
||||
@@ -4517,15 +4516,13 @@ func TestBindToDeviceOption(t *testing.T) {
|
||||
for _, testAction := range testActions {
|
||||
t.Run(testAction.name, func(t *testing.T) {
|
||||
if testAction.setBindToDevice != nil {
|
||||
bindToDevice := tcpip.BindToDeviceOption(*testAction.setBindToDevice)
|
||||
if gotErr, wantErr := ep.SetSockOpt(&bindToDevice), testAction.setBindToDeviceError; gotErr != wantErr {
|
||||
bindToDevice := int32(*testAction.setBindToDevice)
|
||||
if gotErr, wantErr := ep.SocketOptions().SetBindToDevice(bindToDevice), testAction.setBindToDeviceError; gotErr != wantErr {
|
||||
t.Errorf("got SetSockOpt(&%T(%d)) = %s, want = %s", bindToDevice, bindToDevice, gotErr, wantErr)
|
||||
}
|
||||
}
|
||||
bindToDevice := tcpip.BindToDeviceOption(88888)
|
||||
if err := ep.GetSockOpt(&bindToDevice); err != nil {
|
||||
t.Errorf("GetSockOpt(&%T): %s", bindToDevice, err)
|
||||
} else if bindToDevice != testAction.getBindToDevice {
|
||||
bindToDevice := ep.SocketOptions().GetBindToDevice()
|
||||
if bindToDevice != testAction.getBindToDevice {
|
||||
t.Errorf("got bindToDevice = %d, want %d", bindToDevice, testAction.getBindToDevice)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -109,7 +109,6 @@ type endpoint struct {
|
||||
multicastAddr tcpip.Address
|
||||
multicastNICID tcpip.NICID
|
||||
portFlags ports.Flags
|
||||
bindToDevice tcpip.NICID
|
||||
|
||||
lastErrorMu sync.Mutex `state:"nosave"`
|
||||
lastError *tcpip.Error `state:".(string)"`
|
||||
@@ -659,6 +658,10 @@ func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpoint) HasNIC(id int32) bool {
|
||||
return id == 0 || e.stack.HasNIC(tcpip.NICID(id))
|
||||
}
|
||||
|
||||
// SetSockOpt implements tcpip.Endpoint.SetSockOpt.
|
||||
func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
|
||||
switch v := opt.(type) {
|
||||
@@ -775,15 +778,6 @@ func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
|
||||
|
||||
delete(e.multicastMemberships, memToRemove)
|
||||
|
||||
case *tcpip.BindToDeviceOption:
|
||||
id := tcpip.NICID(*v)
|
||||
if id != 0 && !e.stack.HasNIC(id) {
|
||||
return tcpip.ErrUnknownDevice
|
||||
}
|
||||
e.mu.Lock()
|
||||
e.bindToDevice = id
|
||||
e.mu.Unlock()
|
||||
|
||||
case *tcpip.SocketDetachFilterOption:
|
||||
return nil
|
||||
}
|
||||
@@ -859,11 +853,6 @@ func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
|
||||
}
|
||||
e.mu.Unlock()
|
||||
|
||||
case *tcpip.BindToDeviceOption:
|
||||
e.mu.RLock()
|
||||
*o = tcpip.BindToDeviceOption(e.bindToDevice)
|
||||
e.mu.RUnlock()
|
||||
|
||||
default:
|
||||
return tcpip.ErrUnknownProtocolOption
|
||||
}
|
||||
@@ -1113,21 +1102,22 @@ func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, *tcp
|
||||
}
|
||||
|
||||
func (e *endpoint) registerWithStack(nicID tcpip.NICID, netProtos []tcpip.NetworkProtocolNumber, id stack.TransportEndpointID) (stack.TransportEndpointID, tcpip.NICID, *tcpip.Error) {
|
||||
bindToDevice := tcpip.NICID(e.ops.GetBindToDevice())
|
||||
if e.ID.LocalPort == 0 {
|
||||
port, err := e.stack.ReservePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort, e.portFlags, e.bindToDevice, tcpip.FullAddress{}, nil /* testPort */)
|
||||
port, err := e.stack.ReservePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort, e.portFlags, bindToDevice, tcpip.FullAddress{}, nil /* testPort */)
|
||||
if err != nil {
|
||||
return id, e.bindToDevice, err
|
||||
return id, bindToDevice, err
|
||||
}
|
||||
id.LocalPort = port
|
||||
}
|
||||
e.boundPortFlags = e.portFlags
|
||||
|
||||
err := e.stack.RegisterTransportEndpoint(nicID, netProtos, ProtocolNumber, id, e, e.boundPortFlags, e.bindToDevice)
|
||||
err := e.stack.RegisterTransportEndpoint(nicID, netProtos, ProtocolNumber, id, e, e.boundPortFlags, bindToDevice)
|
||||
if err != nil {
|
||||
e.stack.ReleasePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort, e.boundPortFlags, e.bindToDevice, tcpip.FullAddress{})
|
||||
e.stack.ReleasePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort, e.boundPortFlags, bindToDevice, tcpip.FullAddress{})
|
||||
e.boundPortFlags = ports.Flags{}
|
||||
}
|
||||
return id, e.bindToDevice, err
|
||||
return id, bindToDevice, err
|
||||
}
|
||||
|
||||
func (e *endpoint) bindLocked(addr tcpip.FullAddress) *tcpip.Error {
|
||||
|
||||
@@ -78,7 +78,7 @@ func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint,
|
||||
route.ResolveWith(r.pkt.SourceLinkAddress())
|
||||
|
||||
ep := newEndpoint(r.stack, r.pkt.NetworkProtocolNumber, queue)
|
||||
if err := r.stack.RegisterTransportEndpoint(r.pkt.NICID, []tcpip.NetworkProtocolNumber{r.pkt.NetworkProtocolNumber}, ProtocolNumber, r.id, ep, ep.portFlags, ep.bindToDevice); err != nil {
|
||||
if err := r.stack.RegisterTransportEndpoint(r.pkt.NICID, []tcpip.NetworkProtocolNumber{r.pkt.NetworkProtocolNumber}, ProtocolNumber, r.id, ep, ep.portFlags, tcpip.NICID(ep.ops.GetBindToDevice())); err != nil {
|
||||
ep.Close()
|
||||
route.Release()
|
||||
return nil, err
|
||||
|
||||
@@ -554,7 +554,7 @@ func TestBindToDeviceOption(t *testing.T) {
|
||||
name string
|
||||
setBindToDevice *tcpip.NICID
|
||||
setBindToDeviceError *tcpip.Error
|
||||
getBindToDevice tcpip.BindToDeviceOption
|
||||
getBindToDevice int32
|
||||
}{
|
||||
{"GetDefaultValue", nil, nil, 0},
|
||||
{"BindToNonExistent", nicIDPtr(999), tcpip.ErrUnknownDevice, 0},
|
||||
@@ -564,15 +564,13 @@ func TestBindToDeviceOption(t *testing.T) {
|
||||
for _, testAction := range testActions {
|
||||
t.Run(testAction.name, func(t *testing.T) {
|
||||
if testAction.setBindToDevice != nil {
|
||||
bindToDevice := tcpip.BindToDeviceOption(*testAction.setBindToDevice)
|
||||
if gotErr, wantErr := ep.SetSockOpt(&bindToDevice), testAction.setBindToDeviceError; gotErr != wantErr {
|
||||
bindToDevice := int32(*testAction.setBindToDevice)
|
||||
if gotErr, wantErr := ep.SocketOptions().SetBindToDevice(bindToDevice), testAction.setBindToDeviceError; gotErr != wantErr {
|
||||
t.Errorf("got SetSockOpt(&%T(%d)) = %s, want = %s", bindToDevice, bindToDevice, gotErr, wantErr)
|
||||
}
|
||||
}
|
||||
bindToDevice := tcpip.BindToDeviceOption(88888)
|
||||
if err := ep.GetSockOpt(&bindToDevice); err != nil {
|
||||
t.Errorf("GetSockOpt(&%T): %s", bindToDevice, err)
|
||||
} else if bindToDevice != testAction.getBindToDevice {
|
||||
bindToDevice := ep.SocketOptions().GetBindToDevice()
|
||||
if bindToDevice != testAction.getBindToDevice {
|
||||
t.Errorf("got bindToDevice = %d, want = %d", bindToDevice, testAction.getBindToDevice)
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user