mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-05-11 06:36:35 +00:00

The idle period is set to 5sec. We now also ping every second since last activity. This makes the quic.Connection less prone to being closed with no network activity, since we send multiple pings per idle period, and thus a single packet loss cannot cause the problem.
92 lines
3.1 KiB
Go
92 lines
3.1 KiB
Go
package quic
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/lucas-clemente/quic-go"
|
|
"github.com/pkg/errors"
|
|
"github.com/rs/zerolog"
|
|
)
|
|
|
|
const (
|
|
sessionIDLen = len(uuid.UUID{})
|
|
)
|
|
|
|
type DatagramMuxer struct {
|
|
session quic.Connection
|
|
logger *zerolog.Logger
|
|
}
|
|
|
|
func NewDatagramMuxer(quicSession quic.Connection, logger *zerolog.Logger) (*DatagramMuxer, error) {
|
|
return &DatagramMuxer{
|
|
session: quicSession,
|
|
logger: logger,
|
|
}, nil
|
|
}
|
|
|
|
// SendTo suffix the session ID to the payload so the other end of the QUIC session can demultiplex
|
|
// the payload from multiple datagram sessions
|
|
func (dm *DatagramMuxer) SendTo(sessionID uuid.UUID, payload []byte) error {
|
|
if len(payload) > maxDatagramPayloadSize {
|
|
// TODO: TUN-5302 return ICMP packet too big message
|
|
return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(payload), dm.MTU())
|
|
}
|
|
msgWithID, err := suffixSessionID(sessionID, payload)
|
|
if err != nil {
|
|
return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped")
|
|
}
|
|
if err := dm.session.SendMessage(msgWithID); err != nil {
|
|
return errors.Wrap(err, "Failed to send datagram back to edge")
|
|
}
|
|
dm.logger.Debug().Str("sessionID", sessionID.String()).Int("bytes", len(payload)).Msg("Send datagram back to edge")
|
|
return nil
|
|
}
|
|
|
|
// ReceiveFrom extracts datagram session ID, then sends the session ID and payload to session manager
|
|
// which determines how to proxy to the origin. It assumes the datagram session has already been
|
|
// registered with session manager through other side channel
|
|
func (dm *DatagramMuxer) ReceiveFrom() (uuid.UUID, []byte, error) {
|
|
msg, err := dm.session.ReceiveMessage()
|
|
if err != nil {
|
|
return uuid.Nil, nil, err
|
|
}
|
|
sessionID, payload, err := extractSessionID(msg)
|
|
if err != nil {
|
|
return uuid.Nil, nil, err
|
|
}
|
|
dm.logger.Debug().Str("sessionID", sessionID.String()).Int("bytes", len(payload)).Msg("Received datagram from edge")
|
|
return sessionID, payload, nil
|
|
}
|
|
|
|
// Maximum application payload to send to / receive from QUIC datagram frame
|
|
func (dm *DatagramMuxer) MTU() int {
|
|
return maxDatagramPayloadSize
|
|
}
|
|
|
|
// Each QUIC datagram should be suffixed with session ID.
|
|
// extractSessionID extracts the session ID and a slice with only the payload
|
|
func extractSessionID(b []byte) (uuid.UUID, []byte, error) {
|
|
msgLen := len(b)
|
|
if msgLen < sessionIDLen {
|
|
return uuid.Nil, nil, fmt.Errorf("session ID has %d bytes, but data only has %d", sessionIDLen, len(b))
|
|
}
|
|
// Parse last 16 bytess as UUID and remove it from slice
|
|
sessionID, err := uuid.FromBytes(b[len(b)-sessionIDLen:])
|
|
if err != nil {
|
|
return uuid.Nil, nil, err
|
|
}
|
|
b = b[:len(b)-sessionIDLen]
|
|
return sessionID, b, nil
|
|
}
|
|
|
|
// SuffixSessionID appends the session ID at the end of the payload. Suffix is more performant than prefix because
|
|
// the payload slice might already have enough capacity to append the session ID at the end
|
|
func suffixSessionID(sessionID uuid.UUID, b []byte) ([]byte, error) {
|
|
if len(b)+len(sessionID) > MaxDatagramFrameSize {
|
|
return nil, fmt.Errorf("datagram size exceed %d", MaxDatagramFrameSize)
|
|
}
|
|
b = append(b, sessionID[:]...)
|
|
return b, nil
|
|
}
|