From b255d82ae210f148c6ad05774500b7a27c9058ba Mon Sep 17 00:00:00 2001 From: ZmnSCPxj Date: Wed, 17 Jan 2018 23:32:36 +0000 Subject: [PATCH] gossipd: Implement `gossip_routing_failure` for master to report routing failures. --- gossipd/Makefile | 3 +- gossipd/gossip.c | 24 ++++++ gossipd/gossip_wire.csv | 7 ++ gossipd/routing.c | 103 +++++++++++++++++++++++++ gossipd/routing.h | 6 ++ gossipd/test/run-bench-find_route.c | 3 + gossipd/test/run-find_route-specific.c | 3 + gossipd/test/run-find_route.c | 3 + lightningd/gossip_control.c | 1 + 9 files changed, 152 insertions(+), 1 deletion(-) diff --git a/gossipd/Makefile b/gossipd/Makefile index 5740cc93e..0a8b3f93f 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -56,7 +56,8 @@ GOSSIPD_COMMON_OBJS := \ common/wire_error.o \ hsmd/client.o \ hsmd/gen_hsm_client_wire.o \ - lightningd/gossip_msg.o + lightningd/gossip_msg.o \ + wire/gen_onion_wire.o $(LIGHTNINGD_GOSSIP_OBJS) $(LIGHTNINGD_GOSSIP_CLIENT_OBJS): $(LIGHTNINGD_HEADERS) $(LIGHTNINGD_GOSSIP_HEADERS) diff --git a/gossipd/gossip.c b/gossipd/gossip.c index c4d9c58eb..439ce8253 100644 --- a/gossipd/gossip.c +++ b/gossipd/gossip.c @@ -1883,6 +1883,27 @@ fail: tal_free(tmpctx); return daemon_conn_read_next(conn, &daemon->master); } +static struct io_plan *handle_routing_failure(struct io_conn *conn, + struct daemon *daemon, + const u8 *msg) +{ + struct pubkey erring_node; + struct short_channel_id erring_channel; + u16 failcode; + + if (!fromwire_gossip_routing_failure(msg, NULL, + &erring_node, + &erring_channel, + &failcode)) + master_badmsg(WIRE_GOSSIP_ROUTING_FAILURE, msg); + + routing_failure(daemon->rstate, + &erring_node, + &erring_channel, + (enum onion_type) failcode); + + return daemon_conn_read_next(conn, &daemon->master); +} static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master) { @@ -1932,6 +1953,9 @@ static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master case WIRE_GOSSIP_DISABLE_CHANNEL: return handle_disable_channel(conn, daemon, master->msg_in); + case WIRE_GOSSIP_ROUTING_FAILURE: + return handle_routing_failure(conn, daemon, master->msg_in); + /* We send these, we don't receive them */ case WIRE_GOSSIPCTL_RELEASE_PEER_REPLY: case WIRE_GOSSIPCTL_RELEASE_PEER_REPLYFAIL: diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 41e183c91..e6c35ee22 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -1,5 +1,6 @@ #include #include +#include # Initialize the gossip daemon. gossipctl_init,3000 @@ -195,3 +196,9 @@ gossip_disable_channel,3019 gossip_disable_channel,,short_channel_id,struct short_channel_id gossip_disable_channel,,direction,u8 gossip_disable_channel,,active,bool + +# master->gossipd a routing failure occurred +gossip_routing_failure,3021 +gossip_routing_failure,,erring_node,struct pubkey +gossip_routing_failure,,erring_channel,struct short_channel_id +gossip_routing_failure,,failcode,u16 diff --git a/gossipd/routing.c b/gossipd/routing.c index 46d6c4e3d..08e6bd615 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #ifndef SUPERVERBOSE @@ -238,6 +239,12 @@ get_or_make_connection(struct routing_state *rstate, return nc; } +static void delete_connection(struct routing_state *rstate, + const struct node_connection *connection) +{ + tal_free(connection); +} + struct node_connection *half_add_connection( struct routing_state *rstate, const struct pubkey *from, @@ -1007,3 +1014,99 @@ struct route_hop *get_route(tal_t *ctx, struct routing_state *rstate, /* FIXME: Shadow route! */ return hops; } + +/* Get the struct node_connection matching the short_channel_id, + * which must be an out connection of the given node. */ +static struct node_connection * +get_out_node_connection_of(struct routing_state *rstate, + const struct node *node, + const struct short_channel_id *short_channel_id) +{ + int i; + + for (i = 0; i < tal_count(node->out); ++i) { + if (short_channel_id_eq(&node->out[i]->short_channel_id, short_channel_id)) + return node->out[i]; + } + + return NULL; +} + +/** + * routing_failure_on_nc - Handle routing failure on a specific + * node_connection. + */ +static void routing_failure_on_nc(struct routing_state *rstate, + enum onion_type failcode, + struct node_connection *nc) +{ + /* BOLT #4: + * + * - if the PERM bit is NOT set: + * - SHOULD restore the channels as it receives new `channel_update`s. + */ + if (failcode & PERM) + nc->active = false; + else + delete_connection(rstate, nc); +} + +void routing_failure(struct routing_state *rstate, + const struct pubkey *erring_node_pubkey, + const struct short_channel_id *scid, + enum onion_type failcode) +{ + const tal_t *tmpctx = tal_tmpctx(rstate); + struct node *node; + struct node_connection *nc; + int i; + + status_trace("Received routing failure 0x%04x (%s), " + "erring node %s, " + "channel %s", + (int) failcode, onion_type_name(failcode), + type_to_string(tmpctx, struct pubkey, erring_node_pubkey), + type_to_string(tmpctx, struct short_channel_id, scid)); + + node = get_node(rstate, erring_node_pubkey); + if (!node) { + status_trace("UNUSUAL routing_failure: " + "Erring node %s not in map", + type_to_string(tmpctx, struct pubkey, + erring_node_pubkey)); + /* No node, so no channel, so any channel_update + * can also be ignored. */ + goto out; + } + + /* BOLT #4: + * + * - if the NODE bit is set: + * - SHOULD remove all channels connected with the erring node from + * consideration. + * + */ + if (failcode & NODE) { + for (i = 0; i < tal_count(node->in); ++i) + routing_failure_on_nc(rstate, failcode, node->in[i]); + for (i = 0; i < tal_count(node->out); ++i) + routing_failure_on_nc(rstate, failcode, node->out[i]); + } else { + nc = get_out_node_connection_of(rstate, node, scid); + if (nc) + routing_failure_on_nc(rstate, failcode, nc); + else + status_trace("UNUSUAL routing_failure: " + "Channel %s not an out channel " + "of node %s", + type_to_string(tmpctx, + struct short_channel_id, + scid), + type_to_string(tmpctx, struct pubkey, + erring_node_pubkey)); + } + + /* FIXME: if UPDATE is set, apply the channel update. */ +out: + tal_free(tmpctx); +} diff --git a/gossipd/routing.h b/gossipd/routing.h index c3c794961..aa5a74ce4 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #define ROUTING_MAX_HOPS 20 @@ -148,6 +149,11 @@ struct route_hop *get_route(tal_t *ctx, struct routing_state *rstate, const struct pubkey *destination, const u32 msatoshi, double riskfactor, u32 final_cltv); +/* Disable channel(s) based on the given routing failure. */ +void routing_failure(struct routing_state *rstate, + const struct pubkey *erring_node, + const struct short_channel_id *erring_channel, + enum onion_type failcode); /* Utility function that, given a source and a destination, gives us * the direction bit the matching channel should get */ diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c index adaed335a..05b70001f 100644 --- a/gossipd/test/run-bench-find_route.c +++ b/gossipd/test/run-bench-find_route.c @@ -63,6 +63,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_wireaddr */ bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for onion_type_name */ +const char *onion_type_name(int e UNNEEDED) +{ fprintf(stderr, "onion_type_name called!\n"); abort(); } /* Generated stub for queue_broadcast */ bool queue_broadcast(struct broadcast_state *bstate UNNEEDED, const int type UNNEEDED, diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c index 889b2a530..837c843ff 100644 --- a/gossipd/test/run-find_route-specific.c +++ b/gossipd/test/run-find_route-specific.c @@ -34,6 +34,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_wireaddr */ bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for onion_type_name */ +const char *onion_type_name(int e UNNEEDED) +{ fprintf(stderr, "onion_type_name called!\n"); abort(); } /* Generated stub for queue_broadcast */ bool queue_broadcast(struct broadcast_state *bstate UNNEEDED, const int type UNNEEDED, diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c index bcb24d683..095cb89a6 100644 --- a/gossipd/test/run-find_route.c +++ b/gossipd/test/run-find_route.c @@ -27,6 +27,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_wireaddr */ bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for onion_type_name */ +const char *onion_type_name(int e UNNEEDED) +{ fprintf(stderr, "onion_type_name called!\n"); abort(); } /* Generated stub for queue_broadcast */ bool queue_broadcast(struct broadcast_state *bstate UNNEEDED, const int type UNNEEDED, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 992bc9f7c..f5a256f9a 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -110,6 +110,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIP_SEND_GOSSIP: case WIRE_GOSSIP_GET_TXOUT_REPLY: case WIRE_GOSSIP_DISABLE_CHANNEL: + case WIRE_GOSSIP_ROUTING_FAILURE: /* This is a reply, so never gets through to here. */ case WIRE_GOSSIP_GET_UPDATE_REPLY: case WIRE_GOSSIP_GETNODES_REPLY: