From 319c2ec5fc375555b5b3d2c8dc136bba1832f7bf Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 18 Aug 2016 14:25:14 +0930 Subject: [PATCH] peer: keep addresses separately from peers. This makes more sense eventually: we may know the network addresses of many peers, not just those we're connecting to. So keep a mapping, and update it when we successfully connect outwards. Signed-off-by: Rusty Russell --- daemon/db.c | 108 +++++++++++++++++++------------------- daemon/db.h | 3 ++ daemon/lightningd.c | 1 + daemon/lightningd.h | 3 ++ daemon/netaddr.c | 9 ++++ daemon/netaddr.h | 2 + daemon/peer.c | 124 +++++++++++++++++++++++++------------------- daemon/peer.h | 10 ++-- 8 files changed, 153 insertions(+), 107 deletions(-) diff --git a/daemon/db.c b/daemon/db.c index e74634aaf..f5f4fbd3b 100644 --- a/daemon/db.c +++ b/daemon/db.c @@ -203,48 +203,6 @@ void db_add_wallet_privkey(struct lightningd_state *dstate, fatal("db_add_wallet_privkey:%s", err); } -static void load_peer_address(struct peer *peer) -{ - int err; - sqlite3_stmt *stmt; - sqlite3 *sql = peer->dstate->db->sql; - char *ctx = tal(peer, char); - const char *select; - bool addr_set = false; - - select = tal_fmt(ctx, - "SELECT * FROM peer_address WHERE peer = x'%s';", - pubkey_to_hexstr(ctx, peer->dstate->secpctx, peer->id)); - - err = sqlite3_prepare_v2(sql, select, -1, &stmt, NULL); - if (err != SQLITE_OK) - fatal("load_peer_address:prepare gave %s:%s", - sqlite3_errstr(err), sqlite3_errmsg(sql)); - - while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { - if (err != SQLITE_ROW) - fatal("load_peer_address:step gave %s:%s", - sqlite3_errstr(err), sqlite3_errmsg(sql)); - if (addr_set) - fatal("load_peer_address: two addresses for '%s'", - select); - if (!netaddr_from_blob(sqlite3_column_blob(stmt, 1), - sqlite3_column_bytes(stmt, 1), - &peer->addr)) - fatal("load_peer_address: unparsable addresses for '%s'", - select); - addr_set = true; - peer->log = new_log(peer, peer->dstate->log_record, "%s%s:", - log_prefix(peer->dstate->base_log), - netaddr_name(peer, &peer->addr)); - } - - if (!addr_set) - fatal("load_peer_address: no addresses for '%s'", select); - - tal_free(ctx); -} - static void load_peer_secrets(struct peer *peer) { int err; @@ -928,7 +886,6 @@ static void db_load_peers(struct lightningd_state *dstate) sqlite3_errmsg(dstate->db->sql)); list_for_each(&dstate->peers, peer, list) { - load_peer_address(peer); load_peer_secrets(peer); load_peer_closing(peer); peer->anchor.min_depth = 0; @@ -946,10 +903,45 @@ static void db_load_peers(struct lightningd_state *dstate) connect_htlc_src(dstate); } +static void db_load_addresses(struct lightningd_state *dstate) +{ + int err; + sqlite3_stmt *stmt; + sqlite3 *sql = dstate->db->sql; + char *ctx = tal(dstate, char); + const char *select; + + select = tal_fmt(ctx, "SELECT * FROM peer_address;"); + + err = sqlite3_prepare_v2(sql, select, -1, &stmt, NULL); + if (err != SQLITE_OK) + fatal("load_peer_addresses:prepare gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + + while ((err = sqlite3_step(stmt)) != SQLITE_DONE) { + struct peer_address *addr; + + if (err != SQLITE_ROW) + fatal("load_peer_addresses:step gave %s:%s", + sqlite3_errstr(err), sqlite3_errmsg(sql)); + addr = tal(dstate, struct peer_address); + pubkey_from_sql(dstate->secpctx, stmt, 0, &addr->id); + if (!netaddr_from_blob(sqlite3_column_blob(stmt, 1), + sqlite3_column_bytes(stmt, 1), + &addr->addr)) + fatal("load_peer_addresses: unparsable addresses for '%s'", + select); + list_add_tail(&dstate->addresses, &addr->list); + log_debug(dstate->base_log, "load_peer_addresses:%s", + pubkey_to_hexstr(ctx, dstate->secpctx, &addr->id)); + } + tal_free(ctx); +} + static void db_load(struct lightningd_state *dstate) { db_load_wallet(dstate); - + db_load_addresses(dstate); db_load_peers(dstate); } @@ -1152,14 +1144,6 @@ bool db_create_peer(struct peer *peer) if (errmsg) goto out; - errmsg = db_exec(ctx, peer->dstate, - "INSERT INTO peer_address VALUES (x'%s', x'%s');", - peerid, - netaddr_to_hex(ctx, &peer->addr)); - - if (errmsg) - goto out; - if (!db_commit_transaction(peer)) errmsg = "Commit failed"; @@ -1413,6 +1397,26 @@ bool db_add_commit_map(struct peer *peer, return !errmsg; } +/* FIXME: Clean out old ones! */ +bool db_add_peer_address(struct lightningd_state *dstate, + const struct peer_address *addr) +{ + const char *errmsg, *ctx = tal(dstate, char); + + log_debug(dstate->base_log, "%s", __func__); + + assert(!dstate->db->in_transaction); + errmsg = db_exec(ctx, dstate, + "INSERT OR REPLACE INTO peer_address VALUES (x'%s', x'%s');", + pubkey_to_hexstr(ctx, dstate->secpctx, &addr->id), + netaddr_to_hex(ctx, &addr->addr)); + + if (errmsg) + log_broken(dstate->base_log, "%s:%s", __func__, errmsg); + tal_free(ctx); + return !errmsg; +} + void db_forget_peer(struct peer *peer) { const char *ctx = tal(peer, char); diff --git a/daemon/db.h b/daemon/db.h index a4d234d66..89feab598 100644 --- a/daemon/db.h +++ b/daemon/db.h @@ -17,6 +17,9 @@ bool db_commit_transaction(struct peer *peer); void db_add_wallet_privkey(struct lightningd_state *dstate, const struct privkey *privkey); +bool db_add_peer_address(struct lightningd_state *dstate, + const struct peer_address *addr); + /* Must NOT be inside transaction. */ bool db_htlc_fulfilled(struct peer *peer, const struct htlc *htlc); bool db_set_our_closing_script(struct peer *peer); diff --git a/daemon/lightningd.c b/daemon/lightningd.c index 0baff81c2..ab7561df5 100644 --- a/daemon/lightningd.c +++ b/daemon/lightningd.c @@ -251,6 +251,7 @@ static struct lightningd_state *lightningd_state(void) list_head_init(&dstate->bitcoin_req); list_head_init(&dstate->wallet); list_head_init(&dstate->payments); + list_head_init(&dstate->addresses); dstate->dev_never_routefail = false; dstate->bitcoin_req_running = false; dstate->nodes = empty_node_map(dstate); diff --git a/daemon/lightningd.h b/daemon/lightningd.h index aceb8c095..3923ccee0 100644 --- a/daemon/lightningd.h +++ b/daemon/lightningd.h @@ -84,6 +84,9 @@ struct lightningd_state { /* Our peers. */ struct list_head peers; + /* Addresses to contact peers. */ + struct list_head addresses; + /* Crypto tables for global use. */ secp256k1_context *secpctx; diff --git a/daemon/netaddr.c b/daemon/netaddr.c index 2f2db15bc..62c81af75 100644 --- a/daemon/netaddr.c +++ b/daemon/netaddr.c @@ -77,3 +77,12 @@ bool netaddr_from_blob(const void *linear, size_t len, struct netaddr *a) pull(&p, &len, &a->saddr, a->addrlen); return p != NULL && len == 0; } + +bool netaddr_from_fd(int fd, int type, int protocol, struct netaddr *a) +{ + a->type = type; + a->protocol = protocol; + a->addrlen = sizeof(a->saddr); + return getpeername(fd, &a->saddr.s, &a->addrlen) == 0; +} + diff --git a/daemon/netaddr.h b/daemon/netaddr.h index 87becbd64..de55aa08a 100644 --- a/daemon/netaddr.h +++ b/daemon/netaddr.h @@ -26,6 +26,8 @@ char *netaddr_name(const tal_t *ctx, const struct netaddr *a); /* Create a addrinfo (as wanted by io_connect) for this address. */ void netaddr_to_addrinfo(struct addrinfo *ai, const struct netaddr *a); +bool netaddr_from_fd(int fd, int type, int protocol, struct netaddr *a); + bool netaddr_from_blob(const void *linear, size_t len, struct netaddr *a); char *netaddr_to_hex(const tal_t *ctx, const struct netaddr *a); diff --git a/daemon/peer.c b/daemon/peer.c index fa37911b8..305affe2c 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -2101,21 +2101,6 @@ struct commit_info *new_commit_info(const tal_t *ctx, u64 commit_num) return ci; } -static bool peer_getaddr(struct peer *peer, - int fd, int addr_type, int addr_protocol) -{ - peer->addr.type = addr_type; - peer->addr.protocol = addr_protocol; - peer->addr.addrlen = sizeof(peer->addr.saddr); - if (getpeername(fd, &peer->addr.saddr.s, &peer->addr.addrlen) != 0) { - log_broken(peer->dstate->base_log, - "Could not get address for peer: %s", - strerror(errno)); - return false; - } - return true; -} - static bool peer_reconnected(struct peer *peer, struct io_conn *conn, int addr_type, int addr_protocol, @@ -2123,13 +2108,15 @@ static bool peer_reconnected(struct peer *peer, const struct pubkey *id, bool we_connected) { - char *prefix; + char *name; + struct netaddr addr; assert(structeq(peer->id, id)); peer->io_data = tal_steal(peer, iod); + /* FIXME: Attach IO logging for this peer. */ - if (!peer_getaddr(peer, io_conn_fd(conn), addr_type, addr_protocol)) + if (!netaddr_from_fd(io_conn_fd(conn), addr_type, addr_protocol, &addr)) return false; /* If we free peer, conn should be closed, but can't be freed @@ -2137,18 +2124,10 @@ static bool peer_reconnected(struct peer *peer, peer->conn = conn; io_set_finish(conn, peer_disconnect, peer); - prefix = tal_fmt(peer, "%s%s:%s:", - log_prefix(peer->dstate->base_log), - we_connected ? "out" : "in", - netaddr_name(peer, &peer->addr)); - if (peer->log) { - log_info(peer->log, "Reconnected as %s", prefix); - set_log_prefix(peer->log, prefix); - } else { - peer->log = new_log(peer, peer->dstate->log_record, - "%s", prefix); - } - tal_free(prefix); + name = netaddr_name(peer, &addr); + log_info(peer->log, "Reconnected %s %s", + we_connected ? "out to" : "in from", name); + tal_free(name); return true; } @@ -2202,6 +2181,8 @@ struct peer *new_peer(struct lightningd_state *dstate, peer->local.mindepth = dstate->config.anchor_confirms; peer->local.commit = peer->remote.commit = NULL; peer->local.staging_cstate = peer->remote.staging_cstate = NULL; + peer->log = new_log(peer, peer->dstate->log_record, "%s:peer %p:", + log_prefix(peer->dstate->base_log), peer); htlc_map_init(&peer->htlcs); shachain_init(&peer->their_preimages); @@ -2211,6 +2192,34 @@ struct peer *new_peer(struct lightningd_state *dstate, return peer; } +static struct peer_address *find_address(struct lightningd_state *dstate, + const struct pubkey *id) +{ + struct peer_address *i; + + list_for_each(&dstate->addresses, i, list) { + if (structeq(&id->pubkey, &i->id.pubkey)) + return i; + } + return NULL; +} + +static bool add_peer_address(struct lightningd_state *dstate, + const struct pubkey *id, + const struct netaddr *addr) +{ + struct peer_address *a = find_address(dstate, id); + if (a) { + a->addr = *addr; + } else { + a = tal(dstate, struct peer_address); + a->addr = *addr; + a->id = *id; + list_add_tail(&dstate->addresses, &a->list); + } + return db_add_peer_address(dstate, a); +} + static bool peer_first_connected(struct peer *peer, struct io_conn *conn, int addr_type, int addr_protocol, @@ -2218,6 +2227,9 @@ static bool peer_first_connected(struct peer *peer, const struct pubkey *id, bool we_connected) { + char *name; + struct netaddr addr; + peer->io_data = tal_steal(peer, iod); peer->id = tal_dup(peer, struct pubkey, id); /* FIXME: Make this dynamic! */ @@ -2234,28 +2246,25 @@ static bool peer_first_connected(struct peer *peer, peer->conn = conn; io_set_finish(conn, peer_disconnect, peer); - /* FIXME: Attach IO logging for this peer. */ - - peer->addr.type = addr_type; - peer->addr.protocol = addr_protocol; - peer->addr.addrlen = sizeof(peer->addr.saddr); - if (getpeername(io_conn_fd(conn), &peer->addr.saddr.s, - &peer->addr.addrlen) != 0) { - log_unusual(peer->dstate->base_log, - "Could not get address for peer: %s", - strerror(errno)); - return tal_free(peer); - } peer->anchor.min_depth = get_block_height(peer->dstate); - peer->log = new_log(peer, peer->dstate->log_record, "%s%s:%s:", - log_prefix(peer->dstate->base_log), - we_connected ? "out" : "in", - netaddr_name(peer, &peer->addr)); + /* FIXME: Attach IO logging for this peer. */ + if (!netaddr_from_fd(io_conn_fd(conn), addr_type, addr_protocol, &addr)) + return false; + + /* Save/update address if we connected to them. */ + if (we_connected && !add_peer_address(peer->dstate, peer->id, &addr)) + return false; + + name = netaddr_name(peer, &addr); + log_info(peer->log, "Connected %s %s id %s", + we_connected ? "out to" : "in from", name, + pubkey_to_hexstr(name, peer->dstate->secpctx, peer->id)); + tal_free(name); log_debug(peer->log, "Using fee rate %"PRIu64, peer->local.commit_fee_rate); - return peer; + return true; } static u64 peer_commitsigs_received(struct peer *peer) @@ -2372,8 +2381,8 @@ static struct io_plan *crypto_on_reconnect(struct io_conn *conn, u64 sigs, revokes, shutdown, closing; /* Setup peer->conn and peer->io_data */ - if (!peer_reconnected(peer, conn, peer->addr.type, - peer->addr.protocol, iod, id, we_connected)) + if (!peer_reconnected(peer, conn, SOCK_STREAM, IPPROTO_TCP, + iod, id, we_connected)) return io_close(conn); sigs = peer_commitsigs_received(peer); @@ -3849,14 +3858,17 @@ static void reconnect_failed(struct io_conn *conn, struct peer *peer) static struct io_plan *init_conn(struct io_conn *conn, struct peer *peer) { struct addrinfo a; + struct peer_address *addr = find_address(peer->dstate, peer->id); - netaddr_to_addrinfo(&a, &peer->addr); + netaddr_to_addrinfo(&a, &addr->addr); return io_connect(conn, &a, peer_reconnect, peer); } static void try_reconnect(struct peer *peer) { struct io_conn *conn; + struct peer_address *addr; + char *name; int fd; /* Already reconnected? */ @@ -3865,8 +3877,14 @@ static void try_reconnect(struct peer *peer) return; } - fd = socket(peer->addr.saddr.s.sa_family, peer->addr.type, - peer->addr.protocol); + addr = find_address(peer->dstate, peer->id); + if (!addr) { + log_debug(peer->log, "try_reconnect: no known address"); + return; + } + + fd = socket(addr->addr.saddr.s.sa_family, addr->addr.type, + addr->addr.protocol); if (fd < 0) { log_broken(peer->log, "do_reconnect: failed to create socket: %s", strerror(errno)); @@ -3877,7 +3895,9 @@ static void try_reconnect(struct peer *peer) assert(!peer->conn); conn = io_new_conn(peer->dstate, fd, init_conn, peer); - log_debug(peer->log, "Trying to reconnect..."); + name = netaddr_name(peer, &addr->addr); + log_debug(peer->log, "Trying to reconnect to %s", name); + tal_free(name); io_set_finish(conn, reconnect_failed, peer); } diff --git a/daemon/peer.h b/daemon/peer.h index 960868c0b..96d04bfd9 100644 --- a/daemon/peer.h +++ b/daemon/peer.h @@ -103,9 +103,6 @@ struct peer { /* Global state. */ struct lightningd_state *dstate; - /* The other end's address. */ - struct netaddr addr; - /* Their ID. */ struct pubkey *id; @@ -228,6 +225,13 @@ struct peer { struct shachain their_preimages; }; +/* Mapping for id -> network address. */ +struct peer_address { + struct list_node list; + struct pubkey id; + struct netaddr addr; +}; + void setup_listeners(struct lightningd_state *dstate, unsigned int portnum); struct peer *find_peer(struct lightningd_state *dstate, const struct pubkey *id);