mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-30 12:42:31 +00:00
net/udprelay: change Server.AllocateEndpoint existing alloc strategy (#15792)
The previous strategy assumed clients maintained adequate state to understand the relationship between endpoint allocation and the server it was allocated on. magicsock will not have awareness of the server's disco key pre-allocation, it only understands peerAPI address at this point. The second client to allocate on the same server could trigger re-allocation, breaking a functional relay server endpoint. If magicsock needs to force reallocation we can add opt-in behaviors for this later. Updates tailscale/corp#27502 Signed-off-by: Jordan Whited <jordan@tailscale.com>
This commit is contained in:
parent
dae2319e11
commit
f701d39ba4
@ -454,9 +454,10 @@ func (s *Server) packetReadLoop() {
|
||||
|
||||
var ErrServerClosed = errors.New("server closed")
|
||||
|
||||
// AllocateEndpoint allocates a ServerEndpoint for the provided pair of
|
||||
// key.DiscoPublic's. It returns an error (ErrServerClosed) if the server has
|
||||
// been closed.
|
||||
// AllocateEndpoint allocates a [ServerEndpoint] for the provided pair of
|
||||
// [key.DiscoPublic]'s. If an allocation already exists for discoA and discoB it
|
||||
// is returned without modification/reallocation. AllocateEndpoint returns
|
||||
// [ErrServerClosed] if the server has been closed.
|
||||
func (s *Server) AllocateEndpoint(discoA, discoB key.DiscoPublic) (ServerEndpoint, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
@ -471,20 +472,11 @@ func (s *Server) AllocateEndpoint(discoA, discoB key.DiscoPublic) (ServerEndpoin
|
||||
pair := newPairOfDiscoPubKeys(discoA, discoB)
|
||||
e, ok := s.byDisco[pair]
|
||||
if ok {
|
||||
if !e.isBound() {
|
||||
// If the endpoint is not yet bound this is likely an allocation
|
||||
// race between two clients on the same Server. Instead of
|
||||
// re-allocating we return the existing allocation. We do not reset
|
||||
// e.allocatedAt in case a client is "stuck" in an allocation
|
||||
// loop and will not be able to complete a handshake, for whatever
|
||||
// reason. Once the endpoint expires a new endpoint will be
|
||||
// allocated. Clients can resolve duplicate ServerEndpoint details
|
||||
// via ServerEndpoint.LamportID.
|
||||
// Return the existing allocation. Clients can resolve duplicate
|
||||
// [ServerEndpoint]'s via [ServerEndpoint.LamportID].
|
||||
//
|
||||
// TODO: consider ServerEndpoint.BindLifetime -= time.Now()-e.allocatedAt
|
||||
// to give the client a more accurate picture of the bind window.
|
||||
// Or, some threshold to trigger re-allocation if too much time has
|
||||
// already passed since it was originally allocated.
|
||||
return ServerEndpoint{
|
||||
ServerDisco: s.discoPublic,
|
||||
AddrPorts: s.addrPorts,
|
||||
@ -494,14 +486,6 @@ func (s *Server) AllocateEndpoint(discoA, discoB key.DiscoPublic) (ServerEndpoin
|
||||
SteadyStateLifetime: tstime.GoDuration{Duration: s.steadyStateLifetime},
|
||||
}, nil
|
||||
}
|
||||
// If an endpoint exists for the pair of key.DiscoPublic's, and is
|
||||
// already bound, delete it. We will re-allocate a new endpoint. Chances
|
||||
// are clients cannot make use of the existing, bound allocation if
|
||||
// they are requesting a new one.
|
||||
delete(s.byDisco, pair)
|
||||
delete(s.byVNI, e.vni)
|
||||
s.vniPool = append(s.vniPool, e.vni)
|
||||
}
|
||||
|
||||
if len(s.vniPool) == 0 {
|
||||
return ServerEndpoint{}, errors.New("VNI pool exhausted")
|
||||
|
@ -174,8 +174,7 @@ func TestServer(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// We expect the same endpoint details as the 3-way bind handshake has not
|
||||
// yet been completed for both relay client parties.
|
||||
// We expect the same endpoint details pre-handshake.
|
||||
if diff := cmp.Diff(dupEndpoint, endpoint, cmpopts.EquateComparable(netip.AddrPort{}, key.DiscoPublic{})); diff != "" {
|
||||
t.Fatalf("wrong dupEndpoint (-got +want)\n%s", diff)
|
||||
}
|
||||
@ -191,6 +190,15 @@ func TestServer(t *testing.T) {
|
||||
tcA.handshake(t)
|
||||
tcB.handshake(t)
|
||||
|
||||
dupEndpoint, err = server.AllocateEndpoint(discoA.Public(), discoB.Public())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// We expect the same endpoint details post-handshake.
|
||||
if diff := cmp.Diff(dupEndpoint, endpoint, cmpopts.EquateComparable(netip.AddrPort{}, key.DiscoPublic{})); diff != "" {
|
||||
t.Fatalf("wrong dupEndpoint (-got +want)\n%s", diff)
|
||||
}
|
||||
|
||||
txToB := []byte{1, 2, 3}
|
||||
tcA.writeDataPkt(t, txToB)
|
||||
rxFromA := tcB.readDataPkt(t)
|
||||
|
Loading…
x
Reference in New Issue
Block a user