lightningd: --dev-disconnect support.

We use a file descriptor, so when we consume an entry, we move past it
(and everyone shares a file offset, so this works).

The file contains packet names prefixed by - (treat fd as closed when
we try to write this packet), + (write the packet then ensure the file
descriptor fails), or @ ("lose" the packet then ensure the file
descriptor fails).

The sync and async peer-write functions hook this in automatically.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>



Header from folded patch 'test-run-cryptomsg__fix_compilation.patch':

test/run-cryptomsg: fix compilation.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2017-05-24 19:40:16 +09:30
parent 8f190c673c
commit 9b1d240c1f
11 changed files with 175 additions and 9 deletions

View File

@ -44,6 +44,7 @@ LIGHTNINGD_LIB_SRC := \
lightningd/debug.c \ lightningd/debug.c \
lightningd/daemon_conn.c \ lightningd/daemon_conn.c \
lightningd/derive_basepoints.c \ lightningd/derive_basepoints.c \
lightningd/dev_disconnect.c \
lightningd/funding_tx.c \ lightningd/funding_tx.c \
lightningd/gossip_msg.c \ lightningd/gossip_msg.c \
lightningd/htlc_tx.c \ lightningd/htlc_tx.c \

View File

@ -3,19 +3,38 @@
#include <inttypes.h> #include <inttypes.h>
#include <lightningd/crypto_sync.h> #include <lightningd/crypto_sync.h>
#include <lightningd/cryptomsg.h> #include <lightningd/cryptomsg.h>
#include <lightningd/dev_disconnect.h>
#include <lightningd/status.h> #include <lightningd/status.h>
#include <utils.h> #include <utils.h>
#include <wire/wire.h>
#include <wire/wire_sync.h> #include <wire/wire_sync.h>
bool sync_crypto_write(struct crypto_state *cs, int fd, const void *msg) bool sync_crypto_write(struct crypto_state *cs, int fd, const void *msg)
{ {
u8 *enc = cryptomsg_encrypt_msg(msg, cs, msg); u8 *enc = cryptomsg_encrypt_msg(msg, cs, msg);
bool ret; bool ret;
bool post_sabotage = false;
status_trace("Writing crypto with sn=%"PRIu64" msg=%s", status_trace("Writing crypto with sn=%"PRIu64" msg=%s",
cs->sn-2, tal_hex(trc, msg)); cs->sn-2, tal_hex(trc, msg));
switch (dev_disconnect(fromwire_peektype(msg))) {
case DEV_DISCONNECT_BEFORE:
dev_sabotage_fd(fd);
return false;
case DEV_DISCONNECT_DROPPKT:
enc = tal_free(enc); /* FALL THRU */
case DEV_DISCONNECT_AFTER:
post_sabotage = true;
break;
default:
break;
}
ret = write_all(fd, enc, tal_len(enc)); ret = write_all(fd, enc, tal_len(enc));
tal_free(enc); tal_free(enc);
if (post_sabotage)
dev_sabotage_fd(fd);
return ret; return ret;
} }

View File

@ -7,6 +7,7 @@
#include <ccan/short_types/short_types.h> #include <ccan/short_types/short_types.h>
#include <ccan/take/take.h> #include <ccan/take/take.h>
#include <lightningd/cryptomsg.h> #include <lightningd/cryptomsg.h>
#include <lightningd/dev_disconnect.h>
#include <lightningd/status.h> #include <lightningd/status.h>
#include <sodium/crypto_aead_chacha20poly1305.h> #include <sodium/crypto_aead_chacha20poly1305.h>
#include <utils.h> #include <utils.h>
@ -316,12 +317,21 @@ u8 *cryptomsg_encrypt_msg(const tal_t *ctx,
return out; return out;
} }
static struct io_plan *peer_write_postclose(struct io_conn *conn,
struct peer_crypto_state *pcs)
{
pcs->out = tal_free(pcs->out);
dev_sabotage_fd(io_conn_fd(conn));
return pcs->next_out(conn, pcs->peer);
}
struct io_plan *peer_write_message(struct io_conn *conn, struct io_plan *peer_write_message(struct io_conn *conn,
struct peer_crypto_state *pcs, struct peer_crypto_state *pcs,
const u8 *msg, const u8 *msg,
struct io_plan *(*next)(struct io_conn *, struct io_plan *(*next)(struct io_conn *,
struct peer *)) struct peer *))
{ {
struct io_plan *(*post)(struct io_conn *, struct peer_crypto_state *);
assert(!pcs->out); assert(!pcs->out);
pcs->out = cryptomsg_encrypt_msg(conn, &pcs->cs, msg); pcs->out = cryptomsg_encrypt_msg(conn, &pcs->cs, msg);
@ -329,11 +339,24 @@ struct io_plan *peer_write_message(struct io_conn *conn,
tal_free(msg); tal_free(msg);
pcs->next_out = next; pcs->next_out = next;
post = peer_write_done;
switch (dev_disconnect(fromwire_peektype(msg))) {
case DEV_DISCONNECT_BEFORE:
return io_close(conn);
case DEV_DISCONNECT_DROPPKT:
pcs->out = NULL; /* FALL THRU */
case DEV_DISCONNECT_AFTER:
post = peer_write_postclose;
break;
default:
break;
}
/* BOLT #8: /* BOLT #8:
* * Send `lc || c` over the network buffer. * * Send `lc || c` over the network buffer.
*/ */
return io_write(conn, pcs->out, tal_count(pcs->out), return io_write(conn, pcs->out, tal_count(pcs->out), post, pcs);
peer_write_done, pcs);
} }
void init_peer_crypto_state(struct peer *peer, struct peer_crypto_state *pcs) void init_peer_crypto_state(struct peer *peer, struct peer_crypto_state *pcs)

View File

@ -1,6 +1,8 @@
#include <ccan/str/str.h> #include <ccan/str/str.h>
#include <lightningd/debug.h> #include <lightningd/debug.h>
#include <lightningd/dev_disconnect.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
@ -9,6 +11,13 @@ void subdaemon_debug(int argc, char *argv[])
int i; int i;
bool printed = false; bool printed = false;
for (i = 1; i < argc; i++) {
if (strstarts(argv[i], "--dev-disconnect=")) {
dev_disconnect_init(atoi(argv[i]
+ strlen("--dev-disconnect=")));
}
}
/* From debugger, tell gdb "return". */ /* From debugger, tell gdb "return". */
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
while (streq(argv[i], "--debugger")) { while (streq(argv[i], "--debugger")) {

View File

@ -0,0 +1,55 @@
#include <assert.h>
#include <ccan/err/err.h>
#include <ccan/str/str.h>
#include <lightningd/dev_disconnect.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <wire/gen_peer_wire.h>
/* We move the fd IFF we do a disconnect. */
static int dev_disconnect_fd = -1;
static char dev_disconnect_line[200];
void dev_disconnect_init(int fd)
{
int r;
r = read(fd, dev_disconnect_line, sizeof(dev_disconnect_line)-1);
if (r < 0)
err(1, "Reading dev_disconnect file");
lseek(fd, -r, SEEK_CUR);
/* Get first line */
dev_disconnect_line[r] = '\n';
*strchr(dev_disconnect_line, '\n') = '\0';
/* So we can move forward if we do use the line. */
dev_disconnect_fd = fd;
}
char dev_disconnect(int pkt_type)
{
if (!streq(wire_type_name(pkt_type), dev_disconnect_line+1))
return DEV_DISCONNECT_NORMAL;
assert(dev_disconnect_fd != -1);
lseek(dev_disconnect_fd, strlen(dev_disconnect_line)+1, SEEK_CUR);
return dev_disconnect_line[0];
}
void dev_sabotage_fd(int fd)
{
int fds[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0)
errx(1, "dev_sabotage_fd: creating socketpair");
/* Close one. */
close(fds[0]);
/* Move other over to the fd we want to sabotage. */
dup2(fds[1], fd);
close(fds[1]);
}

View File

@ -0,0 +1,20 @@
#ifndef LIGHTNING_LIGHTNINGD_DEV_DISCONNECT_H
#define LIGHTNING_LIGHTNINGD_DEV_DISCONNECT_H
#include "config.h"
#include <stdbool.h>
#define DEV_DISCONNECT_BEFORE '-'
#define DEV_DISCONNECT_AFTER '+'
#define DEV_DISCONNECT_DROPPKT '@'
#define DEV_DISCONNECT_NORMAL 0
/* Force a close fd before or after a certain packet type */
char dev_disconnect(int pkt_type);
/* Make next write on fd fail as if they'd disconnected. */
void dev_sabotage_fd(int fd);
/* For debug code to set in daemon. */
void dev_disconnect_init(int fd);
#endif /* LIGHTNING_LIGHTNINGD_DEV_DISCONNECT_H */

View File

@ -111,6 +111,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
ld->dev_debug_subdaemon = NULL; ld->dev_debug_subdaemon = NULL;
list_head_init(&ld->utxos); list_head_init(&ld->utxos);
htlc_end_map_init(&ld->htlc_ends); htlc_end_map_init(&ld->htlc_ends);
ld->dev_disconnect_fd = -1;
ld->dstate.log_book = new_log_book(&ld->dstate, 20*1024*1024,LOG_INFORM); ld->dstate.log_book = new_log_book(&ld->dstate, 20*1024*1024,LOG_INFORM);
ld->log = ld->dstate.base_log = new_log(&ld->dstate, ld->log = ld->dstate.base_log = new_log(&ld->dstate,
ld->dstate.log_book, ld->dstate.log_book,
@ -233,6 +234,9 @@ int main(int argc, char *argv[])
opt_show_uintval, &ld->broadcast_interval, opt_show_uintval, &ld->broadcast_interval,
"Time between gossip broadcasts in milliseconds (default: 30000)"); "Time between gossip broadcasts in milliseconds (default: 30000)");
opt_register_arg("--dev-disconnect=<filename>", opt_subd_dev_disconnect,
NULL, ld, "File containing disconnection points");
/* FIXME: move to option initialization once we drop the /* FIXME: move to option initialization once we drop the
* legacy daemon */ * legacy daemon */
ld->broadcast_interval = 30000; ld->broadcast_interval = 30000;

View File

@ -50,6 +50,9 @@ struct lightningd {
/* If we want to debug a subdaemon. */ /* If we want to debug a subdaemon. */
const char *dev_debug_subdaemon; const char *dev_debug_subdaemon;
/* If we have a --dev-disconnect file */
int dev_disconnect_fd;
/* UTXOs we have available to spend. */ /* UTXOs we have available to spend. */
struct list_head utxos; struct list_head utxos;

View File

@ -3,6 +3,7 @@
#include <ccan/noerr/noerr.h> #include <ccan/noerr/noerr.h>
#include <ccan/take/take.h> #include <ccan/take/take.h>
#include <ccan/tal/path/path.h> #include <ccan/tal/path/path.h>
#include <ccan/tal/str/str.h>
#include <daemon/log.h> #include <daemon/log.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@ -120,7 +121,7 @@ static struct subd_req *get_req(struct subd *sd, int reply_type)
/* We use sockets, not pipes, because fds are bidir. */ /* We use sockets, not pipes, because fds are bidir. */
static int subd(const char *dir, const char *name, bool debug, static int subd(const char *dir, const char *name, bool debug,
int *msgfd, va_list ap) int *msgfd, int dev_disconnect_fd, va_list ap)
{ {
int childmsg[2], execfail[2]; int childmsg[2], execfail[2];
pid_t childpid; pid_t childpid;
@ -143,7 +144,7 @@ static int subd(const char *dir, const char *name, bool debug,
if (childpid == 0) { if (childpid == 0) {
int fdnum = 3, i; int fdnum = 3, i;
long max; long max;
const char *debug_arg = NULL; const char *debug_arg[2] = { NULL, NULL };
close(childmsg[0]); close(childmsg[0]);
close(execfail[0]); close(execfail[0]);
@ -154,6 +155,13 @@ static int subd(const char *dir, const char *name, bool debug,
goto child_errno_fail; goto child_errno_fail;
} }
// Move dev_disconnect_fd out the way.
if (dev_disconnect_fd != -1) {
if (!move_fd(dev_disconnect_fd, 101))
goto child_errno_fail;
dev_disconnect_fd = 101;
}
/* Dup any extra fds up first. */ /* Dup any extra fds up first. */
while ((fd = va_arg(ap, int *)) != NULL) { while ((fd = va_arg(ap, int *)) != NULL) {
/* If this were stdin, dup2 closed! */ /* If this were stdin, dup2 closed! */
@ -166,11 +174,14 @@ static int subd(const char *dir, const char *name, bool debug,
/* Make (fairly!) sure all other fds are closed. */ /* Make (fairly!) sure all other fds are closed. */
max = sysconf(_SC_OPEN_MAX); max = sysconf(_SC_OPEN_MAX);
for (i = fdnum; i < max; i++) for (i = fdnum; i < max; i++)
if (i != dev_disconnect_fd)
close(i); close(i);
if (dev_disconnect_fd != -1)
debug_arg[0] = tal_fmt(NULL, "--dev-disconnect=%i", dev_disconnect_fd);
if (debug) if (debug)
debug_arg = "--debugger"; debug_arg[debug_arg[0] ? 1 : 0] = "--debugger";
execl(path_join(NULL, dir, name), name, debug_arg, NULL); execl(path_join(NULL, dir, name), name, debug_arg[0], debug_arg[1], NULL);
child_errno_fail: child_errno_fail:
err = errno; err = errno;
@ -381,7 +392,8 @@ struct subd *new_subd(const tal_t *ctx,
debug = ld->dev_debug_subdaemon debug = ld->dev_debug_subdaemon
&& strends(name, ld->dev_debug_subdaemon); && strends(name, ld->dev_debug_subdaemon);
va_start(ap, finished); va_start(ap, finished);
sd->pid = subd(ld->daemon_dir, name, debug, &msg_fd, ap); sd->pid = subd(ld->daemon_dir, name, debug, &msg_fd,
ld->dev_disconnect_fd, ap);
va_end(ap); va_end(ap);
if (sd->pid == (pid_t)-1) { if (sd->pid == (pid_t)-1) {
log_unusual(ld->log, "subd %s failed: %s", log_unusual(ld->log, "subd %s failed: %s",
@ -467,3 +479,12 @@ char *opt_subd_debug(const char *optarg, struct lightningd *ld)
ld->dev_debug_subdaemon = optarg; ld->dev_debug_subdaemon = optarg;
return NULL; return NULL;
} }
char *opt_subd_dev_disconnect(const char *optarg, struct lightningd *ld)
{
ld->dev_disconnect_fd = open(optarg, O_RDONLY);
if (ld->dev_disconnect_fd < 0)
return tal_fmt(ld, "Could not open --dev-disconnect=%s: %s",
optarg, strerror(errno));
return NULL;
}

View File

@ -122,5 +122,5 @@ void subd_req_(const tal_t *ctx,
void subd_shutdown(struct subd *subd, unsigned int seconds); void subd_shutdown(struct subd *subd, unsigned int seconds);
char *opt_subd_debug(const char *optarg, struct lightningd *ld); char *opt_subd_debug(const char *optarg, struct lightningd *ld);
char *opt_subd_dev_disconnect(const char *optarg, struct lightningd *ld);
#endif /* LIGHTNING_LIGHTNINGD_SUBD_H */ #endif /* LIGHTNING_LIGHTNINGD_SUBD_H */

View File

@ -1,6 +1,7 @@
#include <assert.h> #include <assert.h>
#include <ccan/str/hex/hex.h> #include <ccan/str/hex/hex.h>
#include <ccan/tal/str/str.h> #include <ccan/tal/str/str.h>
#include <lightningd/dev_disconnect.h>
#include <lightningd/status.h> #include <lightningd/status.h>
#include <stdio.h> #include <stdio.h>
#include <wire/peer_wire.h> #include <wire/peer_wire.h>
@ -38,6 +39,16 @@ static void do_write(const void *buf, size_t len)
#define status_trace(fmt, ...) \ #define status_trace(fmt, ...) \
printf(fmt "\n", __VA_ARGS__) printf(fmt "\n", __VA_ARGS__)
void dev_sabotage_fd(int fd)
{
abort();
}
char dev_disconnect(int pkt_type)
{
return DEV_DISCONNECT_NORMAL;
}
/* We test what look like unknown messages. */ /* We test what look like unknown messages. */
#define unknown_msg_discardable(x) 0 #define unknown_msg_discardable(x) 0