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_vc.c b/nhrpd/nhrp_vc.c
new file mode 100644
index 0000000..f9e1ee0
--- /dev/null
+++ b/nhrpd/nhrp_vc.c
@@ -0,0 +1,217 @@
+/* NHRP virtual connection
+ * 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 "memory.h"
+#include "stream.h"
+#include "hash.h"
+#include "thread.h"
+#include "jhash.h"
+
+#include "nhrpd.h"
+#include "os.h"
+
+struct child_sa {
+	uint32_t id;
+	struct nhrp_vc *vc;
+	struct list_head childlist_entry;
+};
+
+static struct hash *nhrp_vc_hash;
+static struct list_head childlist_head[512];
+
+static unsigned int nhrp_vc_key(void *peer_data)
+{
+	struct nhrp_vc *vc = peer_data;
+	return jhash_2words(
+		sockunion_hash(&vc->local.nbma),
+		sockunion_hash(&vc->remote.nbma),
+		0);
+}
+
+static int nhrp_vc_cmp(const void *cache_data, const void *key_data)
+{
+	const struct nhrp_vc *a = cache_data;
+	const struct nhrp_vc *b = key_data;
+	return	sockunion_same(&a->local.nbma, &b->local.nbma) &&
+		sockunion_same(&a->remote.nbma, &b->remote.nbma);
+}
+
+static void *nhrp_vc_alloc(void *data)
+{
+	struct nhrp_vc *vc, *key = data;
+
+	vc = XMALLOC(MTYPE_NHRP_VC, sizeof(struct nhrp_vc));
+	if (vc) {
+		*vc = (struct nhrp_vc) {
+			.local.nbma = key->local.nbma,
+			.remote.nbma = key->remote.nbma,
+			.notifier_list = NOTIFIER_LIST_INITIALIZER(&vc->notifier_list),
+		};
+	}
+
+	return vc;
+}
+
+static void nhrp_vc_free(void *data)
+{
+	XFREE(MTYPE_NHRP_VC, data);
+}
+
+struct nhrp_vc *nhrp_vc_get(const union sockunion *src, const union sockunion *dst, int create)
+{
+	struct nhrp_vc key;
+	key.local.nbma = *src;
+	key.remote.nbma = *dst;
+	return hash_get(nhrp_vc_hash, &key, create ? nhrp_vc_alloc : 0);
+}
+
+static void nhrp_vc_check_delete(struct nhrp_vc *vc)
+{
+	if (vc->updating || vc->ipsec || notifier_active(&vc->notifier_list))
+		return;
+	hash_release(nhrp_vc_hash, vc);
+	nhrp_vc_free(vc);
+}
+
+static void nhrp_vc_update(struct nhrp_vc *vc, long cmd)
+{
+	vc->updating = 1;
+	notifier_call(&vc->notifier_list, cmd);
+	vc->updating = 0;
+	nhrp_vc_check_delete(vc);
+}
+
+static void nhrp_vc_ipsec_reset(struct nhrp_vc *vc)
+{
+	vc->local.id[0] = 0;
+	vc->local.certlen = 0;
+	vc->remote.id[0] = 0;
+	vc->remote.certlen = 0;
+}
+
+int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc)
+{
+	char buf[2][SU_ADDRSTRLEN];
+	struct child_sa *sa = NULL, *lsa;
+	uint32_t child_hash = child_id % ZEBRA_NUM_OF(childlist_head);
+	int abort_migration = 0;
+
+	list_for_each_entry(lsa, &childlist_head[child_hash], childlist_entry) {
+		if (lsa->id == child_id) {
+			sa = lsa;
+			break;
+		}
+	}
+
+	if (!sa) {
+		if (!vc) return 0;
+
+		sa = XMALLOC(MTYPE_NHRP_VC, sizeof(struct child_sa));
+		if (!sa) return 0;
+
+		*sa = (struct child_sa) {
+			.id = child_id,
+			.childlist_entry = LIST_INITIALIZER(sa->childlist_entry),
+			.vc = NULL,
+		};
+		list_add_tail(&sa->childlist_entry, &childlist_head[child_hash]);
+	}
+
+	if (sa->vc == vc)
+		return 0;
+
+	if (vc) {
+		/* Attach first to new VC */
+		vc->ipsec++;
+		nhrp_vc_update(vc, NOTIFY_VC_IPSEC_CHANGED);
+	}
+	if (sa->vc && vc) {
+		/* Notify old VC of migration */
+		sa->vc->abort_migration = 0;
+		debugf(NHRP_DEBUG_COMMON, "IPsec NBMA change of %s to %s",
+			sockunion2str(&sa->vc->remote.nbma, buf[0], sizeof buf[0]),
+			sockunion2str(&vc->remote.nbma, buf[1], sizeof buf[1]));
+		nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_UPDATE_NBMA);
+		abort_migration = sa->vc->abort_migration;
+	}
+	if (sa->vc) {
+		/* Deattach old VC */
+		sa->vc->ipsec--;
+		if (!sa->vc->ipsec) nhrp_vc_ipsec_reset(sa->vc);
+		nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_CHANGED);
+	}
+
+	/* Update */
+	sa->vc = vc;
+	if (!vc) {
+		list_del(&sa->childlist_entry);
+		XFREE(MTYPE_NHRP_VC, sa);
+	}
+
+	return abort_migration;
+}
+
+void nhrp_vc_notify_add(struct nhrp_vc *vc, struct notifier_block *n, notifier_fn_t action)
+{
+	notifier_add(n, &vc->notifier_list, action);
+}
+
+void nhrp_vc_notify_del(struct nhrp_vc *vc, struct notifier_block *n)
+{
+	notifier_del(n);
+	nhrp_vc_check_delete(vc);
+}
+
+
+struct nhrp_vc_iterator_ctx {
+	void (*cb)(struct nhrp_vc *, void *);
+	void *ctx;
+};
+
+static void nhrp_vc_iterator(struct hash_backet *b, void *ctx)
+{
+	struct nhrp_vc_iterator_ctx *ic = ctx;
+	ic->cb(b->data, ic->ctx);
+}
+
+void nhrp_vc_foreach(void (*cb)(struct nhrp_vc *, void *), void *ctx)
+{
+	struct nhrp_vc_iterator_ctx ic = {
+		.cb = cb,
+		.ctx = ctx,
+	};
+	hash_iterate(nhrp_vc_hash, nhrp_vc_iterator, &ic);
+}
+
+void nhrp_vc_init(void)
+{
+	size_t i;
+
+	nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp);
+	for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++)
+		list_init(&childlist_head[i]);
+}
+
+void nhrp_vc_reset(void)
+{
+	struct child_sa *sa, *n;
+	size_t i;
+
+	for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) {
+		list_for_each_entry_safe(sa, n, &childlist_head[i], childlist_entry)
+			nhrp_vc_ipsec_updown(sa->id, 0);
+	}
+}
+
+void nhrp_vc_terminate(void)
+{
+	nhrp_vc_reset();
+	hash_clean(nhrp_vc_hash, nhrp_vc_free);
+}