Browse Source

gossipd: don't try to connect to non-routable addresses.

Someone could try to announce an internal address, and we might probe
it.

This breaks tests, so we add '--dev-allow-localhost' for our tests, so
we don't eliminate that one.  Of course, now we need to skip some more
tests in non-developer mode.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 7 years ago
committed by Christian Decker
parent
commit
d40d22b68e
  1. 6
      common/test/run-ip_port_parsing.c
  2. 41
      gossipd/gossip.c
  3. 1
      gossipd/gossip_wire.csv
  4. 4
      gossipd/netaddress.c
  5. 3
      gossipd/netaddress.h
  6. 4
      gossipd/routing.c
  7. 6
      gossipd/routing.h
  8. 2
      gossipd/test/run-bench-find_route.c
  9. 2
      gossipd/test/run-find_route-specific.c
  10. 2
      gossipd/test/run-find_route.c
  11. 8
      lightningd/gossip_control.c
  12. 1
      lightningd/lightningd.c
  13. 3
      lightningd/lightningd.h
  14. 3
      lightningd/options.c
  15. 12
      tests/test_lightningd.py
  16. 6
      tests/utils.py

6
common/test/run-ip_port_parsing.c

@ -8,9 +8,6 @@
/* Generated stub for fromwire */ /* Generated stub for fromwire */
const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED)
{ fprintf(stderr, "fromwire called!\n"); abort(); } { fprintf(stderr, "fromwire called!\n"); abort(); }
/* Generated stub for fromwire_bool */
bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_bool called!\n"); abort(); }
/* Generated stub for fromwire_fail */ /* Generated stub for fromwire_fail */
const void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) const void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } { fprintf(stderr, "fromwire_fail called!\n"); abort(); }
@ -26,9 +23,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr
/* Generated stub for towire */ /* Generated stub for towire */
void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED)
{ fprintf(stderr, "towire called!\n"); abort(); } { fprintf(stderr, "towire called!\n"); abort(); }
/* Generated stub for towire_bool */
void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED)
{ fprintf(stderr, "towire_bool called!\n"); abort(); }
/* Generated stub for towire_u16 */ /* Generated stub for towire_u16 */
void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED)
{ fprintf(stderr, "towire_u16 called!\n"); abort(); } { fprintf(stderr, "towire_u16 called!\n"); abort(); }

41
gossipd/gossip.c

@ -1606,14 +1606,14 @@ static bool handle_wireaddr_listen(struct daemon *daemon,
} }
/* If it's a wildcard, turns it into a real address pointing to internet */ /* If it's a wildcard, turns it into a real address pointing to internet */
static bool public_address(struct wireaddr *wireaddr) static bool public_address(struct daemon *daemon, struct wireaddr *wireaddr)
{ {
if (wireaddr_is_wildcard(wireaddr)) { if (wireaddr_is_wildcard(wireaddr)) {
if (!guess_address(wireaddr)) if (!guess_address(wireaddr))
return false; return false;
} }
return address_routable(wireaddr); return address_routable(wireaddr, daemon->rstate->dev_allow_localhost);
} }
static void add_announcable(struct daemon *daemon, const struct wireaddr *addr) static void add_announcable(struct daemon *daemon, const struct wireaddr *addr)
@ -1684,7 +1684,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx,
true); true);
if (ipv6_ok) { if (ipv6_ok) {
add_binding(&binding, &wa); add_binding(&binding, &wa);
if (public_address(&wa.u.wireaddr)) if (public_address(daemon, &wa.u.wireaddr))
add_announcable(daemon, &wa.u.wireaddr); add_announcable(daemon, &wa.u.wireaddr);
} }
wa.u.wireaddr.type = ADDR_TYPE_IPV4; wa.u.wireaddr.type = ADDR_TYPE_IPV4;
@ -1693,7 +1693,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx,
if (handle_wireaddr_listen(daemon, &wa.u.wireaddr, if (handle_wireaddr_listen(daemon, &wa.u.wireaddr,
ipv6_ok)) { ipv6_ok)) {
add_binding(&binding, &wa); add_binding(&binding, &wa);
if (public_address(&wa.u.wireaddr)) if (public_address(daemon, &wa.u.wireaddr))
add_announcable(daemon, &wa.u.wireaddr); add_announcable(daemon, &wa.u.wireaddr);
} }
continue; continue;
@ -1701,7 +1701,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx,
case ADDR_INTERNAL_WIREADDR: case ADDR_INTERNAL_WIREADDR:
handle_wireaddr_listen(daemon, &wa.u.wireaddr, false); handle_wireaddr_listen(daemon, &wa.u.wireaddr, false);
add_binding(&binding, &wa); add_binding(&binding, &wa);
if (public_address(&wa.u.wireaddr)) if (public_address(daemon, &wa.u.wireaddr))
add_announcable(daemon, &wa.u.wireaddr); add_announcable(daemon, &wa.u.wireaddr);
continue; continue;
} }
@ -1723,18 +1723,21 @@ static struct io_plan *gossip_init(struct daemon_conn *master,
{ {
struct bitcoin_blkid chain_hash; struct bitcoin_blkid chain_hash;
u32 update_channel_interval; u32 update_channel_interval;
bool dev_allow_localhost;
if (!fromwire_gossipctl_init( if (!fromwire_gossipctl_init(
daemon, msg, &daemon->broadcast_interval, &chain_hash, daemon, msg, &daemon->broadcast_interval, &chain_hash,
&daemon->id, &daemon->globalfeatures, &daemon->id, &daemon->globalfeatures,
&daemon->localfeatures, &daemon->proposed_wireaddr, &daemon->localfeatures, &daemon->proposed_wireaddr,
&daemon->proposed_listen_announce, daemon->rgb, &daemon->proposed_listen_announce, daemon->rgb,
daemon->alias, &update_channel_interval, &daemon->reconnect)) { daemon->alias, &update_channel_interval, &daemon->reconnect,
&dev_allow_localhost)) {
master_badmsg(WIRE_GOSSIPCTL_INIT, msg); master_badmsg(WIRE_GOSSIPCTL_INIT, msg);
} }
/* Prune time is twice update time */ /* Prune time is twice update time */
daemon->rstate = new_routing_state(daemon, &chain_hash, &daemon->id, daemon->rstate = new_routing_state(daemon, &chain_hash, &daemon->id,
update_channel_interval * 2); update_channel_interval * 2,
dev_allow_localhost);
/* Load stored gossip messages */ /* Load stored gossip messages */
gossip_store_load(daemon->rstate, daemon->rstate->store); gossip_store_load(daemon->rstate, daemon->rstate->store);
@ -1944,7 +1947,6 @@ gossip_resolve_addr(const tal_t *ctx,
const struct pubkey *id) const struct pubkey *id)
{ {
struct node *node; struct node *node;
struct addrhint *a;
/* Get from routing state. */ /* Get from routing state. */
node = get_node(rstate, id); node = get_node(rstate, id);
@ -1952,18 +1954,23 @@ gossip_resolve_addr(const tal_t *ctx,
/* No matching node? */ /* No matching node? */
if (!node) if (!node)
return NULL; return NULL;
/* Node has no addresses? */
if (tal_count(node->addresses) == 0)
return NULL;
/* FIXME: When struct addrhint can contain more than one address, /* FIXME: When struct addrhint can contain more than one address,
* we should copy all addresses. * we should copy all routable addresses. */
* For now getting first address should be fine. */ for (size_t i = 0; i < tal_count(node->addresses); i++) {
a = tal(ctx, struct addrhint); struct addrhint *a;
a->addr.itype = ADDR_INTERNAL_WIREADDR;
a->addr.u.wireaddr = node->addresses[0]; if (!address_routable(&node->addresses[i],
rstate->dev_allow_localhost))
continue;
return a; a = tal(ctx, struct addrhint);
a->addr.itype = ADDR_INTERNAL_WIREADDR;
a->addr.u.wireaddr = node->addresses[i];
return a;
}
return NULL;
} }
static void try_reach_peer(struct daemon *daemon, const struct pubkey *id, static void try_reach_peer(struct daemon *daemon, const struct pubkey *id,

1
gossipd/gossip_wire.csv

@ -18,6 +18,7 @@ gossipctl_init,,rgb,3*u8
gossipctl_init,,alias,32*u8 gossipctl_init,,alias,32*u8
gossipctl_init,,update_channel_interval,u32 gossipctl_init,,update_channel_interval,u32
gossipctl_init,,reconnect,bool gossipctl_init,,reconnect,bool
gossipctl_init,,dev_allow_localhost,bool
# Activate the gossip daemon, so others can connect. # Activate the gossip daemon, so others can connect.
gossipctl_activate,3025 gossipctl_activate,3025

Can't render this file because it has a wrong number of fields in line 6.

4
gossipd/netaddress.c

@ -265,7 +265,9 @@ bool guess_address(struct wireaddr *addr)
abort(); abort();
} }
bool address_routable(const struct wireaddr *wireaddr) bool address_routable(const struct wireaddr *wireaddr, bool allow_localhost)
{ {
if (allow_localhost && IsLocal(wireaddr))
return true;
return IsRoutable(wireaddr); return IsRoutable(wireaddr);
} }

3
gossipd/netaddress.h

@ -8,6 +8,7 @@
bool guess_address(struct wireaddr *wireaddr); bool guess_address(struct wireaddr *wireaddr);
/* Is this address public? */ /* Is this address public? */
bool address_routable(const struct wireaddr *wireaddr); bool address_routable(const struct wireaddr *wireaddr,
bool allow_localhost);
#endif /* LIGHTNING_GOSSIPD_NETADDRESS_H */ #endif /* LIGHTNING_GOSSIPD_NETADDRESS_H */

4
gossipd/routing.c

@ -86,7 +86,8 @@ static struct node_map *empty_node_map(const tal_t *ctx)
struct routing_state *new_routing_state(const tal_t *ctx, struct routing_state *new_routing_state(const tal_t *ctx,
const struct bitcoin_blkid *chain_hash, const struct bitcoin_blkid *chain_hash,
const struct pubkey *local_id, const struct pubkey *local_id,
u32 prune_timeout) u32 prune_timeout,
bool dev_allow_localhost)
{ {
struct routing_state *rstate = tal(ctx, struct routing_state); struct routing_state *rstate = tal(ctx, struct routing_state);
rstate->nodes = empty_node_map(rstate); rstate->nodes = empty_node_map(rstate);
@ -95,6 +96,7 @@ struct routing_state *new_routing_state(const tal_t *ctx,
rstate->local_id = *local_id; rstate->local_id = *local_id;
rstate->prune_timeout = prune_timeout; rstate->prune_timeout = prune_timeout;
rstate->store = gossip_store_new(rstate); rstate->store = gossip_store_new(rstate);
rstate->dev_allow_localhost = dev_allow_localhost;
list_head_init(&rstate->pending_cannouncement); list_head_init(&rstate->pending_cannouncement);
uintmap_init(&rstate->chanmap); uintmap_init(&rstate->chanmap);

6
gossipd/routing.h

@ -162,6 +162,9 @@ struct routing_state {
* restarts */ * restarts */
struct gossip_store *store; struct gossip_store *store;
/* For testing, we announce and accept localhost */
bool dev_allow_localhost;
/* A map of channels indexed by short_channel_ids */ /* A map of channels indexed by short_channel_ids */
UINTMAP(struct chan *) chanmap; UINTMAP(struct chan *) chanmap;
}; };
@ -183,7 +186,8 @@ struct route_hop {
struct routing_state *new_routing_state(const tal_t *ctx, struct routing_state *new_routing_state(const tal_t *ctx,
const struct bitcoin_blkid *chain_hash, const struct bitcoin_blkid *chain_hash,
const struct pubkey *local_id, const struct pubkey *local_id,
u32 prune_timeout); u32 prune_timeout,
bool dev_allow_localhost);
struct chan *new_chan(struct routing_state *rstate, struct chan *new_chan(struct routing_state *rstate,
const struct short_channel_id *scid, const struct short_channel_id *scid,

2
gossipd/test/run-bench-find_route.c

@ -219,7 +219,7 @@ int main(int argc, char *argv[])
| SECP256K1_CONTEXT_SIGN); | SECP256K1_CONTEXT_SIGN);
setup_tmpctx(); setup_tmpctx();
rstate = new_routing_state(tmpctx, &zerohash, &me, 0); rstate = new_routing_state(tmpctx, &zerohash, &me, 0, false);
opt_register_noarg("--perfme", opt_set_bool, &perfme, opt_register_noarg("--perfme", opt_set_bool, &perfme,
"Run perfme-start and perfme-stop around benchmark"); "Run perfme-start and perfme-stop around benchmark");

2
gossipd/test/run-find_route-specific.c

@ -152,7 +152,7 @@ int main(void)
strlen("02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06"), strlen("02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06"),
&c); &c);
rstate = new_routing_state(tmpctx, &zerohash, &a, 0); rstate = new_routing_state(tmpctx, &zerohash, &a, 0, false);
/* [{'active': True, 'short_id': '6990:2:1/1', 'fee_per_kw': 10, 'delay': 5, 'flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'last_update': 1504064344}, */ /* [{'active': True, 'short_id': '6990:2:1/1', 'fee_per_kw': 10, 'delay': 5, 'flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'last_update': 1504064344}, */
nc = get_or_make_connection(rstate, &c, &b, "6990:2:1"); nc = get_or_make_connection(rstate, &c, &b, "6990:2:1");

2
gossipd/test/run-find_route.c

@ -188,7 +188,7 @@ int main(void)
memset(&tmp, 'a', sizeof(tmp)); memset(&tmp, 'a', sizeof(tmp));
pubkey_from_privkey(&tmp, &a); pubkey_from_privkey(&tmp, &a);
rstate = new_routing_state(tmpctx, &zerohash, &a, 0); rstate = new_routing_state(tmpctx, &zerohash, &a, 0, false);
new_node(rstate, &a); new_node(rstate, &a);

8
lightningd/gossip_control.c

@ -185,6 +185,11 @@ void gossip_init(struct lightningd *ld)
u64 capabilities = HSM_CAP_ECDH | HSM_CAP_SIGN_GOSSIP; u64 capabilities = HSM_CAP_ECDH | HSM_CAP_SIGN_GOSSIP;
struct wireaddr_internal *wireaddrs = ld->proposed_wireaddr; struct wireaddr_internal *wireaddrs = ld->proposed_wireaddr;
enum addr_listen_announce *listen_announce = ld->proposed_listen_announce; enum addr_listen_announce *listen_announce = ld->proposed_listen_announce;
bool allow_localhost = false;
#if DEVELOPER
if (ld->dev_allow_localhost)
allow_localhost = true;
#endif
msg = towire_hsm_client_hsmfd(tmpctx, &ld->id, capabilities); msg = towire_hsm_client_hsmfd(tmpctx, &ld->id, capabilities);
if (!wire_sync_write(ld->hsm_fd, msg)) if (!wire_sync_write(ld->hsm_fd, msg))
@ -219,7 +224,8 @@ void gossip_init(struct lightningd *ld)
get_offered_global_features(tmpctx), get_offered_global_features(tmpctx),
get_offered_local_features(tmpctx), wireaddrs, get_offered_local_features(tmpctx), wireaddrs,
listen_announce, ld->rgb, listen_announce, ld->rgb,
ld->alias, ld->config.channel_update_interval, ld->reconnect); ld->alias, ld->config.channel_update_interval, ld->reconnect,
allow_localhost);
subd_send_msg(ld->gossip, msg); subd_send_msg(ld->gossip, msg);
} }

1
lightningd/lightningd.c

@ -51,6 +51,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
ld->dev_debug_subdaemon = NULL; ld->dev_debug_subdaemon = NULL;
ld->dev_disconnect_fd = -1; ld->dev_disconnect_fd = -1;
ld->dev_subdaemon_fail = false; ld->dev_subdaemon_fail = false;
ld->dev_allow_localhost = false;
if (getenv("LIGHTNINGD_DEV_MEMLEAK")) if (getenv("LIGHTNINGD_DEV_MEMLEAK"))
memleak_init(ld, backtrace_state); memleak_init(ld, backtrace_state);

3
lightningd/lightningd.h

@ -189,6 +189,9 @@ struct lightningd {
/* If we have --dev-fail-on-subdaemon-fail */ /* If we have --dev-fail-on-subdaemon-fail */
bool dev_subdaemon_fail; bool dev_subdaemon_fail;
/* Allow and accept localhost node_announcement addresses */
bool dev_allow_localhost;
/* Things we've marked as not leaking. */ /* Things we've marked as not leaking. */
const void **notleaks; const void **notleaks;
#endif /* DEVELOPER */ #endif /* DEVELOPER */

3
lightningd/options.c

@ -415,6 +415,9 @@ static void dev_register_opts(struct lightningd *ld)
"Time between gossip broadcasts in milliseconds"); "Time between gossip broadcasts in milliseconds");
opt_register_arg("--dev-disconnect=<filename>", opt_subd_dev_disconnect, opt_register_arg("--dev-disconnect=<filename>", opt_subd_dev_disconnect,
NULL, ld, "File containing disconnection points"); NULL, ld, "File containing disconnection points");
opt_register_noarg("--dev-allow-localhost", opt_set_bool,
&ld->dev_allow_localhost,
"Announce and allow announcments for localhost address");
} }
#endif #endif

12
tests/test_lightningd.py

@ -664,6 +664,7 @@ class LightningDTests(BaseLightningDTests):
"Cryptographic handshake: ", "Cryptographic handshake: ",
l1.rpc.connect, '032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e', 'localhost', l2.port) l1.rpc.connect, '032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e', 'localhost', l2.port)
@unittest.skipIf(not DEVELOPER, "needs --dev-allow-localhost")
def test_connect_by_gossip(self): def test_connect_by_gossip(self):
"""Test connecting to an unknown peer using node gossip """Test connecting to an unknown peer using node gossip
""" """
@ -4284,10 +4285,13 @@ class LightningDTests(BaseLightningDTests):
def test_address(self): def test_address(self):
l1 = self.node_factory.get_node() l1 = self.node_factory.get_node()
addr = l1.rpc.getinfo()['address'] addr = l1.rpc.getinfo()['address']
assert len(addr) == 1 if 'dev-allow-localhost' in l1.daemon.opts:
assert addr[0]['type'] == 'ipv4' assert len(addr) == 1
assert addr[0]['address'] == '127.0.0.1' assert addr[0]['type'] == 'ipv4'
assert int(addr[0]['port']) == l1.port assert addr[0]['address'] == '127.0.0.1'
assert int(addr[0]['port']) == l1.port
else:
assert len(addr) == 0
bind = l1.rpc.getinfo()['binding'] bind = l1.rpc.getinfo()['binding']
assert len(bind) == 1 assert len(bind) == 1

6
tests/utils.py

@ -261,9 +261,7 @@ class LightningD(TailableProc):
opts = { opts = {
'bitcoin-datadir': bitcoin_dir, 'bitcoin-datadir': bitcoin_dir,
'lightning-dir': lightning_dir, 'lightning-dir': lightning_dir,
'bind-addr': '127.0.0.1:{}'.format(port), 'addr': '127.0.0.1:{}'.format(port),
# lightningd won't announce non-routable addresses by default.
'announce-addr': '127.0.0.1:{}'.format(port),
'allow-deprecated-apis': 'false', 'allow-deprecated-apis': 'false',
'override-fee-rates': '15000/7500/1000', 'override-fee-rates': '15000/7500/1000',
'network': 'regtest', 'network': 'regtest',
@ -283,6 +281,8 @@ class LightningD(TailableProc):
f.write(seed) f.write(seed)
if DEVELOPER: if DEVELOPER:
self.opts['dev-broadcast-interval'] = 1000 self.opts['dev-broadcast-interval'] = 1000
# lightningd won't announce non-routable addresses by default.
self.opts['dev-allow-localhost'] = None
self.prefix = 'lightningd-%d' % (node_id) self.prefix = 'lightningd-%d' % (node_id)
@property @property

Loading…
Cancel
Save