mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-05-20 20:56:35 +00:00

New session manager leverages similar functionality that was previously provided with datagram v2, with the distinct difference that the sessions are registered via QUIC Datagrams and unregistered via timeouts only; the sessions will no longer attempt to unregister sessions remotely with the edge service. The Session Manager is shared across all QUIC connections that cloudflared uses to connect to the edge (typically 4). This will help cloudflared be able to monitor all sessions across the connections and help correlate in the future if sessions migrate across connections. The UDP payload size is still limited to 1280 bytes across all OS's. Any UDP packet that provides a payload size of greater than 1280 will cause cloudflared to report (as it currently does) a log error and drop the packet. Closes TUN-8667
88 lines
2.7 KiB
Go
88 lines
2.7 KiB
Go
package v3
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"net/netip"
|
|
"sync"
|
|
|
|
"github.com/rs/zerolog"
|
|
|
|
"github.com/cloudflare/cloudflared/ingress"
|
|
)
|
|
|
|
var (
|
|
ErrSessionNotFound = errors.New("session not found")
|
|
ErrSessionBoundToOtherConn = errors.New("session is in use by another connection")
|
|
)
|
|
|
|
type SessionManager interface {
|
|
// RegisterSession will register a new session if it does not already exist for the request ID.
|
|
// During new session creation, the session will also bind the UDP socket for the origin.
|
|
// If the session exists for a different connection, it will return [ErrSessionBoundToOtherConn].
|
|
RegisterSession(request *UDPSessionRegistrationDatagram, conn DatagramWriter) (Session, error)
|
|
// GetSession returns an active session if available for the provided connection.
|
|
// If the session does not exist, it will return [ErrSessionNotFound]. If the session exists for a different
|
|
// connection, it will return [ErrSessionBoundToOtherConn].
|
|
GetSession(requestID RequestID) (Session, error)
|
|
// UnregisterSession will remove a session from the current session manager. It will attempt to close the session
|
|
// before removal.
|
|
UnregisterSession(requestID RequestID)
|
|
}
|
|
|
|
type DialUDP func(dest netip.AddrPort) (*net.UDPConn, error)
|
|
|
|
type sessionManager struct {
|
|
sessions map[RequestID]Session
|
|
mutex sync.RWMutex
|
|
log *zerolog.Logger
|
|
}
|
|
|
|
func NewSessionManager(log *zerolog.Logger, originDialer DialUDP) SessionManager {
|
|
return &sessionManager{
|
|
sessions: make(map[RequestID]Session),
|
|
log: log,
|
|
}
|
|
}
|
|
|
|
func (s *sessionManager) RegisterSession(request *UDPSessionRegistrationDatagram, conn DatagramWriter) (Session, error) {
|
|
s.mutex.Lock()
|
|
defer s.mutex.Unlock()
|
|
// Check to make sure session doesn't already exist for requestID
|
|
_, exists := s.sessions[request.RequestID]
|
|
if exists {
|
|
return nil, ErrSessionBoundToOtherConn
|
|
}
|
|
// Attempt to bind the UDP socket for the new session
|
|
origin, err := ingress.DialUDPAddrPort(request.Dest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Create and insert the new session in the map
|
|
session := NewSession(request.RequestID, request.IdleDurationHint, origin, conn, s.log)
|
|
s.sessions[request.RequestID] = session
|
|
return session, nil
|
|
}
|
|
|
|
func (s *sessionManager) GetSession(requestID RequestID) (Session, error) {
|
|
s.mutex.RLock()
|
|
defer s.mutex.RUnlock()
|
|
session, exists := s.sessions[requestID]
|
|
if exists {
|
|
return session, nil
|
|
}
|
|
return nil, ErrSessionNotFound
|
|
}
|
|
|
|
func (s *sessionManager) UnregisterSession(requestID RequestID) {
|
|
s.mutex.Lock()
|
|
defer s.mutex.Unlock()
|
|
// Get the session and make sure to close it if it isn't already closed
|
|
session, exists := s.sessions[requestID]
|
|
if exists {
|
|
// We ignore any errors when attempting to close the session
|
|
_ = session.Close()
|
|
}
|
|
delete(s.sessions, requestID)
|
|
}
|