Skip to content

Commit 5f54403

Browse files
committed
lightningd: fix race with crossover pings.
We cannot use subd_req() here: replies will come out of order, and the we should not simply assign the reponses in FIFO order. Changelog-Fixed: lightningd: don't get confused with parallel ping commands. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1 parent a0fd72e commit 5f54403

File tree

8 files changed

+80
-27
lines changed

8 files changed

+80
-27
lines changed

connectd/connectd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2368,7 +2368,7 @@ static struct io_plan *recv_req(struct io_conn *conn,
23682368
case WIRE_CONNECTD_PEER_SPOKE:
23692369
case WIRE_CONNECTD_CONNECT_FAILED:
23702370
case WIRE_CONNECTD_DEV_MEMLEAK_REPLY:
2371-
case WIRE_CONNECTD_PING_REPLY:
2371+
case WIRE_CONNECTD_PING_DONE:
23722372
case WIRE_CONNECTD_GOT_ONIONMSG_TO_US:
23732373
case WIRE_CONNECTD_CUSTOMMSG_IN:
23742374
case WIRE_CONNECTD_PEER_DISCONNECT_DONE:

connectd/connectd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ struct peer {
8989

9090
/* Are we expecting a pong? */
9191
enum pong_expect_type expecting_pong;
92+
u64 ping_reqid;
9293

9394
/* Random ping timer, to detect dead connections. */
9495
struct oneshot *ping_timer;

connectd/connectd_wire.csv

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,17 @@ msgtype,connectd_dev_report_fds,2034
130130

131131
# Ping/pong test. Waits for a reply if it expects one.
132132
msgtype,connectd_ping,2030
133+
msgdata,connectd_ping,reqid,u64,
133134
msgdata,connectd_ping,id,node_id,
134135
msgdata,connectd_ping,num_pong_bytes,u16,
135136
msgdata,connectd_ping,len,u16,
136137

137-
msgtype,connectd_ping_reply,2130
138+
msgtype,connectd_ping_done,2037
139+
msgdata,connectd_ping_done,reqid,u64,
138140
# False if we there was already a ping in progress.
139-
msgdata,connectd_ping_reply,sent,bool,
141+
msgdata,connectd_ping_done,sent,bool,
140142
# 0 == no pong expected, otherwise length of pong.
141-
msgdata,connectd_ping_reply,totlen,u16,
143+
msgdata,connectd_ping_done,totlen,u16,
142144

143145
# We tell lightningd we got an onionmsg
144146
msgtype,connectd_got_onionmsg_to_us,2145

connectd/multiplex.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -719,8 +719,8 @@ static void handle_ping_reply(struct peer *peer, const u8 *msg)
719719
status_debug("Got pong %zu bytes (%.*s...)",
720720
tal_count(ignored), (int)i, (char *)ignored);
721721
daemon_conn_send(peer->daemon->master,
722-
take(towire_connectd_ping_reply(NULL, true,
723-
tal_bytelen(msg))));
722+
take(towire_connectd_ping_done(NULL, peer->ping_reqid, true,
723+
tal_bytelen(msg))));
724724
}
725725

726726
static void handle_pong_in(struct peer *peer, const u8 *msg)
@@ -1470,25 +1470,26 @@ void send_manual_ping(struct daemon *daemon, const u8 *msg)
14701470
{
14711471
u8 *ping;
14721472
struct node_id id;
1473+
u64 reqid;
14731474
u16 len, num_pong_bytes;
14741475
struct peer *peer;
14751476

1476-
if (!fromwire_connectd_ping(msg, &id, &num_pong_bytes, &len))
1477+
if (!fromwire_connectd_ping(msg, &reqid, &id, &num_pong_bytes, &len))
14771478
master_badmsg(WIRE_CONNECTD_PING, msg);
14781479

14791480
peer = peer_htable_get(daemon->peers, &id);
14801481
if (!peer) {
14811482
daemon_conn_send(daemon->master,
1482-
take(towire_connectd_ping_reply(NULL,
1483-
false, 0)));
1483+
take(towire_connectd_ping_done(NULL, reqid,
1484+
false, 0)));
14841485
return;
14851486
}
14861487

14871488
/* We're not supposed to send another ping until previous replied */
14881489
if (peer->expecting_pong != PONG_UNEXPECTED) {
14891490
daemon_conn_send(daemon->master,
1490-
take(towire_connectd_ping_reply(NULL,
1491-
false, 0)));
1491+
take(towire_connectd_ping_done(NULL, reqid,
1492+
false, 0)));
14921493
return;
14931494
}
14941495

@@ -1513,13 +1514,14 @@ void send_manual_ping(struct daemon *daemon, const u8 *msg)
15131514
*/
15141515
if (num_pong_bytes >= 65532) {
15151516
daemon_conn_send(daemon->master,
1516-
take(towire_connectd_ping_reply(NULL,
1517+
take(towire_connectd_ping_done(NULL, reqid,
15171518
true, 0)));
15181519
return;
15191520
}
15201521

15211522
/* We'll respond to lightningd once the pong comes in */
15221523
peer->expecting_pong = PONG_EXPECTED_COMMAND;
1524+
peer->ping_reqid = reqid;
15231525

15241526
/* Since we're doing this manually, kill and restart timer. */
15251527
tal_free(peer->ping_timer);

lightningd/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ LIGHTNINGD_SRC := \
3636
lightningd/peer_control.c \
3737
lightningd/peer_fd.c \
3838
lightningd/peer_htlcs.c \
39+
lightningd/ping.c \
3940
lightningd/plugin.c \
4041
lightningd/plugin_control.c \
4142
lightningd/plugin_hook.c \
@@ -48,7 +49,6 @@ LIGHTNINGD_SRC := \
4849
LIGHTNINGD_SRC_NOHDR := \
4950
lightningd/configs.c \
5051
lightningd/datastore.c \
51-
lightningd/ping.c \
5252
lightningd/offer.c \
5353
lightningd/signmessage.c
5454

lightningd/connect_control.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <lightningd/opening_common.h>
2222
#include <lightningd/opening_control.h>
2323
#include <lightningd/peer_control.h>
24+
#include <lightningd/ping.h>
2425
#include <lightningd/plugin_hook.h>
2526

2627
struct connect {
@@ -494,7 +495,6 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd
494495
case WIRE_CONNECTD_INIT_REPLY:
495496
case WIRE_CONNECTD_ACTIVATE_REPLY:
496497
case WIRE_CONNECTD_DEV_MEMLEAK_REPLY:
497-
case WIRE_CONNECTD_PING_REPLY:
498498
case WIRE_CONNECTD_START_SHUTDOWN_REPLY:
499499
case WIRE_CONNECTD_INJECT_ONIONMSG_REPLY:
500500
break;
@@ -526,6 +526,10 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd
526526
case WIRE_CONNECTD_ONIONMSG_FORWARD_FAIL:
527527
handle_onionmsg_forward_fail(connectd->ld, msg);
528528
break;
529+
530+
case WIRE_CONNECTD_PING_DONE:
531+
handle_ping_done(connectd, msg);
532+
break;
529533
}
530534
return 0;
531535
}

lightningd/ping.c

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,65 @@
22
#include <common/json_command.h>
33
#include <common/json_param.h>
44
#include <connectd/connectd_wiregen.h>
5+
#include <inttypes.h>
56
#include <lightningd/channel.h>
67
#include <lightningd/jsonrpc.h>
78
#include <lightningd/lightningd.h>
89
#include <lightningd/peer_control.h>
10+
#include <lightningd/ping.h>
911
#include <lightningd/subd.h>
1012

11-
static void ping_reply(struct subd *connectd,
12-
const u8 *msg, const int *fds,
13-
struct command *cmd)
13+
struct ping_command {
14+
struct list_node list;
15+
u64 reqid;
16+
struct command *cmd;
17+
};
18+
19+
static void destroy_ping_command(struct ping_command *ping_command,
20+
struct lightningd *ld)
21+
{
22+
list_del_from(&ld->ping_commands, &ping_command->list);
23+
}
24+
25+
static struct ping_command *find_ping_command(struct lightningd *ld,
26+
u64 reqid)
27+
{
28+
struct ping_command *i;
29+
list_for_each(&ld->ping_commands, i, list) {
30+
if (i->reqid == reqid)
31+
return i;
32+
}
33+
return NULL;
34+
}
35+
36+
void handle_ping_done(struct subd *connectd, const u8 *msg)
1437
{
1538
u16 totlen;
1639
bool sent;
40+
u64 reqid;
41+
struct ping_command *ping_command;
1742

18-
log_debug(connectd->log, "Got ping reply!");
19-
20-
if (!fromwire_connectd_ping_reply(msg, &sent, &totlen)) {
43+
if (!fromwire_connectd_ping_done(msg, &reqid, &sent, &totlen)) {
2144
log_broken(connectd->log, "Malformed ping reply %s",
2245
tal_hex(tmpctx, msg));
23-
was_pending(command_fail(cmd, LIGHTNINGD,
24-
"Bad reply message"));
2546
return;
2647
}
2748

49+
ping_command = find_ping_command(connectd->ld, reqid);
50+
if (!ping_command) {
51+
log_broken(connectd->log, "ping reply for unknown reqid %"PRIu64, reqid);
52+
return;
53+
}
54+
55+
log_debug(connectd->log, "Got ping reply!");
2856
if (!sent)
29-
was_pending(command_fail(cmd, LIGHTNINGD,
57+
was_pending(command_fail(ping_command->cmd, LIGHTNINGD,
3058
"Ping already pending"));
3159
else {
32-
struct json_stream *response = json_stream_success(cmd);
60+
struct json_stream *response = json_stream_success(ping_command->cmd);
3361

3462
json_add_num(response, "totlen", totlen);
35-
was_pending(command_success(cmd, response));
63+
was_pending(command_success(ping_command->cmd, response));
3664
}
3765
}
3866

@@ -44,6 +72,8 @@ static struct command_result *json_ping(struct command *cmd,
4472
unsigned int *len, *pongbytes;
4573
struct node_id *id;
4674
u8 *msg;
75+
static u64 reqid;
76+
struct ping_command *ping_command;
4777

4878
if (!param_check(cmd, buffer, params,
4979
p_req("id", param_node_id, &id),
@@ -83,8 +113,14 @@ static struct command_result *json_ping(struct command *cmd,
83113
if (command_check_only(cmd))
84114
return command_check_done(cmd);
85115

86-
msg = towire_connectd_ping(NULL, id, *pongbytes, *len);
87-
subd_req(cmd, cmd->ld->connectd, take(msg), -1, 0, ping_reply, cmd);
116+
ping_command = tal(cmd, struct ping_command);
117+
ping_command->cmd = cmd;
118+
ping_command->reqid = ++reqid;
119+
list_add_tail(&cmd->ld->ping_commands, &ping_command->list);
120+
tal_add_destructor2(ping_command, destroy_ping_command, cmd->ld);
121+
122+
msg = towire_connectd_ping(NULL, ping_command->reqid, id, *pongbytes, *len);
123+
subd_send_msg(cmd->ld->connectd, take(msg));
88124

89125
return command_still_pending(cmd);
90126
}

lightningd/ping.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef LIGHTNING_LIGHTNINGD_PING_H
2+
#define LIGHTNING_LIGHTNINGD_PING_H
3+
#include "config.h"
4+
5+
struct subd;
6+
7+
void handle_ping_done(struct subd *connectd, const u8 *msg);
8+
#endif /* LIGHTNING_LIGHTNINGD_PING_H */

0 commit comments

Comments
 (0)