updatePeerLocked processes a single update for a single peer. It is intended as internal function since it returns whether or not the config is dirtied by the update (instead of handling it directly like updatePeers). c.L must be held.
(update *proto.CoordinateResponse_PeerUpdate, status *ipnstate.Status)
| 429 | // as internal function since it returns whether or not the config is dirtied by |
| 430 | // the update (instead of handling it directly like updatePeers). c.L must be held. |
| 431 | func (c *configMaps) updatePeerLocked(update *proto.CoordinateResponse_PeerUpdate, status *ipnstate.Status) (dirty bool) { |
| 432 | id, err := uuid.FromBytes(update.Id) |
| 433 | if err != nil { |
| 434 | c.logger.Critical(context.Background(), "received update with bad id", slog.F("id", update.Id)) |
| 435 | return false |
| 436 | } |
| 437 | logger := c.logger.With(slog.F("peer_id", id)) |
| 438 | lc, peerOk := c.peers[id] |
| 439 | var node *tailcfg.Node |
| 440 | if update.Kind == proto.CoordinateResponse_PeerUpdate_NODE { |
| 441 | // If no preferred DERP is provided, we can't reach the node. |
| 442 | if update.Node.PreferredDerp == 0 { |
| 443 | logger.Warn(context.Background(), "no preferred DERP, peer update", slog.F("node_proto", update.Node)) |
| 444 | return false |
| 445 | } |
| 446 | node, err = c.protoNodeToTailcfg(update.Node) |
| 447 | if err != nil { |
| 448 | logger.Critical(context.Background(), "failed to convert proto node to tailcfg", slog.F("node_proto", update.Node)) |
| 449 | return false |
| 450 | } |
| 451 | logger = logger.With(slog.F("key_id", node.Key.ShortString()), slog.F("node", node)) |
| 452 | node.KeepAlive = c.nodeKeepalive(lc, status, node) |
| 453 | } |
| 454 | switch { |
| 455 | case !peerOk && update.Kind == proto.CoordinateResponse_PeerUpdate_NODE: |
| 456 | // new! |
| 457 | var lastHandshake time.Time |
| 458 | if ps, ok := status.Peer[node.Key]; ok { |
| 459 | lastHandshake = ps.LastHandshake |
| 460 | } |
| 461 | lc = &peerLifecycle{ |
| 462 | peerID: id, |
| 463 | node: node, |
| 464 | lastHandshake: lastHandshake, |
| 465 | lost: false, |
| 466 | } |
| 467 | c.peers[id] = lc |
| 468 | logger.Debug(context.Background(), "adding new peer") |
| 469 | return lc.validForWireguard() |
| 470 | case peerOk && update.Kind == proto.CoordinateResponse_PeerUpdate_NODE: |
| 471 | // update |
| 472 | if lc.node != nil { |
| 473 | node.Created = lc.node.Created |
| 474 | } |
| 475 | dirty = !lc.node.Equal(node) |
| 476 | lc.node = node |
| 477 | // validForWireguard checks that the node is non-nil, so should be |
| 478 | // called after we update the node. |
| 479 | dirty = dirty && lc.validForWireguard() |
| 480 | lc.lost = false |
| 481 | lc.resetLostTimer() |
| 482 | if lc.isDestination && !lc.readyForHandshake { |
| 483 | // We received the node of a destination peer before we've received |
| 484 | // their READY_FOR_HANDSHAKE. Set a timer |
| 485 | lc.setReadyForHandshakeTimer(c) |
| 486 | logger.Debug(context.Background(), "setting ready for handshake timeout") |
| 487 | } |
| 488 | logger.Debug(context.Background(), "node update to existing peer", slog.F("dirty", dirty)) |
no test coverage detected