connectd: at disconnected, tell lightningd how long we were connected.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2025-11-11 11:33:22 +10:30
parent 88b9b0bc28
commit 565f7deec0
15 changed files with 79 additions and 25 deletions

View File

@@ -112,6 +112,7 @@ static struct peer *new_peer(struct daemon *daemon,
const struct crypto_state *cs,
const u8 *their_features,
enum is_websocket is_websocket,
struct timemono connect_starttime,
struct io_conn *conn STEALS,
int *fd_for_subd)
{
@@ -119,6 +120,7 @@ static struct peer *new_peer(struct daemon *daemon,
peer->daemon = daemon;
peer->id = *id;
peer->connect_starttime = connect_starttime;
peer->counter = daemon->connection_counter++;
peer->cs = *cs;
peer->subds = tal_arr(peer, struct subd *, 0);
@@ -258,11 +260,12 @@ static void reset_reconnect_timer(struct peer *peer)
void send_disconnected(struct daemon *daemon,
const struct node_id *id,
u64 connectd_counter)
u64 connectd_counter,
struct timemono starttime)
{
/* lightningd: it's gone */
daemon_conn_send(daemon->master,
take(towire_connectd_peer_disconnected(NULL, id, connectd_counter)));
take(towire_connectd_peer_disconnected(NULL, id, connectd_counter, time_to_nsec(timemono_since(starttime)))));
/* Tell gossipd to stop asking this peer gossip queries */
daemon_conn_send(daemon->gossipd,
@@ -282,6 +285,7 @@ struct io_plan *peer_connected(struct io_conn *conn,
struct crypto_state *cs,
const u8 *their_features TAKES,
enum is_websocket is_websocket,
struct timemono starttime,
bool incoming)
{
u8 *msg;
@@ -294,11 +298,13 @@ struct io_plan *peer_connected(struct io_conn *conn,
const char *connect_reason;
u64 connect_time_nsec;
u64 prev_connectd_counter;
struct timemono prev_connect_start;
/* We remove any previous connection immediately, on the assumption it's dead */
oldpeer = peer_htable_get(daemon->peers, id);
if (oldpeer) {
prev_connectd_counter = oldpeer->counter;
prev_connect_start = oldpeer->connect_starttime;
destroy_peer_immediately(oldpeer);
}
@@ -320,7 +326,8 @@ struct io_plan *peer_connected(struct io_conn *conn,
if (unsup != -1) {
/* We were going to send a reconnect message, but not now! */
if (oldpeer)
send_disconnected(daemon, id, prev_connectd_counter);
send_disconnected(daemon, id, prev_connectd_counter,
prev_connect_start);
status_peer_unusual(id, "Unsupported feature %u", unsup);
msg = towire_warningfmt(NULL, NULL, "Unsupported feature %u",
unsup);
@@ -331,7 +338,8 @@ struct io_plan *peer_connected(struct io_conn *conn,
if (!feature_check_depends(their_features, &depender, &missing)) {
/* We were going to send a reconnect message, but not now! */
if (oldpeer)
send_disconnected(daemon, id, prev_connectd_counter);
send_disconnected(daemon, id, prev_connectd_counter,
prev_connect_start);
status_peer_unusual(id, "Feature %zu requires feature %zu",
depender, missing);
msg = towire_warningfmt(NULL, NULL,
@@ -374,13 +382,14 @@ struct io_plan *peer_connected(struct io_conn *conn,
}
/* This contains the per-peer state info; gossipd fills in pps->gs */
peer = new_peer(daemon, id, cs, their_features, is_websocket, conn,
peer = new_peer(daemon, id, cs, their_features, is_websocket, starttime, conn,
&subd_fd);
/* Only takes over conn if it succeeds. */
if (!peer) {
/* We were going to send a reconnect message, but not now! */
if (oldpeer)
send_disconnected(daemon, id, prev_connectd_counter);
send_disconnected(daemon, id, prev_connectd_counter,
prev_connect_start);
return io_close(conn);
}
@@ -398,7 +407,8 @@ struct io_plan *peer_connected(struct io_conn *conn,
prev_connectd_counter,
peer->counter,
addr, remote_addr,
incoming, their_features);
incoming, their_features,
time_to_nsec(timemono_since(prev_connect_start)));
} else {
/* Tell gossipd about new peer */
msg = towire_gossipd_new_peer(NULL, id, option_gossip_queries);
@@ -438,13 +448,15 @@ static struct io_plan *handshake_in_success(struct io_conn *conn,
struct crypto_state *cs,
struct oneshot *timeout,
enum is_websocket is_websocket,
struct timemono starttime,
struct daemon *daemon)
{
struct node_id id;
node_id_from_pubkey(&id, id_key);
status_peer_debug(&id, "Connect IN");
return peer_exchange_initmsg(conn, daemon, daemon->our_features,
cs, &id, addr, timeout, is_websocket, true);
cs, &id, addr, timeout, is_websocket,
starttime, true);
}
/*~ If the timer goes off, we simply free everything, which hangs up. */
@@ -730,6 +742,7 @@ static struct io_plan *handshake_out_success(struct io_conn *conn,
struct crypto_state *cs,
struct oneshot *timeout,
enum is_websocket is_websocket,
struct timemono starttime,
struct connecting *connect)
{
struct node_id id;
@@ -739,7 +752,8 @@ static struct io_plan *handshake_out_success(struct io_conn *conn,
status_peer_debug(&id, "Connect OUT");
return peer_exchange_initmsg(conn, connect->daemon,
connect->daemon->our_features,
cs, &id, addr, timeout, is_websocket, false);
cs, &id, addr, timeout, is_websocket,
starttime, false);
}
struct io_plan *connection_out(struct io_conn *conn, struct connecting *connect)

View File

@@ -64,6 +64,8 @@ struct peer {
struct node_id id;
/* Counters and keys for symmetric crypto */
struct crypto_state cs;
/* Time when we first connected */
struct timemono connect_starttime;
/* Connection to the peer (NULL if it's disconnected and we're flushing) */
struct io_conn *to_peer;
@@ -388,12 +390,14 @@ struct io_plan *peer_connected(struct io_conn *conn,
struct crypto_state *cs,
const u8 *their_features TAKES,
enum is_websocket is_websocket,
struct timemono starttime,
bool incoming);
/* Tell gossipd and lightningd that this peer is gone. */
void send_disconnected(struct daemon *daemon,
const struct node_id *id,
u64 connectd_counter);
u64 connectd_counter,
struct timemono starttime);
/* Free peer immediately (don't wait for draining). */
void destroy_peer_immediately(struct peer *peer);

View File

@@ -90,6 +90,7 @@ msgdata,connectd_peer_connected,connect_nsec,u64,
msgtype,connectd_peer_disconnected,2006
msgdata,connectd_peer_disconnected,id,node_id,
msgdata,connectd_peer_disconnected,counter,u64,
msgdata,connectd_peer_disconnected,connected_time_nsec,u64,
# Connectd -> master: peer reconnected (disconnect & connect)
msgtype,connectd_peer_reconnected,2010
@@ -101,6 +102,7 @@ msgdata,connectd_peer_reconnected,remote_addr,?wireaddr,
msgdata,connectd_peer_reconnected,incoming,bool,
msgdata,connectd_peer_reconnected,flen,u16,
msgdata,connectd_peer_reconnected,features,u8,flen
msgdata,connectd_peer_reconnected,connected_time_nsec,u64,
# Master -> connectd: make peer active immediately (we want to talk) (+ fd to subd).
msgtype,connectd_peer_connect_subd,2004
1 #include <common/channel_id.h>
90 msgdata,connectd_peer_reconnected,incoming,bool, msgdata,connectd_peer_reconnected,remote_addr,?wireaddr,
91 msgdata,connectd_peer_reconnected,flen,u16, msgdata,connectd_peer_reconnected,incoming,bool,
92 msgdata,connectd_peer_reconnected,features,u8,flen msgdata,connectd_peer_reconnected,flen,u16,
93 msgdata,connectd_peer_reconnected,features,u8,flen
94 # Master -> connectd: make peer active immediately (we want to talk) (+ fd to subd). msgdata,connectd_peer_reconnected,connected_time_nsec,u64,
95 msgtype,connectd_peer_connect_subd,2004 # Master -> connectd: make peer active immediately (we want to talk) (+ fd to subd).
96 msgdata,connectd_peer_connect_subd,id,node_id, msgtype,connectd_peer_connect_subd,2004
102 msgdata,connectd_peer_spoke,counter,u64, msgdata,connectd_peer_spoke,id,node_id,
103 msgdata,connectd_peer_spoke,msgtype,u16, msgdata,connectd_peer_spoke,counter,u64,
104 msgdata,connectd_peer_spoke,channel_id,channel_id, msgdata,connectd_peer_spoke,msgtype,u16,
105 msgdata,connectd_peer_spoke,channel_id,channel_id,
106 # If msgtype == WIRE_ERROR, this is the string.
107 msgdata,connectd_peer_spoke,error,?wirestring,
108 # master -> connectd: peer no longer reqeuired, don't keep reconnecting.

View File

@@ -5,6 +5,7 @@
#include <ccan/endian/endian.h>
#include <ccan/io/io.h>
#include <ccan/mem/mem.h>
#include <ccan/time/time.h>
#include <common/crypto_state.h>
#include <common/ecdh.h>
#include <common/status.h>
@@ -178,6 +179,9 @@ struct handshake {
/* Are we connected via a websocket? */
enum is_websocket is_websocket;
/* Time they first connected */
struct timemono starttime;
/* Function to call once handshake complete. */
struct io_plan *(*cb)(struct io_conn *conn,
const struct pubkey *their_id,
@@ -185,6 +189,7 @@ struct handshake {
struct crypto_state *cs,
struct oneshot *timeout,
enum is_websocket is_websocket,
struct timemono starttime,
void *cbarg);
void *cbarg;
};
@@ -357,12 +362,14 @@ static struct io_plan *handshake_succeeded(struct io_conn *conn,
struct crypto_state *cs,
struct oneshot *timeout,
enum is_websocket is_websocket,
struct timemono starttime,
void *cbarg);
void *cbarg;
struct pubkey their_id;
struct wireaddr_internal addr;
struct oneshot *timeout;
enum is_websocket is_websocket;
struct timemono starttime;
/* BOLT #8:
*
@@ -396,9 +403,10 @@ static struct io_plan *handshake_succeeded(struct io_conn *conn,
addr = h->addr;
timeout = h->timeout;
is_websocket = h->is_websocket;
starttime = h->starttime;
tal_free(h);
return cb(conn, &their_id, &addr, &cs, timeout, is_websocket, cbarg);
return cb(conn, &their_id, &addr, &cs, timeout, is_websocket, starttime, cbarg);
}
static struct handshake *new_handshake(const tal_t *ctx,
@@ -983,12 +991,14 @@ struct io_plan *responder_handshake_(struct io_conn *conn,
struct crypto_state *,
struct oneshot *,
enum is_websocket,
struct timemono,
void *cbarg),
void *cbarg)
{
struct handshake *h = new_handshake(conn, my_id);
h->side = RESPONDER;
h->starttime = time_mono();
h->my_id = *my_id;
h->addr = *addr;
h->cbarg = cbarg;
@@ -1011,12 +1021,14 @@ struct io_plan *initiator_handshake_(struct io_conn *conn,
struct crypto_state *,
struct oneshot *timeout,
enum is_websocket is_websocket,
struct timemono,
void *cbarg),
void *cbarg)
{
struct handshake *h = new_handshake(conn, their_id);
h->side = INITIATOR;
h->starttime = time_mono();
h->my_id = *my_id;
h->their_id = *their_id;
h->addr = *addr;

View File

@@ -26,7 +26,8 @@ enum is_websocket {
const struct wireaddr_internal *, \
struct crypto_state *, \
struct oneshot *, \
enum is_websocket), \
enum is_websocket, \
struct timemono), \
(cbarg))
@@ -42,6 +43,7 @@ struct io_plan *initiator_handshake_(struct io_conn *conn,
struct crypto_state *,
struct oneshot *timeout,
enum is_websocket,
struct timemono,
void *cbarg),
void *cbarg);
@@ -55,7 +57,8 @@ struct io_plan *initiator_handshake_(struct io_conn *conn,
const struct wireaddr_internal *, \
struct crypto_state *, \
struct oneshot *, \
enum is_websocket), \
enum is_websocket, \
struct timemono), \
(cbarg))
struct io_plan *responder_handshake_(struct io_conn *conn,
@@ -69,6 +72,7 @@ struct io_plan *responder_handshake_(struct io_conn *conn,
struct crypto_state *,
struct oneshot *,
enum is_websocket,
struct timemono,
void *cbarg),
void *cbarg);
#endif /* LIGHTNING_CONNECTD_HANDSHAKE_H */

View File

@@ -1392,9 +1392,10 @@ static void destroy_peer_conn(struct io_conn *peer_conn, struct peer *peer)
{
assert(peer->to_peer == peer_conn);
/* We are no longer connected. Tell lightningd & gossipd*/
/* We are no longer connected. Tell lightningd & gossipd */
peer->to_peer = NULL;
send_disconnected(peer->daemon, &peer->id, peer->counter);
send_disconnected(peer->daemon, &peer->id, peer->counter,
peer->connect_starttime);
/* Wake subds: give them 5 seconds to flush. */
for (size_t i = 0; i < tal_count(peer->subds); i++) {

View File

@@ -34,6 +34,9 @@ struct early_peer {
/* Timeout in case it takes too long */
struct oneshot *timeout;
/* We record time when we first accepted socket */
struct timemono starttime;
};
static bool contains_common_chain(struct bitcoin_blkid *chains)
@@ -152,6 +155,7 @@ static struct io_plan *peer_init_received(struct io_conn *conn,
&peer->cs,
take(features),
peer->is_websocket,
peer->starttime,
peer->incoming);
}
@@ -206,6 +210,7 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn,
const struct wireaddr_internal *addr,
struct oneshot *timeout,
enum is_websocket is_websocket,
struct timemono starttime,
bool incoming)
{
/* If conn is closed, forget peer */
@@ -220,6 +225,7 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn,
peer->incoming = incoming;
peer->is_websocket = is_websocket;
peer->timeout = timeout;
peer->starttime = starttime;
/* BOLT #1:
*

View File

@@ -19,6 +19,7 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn,
const struct wireaddr_internal *addr,
struct oneshot *timeout,
enum is_websocket is_websocket,
struct timemono starttime,
bool incoming);
#endif /* LIGHTNING_CONNECTD_PEER_EXCHANGE_INITMSG_H */

View File

@@ -186,6 +186,7 @@ static struct io_plan *success(struct io_conn *conn UNUSED,
struct crypto_state *cs,
struct oneshot *timeout UNUSED,
enum is_websocket is_websocket UNUSED,
struct timemono starttime UNUSED,
void *unused UNUSED)
{
assert(pubkey_eq(them, &rs_pub));

View File

@@ -185,6 +185,7 @@ static struct io_plan *success(struct io_conn *conn UNUSED,
struct crypto_state *cs,
struct oneshot *timeout UNUSED,
enum is_websocket is_websocket UNUSED,
struct timemono starttime UNUSED,
void *unused UNUSED)
{
assert(secret_eq_str(&cs->sk, expect_sk));

View File

@@ -182,6 +182,7 @@ static struct io_plan *handshake_success(struct io_conn *conn,
struct crypto_state *cs,
struct oneshot *timer,
enum is_websocket is_websocket,
struct timemono starttime,
char **args)
{
int peer_fd = io_conn_fd(conn);

View File

@@ -43,6 +43,7 @@
static void peer_disconnected(struct lightningd *ld,
const struct node_id *id,
u64 connectd_counter,
u64 connected_time_nsec,
bool fail_connect_attempts);
/* Common pattern: create a sockpair for this channel, return one as a peer_fd */
@@ -1723,21 +1724,22 @@ void handle_peer_connected(struct lightningd *ld, const u8 *msg)
&their_features,
&connect_reason,
&connect_nsec)) {
u64 prev_connectd_counter;
u64 prev_connectd_counter, connected_time_nsec;
if (!fromwire_connectd_peer_reconnected(hook_payload, msg,
&id, &prev_connectd_counter,
&connectd_counter,
&hook_payload->addr,
&hook_payload->remote_addr,
&hook_payload->incoming,
&their_features)) {
&their_features,
&connected_time_nsec)) {
fatal("Connectd gave bad CONNECT_PEER_(RE)CONNECTED message %s",
tal_hex(msg, msg));
}
/* Reconnect? Mark the disconnect *first*, but don't
* fail any connect attempts: this is a race. */
log_peer_debug(ld->log, &id, "peer reconnected");
peer_disconnected(ld, &id, prev_connectd_counter, false);
peer_disconnected(ld, &id, prev_connectd_counter, connected_time_nsec, false);
connect_reason = tal_strdup(hook_payload, "");
connect_nsec = 0;
}
@@ -2083,6 +2085,7 @@ static void destroy_disconnect_command(struct disconnect_command *dc)
static void peer_disconnected(struct lightningd *ld,
const struct node_id *id,
u64 connectd_counter,
u64 connected_time_nsec,
bool fail_connect_attempts)
{
struct disconnect_command *i, *next;
@@ -2127,13 +2130,16 @@ static void peer_disconnected(struct lightningd *ld,
void handle_peer_disconnected(struct lightningd *ld, const u8 *msg)
{
struct node_id id;
u64 connectd_counter;
u64 connectd_counter, connected_time_nsec;
if (!fromwire_connectd_peer_disconnected(msg, &id, &connectd_counter))
if (!fromwire_connectd_peer_disconnected(msg,
&id,
&connectd_counter,
&connected_time_nsec))
fatal("Connectd gave bad PEER_DISCONNECTED message %s",
tal_hex(msg, msg));
peer_disconnected(ld, &id, connectd_counter, true);
peer_disconnected(ld, &id, connectd_counter, connected_time_nsec, true);
}
void update_channel_from_inflight(struct lightningd *ld,

View File

@@ -273,10 +273,10 @@ bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNE
bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED, wirestring **connect_reason UNNEEDED, u64 *connect_nsec UNNEEDED)
{ fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); }
/* Generated stub for fromwire_connectd_peer_disconnected */
bool fromwire_connectd_peer_disconnected(const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED)
bool fromwire_connectd_peer_disconnected(const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED, u64 *connect_time_nsec UNNEEDED)
{ fprintf(stderr, "fromwire_connectd_peer_disconnected called!\n"); abort(); }
/* Generated stub for fromwire_connectd_peer_reconnected */
bool fromwire_connectd_peer_reconnected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *prev_counter UNNEEDED, u64 *counter UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED)
bool fromwire_connectd_peer_reconnected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *prev_counter UNNEEDED, u64 *counter UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED, u64 *connect_time_nsec UNNEEDED)
{ fprintf(stderr, "fromwire_connectd_peer_reconnected called!\n"); abort(); }
/* Generated stub for fromwire_connectd_peer_spoke */
bool fromwire_connectd_peer_spoke(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED, u16 *msgtype UNNEEDED, struct channel_id *channel_id UNNEEDED, wirestring **error UNNEEDED)

View File

@@ -147,6 +147,7 @@ static struct io_plan *
success(struct io_conn *conn UNUSED, const struct pubkey *them UNUSED,
const struct wireaddr_internal *addr UNUSED, struct crypto_state *cs,
struct oneshot *timeout UNUSED, enum is_websocket is_websocket UNUSED,
struct timemono starttime UNUSED,
void *unused UNUSED)
{
assert(false && "handshake unexpectedly succeeded");

View File

@@ -302,10 +302,10 @@ bool fromwire_channeld_sending_commitsig(const tal_t *ctx UNNEEDED, const void *
bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED, wirestring **connect_reason UNNEEDED, u64 *connect_nsec UNNEEDED)
{ fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); }
/* Generated stub for fromwire_connectd_peer_disconnected */
bool fromwire_connectd_peer_disconnected(const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED)
bool fromwire_connectd_peer_disconnected(const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED, u64 *connect_time_nsec UNNEEDED)
{ fprintf(stderr, "fromwire_connectd_peer_disconnected called!\n"); abort(); }
/* Generated stub for fromwire_connectd_peer_reconnected */
bool fromwire_connectd_peer_reconnected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *prev_counter UNNEEDED, u64 *counter UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED)
bool fromwire_connectd_peer_reconnected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *prev_counter UNNEEDED, u64 *counter UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED, u64 *connect_time_nsec UNNEEDED)
{ fprintf(stderr, "fromwire_connectd_peer_reconnected called!\n"); abort(); }
/* Generated stub for fromwire_connectd_peer_spoke */
bool fromwire_connectd_peer_spoke(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED, u16 *msgtype UNNEEDED, struct channel_id *channel_id UNNEEDED, wirestring **error UNNEEDED)