Do not allow unbinding network protocol

Once a packet socket is bound to a network protocol, it cannot be
unbound from that protocol; the network protocol binding may only be
updated to a different network protocol.

To comply with Linux.

PiperOrigin-RevId: 397810878
This commit is contained in:
Ghanan Gowripalan
2021-09-20 12:17:13 -07:00
committed by gVisor bot
parent d139087b3f
commit 5951ec5bce
3 changed files with 29 additions and 22 deletions
+21 -19
View File
@@ -82,11 +82,9 @@ type endpoint struct {
mu sync.RWMutex `state:"nosave"`
// +checklocks:mu
netProto tcpip.NetworkProtocolNumber
// +checklocks:mu
closed bool
// +checklocks:mu
bound bool
boundNetProto tcpip.NetworkProtocolNumber
// +checklocks:mu
boundNIC tcpip.NICID
@@ -98,10 +96,10 @@ type endpoint struct {
// NewEndpoint returns a new packet endpoint.
func NewEndpoint(s *stack.Stack, cooked bool, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
ep := &endpoint{
stack: s,
cooked: cooked,
netProto: netProto,
waiterQueue: waiterQueue,
stack: s,
cooked: cooked,
boundNetProto: netProto,
waiterQueue: waiterQueue,
}
ep.ops.InitHandler(ep, ep.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
ep.ops.SetReceiveBufferSize(32*1024, false /* notify */)
@@ -137,7 +135,7 @@ func (ep *endpoint) Close() {
return
}
ep.stack.UnregisterPacketEndpoint(0, ep.netProto, ep)
ep.stack.UnregisterPacketEndpoint(ep.boundNIC, ep.boundNetProto, ep)
ep.rcvMu.Lock()
defer ep.rcvMu.Unlock()
@@ -150,7 +148,6 @@ func (ep *endpoint) Close() {
}
ep.closed = true
ep.bound = false
ep.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.ReadableEvents | waiter.WritableEvents)
}
@@ -211,7 +208,7 @@ func (ep *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tc
ep.mu.Lock()
closed := ep.closed
nicID := ep.boundNIC
proto := ep.netProto
proto := ep.boundNetProto
ep.mu.Unlock()
if closed {
return 0, &tcpip.ErrClosedForSend{}
@@ -294,24 +291,29 @@ func (ep *endpoint) Bind(addr tcpip.FullAddress) tcpip.Error {
defer ep.mu.Unlock()
netProto := tcpip.NetworkProtocolNumber(addr.Port)
if ep.bound && ep.boundNIC == addr.NIC && ep.netProto == netProto {
// If the NIC being bound is the same then just return success.
if netProto == 0 {
// Do not allow unbinding the network protocol.
netProto = ep.boundNetProto
}
if ep.boundNIC == addr.NIC && ep.boundNetProto == netProto {
// Already bound to the requested NIC and network protocol.
return nil
}
// Unregister endpoint with all the nics.
ep.stack.UnregisterPacketEndpoint(0, ep.netProto, ep)
ep.bound = false
// TODO(https://gvisor.dev/issue/6618): Unregister after registering the new
// binding.
ep.stack.UnregisterPacketEndpoint(ep.boundNIC, ep.boundNetProto, ep)
ep.boundNIC = 0
ep.boundNetProto = 0
// Bind endpoint to receive packets from specific interface.
if err := ep.stack.RegisterPacketEndpoint(addr.NIC, netProto, ep); err != nil {
return err
}
ep.bound = true
ep.boundNIC = addr.NIC
ep.netProto = netProto
ep.boundNetProto = netProto
return nil
}
@@ -473,7 +475,7 @@ func (*endpoint) State() uint32 {
func (ep *endpoint) Info() tcpip.EndpointInfo {
ep.mu.RLock()
defer ep.mu.RUnlock()
return &stack.TransportEndpointInfo{NetProto: ep.netProto}
return &stack.TransportEndpointInfo{NetProto: ep.boundNetProto}
}
// Stats returns a pointer to the endpoint stats.
+3 -3
View File
@@ -15,6 +15,7 @@
package packet
import (
"fmt"
"time"
"gvisor.dev/gvisor/pkg/tcpip"
@@ -57,9 +58,8 @@ func (ep *endpoint) afterLoad() {
ep.stack = stack.StackFromEnv
ep.ops.InitHandler(ep, ep.stack, tcpip.GetStackSendBufferLimits, tcpip.GetStackReceiveBufferLimits)
// TODO(gvisor.dev/173): Once bind is supported, choose the right NIC.
if err := ep.stack.RegisterPacketEndpoint(0, ep.netProto, ep); err != nil {
panic(err)
if err := ep.stack.RegisterPacketEndpoint(ep.boundNIC, ep.boundNetProto, ep); err != nil {
panic(fmt.Sprintf("RegisterPacketEndpoint(%d, %d, _): %s", ep.boundNIC, ep.boundNetProto, err))
}
ep.rcvMu.Lock()
+5
View File
@@ -214,6 +214,11 @@ TEST_P(PacketSocketTest, RebindProtocol) {
ASSERT_NO_FATAL_FAILURE(bind_to_network_protocol(ETH_P_IP));
ASSERT_NO_FATAL_FAILURE(send_udp_message(++counter));
ASSERT_NO_FATAL_FAILURE(test_recv(counter));
// A zero valued protocol number should not change the bound network protocol.
ASSERT_NO_FATAL_FAILURE(bind_to_network_protocol(0));
ASSERT_NO_FATAL_FAILURE(send_udp_message(++counter));
ASSERT_NO_FATAL_FAILURE(test_recv(counter));
}
INSTANTIATE_TEST_SUITE_P(AllPacketSocketTests, PacketSocketTest,