From d5be8d26f2f6accb1b93d4ffa628960e801ecfac Mon Sep 17 00:00:00 2001
From: Rusty Russell <rusty@rustcorp.com.au>
Date: Wed, 12 Apr 2017 09:10:10 -0700
Subject: [PATCH] lightningd/ping: ping support.

A spec update brings ping support.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 lightningd/Makefile          |  1 +
 lightningd/channel/channel.c | 21 ++++++++++++++
 lightningd/gossip/gossip.c   | 30 ++++++++++++++++++++
 lightningd/ping.c            | 53 ++++++++++++++++++++++++++++++++++++
 lightningd/ping.h            | 13 +++++++++
 wire/gen_peer_wire_csv       |  7 +++++
 wire/peer_wire.c             |  2 ++
 7 files changed, 127 insertions(+)
 create mode 100644 lightningd/ping.c
 create mode 100644 lightningd/ping.h

diff --git a/lightningd/Makefile b/lightningd/Makefile
index 2485ed108..dbc60da5e 100644
--- a/lightningd/Makefile
+++ b/lightningd/Makefile
@@ -51,6 +51,7 @@ LIGHTNINGD_LIB_SRC :=				\
 	lightningd/key_derive.c			\
 	lightningd/msg_queue.c			\
 	lightningd/peer_failed.c		\
+	lightningd/ping.c			\
 	lightningd/status.c			\
 	lightningd/utxo.c
 
diff --git a/lightningd/channel/channel.c b/lightningd/channel/channel.c
index 5365d99f6..5db314667 100644
--- a/lightningd/channel/channel.c
+++ b/lightningd/channel/channel.c
@@ -28,6 +28,7 @@
 #include <lightningd/key_derive.h>
 #include <lightningd/msg_queue.h>
 #include <lightningd/peer_failed.h>
+#include <lightningd/ping.h>
 #include <lightningd/status.h>
 #include <secp256k1.h>
 #include <signal.h>
@@ -819,6 +820,20 @@ static void handle_peer_fail_htlc(struct peer *peer, const u8 *msg)
 	abort();
 }
 
+static void handle_ping(struct peer *peer, const u8 *msg)
+{
+	u8 *pong;
+
+	if (!check_ping_make_pong(peer, msg, &pong))
+		peer_failed(io_conn_fd(peer->peer_conn),
+			    &peer->pcs.cs,
+			    &peer->channel_id,
+			    WIRE_CHANNEL_PEER_BAD_MESSAGE,
+			    "Bad ping");
+	if (pong)
+		msg_enqueue(&peer->peer_out, take(pong));
+}
+
 static struct io_plan *peer_in(struct io_conn *conn, struct peer *peer, u8 *msg)
 {
 	enum wire_type type = fromwire_peektype(msg);
@@ -868,6 +883,12 @@ static struct io_plan *peer_in(struct io_conn *conn, struct peer *peer, u8 *msg)
 	case WIRE_UPDATE_FAIL_HTLC:
 		handle_peer_fail_htlc(peer, msg);
 		goto done;
+	case WIRE_PING:
+		handle_ping(peer, msg);
+		goto done;
+
+	/* We don't send pings, so don't expect pongs. */
+	case WIRE_PONG:
 	case WIRE_INIT:
 	case WIRE_ERROR:
 	case WIRE_OPEN_CHANNEL:
diff --git a/lightningd/gossip/gossip.c b/lightningd/gossip/gossip.c
index 35168c68e..43d63985d 100644
--- a/lightningd/gossip/gossip.c
+++ b/lightningd/gossip/gossip.c
@@ -21,6 +21,7 @@
 #include <lightningd/debug.h>
 #include <lightningd/gossip/gen_gossip_wire.h>
 #include <lightningd/gossip_msg.h>
+#include <lightningd/ping.h>
 #include <lightningd/status.h>
 #include <secp256k1_ecdh.h>
 #include <sodium/randombytes.h>
@@ -168,6 +169,20 @@ static void handle_gossip_msg(struct routing_state *rstate, u8 *msg)
 	}
 }
 
+static bool handle_ping(struct peer *peer, u8 *ping)
+{
+	u8 *pong;
+
+	if (!check_ping_make_pong(peer, ping, &pong)) {
+		peer->error = "Bad ping";
+		return false;
+	}
+
+	if (pong)
+		msg_enqueue(&peer->peer_out, take(pong));
+	return true;
+}
+
 static struct io_plan *peer_msgin(struct io_conn *conn,
 				  struct peer *peer, u8 *msg)
 {
@@ -190,6 +205,21 @@ static struct io_plan *peer_msgin(struct io_conn *conn,
 		peer->error = "Duplicate INIT message received";
 		return io_close(conn);
 
+	case WIRE_PING:
+		if (!handle_ping(peer, msg))
+			return io_close(conn);
+		return peer_read_message(conn, &peer->pcs, peer_msgin);
+
+	case WIRE_PONG:
+		/* BOLT #1:
+		 *
+		 * A node receiving a `pong` message MAY fail the channels if
+		 * `byteslen` does not correspond to any `ping`
+		 * `num_pong_bytes` value it has sent.
+		 */
+		peer->error = "Unexpected pong received";
+		return io_close(conn);
+
 	case WIRE_OPEN_CHANNEL:
 	case WIRE_ACCEPT_CHANNEL:
 	case WIRE_FUNDING_CREATED:
diff --git a/lightningd/ping.c b/lightningd/ping.c
new file mode 100644
index 000000000..9a9e9ca8c
--- /dev/null
+++ b/lightningd/ping.c
@@ -0,0 +1,53 @@
+#include <lightningd/ping.h>
+#include <wire/gen_peer_wire.h>
+
+bool check_ping_make_pong(const tal_t *ctx, const u8 *ping, u8 **pong)
+{
+	u16 num_pong_bytes;
+	u8 *ignored;
+
+	if (!fromwire_ping(ctx, ping, NULL, &num_pong_bytes, &ignored))
+		return false;
+	tal_free(ignored);
+
+	/* FIXME: */
+	/* BOLT #1:
+	 *
+	 * A node receiving a `ping` message SHOULD fail the channels if it
+	 * has received significantly in excess of one `ping` per 30 seconds,
+	 */
+
+	/* BOLT #1:
+	 *
+	 * ... otherwise if `num_pong_bytes` is less than 65532 it MUST
+	 * respond by sending a `pong` message with `byteslen` equal to
+	 * `num_pong_bytes`, otherwise it MUST ignore the `ping`.
+	 */
+	if (num_pong_bytes < 65532) {
+		/* BOLT #1:
+		 *
+		 * A node sending `pong` or `ping` SHOULD set `ignored` to
+		 * zeroes, but MUST NOT set `ignored` to sensitive data such
+		 * as secrets, or portions of initialized memory.
+		*/
+		ignored = tal_arrz(ctx, u8, num_pong_bytes);
+		*pong = towire_pong(ctx, ignored);
+		tal_free(ignored);
+	}
+	return true;
+}
+
+u8 *make_ping(const tal_t *ctx, u16 num_pong_bytes, u16 padlen)
+{
+	/* BOLT #1:
+	 *
+	 * A node sending `pong` or `ping` SHOULD set `ignored` to zeroes, but
+	 * MUST NOT set `ignored` to sensitive data such as secrets, or
+	 * portions of initialized memory.
+	 */
+	u8 *ping, *ignored = tal_arrz(ctx, u8, padlen);
+
+	ping = towire_ping(ctx, num_pong_bytes, ignored);
+	tal_free(ignored);
+	return ping;
+}
diff --git a/lightningd/ping.h b/lightningd/ping.h
new file mode 100644
index 000000000..92683493f
--- /dev/null
+++ b/lightningd/ping.h
@@ -0,0 +1,13 @@
+#ifndef LIGHTNING_LIGHTNINGD_PING_H
+#define LIGHTNING_LIGHTNINGD_PING_H
+#include "config.h"
+#include <ccan/short_types/short_types.h>
+#include <ccan/tal/tal.h>
+
+/* Returns false on error, otherwise *pong set if reply needed. */
+bool check_ping_make_pong(const tal_t *ctx, const u8 *ping, u8 **pong);
+
+/* Make a ping packet requesting num_pong_bytes */
+u8 *make_ping(const tal_t *ctx, u16 num_pong_bytes, u16 padlen);
+
+#endif /* LIGHTNING_LIGHTNINGD_PING_H */
diff --git a/wire/gen_peer_wire_csv b/wire/gen_peer_wire_csv
index 308fb526c..e31d8e545 100644
--- a/wire/gen_peer_wire_csv
+++ b/wire/gen_peer_wire_csv
@@ -7,6 +7,13 @@ error,17
 error,0,channel-id,32
 error,32,len,2
 error,34,data,len
+ping,18
+ping,0,num_pong_bytes,2
+ping,2,byteslen,2
+ping,4,ignored,byteslen
+pong,19
+pong,0,byteslen,2
+pong,2,ignored,byteslen
 open_channel,32
 open_channel,0,chain-hash,32
 open_channel,32,temporary-channel-id,32
diff --git a/wire/peer_wire.c b/wire/peer_wire.c
index 17a37e6cb..976fb8c3e 100644
--- a/wire/peer_wire.c
+++ b/wire/peer_wire.c
@@ -23,6 +23,8 @@ static bool unknown_type(enum wire_type t)
 	case WIRE_CHANNEL_ANNOUNCEMENT:
 	case WIRE_NODE_ANNOUNCEMENT:
 	case WIRE_CHANNEL_UPDATE:
+	case WIRE_PING:
+	case WIRE_PONG:
 		return false;
 	}
 	return true;