Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better handling unexpected msgs #841

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 72 additions & 22 deletions channeld/channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1555,7 +1555,6 @@ static void handle_peer_shutdown(struct peer *peer, const u8 *shutdown)
static void peer_in(struct peer *peer, const u8 *msg)
{
enum wire_type type = fromwire_peektype(msg);
status_trace("peer_in %s", wire_type_name(type));

/* FIXME: We don't support concurrent channels with same peer. */
if (type == WIRE_OPEN_CHANNEL) {
Expand Down Expand Up @@ -1767,6 +1766,73 @@ static void resend_commitment(struct peer *peer, const struct changed_htlc *last
assert(peer->revocations_received == peer->next_index[REMOTE] - 2);
}

/* Handle random messages we might get, returning NULL if we handled it. */
static u8 *read_peer_msg(struct peer *peer)
{
u8 *msg;
struct channel_id channel_id;

msg = sync_crypto_read(peer, &peer->cs, PEER_FD);
if (!msg)
peer_conn_broken(peer);

status_trace("peer_in %s", wire_type_name(fromwire_peektype(msg)));

if (is_gossip_msg(msg)) {
/* Forward to gossip daemon */
wire_sync_write(GOSSIP_FD, take(msg));
return NULL;
}

if (fromwire_peektype(msg) == WIRE_PING) {
handle_ping(peer, msg);
return tal_free(msg);
}

if (fromwire_peektype(msg) == WIRE_ERROR) {
struct channel_id chanid;
char *err = sanitize_error(msg, msg, &chanid);

/* BOLT #1:
*
* The channel is referred to by `channel_id`, unless
* `channel_id` is 0 (i.e. all bytes are 0), in which
* case it refers to all channels.
* ...

* The receiving node:
* - upon receiving `error`:
* - MUST fail the channel referred to by the error
* message.
* - if no existing channel is referred to by the
* message:
* - MUST ignore the message.
*/
if (channel_id_is_all(&chanid)
|| structeq(&chanid, &peer->channel_id)) {
status_failed(STATUS_FAIL_PEER_BAD,
"Received ERROR %s", err);
}
return tal_free(msg);
}

/* They're talking about a different channel? */
if (extract_channel_id(msg, &channel_id)
&& !structeq(&channel_id, &peer->channel_id)) {
status_trace("Rejecting %s for unknown channel_id %s",
wire_type_name(fromwire_peektype(msg)),
type_to_string(msg, struct channel_id,
&channel_id));
enqueue_peer_msg(peer,
take(towire_errorfmt(msg, &channel_id,
"Multiple channels"
" unsupported")));
return tal_free(msg);
}

return msg;
}

static void peer_reconnect(struct peer *peer)
{
struct channel_id channel_id;
Expand Down Expand Up @@ -1797,22 +1863,8 @@ static void peer_reconnect(struct peer *peer)
status_failed(STATUS_FAIL_PEER_IO,
"Failed writing reestablish: %s", strerror(errno));

again:
msg = sync_crypto_read(peer, &peer->cs, PEER_FD);
if (!msg)
status_failed(STATUS_FAIL_PEER_IO,
"Failed reading reestablish: %s", strerror(errno));

if (is_gossip_msg(msg)) {
/* Forward to gossip daemon */
wire_sync_write(GOSSIP_FD, take(msg));
goto again;
}

if (fromwire_peektype(msg) == WIRE_PING) {
handle_ping(peer, msg);
goto again;
}
/* Read until they say something interesting */
while ((msg = read_peer_msg(peer)) == NULL);

if (!fromwire_channel_reestablish(msg, NULL, &channel_id,
&next_local_commitment_number,
Expand Down Expand Up @@ -2706,11 +2758,9 @@ int main(int argc, char *argv[])
gossip_in(peer, msg);
} else if (FD_ISSET(PEER_FD, &rfds)) {
/* This could take forever, but who cares? */
msg = sync_crypto_read(peer, &peer->cs, PEER_FD);

if (!msg)
peer_conn_broken(peer);
peer_in(peer, msg);
msg = read_peer_msg(peer);
if (msg)
peer_in(peer, msg);
} else
msg = NULL;
tal_free(msg);
Expand Down
1 change: 1 addition & 0 deletions closingd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ CLOSINGD_COMMON_OBJS := \
common/msg_queue.o \
common/peer_failed.o \
common/permute_tx.o \
common/ping.o \
common/status.o \
common/subdaemon.o \
common/type_to_string.o \
Expand Down
115 changes: 93 additions & 22 deletions closingd/closing.c
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
#include <bitcoin/script.h>
#include <ccan/structeq/structeq.h>
#include <closingd/gen_closing_wire.h>
#include <common/close_tx.h>
#include <common/crypto_sync.h>
#include <common/derive_basepoints.h>
#include <common/htlc.h>
#include <common/peer_failed.h>
#include <common/ping.h>
#include <common/status.h>
#include <common/subdaemon.h>
#include <common/type_to_string.h>
#include <common/utils.h>
#include <common/version.h>
#include <common/wire_error.h>
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
Expand Down Expand Up @@ -80,6 +83,92 @@ static u64 one_towards(u64 target, u64 value)
return value;
}

static void handle_ping(const u8 *msg,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cannot we factor out this function to common/ping.c? I dislike multiple functions with different contents having the same name. In addition, the handle_ping in openingd and closingd look very very similar (possibly except for PEER_FD but that can be passed in). Only the handle_ping in channeld is different because it enqueues the pong reply rather than sending it synchronously (but maybe sending it synchronously is acceptable to reduce code duplication...?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, as commit msg says, they're just similar enough to be annoying. I think trying to merge this is a future TODO.

struct crypto_state *cs,
const struct channel_id *our_channel_id)
{
u8 *pong;

if (!check_ping_make_pong(msg, msg, &pong))
peer_failed(PEER_FD, cs, our_channel_id, "Bad ping");

status_trace("Got ping, sending %s", pong ?
wire_type_name(fromwire_peektype(pong))
: "nothing");

if (pong && !sync_crypto_write(cs, PEER_FD, take(pong)))
status_failed(STATUS_FAIL_PEER_IO,
"Failed writing pong: %s", strerror(errno));
}

/* Handle random messages we might get, returning NULL if we handled it. */
static u8 *read_peer_msg(const tal_t *ctx,
struct crypto_state *cs,
const struct channel_id *our_channel_id)
{
u8 *msg;
struct channel_id channel_id;

msg = sync_crypto_read(ctx, cs, PEER_FD);
if (!msg)
status_failed(STATUS_FAIL_PEER_IO,
"Failed reading from peer: %s", strerror(errno));

if (is_gossip_msg(msg)) {
/* Forward to gossip daemon */
wire_sync_write(GOSSIP_FD, take(msg));
return NULL;
}

if (fromwire_peektype(msg) == WIRE_PING) {
handle_ping(msg, cs, our_channel_id);
return tal_free(msg);
}

if (fromwire_peektype(msg) == WIRE_ERROR) {
struct channel_id chanid;
char *err = sanitize_error(msg, msg, &chanid);

/* BOLT #1:
*
* The channel is referred to by `channel_id`, unless
* `channel_id` is 0 (i.e. all bytes are 0), in which
* case it refers to all channels.
* ...

* The receiving node:
* - upon receiving `error`:
* - MUST fail the channel referred to by the error
* message.
* - if no existing channel is referred to by the
* message:
* - MUST ignore the message.
*/
if (channel_id_is_all(&chanid)
|| structeq(&chanid, our_channel_id)) {
status_failed(STATUS_FAIL_PEER_BAD,
"Received ERROR %s", err);
}
return tal_free(msg);
}

/* They're talking about a different channel? */
if (extract_channel_id(msg, &channel_id)
&& !structeq(&channel_id, our_channel_id)) {
status_trace("Rejecting %s for unknown channel_id %s",
wire_type_name(fromwire_peektype(msg)),
type_to_string(msg, struct channel_id,
&channel_id));
sync_crypto_write(cs, PEER_FD,
take(towire_errorfmt(msg, &channel_id,
"Multiple channels"
" unsupported")));
return tal_free(msg);
}

return msg;
}

static void do_reconnect(struct crypto_state *cs,
const struct channel_id *channel_id,
const u64 next_index[NUM_SIDES],
Expand Down Expand Up @@ -108,17 +197,8 @@ static void do_reconnect(struct crypto_state *cs,
status_failed(STATUS_FAIL_PEER_IO,
"Failed writing reestablish: %s", strerror(errno));

again:
msg = sync_crypto_read(tmpctx, cs, PEER_FD);
if (!msg)
status_failed(STATUS_FAIL_PEER_IO,
"Failed reading reestablish: %s", strerror(errno));

if (is_gossip_msg(msg)) {
if (!wire_sync_write(GOSSIP_FD, take(msg)))
status_failed(STATUS_FAIL_GOSSIP_IO, "Writing gossip");
goto again;
}
/* Wait for them to say something interesting */
while ((msg = read_peer_msg(tmpctx, cs, channel_id)) == NULL);

if (!fromwire_channel_reestablish(msg, NULL, &their_channel_id,
&next_local_commitment_number,
Expand Down Expand Up @@ -263,17 +343,8 @@ int main(int argc, char *argv[])
break;

again:
msg = sync_crypto_read(tmpctx, &cs, PEER_FD);
if (!msg)
status_failed(STATUS_FAIL_PEER_IO, "Reading input");

/* We don't send gossip at this stage, but we can recv it */
if (is_gossip_msg(msg)) {
if (!wire_sync_write(GOSSIP_FD, take(msg)))
status_failed(STATUS_FAIL_GOSSIP_IO,
"Writing gossip");
goto again;
}
/* Wait for them to say something interesting */
while ((msg = read_peer_msg(tmpctx, &cs, &channel_id)) == NULL);

/* BOLT #2:
*
Expand Down
3 changes: 2 additions & 1 deletion lightningd/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ new_log(const tal_t *ctx, struct log_book *record, const char *fmt, ...)
log->lr = record;
va_start(ap, fmt);
/* log->lr owns this, since its entries keep a pointer to it. */
log->prefix = tal_vfmt(log->lr, fmt, ap);
/* FIXME: Refcount this! */
log->prefix = notleak(tal_vfmt(log->lr, fmt, ap));
va_end(ap);

return log;
Expand Down
6 changes: 2 additions & 4 deletions lightningd/peer_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -699,12 +699,10 @@ static void copy_to_parent_log(const char *prefix,
const char *str,
struct peer *peer)
{
const char *idstr = type_to_string(peer, struct pubkey, &peer->id);
if (continued)
log_add(peer->ld->log, "Peer %s: ... %s", idstr, str);
log_add(peer->ld->log, "%s ... %s", prefix, str);
else
log_(peer->ld->log, level, "Peer %s: %s", idstr, str);
tal_free(idstr);
log_(peer->ld->log, level, "%s %s", prefix, str);
}

void populate_peer(struct lightningd *ld, struct peer *peer)
Expand Down
11 changes: 10 additions & 1 deletion lightningd/subd.c
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,16 @@ static struct subd *new_subd(struct lightningd *ld,
return tal_free(sd);
}
sd->ld = ld;
sd->log = new_log(sd, ld->log_book, "%s(%u):", name, sd->pid);
if (peer) {
/* FIXME: Use minimal unique pubkey prefix for logs! */
const char *idstr = type_to_string(peer, struct pubkey,
&peer->id);
sd->log = new_log(sd, peer->log_book, "%s(%s):", name, idstr);
tal_free(idstr);
} else {
sd->log = new_log(sd, ld->log_book, "%s(%u):", name, sd->pid);
}

sd->name = name;
sd->must_not_exit = false;
sd->msgname = msgname;
Expand Down
Loading