Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion dtlstransport.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"crypto/x509"
"errors"
"fmt"
"net"
"strings"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -319,7 +320,16 @@ func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error {
return t.failStart(err)
}

// Configure ICE for SPED after we created the DTLS transport.
if t.api.settingEngine.enableSped {
t.iceTransport.SetDtlsCallback(func(packet []byte, rAddr net.Addr) {
dtlsConn.InjectInboundPacket(packet, rAddr)
})
}

// This awaits the DTLS handshake.
if err = t.handshakeDTLS(dtlsConn); err != nil {
fmt.Println("DTLS handshake complete")
dtlsEndpoint.SetOnClose(nil)
_ = dtlsConn.Close()

Expand Down Expand Up @@ -368,7 +378,8 @@ func (t *DTLSTransport) dtlsSharedOptions(certificate tls.Certificate) []dtls.Op
dtls.WithCertificates(certificate),
dtls.WithSRTPProtectionProfiles(t.srtpProtectionProfiles()...),
dtls.WithExtendedMasterSecret(t.api.settingEngine.dtls.extendedMasterSecret),
dtls.WithInsecureSkipVerify(!t.api.settingEngine.dtls.disableInsecureSkipVerify),
// TODO: this should be the default, DTLS runs over ICE which *hopefully* checks the source.
dtls.WithInsecureSkipVerify(true),
dtls.WithLoggerFactory(t.api.settingEngine.LoggerFactory),
dtls.WithVerifyPeerCertificate(t.verifyPeerCertificateFunc()),
}
Expand All @@ -380,13 +391,28 @@ func (t *DTLSTransport) dtlsSharedOptions(certificate tls.Certificate) []dtls.Op
)
}

// TODO: should this initially be set to one day for SPED?
if t.api.settingEngine.dtls.retransmissionInterval > 0 {
sharedOpts = append(
sharedOpts,
dtls.WithFlightInterval(t.api.settingEngine.dtls.retransmissionInterval),
)
}

// Configure DTLS for SPED.
if t.api.settingEngine.enableSped {
sharedOpts = append(
sharedOpts,
dtls.WithOutboundHandshakePacketInterceptor(func(packet []byte, end bool) bool {
// Forward the packet to the ICE transport for piggybacking.
return t.iceTransport.Piggyback(packet, end)
}),
dtls.WithInboundHandshakePacketNotifier(func(packet []byte) {
t.iceTransport.ReportDtlsPacket(packet)
}),
)
}

if t.api.settingEngine.replayProtection.DTLS != nil {
sharedOpts = append(
sharedOpts,
Expand Down Expand Up @@ -559,6 +585,10 @@ func (t *DTLSTransport) completeStart(dtlsConn *dtls.Conn) error {
t.srtpProtectionProfile = srtpProtectionProfile
t.conn = dtlsConn
t.onStateChange(DTLSTransportStateConnected)
if t.api.settingEngine.enableSped {
t.iceTransport.Piggyback(nil, true)
t.iceTransport.SetDtlsCallback(nil)
}

return t.startSRTP()
}
Expand Down
3 changes: 2 additions & 1 deletion examples/warp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ func setupOfferHandler(pc **webrtc.PeerConnection) {
return
}

// Enable SNAP.
// Enable SNAP and SPED.
s := webrtc.SettingEngine{}
s.EnableSctpSnap(true)
s.EnableSped(true)
api := webrtc.NewAPI(webrtc.WithSettingEngine(s))

var err error
Expand Down
6 changes: 6 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ module github.com/pion/webrtc/v4

go 1.24.0

replace github.com/pion/sctp => /home/fippo/pion/sctp
replace github.com/pion/dtls/v3 => /home/fippo/pion/dtls
replace github.com/pion/stun/v3 => /home/fippo/pion/stun
replace github.com/pion/ice/v4 => /home/fippo/pion/ice

require (
github.com/pion/datachannel v1.6.0
github.com/pion/dtls/v3 v3.1.2
Expand All @@ -28,6 +33,7 @@ require (
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.17.0 // indirect
github.com/pion/mdns/v2 v2.1.0 // indirect
github.com/pion/transport/v3 v3.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/wlynxg/anet v0.0.5 // indirect
golang.org/x/crypto v0.48.0 // indirect
Expand Down
59 changes: 56 additions & 3 deletions icetransport.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package webrtc
import (
"context"
"fmt"
"net"
"sync"
"sync/atomic"
"time"
Expand Down Expand Up @@ -39,6 +40,8 @@ type ICETransport struct {

loggerFactory logging.LoggerFactory

dtlsCallback func(packet []byte, rAddr net.Addr)

log logging.LeveledLogger
}

Expand Down Expand Up @@ -69,7 +72,7 @@ func (t *ICETransport) GetSelectedCandidatePair() (*ICECandidatePair, error) {
}

// GetSelectedCandidatePairStats returns the selected candidate pair stats on which packets are sent
// if there is no selected pair empty stats, false is returned to indicate stats not available.
// if there is no selected pair, false is returned to indicate stats are not available.
func (t *ICETransport) GetSelectedCandidatePairStats() (ICECandidatePairStats, bool) {
return t.gatherer.getSelectedCandidatePairStats()
}
Expand Down Expand Up @@ -107,6 +110,7 @@ func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role *
if agent == nil {
return fmt.Errorf("%w: unable to start ICETransport", errICEAgentNotExist)
}
agent.SetDtlsCallback(t.dtlsCallback)

if err := agent.OnConnectionStateChange(func(iceState ice.ConnectionState) {
state := newICETransportStateFromICE(iceState)
Expand Down Expand Up @@ -145,19 +149,30 @@ func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role *
var err error
switch *role {
case ICERoleControlling:
iceConn, err = agent.Dial(ctx,
iceConn, err = agent.StartDial(
params.UsernameFragment,
params.Password)

case ICERoleControlled:
iceConn, err = agent.Accept(ctx,
iceConn, err = agent.StartAccept(
params.UsernameFragment,
params.Password)

default:
err = errICERoleUnknown
}

if err != nil {
t.lock.Lock()

return err
}

if !t.gatherer.api.settingEngine.enableSped {
// Note: this blocks until a pair is found.
err = agent.AwaitConnect(ctx)
}

// Reacquire the lock to set the connection/mux
t.lock.Lock()
if err != nil {
Expand All @@ -180,6 +195,16 @@ func (t *ICETransport) Start(gatherer *ICEGatherer, params ICEParameters, role *
return nil
}

func (t *ICETransport) SetDtlsCallback(cb func(packet []byte, rAddr net.Addr)) {
t.lock.Lock()
defer t.lock.Unlock()
if agent := t.gatherer.getAgent(); agent != nil {
agent.SetDtlsCallback(cb)
} else {
t.dtlsCallback = cb
}
}

// restart is not exposed currently because ORTC has users create a whole new ICETransport
// so for now lets keep it private so we don't cause ORTC users to depend on non-standard APIs.
func (t *ICETransport) restart() error {
Expand Down Expand Up @@ -455,3 +480,31 @@ func (t *ICETransport) setRemoteCredentials(newUfrag, newPwd string) error {

return agent.SetRemoteCredentials(newUfrag, newPwd)
}

// Piggyback forwards a raw packet to the ICE Agent.
func (t *ICETransport) Piggyback(packet []byte, end bool) bool {
t.lock.Lock()
defer t.lock.Unlock()

agent := t.gatherer.getAgent()
if agent == nil {
t.log.Warnf("%w: unable to Piggyback", errICEAgentNotExist)

return false
}

return agent.Piggyback(packet, end)
}

func (t *ICETransport) ReportDtlsPacket(packet []byte) {
t.lock.Lock()
defer t.lock.Unlock()

agent := t.gatherer.getAgent()
if agent == nil {
t.log.Warnf("%w: unable report DTLS packet", errICEAgentNotExist)

return
}
agent.ReportDtlsPacket(packet)
}
2 changes: 2 additions & 0 deletions peerconnection.go
Original file line number Diff line number Diff line change
Expand Up @@ -2770,6 +2770,7 @@ func (pc *PeerConnection) startTransports(
dtlsRole DTLSRole,
remoteUfrag, remotePwd, fingerprint, fingerprintHash string,
) {
fmt.Println("START ICE", time.Now())
// Start the ice transport
err := pc.iceTransport.Start(
pc.iceGatherer,
Expand Down Expand Up @@ -2799,6 +2800,7 @@ func (pc *PeerConnection) startTransports(
}()
}

fmt.Println("START DTLS", time.Now(), dtlsRole)
// Start the dtls transport
err = pc.dtlsTransport.Start(DTLSParameters{
Role: dtlsRole,
Expand Down
42 changes: 42 additions & 0 deletions peerconnection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package webrtc

import (
"runtime"
"strings"
"sync"
"sync/atomic"
"testing"
Expand Down Expand Up @@ -968,3 +969,44 @@ func TestICETrickleCapabilityString(t *testing.T) {
assert.Equal(t, tt.expected, tt.value.String())
}
}

func TestWarp(t *testing.T) {
s := SettingEngine{}
s.EnableSped(true)
api := NewAPI(WithSettingEngine(s))

offer, err := api.NewPeerConnection(Configuration{})
assert.NoError(t, err)
answer, err := api.NewPeerConnection(Configuration{})
assert.NoError(t, err)

peerConnectionsConnected := untilConnectionState(PeerConnectionStateConnected, offer, answer)
assert.NoError(t, signalPair(offer, answer))
peerConnectionsConnected.Wait()

closePairNow(t, offer, answer)
}

func TestWarpClient(t *testing.T) {
s := SettingEngine{}
s.EnableSped(true)
api := NewAPI(WithSettingEngine(s))

offer, err := api.NewPeerConnection(Configuration{})
assert.NoError(t, err)
answer, err := api.NewPeerConnection(Configuration{})
assert.NoError(t, err)

peerConnectionsConnected := untilConnectionState(PeerConnectionStateConnected, offer, answer)
assert.NoError(t, signalPairWithModification(
offer, answer,
func(sessionDescription string) string {
return strings.ReplaceAll(
sessionDescription,
"setup:actpass",
"setup:active")
}))
peerConnectionsConnected.Wait()

closePairNow(t, offer, answer)
}
10 changes: 8 additions & 2 deletions settingengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"time"

"github.com/pion/dtls/v3"
dtlsElliptic "github.com/pion/dtls/v3/pkg/crypto/elliptic"

Check failure on line 17 in settingengine.go

View workflow job for this annotation

GitHub Actions / test-macos (1.24) / Go macOS 1.24

github.com/pion/dtls/v3@v3.1.2: replacement directory /home/fippo/pion/dtls does not exist

Check failure on line 17 in settingengine.go

View workflow job for this annotation

GitHub Actions / test-macos (1.24) / Go macOS 1.24

github.com/pion/dtls/v3@v3.1.2: replacement directory /home/fippo/pion/dtls does not exist

Check failure on line 17 in settingengine.go

View workflow job for this annotation

GitHub Actions / test-macos (1.25) / Go macOS 1.25

github.com/pion/dtls/v3@v3.1.2: replacement directory /home/fippo/pion/dtls does not exist

Check failure on line 17 in settingengine.go

View workflow job for this annotation

GitHub Actions / test-macos (1.25) / Go macOS 1.25

github.com/pion/dtls/v3@v3.1.2: replacement directory /home/fippo/pion/dtls does not exist
"github.com/pion/dtls/v3/pkg/protocol/handshake"

Check failure on line 18 in settingengine.go

View workflow job for this annotation

GitHub Actions / test-macos (1.24) / Go macOS 1.24

github.com/pion/dtls/v3@v3.1.2: replacement directory /home/fippo/pion/dtls does not exist

Check failure on line 18 in settingengine.go

View workflow job for this annotation

GitHub Actions / test-macos (1.25) / Go macOS 1.25

github.com/pion/dtls/v3@v3.1.2: replacement directory /home/fippo/pion/dtls does not exist
"github.com/pion/ice/v4"
"github.com/pion/logging"
"github.com/pion/stun/v3"
Expand Down Expand Up @@ -117,6 +117,7 @@
dataChannelBlockWrite bool
handleUndeclaredSSRCWithoutAnswer bool
ignoreRidPauseForRecv bool
enableSped bool
}

type renominationSettings struct {
Expand Down Expand Up @@ -435,7 +436,7 @@
e.candidates.MulticastDNSHostName = hostName
}

// SetICECredentials sets a staic uFrag/uPwd to be used by pion/ice
// SetICECredentials sets a static ice-ufrag/ice-pwd to be used by pion/ice
//
// This is useful if you want to do signalless WebRTC session,
// or having a reproducible environment with static credentials.
Expand Down Expand Up @@ -477,7 +478,7 @@
}

// SetSDPMediaLevelFingerprints configures the logic for DTLS Fingerprint insertion
// If true, fingerprints will be inserted in the sdp at the fingerprint
// If true, fingerprints will be inserted in the sdp at the media
// level, instead of the session level. This helps with compatibility with
// some webrtc implementations.
func (e *SettingEngine) SetSDPMediaLevelFingerprints(sdpMediaLevelFingerprints bool) {
Expand Down Expand Up @@ -722,3 +723,8 @@
func (e *SettingEngine) SetIgnoreRidPauseForRecv(ignoreRidPauseForRecv bool) {
e.ignoreRidPauseForRecv = ignoreRidPauseForRecv
}

// EnableSped enabled SPED/dtls-in-stun.
func (e *SettingEngine) EnableSped(enable bool) {
e.enableSped = enable
}
Loading