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

Part 2: connectd onionmessage improvements #7455

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
93 changes: 47 additions & 46 deletions common/onion_message_parse.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* Caller does fromwire_onion_message(), this does the rest. */
#include "config.h"
#include <assert.h>
#include <ccan/tal/str/str.h>
#include <common/blindedpath.h>
#include <common/ecdh.h>
#include <common/onion_message_parse.h>
Expand Down Expand Up @@ -39,7 +40,7 @@ static bool decrypt_final_onionmsg(const tal_t *ctx,
static bool decrypt_forwarding_onionmsg(const struct pubkey *blinding,
const struct secret *ss,
const u8 *enctlv,
struct pubkey *next_node,
struct sciddir_or_pubkey *next_node,
struct pubkey *next_blinding)
{
struct tlv_encrypted_data_tlv *encmsg;
Expand All @@ -57,29 +58,41 @@ static bool decrypt_forwarding_onionmsg(const struct pubkey *blinding,
if (encmsg->path_id)
return false;

/* BOLT #4:
* - SHOULD forward the message using `onion_message` to the next peer
* indicated by `next_node_id`.
/* BOLT-offers #4:
* - if it is not the final node according to the onion encryption:
*...
* - if `next_node_id` is present:
* - the *next peer* is the peer with that node id.
* - otherwise, if `short_channel_id` is present and corresponds to an announced short_channel_id or a local alias for a channel:
* - the *next peer* is the peer at the other end of that channel.
* - otherwise:
* - MUST ignore the message.
*/
if (!encmsg->next_node_id)
if (encmsg->next_node_id)
sciddir_or_pubkey_from_pubkey(next_node, encmsg->next_node_id);
else if (encmsg->short_channel_id) {
/* This is actually scid, not sciddir, but the type is convenient! */
struct short_channel_id_dir scidd;
scidd.scid = *encmsg->short_channel_id;
scidd.dir = 0;
sciddir_or_pubkey_from_scidd(next_node, &scidd);
} else
return false;

Comment on lines +79 to 81
Copy link
Collaborator

Choose a reason for hiding this comment

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

This looks like useful information to log, no? like "ignoring message blabla .." but this can be a followup PR

*next_node = *encmsg->next_node_id;
blindedpath_next_blinding(encmsg, blinding, ss, next_blinding);
return true;
}

/* Returns false on failure */
bool onion_message_parse(const tal_t *ctx,
const u8 *onion_message_packet,
const struct pubkey *blinding,
const struct node_id *peer,
const struct pubkey *me,
u8 **next_onion_msg,
struct pubkey *next_node_id,
struct tlv_onionmsg_tlv **final_om,
struct pubkey *final_alias,
struct secret **final_path_id)
const char *onion_message_parse(const tal_t *ctx,
const u8 *onion_message_packet,
const struct pubkey *blinding,
const struct pubkey *me,
u8 **next_onion_msg,
struct sciddir_or_pubkey *next_node,
struct tlv_onionmsg_tlv **final_om,
struct pubkey *final_alias,
struct secret **final_path_id)
{
enum onion_wire badreason;
struct onionpacket *op;
Expand All @@ -96,47 +109,40 @@ bool onion_message_parse(const tal_t *ctx,
tal_bytelen(onion_message_packet),
&badreason);
if (!op) {
status_peer_debug(peer, "onion_message_parse: can't parse onionpacket: %s",
onion_wire_name(badreason));
return false;
return tal_fmt(ctx, "onion_message_parse: can't parse onionpacket: %s",
onion_wire_name(badreason));
}

ephemeral = op->ephemeralkey;
if (!unblind_onion(blinding, ecdh, &ephemeral, &ss)) {
status_peer_debug(peer, "onion_message_parse: can't unblind onionpacket");
return false;
return tal_fmt(ctx, "onion_message_parse: can't unblind onionpacket");
}

/* Now get onion shared secret and parse it. */
ecdh(&ephemeral, &onion_ss);
rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0);
if (!rs) {
status_peer_debug(peer,
"onion_message_parse: can't process onionpacket ss=%s",
fmt_secret(tmpctx, &onion_ss));
return false;
return tal_fmt(ctx, "onion_message_parse: can't process onionpacket ss=%s",
fmt_secret(tmpctx, &onion_ss));
}

/* The raw payload is prepended with length in the modern onion. */
cursor = rs->raw_payload;
max = tal_bytelen(rs->raw_payload);
maxlen = fromwire_bigsize(&cursor, &max);
if (!cursor) {
status_peer_debug(peer, "onion_message_parse: Invalid hop payload %s",
tal_hex(tmpctx, rs->raw_payload));
return false;
return tal_fmt(ctx, "onion_message_parse: Invalid hop payload %s",
tal_hex(tmpctx, rs->raw_payload));
}
if (maxlen > max) {
status_peer_debug(peer, "onion_message_parse: overlong hop payload %s",
tal_hex(tmpctx, rs->raw_payload));
return false;
return tal_fmt(ctx, "onion_message_parse: overlong hop payload %s",
tal_hex(tmpctx, rs->raw_payload));
}

om = fromwire_tlv_onionmsg_tlv(tmpctx, &cursor, &maxlen);
if (!om) {
status_peer_debug(peer, "onion_message_parse: invalid onionmsg_tlv %s",
tal_hex(tmpctx, rs->raw_payload));
return false;
return tal_fmt(ctx, "onion_message_parse: invalid onionmsg_tlv %s",
tal_hex(tmpctx, rs->raw_payload));
}
if (rs->nextcase == ONION_END) {
*next_onion_msg = NULL;
Expand All @@ -149,10 +155,9 @@ bool onion_message_parse(const tal_t *ctx,
om->encrypted_recipient_data, me,
final_alias,
final_path_id)) {
status_peer_debug(peer,
return tal_fmt(ctx,
"onion_message_parse: failed to decrypt encrypted_recipient_data"
" %s", tal_hex(tmpctx, om->encrypted_recipient_data));
return false;
}
} else {
struct pubkey next_blinding;
Expand All @@ -165,19 +170,15 @@ bool onion_message_parse(const tal_t *ctx,
* - MUST ignore the message.
*/
if (tal_count(om->fields) != 1) {
status_peer_debug(peer,
"onion_message_parse: "
"disallowed tlv field");
return false;
return tal_fmt(ctx, "onion_message_parse: disallowed tlv field");
}

/* This fails as expected if no enctlv. */
if (!decrypt_forwarding_onionmsg(blinding, &ss, om->encrypted_recipient_data, next_node_id,
if (!decrypt_forwarding_onionmsg(blinding, &ss, om->encrypted_recipient_data, next_node,
&next_blinding)) {
status_peer_debug(peer,
"onion_message_parse: invalid encrypted_recipient_data %s",
tal_hex(tmpctx, om->encrypted_recipient_data));
return false;
return tal_fmt(ctx,
"onion_message_parse: invalid encrypted_recipient_data %s",
tal_hex(tmpctx, om->encrypted_recipient_data));
}
*next_onion_msg = towire_onion_message(ctx,
&next_blinding,
Expand All @@ -186,5 +187,5 @@ bool onion_message_parse(const tal_t *ctx,

/* Exactly one is set */
assert(!*next_onion_msg + !*final_om == 1);
return true;
return NULL;
}
26 changes: 12 additions & 14 deletions common/onion_message_parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,31 @@
#include <common/amount.h>

struct tlv_onionmsg_tlv;
struct node_id;
struct sciddir_or_pubkey;
struct pubkey;

/**
* onion_message_parse: core routine to check onion_message
* @ctx: context to allocate @next_onion_msg or @final_om/@path_id off
* @onion_message_packet: Sphinx-encrypted onion
* @blinding: Blinding we were given for @onion_message_packet
* @peer: node_id of peer (for status_peer_debug msgs)
* @me: my pubkey
* @next_onion_msg (out): set if we should forward, otherwise NULL.
* @next_node_id (out): set to node id to fwd to, iff *@next_onion_msg.
* @next_node (out): set to node id or scid to fwd to, iff *@next_onion_msg.
* @final_om (out): set if we're the final hop, otherwise NULL.
* @final_alias (out): our alias (if *@final_om), or our own ID
* @final_path_id (out): secret enclosed, if any (iff *@final_om).
*
* Returns false if it wasn't valid.
* Returns NULL if it was valid, otherwise an error string.
*/
bool onion_message_parse(const tal_t *ctx,
const u8 *onion_message_packet,
const struct pubkey *blinding,
const struct node_id *peer,
const struct pubkey *me,
u8 **next_onion_msg,
struct pubkey *next_node_id,
struct tlv_onionmsg_tlv **final_om,
struct pubkey *final_alias,
struct secret **final_path_id);
const char *onion_message_parse(const tal_t *ctx,
const u8 *onion_message_packet,
const struct pubkey *blinding,
const struct pubkey *me,
u8 **next_onion_msg,
struct sciddir_or_pubkey *next_node,
struct tlv_onionmsg_tlv **final_om,
struct pubkey *final_alias,
struct secret **final_path_id);

#endif /* LIGHTNING_COMMON_ONION_MESSAGE_PARSE_H */
26 changes: 7 additions & 19 deletions common/test/run-onion-message-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ static void maybe_print(const char *fmt, ...);
#include "../hmac.c"
#include "../onion_encode.c"
#include "../onion_message_parse.c"
#include "../sciddir_or_pubkey.c"
#include "../sphinx.c"
#include "../../wire/onion_wiregen.c"
#include "../../wire/peer_wiregen.c"
Expand All @@ -29,32 +30,18 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
/* Generated stub for fromwire_node_id */
void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED)
{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); }
/* Generated stub for fromwire_sciddir_or_pubkey */
void fromwire_sciddir_or_pubkey(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
struct sciddir_or_pubkey *sciddpk UNNEEDED)
{ fprintf(stderr, "fromwire_sciddir_or_pubkey called!\n"); abort(); }
/* Generated stub for new_onionreply */
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED)
{ fprintf(stderr, "new_onionreply called!\n"); abort(); }
/* Generated stub for pubkey_from_node_id */
bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED)
{ fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); }
/* Generated stub for status_fmt */
void status_fmt(enum log_level level UNNEEDED,
const struct node_id *peer UNNEEDED,
const char *fmt UNNEEDED, ...)

{ fprintf(stderr, "status_fmt called!\n"); abort(); }
/* Generated stub for towire_channel_id */
void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "towire_channel_id called!\n"); abort(); }
/* Generated stub for towire_node_id */
void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED)
{ fprintf(stderr, "towire_node_id called!\n"); abort(); }
/* Generated stub for towire_sciddir_or_pubkey */
void towire_sciddir_or_pubkey(u8 **pptr UNNEEDED,
const struct sciddir_or_pubkey *sciddpk UNNEEDED)
{ fprintf(stderr, "towire_sciddir_or_pubkey called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */

static bool comma;
Expand Down Expand Up @@ -355,7 +342,7 @@ int main(int argc, char *argv[])

json_start("hops", '[');
for (size_t i = 0; i < ARRAY_SIZE(erd); i++) {
struct pubkey next_node_id;
struct sciddir_or_pubkey next_node;
struct tlv_onionmsg_tlv *final_om;
struct pubkey final_alias;
struct secret *final_path_id;
Expand All @@ -371,14 +358,15 @@ int main(int argc, char *argv[])

/* For test_ecdh */
mykey = &privkey[i];
assert(onion_message_parse(tmpctx, onion_message_packet, &blinding_pub, NULL,
assert(onion_message_parse(tmpctx, onion_message_packet, &blinding_pub,
&id[i],
&onion_message, &next_node_id,
&onion_message, &next_node,
&final_om,
&final_alias,
&final_path_id));
&final_path_id) == NULL);
if (onion_message) {
json_pubkey("next_node_id", &next_node_id);
assert(next_node.is_pubkey);
json_pubkey("next_node_id", &next_node.pubkey);
} else {
const struct tlv_field *hello;
json_start("tlvs", '{');
Expand Down
35 changes: 35 additions & 0 deletions connectd/connectd.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ static struct peer *new_peer(struct daemon *daemon,
peer->scid_query_idx = 0;
peer->scid_query_nodes = NULL;
peer->scid_query_nodes_idx = 0;
peer->onionmsg_incoming_tokens = ONION_MSG_TOKENS_MAX;
peer->onionmsg_last_incoming = time_mono();
peer->onionmsg_limit_warned = false;

peer->to_peer = conn;

Expand Down Expand Up @@ -1864,6 +1867,26 @@ static void peer_send_msg(struct io_conn *conn,
inject_peer_msg(peer, take(sendmsg));
}

/* lightningd tells us about a new short_channel_id for a peer. */
static void add_scid_map(struct daemon *daemon, const u8 *msg)
{
struct scid_to_node_id *scid_to_node_id, *old;

scid_to_node_id = tal(daemon->scid_htable, struct scid_to_node_id);
if (!fromwire_connectd_scid_map(msg,
&scid_to_node_id->scid,
&scid_to_node_id->node_id))
master_badmsg(WIRE_CONNECTD_SCID_MAP, msg);

/* Make sure we clean up any old entries */
old = scid_htable_get(daemon->scid_htable, scid_to_node_id->scid);
if (old) {
scid_htable_del(daemon->scid_htable, old);
tal_free(old);
}
scid_htable_add(daemon->scid_htable, scid_to_node_id);
}

static void dev_connect_memleak(struct daemon *daemon, const u8 *msg)
{
struct htable *memtable;
Expand All @@ -1875,6 +1898,7 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg)
/* Now delete daemon and those which it has pointers to. */
memleak_scan_obj(memtable, daemon);
memleak_scan_htable(memtable, &daemon->peers->raw);
memleak_scan_htable(memtable, &daemon->scid_htable->raw);

found_leak = dump_memleak(memtable, memleak_status_broken, NULL);
daemon_conn_send(daemon->master,
Expand Down Expand Up @@ -2174,6 +2198,14 @@ static struct io_plan *recv_req(struct io_conn *conn,
set_custommsgs(daemon, msg);
goto out;

case WIRE_CONNECTD_INJECT_ONIONMSG:
inject_onionmsg_req(daemon, msg);
goto out;

case WIRE_CONNECTD_SCID_MAP:
add_scid_map(daemon, msg);
goto out;

case WIRE_CONNECTD_DEV_MEMLEAK:
if (daemon->developer) {
dev_connect_memleak(daemon, msg);
Expand Down Expand Up @@ -2216,6 +2248,7 @@ static struct io_plan *recv_req(struct io_conn *conn,
case WIRE_CONNECTD_CUSTOMMSG_IN:
case WIRE_CONNECTD_PEER_DISCONNECT_DONE:
case WIRE_CONNECTD_START_SHUTDOWN_REPLY:
case WIRE_CONNECTD_INJECT_ONIONMSG_REPLY:
break;
}

Expand Down Expand Up @@ -2301,6 +2334,8 @@ int main(int argc, char *argv[])
daemon->dev_exhausted_fds = false;
/* We generally allow 1MB per second per peer, except for dev testing */
daemon->gossip_stream_limit = 1000000;
daemon->scid_htable = tal(daemon, struct scid_htable);
scid_htable_init(daemon->scid_htable);

/* stdin == control */
daemon->master = daemon_conn_new(daemon, STDIN_FILENO, recv_req, NULL,
Expand Down
Loading
Loading