Skip to content

webrtcprivate: add transport#2576

Closed
sukunrt wants to merge 22 commits into
masterfrom
webrtcprivate/transport
Closed

webrtcprivate: add transport#2576
sukunrt wants to merge 22 commits into
masterfrom
webrtcprivate/transport

Conversation

@sukunrt
Copy link
Copy Markdown
Member

@sukunrt sukunrt commented Sep 20, 2023

Part of: #2009

Depends on: #2586

@p-shahi p-shahi linked an issue Sep 20, 2023 that may be closed by this pull request
6 tasks
@sukunrt sukunrt force-pushed the webrtcprivate/transport branch 9 times, most recently from 912a8ee to 7a47233 Compare September 26, 2023 06:11
@sukunrt sukunrt force-pushed the webrtcprivate/transport branch 2 times, most recently from 6325e4e to 5cad220 Compare October 2, 2023 15:51
@sukunrt sukunrt force-pushed the webrtcprivate/transport branch 2 times, most recently from 5adc210 to eb5a01d Compare October 12, 2023 09:47
@marten-seemann
Copy link
Copy Markdown
Contributor

#2586 was merged. @sukunrt Can you rebase please?

@sukunrt sukunrt force-pushed the webrtcprivate/transport branch from eb5a01d to 962b26a Compare October 13, 2023 08:31
Copy link
Copy Markdown
Contributor

@marten-seemann marten-seemann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of the non-transport parts of this PR.

Comment thread config/config.go
Constructor interface{}
}

type ICEServer = webrtc.ICEServer
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to redeclare this? Is it a requirement for Fx?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. Just so that we redefine it a different way later if we want. But I think it's fine to remove this and just take the pion specific object. We can make a breaking change if this needs to be changed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s a breaking change anyway if we change the underlying type. That should be fine.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove it?

Comment thread options.go
}
}

func EnableWebRTCPrivate(stunServers []config.ICEServer) Option {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a separate option? We already have libp2p.Transport to configure transports. I had hoped that we don't need to introduce a separate knob here.

Comment thread p2p/host/autorelay/relay_finder.go Outdated
rf.metricsTracer.ScheduledWorkUpdated(&scheduledWorkTimes{})
}

var browserProtocols []int = []int{ma.P_WEBTRANSPORT, ma.P_WEBRTC_DIRECT, ma.P_WS, ma.P_WSS}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure about /ws? Is this supposed to catch the /ws/tls case?

Suggested change
var browserProtocols []int = []int{ma.P_WEBTRANSPORT, ma.P_WEBRTC_DIRECT, ma.P_WS, ma.P_WSS}
var browserProtocols = []int{ma.P_WEBTRANSPORT, ma.P_WEBRTC_DIRECT, ma.P_WS, ma.P_WSS}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed ma.P_WS. That shouldn't be here.

Comment thread p2p/host/autorelay/relay_finder.go Outdated
for _, addr := range addrs {
pub := addr.Encapsulate(circuit)
raddrs = append(raddrs, pub)
if rf.conf.webRTCSupport && isBrowserDialableAddr(addr) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed for WebRTC and not for WebTransport?

As a general design principle, it would be nice if AutoRelay didn't need to contain any transport-specific logic.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. This was a poor choice. I've moved this to host.Addrs. Even this is not great but I'm not sure what else to do without an address pipeline. The flow now is:

BasicHost.AllAddrs => RelayFinder.Addrs => BasicHost.AddrFactory => BasicHost.Addrs

}
if isRelayAddr(a) {
if isProtocolAddr(a, ma.P_WEBRTC) {
return s.transports.m[ma.P_WEBRTC]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this require extra logic here? Is it because of the certhash? Why doesn't it apply to WebTransport then?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The peer address is a circuitv2 address. So we need to check it before the circuitv2 check but the check is the same as the circuit v2 check.

The address is of the form
/ip4/1.2.3.4/udp/1/quic-v1/p2p/<relay-id>/p2p-circuit/webrtc
So we need to ensure that we don't use the quic-v1 or the p2p-circuit transport for dialing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't isRelayAddr already do that? Or phrased differently, the same problem would have occurred for a non-WebRTC address, why do we need special logic for WebRTC now?

Copy link
Copy Markdown
Member Author

@sukunrt sukunrt Oct 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but we don't want the circuit-v2 transport to handle dialing to the webrtc address.
The previous code was

if isRelayAddr => Use circuitv2 transport. 

But now we need

if isWebRTCAddr => Use webrtcprivate transport.
else if isRelayAddr => Use circuitv2 transport. 

This is because the webrtcprivate address has a p2p-circuit component but we want to use webrtcprivate transport. I could have written this as:

if isRelayAddr(addr):
	if isWebRTCAddr(addr):
		use webrtcprivate transport
	else
		use circuitv2 transport

The flow I've implemented is:
peerAddr: <relay-addr>/p2p-circuit/webrtc

  1. webrtcprivate.Transport.Dial is called with peerAddr
  2. webrtcprivate.Transport.Dial adds the relay address inferred from the webrtcprivate address inferred from the peerstore. Then makes a host.Connect call to establish a relay connection with the peer.
  3. Once relay connection is established it opens a signaling stream and establishes a webrtc connection.

I've chosen this because it is similar to circuitv2 transport which first establishes a direct connection with the relay node and then a circuitv2 connection to the peer over the relayed connection. This also has the nice property that Transport.Dial can connect to the peer over the address no matter the state of the host when Transport.Dial is called. In this case, it means if no circuit-v2 connection exists, it'll make a circuit-v2 connection first.

Comment thread p2p/protocol/holepunch/svc.go Outdated
for _, addr := range s.host.Peerstore().Addrs(p) {
if _, err := addr.ValueForProtocol(ma.P_WEBRTC); err == nil {
ctx := network.WithForceDirectDial(s.ctx, "webrtc holepunch")
s.host.Connect(ctx, peer.AddrInfo{ID: p}) // address is already in peerstore
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will start multiple Connect calls if there are multiple WebRTC addresses.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It returns immediately after this line, so this should be fine, no?

return
}
p := conn.RemotePeer()
// Peer supports DCUtR, let it trigger holepunch
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the peer supports DCUtR, but there are only WebRTC addresses?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That case is handled here: https://github.com/libp2p/go-libp2p/blob/webrtcprivate/transport/p2p/protocol/holepunch/holepuncher.go#L142

In this case the peer(DCUtR initiator) is best suited to handle the call so I've kept it on that side.

@sukunrt sukunrt force-pushed the webrtcprivate/transport branch from db7c7b2 to bc90626 Compare October 23, 2023 12:12
Copy link
Copy Markdown
Contributor

@marten-seemann marten-seemann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move the shared parts to a separate package that's imported by both webrtc and webrtcprivate?

}
if isRelayAddr(a) {
if isProtocolAddr(a, ma.P_WEBRTC) {
return s.transports.m[ma.P_WEBRTC]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't isRelayAddr already do that? Or phrased differently, the same problem would have occurred for a non-WebRTC address, why do we need special logic for WebRTC now?

type connection struct {
pc *webrtc.PeerConnection
transport *WebRTCTransport
transport tpt.Transport
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

var _ net.Addr = NetAddr{}

func (n NetAddr) Network() string {
return "libp2p-webrtc"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is how it's intended in the net.Addr. The doc says:

name of the network (for example, "tcp", "udp")

}

if l.transport.gater != nil && !l.transport.gater.InterceptSecured(network.DirInbound, s.Conn().RemotePeer(), conn) {
conn.Close()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to close the scope here?

err = fmt.Errorf("failed to read offer: %w", err)
return nil, err
}
if msg.Type == nil || *msg.Type != pb.Message_SDP_OFFER {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if msg.Type == nil || *msg.Type != pb.Message_SDP_OFFER {
if msg.GetType() != pb.Message_SDP_OFFER {

return
}

if msg.Type == nil || *msg.Type != pb.Message_ICE_CANDIDATE {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if msg.Type == nil || *msg.Type != pb.Message_ICE_CANDIDATE {
if msg.GetType() != pb.Message_ICE_CANDIDATE {

// Ignore without Debuging on empty message.
// Pion has a case where OnCandidate callback may be called with a nil
// candidate
if msg.Data == nil || *msg.Data == "" {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if msg.Data == nil || *msg.Data == "" {
if len(msg.GetData()) == 0 {

}

var init webrtc.ICECandidateInit
if err := json.Unmarshal([]byte(*msg.Data), &init); err != nil {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if err := json.Unmarshal([]byte(*msg.Data), &init); err != nil {
if err := json.Unmarshal([]byte(msg.GetData()), &init); err != nil {


var _ tpt.Transport = &transport{}

func AddTransport(h host.Host, gater connmgr.ConnectionGater, stunServers []webrtc.ICEServer) (*transport, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exported function returning unexported type.

Why is this method needed? Why don't we have such a method for other transports?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have this method for circuit-v2 transport. We need this method for the same reason for webrtc private transport.

The peer address is <relay-addr>/p2p-circuit/webrtc. This transport will first make a relayed connection with the peer, which is why it needs access to the host. After this point it establishes the connection over the signaling stream.


type connMultiaddrs struct {
local, remote ma.Multiaddr
type ConnMultiaddrs struct {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Don't make this public. Redefine if necessary.

@cpeliciari
Copy link
Copy Markdown
Contributor

@sukunrt What do we need for webrtcprivate to work?

@sukunrt
Copy link
Copy Markdown
Member Author

sukunrt commented Mar 31, 2025

We'll need to refresh/rewrite this PR, which is significant work. We can reuse parts of the code, but most likely it'll be better to start from scratch and use code from here as much as we can. This is because the webrtc-direct Transport has diverged significantly.

Do you have a usecase that's only solved by this transport?

@cpeliciari
Copy link
Copy Markdown
Contributor

@sukunrt I would like to be able to create private meetings. Then I would share the video between the participants.

@simsman2695
Copy link
Copy Markdown

@sukunrt currently webrtc-direct works only for public networks, my understanding now is that there is no means to peer browser's directly in private networks over the internet. Direct peering for web applications would reduce the overhead for relay requirements for private networks for pubsub. An example would be building a video meeting room with chat and file sharing.

@MarcoPolo
Copy link
Copy Markdown
Collaborator

Automatically closing old open PRs. If you're still interested in merging this please open the PR again and rebase from the latest master, otherwise do nothing.

@MarcoPolo MarcoPolo closed this Sep 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

webrtc: private/browser-to-private/server

5 participants