nhrpd: implement next hop resolution protocol

This provides DMVPN support and integrates to strongSwan. Please read
README.nhrpd and README.kernel for more details.
diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c
new file mode 100644
index 0000000..9b1c69d
--- /dev/null
+++ b/nhrpd/nhrp_vty.c
@@ -0,0 +1,928 @@
+/* NHRP vty handling
+ * Copyright (c) 2014-2015 Timo Teräs
+ *
+ * This file is free software: you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "zebra.h"
+#include "command.h"
+#include "zclient.h"
+#include "stream.h"
+
+#include "nhrpd.h"
+#include "netlink.h"
+
+static struct cmd_node zebra_node = {
+	.node   = ZEBRA_NODE,
+	.prompt = "%s(config-router)# ",
+	.vtysh  = 1,
+};
+
+static struct cmd_node nhrp_interface_node = {
+	.node   = INTERFACE_NODE,
+	.prompt = "%s(config-if)# ",
+	.vtysh  = 1,
+};
+
+#define NHRP_DEBUG_FLAGS_CMD "(all|common|event|interface|kernel|route|vici)"
+
+#define NHRP_DEBUG_FLAGS_STR		\
+	"All messages\n"		\
+	"Common messages (default)\n"	\
+	"Event manager messages\n"	\
+	"Interface messages\n"		\
+	"Kernel messages\n"		\
+	"Route messages\n"		\
+	"VICI messages\n"
+
+static const struct message debug_flags_desc[] = {
+	{ NHRP_DEBUG_ALL, "all" },
+	{ NHRP_DEBUG_COMMON, "common" },
+	{ NHRP_DEBUG_IF, "interface" },
+	{ NHRP_DEBUG_KERNEL, "kernel" },
+	{ NHRP_DEBUG_ROUTE, "route" },
+	{ NHRP_DEBUG_VICI, "vici" },
+	{ NHRP_DEBUG_EVENT, "event" },
+	{ 0, NULL },
+};
+
+static const struct message interface_flags_desc[] = {
+	{ NHRP_IFF_SHORTCUT, "shortcut" },
+	{ NHRP_IFF_REDIRECT, "redirect" },
+	{ NHRP_IFF_REG_NO_UNIQUE, "registration no-unique" },
+	{ 0, NULL },
+};
+
+static int nhrp_vty_return(struct vty *vty, int ret)
+{
+	static const char * const errmsgs[] = {
+		[NHRP_ERR_FAIL]				= "Command failed",
+		[NHRP_ERR_NO_MEMORY]			= "Out of memory",
+		[NHRP_ERR_UNSUPPORTED_INTERFACE]	= "NHRP not supported on this interface",
+		[NHRP_ERR_NHRP_NOT_ENABLED]		= "NHRP not enabled (set 'nhrp network-id' first)",
+		[NHRP_ERR_ENTRY_EXISTS]			= "Entry exists already",
+		[NHRP_ERR_ENTRY_NOT_FOUND]		= "Entry not found",
+		[NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH]	= "Protocol address family does not match command (ip/ipv6 mismatch)",
+	};
+	const char *str = NULL;
+	char buf[256];
+
+	if (ret == NHRP_OK)
+		return CMD_SUCCESS;
+
+	if (ret > 0 && ret <= (int)ZEBRA_NUM_OF(errmsgs))
+		if (errmsgs[ret])
+			str = errmsgs[ret];
+
+	if (!str) {
+		str = buf;
+		snprintf(buf, sizeof(buf), "Unknown error %d", ret);
+	}
+
+	vty_out (vty, "%% %s%s", str, VTY_NEWLINE);
+
+	return CMD_WARNING;
+}
+
+static int toggle_flag(
+	struct vty *vty, const struct message *flag_desc,
+	const char *name, int on_off, unsigned *flags)
+{
+	int i;
+
+	for (i = 0; flag_desc[i].str != NULL; i++) {
+		if (strcmp(flag_desc[i].str, name) != 0)
+			continue;
+		if (on_off)
+			*flags |= flag_desc[i].key;
+		else
+			*flags &= ~flag_desc[i].key;
+		return CMD_SUCCESS;
+	}
+
+	vty_out(vty, "%% Invalid value %s%s", name, VTY_NEWLINE);
+	return CMD_WARNING;
+}
+
+#ifndef NO_DEBUG
+
+DEFUN(show_debugging_nhrp, show_debugging_nhrp_cmd,
+	"show debugging nhrp",
+	SHOW_STR
+	"Debugging information\n"
+	"NHRP configuration\n")
+{
+	int i;
+
+	vty_out(vty, "NHRP debugging status:%s", VTY_NEWLINE);
+
+	for (i = 0; debug_flags_desc[i].str != NULL; i++) {
+		if (debug_flags_desc[i].key == NHRP_DEBUG_ALL)
+			continue;
+		if (!(debug_flags_desc[i].key & debug_flags))
+			continue;
+
+		vty_out(vty, "  NHRP %s debugging is on%s",
+			debug_flags_desc[i].str, VTY_NEWLINE);
+	}
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(debug_nhrp, debug_nhrp_cmd,
+	"debug nhrp " NHRP_DEBUG_FLAGS_CMD,
+	"Enable debug messages for specific or all parts.\n"
+	"NHRP information\n"
+	NHRP_DEBUG_FLAGS_STR)
+{
+	return toggle_flag(vty, debug_flags_desc, argv[0], 1, &debug_flags);
+}
+
+DEFUN(no_debug_nhrp, no_debug_nhrp_cmd,
+	"no debug nhrp " NHRP_DEBUG_FLAGS_CMD,
+	NO_STR
+	"Disable debug messages for specific or all parts.\n"
+	"NHRP information\n"
+	NHRP_DEBUG_FLAGS_STR)
+{
+	return toggle_flag(vty, debug_flags_desc, argv[0], 0, &debug_flags);
+}
+
+#endif /* NO_DEBUG */
+
+static int nhrp_config_write(struct vty *vty)
+{
+#ifndef NO_DEBUG
+	if (debug_flags == NHRP_DEBUG_ALL) {
+		vty_out(vty, "debug nhrp all%s", VTY_NEWLINE);
+	} else {
+		int i;
+
+		for (i = 0; debug_flags_desc[i].str != NULL; i++) {
+			if (debug_flags_desc[i].key == NHRP_DEBUG_ALL)
+				continue;
+			if (!(debug_flags & debug_flags_desc[i].key))
+				continue;
+			vty_out(vty, "debug nhrp %s%s", debug_flags_desc[i].str, VTY_NEWLINE);
+		}
+	}
+	vty_out(vty, "!%s", VTY_NEWLINE);
+#endif /* NO_DEBUG */
+
+	if (nhrp_event_socket_path) {
+		vty_out(vty, "nhrp event socket %s%s",
+			nhrp_event_socket_path, VTY_NEWLINE);
+	}
+	if (netlink_nflog_group) {
+		vty_out(vty, "nhrp nflog-group %d%s",
+			netlink_nflog_group, VTY_NEWLINE);
+	}
+
+	return 0;
+}
+
+#define IP_STR		"IP information\n"
+#define IPV6_STR	"IPv6 information\n"
+#define AFI_CMD		"(ip|ipv6)"
+#define AFI_STR		IP_STR IPV6_STR
+#define NHRP_STR	"Next Hop Resolution Protocol functions\n"
+
+static afi_t cmd_to_afi(const char *cmd)
+{
+	return strncmp(cmd, "ipv6", 4) == 0 ? AFI_IP6 : AFI_IP;
+}
+
+static const char *afi_to_cmd(afi_t afi)
+{
+	if (afi == AFI_IP6) return "ipv6";
+	return "ip";
+}
+
+DEFUN(nhrp_event_socket, nhrp_event_socket_cmd,
+	"nhrp event socket SOCKET",
+	NHRP_STR
+	"Event Manager commands\n"
+	"Event Manager unix socket path\n"
+	"Unix path for the socket")
+{
+	evmgr_set_socket(argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(no_nhrp_event_socket, no_nhrp_event_socket_cmd,
+	"no nhrp event socket [SOCKET]",
+	NO_STR
+	NHRP_STR
+	"Event Manager commands\n"
+	"Event Manager unix socket path\n"
+	"Unix path for the socket")
+{
+	evmgr_set_socket(NULL);
+	return CMD_SUCCESS;
+}
+
+DEFUN(nhrp_nflog_group, nhrp_nflog_group_cmd,
+	"nhrp nflog-group <1-65535>",
+	NHRP_STR
+	"Specify NFLOG group number\n"
+	"NFLOG group number\n")
+{
+	uint32_t nfgroup;
+
+	VTY_GET_INTEGER_RANGE("nflog-group", nfgroup, argv[0], 1, 65535);
+	netlink_set_nflog_group(nfgroup);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(no_nhrp_nflog_group, no_nhrp_nflog_group_cmd,
+	"no nhrp nflog-group [<1-65535>]",
+	NO_STR
+	NHRP_STR
+	"Specify NFLOG group number\n"
+	"NFLOG group number\n")
+{
+	netlink_set_nflog_group(0);
+	return CMD_SUCCESS;
+}
+
+DEFUN(tunnel_protection, tunnel_protection_cmd,
+	"tunnel protection vici profile PROFILE {fallback-profile FALLBACK}",
+	"NHRP/GRE integration\n"
+	"IPsec protection\n"
+	"VICI (StrongSwan)\n"
+	"IPsec profile\n"
+	"IPsec profile name\n"
+	"Fallback IPsec profile\n"
+	"Fallback IPsec profile name\n")
+{
+	struct interface *ifp = vty->index;
+
+	nhrp_interface_set_protection(ifp, argv[0], argv[1]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(no_tunnel_protection, no_tunnel_protection_cmd,
+	"no tunnel protection",
+	NO_STR
+	"NHRP/GRE integration\n"
+	"IPsec protection\n")
+{
+	struct interface *ifp = vty->index;
+
+	nhrp_interface_set_protection(ifp, NULL, NULL);
+	return CMD_SUCCESS;
+}
+
+DEFUN(tunnel_source, tunnel_source_cmd,
+	"tunnel source INTERFACE",
+	"NHRP/GRE integration\n"
+	"Tunnel device binding tracking\n"
+	"Interface name\n")
+{
+	struct interface *ifp = vty->index;
+	nhrp_interface_set_source(ifp, argv[0]);
+	return CMD_SUCCESS;
+}
+
+DEFUN(no_tunnel_source, no_tunnel_source_cmd,
+	"no tunnel source",
+	"NHRP/GRE integration\n"
+	"Tunnel device binding tracking\n"
+	"Interface name\n")
+{
+	struct interface *ifp = vty->index;
+	nhrp_interface_set_source(ifp, NULL);
+	return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_network_id, if_nhrp_network_id_cmd,
+	AFI_CMD " nhrp network-id <1-4294967295>",
+	AFI_STR
+	NHRP_STR
+	"Enable NHRP and specify network-id\n"
+	"System local ID to specify interface group\n")
+{
+	struct interface *ifp = vty->index;
+	struct nhrp_interface *nifp = ifp->info;
+	afi_t afi = cmd_to_afi(argv[0]);
+
+	VTY_GET_INTEGER_RANGE("network-id", nifp->afi[afi].network_id, argv[1], 1, 4294967295);
+	nhrp_interface_update(ifp);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(if_no_nhrp_network_id, if_no_nhrp_network_id_cmd,
+	"no " AFI_CMD " nhrp network-id [<1-4294967295>]",
+	NO_STR
+	AFI_STR
+	NHRP_STR
+	"Enable NHRP and specify network-id\n"
+	"System local ID to specify interface group\n")
+{
+	struct interface *ifp = vty->index;
+	struct nhrp_interface *nifp = ifp->info;
+	afi_t afi = cmd_to_afi(argv[0]);
+
+	nifp->afi[afi].network_id = 0;
+	nhrp_interface_update(ifp);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_flags, if_nhrp_flags_cmd,
+	AFI_CMD " nhrp (shortcut|redirect)",
+	AFI_STR
+	NHRP_STR
+	"Allow shortcut establishment\n"
+	"Send redirect notifications\n")
+{
+	struct interface *ifp = vty->index;
+	struct nhrp_interface *nifp = ifp->info;
+	afi_t afi = cmd_to_afi(argv[0]);
+
+	return toggle_flag(vty, interface_flags_desc, argv[1], 1, &nifp->afi[afi].flags);
+}
+
+DEFUN(if_no_nhrp_flags, if_no_nhrp_flags_cmd,
+	"no " AFI_CMD " nhrp (shortcut|redirect)",
+	NO_STR
+	AFI_STR
+	NHRP_STR
+	"Allow shortcut establishment\n"
+	"Send redirect notifications\n")
+{
+	struct interface *ifp = vty->index;
+	struct nhrp_interface *nifp = ifp->info;
+	afi_t afi = cmd_to_afi(argv[0]);
+
+	return toggle_flag(vty, interface_flags_desc, argv[1], 0, &nifp->afi[afi].flags);
+}
+
+DEFUN(if_nhrp_reg_flags, if_nhrp_reg_flags_cmd,
+	AFI_CMD " nhrp registration (no-unique)",
+	AFI_STR
+	NHRP_STR
+	"Registration configuration\n"
+	"Don't set unique flag\n")
+{
+	struct interface *ifp = vty->index;
+	struct nhrp_interface *nifp = ifp->info;
+	afi_t afi = cmd_to_afi(argv[0]);
+	char name[256];
+	snprintf(name, sizeof(name), "registration %s", argv[1]);
+	return toggle_flag(vty, interface_flags_desc, name, 1, &nifp->afi[afi].flags);
+}
+
+DEFUN(if_no_nhrp_reg_flags, if_no_nhrp_reg_flags_cmd,
+	"no " AFI_CMD " nhrp registration (no-unique)",
+	NO_STR
+	AFI_STR
+	NHRP_STR
+	"Registration configuration\n"
+	"Don't set unique flag\n")
+{
+	struct interface *ifp = vty->index;
+	struct nhrp_interface *nifp = ifp->info;
+	afi_t afi = cmd_to_afi(argv[0]);
+	char name[256];
+	snprintf(name, sizeof(name), "registration %s", argv[1]);
+	return toggle_flag(vty, interface_flags_desc, name, 0, &nifp->afi[afi].flags);
+}
+
+DEFUN(if_nhrp_holdtime, if_nhrp_holdtime_cmd,
+	AFI_CMD " nhrp holdtime <1-65000>",
+	AFI_STR
+	NHRP_STR
+	"Specify NBMA address validity time\n"
+	"Time in seconds that NBMA addresses are advertised valid\n")
+{
+	struct interface *ifp = vty->index;
+	struct nhrp_interface *nifp = ifp->info;
+	afi_t afi = cmd_to_afi(argv[0]);
+
+	VTY_GET_INTEGER_RANGE("holdtime", nifp->afi[afi].holdtime, argv[1], 1, 65000);
+	nhrp_interface_update(ifp);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(if_no_nhrp_holdtime, if_no_nhrp_holdtime_cmd,
+	"no " AFI_CMD " nhrp holdtime [1-65000]",
+	NO_STR
+	AFI_STR
+	NHRP_STR
+	"Specify NBMA address validity time\n"
+	"Time in seconds that NBMA addresses are advertised valid\n")
+{
+	struct interface *ifp = vty->index;
+	struct nhrp_interface *nifp = ifp->info;
+	afi_t afi = cmd_to_afi(argv[0]);
+
+	nifp->afi[afi].holdtime = NHRPD_DEFAULT_HOLDTIME;
+	nhrp_interface_update(ifp);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_mtu, if_nhrp_mtu_cmd,
+	"ip nhrp mtu (<576-1500>|opennhrp)",
+	IP_STR
+	NHRP_STR
+	"Configure NHRP advertised MTU\n"
+	"MTU value\n"
+	"Advertise bound interface MTU similar to OpenNHRP")
+{
+	struct interface *ifp = vty->index;
+	struct nhrp_interface *nifp = ifp->info;
+
+	if (argv[0][0] == 'o') {
+		nifp->afi[AFI_IP].configured_mtu = -1;
+	} else {
+		VTY_GET_INTEGER_RANGE("mtu", nifp->afi[AFI_IP].configured_mtu, argv[0], 576, 1500);
+	}
+	nhrp_interface_update_mtu(ifp, AFI_IP);
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(if_no_nhrp_mtu, if_no_nhrp_mtu_cmd,
+	"no ip nhrp mtu [(<576-1500>|opennhrp)]",
+	NO_STR
+	IP_STR
+	NHRP_STR
+	"Configure NHRP advertised MTU\n"
+	"MTU value\n"
+	"Advertise bound interface MTU similar to OpenNHRP")
+{
+	struct interface *ifp = vty->index;
+	struct nhrp_interface *nifp = ifp->info;
+
+	nifp->afi[AFI_IP].configured_mtu = 0;
+	nhrp_interface_update_mtu(ifp, AFI_IP);
+	return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_map, if_nhrp_map_cmd,
+	AFI_CMD " nhrp map (A.B.C.D|X:X::X:X) (A.B.C.D|local)",
+	AFI_STR
+	NHRP_STR
+	"Nexthop Server configuration\n"
+	"IPv4 protocol address\n"
+	"IPv6 protocol address\n"
+	"IPv4 NBMA address\n"
+	"Handle protocol address locally\n")
+{
+	struct interface *ifp = vty->index;
+	afi_t afi = cmd_to_afi(argv[0]);
+	union sockunion proto_addr, nbma_addr;
+	struct nhrp_cache *c;
+
+	if (str2sockunion(argv[1], &proto_addr) < 0 ||
+	    afi2family(afi) != sockunion_family(&proto_addr))
+		return nhrp_vty_return(vty, NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH);
+
+	c = nhrp_cache_get(ifp, &proto_addr, 1);
+	if (!c)
+		return nhrp_vty_return(vty, NHRP_ERR_FAIL);
+
+	c->map = 1;
+	if (strcmp(argv[2], "local") == 0) {
+		nhrp_cache_update_binding(c, NHRP_CACHE_LOCAL, 0, NULL, 0, NULL);
+	} else{
+		if (str2sockunion(argv[2], &nbma_addr) < 0)
+			return nhrp_vty_return(vty, NHRP_ERR_FAIL);
+		nhrp_cache_update_binding(c, NHRP_CACHE_STATIC, 0,
+			nhrp_peer_get(ifp, &nbma_addr), 0, NULL);
+	}
+
+	return CMD_SUCCESS;
+}
+
+DEFUN(if_nhrp_nhs, if_nhrp_nhs_cmd,
+	AFI_CMD " nhrp nhs (A.B.C.D|X:X::X:X|dynamic) nbma (A.B.C.D|FQDN)",
+	AFI_STR
+	NHRP_STR
+	"Nexthop Server configuration\n"
+	"IPv4 protocol address\n"
+	"IPv6 protocol address\n"
+	"Automatic detection of protocol address\n"
+	"IPv4 NBMA address\n"
+	"Fully qualified domain name for NBMA address(es)\n")
+{
+	struct interface *ifp = vty->index;
+	afi_t afi = cmd_to_afi(argv[0]);
+	union sockunion proto_addr;
+	int ret;
+
+	if (str2sockunion(argv[1], &proto_addr) < 0)
+		sockunion_family(&proto_addr) = AF_UNSPEC;
+
+	ret = nhrp_nhs_add(ifp, afi, &proto_addr, argv[2]);
+	return nhrp_vty_return(vty, ret);
+}
+
+DEFUN(if_no_nhrp_nhs, if_no_nhrp_nhs_cmd,
+	"no " AFI_CMD " nhrp nhs (A.B.C.D|X:X::X:X|dynamic) nbma (A.B.C.D|FQDN)",
+	NO_STR
+	AFI_STR
+	NHRP_STR
+	"Nexthop Server configuration\n"
+	"IPv4 protocol address\n"
+	"IPv6 protocol address\n"
+	"Automatic detection of protocol address\n"
+	"IPv4 NBMA address\n"
+	"Fully qualified domain name for NBMA address(es)\n")
+{
+	struct interface *ifp = vty->index;
+	afi_t afi = cmd_to_afi(argv[0]);
+	union sockunion proto_addr;
+	int ret;
+
+	if (str2sockunion(argv[1], &proto_addr) < 0)
+		sockunion_family(&proto_addr) = AF_UNSPEC;
+
+	ret = nhrp_nhs_del(ifp, afi, &proto_addr, argv[2]);
+	return nhrp_vty_return(vty, ret);
+}
+
+struct info_ctx {
+	struct vty *vty;
+	afi_t afi;
+	int count;
+};
+
+static void show_ip_nhrp_cache(struct nhrp_cache *c, void *pctx)
+{
+	struct info_ctx *ctx = pctx;
+	struct vty *vty = ctx->vty;
+	char buf[2][SU_ADDRSTRLEN];
+
+	if (ctx->afi != family2afi(sockunion_family(&c->remote_addr)))
+		return;
+
+	if (!ctx->count) {
+		vty_out(vty, "%-8s %-8s %-24s %-24s %-6s %s%s",
+			"Iface",
+			"Type",
+			"Protocol",
+			"NBMA",
+			"Flags",
+			"Identity",
+			VTY_NEWLINE);
+	}
+	ctx->count++;
+
+	vty_out(ctx->vty, "%-8s %-8s %-24s %-24s %c%c%c    %s%s",
+		c->ifp->name,
+		nhrp_cache_type_str[c->cur.type],
+		sockunion2str(&c->remote_addr, buf[0], sizeof buf[0]),
+		c->cur.peer ? sockunion2str(&c->cur.peer->vc->remote.nbma, buf[1], sizeof buf[1]) : "-",
+		c->used ? 'U' : ' ',
+		c->t_timeout ? 'T' : ' ',
+		c->t_auth ? 'A' : ' ',
+		c->cur.peer ? c->cur.peer->vc->remote.id : "-",
+		VTY_NEWLINE);
+}
+
+static void show_ip_opennhrp_cache(struct nhrp_cache *c, void *pctx)
+{
+	struct info_ctx *ctx = pctx;
+	struct vty *vty = ctx->vty;
+	char buf[SU_ADDRSTRLEN];
+
+	if (ctx->afi != family2afi(sockunion_family(&c->remote_addr)))
+		return;
+
+	vty_out(ctx->vty,
+		"Type: %s%s"
+		"Flags:%s%s%s"
+		"Protocol-Address: %s/%zu%s",
+		nhrp_cache_type_str[c->cur.type],
+		VTY_NEWLINE,
+		(c->cur.peer && c->cur.peer->online) ? " up": "",
+		c->used ? " used": "",
+		VTY_NEWLINE,
+		sockunion2str(&c->remote_addr, buf, sizeof buf),
+		8 * family2addrsize(sockunion_family(&c->remote_addr)),
+		VTY_NEWLINE);
+
+	if (c->cur.peer) {
+		vty_out(ctx->vty,
+			"NBMA-Address: %s%s",
+			sockunion2str(&c->cur.peer->vc->remote.nbma, buf, sizeof buf),
+			VTY_NEWLINE);
+	}
+
+	if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) {
+		vty_out(ctx->vty,
+			"NBMA-NAT-OA-Address: %s%s",
+			sockunion2str(&c->cur.remote_nbma_natoa, buf, sizeof buf),
+			VTY_NEWLINE);
+	}
+
+	vty_out(ctx->vty, "%s", VTY_NEWLINE);
+}
+
+static void show_ip_nhrp_shortcut(struct nhrp_shortcut *s, void *pctx)
+{
+	struct info_ctx *ctx = pctx;
+	struct nhrp_cache *c;
+	struct vty *vty = ctx->vty;
+	char buf1[PREFIX_STRLEN], buf2[SU_ADDRSTRLEN];
+
+	if (!ctx->count) {
+		vty_out(vty, "%-8s %-24s %-24s %s%s",
+			"Type",
+			"Prefix",
+			"Via",
+			"Identity",
+			VTY_NEWLINE);
+	}
+	ctx->count++;
+
+	c = s->cache;
+	vty_out(ctx->vty, "%-8s %-24s %-24s %s%s",
+		nhrp_cache_type_str[s->type],
+		prefix2str(s->p, buf1, sizeof buf1),
+		c ? sockunion2str(&c->remote_addr, buf2, sizeof buf2) : "",
+		(c && c->cur.peer) ? c->cur.peer->vc->remote.id : "",
+		VTY_NEWLINE);
+}
+
+DEFUN(show_ip_nhrp, show_ip_nhrp_cmd,
+	"show " AFI_CMD " nhrp (cache|shortcut|opennhrp|)",
+	SHOW_STR
+	AFI_STR
+	"NHRP information\n"
+	"Forwarding cache information\n"
+	"Shortcut information\n"
+	"opennhrpctl style cache dump\n")
+{
+	struct listnode *node;
+	struct interface *ifp;
+	struct info_ctx ctx = {
+		.vty = vty,
+		.afi = cmd_to_afi(argv[0]),
+	};
+
+	if (!argv[1] || argv[1][0] == 'c') {
+		for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp))
+			nhrp_cache_foreach(ifp, show_ip_nhrp_cache, &ctx);
+	} else if (argv[1][0] == 'o') {
+		vty_out(vty, "Status: ok%s%s", VTY_NEWLINE, VTY_NEWLINE);
+		ctx.count++;
+		for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp))
+			nhrp_cache_foreach(ifp, show_ip_opennhrp_cache, &ctx);
+	} else {
+		nhrp_shortcut_foreach(ctx.afi, show_ip_nhrp_shortcut, &ctx);
+	}
+
+	if (!ctx.count) {
+		vty_out(vty, "%% No entries%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	return CMD_SUCCESS;
+}
+
+static void show_dmvpn_entry(struct nhrp_vc *vc, void *ctx)
+{
+	struct vty *vty = ctx;
+	char buf[2][SU_ADDRSTRLEN];
+
+	vty_out(vty, "%-24s %-24s %c      %-4d %-24s%s",
+		sockunion2str(&vc->local.nbma, buf[0], sizeof buf[0]),
+		sockunion2str(&vc->remote.nbma, buf[1], sizeof buf[1]),
+		notifier_active(&vc->notifier_list) ? 'n' : ' ',
+		vc->ipsec,
+		vc->remote.id,
+		VTY_NEWLINE);
+}
+
+DEFUN(show_dmvpn, show_dmvpn_cmd,
+	"show dmvpn",
+	SHOW_STR
+	"DMVPN information\n")
+{
+	vty_out(vty, "%-24s %-24s %-6s %-4s %-24s%s",
+		"Src",
+		"Dst",
+		"Flags",
+		"SAs",
+		"Identity",
+		VTY_NEWLINE);
+
+	nhrp_vc_foreach(show_dmvpn_entry, vty);
+
+	return CMD_SUCCESS;
+}
+
+static void clear_nhrp_cache(struct nhrp_cache *c, void *data)
+{
+	struct info_ctx *ctx = data;
+	if (c->cur.type <= NHRP_CACHE_CACHED) {
+		nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
+		ctx->count++;
+	}
+}
+
+static void clear_nhrp_shortcut(struct nhrp_shortcut *s, void *data)
+{
+	struct info_ctx *ctx = data;
+	nhrp_shortcut_purge(s, 1);
+	ctx->count++;
+}
+
+DEFUN(clear_nhrp, clear_nhrp_cmd,
+	"clear " AFI_CMD " nhrp (cache|shortcut)",
+	CLEAR_STR
+	AFI_STR
+	NHRP_STR
+	"Dynamic cache entries\n"
+	"Shortcut entries\n")
+{
+	struct listnode *node;
+	struct interface *ifp;
+	struct info_ctx ctx = {
+		.vty = vty,
+		.afi = cmd_to_afi(argv[0]),
+		.count = 0,
+	};
+
+	if (!argv[1] || argv[1][0] == 'c') {
+		for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp))
+			nhrp_cache_foreach(ifp, clear_nhrp_cache, &ctx);
+	} else {
+		nhrp_shortcut_foreach(ctx.afi, clear_nhrp_shortcut, &ctx);
+	}
+
+	if (!ctx.count) {
+		vty_out(vty, "%% No entries%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	vty_out(vty, "%% %d entries cleared%s", ctx.count, VTY_NEWLINE);
+	return CMD_SUCCESS;
+}
+
+struct write_map_ctx {
+	struct vty *vty;
+	int family;
+	const char *aficmd;
+};
+
+static void interface_config_write_nhrp_map(struct nhrp_cache *c, void *data)
+{
+	struct write_map_ctx *ctx = data;
+	struct vty *vty = ctx->vty;
+	char buf[2][SU_ADDRSTRLEN];
+
+	if (!c->map) return;
+	if (sockunion_family(&c->remote_addr) != ctx->family) return;
+
+	vty_out(vty, " %s nhrp map %s %s%s",
+		ctx->aficmd,
+		sockunion2str(&c->remote_addr, buf[0], sizeof buf[0]),
+		c->cur.type == NHRP_CACHE_LOCAL ? "local" :
+		sockunion2str(&c->cur.peer->vc->remote.nbma, buf[1], sizeof buf[1]),
+		VTY_NEWLINE);
+}
+
+static int interface_config_write(struct vty *vty)
+{
+	struct write_map_ctx mapctx;
+	struct listnode *node;
+	struct interface *ifp;
+	struct nhrp_interface *nifp;
+	struct nhrp_nhs *nhs;
+	const char *aficmd;
+	afi_t afi;
+	char buf[SU_ADDRSTRLEN];
+	int i;
+
+	for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
+		vty_out(vty, "interface %s%s", ifp->name, VTY_NEWLINE);
+		if (ifp->desc)
+			vty_out(vty, " description %s%s", ifp->desc, VTY_NEWLINE);
+
+		nifp = ifp->info;
+		if (nifp->ipsec_profile) {
+			vty_out(vty, " tunnel protection vici profile %s",
+				nifp->ipsec_profile);
+			if (nifp->ipsec_fallback_profile)
+				vty_out(vty, " fallback-profile %s",
+					nifp->ipsec_fallback_profile);
+			vty_out(vty, "%s", VTY_NEWLINE);
+		}
+		if (nifp->source)
+			vty_out(vty, " tunnel source %s%s",
+				nifp->source, VTY_NEWLINE);
+
+		for (afi = 0; afi < AFI_MAX; afi++) {
+			struct nhrp_afi_data *ad = &nifp->afi[afi];
+
+			aficmd = afi_to_cmd(afi);
+
+			if (ad->network_id)
+				vty_out(vty, " %s nhrp network-id %u%s",
+					aficmd, ad->network_id,
+					VTY_NEWLINE);
+
+			if (ad->holdtime != NHRPD_DEFAULT_HOLDTIME)
+				vty_out(vty, " %s nhrp holdtime %u%s",
+					aficmd, ad->holdtime,
+					VTY_NEWLINE);
+
+			if (ad->configured_mtu < 0)
+				vty_out(vty, " %s nhrp mtu opennhrp%s",
+					aficmd, VTY_NEWLINE);
+			else if (ad->configured_mtu)
+				vty_out(vty, " %s nhrp mtu %u%s",
+					aficmd, ad->configured_mtu,
+					VTY_NEWLINE);
+
+			for (i = 0; interface_flags_desc[i].str != NULL; i++) {
+				if (!(ad->flags & interface_flags_desc[i].key))
+					continue;
+				vty_out(vty, " %s nhrp %s%s",
+					aficmd, interface_flags_desc[i].str, VTY_NEWLINE);
+			}
+
+			mapctx = (struct write_map_ctx) {
+				.vty = vty,
+				.family = afi2family(afi),
+				.aficmd = aficmd,
+			};
+			nhrp_cache_foreach(ifp, interface_config_write_nhrp_map, &mapctx);
+
+			list_for_each_entry(nhs, &ad->nhslist_head, nhslist_entry) {
+				vty_out(vty, " %s nhrp nhs %s nbma %s%s",
+					aficmd,
+					sockunion_family(&nhs->proto_addr) == AF_UNSPEC ? "dynamic" : sockunion2str(&nhs->proto_addr, buf, sizeof buf),
+					nhs->nbma_fqdn,
+					VTY_NEWLINE);
+			}
+		}
+
+		vty_out (vty, "!%s", VTY_NEWLINE);
+	}
+
+	return 0;
+}
+
+void nhrp_config_init(void)
+{
+	install_node(&zebra_node, nhrp_config_write);
+	install_default(ZEBRA_NODE);
+
+	/* global commands */
+	install_element(VIEW_NODE, &show_debugging_nhrp_cmd);
+	install_element(VIEW_NODE, &show_ip_nhrp_cmd);
+	install_element(VIEW_NODE, &show_dmvpn_cmd);
+	install_element(ENABLE_NODE, &show_debugging_nhrp_cmd);
+	install_element(ENABLE_NODE, &show_ip_nhrp_cmd);
+	install_element(ENABLE_NODE, &show_dmvpn_cmd);
+	install_element(ENABLE_NODE, &clear_nhrp_cmd);
+
+	install_element(ENABLE_NODE, &debug_nhrp_cmd);
+	install_element(ENABLE_NODE, &no_debug_nhrp_cmd);
+
+	install_element(CONFIG_NODE, &debug_nhrp_cmd);
+	install_element(CONFIG_NODE, &no_debug_nhrp_cmd);
+
+	install_element(CONFIG_NODE, &nhrp_event_socket_cmd);
+	install_element(CONFIG_NODE, &no_nhrp_event_socket_cmd);
+	install_element(CONFIG_NODE, &nhrp_nflog_group_cmd);
+	install_element(CONFIG_NODE, &no_nhrp_nflog_group_cmd);
+
+	/* interface specific commands */
+	install_node(&nhrp_interface_node, interface_config_write);
+	install_default(INTERFACE_NODE);
+
+	install_element(CONFIG_NODE, &interface_cmd);
+	install_element(CONFIG_NODE, &no_interface_cmd);
+	install_element(INTERFACE_NODE, &interface_cmd);
+	install_element(INTERFACE_NODE, &no_interface_cmd);
+	install_element(INTERFACE_NODE, &tunnel_protection_cmd);
+	install_element(INTERFACE_NODE, &no_tunnel_protection_cmd);
+	install_element(INTERFACE_NODE, &tunnel_source_cmd);
+	install_element(INTERFACE_NODE, &no_tunnel_source_cmd);
+	install_element(INTERFACE_NODE, &if_nhrp_network_id_cmd);
+	install_element(INTERFACE_NODE, &if_no_nhrp_network_id_cmd);
+	install_element(INTERFACE_NODE, &if_nhrp_holdtime_cmd);
+	install_element(INTERFACE_NODE, &if_no_nhrp_holdtime_cmd);
+	install_element(INTERFACE_NODE, &if_nhrp_mtu_cmd);
+	install_element(INTERFACE_NODE, &if_no_nhrp_mtu_cmd);
+	install_element(INTERFACE_NODE, &if_nhrp_flags_cmd);
+	install_element(INTERFACE_NODE, &if_no_nhrp_flags_cmd);
+	install_element(INTERFACE_NODE, &if_nhrp_reg_flags_cmd);
+	install_element(INTERFACE_NODE, &if_no_nhrp_reg_flags_cmd);
+	install_element(INTERFACE_NODE, &if_nhrp_map_cmd);
+	install_element(INTERFACE_NODE, &if_nhrp_nhs_cmd);
+	install_element(INTERFACE_NODE, &if_no_nhrp_nhs_cmd);
+}