mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add endpoint tracking to the stack.
In the future this will replace DanglingEndpoints. DanglingEndpoints must be kept for now due to issues with save/restore. This is arguably a cleaner design and allows the stack to know which transport endpoints might still be using its link endpoints. Updates #837 PiperOrigin-RevId: 277386633
This commit is contained in:
@@ -13,5 +13,8 @@ go_library(
|
||||
"test_stack.go",
|
||||
],
|
||||
importpath = "gvisor.dev/gvisor/pkg/sentry/inet",
|
||||
deps = ["//pkg/sentry/context"],
|
||||
deps = [
|
||||
"//pkg/sentry/context",
|
||||
"//pkg/tcpip/stack",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
// Package inet defines semantics for IP stacks.
|
||||
package inet
|
||||
|
||||
import "gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
|
||||
// Stack represents a TCP/IP stack.
|
||||
type Stack interface {
|
||||
// Interfaces returns all network interfaces as a mapping from interface
|
||||
@@ -58,6 +60,16 @@ type Stack interface {
|
||||
|
||||
// Resume restarts the network stack after restore.
|
||||
Resume()
|
||||
|
||||
// RegisteredEndpoints returns all endpoints which are currently registered.
|
||||
RegisteredEndpoints() []stack.TransportEndpoint
|
||||
|
||||
// CleanupEndpoints returns endpoints currently in the cleanup state.
|
||||
CleanupEndpoints() []stack.TransportEndpoint
|
||||
|
||||
// RestoreCleanupEndpoints adds endpoints to cleanup tracking. This is useful
|
||||
// for restoring a stack after a save.
|
||||
RestoreCleanupEndpoints([]stack.TransportEndpoint)
|
||||
}
|
||||
|
||||
// Interface contains information about a network interface.
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package inet
|
||||
|
||||
import "gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
|
||||
// TestStack is a dummy implementation of Stack for tests.
|
||||
type TestStack struct {
|
||||
InterfacesMap map[int32]Interface
|
||||
@@ -94,5 +96,17 @@ func (s *TestStack) RouteTable() []Route {
|
||||
}
|
||||
|
||||
// Resume implements Stack.Resume.
|
||||
func (s *TestStack) Resume() {
|
||||
func (s *TestStack) Resume() {}
|
||||
|
||||
// RegisteredEndpoints implements inet.Stack.RegisteredEndpoints.
|
||||
func (s *TestStack) RegisteredEndpoints() []stack.TransportEndpoint {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanupEndpoints implements inet.Stack.CleanupEndpoints.
|
||||
func (s *TestStack) CleanupEndpoints() []stack.TransportEndpoint {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestoreCleanupEndpoints implements inet.Stack.RestoreCleanupEndpoints.
|
||||
func (s *TestStack) RestoreCleanupEndpoints([]stack.TransportEndpoint) {}
|
||||
|
||||
@@ -32,6 +32,7 @@ go_library(
|
||||
"//pkg/sentry/usermem",
|
||||
"//pkg/syserr",
|
||||
"//pkg/syserror",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/waiter",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/usermem"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
var defaultRecvBufSize = inet.TCPBufferSize{
|
||||
@@ -442,3 +443,12 @@ func (s *Stack) RouteTable() []inet.Route {
|
||||
|
||||
// Resume implements inet.Stack.Resume.
|
||||
func (s *Stack) Resume() {}
|
||||
|
||||
// RegisteredEndpoints implements inet.Stack.RegisteredEndpoints.
|
||||
func (s *Stack) RegisteredEndpoints() []stack.TransportEndpoint { return nil }
|
||||
|
||||
// CleanupEndpoints implements inet.Stack.CleanupEndpoints.
|
||||
func (s *Stack) CleanupEndpoints() []stack.TransportEndpoint { return nil }
|
||||
|
||||
// RestoreCleanupEndpoints implements inet.Stack.RestoreCleanupEndpoints.
|
||||
func (s *Stack) RestoreCleanupEndpoints([]stack.TransportEndpoint) {}
|
||||
|
||||
@@ -291,3 +291,18 @@ func (s *Stack) FillDefaultIPTables() {
|
||||
func (s *Stack) Resume() {
|
||||
s.Stack.Resume()
|
||||
}
|
||||
|
||||
// RegisteredEndpoints implements inet.Stack.RegisteredEndpoints.
|
||||
func (s *Stack) RegisteredEndpoints() []stack.TransportEndpoint {
|
||||
return s.Stack.RegisteredEndpoints()
|
||||
}
|
||||
|
||||
// CleanupEndpoints implements inet.Stack.CleanupEndpoints.
|
||||
func (s *Stack) CleanupEndpoints() []stack.TransportEndpoint {
|
||||
return s.Stack.CleanupEndpoints()
|
||||
}
|
||||
|
||||
// RestoreCleanupEndpoints implements inet.Stack.RestoreCleanupEndpoints.
|
||||
func (s *Stack) RestoreCleanupEndpoints(es []stack.TransportEndpoint) {
|
||||
s.Stack.RestoreCleanupEndpoints(es)
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ go_library(
|
||||
"//pkg/syserror",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/unet",
|
||||
"//pkg/waiter",
|
||||
],
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/rpcinet/conn"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/rpcinet/notifier"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/unet"
|
||||
)
|
||||
|
||||
@@ -165,3 +166,12 @@ func (s *Stack) RouteTable() []inet.Route {
|
||||
|
||||
// Resume implements inet.Stack.Resume.
|
||||
func (s *Stack) Resume() {}
|
||||
|
||||
// RegisteredEndpoints implements inet.Stack.RegisteredEndpoints.
|
||||
func (s *Stack) RegisteredEndpoints() []stack.TransportEndpoint { return nil }
|
||||
|
||||
// CleanupEndpoints implements inet.Stack.CleanupEndpoints.
|
||||
func (s *Stack) CleanupEndpoints() []stack.TransportEndpoint { return nil }
|
||||
|
||||
// RestoreCleanupEndpoints implements inet.Stack.RestoreCleanupEndpoints.
|
||||
func (s *Stack) RestoreCleanupEndpoints([]stack.TransportEndpoint) {}
|
||||
|
||||
@@ -361,9 +361,10 @@ type Stack struct {
|
||||
|
||||
linkAddrCache *linkAddrCache
|
||||
|
||||
mu sync.RWMutex
|
||||
nics map[tcpip.NICID]*NIC
|
||||
forwarding bool
|
||||
mu sync.RWMutex
|
||||
nics map[tcpip.NICID]*NIC
|
||||
forwarding bool
|
||||
cleanupEndpoints map[TransportEndpoint]struct{}
|
||||
|
||||
// route is the route table passed in by the user via SetRouteTable(),
|
||||
// it is used by FindRoute() to build a route for a specific
|
||||
@@ -513,6 +514,7 @@ func New(opts Options) *Stack {
|
||||
networkProtocols: make(map[tcpip.NetworkProtocolNumber]NetworkProtocol),
|
||||
linkAddrResolvers: make(map[tcpip.NetworkProtocolNumber]LinkAddressResolver),
|
||||
nics: make(map[tcpip.NICID]*NIC),
|
||||
cleanupEndpoints: make(map[TransportEndpoint]struct{}),
|
||||
linkAddrCache: newLinkAddrCache(ageLimit, resolutionTimeout, resolutionAttempts),
|
||||
PortManager: ports.NewPortManager(),
|
||||
clock: clock,
|
||||
@@ -1136,6 +1138,25 @@ func (s *Stack) UnregisterTransportEndpoint(nicID tcpip.NICID, netProtos []tcpip
|
||||
s.demux.unregisterEndpoint(netProtos, protocol, id, ep, bindToDevice)
|
||||
}
|
||||
|
||||
// StartTransportEndpointCleanup removes the endpoint with the given id from
|
||||
// the stack transport dispatcher. It also transitions it to the cleanup stage.
|
||||
func (s *Stack) StartTransportEndpointCleanup(nicID tcpip.NICID, netProtos []tcpip.NetworkProtocolNumber, protocol tcpip.TransportProtocolNumber, id TransportEndpointID, ep TransportEndpoint, bindToDevice tcpip.NICID) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
s.cleanupEndpoints[ep] = struct{}{}
|
||||
|
||||
s.demux.unregisterEndpoint(netProtos, protocol, id, ep, bindToDevice)
|
||||
}
|
||||
|
||||
// CompleteTransportEndpointCleanup removes the endpoint from the cleanup
|
||||
// stage.
|
||||
func (s *Stack) CompleteTransportEndpointCleanup(ep TransportEndpoint) {
|
||||
s.mu.Lock()
|
||||
delete(s.cleanupEndpoints, ep)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
// RegisterRawTransportEndpoint registers the given endpoint with the stack
|
||||
// transport dispatcher. Received packets that match the provided transport
|
||||
// protocol will be delivered to the given endpoint.
|
||||
@@ -1157,6 +1178,38 @@ func (s *Stack) RegisterRestoredEndpoint(e ResumableEndpoint) {
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
// RegisteredEndpoints returns all endpoints which are currently registered.
|
||||
func (s *Stack) RegisteredEndpoints() []TransportEndpoint {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
var es []TransportEndpoint
|
||||
for _, e := range s.demux.protocol {
|
||||
es = append(es, e.transportEndpoints()...)
|
||||
}
|
||||
return es
|
||||
}
|
||||
|
||||
// CleanupEndpoints returns endpoints currently in the cleanup state.
|
||||
func (s *Stack) CleanupEndpoints() []TransportEndpoint {
|
||||
s.mu.Lock()
|
||||
es := make([]TransportEndpoint, 0, len(s.cleanupEndpoints))
|
||||
for e := range s.cleanupEndpoints {
|
||||
es = append(es, e)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
return es
|
||||
}
|
||||
|
||||
// RestoreCleanupEndpoints adds endpoints to cleanup tracking. This is useful
|
||||
// for restoring a stack after a save.
|
||||
func (s *Stack) RestoreCleanupEndpoints(es []TransportEndpoint) {
|
||||
s.mu.Lock()
|
||||
for _, e := range es {
|
||||
s.cleanupEndpoints[e] = struct{}{}
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
// Resume restarts the stack after a restore. This must be called after the
|
||||
// entire system has been restored.
|
||||
func (s *Stack) Resume() {
|
||||
|
||||
@@ -41,6 +41,31 @@ type transportEndpoints struct {
|
||||
rawEndpoints []RawTransportEndpoint
|
||||
}
|
||||
|
||||
// unregisterEndpoint unregisters the endpoint with the given id such that it
|
||||
// won't receive any more packets.
|
||||
func (eps *transportEndpoints) unregisterEndpoint(id TransportEndpointID, ep TransportEndpoint, bindToDevice tcpip.NICID) {
|
||||
eps.mu.Lock()
|
||||
defer eps.mu.Unlock()
|
||||
epsByNic, ok := eps.endpoints[id]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !epsByNic.unregisterEndpoint(bindToDevice, ep) {
|
||||
return
|
||||
}
|
||||
delete(eps.endpoints, id)
|
||||
}
|
||||
|
||||
func (eps *transportEndpoints) transportEndpoints() []TransportEndpoint {
|
||||
eps.mu.RLock()
|
||||
defer eps.mu.RUnlock()
|
||||
es := make([]TransportEndpoint, 0, len(eps.endpoints))
|
||||
for _, e := range eps.endpoints {
|
||||
es = append(es, e.transportEndpoints()...)
|
||||
}
|
||||
return es
|
||||
}
|
||||
|
||||
type endpointsByNic struct {
|
||||
mu sync.RWMutex
|
||||
endpoints map[tcpip.NICID]*multiPortEndpoint
|
||||
@@ -48,6 +73,16 @@ type endpointsByNic struct {
|
||||
seed uint32
|
||||
}
|
||||
|
||||
func (epsByNic *endpointsByNic) transportEndpoints() []TransportEndpoint {
|
||||
epsByNic.mu.RLock()
|
||||
defer epsByNic.mu.RUnlock()
|
||||
var eps []TransportEndpoint
|
||||
for _, ep := range epsByNic.endpoints {
|
||||
eps = append(eps, ep.transportEndpoints()...)
|
||||
}
|
||||
return eps
|
||||
}
|
||||
|
||||
// HandlePacket is called by the stack when new packets arrive to this transport
|
||||
// endpoint.
|
||||
func (epsByNic *endpointsByNic) handlePacket(r *Route, id TransportEndpointID, vv buffer.VectorisedView) {
|
||||
@@ -127,21 +162,6 @@ func (epsByNic *endpointsByNic) unregisterEndpoint(bindToDevice tcpip.NICID, t T
|
||||
return len(epsByNic.endpoints) == 0
|
||||
}
|
||||
|
||||
// unregisterEndpoint unregisters the endpoint with the given id such that it
|
||||
// won't receive any more packets.
|
||||
func (eps *transportEndpoints) unregisterEndpoint(id TransportEndpointID, ep TransportEndpoint, bindToDevice tcpip.NICID) {
|
||||
eps.mu.Lock()
|
||||
defer eps.mu.Unlock()
|
||||
epsByNic, ok := eps.endpoints[id]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !epsByNic.unregisterEndpoint(bindToDevice, ep) {
|
||||
return
|
||||
}
|
||||
delete(eps.endpoints, id)
|
||||
}
|
||||
|
||||
// transportDemuxer demultiplexes packets targeted at a transport endpoint
|
||||
// (i.e., after they've been parsed by the network layer). It does two levels
|
||||
// of demultiplexing: first based on the network and transport protocols, then
|
||||
@@ -183,14 +203,27 @@ func (d *transportDemuxer) registerEndpoint(netProtos []tcpip.NetworkProtocolNum
|
||||
// multiPortEndpoint is a container for TransportEndpoints which are bound to
|
||||
// the same pair of address and port. endpointsArr always has at least one
|
||||
// element.
|
||||
//
|
||||
// FIXME(gvisor.dev/issue/873): Restore this properly. Currently, we just save
|
||||
// this to ensure that the underlying endpoints get saved/restored, but not not
|
||||
// use the restored copy.
|
||||
//
|
||||
// +stateify savable
|
||||
type multiPortEndpoint struct {
|
||||
mu sync.RWMutex
|
||||
mu sync.RWMutex `state:"nosave"`
|
||||
endpointsArr []TransportEndpoint
|
||||
endpointsMap map[TransportEndpoint]int
|
||||
// reuse indicates if more than one endpoint is allowed.
|
||||
reuse bool
|
||||
}
|
||||
|
||||
func (ep *multiPortEndpoint) transportEndpoints() []TransportEndpoint {
|
||||
ep.mu.RLock()
|
||||
eps := append([]TransportEndpoint(nil), ep.endpointsArr...)
|
||||
ep.mu.RUnlock()
|
||||
return eps
|
||||
}
|
||||
|
||||
// reciprocalScale scales a value into range [0, n).
|
||||
//
|
||||
// This is similar to val % n, but faster.
|
||||
|
||||
@@ -218,8 +218,7 @@ func (f *fakeTransportEndpoint) State() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (f *fakeTransportEndpoint) ModerateRecvBuf(copied int) {
|
||||
}
|
||||
func (f *fakeTransportEndpoint) ModerateRecvBuf(copied int) {}
|
||||
|
||||
func (f *fakeTransportEndpoint) IPTables() (iptables.IPTables, error) {
|
||||
return iptables.IPTables{}, nil
|
||||
|
||||
@@ -686,7 +686,7 @@ func (e *endpoint) Close() {
|
||||
// in Listen() when trying to register.
|
||||
if e.state == StateListen && e.isPortReserved {
|
||||
if e.isRegistered {
|
||||
e.stack.UnregisterTransportEndpoint(e.boundNICID, e.effectiveNetProtos, ProtocolNumber, e.ID, e, e.bindToDevice)
|
||||
e.stack.StartTransportEndpointCleanup(e.boundNICID, e.effectiveNetProtos, ProtocolNumber, e.ID, e, e.bindToDevice)
|
||||
e.isRegistered = false
|
||||
}
|
||||
|
||||
@@ -747,7 +747,7 @@ func (e *endpoint) cleanupLocked() {
|
||||
e.workerCleanup = false
|
||||
|
||||
if e.isRegistered {
|
||||
e.stack.UnregisterTransportEndpoint(e.boundNICID, e.effectiveNetProtos, ProtocolNumber, e.ID, e, e.bindToDevice)
|
||||
e.stack.StartTransportEndpointCleanup(e.boundNICID, e.effectiveNetProtos, ProtocolNumber, e.ID, e, e.bindToDevice)
|
||||
e.isRegistered = false
|
||||
}
|
||||
|
||||
@@ -757,6 +757,7 @@ func (e *endpoint) cleanupLocked() {
|
||||
}
|
||||
|
||||
e.route.Release()
|
||||
e.stack.CompleteTransportEndpointCleanup(e)
|
||||
tcpip.DeleteDanglingEndpoint(e)
|
||||
}
|
||||
|
||||
|
||||
@@ -193,8 +193,10 @@ func (e *endpoint) Resume(s *stack.Stack) {
|
||||
if len(e.BindAddr) == 0 {
|
||||
e.BindAddr = e.ID.LocalAddress
|
||||
}
|
||||
if err := e.Bind(tcpip.FullAddress{Addr: e.BindAddr, Port: e.ID.LocalPort}); err != nil {
|
||||
panic("endpoint binding failed: " + err.String())
|
||||
addr := e.BindAddr
|
||||
port := e.ID.LocalPort
|
||||
if err := e.Bind(tcpip.FullAddress{Addr: addr, Port: port}); err != nil {
|
||||
panic(fmt.Sprintf("endpoint binding [%v]:%d failed: %v", addr, port, err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,6 +267,7 @@ func (e *endpoint) Resume(s *stack.Stack) {
|
||||
}
|
||||
fallthrough
|
||||
case StateError:
|
||||
e.stack.CompleteTransportEndpointCleanup(e)
|
||||
tcpip.DeleteDanglingEndpoint(e)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user