mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Bind loopback subnets' lifetime to perm address
The lifetime of addreses in a loopback interface's associated subnets should be bound to their respective permanent addresses. This change also fixes a race when the stack attempts to get an IPv4 rereferencedNetworkEndpoint for an address in an associated subnet on a loopback interface. Before this change, the stack would only check if an IPv4 address is contained in an associated subnet while holding a read lock but wouldn't do this same check after releasing the read lock for a write lock to create a temporary address. This may cause the stack to bind the lifetime of the address to a new (temporary) endpoint instead of the associated subnet's permanent address. Test: integration_test.TestLoopbackSubnetLifetimeBoundToAddr PiperOrigin-RevId: 332094719
This commit is contained in:
committed by
gVisor bot
parent
3749e70a69
commit
29ce0ad160
+12
-32
@@ -665,33 +665,15 @@ func (n *NIC) getRefOrCreateTemp(protocol tcpip.NetworkProtocolNumber, address t
|
||||
}
|
||||
}
|
||||
|
||||
// Check if address is a broadcast address for the endpoint's network.
|
||||
//
|
||||
// Only IPv4 has a notion of broadcast addresses.
|
||||
if protocol == header.IPv4ProtocolNumber {
|
||||
if ref := n.getRefForBroadcastRLocked(address); ref != nil {
|
||||
if ref := n.getIPv4RefForBroadcastOrLoopbackRLocked(address); ref != nil {
|
||||
n.mu.RUnlock()
|
||||
return ref
|
||||
}
|
||||
}
|
||||
|
||||
// A usable reference was not found, create a temporary one if requested by
|
||||
// the caller or if the IPv4 address is found in the NIC's subnets and the NIC
|
||||
// is a loopback interface.
|
||||
createTempEP := spoofingOrPromiscuous
|
||||
if !createTempEP && n.isLoopback() && protocol == header.IPv4ProtocolNumber {
|
||||
for _, r := range n.mu.endpoints {
|
||||
addr := r.addrWithPrefix()
|
||||
subnet := addr.Subnet()
|
||||
if subnet.Contains(address) {
|
||||
createTempEP = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
n.mu.RUnlock()
|
||||
|
||||
if !createTempEP {
|
||||
if !spoofingOrPromiscuous {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -704,20 +686,21 @@ func (n *NIC) getRefOrCreateTemp(protocol tcpip.NetworkProtocolNumber, address t
|
||||
return ref
|
||||
}
|
||||
|
||||
// getRefForBroadcastLocked returns an endpoint where address is the IPv4
|
||||
// broadcast address for the endpoint's network.
|
||||
// getRefForBroadcastOrLoopbackRLocked returns an endpoint whose address is the
|
||||
// broadcast address for the endpoint's network or an address in the endpoint's
|
||||
// subnet if the NIC is a loopback interface. This matches linux behaviour.
|
||||
//
|
||||
// n.mu MUST be read locked.
|
||||
func (n *NIC) getRefForBroadcastRLocked(address tcpip.Address) *referencedNetworkEndpoint {
|
||||
// n.mu MUST be read or write locked.
|
||||
func (n *NIC) getIPv4RefForBroadcastOrLoopbackRLocked(address tcpip.Address) *referencedNetworkEndpoint {
|
||||
for _, ref := range n.mu.endpoints {
|
||||
// Only IPv4 has a notion of broadcast addresses.
|
||||
// Only IPv4 has a notion of broadcast addresses or considers the loopback
|
||||
// interface bound to an address's whole subnet (on linux).
|
||||
if ref.protocol != header.IPv4ProtocolNumber {
|
||||
continue
|
||||
}
|
||||
|
||||
addr := ref.addrWithPrefix()
|
||||
subnet := addr.Subnet()
|
||||
if subnet.IsBroadcast(address) && ref.tryIncRef() {
|
||||
subnet := ref.addrWithPrefix().Subnet()
|
||||
if (subnet.IsBroadcast(address) || (n.isLoopback() && subnet.Contains(address))) && ref.isValidForOutgoingRLocked() && ref.tryIncRef() {
|
||||
return ref
|
||||
}
|
||||
}
|
||||
@@ -745,11 +728,8 @@ func (n *NIC) getRefOrCreateTempLocked(protocol tcpip.NetworkProtocolNumber, add
|
||||
n.removeEndpointLocked(ref)
|
||||
}
|
||||
|
||||
// Check if address is a broadcast address for an endpoint's network.
|
||||
//
|
||||
// Only IPv4 has a notion of broadcast addresses.
|
||||
if protocol == header.IPv4ProtocolNumber {
|
||||
if ref := n.getRefForBroadcastRLocked(address); ref != nil {
|
||||
if ref := n.getIPv4RefForBroadcastOrLoopbackRLocked(address); ref != nil {
|
||||
return ref
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,3 +187,64 @@ func TestLoopbackAcceptAllInSubnet(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoopbackSubnetLifetimeBoundToAddr tests that the lifetime of an address
|
||||
// in a loopback interface's associated subnet is bound to the permanently bound
|
||||
// address.
|
||||
func TestLoopbackSubnetLifetimeBoundToAddr(t *testing.T) {
|
||||
const nicID = 1
|
||||
|
||||
protoAddr := tcpip.ProtocolAddress{
|
||||
Protocol: ipv4.ProtocolNumber,
|
||||
AddressWithPrefix: ipv4Addr,
|
||||
}
|
||||
addrBytes := []byte(ipv4Addr.Address)
|
||||
addrBytes[len(addrBytes)-1]++
|
||||
otherAddr := tcpip.Address(addrBytes)
|
||||
|
||||
s := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol()},
|
||||
})
|
||||
if err := s.CreateNIC(nicID, loopback.New()); err != nil {
|
||||
t.Fatalf("s.CreateNIC(%d, _): %s", nicID, err)
|
||||
}
|
||||
if err := s.AddProtocolAddress(nicID, protoAddr); err != nil {
|
||||
t.Fatalf("s.AddProtocolAddress(%d, %#v): %s", nicID, protoAddr, err)
|
||||
}
|
||||
s.SetRouteTable([]tcpip.Route{
|
||||
tcpip.Route{
|
||||
Destination: header.IPv4EmptySubnet,
|
||||
NIC: nicID,
|
||||
},
|
||||
})
|
||||
|
||||
r, err := s.FindRoute(nicID, otherAddr, remoteIPv4Addr, ipv4.ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
t.Fatalf("s.FindRoute(%d, %s, %s, %d, false): %s", nicID, otherAddr, remoteIPv4Addr, ipv4.ProtocolNumber, err)
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
params := stack.NetworkHeaderParams{
|
||||
Protocol: 111,
|
||||
TTL: 64,
|
||||
TOS: stack.DefaultTOS,
|
||||
}
|
||||
data := buffer.View([]byte{1, 2, 3, 4})
|
||||
if err := r.WritePacket(nil /* gso */, params, stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(r.MaxHeaderLength()),
|
||||
Data: data.ToVectorisedView(),
|
||||
})); err != nil {
|
||||
t.Fatalf("r.WritePacket(nil, %#v, _): %s", params, err)
|
||||
}
|
||||
|
||||
// Removing the address should make the endpoint invalid.
|
||||
if err := s.RemoveAddress(nicID, protoAddr.AddressWithPrefix.Address); err != nil {
|
||||
t.Fatalf("s.RemoveAddress(%d, %s): %s", nicID, protoAddr.AddressWithPrefix.Address, err)
|
||||
}
|
||||
if err := r.WritePacket(nil /* gso */, params, stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(r.MaxHeaderLength()),
|
||||
Data: data.ToVectorisedView(),
|
||||
})); err != tcpip.ErrInvalidEndpointState {
|
||||
t.Fatalf("got r.WritePacket(nil, %#v, _) = %s, want = %s", params, err, tcpip.ErrInvalidEndpointState)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user