Skip to content
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
3 changes: 2 additions & 1 deletion gossipd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
28 changes: 28 additions & 0 deletions gossipd/gossip.c
Original file line number Diff line number Diff line change
Expand Up @@ -1883,6 +1883,31 @@ static struct io_plan *handle_disable_channel(struct io_conn *conn,
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;
u8 *channel_update;

if (!fromwire_gossip_routing_failure(msg,
msg, NULL,
&erring_node,
&erring_channel,
&failcode,
&channel_update))
master_badmsg(WIRE_GOSSIP_ROUTING_FAILURE, msg);

routing_failure(daemon->rstate,
&erring_node,
&erring_channel,
(enum onion_type) failcode,
channel_update);

return daemon_conn_read_next(conn, &daemon->master);
}

static struct io_plan *recv_req(struct io_conn *conn, struct daemon_conn *master)
{
Expand Down Expand Up @@ -1932,6 +1957,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:
Expand Down
9 changes: 9 additions & 0 deletions gossipd/gossip_wire.csv
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <common/cryptomsg.h>
#include <common/wireaddr.h>
#include <wire/gen_onion_wire.h>

# Initialize the gossip daemon.
gossipctl_init,3000
Expand Down Expand Up @@ -195,3 +196,11 @@ 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
gossip_routing_failure,,len,u16
gossip_routing_failure,,channel_update,len*u8
133 changes: 133 additions & 0 deletions gossipd/routing.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <common/type_to_string.h>
#include <common/wireaddr.h>
#include <inttypes.h>
#include <wire/gen_onion_wire.h>
#include <wire/gen_peer_wire.h>

#ifndef SUPERVERBOSE
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -994,3 +1001,129 @@ 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 u8 *channel_update)
{
const tal_t *tmpctx = tal_tmpctx(rstate);
struct node *node;
struct node_connection *nc;
int i;
enum wire_type t;

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));
}

/* Update the channel if UPDATE failcode. Do
* this after deactivating, so that if the
* channel_update is newer it will be
* reactivated. */
if (failcode & UPDATE) {
if (tal_len(channel_update) == 0) {
status_trace("UNUSUAL routing_failure: "
"UPDATE bit set, no channel_update. "
"failcode: 0x%04x",
(int) failcode);
goto out;
}
t = fromwire_peektype(channel_update);
if (t != WIRE_CHANNEL_UPDATE) {
status_trace("UNUSUAL routing_failure: "
"not a channel_update. "
"type: %d",
(int) t);
goto out;
}
handle_channel_update(rstate, channel_update);
} else {
if (tal_len(channel_update) != 0)
status_trace("UNUSUAL routing_failure: "
"UPDATE bit clear, channel_update given. "
"failcode: 0x%04x",
(int) failcode);
}

out:
tal_free(tmpctx);
}
7 changes: 7 additions & 0 deletions gossipd/routing.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <bitcoin/pubkey.h>
#include <ccan/htable/htable_type.h>
#include <gossipd/broadcast.h>
#include <wire/gen_onion_wire.h>
#include <wire/wire.h>

#define ROUTING_MAX_HOPS 20
Expand Down Expand Up @@ -148,6 +149,12 @@ 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,
const u8 *channel_update);

/* Utility function that, given a source and a destination, gives us
* the direction bit the matching channel should get */
Expand Down
6 changes: 6 additions & 0 deletions gossipd/test/run-bench-find_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,18 @@ bool fromwire_channel_update(const void *p UNNEEDED, size_t *plen UNNEEDED, secp
/* Generated stub for fromwire_node_announcement */
bool fromwire_node_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, size_t *plen UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, u8 **features UNNEEDED, u32 *timestamp UNNEEDED, struct pubkey *node_id UNNEEDED, u8 rgb_color[3] UNNEEDED, u8 alias[32] UNNEEDED, u8 **addresses UNNEEDED)
{ fprintf(stderr, "fromwire_node_announcement called!\n"); abort(); }
/* Generated stub for fromwire_peektype */
int fromwire_peektype(const u8 *cursor UNNEEDED)
{ fprintf(stderr, "fromwire_peektype called!\n"); abort(); }
/* Generated stub for fromwire_u8 */
u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); }
/* 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,
Expand Down
6 changes: 6 additions & 0 deletions gossipd/test/run-find_route-specific.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,18 @@ bool fromwire_channel_update(const void *p UNNEEDED, size_t *plen UNNEEDED, secp
/* Generated stub for fromwire_node_announcement */
bool fromwire_node_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, size_t *plen UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, u8 **features UNNEEDED, u32 *timestamp UNNEEDED, struct pubkey *node_id UNNEEDED, u8 rgb_color[3] UNNEEDED, u8 alias[32] UNNEEDED, u8 **addresses UNNEEDED)
{ fprintf(stderr, "fromwire_node_announcement called!\n"); abort(); }
/* Generated stub for fromwire_peektype */
int fromwire_peektype(const u8 *cursor UNNEEDED)
{ fprintf(stderr, "fromwire_peektype called!\n"); abort(); }
/* Generated stub for fromwire_u8 */
u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); }
/* 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,
Expand Down
6 changes: 6 additions & 0 deletions gossipd/test/run-find_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@ bool fromwire_channel_update(const void *p UNNEEDED, size_t *plen UNNEEDED, secp
/* Generated stub for fromwire_node_announcement */
bool fromwire_node_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, size_t *plen UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED, u8 **features UNNEEDED, u32 *timestamp UNNEEDED, struct pubkey *node_id UNNEEDED, u8 rgb_color[3] UNNEEDED, u8 alias[32] UNNEEDED, u8 **addresses UNNEEDED)
{ fprintf(stderr, "fromwire_node_announcement called!\n"); abort(); }
/* Generated stub for fromwire_peektype */
int fromwire_peektype(const u8 *cursor UNNEEDED)
{ fprintf(stderr, "fromwire_peektype called!\n"); abort(); }
/* Generated stub for fromwire_u8 */
u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); }
/* 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,
Expand Down
1 change: 1 addition & 0 deletions lightningd/gossip_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading