-
Notifications
You must be signed in to change notification settings - Fork 1.2k
connmgr: fix transport association bug #3221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
91c2947
to
377b411
Compare
@MarcoPolo I have confirmed this resolves the issue and enables the hole punch to succeed. |
@sukunrt can we merge this one? |
I’ll review this on Monday. Thanks for tracking this down @sukunrt |
p2p/transport/quicreuse/reuse.go
Outdated
@@ -360,6 +368,35 @@ func (r *reuse) AddTransport(tr *refcountedTransport, laddr *net.UDPAddr) error | |||
return nil | |||
} | |||
|
|||
func (r *reuse) AssociateListenTransport(association any, tr refCountedQuicTransport) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this method? It's doing some sanity checks, but ultimately it just
calls tr.associate
p2p/transport/quicreuse/connmgr.go
Outdated
} else if c.enableReuseport && association != nil { | ||
reuse, err := c.getReuse(netw) | ||
if err != nil { | ||
return nil, fmt.Errorf("reuse error: %w", err) | ||
} | ||
err = reuse.AssociateListenTransport(association, entry.ln.transport) | ||
if err != nil { | ||
return nil, fmt.Errorf("reuse associate failed: %w", err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} else if c.enableReuseport && association != nil { | |
reuse, err := c.getReuse(netw) | |
if err != nil { | |
return nil, fmt.Errorf("reuse error: %w", err) | |
} | |
err = reuse.AssociateListenTransport(association, entry.ln.transport) | |
if err != nil { | |
return nil, fmt.Errorf("reuse associate failed: %w", err) | |
} | |
} else if c.enableReuseport { | |
if tr, ok := entry.ln.transport.(*refcountedTransport); ok { | |
tr.associate(association) | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's wrong with sanity checks?
- The reuse type isn't exported, so this method is effectively private to this package.
I'd argue for one of the two:
- We move all associate inside reuse which may still require a similar method.
or - We add Associate on the refCountedTransport interface with a NOOP implementation in singleOwnerTransport to avoid the type assertion.
But for a patch release, I think this method with sanity checks is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sanity checks are fine in general. These seem to be in the wrong place and thus add noise to the change. You are checking the reuse struct's invariants. Is there a reason you don't trust these invariants? Would it be better to do these checks where we would break the invariants instead (here)?
If you do trust these invariants, then the sanity checks here are just noise. and the above suggestion is a clearer change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think something like this would also be okay:
} else if c.enableReuseport && association != nil { | |
reuse, err := c.getReuse(netw) | |
if err != nil { | |
return nil, fmt.Errorf("reuse error: %w", err) | |
} | |
err = reuse.AssociateListenTransport(association, entry.ln.transport) | |
if err != nil { | |
return nil, fmt.Errorf("reuse associate failed: %w", err) | |
} | |
} else if c.enableReuseport { | |
// ... get reuse | |
err := reuse.AssertTransportExists(tr) // This method _only_ checks invariants | |
// ... handle err | |
if tr, ok := entry.ln.transport.(*refcountedTransport); ok { | |
tr.associate(association) | |
} |
This split seems clearer to me because you have your sanity check step, and then your normal association step.
Note that I believe I would have also made the same initial design, and am mostly able to make this point as a fresh reviewer of this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason you don't trust these invariants?
The input transport is provided from outside reuse. That user might pass in anything.
On the optimal design though, I've changed my mind. I think we should put associate on refCountedTransport
and have a NO-Op implementation in singleOwnerTransport
. Then we move the associate bit out of transportForListen
in to this function. So associate always happens on all code paths no matter the input.
I'm not sure how semver compliant that is so I'll do that in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The input transport is provided from outside reuse. That user might pass in anything.
I don't follow. The transport here is from the quicListeners
map which is controlled by this quicreuse package.
On the optimal design though, I've changed my mind. I think we should put associate on refCountedTransport and have a NO-Op implementation in singleOwnerTransport.
No strong opinion here, but I think it's fine to keep this method only on refCountedTransport as it's only meaningful there.
I want to add an actual hole punching test using the simconn for this as well. |
38fa860
to
3ea686a
Compare
reuse port didn't work for the second transport, either QUIC or WebTransport, that was used for listening. This change fixes it by calling associate on all paths. This impacted hole punching for some users since you cannot hole punch without reuse port. There's a test in holepunch package to prevent regressions. Fixes #3165 Co-authored-by: Marco Munizaga <[email protected]>
3ea686a
to
f457284
Compare
reuse port didn't work for the second transport, either QUIC or WebTransport, that was used for listening. This change fixes it by calling associate on all paths. This impacted hole punching for some users since you cannot hole punch without reuse port. There's a test in holepunch package to prevent regressions. Fixes #3165 Co-authored-by: Marco Munizaga <[email protected]>
reuse port didn't work for the second transport, either QUIC or WebTransport, that was used for listening. This change fixes it by calling associate on all paths. This impacted hole punching for some users since you cannot hole punch without reuse port. There's a test in holepunch package to prevent regressions. Fixes #3165 Co-authored-by: Marco Munizaga <[email protected]>
reuse port didn't work for the second transport, either QUIC or WebTransport, that was used for listening. This change fixes it by calling associate on all paths. This impacted hole punching for some users since you cannot hole punch without reuse port. There's a test in holepunch package to prevent regressions. Fixes #3165 Co-authored-by: Marco Munizaga <[email protected]>
The second listen for the same IP:Port combination doesn't get tagged with the association. So if you listen on webtransport after quic, the wt dials won't reuse the transport.
We should refactor this is bit more, but any change will probably change the refCountedTransport interface which is returned from one public method, so I'd like to do that in a separate PR, and use this for a patch release.
This probably impacts: #3165