aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--main/quagga-nhrp/0001-zebra-use-FIB-state-for-nexthop-tracking.patch28
-rw-r--r--main/quagga-nhrp/0002-zebra-fix-nht-validity-checking-to-be-same-as-when-i.patch33
-rw-r--r--main/quagga-nhrp/0003-bgpd-honor-disable-connected-check-option-with-next-.patch28
-rw-r--r--main/quagga-nhrp/0004-bgpd-simplify-ebgp-multihop-and-ttl-security-handlin.patch652
-rw-r--r--main/quagga-nhrp/0005-nhrpd-implement-next-hop-resolution-protocol.patch8279
-rw-r--r--main/quagga-nhrp/APKBUILD111
-rw-r--r--main/quagga-nhrp/bgpd.initd22
-rw-r--r--main/quagga-nhrp/dont-hook-core-signals.patch20
-rw-r--r--main/quagga-nhrp/quagga-nhrp.post-install9
l---------main/quagga-nhrp/quagga-nhrp.post-upgrade1
-rw-r--r--main/quagga-nhrp/quagga-nhrp.pre-install6
-rw-r--r--main/quagga-nhrp/zebra.confd7
-rw-r--r--main/quagga-nhrp/zebra.initd34
-rw-r--r--main/quagga/1001-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch (renamed from main/quagga-nhrp/0006-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch)0
-rw-r--r--main/quagga/APKBUILD25
15 files changed, 10 insertions, 9245 deletions
diff --git a/main/quagga-nhrp/0001-zebra-use-FIB-state-for-nexthop-tracking.patch b/main/quagga-nhrp/0001-zebra-use-FIB-state-for-nexthop-tracking.patch
deleted file mode 100644
index d9cee06948..0000000000
--- a/main/quagga-nhrp/0001-zebra-use-FIB-state-for-nexthop-tracking.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 22d31cdc21523aa790c07d84ee1b89dcb53107af Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
-Date: Wed, 30 Mar 2016 13:46:57 +0300
-Subject: [PATCH 1/5] zebra: use FIB state for nexthop tracking
-
-The FIB override routes can override ZEBRA_FLAG_SELECTED routes
-in FIB. Use the FIB state instead to report correct nexthop when
-FIB override routes are present.
----
- zebra/zebra_rnh.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
-index 7b6d0f8..97d3597 100644
---- a/zebra/zebra_rnh.c
-+++ b/zebra/zebra_rnh.c
-@@ -223,7 +223,7 @@ zebra_evaluate_rnh_table (vrf_id_t vrfid, int family)
- {
- if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
- continue;
-- if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
-+ if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB))
- {
- if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED))
- {
---
-2.10.1
-
diff --git a/main/quagga-nhrp/0002-zebra-fix-nht-validity-checking-to-be-same-as-when-i.patch b/main/quagga-nhrp/0002-zebra-fix-nht-validity-checking-to-be-same-as-when-i.patch
deleted file mode 100644
index 88d338184f..0000000000
--- a/main/quagga-nhrp/0002-zebra-fix-nht-validity-checking-to-be-same-as-when-i.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From 17d31f627a305eeba2d12525d3deea516c2ac0c1 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
-Date: Wed, 30 Mar 2016 14:31:14 +0300
-Subject: [PATCH 2/5] zebra: fix nht validity checking to be same as when it's
- resolved
-
-This reverts commit 7e3a435bd99 "A valid BGP nexthop is flagged as invalid"
-
-Problem is BGP thinks the nexthop is accessible when it's recursive, and
-selects it, but zebra rejects it at route install time. Causing FIB and
-BGP state to be out-of-sync. Fix nht to follow same rules as zebra rib.
----
- zebra/zebra_rnh.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
-index 97d3597..5762d3f 100644
---- a/zebra/zebra_rnh.c
-+++ b/zebra/zebra_rnh.c
-@@ -498,8 +498,8 @@ send_client (struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id)
- nump = stream_get_endp(s);
- stream_putc (s, 0);
- for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
-- if ((CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ||
-- CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) &&
-+ if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) &&
-+ ! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE) &&
- CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
- {
- stream_putc (s, nexthop->type);
---
-2.10.1
-
diff --git a/main/quagga-nhrp/0003-bgpd-honor-disable-connected-check-option-with-next-.patch b/main/quagga-nhrp/0003-bgpd-honor-disable-connected-check-option-with-next-.patch
deleted file mode 100644
index 9e338fa074..0000000000
--- a/main/quagga-nhrp/0003-bgpd-honor-disable-connected-check-option-with-next-.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 0f4aa527ebddbe32632db5517dd4b98c1371c072 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
-Date: Wed, 19 Oct 2016 15:35:45 +0300
-Subject: [PATCH 3/5] bgpd: honor disable-connected-check option with next hop
- tracking
-
-Make bgpd ignore connected state again if configured to do so.
----
- bgpd/bgp_fsm.c | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
-index 2e07d93..7da63ea 100644
---- a/bgpd/bgp_fsm.c
-+++ b/bgpd/bgp_fsm.c
-@@ -716,7 +716,8 @@ bgp_start (struct peer *peer)
- }
-
- /* Register to be notified on peer up */
-- if ((peer->ttl == 1) || (peer->gtsm_hops == 1))
-+ if ((peer->ttl == 1 || peer->gtsm_hops == 1) &&
-+ ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
- connected = 1;
-
- bgp_find_or_add_nexthop(family2afi(peer->su.sa.sa_family), NULL, peer,
---
-2.10.1
-
diff --git a/main/quagga-nhrp/0004-bgpd-simplify-ebgp-multihop-and-ttl-security-handlin.patch b/main/quagga-nhrp/0004-bgpd-simplify-ebgp-multihop-and-ttl-security-handlin.patch
deleted file mode 100644
index 58429fd03e..0000000000
--- a/main/quagga-nhrp/0004-bgpd-simplify-ebgp-multihop-and-ttl-security-handlin.patch
+++ /dev/null
@@ -1,652 +0,0 @@
-From f61ef00502afb21473f33fcbde851003b96356d5 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
-Date: Tue, 7 Jul 2015 15:08:51 +0300
-Subject: [PATCH 4/5] bgpd: simplify ebgp-multihop and ttl-security handling
-
-Change to track configured value in ->ttl and ->gtsm_hops;
-not the value set to sockopt. Instead, setting of socket's ttl
-and minttl options are now merged to one function which calculates
-it on demand. This greatly simplifies the code.
----
- bgpd/bgp_fsm.c | 2 +-
- bgpd/bgp_network.c | 58 ++++++-------
- bgpd/bgp_network.h | 1 +
- bgpd/bgp_route.c | 4 +-
- bgpd/bgp_vty.c | 26 ++----
- bgpd/bgp_zebra.c | 13 ++-
- bgpd/bgpd.c | 237 +++++++++++------------------------------------------
- bgpd/bgpd.h | 7 +-
- 8 files changed, 95 insertions(+), 253 deletions(-)
-
-diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
-index 7da63ea..67c50c4 100644
---- a/bgpd/bgp_fsm.c
-+++ b/bgpd/bgp_fsm.c
-@@ -716,7 +716,7 @@ bgp_start (struct peer *peer)
- }
-
- /* Register to be notified on peer up */
-- if ((peer->ttl == 1 || peer->gtsm_hops == 1) &&
-+ if ((peer_ttl(peer) == 1 || peer->gtsm_hops == 1) &&
- ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
- connected = 1;
-
-diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
-index 51a6f60..c7d3389 100644
---- a/bgpd/bgp_network.c
-+++ b/bgpd/bgp_network.c
-@@ -146,47 +146,39 @@ bgp_update_sock_send_buffer_size (int fd)
- }
- }
-
--static void
-+void
- bgp_set_socket_ttl (struct peer *peer, int bgp_sock)
- {
- char buf[INET_ADDRSTRLEN];
-- int ret;
-+ int ret, ttl, minttl;
-
-- /* In case of peer is EBGP, we should set TTL for this connection. */
-- if (!peer->gtsm_hops && (peer_sort (peer) == BGP_PEER_EBGP))
-+ if (bgp_sock < 0)
-+ return;
-+
-+ if (peer->gtsm_hops)
- {
-- ret = sockopt_ttl (peer->su.sa.sa_family, bgp_sock, peer->ttl);
-- if (ret)
-- {
-- zlog_err ("%s: Can't set TxTTL on peer (rtrid %s) socket, err = %d",
-- __func__,
-- inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)),
-- errno);
-- }
-+ ttl = 255;
-+ minttl = 256 - peer->gtsm_hops;
- }
-- else if (peer->gtsm_hops)
-+ else
- {
-- /* On Linux, setting minttl without setting ttl seems to mess with the
-- outgoing ttl. Therefore setting both.
-- */
-- ret = sockopt_ttl (peer->su.sa.sa_family, bgp_sock, MAXTTL);
-- if (ret)
-- {
-- zlog_err ("%s: Can't set TxTTL on peer (rtrid %s) socket, err = %d",
-- __func__,
-- inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)),
-- errno);
-- }
-- ret = sockopt_minttl (peer->su.sa.sa_family, bgp_sock,
-- MAXTTL + 1 - peer->gtsm_hops);
-- if (ret)
-- {
-- zlog_err ("%s: Can't set MinTTL on peer (rtrid %s) socket, err = %d",
-- __func__,
-- inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)),
-- errno);
-- }
-+ ttl = peer_ttl (peer);
-+ minttl = 0;
- }
-+
-+ ret = sockopt_ttl (peer->su.sa.sa_family, bgp_sock, ttl);
-+ if (ret)
-+ zlog_err ("%s: Can't set TxTTL on peer (rtrid %s) socket, err = %d",
-+ __func__,
-+ inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)),
-+ errno);
-+
-+ ret = sockopt_minttl (peer->su.sa.sa_family, bgp_sock, minttl);
-+ if (ret && (errno != ENOTSUP || minttl))
-+ zlog_err ("%s: Can't set MinTTL on peer (rtrid %s) socket, err = %d",
-+ __func__,
-+ inet_ntop (AF_INET, &peer->remote_id, buf, sizeof(buf)),
-+ errno);
- }
-
- /* Accept bgp connection. */
-diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h
-index 1276843..31995ca 100644
---- a/bgpd/bgp_network.h
-+++ b/bgpd/bgp_network.h
-@@ -28,6 +28,7 @@ extern void bgp_close (void);
- extern int bgp_connect (struct peer *);
- extern void bgp_getsockname (struct peer *);
-
-+extern void bgp_set_socket_ttl (struct peer *peer, int bgp_sock);
- extern int bgp_md5_set (struct peer *);
-
- #endif /* _QUAGGA_BGP_NETWORK_H */
-diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
-index 4cb6c14..010fd4d 100644
---- a/bgpd/bgp_route.c
-+++ b/bgpd/bgp_route.c
-@@ -2339,7 +2339,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
- /* Nexthop reachability check. */
- if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST)
- {
-- if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1 &&
-+ if (peer->sort == BGP_PEER_EBGP && peer_ttl (peer) == 1 &&
- ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
- connected = 1;
- else
-@@ -2391,7 +2391,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr,
- /* Nexthop reachability check. */
- if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST)
- {
-- if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1 &&
-+ if (peer->sort == BGP_PEER_EBGP && peer_ttl (peer) == 1 &&
- ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
- connected = 1;
- else
-diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
-index 7af4e81..8eeaff9 100644
---- a/bgpd/bgp_vty.c
-+++ b/bgpd/bgp_vty.c
-@@ -3058,7 +3058,7 @@ peer_ebgp_multihop_unset_vty (struct vty *vty, const char *ip_str)
- if (! peer)
- return CMD_WARNING;
-
-- return bgp_vty_return (vty, peer_ebgp_multihop_unset (peer));
-+ return bgp_vty_return (vty, peer_ebgp_multihop_set (peer, 0));
- }
-
- /* neighbor ebgp-multihop. */
-@@ -4380,7 +4380,7 @@ DEFUN (no_neighbor_ttl_security,
- if (! peer)
- return CMD_WARNING;
-
-- return bgp_vty_return (vty, peer_ttl_security_hops_unset (peer));
-+ return bgp_vty_return (vty, peer_ttl_security_hops_set (peer, 0));
- }
-
- /* Address family configuration. */
-@@ -8364,6 +8364,7 @@ bgp_show_peer (struct vty *vty, struct peer *p)
- char timebuf[BGP_UPTIME_LEN];
- afi_t afi;
- safi_t safi;
-+ int ttl;
-
- bgp = p->bgp;
-
-@@ -8663,21 +8664,12 @@ bgp_show_peer (struct vty *vty, struct peer *p)
- }
-
- /* EBGP Multihop and GTSM */
-- if (p->sort != BGP_PEER_IBGP)
-- {
-- if (p->gtsm_hops > 0)
-- vty_out (vty, " External BGP neighbor may be up to %d hops away.%s",
-- p->gtsm_hops, VTY_NEWLINE);
-- else if (p->ttl > 1)
-- vty_out (vty, " External BGP neighbor may be up to %d hops away.%s",
-- p->ttl, VTY_NEWLINE);
-- }
-- else
-- {
-- if (p->gtsm_hops > 0)
-- vty_out (vty, " Internal BGP neighbor may be up to %d hops away.%s",
-- p->gtsm_hops, VTY_NEWLINE);
-- }
-+ ttl = p->gtsm_hops;
-+ if (! ttl)
-+ ttl = peer_ttl (p);
-+ vty_out (vty, " %s BGP neighbor may be up to %d hops away.%s",
-+ p->sort == BGP_PEER_IBGP ? "Internal" : "External",
-+ ttl, VTY_NEWLINE);
-
- /* Local address. */
- if (p->su_local)
-diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
-index 45d502a..ba87ad1 100644
---- a/bgpd/bgp_zebra.c
-+++ b/bgpd/bgp_zebra.c
-@@ -172,11 +172,10 @@ bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length,
-
- for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
- {
-- if ((peer->ttl != 1) && (peer->gtsm_hops != 1))
-- continue;
--
-- if (ifp == peer->nexthop.ifp)
-- BGP_EVENT_ADD (peer, BGP_Stop);
-+ if (peer->gtsm_hops != 1 && peer_ttl (peer) != 1)
-+ continue;
-+ if (ifp == peer->nexthop.ifp)
-+ BGP_EVENT_ADD (peer, BGP_Stop);
- }
- }
- }
-@@ -716,7 +715,7 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, sa
- SET_FLAG (flags, ZEBRA_FLAG_INTERNAL);
- }
-
-- if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1)
-+ if ((peer->sort == BGP_PEER_EBGP && peer_ttl (peer) != 1)
- || CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
- SET_FLAG (flags, ZEBRA_FLAG_INTERNAL);
-
-@@ -991,7 +990,7 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi)
- SET_FLAG (flags, ZEBRA_FLAG_IBGP);
- }
-
-- if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1)
-+ if ((peer->sort == BGP_PEER_EBGP && peer_ttl (peer) != 1)
- || CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
- SET_FLAG (flags, ZEBRA_FLAG_INTERNAL);
-
-diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
-index 018a599..56e4322 100644
---- a/bgpd/bgpd.c
-+++ b/bgpd/bgpd.c
-@@ -646,7 +646,8 @@ peer_global_config_reset (struct peer *peer)
- {
- peer->weight = 0;
- peer->change_local_as = 0;
-- peer->ttl = (peer_sort (peer) == BGP_PEER_IBGP ? 255 : 1);
-+ peer->ttl = 0;
-+ peer->gtsm_hops = 0;
- if (peer->update_source)
- {
- sockunion_free (peer->update_source);
-@@ -925,9 +926,6 @@ peer_create (union sockunion *su, struct bgp *bgp, as_t local_as,
- /* Last read and reset time set */
- peer->readtime = peer->resettime = bgp_clock ();
-
-- /* Default TTL set. */
-- peer->ttl = (peer->sort == BGP_PEER_IBGP) ? 255 : 1;
--
- /* Make peer's address string. */
- sockunion2str (su, buf, SU_ADDRSTRLEN);
- peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf);
-@@ -957,7 +955,6 @@ peer_create_accept (struct bgp *bgp)
- static void
- peer_as_change (struct peer *peer, as_t as)
- {
-- bgp_peer_sort_t type;
- struct peer *conf;
-
- /* Stop peer. */
-@@ -972,7 +969,6 @@ peer_as_change (struct peer *peer, as_t as)
- else
- BGP_EVENT_ADD (peer, BGP_Stop);
- }
-- type = peer_sort (peer);
- peer->as = as;
-
- if (bgp_config_check (peer->bgp, BGP_CONFIG_CONFEDERATION)
-@@ -995,12 +991,6 @@ peer_as_change (struct peer *peer, as_t as)
- else
- peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
-
-- /* TTL reset */
-- if (peer_sort (peer) == BGP_PEER_IBGP)
-- peer->ttl = 255;
-- else if (type == BGP_PEER_IBGP)
-- peer->ttl = 1;
--
- /* reflector-client reset */
- if (peer_sort (peer) != BGP_PEER_IBGP)
- {
-@@ -1506,7 +1496,7 @@ peer_group_get (struct bgp *bgp, const char *name)
- group->conf->host = XSTRDUP (MTYPE_BGP_PEER_HOST, name);
- group->conf->group = group;
- group->conf->as = 0;
-- group->conf->ttl = 1;
-+ group->conf->ttl = 0;
- group->conf->gtsm_hops = 0;
- group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
- UNSET_FLAG (group->conf->config, PEER_CONFIG_TIMER);
-@@ -1807,6 +1797,16 @@ peer_group_remote_as (struct bgp *bgp, const char *group_name, as_t *as)
- }
-
- int
-+peer_ttl (struct peer *peer)
-+{
-+ if (peer->ttl)
-+ return peer->ttl;
-+ if (peer->gtsm_hops || peer->sort == BGP_PEER_IBGP)
-+ return 255;
-+ return 1;
-+}
-+
-+int
- peer_group_delete (struct peer_group *group)
- {
- struct bgp *bgp;
-@@ -1938,10 +1938,6 @@ peer_group_bind (struct bgp *bgp, union sockunion *su,
- group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
- }
-
-- /* ebgp-multihop reset */
-- if (peer_sort (group->conf) == BGP_PEER_IBGP)
-- group->conf->ttl = 255;
--
- /* local-as reset */
- if (peer_sort (group->conf) != BGP_PEER_EBGP)
- {
-@@ -2870,29 +2866,17 @@ peer_ebgp_multihop_set (struct peer *peer, int ttl)
- struct peer *peer1;
-
- if (peer->sort == BGP_PEER_IBGP)
-- return 0;
-+ return BGP_ERR_NO_IBGP_WITH_TTLHACK;
-
-- /* see comment in peer_ttl_security_hops_set() */
-- if (ttl != MAXTTL)
-- {
-- if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
-- {
-- group = peer->group;
-- if (group->conf->gtsm_hops != 0)
-- return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
--
-- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
-- {
-- if (peer1->sort == BGP_PEER_IBGP)
-- continue;
-+ if (peer->gtsm_hops != 0)
-+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
-
-- if (peer1->gtsm_hops != 0)
-- return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
-- }
-- }
-- else
-+ if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
-+ {
-+ group = peer->group;
-+ for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
- {
-- if (peer->gtsm_hops != 0)
-+ if (peer1->gtsm_hops != 0)
- return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
- }
- }
-@@ -2901,62 +2885,18 @@ peer_ebgp_multihop_set (struct peer *peer, int ttl)
-
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
- {
-- if (peer->fd >= 0 && peer->sort != BGP_PEER_IBGP)
-- sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
-+ bgp_set_socket_ttl (peer, peer->fd);
- }
- else
- {
- group = peer->group;
- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
-- {
-- if (peer->sort == BGP_PEER_IBGP)
-- continue;
--
-- peer->ttl = group->conf->ttl;
--
-- if (peer->fd >= 0)
-- sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
-- }
-- }
-- return 0;
--}
--
--int
--peer_ebgp_multihop_unset (struct peer *peer)
--{
-- struct peer_group *group;
-- struct listnode *node, *nnode;
--
-- if (peer->sort == BGP_PEER_IBGP)
-- return 0;
--
-- if (peer->gtsm_hops != 0 && peer->ttl != MAXTTL)
-- return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
--
-- if (peer_group_active (peer))
-- peer->ttl = peer->group->conf->ttl;
-- else
-- peer->ttl = 1;
--
-- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
-- {
-- if (peer->fd >= 0 && peer->sort != BGP_PEER_IBGP)
-- sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
-+ {
-+ peer->ttl = ttl;
-+ bgp_set_socket_ttl (peer, peer->fd);
-+ }
- }
-- else
-- {
-- group = peer->group;
-- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
-- {
-- if (peer->sort == BGP_PEER_IBGP)
-- continue;
-
-- peer->ttl = 1;
--
-- if (peer->fd >= 0)
-- sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
-- }
-- }
- return 0;
- }
-
-@@ -4652,78 +4592,41 @@ peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi)
- return 0;
- }
-
--static int is_ebgp_multihop_configured (struct peer *peer)
--{
-- struct peer_group *group;
-- struct listnode *node, *nnode;
-- struct peer *peer1;
--
-- if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
-- {
-- group = peer->group;
-- if ((peer_sort(peer) != BGP_PEER_IBGP) &&
-- (group->conf->ttl != 1))
-- return 1;
--
-- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
-- {
-- if ((peer_sort (peer1) != BGP_PEER_IBGP) &&
-- (peer1->ttl != 1))
-- return 1;
-- }
-- }
-- else
-- {
-- if ((peer_sort(peer) != BGP_PEER_IBGP) &&
-- (peer->ttl != 1))
-- return 1;
-- }
-- return 0;
--}
--
- /* Set # of hops between us and BGP peer. */
- int
- peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops)
- {
- struct peer_group *group;
- struct listnode *node, *nnode;
-- int ret;
-+ struct peer *peer1;
-
- zlog_debug ("peer_ttl_security_hops_set: set gtsm_hops to %d for %s", gtsm_hops, peer->host);
-
-- /* We cannot configure ttl-security hops when ebgp-multihop is already
-- set. For non peer-groups, the check is simple. For peer-groups, it's
-- slightly messy, because we need to check both the peer-group structure
-- and all peer-group members for any trace of ebgp-multihop configuration
-- before actually applying the ttl-security rules. Cisco really made a
-- mess of this configuration parameter, and OpenBGPD got it right.
-- */
--
-- if (peer->gtsm_hops == 0)
-- {
-- if (is_ebgp_multihop_configured (peer))
-- return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
-+ if (peer->ttl != 0)
-+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
-
-- /* specify MAXTTL on outgoing packets */
-- /* Routine handles iBGP peers correctly */
-- ret = peer_ebgp_multihop_set (peer, MAXTTL);
-- if (ret != 0)
-- return ret;
-+ if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
-+ {
-+ group = peer->group;
-+ for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
-+ {
-+ if (peer1->ttl != 0)
-+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
-+ }
- }
--
-+
- peer->gtsm_hops = gtsm_hops;
-
- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
- {
-- if (peer->fd >= 0)
-- sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - gtsm_hops);
-+ bgp_set_socket_ttl (peer, peer->fd);
- }
- else
- {
- group = peer->group;
- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
- {
-- peer->gtsm_hops = group->conf->gtsm_hops;
-+ peer->gtsm_hops = gtsm_hops;
-
- /* Change setting of existing peer
- * established then change value (may break connectivity)
-@@ -4732,9 +4635,7 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops)
- */
- if (peer->status == Established)
- {
-- if (peer->fd >= 0 && peer->gtsm_hops != 0)
-- sockopt_minttl (peer->su.sa.sa_family, peer->fd,
-- MAXTTL + 1 - peer->gtsm_hops);
-+ bgp_set_socket_ttl (peer, peer->fd);
- }
- else if (peer->status < Established)
- {
-@@ -4749,42 +4650,6 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops)
- }
-
- int
--peer_ttl_security_hops_unset (struct peer *peer)
--{
-- struct peer_group *group;
-- struct listnode *node, *nnode;
-- struct peer *opeer;
--
-- zlog_debug ("peer_ttl_security_hops_unset: set gtsm_hops to zero for %s", peer->host);
--
-- /* if a peer-group member, then reset to peer-group default rather than 0 */
-- if (peer_group_active (peer))
-- peer->gtsm_hops = peer->group->conf->gtsm_hops;
-- else
-- peer->gtsm_hops = 0;
--
-- opeer = peer;
-- if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
-- {
-- if (peer->fd >= 0)
-- sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0);
-- }
-- else
-- {
-- group = peer->group;
-- for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
-- {
-- peer->gtsm_hops = 0;
--
-- if (peer->fd >= 0)
-- sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0);
-- }
-- }
--
-- return peer_ebgp_multihop_unset (opeer);
--}
--
--int
- peer_clear (struct peer *peer)
- {
- if (! CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN))
-@@ -5094,19 +4959,13 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
- ! CHECK_FLAG (g_peer->flags, PEER_FLAG_PASSIVE))
- vty_out (vty, " neighbor %s passive%s", addr, VTY_NEWLINE);
-
-- /* EBGP multihop. */
-- if (peer->sort != BGP_PEER_IBGP && peer->ttl != 1 &&
-- !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL))
-- if (! peer_group_active (peer) ||
-- g_peer->ttl != peer->ttl)
-- vty_out (vty, " neighbor %s ebgp-multihop %d%s", addr, peer->ttl,
-- VTY_NEWLINE);
--
-- /* ttl-security hops */
-- if (peer->gtsm_hops != 0)
-- if (! peer_group_active (peer) || g_peer->gtsm_hops != peer->gtsm_hops)
-- vty_out (vty, " neighbor %s ttl-security hops %d%s", addr,
-- peer->gtsm_hops, VTY_NEWLINE);
-+ /* TTL option */
-+ if (peer->gtsm_hops && ! peer_group_active (peer))
-+ vty_out (vty, " neighbor %s ttl-security hops %d%s", addr,
-+ peer->gtsm_hops, VTY_NEWLINE);
-+ else if (peer->ttl && ! peer_group_active (peer))
-+ vty_out (vty, " neighbor %s ebgp-multihop %d%s", addr, peer->ttl,
-+ VTY_NEWLINE);
-
- /* disable-connected-check. */
- if (CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
-diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
-index 0058b58..a4c608d 100644
---- a/bgpd/bgpd.h
-+++ b/bgpd/bgpd.h
-@@ -832,8 +832,8 @@ enum bgp_clear_type
- #define BGP_ERR_TCPSIG_FAILED -29
- #define BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK -30
- #define BGP_ERR_NO_IBGP_WITH_TTLHACK -31
--#define BGP_ERR_MAX -32
--#define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS -33
-+#define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS -32
-+#define BGP_ERR_MAX -33
-
- extern struct bgp_master *bm;
-
-@@ -913,6 +913,7 @@ extern int peer_rsclient_active (struct peer *);
-
- extern int peer_remote_as (struct bgp *, union sockunion *, as_t *, afi_t, safi_t);
- extern int peer_group_remote_as (struct bgp *, const char *, as_t *);
-+extern int peer_ttl (struct peer *peer);
- extern int peer_delete (struct peer *peer);
- extern int peer_group_delete (struct peer_group *);
- extern int peer_group_remote_as_delete (struct peer_group *);
-@@ -934,7 +935,6 @@ extern int peer_af_flag_unset (struct peer *, afi_t, safi_t, u_int32_t);
- extern int peer_af_flag_check (struct peer *, afi_t, safi_t, u_int32_t);
-
- extern int peer_ebgp_multihop_set (struct peer *, int);
--extern int peer_ebgp_multihop_unset (struct peer *);
-
- extern int peer_description_set (struct peer *, const char *);
- extern int peer_description_unset (struct peer *);
-@@ -996,7 +996,6 @@ extern int peer_clear (struct peer *);
- extern int peer_clear_soft (struct peer *, afi_t, safi_t, enum bgp_clear_type);
-
- extern int peer_ttl_security_hops_set (struct peer *, int);
--extern int peer_ttl_security_hops_unset (struct peer *);
-
- extern void bgp_scan_finish (void);
- #endif /* _QUAGGA_BGPD_H */
---
-2.10.1
-
diff --git a/main/quagga-nhrp/0005-nhrpd-implement-next-hop-resolution-protocol.patch b/main/quagga-nhrp/0005-nhrpd-implement-next-hop-resolution-protocol.patch
deleted file mode 100644
index 2572b3d14f..0000000000
--- a/main/quagga-nhrp/0005-nhrpd-implement-next-hop-resolution-protocol.patch
+++ /dev/null
@@ -1,8279 +0,0 @@
-From dcd3163ce78dedb1f4e7df7a4788bfcac4dd2c72 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
-Date: Fri, 25 Dec 2015 10:32:11 +0200
-Subject: [PATCH 5/5] nhrpd: implement next hop resolution protocol
-
-This provides DMVPN support and integrates to strongSwan. Please read
-README.nhrpd and README.kernel for more details.
----
- Makefile.am | 4 +-
- SERVICES | 1 +
- configure.ac | 27 +-
- lib/log.c | 5 +
- lib/log.h | 3 +-
- lib/memtypes.c | 14 +
- lib/route_types.txt | 2 +
- nhrpd/Makefile.am | 34 ++
- nhrpd/README.kernel | 145 ++++++++
- nhrpd/README.nhrpd | 137 ++++++++
- nhrpd/linux.c | 153 ++++++++
- nhrpd/list.h | 191 ++++++++++
- nhrpd/netlink.c | 398 +++++++++++++++++++++
- nhrpd/netlink.h | 21 ++
- nhrpd/nhrp_cache.c | 341 ++++++++++++++++++
- nhrpd/nhrp_event.c | 280 +++++++++++++++
- nhrpd/nhrp_interface.c | 404 +++++++++++++++++++++
- nhrpd/nhrp_main.c | 246 +++++++++++++
- nhrpd/nhrp_nhs.c | 369 ++++++++++++++++++++
- nhrpd/nhrp_packet.c | 312 +++++++++++++++++
- nhrpd/nhrp_peer.c | 860 +++++++++++++++++++++++++++++++++++++++++++++
- nhrpd/nhrp_protocol.h | 128 +++++++
- nhrpd/nhrp_route.c | 345 ++++++++++++++++++
- nhrpd/nhrp_shortcut.c | 402 +++++++++++++++++++++
- nhrpd/nhrp_vc.c | 217 ++++++++++++
- nhrpd/nhrp_vty.c | 928 +++++++++++++++++++++++++++++++++++++++++++++++++
- nhrpd/nhrpd.h | 440 +++++++++++++++++++++++
- nhrpd/os.h | 5 +
- nhrpd/reqid.c | 49 +++
- nhrpd/resolver.c | 190 ++++++++++
- nhrpd/vici.c | 482 +++++++++++++++++++++++++
- nhrpd/vici.h | 24 ++
- nhrpd/zbuf.c | 219 ++++++++++++
- nhrpd/zbuf.h | 189 ++++++++++
- nhrpd/znl.c | 160 +++++++++
- nhrpd/znl.h | 29 ++
- vtysh/Makefile.am | 1 +
- vtysh/vtysh.c | 1 +
- vtysh/vtysh.h | 5 +-
- zebra/zebra_rib.c | 2 +
- zebra/zebra_rnh.c | 21 +-
- zebra/zebra_vty.c | 2 +
- 44 files changed, 7778 insertions(+), 11 deletions(-)
- create mode 100644 nhrpd/Makefile.am
- create mode 100644 nhrpd/README.kernel
- create mode 100644 nhrpd/README.nhrpd
- create mode 100644 nhrpd/linux.c
- create mode 100644 nhrpd/list.h
- create mode 100644 nhrpd/netlink.c
- create mode 100644 nhrpd/netlink.h
- create mode 100644 nhrpd/nhrp_cache.c
- create mode 100644 nhrpd/nhrp_event.c
- create mode 100644 nhrpd/nhrp_interface.c
- create mode 100644 nhrpd/nhrp_main.c
- create mode 100644 nhrpd/nhrp_nhs.c
- create mode 100644 nhrpd/nhrp_packet.c
- create mode 100644 nhrpd/nhrp_peer.c
- create mode 100644 nhrpd/nhrp_protocol.h
- create mode 100644 nhrpd/nhrp_route.c
- create mode 100644 nhrpd/nhrp_shortcut.c
- create mode 100644 nhrpd/nhrp_vc.c
- create mode 100644 nhrpd/nhrp_vty.c
- create mode 100644 nhrpd/nhrpd.h
- create mode 100644 nhrpd/os.h
- create mode 100644 nhrpd/reqid.c
- create mode 100644 nhrpd/resolver.c
- create mode 100644 nhrpd/vici.c
- create mode 100644 nhrpd/vici.h
- create mode 100644 nhrpd/zbuf.c
- create mode 100644 nhrpd/zbuf.h
- create mode 100644 nhrpd/znl.c
- create mode 100644 nhrpd/znl.h
-
-diff --git a/Makefile.am b/Makefile.am
-index 0cd75be..3dea489 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -1,10 +1,10 @@
- ## Process this file with automake to produce Makefile.in.
-
--SUBDIRS = lib qpb fpm @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ \
-+SUBDIRS = lib qpb fpm @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @NHRPD@ \
- @ISISD@ @PIMD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \
- redhat @SOLARIS@ tests
-
--DIST_SUBDIRS = lib qpb fpm zebra bgpd ripd ripngd ospfd ospf6d \
-+DIST_SUBDIRS = lib qpb fpm zebra bgpd ripd ripngd ospfd ospf6d nhrpd \
- isisd watchquagga vtysh ospfclient doc m4 pkgsrc redhat tests \
- solaris pimd
-
-diff --git a/SERVICES b/SERVICES
-index c69d0c1..0322d45 100644
---- a/SERVICES
-+++ b/SERVICES
-@@ -18,3 +18,4 @@ ospf6d 2606/tcp
- ospfapi 2607/tcp
- isisd 2608/tcp
- pimd 2611/tcp
-+nhrpd 2612/tcp
-diff --git a/configure.ac b/configure.ac
-index f027762..dc41418 100755
---- a/configure.ac
-+++ b/configure.ac
-@@ -76,6 +76,7 @@ AC_PROG_CPP
- AM_PROG_CC_C_O
- AC_PROG_RANLIB
- AC_PROG_EGREP
-+PKG_PROG_PKG_CONFIG
-
- dnl autoconf 2.59 appears not to support AC_PROG_SED
- dnl AC_PROG_SED
-@@ -245,6 +246,8 @@ AC_ARG_ENABLE(ospfd,
- AS_HELP_STRING([--disable-ospfd], [do not build ospfd]))
- AC_ARG_ENABLE(ospf6d,
- AS_HELP_STRING([--disable-ospf6d], [do not build ospf6d]))
-+AC_ARG_ENABLE(nhrpd,
-+ AS_HELP_STRING([--disable-nhrpd], [do not build nhrpd]))
- AC_ARG_ENABLE(watchquagga,
- AS_HELP_STRING([--disable-watchquagga], [do not build watchquagga]))
- AC_ARG_ENABLE(isisd,
-@@ -1186,6 +1189,17 @@ else
- fi
- AM_CONDITIONAL(OSPFD, test "x$OSPFD" = "xospfd")
-
-+if test x"$opsys" != x"gnu-linux"; then
-+ dnl NHRPd works currently with Linux only.
-+ enable_nhrpd="no"
-+fi
-+if test "${enable_nhrpd}" = "no";then
-+ NHRPD=""
-+else
-+ NHRPD="nhrpd"
-+fi
-+AM_CONDITIONAL(NHRPD, test "x$NHRPD" = "xnhrpd")
-+
- if test "${enable_watchquagga}" = "no";then
- WATCHQUAGGA=""
- else
-@@ -1241,6 +1255,7 @@ AC_SUBST(RIPD)
- AC_SUBST(RIPNGD)
- AC_SUBST(OSPFD)
- AC_SUBST(OSPF6D)
-+AC_SUBST(NHRPD)
- AC_SUBST(WATCHQUAGGA)
- AC_SUBST(ISISD)
- AC_SUBST(PIMD)
-@@ -1276,6 +1291,14 @@ AC_SUBST(HAVE_LIBPCREPOSIX)
- AC_SUBST(LIB_REGEX)
-
- dnl ------------------
-+dnl check C-Ares library
-+dnl ------------------
-+if test "${enable_nhrpd}" != "no";then
-+ PKG_CHECK_MODULES([CARES], [libcares])
-+fi
-+
-+
-+dnl ------------------
- dnl check Net-SNMP library
- dnl ------------------
- if test "${enable_snmp}" != ""; then
-@@ -1551,6 +1574,7 @@ AC_DEFINE_UNQUOTED(PATH_RIPNGD_PID, "$quagga_statedir/ripngd.pid",ripngd PID)
- AC_DEFINE_UNQUOTED(PATH_BGPD_PID, "$quagga_statedir/bgpd.pid",bgpd PID)
- AC_DEFINE_UNQUOTED(PATH_OSPFD_PID, "$quagga_statedir/ospfd.pid",ospfd PID)
- AC_DEFINE_UNQUOTED(PATH_OSPF6D_PID, "$quagga_statedir/ospf6d.pid",ospf6d PID)
-+AC_DEFINE_UNQUOTED(PATH_NHRPD_PID, "$quagga_statedir/nhrpd.pid",nhrpd PID)
- AC_DEFINE_UNQUOTED(PATH_ISISD_PID, "$quagga_statedir/isisd.pid",isisd PID)
- AC_DEFINE_UNQUOTED(PATH_PIMD_PID, "$quagga_statedir/pimd.pid",pimd PID)
- AC_DEFINE_UNQUOTED(PATH_WATCHQUAGGA_PID, "$quagga_statedir/watchquagga.pid",watchquagga PID)
-@@ -1561,6 +1585,7 @@ AC_DEFINE_UNQUOTED(RIPNG_VTYSH_PATH, "$quagga_statedir/ripngd.vty",ripng vty soc
- AC_DEFINE_UNQUOTED(BGP_VTYSH_PATH, "$quagga_statedir/bgpd.vty",bgpd vty socket)
- AC_DEFINE_UNQUOTED(OSPF_VTYSH_PATH, "$quagga_statedir/ospfd.vty",ospfd vty socket)
- AC_DEFINE_UNQUOTED(OSPF6_VTYSH_PATH, "$quagga_statedir/ospf6d.vty",ospf6d vty socket)
-+AC_DEFINE_UNQUOTED(NHRP_VTYSH_PATH, "$quagga_statedir/nhrpd.vty",nhrpd vty socket)
- AC_DEFINE_UNQUOTED(ISIS_VTYSH_PATH, "$quagga_statedir/isisd.vty",isisd vty socket)
- AC_DEFINE_UNQUOTED(PIM_VTYSH_PATH, "$quagga_statedir/pimd.vty",pimd vty socket)
- AC_DEFINE_UNQUOTED(DAEMON_VTY_DIR, "$quagga_statedir",daemon vty directory)
-@@ -1588,7 +1613,7 @@ AC_CONFIG_FILES([Makefile lib/Makefile qpb/Makefile zebra/Makefile ripd/Makefile
- ripngd/Makefile bgpd/Makefile ospfd/Makefile watchquagga/Makefile
- ospf6d/Makefile isisd/Makefile vtysh/Makefile
- doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile
-- pimd/Makefile
-+ pimd/Makefile nhrpd/Makefile
- tests/bgpd.tests/Makefile
- tests/libzebra.tests/Makefile
- redhat/Makefile
-diff --git a/lib/log.c b/lib/log.c
-index 42b7717..d437066 100644
---- a/lib/log.c
-+++ b/lib/log.c
-@@ -53,6 +53,7 @@ const char *zlog_proto_names[] =
- "ISIS",
- "PIM",
- "MASC",
-+ "NHRP",
- NULL,
- };
-
-@@ -986,6 +987,8 @@ proto_redistnum(int afi, const char *s)
- return ZEBRA_ROUTE_BGP;
- else if (strncmp (s, "ba", 2) == 0)
- return ZEBRA_ROUTE_BABEL;
-+ else if (strncmp (s, "n", 1) == 0)
-+ return ZEBRA_ROUTE_NHRP;
- }
- if (afi == AFI_IP6)
- {
-@@ -1005,6 +1008,8 @@ proto_redistnum(int afi, const char *s)
- return ZEBRA_ROUTE_BGP;
- else if (strncmp (s, "ba", 2) == 0)
- return ZEBRA_ROUTE_BABEL;
-+ else if (strncmp (s, "n", 1) == 0)
-+ return ZEBRA_ROUTE_NHRP;
- }
- return -1;
- }
-diff --git a/lib/log.h b/lib/log.h
-index 7aa0896..c172009 100644
---- a/lib/log.h
-+++ b/lib/log.h
-@@ -54,7 +54,8 @@ typedef enum
- ZLOG_OSPF6,
- ZLOG_ISIS,
- ZLOG_PIM,
-- ZLOG_MASC
-+ ZLOG_MASC,
-+ ZLOG_NHRP,
- } zlog_proto_t;
-
- /* If maxlvl is set to ZLOG_DISABLED, then no messages will be sent
-diff --git a/lib/memtypes.c b/lib/memtypes.c
-index 8abe99d..ba2bacf 100644
---- a/lib/memtypes.c
-+++ b/lib/memtypes.c
-@@ -280,6 +280,19 @@ struct memory_list memory_list_pim[] =
- { -1, NULL },
- };
-
-+struct memory_list memory_list_nhrp[] =
-+{
-+ { MTYPE_NHRP_IF, "NHRP interface" },
-+ { MTYPE_NHRP_VC, "NHRP virtual connection" },
-+ { MTYPE_NHRP_PEER, "NHRP peer entry" },
-+ { MTYPE_NHRP_CACHE, "NHRP cache entry" },
-+ { MTYPE_NHRP_NHS, "NHRP next hop server" },
-+ { MTYPE_NHRP_REGISTRATION, "NHRP registration entries" },
-+ { MTYPE_NHRP_SHORTCUT, "NHRP shortcut" },
-+ { MTYPE_NHRP_ROUTE, "NHRP routing entry" },
-+ { -1, NULL }
-+};
-+
- struct memory_list memory_list_vtysh[] =
- {
- { MTYPE_VTYSH_CONFIG, "Vtysh configuration", },
-@@ -297,5 +310,6 @@ struct mlist mlists[] __attribute__ ((unused)) = {
- { memory_list_isis, "ISIS" },
- { memory_list_bgp, "BGP" },
- { memory_list_pim, "PIM" },
-+ { memory_list_nhrp, "NHRP" },
- { NULL, NULL},
- };
-diff --git a/lib/route_types.txt b/lib/route_types.txt
-index 1b85607..811d24e 100644
---- a/lib/route_types.txt
-+++ b/lib/route_types.txt
-@@ -60,6 +60,7 @@ ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM"
- ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, "HSLS"
- ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, "OLSR"
- ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel"
-+ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, "NHRP"
-
- ## help strings
- ZEBRA_ROUTE_SYSTEM, "Reserved route type, for internal use only"
-@@ -76,3 +77,4 @@ ZEBRA_ROUTE_PIM, "Protocol Independent Multicast (PIM)"
- ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)"
- ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)"
- ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)"
-+ZEBRA_ROUTE_NHRP, "Next Hop Resolution Protocol (NHRP)"
-diff --git a/nhrpd/Makefile.am b/nhrpd/Makefile.am
-new file mode 100644
-index 0000000..d7e9e3a
---- /dev/null
-+++ b/nhrpd/Makefile.am
-@@ -0,0 +1,34 @@
-+## Process this file with automake to produce Makefile.in.
-+
-+AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib -DQUAGGA_NO_DEPRECATED_INTERFACES
-+DEFS = @DEFS@ @CARES_CFLAGS@ -DSYSCONFDIR=\"$(sysconfdir)/\"
-+INSTALL_SDATA=@INSTALL@ -m 600
-+
-+AM_CFLAGS = $(PICFLAGS) #$(WERROR)
-+AM_LDFLAGS = $(PICLDFLAGS)
-+
-+sbin_PROGRAMS = nhrpd
-+
-+nhrpd_SOURCES = \
-+ zbuf.c \
-+ znl.c \
-+ resolver.c \
-+ linux.c \
-+ netlink.c \
-+ vici.c \
-+ reqid.c \
-+ nhrp_event.c \
-+ nhrp_packet.c \
-+ nhrp_interface.c \
-+ nhrp_vc.c \
-+ nhrp_peer.c \
-+ nhrp_cache.c \
-+ nhrp_nhs.c \
-+ nhrp_route.c \
-+ nhrp_shortcut.c \
-+ nhrp_vty.c \
-+ nhrp_main.c
-+
-+nhrpd_LDADD = ../lib/libzebra.la @LIBCAP@ @CARES_LIBS@
-+
-+#dist_examples_DATA = nhrpd.conf.sample
-diff --git a/nhrpd/README.kernel b/nhrpd/README.kernel
-new file mode 100644
-index 0000000..5831316
---- /dev/null
-+++ b/nhrpd/README.kernel
-@@ -0,0 +1,145 @@
-+KERNEL REQUIREMENTS
-+===================
-+
-+The linux kernel has had various major regressions, performance
-+issues and subtle bugs (especially in pmtu). Here is a short list
-+of some -stable kernels and the first point release that is supposedly
-+working well with opennhrp/dmvpn:
-+ 3.12.8 or later
-+ 3.14.54 or later
-+ 3.18.22 or later[1]
-+
-+[1] But you need to apply the following two backported commits:
-+ 3cdaa5be9e ipv4: Don't increase PMTU with Datagram Too Big message
-+ cb6ccf09d6 route: Use ipv4_mtu instead of raw rt_pmtu
-+
-+See below for list of known issues in various kernel versions.
-+
-+Kernels earlier than 3.12 need CONFIG_ARPD enabled in the configuration.
-+Many distributions do not enable it by default, and you may need to
-+compile your own kernel.
-+
-+KERNEL BUGS
-+===========
-+
-+DMVPN and mGRE support in the kernel has been brittle. There are various
-+regressions in multiple kernel versions.
-+
-+This list tries to collect them to one source of information:
-+
-+- forward pmtu is disabled intentionally (but tunnel devices rely on it)
-+ Broken since 3.14-rc1:
-+ commit "ipv4: introduce ip_dst_mtu_maybe_forward and protect forwarding path against pmtu spoofing"
-+ Workaround:
-+ Set sysctl net.ipv4.ip_forward_use_pmtu=1
-+ (Should fix kernel to have this by default on for tunnel devices)
-+
-+- subtle path mtu mishandling issues
-+ Broken since (uncertain)
-+ Fixed in 4.1-rc2:
-+ commit "ipv4: Don't increase PMTU with Datagram Too Big message."
-+ commit "route: Use ipv4_mtu instead of raw rt_pmtu"
-+
-+- fragmentation of large packets inside tunnel not working
-+ Broken since 3.11-rc1
-+ commit "ip_tunnels: Use skb-len to PMTU check."
-+ Fixed in 3.14.54, 3.18.22, 4.1.9, 4.2-rc3
-+ commit "ip_tunnel: fix ipv4 pmtu check to honor inner ip header df"
-+
-+- ipsec will crash during xfrm gc
-+ Broke since 3.15-rc1
-+ commit "flowcache: Make flow cache name space aware"
-+ Fixed in 3.18.10, 4.0
-+ commit "flowcache: Fix kernel panic in flow_cache_flush_task"
-+
-+- TSO on GRE tunnels failed, and resulted in very slow performance
-+ Broke since 3.14.24, 3.18-rc3
-+ commit "gre: Use inner mac length when computing tunnel length"
-+ Fixed in 3.14.30, 3.18.4
-+ commit "gre: fix the inner mac header in nbma tunnel xmit path"
-+ commit "gre: Set inner mac header in gro complete"
-+
-+- NAPI GRO handling was broken; causing immediate crash (32-bit only?)
-+ Broken since 3.13-rc1
-+ commit "net: gro: allow to build full sized skb"
-+ Fixed 3.14.5, 3.15-rc7
-+ commit "net: gro: make sure skb->cb[] initial content has not to be zero"
-+
-+- ip_gre dst caching broke NBMA GRE tunnels
-+ Broken since 3.14-rc1
-+ Fixed in 3.14.5, 3.15-rc6
-+ commit "ipv4: ip_tunnels: disable cache for nbma gre tunnels"
-+
-+- Few packets can be lost when neighbor entry is in NUD_PROBE state,
-+ and there is continuous traffic to it.
-+ Broken since dawn of time
-+ Fixed in 3.15-rc1
-+ commit "neigh: probe application via netlink in NUD_PROBE"
-+
-+- GRO was implemented for GRE, but the hw capabilities were not updated
-+ correctly. In practice forwarding from non-GRE (physical) interface
-+ to GRE interface with gro/gso/tx offloads enabled (also on the target
-+ interface) does not work properly.
-+ Broken around 3.9 to 3.11, need to check details.
-+
-+- recvfrom() returned incorrect NBMA address, breaking NAT detection
-+ Broken since 3.10-rc1
-+ commit "GRE: Refactor GRE tunneling code."
-+ Fixed in 3.10.27, 3.12.8, 3.13-rc7
-+ commit "ip_gre: fix msg_name parsing for recvfrom/recvmsg"
-+
-+- sendto() was broken causing opennhrp not work at all
-+ Broken since 3.10-rc1
-+ commit "GRE: Refactor GRE tunneling code."
-+ Fixed in 3.10.12, 3.11-rc6
-+ commit "ip_gre: fix ipgre_header to return correct offset"
-+
-+- PMTU was broken due to GRE driver rewrite
-+ Broken since 3.10-rc1
-+ commit "GRE: Refactor GRE tunneling code."
-+ Fixed in 3.11-rc1
-+ commit "ip_tunnels: Use skb-len to PMTU check."
-+
-+- PMTU was broken due to routing cache removal
-+ Broken since 3.6-rc1
-+ commit "ipv4: Cache input routes in fib_info nexthops"
-+ Fixed in 3.11-rc1
-+ commit "ipv4: use next hop exceptions also for input routes"
-+ + 3 other commits
-+ Patches exist for 3.10, but they were not approved to 3.10-stable.
-+
-+- Race condition during bootup: changing ARP flag did not flush
-+ existing neighbor entries, causing problems if traffic was routed
-+ to gre interface before opennhrp was running.
-+ Broken since dawn of time
-+ Fixed in 3.11-rc1
-+ commit "arp: flush arp cache on IFF_NOARP change"
-+
-+- Crash in IPsec
-+ Broken since 3.9-rc1
-+ commit "xfrm: removes a superfluous check and add a statistic"
-+ Fixed in 3.10-rc3
-+ commit "xfrm: properly handle invalid states as an error"
-+
-+- An incorrect ip_gre change broke NHRP traffic over GRE
-+ Broken since 3.8-rc2
-+ commit "ip_gre: make ipgre_tunnel_xmit() not parse network header as IP unconditionally"
-+ Fixed in 3.8.5, 3.9-rc4
-+ commit "Revert "ip_gre: make ipgre_tunnel_xmit() not parse network header as IP unconditionally""
-+
-+- Multicast traffic over mGRE was broken.
-+ Broken since 2.6.34-rc2
-+ commit "gre: fix hard header destination address checking"
-+ Fixed in 2.6.39-rc2
-+ commit "net: gre: provide multicast mappings for ipv4 and ipv6"
-+
-+- Serious performance issues causing small throughput on medium to large DMVPN networks
-+ Broken since dawn of time
-+ Fixed in 2.6.35
-+ multiple commits rewriting ipsec caching
-+
-+- Even though around 2.6.24 is the first version where opennhrp started
-+ to work, there has been various PMTU, performance, and functionality
-+ bugs before 2.6.34. That's one of the first version I consider stable
-+ wrt. to opennhrp functionality.
-+
-diff --git a/nhrpd/README.nhrpd b/nhrpd/README.nhrpd
-new file mode 100644
-index 0000000..569b3f4
---- /dev/null
-+++ b/nhrpd/README.nhrpd
-@@ -0,0 +1,137 @@
-+Quagga / NHRP Design and Configuration Notes
-+============================================
-+
-+Quagga/NHRP is an NHRP (RFC2332) implementation for Linux. The primary
-+use case is to implement DMVPN. The aim is thus to be compatible with
-+Cisco DMVPN (and potentially with FlexVPN in the future).
-+
-+
-+Current Status
-+--------------
-+
-+- IPsec integration with strongSwan (requires patched strongSwan)
-+- IPv4 over IPv4 NBMA GRE
-+- IPv6 over IPv4 NBMA GRE -- majority of code exist; but is not tested
-+- Spoke (NHC) functionality complete
-+- Hub (NHS) functionality complete
-+- Multicast support is not done yet
-+ (so OSPF will not work, use BGP for now)
-+
-+The code is not (yet) compatible with Cisco FlexVPN style DMVPN. It
-+would require relaying IKEv2 routing messages from strongSwan to nhrpd
-+and parsing that. It is doable, but not implemented for the time being.
-+
-+
-+Routing Design
-+--------------
-+
-+In contrast to opennhrp routing design, Quagga/NHRP routes each NHRP
-+domain address individually (similar to Cisco FlexVPN).
-+
-+To create NBMA GRE tunnel you might use following:
-+ ip tunnel add gre1 mode gre key 42 ttl 64 dev eth0
-+ ip addr add 10.255.255.2/32 dev gre1
-+ ip link set gre1 up
-+
-+This has two important differences compared to opennhrp setup:
-+ 1. The 'tunnel add' now specifies physical device binding. Quagga/NHRP
-+ wants to know stable protocol address to NBMA address mapping. Thus,
-+ add 'dev <physdev>' binding, or specify 'local <nbma-address>'. If
-+ neither of this is specified, NHRP will not be enabled on the interface.
-+ Alternatively you can skip 'dev' binding on tunnel if you allow
-+ nhrpd to manage it using 'tunnel source' command (see below).
-+
-+ 2. The 'addr add' now has host prefix. In opennhrp you would have used
-+ the GRE subnet prefix length here instead, e.g. /24.
-+
-+Quagga/NHRP will automatically create additional host routes pointing to
-+gre1 when a connection with these hosts is established. The gre1 subnet
-+should be announced by routing protocol. This allows routing protocol
-+to decide which is the closest hub and get the gre addresses' traffic.
-+
-+The second benefit is that hubs can then easily exchange host prefixes
-+of directly connected gre addresses. And thus routing of gre addresses
-+inside hubs is based on routing protocol's shortest path choice -- not
-+on random choice from next hop server list.
-+
-+
-+Configuring nhrpd
-+-----------------
-+
-+The configuration is done using vtysh, and most commands do what they
-+do in Cisco. As minimal configuration example one can do:
-+ configure terminal
-+ interface gre1
-+ tunnel protection vici profile dmvpn
-+ tunnel source eth0
-+ ip nhrp network-id 1
-+ ip nhrp shortcut
-+ ip nhrp registration no-unique
-+ ip nhrp nhs dynamic nbma hubs.example.com
-+
-+There's important notes about the "ip nhrp nhs" command:
-+
-+ 1. The 'dynamic' works only against Cisco (or nhrpd), but is not
-+ compatible with opennhrp. To use dynamic detection of opennhrp hub's
-+ protocol address use the GRE broadcast address there. For the above
-+ example of 10.255.255.0/24 the configuration should read instead:
-+ ip nhrp nhs 10.255.255.255 nbma hubs.example.com
-+
-+ 2. nbma <FQDN> works like opennhrp dynamic-map. That is, all of the
-+ A-records are configured as NBMA addresses of different hubs, and
-+ each hub protocol address will be dynamically detected.
-+
-+
-+Hub functionality
-+-----------------
-+
-+Sending Traffic Indication (redirect) notifications is now accomplished
-+using NFLOG.
-+
-+Use:
-+iptables -A FORWARD -i gre1 -o gre1 \
-+ -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \
-+ --hashlimit-mode srcip,dstip --hashlimit-srcmask 16 --hashlimit-dstmask 16 \
-+ --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128
-+
-+or similar to get rate-limited samples of the packets that match traffic
-+flow needing redirection. This kernel NFLOG target's nflog-group is configured
-+in global nhrp config with:
-+ nhrp nflog-group 1
-+
-+To start sending these traffic notices out from hubs, use the nhrp per-interface
-+directive:
-+ ip nhrp redirect
-+
-+opennhrp used PF_PACKET and tried to create packet filter to get only
-+the packets of interest. Though, this was bad if shortcut fails to
-+establish (remote policy, or both are behind NAT or restrictive
-+firewalls), all of the relayaed traffic would match always.
-+
-+
-+Getting information via vtysh
-+-----------------------------
-+
-+Some commands of interest:
-+ - show dmvpn
-+ - show ip nhrp cache
-+ - show ip nhrp shortcut
-+ - show ip route nhrp
-+ - clear ip nhrp cache
-+ - clear ip nhrp shortcut
-+
-+
-+Integration with strongSwan
-+---------------------------
-+
-+Contrary to opennhrp, Quagga/NHRP has tight integration with IKE daemon.
-+Currently strongSwan is supported using the VICI protocol. strongSwan
-+is connected using UNIX socket (hardcoded now as /var/run/charon.vici).
-+Thus nhrpd needs to be run as user that can open that file.
-+
-+Currently, you will need patched strongSwan. The working tree is at:
-+ http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras
-+
-+And the branch with patches against latest release are:
-+ http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras-release
-+
-diff --git a/nhrpd/linux.c b/nhrpd/linux.c
-new file mode 100644
-index 0000000..1e9c69e
---- /dev/null
-+++ b/nhrpd/linux.c
-@@ -0,0 +1,153 @@
-+/* NHRP daemon Linux specific glue
-+ * 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 <fcntl.h>
-+#include <stdio.h>
-+#include <unistd.h>
-+#include <string.h>
-+#include <sys/ioctl.h>
-+#include <sys/socket.h>
-+#include <sys/types.h>
-+#include <asm/types.h>
-+#include <arpa/inet.h>
-+#include <linux/netlink.h>
-+#include <linux/rtnetlink.h>
-+#include <linux/ip.h>
-+#include <linux/if_arp.h>
-+#include <linux/if_tunnel.h>
-+
-+#include "nhrp_protocol.h"
-+#include "os.h"
-+#include "netlink.h"
-+
-+static int nhrp_socket_fd = -1;
-+
-+int os_socket(void)
-+{
-+ if (nhrp_socket_fd < 0)
-+ nhrp_socket_fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_NHRP));
-+ return nhrp_socket_fd;
-+}
-+
-+int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr, size_t addrlen)
-+{
-+ struct sockaddr_ll lladdr;
-+ struct iovec iov = {
-+ .iov_base = (void*) buf,
-+ .iov_len = len,
-+ };
-+ struct msghdr msg = {
-+ .msg_name = &lladdr,
-+ .msg_namelen = sizeof(lladdr),
-+ .msg_iov = &iov,
-+ .msg_iovlen = 1,
-+ };
-+ int status;
-+
-+ if (addrlen > sizeof(lladdr.sll_addr))
-+ return -1;
-+
-+ memset(&lladdr, 0, sizeof(lladdr));
-+ lladdr.sll_family = AF_PACKET;
-+ lladdr.sll_protocol = htons(ETH_P_NHRP);
-+ lladdr.sll_ifindex = ifindex;
-+ lladdr.sll_halen = addrlen;
-+ memcpy(lladdr.sll_addr, addr, addrlen);
-+
-+ status = sendmsg(nhrp_socket_fd, &msg, 0);
-+ if (status < 0)
-+ return -1;
-+
-+ return 0;
-+}
-+
-+int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr, size_t *addrlen)
-+{
-+ struct sockaddr_ll lladdr;
-+ struct iovec iov = {
-+ .iov_base = buf,
-+ .iov_len = *len,
-+ };
-+ struct msghdr msg = {
-+ .msg_name = &lladdr,
-+ .msg_namelen = sizeof(lladdr),
-+ .msg_iov = &iov,
-+ .msg_iovlen = 1,
-+ };
-+ int r;
-+
-+ r = recvmsg(nhrp_socket_fd, &msg, MSG_DONTWAIT);
-+ if (r < 0)
-+ return r;
-+
-+ *len = r;
-+ *ifindex = lladdr.sll_ifindex;
-+
-+ if (*addrlen <= (size_t) lladdr.sll_addr) {
-+ if (memcmp(lladdr.sll_addr, "\x00\x00\x00\x00", 4) != 0) {
-+ memcpy(addr, lladdr.sll_addr, lladdr.sll_halen);
-+ *addrlen = lladdr.sll_halen;
-+ } else {
-+ *addrlen = 0;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static int linux_configure_arp(const char *iface, int on)
-+{
-+ struct ifreq ifr;
-+
-+ strncpy(ifr.ifr_name, iface, IFNAMSIZ);
-+ if (ioctl(nhrp_socket_fd, SIOCGIFFLAGS, &ifr))
-+ return -1;
-+
-+ if (on)
-+ ifr.ifr_flags &= ~IFF_NOARP;
-+ else
-+ ifr.ifr_flags |= IFF_NOARP;
-+
-+ if (ioctl(nhrp_socket_fd, SIOCSIFFLAGS, &ifr))
-+ return -1;
-+
-+ return 0;
-+}
-+
-+static int linux_icmp_redirect_off(const char *iface)
-+{
-+ char fname[256];
-+ int fd, ret = -1;
-+
-+ sprintf(fname, "/proc/sys/net/ipv4/conf/%s/send_redirects", iface);
-+ fd = open(fname, O_WRONLY);
-+ if (fd < 0)
-+ return -1;
-+ if (write(fd, "0\n", 2) == 2)
-+ ret = 0;
-+ close(fd);
-+
-+ return ret;
-+}
-+
-+int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af)
-+{
-+ int ret = -1;
-+
-+ switch (af) {
-+ case AF_INET:
-+ ret = linux_icmp_redirect_off("all");
-+ ret |= linux_icmp_redirect_off(ifname);
-+ ret |= netlink_configure_arp(ifindex, AF_INET);
-+ ret |= linux_configure_arp(ifname, 1);
-+ break;
-+ }
-+
-+ return ret;
-+}
-diff --git a/nhrpd/list.h b/nhrpd/list.h
-new file mode 100644
-index 0000000..32f21ed
---- /dev/null
-+++ b/nhrpd/list.h
-@@ -0,0 +1,191 @@
-+/* Linux kernel style list handling function
-+ *
-+ * Written from scratch by Timo Teräs <timo.teras@iki.fi>, but modeled
-+ * after the linux kernel code.
-+ *
-+ * 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.
-+ */
-+
-+#ifndef LIST_H
-+#define LIST_H
-+
-+#ifndef NULL
-+#define NULL 0L
-+#endif
-+
-+#ifndef offsetof
-+#ifdef __compiler_offsetof
-+#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
-+#else
-+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-+#endif
-+#endif
-+
-+#ifndef container_of
-+#define container_of(ptr, type, member) ({ \
-+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
-+ (type *)( (char *)__mptr - offsetof(type,member) );})
-+#endif
-+
-+struct hlist_head {
-+ struct hlist_node *first;
-+};
-+
-+struct hlist_node {
-+ struct hlist_node *next;
-+ struct hlist_node **pprev;
-+};
-+
-+static inline int hlist_empty(const struct hlist_head *h)
-+{
-+ return !h->first;
-+}
-+
-+static inline int hlist_hashed(const struct hlist_node *n)
-+{
-+ return n->pprev != NULL;
-+}
-+
-+static inline void hlist_del(struct hlist_node *n)
-+{
-+ struct hlist_node *next = n->next;
-+ struct hlist_node **pprev = n->pprev;
-+
-+ *pprev = next;
-+ if (next)
-+ next->pprev = pprev;
-+
-+ n->next = NULL;
-+ n->pprev = NULL;
-+}
-+
-+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
-+{
-+ struct hlist_node *first = h->first;
-+
-+ n->next = first;
-+ if (first)
-+ first->pprev = &n->next;
-+ n->pprev = &h->first;
-+ h->first = n;
-+}
-+
-+static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *prev)
-+{
-+ n->next = prev->next;
-+ n->pprev = &prev->next;
-+ prev->next = n;
-+}
-+
-+static inline struct hlist_node **hlist_tail_ptr(struct hlist_head *h)
-+{
-+ struct hlist_node *n = h->first;
-+ if (n == NULL)
-+ return &h->first;
-+ while (n->next != NULL)
-+ n = n->next;
-+ return &n->next;
-+}
-+
-+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
-+
-+#define hlist_for_each(pos, head) \
-+ for (pos = (head)->first; pos; pos = pos->next)
-+
-+#define hlist_for_each_safe(pos, n, head) \
-+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); pos = n)
-+
-+#define hlist_for_each_entry(tpos, pos, head, member) \
-+ for (pos = (head)->first; pos && \
-+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
-+ pos = pos->next)
-+
-+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
-+ for (pos = (head)->first; \
-+ pos && ({ n = pos->next; 1; }) && \
-+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
-+ pos = n)
-+
-+
-+struct list_head {
-+ struct list_head *next, *prev;
-+};
-+
-+#define LIST_INITIALIZER(l) { .next = &l, .prev = &l }
-+
-+static inline void list_init(struct list_head *list)
-+{
-+ list->next = list;
-+ list->prev = list;
-+}
-+
-+static inline void __list_add(struct list_head *new,
-+ struct list_head *prev,
-+ struct list_head *next)
-+{
-+ next->prev = new;
-+ new->next = next;
-+ new->prev = prev;
-+ prev->next = new;
-+}
-+
-+static inline void list_add(struct list_head *new, struct list_head *head)
-+{
-+ __list_add(new, head, head->next);
-+}
-+
-+static inline void list_add_tail(struct list_head *new, struct list_head *head)
-+{
-+ __list_add(new, head->prev, head);
-+}
-+
-+static inline void __list_del(struct list_head * prev, struct list_head * next)
-+{
-+ next->prev = prev;
-+ prev->next = next;
-+}
-+
-+static inline void list_del(struct list_head *entry)
-+{
-+ __list_del(entry->prev, entry->next);
-+ entry->next = NULL;
-+ entry->prev = NULL;
-+}
-+
-+static inline int list_hashed(const struct list_head *n)
-+{
-+ return n->next != n && n->next != NULL;
-+}
-+
-+static inline int list_empty(const struct list_head *n)
-+{
-+ return !list_hashed(n);
-+}
-+
-+#define list_next(ptr, type, member) \
-+ (list_hashed(ptr) ? container_of((ptr)->next,type,member) : NULL)
-+
-+#define list_entry(ptr, type, member) container_of(ptr,type,member)
-+
-+#define list_for_each(pos, head) \
-+ for (pos = (head)->next; pos != (head); pos = pos->next)
-+
-+#define list_for_each_safe(pos, n, head) \
-+ for (pos = (head)->next, n = pos->next; pos != (head); \
-+ pos = n, n = pos->next)
-+
-+#define list_for_each_entry(pos, head, member) \
-+ for (pos = list_entry((head)->next, typeof(*pos), member); \
-+ &pos->member != (head); \
-+ pos = list_entry(pos->member.next, typeof(*pos), member))
-+
-+#define list_for_each_entry_safe(pos, n, head, member) \
-+ for (pos = list_entry((head)->next, typeof(*pos), member), \
-+ n = list_entry(pos->member.next, typeof(*pos), member); \
-+ &pos->member != (head); \
-+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
-+
-+#endif
-diff --git a/nhrpd/netlink.c b/nhrpd/netlink.c
-new file mode 100644
-index 0000000..7fe2e01
---- /dev/null
-+++ b/nhrpd/netlink.c
-@@ -0,0 +1,398 @@
-+/* NHRP netlink/neighbor table arpd code
-+ * 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 <fcntl.h>
-+#include <net/if.h>
-+#include <netinet/ip.h>
-+#include <netinet/if_ether.h>
-+#include <linux/netlink.h>
-+#include <linux/neighbour.h>
-+#include <linux/netfilter/nfnetlink_log.h>
-+#include <linux/if_tunnel.h>
-+
-+#include "thread.h"
-+#include "nhrpd.h"
-+#include "netlink.h"
-+#include "znl.h"
-+
-+int netlink_nflog_group;
-+static int netlink_log_fd = -1;
-+static struct thread *netlink_log_thread;
-+static int netlink_req_fd = -1;
-+static int netlink_listen_fd = -1;
-+
-+typedef void (*netlink_dispatch_f)(struct nlmsghdr *msg, struct zbuf *zb);
-+
-+void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma)
-+{
-+ struct nlmsghdr *n;
-+ struct ndmsg *ndm;
-+ struct zbuf *zb = zbuf_alloc(512);
-+
-+ n = znl_nlmsg_push(zb, nbma ? RTM_NEWNEIGH : RTM_DELNEIGH, NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE);
-+ ndm = znl_push(zb, sizeof(*ndm));
-+ *ndm = (struct ndmsg) {
-+ .ndm_family = sockunion_family(proto),
-+ .ndm_ifindex = ifp->ifindex,
-+ .ndm_type = RTN_UNICAST,
-+ .ndm_state = nbma ? NUD_REACHABLE : NUD_FAILED,
-+ };
-+ znl_rta_push(zb, NDA_DST, sockunion_get_addr(proto), family2addrsize(sockunion_family(proto)));
-+ if (nbma)
-+ znl_rta_push(zb, NDA_LLADDR, sockunion_get_addr(nbma), family2addrsize(sockunion_family(nbma)));
-+ znl_nlmsg_complete(zb, n);
-+ zbuf_send(zb, netlink_req_fd);
-+ zbuf_recv(zb, netlink_req_fd);
-+ zbuf_free(zb);
-+}
-+
-+static void netlink_neigh_msg(struct nlmsghdr *msg, struct zbuf *zb)
-+{
-+ struct ndmsg *ndm;
-+ struct rtattr *rta;
-+ struct nhrp_cache *c;
-+ struct interface *ifp;
-+ struct zbuf payload;
-+ union sockunion addr;
-+ size_t len;
-+ char buf[SU_ADDRSTRLEN];
-+ int state;
-+
-+ ndm = znl_pull(zb, sizeof(*ndm));
-+ if (!ndm) return;
-+
-+ sockunion_family(&addr) = AF_UNSPEC;
-+ while ((rta = znl_rta_pull(zb, &payload)) != NULL) {
-+ len = zbuf_used(&payload);
-+ switch (rta->rta_type) {
-+ case NDA_DST:
-+ sockunion_set(&addr, ndm->ndm_family, zbuf_pulln(&payload, len), len);
-+ break;
-+ }
-+ }
-+
-+ ifp = if_lookup_by_index(ndm->ndm_ifindex);
-+ if (!ifp || sockunion_family(&addr) == AF_UNSPEC)
-+ return;
-+
-+ c = nhrp_cache_get(ifp, &addr, 0);
-+ if (!c)
-+ return;
-+
-+ if (msg->nlmsg_type == RTM_GETNEIGH) {
-+ debugf(NHRP_DEBUG_KERNEL, "Netlink: who-has %s dev %s",
-+ sockunion2str(&addr, buf, sizeof buf),
-+ ifp->name);
-+
-+ if (c->cur.type >= NHRP_CACHE_CACHED) {
-+ nhrp_cache_set_used(c, 1);
-+ netlink_update_binding(ifp, &addr, &c->cur.peer->vc->remote.nbma);
-+ }
-+ } else {
-+ debugf(NHRP_DEBUG_KERNEL, "Netlink: update %s dev %s nud %x",
-+ sockunion2str(&addr, buf, sizeof buf),
-+ ifp->name, ndm->ndm_state);
-+
-+ state = (msg->nlmsg_type == RTM_NEWNEIGH) ? ndm->ndm_state : NUD_FAILED;
-+ nhrp_cache_set_used(c, state == NUD_REACHABLE);
-+ }
-+}
-+
-+static int netlink_route_recv(struct thread *t)
-+{
-+ uint8_t buf[ZNL_BUFFER_SIZE];
-+ int fd = THREAD_FD(t);
-+ struct zbuf payload, zb;
-+ struct nlmsghdr *n;
-+
-+ zbuf_init(&zb, buf, sizeof(buf), 0);
-+ while (zbuf_recv(&zb, fd) > 0) {
-+ while ((n = znl_nlmsg_pull(&zb, &payload)) != 0) {
-+ debugf(NHRP_DEBUG_KERNEL, "Netlink: Received msg_type %u, msg_flags %u",
-+ n->nlmsg_type, n->nlmsg_flags);
-+ switch (n->nlmsg_type) {
-+ case RTM_GETNEIGH:
-+ case RTM_NEWNEIGH:
-+ case RTM_DELNEIGH:
-+ netlink_neigh_msg(n, &payload);
-+ break;
-+ }
-+ }
-+ }
-+
-+ thread_add_read(master, netlink_route_recv, 0, fd);
-+
-+ return 0;
-+}
-+
-+static void netlink_log_register(int fd, int group)
-+{
-+ struct nlmsghdr *n;
-+ struct nfgenmsg *nf;
-+ struct nfulnl_msg_config_cmd cmd;
-+ struct zbuf *zb = zbuf_alloc(512);
-+
-+ n = znl_nlmsg_push(zb, (NFNL_SUBSYS_ULOG<<8) | NFULNL_MSG_CONFIG, NLM_F_REQUEST | NLM_F_ACK);
-+ nf = znl_push(zb, sizeof(*nf));
-+ *nf = (struct nfgenmsg) {
-+ .nfgen_family = AF_UNSPEC,
-+ .version = NFNETLINK_V0,
-+ .res_id = htons(group),
-+ };
-+ cmd.command = NFULNL_CFG_CMD_BIND;
-+ znl_rta_push(zb, NFULA_CFG_CMD, &cmd, sizeof(cmd));
-+ znl_nlmsg_complete(zb, n);
-+
-+ zbuf_send(zb, fd);
-+ zbuf_free(zb);
-+}
-+
-+static void netlink_log_indication(struct nlmsghdr *msg, struct zbuf *zb)
-+{
-+ struct nfgenmsg *nf;
-+ struct rtattr *rta;
-+ struct zbuf rtapl, pktpl;
-+ struct interface *ifp;
-+ struct nfulnl_msg_packet_hdr *pkthdr = NULL;
-+ uint32_t *in_ndx = NULL;
-+
-+ nf = znl_pull(zb, sizeof(*nf));
-+ if (!nf) return;
-+
-+ memset(&pktpl, 0, sizeof(pktpl));
-+ while ((rta = znl_rta_pull(zb, &rtapl)) != NULL) {
-+ switch (rta->rta_type) {
-+ case NFULA_PACKET_HDR:
-+ pkthdr = znl_pull(&rtapl, sizeof(*pkthdr));
-+ break;
-+ case NFULA_IFINDEX_INDEV:
-+ in_ndx = znl_pull(&rtapl, sizeof(*in_ndx));
-+ break;
-+ case NFULA_PAYLOAD:
-+ pktpl = rtapl;
-+ break;
-+ /* NFULA_HWHDR exists and is supposed to contain source
-+ * hardware address. However, for ip_gre it seems to be
-+ * the nexthop destination address if the packet matches
-+ * route. */
-+ }
-+ }
-+
-+ if (!pkthdr || !in_ndx || !zbuf_used(&pktpl))
-+ return;
-+
-+ ifp = if_lookup_by_index(htonl(*in_ndx));
-+ if (!ifp)
-+ return;
-+
-+ nhrp_peer_send_indication(ifp, htons(pkthdr->hw_protocol), &pktpl);
-+}
-+
-+static int netlink_log_recv(struct thread *t)
-+{
-+ uint8_t buf[ZNL_BUFFER_SIZE];
-+ int fd = THREAD_FD(t);
-+ struct zbuf payload, zb;
-+ struct nlmsghdr *n;
-+
-+ netlink_log_thread = NULL;
-+
-+ zbuf_init(&zb, buf, sizeof(buf), 0);
-+ while (zbuf_recv(&zb, fd) > 0) {
-+ while ((n = znl_nlmsg_pull(&zb, &payload)) != 0) {
-+ debugf(NHRP_DEBUG_KERNEL, "Netlink-log: Received msg_type %u, msg_flags %u",
-+ n->nlmsg_type, n->nlmsg_flags);
-+ switch (n->nlmsg_type) {
-+ case (NFNL_SUBSYS_ULOG<<8) | NFULNL_MSG_PACKET:
-+ netlink_log_indication(n, &payload);
-+ break;
-+ }
-+ }
-+ }
-+
-+ THREAD_READ_ON(master, netlink_log_thread, netlink_log_recv, 0, netlink_log_fd);
-+
-+ return 0;
-+}
-+
-+void netlink_set_nflog_group(int nlgroup)
-+{
-+ if (netlink_log_fd >= 0) {
-+ THREAD_OFF(netlink_log_thread);
-+ close(netlink_log_fd);
-+ netlink_log_fd = -1;
-+ }
-+ netlink_nflog_group = nlgroup;
-+ if (nlgroup) {
-+ netlink_log_fd = znl_open(NETLINK_NETFILTER, 0);
-+ netlink_log_register(netlink_log_fd, nlgroup);
-+ THREAD_READ_ON(master, netlink_log_thread, netlink_log_recv, 0, netlink_log_fd);
-+ }
-+}
-+
-+int netlink_init(void)
-+{
-+ netlink_req_fd = znl_open(NETLINK_ROUTE, 0);
-+ netlink_listen_fd = znl_open(NETLINK_ROUTE, RTMGRP_NEIGH);
-+ thread_add_read(master, netlink_route_recv, 0, netlink_listen_fd);
-+
-+ return 0;
-+}
-+
-+int netlink_configure_arp(unsigned int ifindex, int pf)
-+{
-+ struct nlmsghdr *n;
-+ struct ndtmsg *ndtm;
-+ struct rtattr *rta;
-+ struct zbuf *zb = zbuf_alloc(512);
-+ int r;
-+
-+ n = znl_nlmsg_push(zb, RTM_SETNEIGHTBL, NLM_F_REQUEST | NLM_F_REPLACE);
-+ ndtm = znl_push(zb, sizeof(*ndtm));
-+ *ndtm = (struct ndtmsg) {
-+ .ndtm_family = pf,
-+ };
-+
-+ znl_rta_push(zb, NDTA_NAME, pf == AF_INET ? "arp_cache" : "ndisc_cache", 10);
-+
-+ rta = znl_rta_nested_push(zb, NDTA_PARMS);
-+ znl_rta_push_u32(zb, NDTPA_IFINDEX, ifindex);
-+ znl_rta_push_u32(zb, NDTPA_APP_PROBES, 1);
-+ znl_rta_push_u32(zb, NDTPA_MCAST_PROBES, 0);
-+ znl_rta_push_u32(zb, NDTPA_UCAST_PROBES, 0);
-+ znl_rta_nested_complete(zb, rta);
-+
-+ znl_nlmsg_complete(zb, n);
-+ r = zbuf_send(zb, netlink_req_fd);
-+ zbuf_recv(zb, netlink_req_fd);
-+ zbuf_free(zb);
-+
-+ return r;
-+}
-+
-+static int __netlink_gre_get_data(struct zbuf *zb, struct zbuf *data, int ifindex)
-+{
-+ struct nlmsghdr *n;
-+ struct ifinfomsg *ifi;
-+ struct zbuf payload, rtapayload;
-+ struct rtattr *rta;
-+
-+ debugf(NHRP_DEBUG_KERNEL, "netlink-link-gre: get-info %u", ifindex);
-+
-+ n = znl_nlmsg_push(zb, RTM_GETLINK, NLM_F_REQUEST);
-+ ifi = znl_push(zb, sizeof(*ifi));
-+ *ifi = (struct ifinfomsg) {
-+ .ifi_index = ifindex,
-+ };
-+ znl_nlmsg_complete(zb, n);
-+
-+ if (zbuf_send(zb, netlink_req_fd) < 0 ||
-+ zbuf_recv(zb, netlink_req_fd) < 0)
-+ return -1;
-+
-+ n = znl_nlmsg_pull(zb, &payload);
-+ if (!n) return -1;
-+
-+ if (n->nlmsg_type != RTM_NEWLINK)
-+ return -1;
-+
-+ ifi = znl_pull(&payload, sizeof(struct ifinfomsg));
-+ if (!ifi)
-+ return -1;
-+
-+ debugf(NHRP_DEBUG_KERNEL, "netlink-link-gre: ifindex %u, receive msg_type %u, msg_flags %u",
-+ ifi->ifi_index, n->nlmsg_type, n->nlmsg_flags);
-+
-+ if (ifi->ifi_index != ifindex)
-+ return -1;
-+
-+ while ((rta = znl_rta_pull(&payload, &rtapayload)) != NULL)
-+ if (rta->rta_type == IFLA_LINKINFO)
-+ break;
-+ if (!rta) return -1;
-+
-+ payload = rtapayload;
-+ while ((rta = znl_rta_pull(&payload, &rtapayload)) != NULL)
-+ if (rta->rta_type == IFLA_INFO_DATA)
-+ break;
-+ if (!rta) return -1;
-+
-+ *data = rtapayload;
-+ return 0;
-+}
-+
-+void netlink_gre_get_info(unsigned int ifindex, uint32_t *gre_key, unsigned int *link_index, struct in_addr *saddr)
-+{
-+ struct zbuf *zb = zbuf_alloc(8192), data, rtapl;
-+ struct rtattr *rta;
-+
-+ *link_index = 0;
-+ *gre_key = 0;
-+ saddr->s_addr = 0;
-+
-+ if (__netlink_gre_get_data(zb, &data, ifindex) < 0)
-+ goto err;
-+
-+ while ((rta = znl_rta_pull(&data, &rtapl)) != NULL) {
-+ switch (rta->rta_type) {
-+ case IFLA_GRE_LINK:
-+ *link_index = zbuf_get32(&rtapl);
-+ break;
-+ case IFLA_GRE_IKEY:
-+ case IFLA_GRE_OKEY:
-+ *gre_key = zbuf_get32(&rtapl);
-+ break;
-+ case IFLA_GRE_LOCAL:
-+ saddr->s_addr = zbuf_get32(&rtapl);
-+ break;
-+ }
-+ }
-+err:
-+ zbuf_free(zb);
-+}
-+
-+void netlink_gre_set_link(unsigned int ifindex, unsigned int link_index)
-+{
-+ struct nlmsghdr *n;
-+ struct ifinfomsg *ifi;
-+ struct rtattr *rta_info, *rta_data, *rta;
-+ struct zbuf *zr = zbuf_alloc(8192), data, rtapl;
-+ struct zbuf *zb = zbuf_alloc(8192);
-+ size_t len;
-+
-+ if (__netlink_gre_get_data(zr, &data, ifindex) < 0)
-+ goto err;
-+
-+ n = znl_nlmsg_push(zb, RTM_NEWLINK, NLM_F_REQUEST);
-+ ifi = znl_push(zb, sizeof(*ifi));
-+ *ifi = (struct ifinfomsg) {
-+ .ifi_index = ifindex,
-+ };
-+ rta_info = znl_rta_nested_push(zb, IFLA_LINKINFO);
-+ znl_rta_push(zb, IFLA_INFO_KIND, "gre", 3);
-+ rta_data = znl_rta_nested_push(zb, IFLA_INFO_DATA);
-+
-+ znl_rta_push_u32(zb, IFLA_GRE_LINK, link_index);
-+ while ((rta = znl_rta_pull(&data, &rtapl)) != NULL) {
-+ if (rta->rta_type == IFLA_GRE_LINK)
-+ continue;
-+ len = zbuf_used(&rtapl);
-+ znl_rta_push(zb, rta->rta_type, zbuf_pulln(&rtapl, len), len);
-+ }
-+
-+ znl_rta_nested_complete(zb, rta_data);
-+ znl_rta_nested_complete(zb, rta_info);
-+
-+ znl_nlmsg_complete(zb, n);
-+ zbuf_send(zb, netlink_req_fd);
-+ zbuf_recv(zb, netlink_req_fd);
-+err:
-+ zbuf_free(zb);
-+ zbuf_free(zr);
-+}
-diff --git a/nhrpd/netlink.h b/nhrpd/netlink.h
-new file mode 100644
-index 0000000..2285093
---- /dev/null
-+++ b/nhrpd/netlink.h
-@@ -0,0 +1,21 @@
-+/* NHRP netlink/neighbor table API
-+ * 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.
-+ */
-+
-+union sockunion;
-+struct interface;
-+
-+extern int netlink_nflog_group;
-+
-+int netlink_init(void);
-+int netlink_configure_arp(unsigned int ifindex, int pf);
-+void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma);
-+void netlink_set_nflog_group(int nlgroup);
-+
-+void netlink_gre_get_info(unsigned int ifindex, uint32_t *gre_key, unsigned int *link_index, struct in_addr *saddr);
-+void netlink_gre_set_link(unsigned int ifindex, unsigned int link_index);
-diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c
-new file mode 100644
-index 0000000..447a814
---- /dev/null
-+++ b/nhrpd/nhrp_cache.c
-@@ -0,0 +1,341 @@
-+/* NHRP cache
-+ * 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 "thread.h"
-+#include "hash.h"
-+#include "nhrpd.h"
-+
-+#include "netlink.h"
-+
-+unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES];
-+
-+const char * const nhrp_cache_type_str[] = {
-+ [NHRP_CACHE_INVALID] = "invalid",
-+ [NHRP_CACHE_INCOMPLETE] = "incomplete",
-+ [NHRP_CACHE_NEGATIVE] = "negative",
-+ [NHRP_CACHE_CACHED] = "cached",
-+ [NHRP_CACHE_DYNAMIC] = "dynamic",
-+ [NHRP_CACHE_NHS] = "nhs",
-+ [NHRP_CACHE_STATIC] = "static",
-+ [NHRP_CACHE_LOCAL] = "local",
-+};
-+
-+static unsigned int nhrp_cache_protocol_key(void *peer_data)
-+{
-+ struct nhrp_cache *p = peer_data;
-+ return sockunion_hash(&p->remote_addr);
-+}
-+
-+static int nhrp_cache_protocol_cmp(const void *cache_data, const void *key_data)
-+{
-+ const struct nhrp_cache *a = cache_data;
-+ const struct nhrp_cache *b = key_data;
-+ return sockunion_same(&a->remote_addr, &b->remote_addr);
-+}
-+
-+static void *nhrp_cache_alloc(void *data)
-+{
-+ struct nhrp_cache *p, *key = data;
-+
-+ p = XMALLOC(MTYPE_NHRP_CACHE, sizeof(struct nhrp_cache));
-+ if (p) {
-+ *p = (struct nhrp_cache) {
-+ .cur.type = NHRP_CACHE_INVALID,
-+ .new.type = NHRP_CACHE_INVALID,
-+ .remote_addr = key->remote_addr,
-+ .ifp = key->ifp,
-+ .notifier_list = NOTIFIER_LIST_INITIALIZER(&p->notifier_list),
-+ };
-+ nhrp_cache_counts[p->cur.type]++;
-+ }
-+
-+ return p;
-+}
-+
-+static void nhrp_cache_free(struct nhrp_cache *c)
-+{
-+ struct nhrp_interface *nifp = c->ifp->info;
-+
-+ zassert(c->cur.type == NHRP_CACHE_INVALID && c->cur.peer == NULL);
-+ zassert(c->new.type == NHRP_CACHE_INVALID && c->new.peer == NULL);
-+ nhrp_cache_counts[c->cur.type]--;
-+ notifier_call(&c->notifier_list, NOTIFY_CACHE_DELETE);
-+ zassert(!notifier_active(&c->notifier_list));
-+ hash_release(nifp->cache_hash, c);
-+ XFREE(MTYPE_NHRP_CACHE, c);
-+}
-+
-+struct nhrp_cache *nhrp_cache_get(struct interface *ifp, union sockunion *remote_addr, int create)
-+{
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_cache key;
-+
-+ if (!nifp->cache_hash) {
-+ nifp->cache_hash = hash_create(nhrp_cache_protocol_key, nhrp_cache_protocol_cmp);
-+ if (!nifp->cache_hash)
-+ return NULL;
-+ }
-+
-+ key.remote_addr = *remote_addr;
-+ key.ifp = ifp;
-+
-+ return hash_get(nifp->cache_hash, &key, create ? nhrp_cache_alloc : NULL);
-+}
-+
-+static int nhrp_cache_do_free(struct thread *t)
-+{
-+ struct nhrp_cache *c = THREAD_ARG(t);
-+ c->t_timeout = NULL;
-+ nhrp_cache_free(c);
-+ return 0;
-+}
-+
-+static int nhrp_cache_do_timeout(struct thread *t)
-+{
-+ struct nhrp_cache *c = THREAD_ARG(t);
-+ c->t_timeout = NULL;
-+ if (c->cur.type != NHRP_CACHE_INVALID)
-+ nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
-+ return 0;
-+}
-+
-+static void nhrp_cache_update_route(struct nhrp_cache *c)
-+{
-+ struct prefix pfx;
-+ struct nhrp_peer *p = c->cur.peer;
-+
-+ sockunion2hostprefix(&c->remote_addr, &pfx);
-+
-+ if (p && nhrp_peer_check(p, 1)) {
-+ netlink_update_binding(p->ifp, &c->remote_addr, &p->vc->remote.nbma);
-+ nhrp_route_announce(1, c->cur.type, &pfx, c->ifp, NULL, c->cur.mtu);
-+ if (c->cur.type >= NHRP_CACHE_DYNAMIC) {
-+ nhrp_route_update_nhrp(&pfx, c->ifp);
-+ c->nhrp_route_installed = 1;
-+ } else if (c->nhrp_route_installed) {
-+ nhrp_route_update_nhrp(&pfx, NULL);
-+ c->nhrp_route_installed = 0;
-+ }
-+ if (!c->route_installed) {
-+ notifier_call(&c->notifier_list, NOTIFY_CACHE_UP);
-+ c->route_installed = 1;
-+ }
-+ } else {
-+ if (c->nhrp_route_installed) {
-+ nhrp_route_update_nhrp(&pfx, NULL);
-+ c->nhrp_route_installed = 0;
-+ }
-+ if (c->route_installed) {
-+ sockunion2hostprefix(&c->remote_addr, &pfx);
-+ notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
-+ nhrp_route_announce(0, c->cur.type, &pfx, NULL, NULL, 0);
-+ c->route_installed = 0;
-+ }
-+ }
-+}
-+
-+static void nhrp_cache_peer_notifier(struct notifier_block *n, unsigned long cmd)
-+{
-+ struct nhrp_cache *c = container_of(n, struct nhrp_cache, peer_notifier);
-+
-+ switch (cmd) {
-+ case NOTIFY_PEER_UP:
-+ nhrp_cache_update_route(c);
-+ break;
-+ case NOTIFY_PEER_DOWN:
-+ case NOTIFY_PEER_IFCONFIG_CHANGED:
-+ notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
-+ nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
-+ break;
-+ case NOTIFY_PEER_NBMA_CHANGING:
-+ if (c->cur.type == NHRP_CACHE_DYNAMIC)
-+ c->cur.peer->vc->abort_migration = 1;
-+ break;
-+ }
-+}
-+
-+static void nhrp_cache_reset_new(struct nhrp_cache *c)
-+{
-+ THREAD_OFF(c->t_auth);
-+ if (list_hashed(&c->newpeer_notifier.notifier_entry))
-+ nhrp_peer_notify_del(c->new.peer, &c->newpeer_notifier);
-+ nhrp_peer_unref(c->new.peer);
-+ memset(&c->new, 0, sizeof(c->new));
-+ c->new.type = NHRP_CACHE_INVALID;
-+}
-+
-+static void nhrp_cache_update_timers(struct nhrp_cache *c)
-+{
-+ THREAD_OFF(c->t_timeout);
-+
-+ switch (c->cur.type) {
-+ case NHRP_CACHE_INVALID:
-+ if (!c->t_auth)
-+ THREAD_TIMER_MSEC_ON(master, c->t_timeout, nhrp_cache_do_free, c, 10);
-+ break;
-+ default:
-+ if (c->cur.expires)
-+ THREAD_TIMER_ON(master, c->t_timeout, nhrp_cache_do_timeout, c, c->cur.expires - recent_relative_time().tv_sec);
-+ break;
-+ }
-+}
-+
-+static void nhrp_cache_authorize_binding(struct nhrp_reqid *r, void *arg)
-+{
-+ struct nhrp_cache *c = container_of(r, struct nhrp_cache, eventid);
-+ char buf[SU_ADDRSTRLEN];
-+
-+ debugf(NHRP_DEBUG_COMMON, "cache: %s %s: %s",
-+ c->ifp->name, sockunion2str(&c->remote_addr, buf, sizeof buf),
-+ (const char *) arg);
-+
-+ nhrp_reqid_free(&nhrp_event_reqid, r);
-+
-+ if (arg && strcmp(arg, "accept") == 0) {
-+ if (c->cur.peer) {
-+ netlink_update_binding(c->cur.peer->ifp, &c->remote_addr, NULL);
-+ nhrp_peer_notify_del(c->cur.peer, &c->peer_notifier);
-+ nhrp_peer_unref(c->cur.peer);
-+ }
-+ nhrp_cache_counts[c->cur.type]--;
-+ nhrp_cache_counts[c->new.type]++;
-+ c->cur = c->new;
-+ c->cur.peer = nhrp_peer_ref(c->cur.peer);
-+ nhrp_cache_reset_new(c);
-+ if (c->cur.peer)
-+ nhrp_peer_notify_add(c->cur.peer, &c->peer_notifier, nhrp_cache_peer_notifier);
-+ nhrp_cache_update_route(c);
-+ notifier_call(&c->notifier_list, NOTIFY_CACHE_BINDING_CHANGE);
-+ } else {
-+ nhrp_cache_reset_new(c);
-+ }
-+
-+ nhrp_cache_update_timers(c);
-+}
-+
-+static int nhrp_cache_do_auth_timeout(struct thread *t)
-+{
-+ struct nhrp_cache *c = THREAD_ARG(t);
-+ c->t_auth = NULL;
-+ nhrp_cache_authorize_binding(&c->eventid, (void *) "timeout");
-+ return 0;
-+}
-+
-+static void nhrp_cache_newpeer_notifier(struct notifier_block *n, unsigned long cmd)
-+{
-+ struct nhrp_cache *c = container_of(n, struct nhrp_cache, newpeer_notifier);
-+
-+ switch (cmd) {
-+ case NOTIFY_PEER_UP:
-+ if (nhrp_peer_check(c->new.peer, 1)) {
-+ evmgr_notify("authorize-binding", c, nhrp_cache_authorize_binding);
-+ THREAD_TIMER_ON(master, c->t_auth, nhrp_cache_do_auth_timeout, c, 10);
-+ }
-+ break;
-+ case NOTIFY_PEER_DOWN:
-+ case NOTIFY_PEER_IFCONFIG_CHANGED:
-+ nhrp_cache_reset_new(c);
-+ break;
-+ }
-+}
-+
-+int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type, int holding_time, struct nhrp_peer *p, uint32_t mtu, union sockunion *nbma_oa)
-+{
-+ if (c->cur.type > type || c->new.type > type) {
-+ nhrp_peer_unref(p);
-+ return 0;
-+ }
-+
-+ /* Sanitize MTU */
-+ switch (sockunion_family(&c->remote_addr)) {
-+ case AF_INET:
-+ if (mtu < 576 || mtu >= 1500)
-+ mtu = 0;
-+ /* Opennhrp announces nbma mtu, but we use protocol mtu.
-+ * This heuristic tries to fix up it. */
-+ if (mtu > 1420) mtu = (mtu & -16) - 80;
-+ break;
-+ default:
-+ mtu = 0;
-+ break;
-+ }
-+
-+ nhrp_cache_reset_new(c);
-+ if (c->cur.type == type && c->cur.peer == p && c->cur.mtu == mtu) {
-+ if (holding_time > 0) c->cur.expires = recent_relative_time().tv_sec + holding_time;
-+ if (nbma_oa) c->cur.remote_nbma_natoa = *nbma_oa;
-+ else memset(&c->cur.remote_nbma_natoa, 0, sizeof c->cur.remote_nbma_natoa);
-+ nhrp_peer_unref(p);
-+ } else {
-+ c->new.type = type;
-+ c->new.peer = p;
-+ c->new.mtu = mtu;
-+ if (nbma_oa) c->new.remote_nbma_natoa = *nbma_oa;
-+
-+ if (holding_time > 0)
-+ c->new.expires = recent_relative_time().tv_sec + holding_time;
-+ else if (holding_time < 0)
-+ c->new.type = NHRP_CACHE_INVALID;
-+
-+ if (c->new.type == NHRP_CACHE_INVALID ||
-+ c->new.type >= NHRP_CACHE_STATIC ||
-+ c->map) {
-+ nhrp_cache_authorize_binding(&c->eventid, (void *) "accept");
-+ } else {
-+ nhrp_peer_notify_add(c->new.peer, &c->newpeer_notifier, nhrp_cache_newpeer_notifier);
-+ nhrp_cache_newpeer_notifier(&c->newpeer_notifier, NOTIFY_PEER_UP);
-+ THREAD_TIMER_ON(master, c->t_auth, nhrp_cache_do_auth_timeout, c, 60);
-+ }
-+ }
-+ nhrp_cache_update_timers(c);
-+
-+ return 1;
-+}
-+
-+void nhrp_cache_set_used(struct nhrp_cache *c, int used)
-+{
-+ c->used = used;
-+ if (c->used)
-+ notifier_call(&c->notifier_list, NOTIFY_CACHE_USED);
-+}
-+
-+struct nhrp_cache_iterator_ctx {
-+ void (*cb)(struct nhrp_cache *, void *);
-+ void *ctx;
-+};
-+
-+static void nhrp_cache_iterator(struct hash_backet *b, void *ctx)
-+{
-+ struct nhrp_cache_iterator_ctx *ic = ctx;
-+ ic->cb(b->data, ic->ctx);
-+}
-+
-+void nhrp_cache_foreach(struct interface *ifp, void (*cb)(struct nhrp_cache *, void *), void *ctx)
-+{
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_cache_iterator_ctx ic = {
-+ .cb = cb,
-+ .ctx = ctx,
-+ };
-+
-+ if (nifp->cache_hash)
-+ hash_iterate(nifp->cache_hash, nhrp_cache_iterator, &ic);
-+}
-+
-+void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *n, notifier_fn_t fn)
-+{
-+ notifier_add(n, &c->notifier_list, fn);
-+}
-+
-+void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *n)
-+{
-+ notifier_del(n);
-+}
-diff --git a/nhrpd/nhrp_event.c b/nhrpd/nhrp_event.c
-new file mode 100644
-index 0000000..aab9ec6
---- /dev/null
-+++ b/nhrpd/nhrp_event.c
-@@ -0,0 +1,280 @@
-+/* NHRP event manager
-+ * 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 <string.h>
-+#include <sys/socket.h>
-+#include <sys/un.h>
-+
-+#include "thread.h"
-+#include "zbuf.h"
-+#include "log.h"
-+#include "nhrpd.h"
-+
-+const char *nhrp_event_socket_path;
-+struct nhrp_reqid_pool nhrp_event_reqid;
-+
-+struct event_manager {
-+ struct thread *t_reconnect, *t_read, *t_write;
-+ struct zbuf ibuf;
-+ struct zbuf_queue obuf;
-+ int fd;
-+ uint8_t ibuf_data[4*1024];
-+};
-+
-+static int evmgr_reconnect(struct thread *t);
-+
-+static void evmgr_connection_error(struct event_manager *evmgr)
-+{
-+ THREAD_OFF(evmgr->t_read);
-+ THREAD_OFF(evmgr->t_write);
-+ zbuf_reset(&evmgr->ibuf);
-+ zbufq_reset(&evmgr->obuf);
-+
-+ if (evmgr->fd >= 0)
-+ close(evmgr->fd);
-+ evmgr->fd = -1;
-+ if (nhrp_event_socket_path)
-+ THREAD_TIMER_MSEC_ON(master, evmgr->t_reconnect, evmgr_reconnect,
-+ evmgr, 10);
-+}
-+
-+static void evmgr_recv_message(struct event_manager *evmgr, struct zbuf *zb)
-+{
-+ struct zbuf zl;
-+ uint32_t eventid = 0;
-+ size_t len;
-+ char buf[256], result[64] = "";
-+
-+ while (zbuf_may_pull_until(zb, "\n", &zl)) {
-+ len = zbuf_used(&zl) - 1;
-+ if (len >= sizeof(buf)-1)
-+ continue;
-+ memcpy(buf, zbuf_pulln(&zl, len), len);
-+ buf[len] = 0;
-+
-+ debugf(NHRP_DEBUG_EVENT, "evmgr: msg: %s", buf);
-+ sscanf(buf, "eventid=%d", &eventid);
-+ sscanf(buf, "result=%63s", result);
-+ }
-+ debugf(NHRP_DEBUG_EVENT, "evmgr: received: eventid=%d result=%s", eventid, result);
-+ if (eventid && result[0]) {
-+ struct nhrp_reqid *r = nhrp_reqid_lookup(&nhrp_event_reqid, eventid);
-+ if (r) r->cb(r, result);
-+ }
-+}
-+
-+static int evmgr_read(struct thread *t)
-+{
-+ struct event_manager *evmgr = THREAD_ARG(t);
-+ struct zbuf *ibuf = &evmgr->ibuf;
-+ struct zbuf msg;
-+
-+ evmgr->t_read = NULL;
-+ if (zbuf_read(ibuf, evmgr->fd, (size_t) -1) < 0) {
-+ evmgr_connection_error(evmgr);
-+ return 0;
-+ }
-+
-+ /* Process all messages in buffer */
-+ while (zbuf_may_pull_until(ibuf, "\n\n", &msg))
-+ evmgr_recv_message(evmgr, &msg);
-+
-+ THREAD_READ_ON(master, evmgr->t_read, evmgr_read, evmgr, evmgr->fd);
-+ return 0;
-+}
-+
-+static int evmgr_write(struct thread *t)
-+{
-+ struct event_manager *evmgr = THREAD_ARG(t);
-+ int r;
-+
-+ evmgr->t_write = NULL;
-+ r = zbufq_write(&evmgr->obuf, evmgr->fd);
-+ if (r > 0) {
-+ THREAD_WRITE_ON(master, evmgr->t_write, evmgr_write, evmgr, evmgr->fd);
-+ } else if (r < 0) {
-+ evmgr_connection_error(evmgr);
-+ }
-+
-+ return 0;
-+}
-+
-+static void evmgr_hexdump(struct zbuf *zb, const uint8_t *val, size_t vallen)
-+{
-+ static const char xd[] = "0123456789abcdef";
-+ size_t i;
-+ char *ptr;
-+
-+ ptr = zbuf_pushn(zb, 2*vallen);
-+ if (!ptr) return;
-+
-+ for (i = 0; i < vallen; i++) {
-+ uint8_t b = val[i];
-+ *(ptr++) = xd[b >> 4];
-+ *(ptr++) = xd[b & 0xf];
-+ }
-+}
-+
-+static void evmgr_put(struct zbuf *zb, const char *fmt, ...)
-+{
-+ const char *pos, *nxt, *str;
-+ const uint8_t *bin;
-+ const union sockunion *su;
-+ int len;
-+ va_list va;
-+
-+ va_start(va, fmt);
-+ for (pos = fmt; (nxt = strchr(pos, '%')) != NULL; pos = nxt + 2) {
-+ zbuf_put(zb, pos, nxt-pos);
-+ switch (nxt[1]) {
-+ case '%':
-+ zbuf_put8(zb, '%');
-+ break;
-+ case 'u':
-+ zb->tail += snprintf((char *) zb->tail, zbuf_tailroom(zb), "%u", va_arg(va, uint32_t));
-+ break;
-+ case 's':
-+ str = va_arg(va, const char *);
-+ zbuf_put(zb, str, strlen(str));
-+ break;
-+ case 'U':
-+ su = va_arg(va, const union sockunion *);
-+ if (sockunion2str(su, (char *) zb->tail, zbuf_tailroom(zb)))
-+ zb->tail += strlen((char *) zb->tail);
-+ else
-+ zbuf_set_werror(zb);
-+ break;
-+ case 'H':
-+ bin = va_arg(va, const uint8_t *);
-+ len = va_arg(va, int);
-+ evmgr_hexdump(zb, bin, len);
-+ break;
-+ }
-+ }
-+ va_end(va);
-+ zbuf_put(zb, pos, strlen(pos));
-+}
-+
-+static void evmgr_submit(struct event_manager *evmgr, struct zbuf *obuf)
-+{
-+ if (obuf->error) {
-+ zbuf_free(obuf);
-+ return;
-+ }
-+ zbuf_put(obuf, "\n", 1);
-+ zbufq_queue(&evmgr->obuf, obuf);
-+ if (evmgr->fd >= 0)
-+ THREAD_WRITE_ON(master, evmgr->t_write, evmgr_write, evmgr, evmgr->fd);
-+}
-+
-+static int evmgr_reconnect(struct thread *t)
-+{
-+ struct event_manager *evmgr = THREAD_ARG(t);
-+ int fd;
-+
-+ evmgr->t_reconnect = NULL;
-+ if (evmgr->fd >= 0 || !nhrp_event_socket_path) return 0;
-+
-+ fd = sock_open_unix(nhrp_event_socket_path);
-+ if (fd < 0) {
-+ zlog_warn("%s: failure connecting nhrp-event socket: %s",
-+ __PRETTY_FUNCTION__, strerror(errno));
-+ zbufq_reset(&evmgr->obuf);
-+ THREAD_TIMER_ON(master, evmgr->t_reconnect, evmgr_reconnect, evmgr, 10);
-+ return 0;
-+ }
-+
-+ zlog_info("Connected to Event Manager");
-+ evmgr->fd = fd;
-+ THREAD_READ_ON(master, evmgr->t_read, evmgr_read, evmgr, evmgr->fd);
-+
-+ return 0;
-+}
-+
-+static struct event_manager evmgr_connection;
-+
-+void evmgr_init(void)
-+{
-+ struct event_manager *evmgr = &evmgr_connection;
-+
-+ evmgr->fd = -1;
-+ zbuf_init(&evmgr->ibuf, evmgr->ibuf_data, sizeof(evmgr->ibuf_data), 0);
-+ zbufq_init(&evmgr->obuf);
-+ THREAD_TIMER_MSEC_ON(master, evmgr->t_reconnect, evmgr_reconnect, evmgr, 10);
-+}
-+
-+void evmgr_set_socket(const char *socket)
-+{
-+ if (nhrp_event_socket_path)
-+ free((char *) nhrp_event_socket_path);
-+ nhrp_event_socket_path = strdup(socket);
-+ evmgr_connection_error(&evmgr_connection);
-+}
-+
-+void evmgr_terminate(void)
-+{
-+}
-+
-+void evmgr_notify(const char *name, struct nhrp_cache *c, void (*cb)(struct nhrp_reqid *, void *))
-+{
-+ struct event_manager *evmgr = &evmgr_connection;
-+ struct nhrp_vc *vc;
-+ struct nhrp_interface *nifp = c->ifp->info;
-+ struct zbuf *zb;
-+ afi_t afi = family2afi(sockunion_family(&c->remote_addr));
-+
-+ if (!nhrp_event_socket_path) {
-+ cb(&c->eventid, (void*) "accept");
-+ return;
-+ }
-+
-+ debugf(NHRP_DEBUG_EVENT, "evmgr: sending event %s", name);
-+
-+ vc = c->new.peer ? c->new.peer->vc : NULL;
-+ zb = zbuf_alloc(1024 + (vc ? (vc->local.certlen + vc->remote.certlen) * 2 : 0));
-+
-+ if (cb) {
-+ nhrp_reqid_free(&nhrp_event_reqid, &c->eventid);
-+ evmgr_put(zb,
-+ "eventid=%u\n",
-+ nhrp_reqid_alloc(&nhrp_event_reqid, &c->eventid, cb));
-+ }
-+
-+ evmgr_put(zb,
-+ "event=%s\n"
-+ "type=%s\n"
-+ "old_type=%s\n"
-+ "num_nhs=%u\n"
-+ "interface=%s\n"
-+ "local_addr=%U\n",
-+ name,
-+ nhrp_cache_type_str[c->new.type],
-+ nhrp_cache_type_str[c->cur.type],
-+ (unsigned int) nhrp_cache_counts[NHRP_CACHE_NHS],
-+ c->ifp->name,
-+ &nifp->afi[afi].addr);
-+
-+ if (vc) {
-+ evmgr_put(zb,
-+ "vc_initiated=%s\n"
-+ "local_nbma=%U\n"
-+ "local_cert=%H\n"
-+ "remote_addr=%U\n"
-+ "remote_nbma=%U\n"
-+ "remote_cert=%H\n",
-+ c->new.peer->requested ? "yes" : "no",
-+ &vc->local.nbma,
-+ vc->local.cert, vc->local.certlen,
-+ &c->remote_addr, &vc->remote.nbma,
-+ vc->remote.cert, vc->remote.certlen);
-+ }
-+
-+ evmgr_submit(evmgr, zb);
-+}
-+
-diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c
-new file mode 100644
-index 0000000..8118927
---- /dev/null
-+++ b/nhrpd/nhrp_interface.c
-@@ -0,0 +1,404 @@
-+/* NHRP interface
-+ * 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 <net/if_arp.h>
-+#include "zebra.h"
-+#include "linklist.h"
-+#include "memory.h"
-+#include "thread.h"
-+
-+#include "nhrpd.h"
-+#include "os.h"
-+#include "netlink.h"
-+
-+static int nhrp_if_new_hook(struct interface *ifp)
-+{
-+ struct nhrp_interface *nifp;
-+ afi_t afi;
-+
-+ nifp = XCALLOC(MTYPE_NHRP_IF, sizeof(struct nhrp_interface));
-+ if (!nifp) return 0;
-+
-+ ifp->info = nifp;
-+ nifp->ifp = ifp;
-+
-+ notifier_init(&nifp->notifier_list);
-+ for (afi = 0; afi < AFI_MAX; afi++) {
-+ struct nhrp_afi_data *ad = &nifp->afi[afi];
-+ ad->holdtime = NHRPD_DEFAULT_HOLDTIME;
-+ list_init(&ad->nhslist_head);
-+ }
-+
-+ return 0;
-+}
-+
-+static int nhrp_if_delete_hook(struct interface *ifp)
-+{
-+ XFREE(MTYPE_NHRP_IF, ifp->info);
-+ return 0;
-+}
-+
-+void nhrp_interface_init(void)
-+{
-+ if_add_hook(IF_NEW_HOOK, nhrp_if_new_hook);
-+ if_add_hook(IF_DELETE_HOOK, nhrp_if_delete_hook);
-+}
-+
-+void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi)
-+{
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_afi_data *if_ad = &nifp->afi[afi];
-+ unsigned short new_mtu;
-+
-+ if (if_ad->configured_mtu < 0)
-+ new_mtu = nifp->nbmaifp ? nifp->nbmaifp->mtu : 0;
-+ else
-+ new_mtu = if_ad->configured_mtu;
-+ if (new_mtu >= 1500)
-+ new_mtu = 0;
-+
-+ if (new_mtu != if_ad->mtu) {
-+ debugf(NHRP_DEBUG_IF, "%s: MTU changed to %d", ifp->name, new_mtu);
-+ if_ad->mtu = new_mtu;
-+ notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_MTU_CHANGED);
-+ }
-+}
-+
-+static void nhrp_interface_update_source(struct interface *ifp)
-+{
-+ struct nhrp_interface *nifp = ifp->info;
-+
-+ if (!nifp->source || !nifp->nbmaifp ||
-+ nifp->linkidx == nifp->nbmaifp->ifindex)
-+ return;
-+
-+ nifp->linkidx = nifp->nbmaifp->ifindex;
-+ debugf(NHRP_DEBUG_IF, "%s: bound device index changed to %d", ifp->name, nifp->linkidx);
-+ netlink_gre_set_link(ifp->ifindex, nifp->linkidx);
-+}
-+
-+static void nhrp_interface_interface_notifier(struct notifier_block *n, unsigned long cmd)
-+{
-+ struct nhrp_interface *nifp = container_of(n, struct nhrp_interface, nbmanifp_notifier);
-+ struct interface *nbmaifp = nifp->nbmaifp;
-+ struct nhrp_interface *nbmanifp = nbmaifp->info;
-+ char buf[SU_ADDRSTRLEN];
-+
-+ switch (cmd) {
-+ case NOTIFY_INTERFACE_CHANGED:
-+ nhrp_interface_update_mtu(nifp->ifp, AFI_IP);
-+ nhrp_interface_update_source(nifp->ifp);
-+ break;
-+ case NOTIFY_INTERFACE_ADDRESS_CHANGED:
-+ nifp->nbma = nbmanifp->afi[AFI_IP].addr;
-+ nhrp_interface_update(nifp->ifp);
-+ notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_NBMA_CHANGED);
-+ debugf(NHRP_DEBUG_IF, "%s: NBMA change: address %s",
-+ nifp->ifp->name,
-+ sockunion2str(&nifp->nbma, buf, sizeof buf));
-+ break;
-+ }
-+}
-+
-+static void nhrp_interface_update_nbma(struct interface *ifp)
-+{
-+ struct nhrp_interface *nifp = ifp->info, *nbmanifp = NULL;
-+ struct interface *nbmaifp = NULL;
-+ union sockunion nbma;
-+
-+ sockunion_family(&nbma) = AF_UNSPEC;
-+
-+ if (nifp->source)
-+ nbmaifp = if_lookup_by_name(nifp->source);
-+
-+ switch (ifp->ll_type) {
-+ case ZEBRA_LLT_IPGRE: {
-+ struct in_addr saddr = {0};
-+ netlink_gre_get_info(ifp->ifindex, &nifp->grekey, &nifp->linkidx, &saddr);
-+ debugf(NHRP_DEBUG_IF, "%s: GRE: %x %x %x", ifp->name, nifp->grekey, nifp->linkidx, saddr.s_addr);
-+ if (saddr.s_addr)
-+ sockunion_set(&nbma, AF_INET, (u_char *) &saddr.s_addr, sizeof(saddr.s_addr));
-+ else if (!nbmaifp && nifp->linkidx != IFINDEX_INTERNAL)
-+ nbmaifp = if_lookup_by_index(nifp->linkidx);
-+ }
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ if (nbmaifp)
-+ nbmanifp = nbmaifp->info;
-+
-+ if (nbmaifp != nifp->nbmaifp) {
-+ if (nifp->nbmaifp)
-+ notifier_del(&nifp->nbmanifp_notifier);
-+ nifp->nbmaifp = nbmaifp;
-+ if (nbmaifp) {
-+ notifier_add(&nifp->nbmanifp_notifier, &nbmanifp->notifier_list, nhrp_interface_interface_notifier);
-+ debugf(NHRP_DEBUG_IF, "%s: bound to %s", ifp->name, nbmaifp->name);
-+ }
-+ }
-+
-+ if (nbmaifp) {
-+ if (sockunion_family(&nbma) == AF_UNSPEC)
-+ nbma = nbmanifp->afi[AFI_IP].addr;
-+ nhrp_interface_update_mtu(ifp, AFI_IP);
-+ nhrp_interface_update_source(ifp);
-+ }
-+
-+ if (!sockunion_same(&nbma, &nifp->nbma)) {
-+ nifp->nbma = nbma;
-+ nhrp_interface_update(nifp->ifp);
-+ debugf(NHRP_DEBUG_IF, "%s: NBMA address changed", ifp->name);
-+ notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_NBMA_CHANGED);
-+ }
-+
-+ nhrp_interface_update(ifp);
-+}
-+
-+static void nhrp_interface_update_address(struct interface *ifp, afi_t afi, int force)
-+{
-+ const int family = afi2family(afi);
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_afi_data *if_ad = &nifp->afi[afi];
-+ struct nhrp_cache *nc;
-+ struct connected *c, *best;
-+ struct listnode *cnode;
-+ union sockunion addr;
-+ char buf[PREFIX_STRLEN];
-+
-+ /* Select new best match preferring primary address */
-+ best = NULL;
-+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
-+ if (PREFIX_FAMILY(c->address) != family)
-+ continue;
-+ if (best == NULL) {
-+ best = c;
-+ continue;
-+ }
-+ if ((best->flags & ZEBRA_IFA_SECONDARY) && !(c->flags & ZEBRA_IFA_SECONDARY)) {
-+ best = c;
-+ continue;
-+ }
-+ if (!(best->flags & ZEBRA_IFA_SECONDARY) && (c->flags & ZEBRA_IFA_SECONDARY))
-+ continue;
-+ if (best->address->prefixlen > c->address->prefixlen) {
-+ best = c;
-+ continue;
-+ }
-+ if (best->address->prefixlen < c->address->prefixlen)
-+ continue;
-+ }
-+
-+ /* On NHRP interfaces a host prefix is required */
-+ if (best && if_ad->configured && best->address->prefixlen != 8 * prefix_blen(best->address)) {
-+ zlog_notice("%s: %s is not a host prefix", ifp->name,
-+ prefix2str(best->address, buf, sizeof buf));
-+ best = NULL;
-+ }
-+
-+ /* Update address if it changed */
-+ if (best)
-+ prefix2sockunion(best->address, &addr);
-+ else
-+ memset(&addr, 0, sizeof(addr));
-+
-+ if (!force && sockunion_same(&if_ad->addr, &addr))
-+ return;
-+
-+ if (sockunion_family(&if_ad->addr) != AF_UNSPEC) {
-+ nc = nhrp_cache_get(ifp, &if_ad->addr, 0);
-+ if (nc) nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, -1, NULL, 0, NULL);
-+ }
-+
-+ debugf(NHRP_DEBUG_KERNEL, "%s: IPv%d address changed to %s",
-+ ifp->name, afi == AFI_IP ? 4 : 6,
-+ best ? prefix2str(best->address, buf, sizeof buf) : "(none)");
-+ if_ad->addr = addr;
-+
-+ if (if_ad->configured && sockunion_family(&if_ad->addr) != AF_UNSPEC) {
-+ nc = nhrp_cache_get(ifp, &addr, 1);
-+ if (nc) nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, 0, NULL, 0, NULL);
-+ }
-+
-+ notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_ADDRESS_CHANGED);
-+}
-+
-+void nhrp_interface_update(struct interface *ifp)
-+{
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_afi_data *if_ad;
-+ afi_t afi;
-+ int enabled = 0;
-+
-+ notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_CHANGED);
-+
-+ for (afi = 0; afi < AFI_MAX; afi++) {
-+ if_ad = &nifp->afi[afi];
-+
-+ if (sockunion_family(&nifp->nbma) == AF_UNSPEC ||
-+ ifp->ifindex == IFINDEX_INTERNAL || !if_is_up(ifp) ||
-+ !if_ad->network_id) {
-+ if (if_ad->configured) {
-+ if_ad->configured = 0;
-+ nhrp_interface_update_address(ifp, afi, 1);
-+ }
-+ continue;
-+ }
-+
-+ if (!if_ad->configured) {
-+ os_configure_dmvpn(ifp->ifindex, ifp->name, afi2family(afi));
-+ if_ad->configured = 1;
-+ nhrp_interface_update_address(ifp, afi, 1);
-+ }
-+
-+ enabled = 1;
-+ }
-+
-+ if (enabled != nifp->enabled) {
-+ nifp->enabled = enabled;
-+ notifier_call(&nifp->notifier_list, enabled ? NOTIFY_INTERFACE_UP : NOTIFY_INTERFACE_DOWN);
-+ }
-+}
-+
-+int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id)
-+{
-+ struct interface *ifp;
-+
-+ /* read and add the interface in the iflist. */
-+ ifp = zebra_interface_add_read(client->ibuf, vrf_id);
-+ if (ifp == NULL)
-+ return 0;
-+
-+ debugf(NHRP_DEBUG_IF, "if-add: %s, ifindex: %u, hw_type: %d %s",
-+ ifp->name, ifp->ifindex,
-+ ifp->ll_type, if_link_type_str(ifp->ll_type));
-+
-+ nhrp_interface_update_nbma(ifp);
-+
-+ return 0;
-+}
-+
-+int nhrp_interface_delete(int cmd, struct zclient *client,
-+ zebra_size_t length, vrf_id_t vrf_id)
-+{
-+ struct interface *ifp;
-+ struct stream *s;
-+
-+ s = client->ibuf;
-+ ifp = zebra_interface_state_read(s, vrf_id);
-+ if (ifp == NULL)
-+ return 0;
-+
-+ debugf(NHRP_DEBUG_IF, "if-delete: %s", ifp->name);
-+ ifp->ifindex = IFINDEX_INTERNAL;
-+ nhrp_interface_update(ifp);
-+ /* if_delete(ifp); */
-+ return 0;
-+}
-+
-+int nhrp_interface_up(int cmd, struct zclient *client,
-+ zebra_size_t length, vrf_id_t vrf_id)
-+{
-+ struct interface *ifp;
-+
-+ ifp = zebra_interface_state_read(client->ibuf, vrf_id);
-+ if (ifp == NULL)
-+ return 0;
-+
-+ debugf(NHRP_DEBUG_IF, "if-up: %s", ifp->name);
-+ nhrp_interface_update_nbma(ifp);
-+
-+ return 0;
-+}
-+
-+int nhrp_interface_down(int cmd, struct zclient *client,
-+ zebra_size_t length, vrf_id_t vrf_id)
-+{
-+ struct interface *ifp;
-+
-+ ifp = zebra_interface_state_read(client->ibuf, vrf_id);
-+ if (ifp == NULL)
-+ return 0;
-+
-+ debugf(NHRP_DEBUG_IF, "if-down: %s", ifp->name);
-+ nhrp_interface_update(ifp);
-+ return 0;
-+}
-+
-+int nhrp_interface_address_add(int cmd, struct zclient *client,
-+ zebra_size_t length, vrf_id_t vrf_id)
-+{
-+ struct connected *ifc;
-+ char buf[PREFIX_STRLEN];
-+
-+ ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id);
-+ if (ifc == NULL)
-+ return 0;
-+
-+ debugf(NHRP_DEBUG_IF, "if-addr-add: %s: %s",
-+ ifc->ifp->name,
-+ prefix2str(ifc->address, buf, sizeof buf));
-+
-+ nhrp_interface_update_address(ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0);
-+
-+ return 0;
-+}
-+
-+int nhrp_interface_address_delete(int cmd, struct zclient *client,
-+ zebra_size_t length, vrf_id_t vrf_id)
-+{
-+ struct connected *ifc;
-+ char buf[PREFIX_STRLEN];
-+
-+ ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id);
-+ if (ifc == NULL)
-+ return 0;
-+
-+ debugf(NHRP_DEBUG_IF, "if-addr-del: %s: %s",
-+ ifc->ifp->name,
-+ prefix2str(ifc->address, buf, sizeof buf));
-+
-+ nhrp_interface_update_address(ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0);
-+ connected_free(ifc);
-+
-+ return 0;
-+}
-+
-+void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn)
-+{
-+ struct nhrp_interface *nifp = ifp->info;
-+ notifier_add(n, &nifp->notifier_list, fn);
-+}
-+
-+void nhrp_interface_notify_del(struct interface *ifp, struct notifier_block *n)
-+{
-+ notifier_del(n);
-+}
-+
-+void nhrp_interface_set_protection(struct interface *ifp, const char *profile, const char *fallback_profile)
-+{
-+ struct nhrp_interface *nifp = ifp->info;
-+
-+ if (nifp->ipsec_profile) free(nifp->ipsec_profile);
-+ nifp->ipsec_profile = profile ? strdup(profile) : NULL;
-+
-+ if (nifp->ipsec_fallback_profile) free(nifp->ipsec_fallback_profile);
-+ nifp->ipsec_fallback_profile = fallback_profile ? strdup(fallback_profile) : NULL;
-+}
-+
-+void nhrp_interface_set_source(struct interface *ifp, const char *ifname)
-+{
-+ struct nhrp_interface *nifp = ifp->info;
-+
-+ if (nifp->source) free(nifp->source);
-+ nifp->source = ifname ? strdup(ifname) : NULL;
-+
-+ nhrp_interface_update_nbma(ifp);
-+}
-diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c
-new file mode 100644
-index 0000000..29349a0
---- /dev/null
-+++ b/nhrpd/nhrp_main.c
-@@ -0,0 +1,246 @@
-+/* NHRP daemon main functions
-+ * 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 <unistd.h>
-+
-+#include "zebra.h"
-+#include "privs.h"
-+#include "getopt.h"
-+#include "thread.h"
-+#include "sigevent.h"
-+#include "version.h"
-+#include "log.h"
-+#include "memory.h"
-+#include "command.h"
-+
-+#include "nhrpd.h"
-+#include "netlink.h"
-+
-+unsigned int debug_flags = 0;
-+
-+struct thread_master *master;
-+struct timeval current_time;
-+static const char *pid_file = PATH_NHRPD_PID;
-+static char config_default[] = SYSCONFDIR NHRP_DEFAULT_CONFIG;
-+static char *config_file = NULL;
-+static char *vty_addr = NULL;
-+static int vty_port = NHRP_VTY_PORT;
-+static int do_daemonise = 0;
-+
-+/* nhrpd options. */
-+struct option longopts[] = {
-+ { "daemon", no_argument, NULL, 'd'},
-+ { "config_file", required_argument, NULL, 'f'},
-+ { "pid_file", required_argument, NULL, 'i'},
-+ { "socket", required_argument, NULL, 'z'},
-+ { "help", no_argument, NULL, 'h'},
-+ { "vty_addr", required_argument, NULL, 'A'},
-+ { "vty_port", required_argument, NULL, 'P'},
-+ { "user", required_argument, NULL, 'u'},
-+ { "group", required_argument, NULL, 'g'},
-+ { "version", no_argument, NULL, 'v'},
-+ { 0 }
-+};
-+
-+/* nhrpd privileges */
-+static zebra_capabilities_t _caps_p [] = {
-+ ZCAP_NET_RAW,
-+ ZCAP_NET_ADMIN,
-+ ZCAP_DAC_OVERRIDE, /* for now needed to write to /proc/sys/net/ipv4/<if>/send_redirect */
-+};
-+
-+static struct zebra_privs_t nhrpd_privs = {
-+#ifdef QUAGGA_USER
-+ .user = QUAGGA_USER,
-+#endif
-+#ifdef QUAGGA_GROUP
-+ .group = QUAGGA_GROUP,
-+#endif
-+#ifdef VTY_GROUP
-+ .vty_group = VTY_GROUP,
-+#endif
-+ .caps_p = _caps_p,
-+ .cap_num_p = ZEBRA_NUM_OF(_caps_p),
-+};
-+
-+static void usage(const char *progname, int status)
-+{
-+ if (status != 0)
-+ fprintf(stderr, "Try `%s --help' for more information.\n", progname);
-+ else
-+ printf(
-+"Usage : %s [OPTION...]\n\
-+Daemon which manages NHRP protocol.\n\n\
-+-d, --daemon Runs in daemon mode\n\
-+-f, --config_file Set configuration file name\n\
-+-i, --pid_file Set process identifier file name\n\
-+-z, --socket Set path of zebra socket\n\
-+-A, --vty_addr Set vty's bind address\n\
-+-P, --vty_port Set vty's port number\n\
-+-u, --user User to run as\n\
-+-g, --group Group to run as\n\
-+-v, --version Print program version\n\
-+-h, --help Display this help and exit\n\
-+\n\
-+Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
-+
-+ exit(status);
-+}
-+
-+static void parse_arguments(const char *progname, int argc, char **argv)
-+{
-+ int opt;
-+
-+ while (1) {
-+ opt = getopt_long(argc, argv, "df:i:z:hA:P:u:g:v", longopts, 0);
-+ if(opt < 0) break;
-+
-+ switch (opt) {
-+ case 0:
-+ break;
-+ case 'd':
-+ do_daemonise = -1;
-+ break;
-+ case 'f':
-+ config_file = optarg;
-+ break;
-+ case 'i':
-+ pid_file = optarg;
-+ break;
-+ case 'z':
-+ zclient_serv_path_set(optarg);
-+ break;
-+ case 'A':
-+ vty_addr = optarg;
-+ break;
-+ case 'P':
-+ vty_port = atoi (optarg);
-+ if (vty_port <= 0 || vty_port > 0xffff)
-+ vty_port = NHRP_VTY_PORT;
-+ break;
-+ case 'u':
-+ nhrpd_privs.user = optarg;
-+ break;
-+ case 'g':
-+ nhrpd_privs.group = optarg;
-+ break;
-+ case 'v':
-+ print_version(progname);
-+ exit(0);
-+ break;
-+ case 'h':
-+ usage(progname, 0);
-+ break;
-+ default:
-+ usage(progname, 1);
-+ break;
-+ }
-+ }
-+}
-+
-+static void nhrp_sigusr1(void)
-+{
-+ zlog_rotate(NULL);
-+}
-+
-+static void nhrp_request_stop(void)
-+{
-+ debugf(NHRP_DEBUG_COMMON, "Exiting...");
-+
-+ nhrp_shortcut_terminate();
-+ nhrp_nhs_terminate();
-+ nhrp_zebra_terminate();
-+ vici_terminate();
-+ evmgr_terminate();
-+ nhrp_vc_terminate();
-+ vrf_terminate();
-+ /* memory_terminate(); */
-+ /* vty_terminate(); */
-+ cmd_terminate();
-+ /* signal_terminate(); */
-+ zprivs_terminate(&nhrpd_privs);
-+
-+ debugf(NHRP_DEBUG_COMMON, "Remove pid file.");
-+ if (pid_file) unlink(pid_file);
-+ debugf(NHRP_DEBUG_COMMON, "Done.");
-+
-+ closezlog(zlog_default);
-+
-+ exit(0);
-+}
-+
-+static struct quagga_signal_t sighandlers[] = {
-+ { .signal = SIGUSR1, .handler = &nhrp_sigusr1, },
-+ { .signal = SIGINT, .handler = &nhrp_request_stop, },
-+ { .signal = SIGTERM, .handler = &nhrp_request_stop, },
-+};
-+
-+int main(int argc, char **argv)
-+{
-+ struct thread thread;
-+ const char *progname;
-+
-+ /* Set umask before anything for security */
-+ umask(0027);
-+ progname = basename(argv[0]);
-+ zlog_default = openzlog(progname, ZLOG_NHRP, LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
-+ zlog_set_level(NULL, ZLOG_DEST_STDOUT, LOG_WARNING);
-+
-+ parse_arguments(progname, argc, argv);
-+
-+ /* Library inits. */
-+ master = thread_master_create();
-+ zprivs_init(&nhrpd_privs);
-+ signal_init(master, array_size(sighandlers), sighandlers);
-+ cmd_init(1);
-+ vty_init(master);
-+ memory_init();
-+ nhrp_interface_init();
-+ vrf_init();
-+ resolver_init();
-+
-+ /* Run with elevated capabilities, as for all netlink activity
-+ * we need privileges anyway. */
-+ nhrpd_privs.change(ZPRIVS_RAISE);
-+
-+ netlink_init();
-+ evmgr_init();
-+ nhrp_vc_init();
-+ nhrp_packet_init();
-+ vici_init();
-+ nhrp_zebra_init();
-+ nhrp_shortcut_init();
-+
-+ nhrp_config_init();
-+
-+ /* Get zebra configuration file. */
-+ zlog_set_level(NULL, ZLOG_DEST_STDOUT, do_daemonise ? ZLOG_DISABLED : LOG_DEBUG);
-+ vty_read_config(config_file, config_default);
-+
-+ if (do_daemonise && daemon(0, 0) < 0) {
-+ zlog_err("daemonise: %s", safe_strerror(errno));
-+ exit (1);
-+ }
-+
-+ /* write pid file */
-+ if (pid_output(pid_file) < 0) {
-+ zlog_err("error while writing pidfile");
-+ exit (1);
-+ }
-+
-+ /* Create VTY socket */
-+ vty_serv_sock(vty_addr, vty_port, NHRP_VTYSH_PATH);
-+ zlog_notice("nhrpd starting: vty@%d", vty_port);
-+
-+ /* Main loop */
-+ while (thread_fetch(master, &thread))
-+ thread_call(&thread);
-+
-+ return 0;
-+}
-diff --git a/nhrpd/nhrp_nhs.c b/nhrpd/nhrp_nhs.c
-new file mode 100644
-index 0000000..d463e06
---- /dev/null
-+++ b/nhrpd/nhrp_nhs.c
-@@ -0,0 +1,369 @@
-+/* NHRP NHC nexthop server functions (registration)
-+ * 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 "zbuf.h"
-+#include "memory.h"
-+#include "thread.h"
-+#include "nhrpd.h"
-+#include "nhrp_protocol.h"
-+
-+static int nhrp_nhs_resolve(struct thread *t);
-+
-+struct nhrp_registration {
-+ struct list_head reglist_entry;
-+ struct thread *t_register;
-+ struct nhrp_nhs *nhs;
-+ struct nhrp_reqid reqid;
-+ unsigned int timeout;
-+ unsigned mark : 1;
-+ union sockunion proto_addr;
-+ struct nhrp_peer *peer;
-+ struct notifier_block peer_notifier;
-+};
-+
-+static int nhrp_reg_send_req(struct thread *t);
-+
-+static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg)
-+{
-+ struct nhrp_packet_parser *p = arg;
-+ struct nhrp_registration *r = container_of(reqid, struct nhrp_registration, reqid);
-+ struct nhrp_nhs *nhs = r->nhs;
-+ struct interface *ifp = nhs->ifp;
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_extension_header *ext;
-+ struct nhrp_cie_header *cie;
-+ struct nhrp_cache *c;
-+ struct zbuf extpl;
-+ union sockunion cie_nbma, cie_proto, *proto;
-+ char buf[64];
-+ int ok = 0, holdtime;
-+
-+ nhrp_reqid_free(&nhrp_packet_reqid, &r->reqid);
-+
-+ if (p->hdr->type != NHRP_PACKET_REGISTRATION_REPLY) {
-+ debugf(NHRP_DEBUG_COMMON, "NHS: Registration failed");
-+ return;
-+ }
-+
-+ debugf(NHRP_DEBUG_COMMON, "NHS: Reg.reply received");
-+
-+ ok = 1;
-+ while ((cie = nhrp_cie_pull(&p->payload, p->hdr, &cie_nbma, &cie_proto)) != NULL) {
-+ proto = sockunion_family(&cie_proto) != AF_UNSPEC ? &cie_proto : &p->src_proto;
-+ debugf(NHRP_DEBUG_COMMON, "NHS: CIE registration: %s: %d",
-+ sockunion2str(proto, buf, sizeof(buf)),
-+ cie->code);
-+ if (!((cie->code == NHRP_CODE_SUCCESS) ||
-+ (cie->code == NHRP_CODE_ADMINISTRATIVELY_PROHIBITED && nhs->hub)))
-+ ok = 0;
-+ }
-+
-+ if (!ok)
-+ return;
-+
-+ /* Parse extensions */
-+ sockunion_family(&nifp->nat_nbma) = AF_UNSPEC;
-+ while ((ext = nhrp_ext_pull(&p->extensions, &extpl)) != NULL) {
-+ switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
-+ case NHRP_EXTENSION_NAT_ADDRESS:
-+ /* NHS adds second CIE if NAT is detected */
-+ if (nhrp_cie_pull(&extpl, p->hdr, &cie_nbma, &cie_proto) &&
-+ nhrp_cie_pull(&extpl, p->hdr, &cie_nbma, &cie_proto)) {
-+ nifp->nat_nbma = cie_nbma;
-+ debugf(NHRP_DEBUG_IF, "%s: NAT detected, real NBMA address: %s",
-+ ifp->name, sockunion2str(&nifp->nbma, buf, sizeof(buf)));
-+ }
-+ break;
-+ }
-+ }
-+
-+ /* Success - schedule next registration, and route NHS */
-+ r->timeout = 2;
-+ holdtime = nifp->afi[nhs->afi].holdtime;
-+ THREAD_OFF(r->t_register);
-+
-+ /* RFC 2332 5.2.3 - Registration is recommend to be renewed
-+ * every one third of holdtime */
-+ THREAD_TIMER_ON(master, r->t_register, nhrp_reg_send_req, r, holdtime / 3);
-+
-+ r->proto_addr = p->dst_proto;
-+ c = nhrp_cache_get(ifp, &p->dst_proto, 1);
-+ if (c) nhrp_cache_update_binding(c, NHRP_CACHE_NHS, holdtime, nhrp_peer_ref(r->peer), 0, NULL);
-+}
-+
-+static int nhrp_reg_timeout(struct thread *t)
-+{
-+ struct nhrp_registration *r = THREAD_ARG(t);
-+ struct nhrp_cache *c;
-+
-+ r->t_register = NULL;
-+
-+ if (r->timeout >= 16 && sockunion_family(&r->proto_addr) != AF_UNSPEC) {
-+ nhrp_reqid_free(&nhrp_packet_reqid, &r->reqid);
-+ c = nhrp_cache_get(r->nhs->ifp, &r->proto_addr, 0);
-+ if (c) nhrp_cache_update_binding(c, NHRP_CACHE_NHS, -1, NULL, 0, NULL);
-+ sockunion_family(&r->proto_addr) = AF_UNSPEC;
-+ }
-+
-+ r->timeout <<= 1;
-+ if (r->timeout > 64) r->timeout = 2;
-+ THREAD_TIMER_MSEC_ON(master, r->t_register, nhrp_reg_send_req, r, 10);
-+
-+ return 0;
-+}
-+
-+static void nhrp_reg_peer_notify(struct notifier_block *n, unsigned long cmd)
-+{
-+ struct nhrp_registration *r = container_of(n, struct nhrp_registration, peer_notifier);
-+ char buf[SU_ADDRSTRLEN];
-+
-+ switch (cmd) {
-+ case NOTIFY_PEER_UP:
-+ case NOTIFY_PEER_DOWN:
-+ case NOTIFY_PEER_IFCONFIG_CHANGED:
-+ case NOTIFY_PEER_MTU_CHANGED:
-+ debugf(NHRP_DEBUG_COMMON, "NHS: Flush timer for %s",
-+ sockunion2str(&r->peer->vc->remote.nbma, buf, sizeof buf));
-+ THREAD_TIMER_OFF(r->t_register);
-+ THREAD_TIMER_MSEC_ON(master, r->t_register, nhrp_reg_send_req, r, 10);
-+ break;
-+ }
-+}
-+
-+static int nhrp_reg_send_req(struct thread *t)
-+{
-+ struct nhrp_registration *r = THREAD_ARG(t);
-+ struct nhrp_nhs *nhs = r->nhs;
-+ char buf1[SU_ADDRSTRLEN], buf2[SU_ADDRSTRLEN];
-+ struct interface *ifp = nhs->ifp;
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_afi_data *if_ad = &nifp->afi[nhs->afi];
-+ union sockunion *dst_proto;
-+ struct zbuf *zb;
-+ struct nhrp_packet_header *hdr;
-+ struct nhrp_extension_header *ext;
-+ struct nhrp_cie_header *cie;
-+
-+ r->t_register = NULL;
-+ if (!nhrp_peer_check(r->peer, 2)) {
-+ debugf(NHRP_DEBUG_COMMON, "NHS: Waiting link for %s",
-+ sockunion2str(&r->peer->vc->remote.nbma, buf1, sizeof buf1));
-+ THREAD_TIMER_ON(master, r->t_register, nhrp_reg_send_req, r, 120);
-+ return 0;
-+ }
-+
-+ THREAD_TIMER_ON(master, r->t_register, nhrp_reg_timeout, r, r->timeout);
-+
-+ /* RFC2332 5.2.3 NHC uses it's own address as dst if NHS is unknown */
-+ dst_proto = &nhs->proto_addr;
-+ if (sockunion_family(dst_proto) == AF_UNSPEC)
-+ dst_proto = &if_ad->addr;
-+
-+ sockunion2str(&if_ad->addr, buf1, sizeof(buf1));
-+ sockunion2str(dst_proto, buf2, sizeof(buf2));
-+ debugf(NHRP_DEBUG_COMMON, "NHS: Register %s -> %s (timeout %d)", buf1, buf2, r->timeout);
-+
-+ /* No protocol address configured for tunnel interface */
-+ if (sockunion_family(&if_ad->addr) == AF_UNSPEC)
-+ return 0;
-+
-+ zb = zbuf_alloc(1400);
-+ hdr = nhrp_packet_push(zb, NHRP_PACKET_REGISTRATION_REQUEST, &nifp->nbma, &if_ad->addr, dst_proto);
-+ hdr->hop_count = 0;
-+ if (!(if_ad->flags & NHRP_IFF_REG_NO_UNIQUE))
-+ hdr->flags |= htons(NHRP_FLAG_REGISTRATION_UNIQUE);
-+
-+ hdr->u.request_id = htonl(nhrp_reqid_alloc(&nhrp_packet_reqid, &r->reqid, nhrp_reg_reply));
-+
-+ /* FIXME: push CIE for each local protocol address */
-+ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL);
-+ cie->prefix_length = 0xff;
-+ cie->holding_time = htons(if_ad->holdtime);
-+ cie->mtu = htons(if_ad->mtu);
-+
-+ nhrp_ext_request(zb, hdr, ifp);
-+
-+ /* Cisco NAT detection extension */
-+ hdr->flags |= htons(NHRP_FLAG_REGISTRATION_NAT);
-+ ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
-+ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &if_ad->addr);
-+ cie->prefix_length = 8 * sockunion_get_addrlen(&nifp->nbma);
-+ nhrp_ext_complete(zb, ext);
-+
-+ nhrp_packet_complete(zb, hdr);
-+ nhrp_peer_send(r->peer, zb);
-+ zbuf_free(zb);
-+
-+ return 0;
-+}
-+
-+static void nhrp_reg_delete(struct nhrp_registration *r)
-+{
-+ nhrp_peer_notify_del(r->peer, &r->peer_notifier);
-+ nhrp_peer_unref(r->peer);
-+ list_del(&r->reglist_entry);
-+ THREAD_OFF(r->t_register);
-+ XFREE(MTYPE_NHRP_REGISTRATION, r);
-+}
-+
-+static struct nhrp_registration *nhrp_reg_by_nbma(struct nhrp_nhs *nhs, const union sockunion *nbma_addr)
-+{
-+ struct nhrp_registration *r;
-+
-+ list_for_each_entry(r, &nhs->reglist_head, reglist_entry)
-+ if (sockunion_same(&r->peer->vc->remote.nbma, nbma_addr))
-+ return r;
-+ return NULL;
-+}
-+
-+static void nhrp_nhs_resolve_cb(struct resolver_query *q, int n, union sockunion *addrs)
-+{
-+ struct nhrp_nhs *nhs = container_of(q, struct nhrp_nhs, dns_resolve);
-+ struct nhrp_interface *nifp = nhs->ifp->info;
-+ struct nhrp_registration *reg, *regn;
-+ int i;
-+
-+ nhs->t_resolve = NULL;
-+ if (n < 0) {
-+ /* Failed, retry in a moment */
-+ THREAD_TIMER_ON(master, nhs->t_resolve, nhrp_nhs_resolve, nhs, 5);
-+ return;
-+ }
-+
-+ THREAD_TIMER_ON(master, nhs->t_resolve, nhrp_nhs_resolve, nhs, 2*60*60);
-+
-+ list_for_each_entry(reg, &nhs->reglist_head, reglist_entry)
-+ reg->mark = 1;
-+
-+ nhs->hub = 0;
-+ for (i = 0; i < n; i++) {
-+ if (sockunion_same(&addrs[i], &nifp->nbma)) {
-+ nhs->hub = 1;
-+ continue;
-+ }
-+
-+ reg = nhrp_reg_by_nbma(nhs, &addrs[i]);
-+ if (reg) {
-+ reg->mark = 0;
-+ continue;
-+ }
-+
-+ reg = XCALLOC(MTYPE_NHRP_REGISTRATION, sizeof(*reg));
-+ reg->peer = nhrp_peer_get(nhs->ifp, &addrs[i]);
-+ reg->nhs = nhs;
-+ reg->timeout = 1;
-+ list_init(&reg->reglist_entry);
-+ list_add_tail(&reg->reglist_entry, &nhs->reglist_head);
-+ nhrp_peer_notify_add(reg->peer, &reg->peer_notifier, nhrp_reg_peer_notify);
-+ THREAD_TIMER_MSEC_ON(master, reg->t_register, nhrp_reg_send_req, reg, 50);
-+ }
-+
-+ list_for_each_entry_safe(reg, regn, &nhs->reglist_head, reglist_entry) {
-+ if (reg->mark)
-+ nhrp_reg_delete(reg);
-+ }
-+}
-+
-+static int nhrp_nhs_resolve(struct thread *t)
-+{
-+ struct nhrp_nhs *nhs = THREAD_ARG(t);
-+
-+ resolver_resolve(&nhs->dns_resolve, AF_INET, nhs->nbma_fqdn, nhrp_nhs_resolve_cb);
-+
-+ return 0;
-+}
-+
-+int nhrp_nhs_add(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn)
-+{
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_nhs *nhs;
-+
-+ if (sockunion_family(proto_addr) != AF_UNSPEC &&
-+ sockunion_family(proto_addr) != afi2family(afi))
-+ return NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH;
-+
-+ list_for_each_entry(nhs, &nifp->afi[afi].nhslist_head, nhslist_entry) {
-+ if (sockunion_family(&nhs->proto_addr) != AF_UNSPEC &&
-+ sockunion_family(proto_addr) != AF_UNSPEC &&
-+ sockunion_same(&nhs->proto_addr, proto_addr))
-+ return NHRP_ERR_ENTRY_EXISTS;
-+
-+ if (strcmp(nhs->nbma_fqdn, nbma_fqdn) == 0)
-+ return NHRP_ERR_ENTRY_EXISTS;
-+ }
-+
-+ nhs = XMALLOC(MTYPE_NHRP_NHS, sizeof(struct nhrp_nhs));
-+ if (!nhs) return NHRP_ERR_NO_MEMORY;
-+
-+ *nhs = (struct nhrp_nhs) {
-+ .afi = afi,
-+ .ifp = ifp,
-+ .proto_addr = *proto_addr,
-+ .nbma_fqdn = strdup(nbma_fqdn),
-+ .reglist_head = LIST_INITIALIZER(nhs->reglist_head),
-+ };
-+ list_add_tail(&nhs->nhslist_entry, &nifp->afi[afi].nhslist_head);
-+ THREAD_TIMER_MSEC_ON(master, nhs->t_resolve, nhrp_nhs_resolve, nhs, 1000);
-+
-+ return NHRP_OK;
-+}
-+
-+int nhrp_nhs_del(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn)
-+{
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_nhs *nhs, *nnhs;
-+ int ret = NHRP_ERR_ENTRY_NOT_FOUND;
-+
-+ if (sockunion_family(proto_addr) != AF_UNSPEC &&
-+ sockunion_family(proto_addr) != afi2family(afi))
-+ return NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH;
-+
-+ list_for_each_entry_safe(nhs, nnhs, &nifp->afi[afi].nhslist_head, nhslist_entry) {
-+ if (!sockunion_same(&nhs->proto_addr, proto_addr))
-+ continue;
-+ if (strcmp(nhs->nbma_fqdn, nbma_fqdn) != 0)
-+ continue;
-+
-+ nhrp_nhs_free(nhs);
-+ ret = NHRP_OK;
-+ }
-+
-+ return ret;
-+}
-+
-+int nhrp_nhs_free(struct nhrp_nhs *nhs)
-+{
-+ struct nhrp_registration *r, *rn;
-+
-+ list_for_each_entry_safe(r, rn, &nhs->reglist_head, reglist_entry)
-+ nhrp_reg_delete(r);
-+ THREAD_OFF(nhs->t_resolve);
-+ list_del(&nhs->nhslist_entry);
-+ free((void*) nhs->nbma_fqdn);
-+ XFREE(MTYPE_NHRP_NHS, nhs);
-+ return 0;
-+}
-+
-+void nhrp_nhs_terminate(void)
-+{
-+ struct interface *ifp;
-+ struct nhrp_interface *nifp;
-+ struct nhrp_nhs *nhs, *tmp;
-+ struct listnode *node;
-+ afi_t afi;
-+
-+ for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) {
-+ nifp = ifp->info;
-+ for (afi = 0; afi < AFI_MAX; afi++) {
-+ list_for_each_entry_safe(nhs, tmp, &nifp->afi[afi].nhslist_head, nhslist_entry)
-+ nhrp_nhs_free(nhs);
-+ }
-+ }
-+}
-diff --git a/nhrpd/nhrp_packet.c b/nhrpd/nhrp_packet.c
-new file mode 100644
-index 0000000..5d2866a
---- /dev/null
-+++ b/nhrpd/nhrp_packet.c
-@@ -0,0 +1,312 @@
-+/* NHRP packet handling functions
-+ * 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 <netinet/if_ether.h>
-+#include "nhrpd.h"
-+#include "zbuf.h"
-+#include "thread.h"
-+#include "hash.h"
-+
-+#include "nhrp_protocol.h"
-+#include "os.h"
-+
-+struct nhrp_reqid_pool nhrp_packet_reqid;
-+
-+static uint16_t family2proto(int family)
-+{
-+ switch (family) {
-+ case AF_INET: return ETH_P_IP;
-+ case AF_INET6: return ETH_P_IPV6;
-+ }
-+ return 0;
-+}
-+
-+static int proto2family(uint16_t proto)
-+{
-+ switch (proto) {
-+ case ETH_P_IP: return AF_INET;
-+ case ETH_P_IPV6: return AF_INET6;
-+ }
-+ return AF_UNSPEC;
-+}
-+
-+struct nhrp_packet_header *nhrp_packet_push(
-+ struct zbuf *zb, uint8_t type,
-+ const union sockunion *src_nbma,
-+ const union sockunion *src_proto,
-+ const union sockunion *dst_proto)
-+{
-+ struct nhrp_packet_header *hdr;
-+
-+ hdr = zbuf_push(zb, struct nhrp_packet_header);
-+ if (!hdr) return NULL;
-+
-+ *hdr = (struct nhrp_packet_header) {
-+ .afnum = htons(family2afi(sockunion_family(src_nbma))),
-+ .protocol_type = htons(family2proto(sockunion_family(src_proto))),
-+ .version = NHRP_VERSION_RFC2332,
-+ .type = type,
-+ .hop_count = 64,
-+ .src_nbma_address_len = sockunion_get_addrlen(src_nbma),
-+ .src_protocol_address_len = sockunion_get_addrlen(src_proto),
-+ .dst_protocol_address_len = sockunion_get_addrlen(dst_proto),
-+ };
-+
-+ zbuf_put(zb, sockunion_get_addr(src_nbma), hdr->src_nbma_address_len);
-+ zbuf_put(zb, sockunion_get_addr(src_proto), hdr->src_protocol_address_len);
-+ zbuf_put(zb, sockunion_get_addr(dst_proto), hdr->dst_protocol_address_len);
-+
-+ return hdr;
-+}
-+
-+struct nhrp_packet_header *nhrp_packet_pull(
-+ struct zbuf *zb,
-+ union sockunion *src_nbma,
-+ union sockunion *src_proto,
-+ union sockunion *dst_proto)
-+{
-+ struct nhrp_packet_header *hdr;
-+
-+ hdr = zbuf_pull(zb, struct nhrp_packet_header);
-+ if (!hdr) return NULL;
-+
-+ sockunion_set(
-+ src_nbma, afi2family(htons(hdr->afnum)),
-+ zbuf_pulln(zb, hdr->src_nbma_address_len + hdr->src_nbma_subaddress_len),
-+ hdr->src_nbma_address_len + hdr->src_nbma_subaddress_len);
-+ sockunion_set(
-+ src_proto, proto2family(htons(hdr->protocol_type)),
-+ zbuf_pulln(zb, hdr->src_protocol_address_len),
-+ hdr->src_protocol_address_len);
-+ sockunion_set(
-+ dst_proto, proto2family(htons(hdr->protocol_type)),
-+ zbuf_pulln(zb, hdr->dst_protocol_address_len),
-+ hdr->dst_protocol_address_len);
-+
-+ return hdr;
-+}
-+
-+uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len)
-+{
-+ const uint16_t *pdu16 = (const uint16_t *) pdu;
-+ uint32_t csum = 0;
-+ int i;
-+
-+ for (i = 0; i < len / 2; i++)
-+ csum += pdu16[i];
-+ if (len & 1)
-+ csum += htons(pdu[len - 1]);
-+
-+ while (csum & 0xffff0000)
-+ csum = (csum & 0xffff) + (csum >> 16);
-+
-+ return (~csum) & 0xffff;
-+}
-+
-+void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr)
-+{
-+ unsigned short size;
-+
-+ if (hdr->extension_offset)
-+ nhrp_ext_push(zb, hdr, NHRP_EXTENSION_END | NHRP_EXTENSION_FLAG_COMPULSORY);
-+
-+ size = zb->tail - (uint8_t *)hdr;
-+ hdr->packet_size = htons(size);
-+ hdr->checksum = 0;
-+ hdr->checksum = nhrp_packet_calculate_checksum((uint8_t *) hdr, size);
-+}
-+
-+struct nhrp_cie_header *nhrp_cie_push(
-+ struct zbuf *zb,
-+ uint8_t code,
-+ const union sockunion *nbma,
-+ const union sockunion *proto)
-+{
-+ struct nhrp_cie_header *cie;
-+
-+ cie = zbuf_push(zb, struct nhrp_cie_header);
-+ *cie = (struct nhrp_cie_header) {
-+ .code = code,
-+ };
-+ if (nbma) {
-+ cie->nbma_address_len = sockunion_get_addrlen(nbma);
-+ zbuf_put(zb, sockunion_get_addr(nbma), cie->nbma_address_len);
-+ }
-+ if (proto) {
-+ cie->protocol_address_len = sockunion_get_addrlen(proto);
-+ zbuf_put(zb, sockunion_get_addr(proto), cie->protocol_address_len);
-+ }
-+
-+ return cie;
-+}
-+
-+struct nhrp_cie_header *nhrp_cie_pull(
-+ struct zbuf *zb,
-+ struct nhrp_packet_header *hdr,
-+ union sockunion *nbma,
-+ union sockunion *proto)
-+{
-+ struct nhrp_cie_header *cie;
-+
-+ cie = zbuf_pull(zb, struct nhrp_cie_header);
-+ if (!cie) return NULL;
-+
-+ if (cie->nbma_address_len + cie->nbma_subaddress_len) {
-+ sockunion_set(
-+ nbma, afi2family(htons(hdr->afnum)),
-+ zbuf_pulln(zb, cie->nbma_address_len + cie->nbma_subaddress_len),
-+ cie->nbma_address_len + cie->nbma_subaddress_len);
-+ } else {
-+ sockunion_family(nbma) = AF_UNSPEC;
-+ }
-+
-+ if (cie->protocol_address_len) {
-+ sockunion_set(
-+ proto, proto2family(htons(hdr->protocol_type)),
-+ zbuf_pulln(zb, cie->protocol_address_len),
-+ cie->protocol_address_len);
-+ } else {
-+ sockunion_family(proto) = AF_UNSPEC;
-+ }
-+
-+ return cie;
-+}
-+
-+struct nhrp_extension_header *nhrp_ext_push(struct zbuf *zb, struct nhrp_packet_header *hdr, uint16_t type)
-+{
-+ struct nhrp_extension_header *ext;
-+ ext = zbuf_push(zb, struct nhrp_extension_header);
-+ if (!ext) return NULL;
-+
-+ if (!hdr->extension_offset)
-+ hdr->extension_offset = htons(zb->tail - (uint8_t*) hdr - sizeof(struct nhrp_extension_header));
-+
-+ *ext = (struct nhrp_extension_header) {
-+ .type = htons(type),
-+ .length = 0,
-+ };
-+ return ext;
-+}
-+
-+void nhrp_ext_complete(struct zbuf *zb, struct nhrp_extension_header *ext)
-+{
-+ ext->length = htons(zb->tail - (uint8_t*)ext - sizeof(struct nhrp_extension_header));
-+}
-+
-+struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb, struct zbuf *payload)
-+{
-+ struct nhrp_extension_header *ext;
-+ uint16_t plen;
-+
-+ ext = zbuf_pull(zb, struct nhrp_extension_header);
-+ if (!ext) return NULL;
-+
-+ plen = htons(ext->length);
-+ zbuf_init(payload, zbuf_pulln(zb, plen), plen, plen);
-+ return ext;
-+}
-+
-+void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp)
-+{
-+ /* Place holders for standard extensions */
-+ nhrp_ext_push(zb, hdr, NHRP_EXTENSION_FORWARD_TRANSIT_NHS | NHRP_EXTENSION_FLAG_COMPULSORY);
-+ nhrp_ext_push(zb, hdr, NHRP_EXTENSION_REVERSE_TRANSIT_NHS | NHRP_EXTENSION_FLAG_COMPULSORY);
-+ nhrp_ext_push(zb, hdr, NHRP_EXTENSION_RESPONDER_ADDRESS | NHRP_EXTENSION_FLAG_COMPULSORY);
-+}
-+
-+int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp, struct nhrp_extension_header *ext, struct zbuf *extpayload)
-+{
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_afi_data *ad = &nifp->afi[htons(hdr->afnum)];
-+ struct nhrp_extension_header *dst;
-+ struct nhrp_cie_header *cie;
-+ uint16_t type;
-+
-+ type = htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY;
-+ if (type == NHRP_EXTENSION_END)
-+ return 0;
-+
-+ dst = nhrp_ext_push(zb, hdr, htons(ext->type));
-+ if (!dst) goto err;
-+
-+ switch (type) {
-+ case NHRP_EXTENSION_RESPONDER_ADDRESS:
-+ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &ad->addr);
-+ if (!cie) goto err;
-+ cie->holding_time = htons(ad->holdtime);
-+ break;
-+ default:
-+ if (type & NHRP_EXTENSION_FLAG_COMPULSORY)
-+ goto err;
-+ case NHRP_EXTENSION_FORWARD_TRANSIT_NHS:
-+ case NHRP_EXTENSION_REVERSE_TRANSIT_NHS:
-+ /* Supported compulsory extensions, and any
-+ * non-compulsory that is not explicitly handled,
-+ * should be just copied. */
-+ zbuf_copy(zb, extpayload, zbuf_used(extpayload));
-+ break;
-+ }
-+ nhrp_ext_complete(zb, dst);
-+ return 0;
-+err:
-+ zbuf_set_werror(zb);
-+ return -1;
-+}
-+
-+static int nhrp_packet_recvraw(struct thread *t)
-+{
-+ int fd = THREAD_FD(t), ifindex;
-+ struct zbuf *zb;
-+ struct interface *ifp;
-+ struct nhrp_peer *p;
-+ union sockunion remote_nbma;
-+ uint8_t addr[64];
-+ size_t len, addrlen;
-+
-+ thread_add_read(master, nhrp_packet_recvraw, 0, fd);
-+
-+ zb = zbuf_alloc(1500);
-+ if (!zb) return 0;
-+
-+ len = zbuf_size(zb);
-+ addrlen = sizeof(addr);
-+ if (os_recvmsg(zb->buf, &len, &ifindex, addr, &addrlen) < 0)
-+ goto err;
-+
-+ zb->head = zb->buf;
-+ zb->tail = zb->buf + len;
-+
-+ switch (addrlen) {
-+ case 4:
-+ sockunion_set(&remote_nbma, AF_INET, addr, addrlen);
-+ break;
-+ default:
-+ goto err;
-+ }
-+
-+ ifp = if_lookup_by_index(ifindex);
-+ if (!ifp) goto err;
-+
-+ p = nhrp_peer_get(ifp, &remote_nbma);
-+ if (!p) goto err;
-+
-+ nhrp_peer_recv(p, zb);
-+ nhrp_peer_unref(p);
-+ return 0;
-+
-+err:
-+ zbuf_free(zb);
-+ return 0;
-+}
-+
-+int nhrp_packet_init(void)
-+{
-+ thread_add_read(master, nhrp_packet_recvraw, 0, os_socket());
-+ return 0;
-+}
-diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c
-new file mode 100644
-index 0000000..45bfd7d
---- /dev/null
-+++ b/nhrpd/nhrp_peer.c
-@@ -0,0 +1,860 @@
-+/* NHRP peer functions
-+ * 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 <netinet/if_ether.h>
-+
-+#include "zebra.h"
-+#include "memory.h"
-+#include "thread.h"
-+#include "hash.h"
-+
-+#include "nhrpd.h"
-+#include "nhrp_protocol.h"
-+#include "os.h"
-+
-+struct ipv6hdr {
-+ uint8_t priority_version;
-+ uint8_t flow_lbl[3];
-+ uint16_t payload_len;
-+ uint8_t nexthdr;
-+ uint8_t hop_limit;
-+ struct in6_addr saddr;
-+ struct in6_addr daddr;
-+};
-+
-+static void nhrp_packet_debug(struct zbuf *zb, const char *dir);
-+
-+static void nhrp_peer_check_delete(struct nhrp_peer *p)
-+{
-+ struct nhrp_interface *nifp = p->ifp->info;
-+
-+ if (p->ref || notifier_active(&p->notifier_list))
-+ return;
-+
-+ THREAD_OFF(p->t_fallback);
-+ hash_release(nifp->peer_hash, p);
-+ nhrp_interface_notify_del(p->ifp, &p->ifp_notifier);
-+ nhrp_vc_notify_del(p->vc, &p->vc_notifier);
-+ XFREE(MTYPE_NHRP_PEER, p);
-+}
-+
-+static int nhrp_peer_notify_up(struct thread *t)
-+{
-+ struct nhrp_peer *p = THREAD_ARG(t);
-+ struct nhrp_vc *vc = p->vc;
-+ struct interface *ifp = p->ifp;
-+ struct nhrp_interface *nifp = ifp->info;
-+
-+ p->t_fallback = NULL;
-+ if (nifp->enabled && (!nifp->ipsec_profile || vc->ipsec)) {
-+ p->online = 1;
-+ nhrp_peer_ref(p);
-+ notifier_call(&p->notifier_list, NOTIFY_PEER_UP);
-+ nhrp_peer_unref(p);
-+ }
-+
-+ return 0;
-+}
-+
-+static void __nhrp_peer_check(struct nhrp_peer *p)
-+{
-+ struct nhrp_vc *vc = p->vc;
-+ struct interface *ifp = p->ifp;
-+ struct nhrp_interface *nifp = ifp->info;
-+ unsigned online;
-+
-+ online = nifp->enabled && (!nifp->ipsec_profile || vc->ipsec);
-+ if (p->online != online) {
-+ THREAD_OFF(p->t_fallback);
-+ if (online && notifier_active(&p->notifier_list)) {
-+ /* If we requested the IPsec connection, delay
-+ * the up notification a bit to allow things
-+ * settle down. This allows IKE to install
-+ * SPDs and SAs. */
-+ THREAD_TIMER_MSEC_ON(
-+ master, p->t_fallback,
-+ nhrp_peer_notify_up, p, 50);
-+ } else {
-+ nhrp_peer_ref(p);
-+ p->online = online;
-+ if (online) {
-+ notifier_call(&p->notifier_list, NOTIFY_PEER_UP);
-+ } else {
-+ p->requested = p->fallback_requested = 0;
-+ notifier_call(&p->notifier_list, NOTIFY_PEER_DOWN);
-+ }
-+ nhrp_peer_unref(p);
-+ }
-+ }
-+}
-+
-+static void nhrp_peer_vc_notify(struct notifier_block *n, unsigned long cmd)
-+{
-+ struct nhrp_peer *p = container_of(n, struct nhrp_peer, vc_notifier);
-+
-+ switch (cmd) {
-+ case NOTIFY_VC_IPSEC_CHANGED:
-+ __nhrp_peer_check(p);
-+ break;
-+ case NOTIFY_VC_IPSEC_UPDATE_NBMA:
-+ nhrp_peer_ref(p);
-+ notifier_call(&p->notifier_list, NOTIFY_PEER_NBMA_CHANGING);
-+ nhrp_peer_unref(p);
-+ break;
-+ }
-+}
-+
-+static void nhrp_peer_ifp_notify(struct notifier_block *n, unsigned long cmd)
-+{
-+ struct nhrp_peer *p = container_of(n, struct nhrp_peer, ifp_notifier);
-+ struct nhrp_interface *nifp;
-+ struct nhrp_vc *vc;
-+
-+ nhrp_peer_ref(p);
-+ switch (cmd) {
-+ case NOTIFY_INTERFACE_UP:
-+ case NOTIFY_INTERFACE_DOWN:
-+ __nhrp_peer_check(p);
-+ break;
-+ case NOTIFY_INTERFACE_NBMA_CHANGED:
-+ /* Source NBMA changed, rebind to new VC */
-+ nifp = p->ifp->info;
-+ vc = nhrp_vc_get(&nifp->nbma, &p->vc->remote.nbma, 1);
-+ if (vc && p->vc != vc) {
-+ nhrp_vc_notify_del(p->vc, &p->vc_notifier);
-+ p->vc = vc;
-+ nhrp_vc_notify_add(p->vc, &p->vc_notifier, nhrp_peer_vc_notify);
-+ __nhrp_peer_check(p);
-+ }
-+ /* Fall-through to post config update */
-+ case NOTIFY_INTERFACE_ADDRESS_CHANGED:
-+ notifier_call(&p->notifier_list, NOTIFY_PEER_IFCONFIG_CHANGED);
-+ break;
-+ case NOTIFY_INTERFACE_MTU_CHANGED:
-+ notifier_call(&p->notifier_list, NOTIFY_PEER_MTU_CHANGED);
-+ break;
-+ }
-+ nhrp_peer_unref(p);
-+}
-+
-+static unsigned int nhrp_peer_key(void *peer_data)
-+{
-+ struct nhrp_peer *p = peer_data;
-+ return sockunion_hash(&p->vc->remote.nbma);
-+}
-+
-+static int nhrp_peer_cmp(const void *cache_data, const void *key_data)
-+{
-+ const struct nhrp_peer *a = cache_data;
-+ const struct nhrp_peer *b = key_data;
-+ return a->ifp == b->ifp && a->vc == b->vc;
-+}
-+
-+static void *nhrp_peer_create(void *data)
-+{
-+ struct nhrp_peer *p, *key = data;
-+
-+ p = XMALLOC(MTYPE_NHRP_PEER, sizeof(*p));
-+ if (p) {
-+ *p = (struct nhrp_peer) {
-+ .ref = 0,
-+ .ifp = key->ifp,
-+ .vc = key->vc,
-+ .notifier_list = NOTIFIER_LIST_INITIALIZER(&p->notifier_list),
-+ };
-+ nhrp_vc_notify_add(p->vc, &p->vc_notifier, nhrp_peer_vc_notify);
-+ nhrp_interface_notify_add(p->ifp, &p->ifp_notifier, nhrp_peer_ifp_notify);
-+ }
-+ return p;
-+}
-+
-+struct nhrp_peer *nhrp_peer_get(struct interface *ifp, const union sockunion *remote_nbma)
-+{
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_peer key, *p;
-+ struct nhrp_vc *vc;
-+
-+ if (!nifp->peer_hash) {
-+ nifp->peer_hash = hash_create(nhrp_peer_key, nhrp_peer_cmp);
-+ if (!nifp->peer_hash) return NULL;
-+ }
-+
-+ vc = nhrp_vc_get(&nifp->nbma, remote_nbma, 1);
-+ if (!vc) return NULL;
-+
-+ key.ifp = ifp;
-+ key.vc = vc;
-+
-+ p = hash_get(nifp->peer_hash, &key, nhrp_peer_create);
-+ nhrp_peer_ref(p);
-+ if (p->ref == 1) __nhrp_peer_check(p);
-+
-+ return p;
-+}
-+
-+struct nhrp_peer *nhrp_peer_ref(struct nhrp_peer *p)
-+{
-+ if (p) p->ref++;
-+ return p;
-+}
-+
-+void nhrp_peer_unref(struct nhrp_peer *p)
-+{
-+ if (p) {
-+ p->ref--;
-+ nhrp_peer_check_delete(p);
-+ }
-+}
-+
-+static int nhrp_peer_request_timeout(struct thread *t)
-+{
-+ struct nhrp_peer *p = THREAD_ARG(t);
-+ struct nhrp_vc *vc = p->vc;
-+ struct interface *ifp = p->ifp;
-+ struct nhrp_interface *nifp = ifp->info;
-+
-+ p->t_fallback = NULL;
-+
-+ if (p->online)
-+ return 0;
-+
-+ if (nifp->ipsec_fallback_profile && !p->prio && !p->fallback_requested) {
-+ p->fallback_requested = 1;
-+ vici_request_vc(nifp->ipsec_fallback_profile,
-+ &vc->local.nbma, &vc->remote.nbma, p->prio);
-+ THREAD_TIMER_ON(master, p->t_fallback, nhrp_peer_request_timeout, p, 30);
-+ } else {
-+ p->requested = p->fallback_requested = 0;
-+ }
-+
-+ return 0;
-+}
-+
-+int nhrp_peer_check(struct nhrp_peer *p, int establish)
-+{
-+ struct nhrp_vc *vc = p->vc;
-+ struct interface *ifp = p->ifp;
-+ struct nhrp_interface *nifp = ifp->info;
-+
-+ if (p->online)
-+ return 1;
-+ if (!establish)
-+ return 0;
-+ if (p->requested)
-+ return 0;
-+ if (sockunion_family(&vc->local.nbma) == AF_UNSPEC)
-+ return 0;
-+
-+ p->prio = establish > 1;
-+ p->requested = 1;
-+ vici_request_vc(nifp->ipsec_profile, &vc->local.nbma, &vc->remote.nbma, p->prio);
-+ THREAD_TIMER_ON(master, p->t_fallback, nhrp_peer_request_timeout, p,
-+ (nifp->ipsec_fallback_profile && !p->prio) ? 15 : 30);
-+
-+ return 0;
-+}
-+
-+void nhrp_peer_notify_add(struct nhrp_peer *p, struct notifier_block *n, notifier_fn_t fn)
-+{
-+ notifier_add(n, &p->notifier_list, fn);
-+}
-+
-+void nhrp_peer_notify_del(struct nhrp_peer *p, struct notifier_block *n)
-+{
-+ notifier_del(n);
-+ nhrp_peer_check_delete(p);
-+}
-+
-+void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb)
-+{
-+ char buf[2][256];
-+
-+ nhrp_packet_debug(zb, "Send");
-+
-+ if (!p->online)
-+ return;
-+
-+ debugf(NHRP_DEBUG_KERNEL, "PACKET: Send %s -> %s",
-+ sockunion2str(&p->vc->local.nbma, buf[0], sizeof buf[0]),
-+ sockunion2str(&p->vc->remote.nbma, buf[1], sizeof buf[1]));
-+
-+ os_sendmsg(zb->head, zbuf_used(zb),
-+ p->ifp->ifindex,
-+ sockunion_get_addr(&p->vc->remote.nbma),
-+ sockunion_get_addrlen(&p->vc->remote.nbma));
-+ zbuf_reset(zb);
-+}
-+
-+static void nhrp_handle_resolution_req(struct nhrp_packet_parser *p)
-+{
-+ struct zbuf *zb, payload;
-+ struct nhrp_packet_header *hdr;
-+ struct nhrp_cie_header *cie;
-+ struct nhrp_extension_header *ext;
-+ struct nhrp_interface *nifp;
-+ struct nhrp_peer *peer;
-+
-+ if (!(p->if_ad->flags & NHRP_IFF_SHORTCUT)) {
-+ debugf(NHRP_DEBUG_COMMON, "Shortcuts disabled");
-+ /* FIXME: Send error indication? */
-+ return;
-+ }
-+
-+ if (p->if_ad->network_id &&
-+ p->route_type == NHRP_ROUTE_OFF_NBMA &&
-+ p->route_prefix.prefixlen < 8) {
-+ debugf(NHRP_DEBUG_COMMON, "Shortcut to more generic than /8 dropped");
-+ return;
-+ }
-+
-+ debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Resolution Req");
-+
-+ if (nhrp_route_address(p->ifp, &p->src_proto, NULL, &peer) != NHRP_ROUTE_NBMA_NEXTHOP)
-+ return;
-+
-+#if 0
-+ /* FIXME: Update requestors binding if CIE specifies holding time */
-+ nhrp_cache_update_binding(
-+ NHRP_CACHE_CACHED, &p->src_proto,
-+ nhrp_peer_get(p->ifp, &p->src_nbma),
-+ htons(cie->holding_time));
-+#endif
-+
-+ nifp = peer->ifp->info;
-+
-+ /* Create reply */
-+ zb = zbuf_alloc(1500);
-+ hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REPLY, &p->src_nbma, &p->src_proto, &p->dst_proto);
-+
-+ /* Copied information from request */
-+ hdr->flags = p->hdr->flags & htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER|NHRP_FLAG_RESOLUTION_SOURCE_STABLE);
-+ hdr->flags |= htons(NHRP_FLAG_RESOLUTION_DESTINATION_STABLE | NHRP_FLAG_RESOLUTION_AUTHORATIVE);
-+ hdr->u.request_id = p->hdr->u.request_id;
-+
-+ /* CIE payload */
-+ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &p->if_ad->addr);
-+ cie->holding_time = htons(p->if_ad->holdtime);
-+ cie->mtu = htons(p->if_ad->mtu);
-+ if (p->if_ad->network_id && p->route_type == NHRP_ROUTE_OFF_NBMA)
-+ cie->prefix_length = p->route_prefix.prefixlen;
-+ else
-+ cie->prefix_length = 8 * sockunion_get_addrlen(&p->if_ad->addr);
-+
-+ /* Handle extensions */
-+ while ((ext = nhrp_ext_pull(&p->extensions, &payload)) != NULL) {
-+ switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
-+ case NHRP_EXTENSION_NAT_ADDRESS:
-+ if (sockunion_family(&nifp->nat_nbma) == AF_UNSPEC)
-+ break;
-+ ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
-+ if (!ext) goto err;
-+ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nat_nbma, &p->if_ad->addr);
-+ if (!cie) goto err;
-+ nhrp_ext_complete(zb, ext);
-+ break;
-+ default:
-+ if (nhrp_ext_reply(zb, hdr, p->ifp, ext, &payload) < 0)
-+ goto err;
-+ break;
-+ }
-+ }
-+
-+ nhrp_packet_complete(zb, hdr);
-+ nhrp_peer_send(peer, zb);
-+err:
-+ nhrp_peer_unref(peer);
-+ zbuf_free(zb);
-+}
-+
-+static void nhrp_handle_registration_request(struct nhrp_packet_parser *p)
-+{
-+ struct interface *ifp = p->ifp;
-+ struct zbuf *zb, payload;
-+ struct nhrp_packet_header *hdr;
-+ struct nhrp_cie_header *cie;
-+ struct nhrp_extension_header *ext;
-+ struct nhrp_cache *c;
-+ union sockunion cie_nbma, cie_proto, *proto_addr, *nbma_addr, *nbma_natoa;
-+ int holdtime, natted = 0;
-+ size_t paylen;
-+ void *pay;
-+
-+ debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Registration Req");
-+
-+ if (!sockunion_same(&p->src_nbma, &p->peer->vc->remote.nbma))
-+ natted = 1;
-+
-+ /* Create reply */
-+ zb = zbuf_alloc(1500);
-+ hdr = nhrp_packet_push(zb, NHRP_PACKET_REGISTRATION_REPLY,
-+ &p->src_nbma, &p->src_proto, &p->if_ad->addr);
-+
-+ /* Copied information from request */
-+ hdr->flags = p->hdr->flags & htons(NHRP_FLAG_REGISTRATION_UNIQUE | NHRP_FLAG_REGISTRATION_NAT);
-+ hdr->u.request_id = p->hdr->u.request_id;
-+
-+ /* Copy payload CIEs */
-+ paylen = zbuf_used(&p->payload);
-+ pay = zbuf_pushn(zb, paylen);
-+ if (!pay) goto err;
-+ memcpy(pay, zbuf_pulln(&p->payload, paylen), paylen);
-+ zbuf_init(&payload, pay, paylen, paylen);
-+
-+ while ((cie = nhrp_cie_pull(&payload, hdr, &cie_nbma, &cie_proto)) != NULL) {
-+ if (cie->prefix_length != 0xff && !(p->hdr->flags & htons(NHRP_FLAG_REGISTRATION_UNIQUE))) {
-+ cie->code = NHRP_CODE_BINDING_NON_UNIQUE;
-+ continue;
-+ }
-+
-+ /* We currently support only unique prefix registrations */
-+ if (cie->prefix_length != 0xff) {
-+ cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
-+ continue;
-+ }
-+
-+ proto_addr = (sockunion_family(&cie_proto) == AF_UNSPEC) ? &p->src_proto : &cie_proto;
-+ nbma_addr = (sockunion_family(&cie_nbma) == AF_UNSPEC) ? &p->src_nbma : &cie_nbma;
-+ nbma_natoa = NULL;
-+ if (natted) {
-+ nbma_natoa = nbma_addr;
-+ nbma_addr = &p->peer->vc->remote.nbma;
-+ }
-+
-+ holdtime = htons(cie->holding_time);
-+ if (!holdtime) holdtime = p->if_ad->holdtime;
-+
-+ c = nhrp_cache_get(ifp, proto_addr, 1);
-+ if (!c) {
-+ cie->code = NHRP_CODE_INSUFFICIENT_RESOURCES;
-+ continue;
-+ }
-+
-+ if (!nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC, holdtime, nhrp_peer_ref(p->peer), htons(cie->mtu), nbma_natoa)) {
-+ cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
-+ continue;
-+ }
-+
-+ cie->code = NHRP_CODE_SUCCESS;
-+ }
-+
-+ /* Handle extensions */
-+ while ((ext = nhrp_ext_pull(&p->extensions, &payload)) != NULL) {
-+ switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
-+ case NHRP_EXTENSION_NAT_ADDRESS:
-+ ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
-+ if (!ext) goto err;
-+ zbuf_copy(zb, &payload, zbuf_used(&payload));
-+ if (natted) {
-+ nhrp_cie_push(zb, NHRP_CODE_SUCCESS,
-+ &p->peer->vc->remote.nbma,
-+ &p->src_proto);
-+ }
-+ nhrp_ext_complete(zb, ext);
-+ break;
-+ default:
-+ if (nhrp_ext_reply(zb, hdr, ifp, ext, &payload) < 0)
-+ goto err;
-+ break;
-+ }
-+ }
-+
-+ nhrp_packet_complete(zb, hdr);
-+ nhrp_peer_send(p->peer, zb);
-+err:
-+ zbuf_free(zb);
-+}
-+
-+static int parse_ether_packet(struct zbuf *zb, uint16_t protocol_type, union sockunion *src, union sockunion *dst)
-+{
-+ switch (protocol_type) {
-+ case ETH_P_IP: {
-+ struct iphdr *iph = zbuf_pull(zb, struct iphdr);
-+ if (iph) {
-+ if (src) sockunion_set(src, AF_INET, (uint8_t*) &iph->saddr, sizeof(iph->saddr));
-+ if (dst) sockunion_set(dst, AF_INET, (uint8_t*) &iph->daddr, sizeof(iph->daddr));
-+ }
-+ }
-+ break;
-+ case ETH_P_IPV6: {
-+ struct ipv6hdr *iph = zbuf_pull(zb, struct ipv6hdr);
-+ if (iph) {
-+ if (src) sockunion_set(src, AF_INET6, (uint8_t*) &iph->saddr, sizeof(iph->saddr));
-+ if (dst) sockunion_set(dst, AF_INET6, (uint8_t*) &iph->daddr, sizeof(iph->daddr));
-+ }
-+ }
-+ break;
-+ default:
-+ return 0;
-+ }
-+ return 1;
-+}
-+
-+void nhrp_peer_send_indication(struct interface *ifp, uint16_t protocol_type, struct zbuf *pkt)
-+{
-+ union sockunion dst;
-+ struct zbuf *zb, payload;
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_afi_data *if_ad;
-+ struct nhrp_packet_header *hdr;
-+ struct nhrp_peer *p;
-+ char buf[2][SU_ADDRSTRLEN];
-+
-+ if (!nifp->enabled) return;
-+
-+ payload = *pkt;
-+ if (!parse_ether_packet(&payload, protocol_type, &dst, NULL))
-+ return;
-+
-+ if (nhrp_route_address(ifp, &dst, NULL, &p) != NHRP_ROUTE_NBMA_NEXTHOP)
-+ return;
-+
-+ if_ad = &nifp->afi[family2afi(sockunion_family(&dst))];
-+ if (!(if_ad->flags & NHRP_IFF_REDIRECT)) {
-+ debugf(NHRP_DEBUG_COMMON, "Send Traffic Indication to %s about packet to %s ignored",
-+ sockunion2str(&p->vc->remote.nbma, buf[0], sizeof buf[0]),
-+ sockunion2str(&dst, buf[1], sizeof buf[1]));
-+ return;
-+ }
-+
-+ debugf(NHRP_DEBUG_COMMON, "Send Traffic Indication to %s (online=%d) about packet to %s",
-+ sockunion2str(&p->vc->remote.nbma, buf[0], sizeof buf[0]),
-+ p->online,
-+ sockunion2str(&dst, buf[1], sizeof buf[1]));
-+
-+ /* Create reply */
-+ zb = zbuf_alloc(1500);
-+ hdr = nhrp_packet_push(zb, NHRP_PACKET_TRAFFIC_INDICATION, &nifp->nbma, &if_ad->addr, &dst);
-+ hdr->hop_count = 0;
-+
-+ /* Payload is the packet causing indication */
-+ zbuf_copy(zb, pkt, zbuf_used(pkt));
-+ nhrp_packet_complete(zb, hdr);
-+ nhrp_peer_send(p, zb);
-+ nhrp_peer_unref(p);
-+ zbuf_free(zb);
-+}
-+
-+static void nhrp_handle_error_ind(struct nhrp_packet_parser *pp)
-+{
-+ struct zbuf origmsg = pp->payload;
-+ struct nhrp_packet_header *hdr;
-+ struct nhrp_reqid *reqid;
-+ union sockunion src_nbma, src_proto, dst_proto;
-+ char buf[2][SU_ADDRSTRLEN];
-+
-+ hdr = nhrp_packet_pull(&origmsg, &src_nbma, &src_proto, &dst_proto);
-+ if (!hdr) return;
-+
-+ debugf(NHRP_DEBUG_COMMON, "Error Indication from %s about packet to %s ignored",
-+ sockunion2str(&pp->src_proto, buf[0], sizeof buf[0]),
-+ sockunion2str(&dst_proto, buf[1], sizeof buf[1]));
-+
-+ reqid = nhrp_reqid_lookup(&nhrp_packet_reqid, htonl(hdr->u.request_id));
-+ if (reqid)
-+ reqid->cb(reqid, pp);
-+}
-+
-+static void nhrp_handle_traffic_ind(struct nhrp_packet_parser *p)
-+{
-+ union sockunion dst;
-+ char buf[2][SU_ADDRSTRLEN];
-+
-+ if (!parse_ether_packet(&p->payload, htons(p->hdr->protocol_type), NULL, &dst))
-+ return;
-+
-+ debugf(NHRP_DEBUG_COMMON, "Traffic Indication from %s about packet to %s: %s",
-+ sockunion2str(&p->src_proto, buf[0], sizeof buf[0]),
-+ sockunion2str(&dst, buf[1], sizeof buf[1]),
-+ (p->if_ad->flags & NHRP_IFF_SHORTCUT) ? "trying shortcut" : "ignored");
-+
-+ if (p->if_ad->flags & NHRP_IFF_SHORTCUT)
-+ nhrp_shortcut_initiate(&dst);
-+}
-+
-+enum packet_type_t {
-+ PACKET_UNKNOWN = 0,
-+ PACKET_REQUEST,
-+ PACKET_REPLY,
-+ PACKET_INDICATION,
-+};
-+
-+static struct {
-+ enum packet_type_t type;
-+ const char *name;
-+ void (*handler)(struct nhrp_packet_parser *);
-+} packet_types[] = {
-+ [NHRP_PACKET_RESOLUTION_REQUEST] = {
-+ .type = PACKET_REQUEST,
-+ .name = "Resolution-Request",
-+ .handler = nhrp_handle_resolution_req,
-+ },
-+ [NHRP_PACKET_RESOLUTION_REPLY] = {
-+ .type = PACKET_REPLY,
-+ .name = "Resolution-Reply",
-+ },
-+ [NHRP_PACKET_REGISTRATION_REQUEST] = {
-+ .type = PACKET_REQUEST,
-+ .name = "Registration-Request",
-+ .handler = nhrp_handle_registration_request,
-+ },
-+ [NHRP_PACKET_REGISTRATION_REPLY] = {
-+ .type = PACKET_REPLY,
-+ .name = "Registration-Reply",
-+ },
-+ [NHRP_PACKET_PURGE_REQUEST] = {
-+ .type = PACKET_REQUEST,
-+ .name = "Purge-Request",
-+ },
-+ [NHRP_PACKET_PURGE_REPLY] = {
-+ .type = PACKET_REPLY,
-+ .name = "Purge-Reply",
-+ },
-+ [NHRP_PACKET_ERROR_INDICATION] = {
-+ .type = PACKET_INDICATION,
-+ .name = "Error-Indication",
-+ .handler = nhrp_handle_error_ind,
-+ },
-+ [NHRP_PACKET_TRAFFIC_INDICATION] = {
-+ .type = PACKET_INDICATION,
-+ .name = "Traffic-Indication",
-+ .handler = nhrp_handle_traffic_ind,
-+ }
-+};
-+
-+static void nhrp_peer_forward(struct nhrp_peer *p, struct nhrp_packet_parser *pp)
-+{
-+ struct zbuf *zb, extpl;
-+ struct nhrp_packet_header *hdr;
-+ struct nhrp_extension_header *ext, *dst;
-+ struct nhrp_cie_header *cie;
-+ struct nhrp_interface *nifp = pp->ifp->info;
-+ struct nhrp_afi_data *if_ad = pp->if_ad;
-+ union sockunion cie_nbma, cie_protocol;
-+ uint16_t type, len;
-+
-+ if (pp->hdr->hop_count == 0)
-+ return;
-+
-+ /* Create forward packet - copy header */
-+ zb = zbuf_alloc(1500);
-+ hdr = nhrp_packet_push(zb, pp->hdr->type, &pp->src_nbma, &pp->src_proto, &pp->dst_proto);
-+ hdr->flags = pp->hdr->flags;
-+ hdr->hop_count = pp->hdr->hop_count - 1;
-+ hdr->u.request_id = pp->hdr->u.request_id;
-+
-+ /* Copy payload */
-+ zbuf_copy(zb, &pp->payload, zbuf_used(&pp->payload));
-+
-+ /* Copy extensions */
-+ while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) {
-+ type = htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY;
-+ len = htons(ext->length);
-+
-+ if (type == NHRP_EXTENSION_END)
-+ break;
-+
-+ dst = nhrp_ext_push(zb, hdr, htons(ext->type));
-+ if (!dst) goto err;
-+
-+ switch (type) {
-+ case NHRP_EXTENSION_FORWARD_TRANSIT_NHS:
-+ case NHRP_EXTENSION_REVERSE_TRANSIT_NHS:
-+ zbuf_put(zb, extpl.head, len);
-+ if ((type == NHRP_EXTENSION_REVERSE_TRANSIT_NHS) ==
-+ (packet_types[hdr->type].type == PACKET_REPLY)) {
-+ /* Check NHS list for forwarding loop */
-+ while ((cie = nhrp_cie_pull(&extpl, pp->hdr, &cie_nbma, &cie_protocol)) != NULL) {
-+ if (sockunion_same(&p->vc->remote.nbma, &cie_nbma))
-+ goto err;
-+ }
-+ /* Append our selves to the list */
-+ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &if_ad->addr);
-+ if (!cie) goto err;
-+ cie->holding_time = htons(if_ad->holdtime);
-+ }
-+ break;
-+ default:
-+ if (htons(ext->type) & NHRP_EXTENSION_FLAG_COMPULSORY)
-+ /* FIXME: RFC says to just copy, but not
-+ * append our selves to the transit NHS list */
-+ goto err;
-+ case NHRP_EXTENSION_RESPONDER_ADDRESS:
-+ /* Supported compulsory extensions, and any
-+ * non-compulsory that is not explicitly handled,
-+ * should be just copied. */
-+ zbuf_copy(zb, &extpl, len);
-+ break;
-+ }
-+ nhrp_ext_complete(zb, dst);
-+ }
-+
-+ nhrp_packet_complete(zb, hdr);
-+ nhrp_peer_send(p, zb);
-+ zbuf_free(zb);
-+ return;
-+err:
-+ nhrp_packet_debug(pp->pkt, "FWD-FAIL");
-+ zbuf_free(zb);
-+}
-+
-+static void nhrp_packet_debug(struct zbuf *zb, const char *dir)
-+{
-+ char buf[2][SU_ADDRSTRLEN];
-+ union sockunion src_nbma, src_proto, dst_proto;
-+ struct nhrp_packet_header *hdr;
-+ struct zbuf zhdr;
-+ int reply;
-+
-+ if (likely(!(debug_flags & NHRP_DEBUG_COMMON)))
-+ return;
-+
-+ zbuf_init(&zhdr, zb->buf, zb->tail-zb->buf, zb->tail-zb->buf);
-+ hdr = nhrp_packet_pull(&zhdr, &src_nbma, &src_proto, &dst_proto);
-+
-+ sockunion2str(&src_proto, buf[0], sizeof buf[0]);
-+ sockunion2str(&dst_proto, buf[1], sizeof buf[1]);
-+
-+ reply = packet_types[hdr->type].type == PACKET_REPLY;
-+ debugf(NHRP_DEBUG_COMMON, "%s %s(%d) %s -> %s",
-+ dir,
-+ packet_types[hdr->type].name ? : "Unknown",
-+ hdr->type,
-+ reply ? buf[1] : buf[0],
-+ reply ? buf[0] : buf[1]);
-+}
-+
-+struct nhrp_route_info {
-+ int local;
-+ struct interface *ifp;
-+ struct nhrp_vc *vc;
-+};
-+
-+void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb)
-+{
-+ char buf[2][SU_ADDRSTRLEN];
-+ struct nhrp_packet_header *hdr;
-+ struct nhrp_vc *vc = p->vc;
-+ struct interface *ifp = p->ifp;
-+ struct nhrp_interface *nifp = ifp->info;
-+ struct nhrp_packet_parser pp;
-+ struct nhrp_peer *peer = NULL;
-+ struct nhrp_reqid *reqid;
-+ const char *info = NULL;
-+ union sockunion *target_addr;
-+ unsigned paylen, extoff, extlen, realsize;
-+ afi_t afi;
-+
-+ debugf(NHRP_DEBUG_KERNEL, "PACKET: Recv %s -> %s",
-+ sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]),
-+ sockunion2str(&vc->local.nbma, buf[1], sizeof buf[1]));
-+
-+ if (!p->online) {
-+ info = "peer not online";
-+ goto drop;
-+ }
-+
-+ if (nhrp_packet_calculate_checksum(zb->head, zbuf_used(zb)) != 0) {
-+ info = "bad checksum";
-+ goto drop;
-+ }
-+
-+ realsize = zbuf_used(zb);
-+ hdr = nhrp_packet_pull(zb, &pp.src_nbma, &pp.src_proto, &pp.dst_proto);
-+ if (!hdr) {
-+ info = "corrupt header";
-+ goto drop;
-+ }
-+
-+ pp.ifp = ifp;
-+ pp.pkt = zb;
-+ pp.hdr = hdr;
-+ pp.peer = p;
-+
-+ afi = htons(hdr->afnum);
-+ if (hdr->type > ZEBRA_NUM_OF(packet_types) ||
-+ hdr->version != NHRP_VERSION_RFC2332 ||
-+ afi >= AFI_MAX ||
-+ packet_types[hdr->type].type == PACKET_UNKNOWN ||
-+ htons(hdr->packet_size) > realsize) {
-+ zlog_info("From %s: error: packet type %d, version %d, AFI %d, size %d (real size %d)",
-+ sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]),
-+ (int) hdr->type, (int) hdr->version, (int) afi,
-+ (int) htons(hdr->packet_size),
-+ (int) realsize);
-+ goto drop;
-+ }
-+ pp.if_ad = &((struct nhrp_interface *)ifp->info)->afi[afi];
-+
-+ extoff = htons(hdr->extension_offset);
-+ if (extoff) {
-+ if (extoff >= realsize) {
-+ info = "extoff larger than packet";
-+ goto drop;
-+ }
-+ paylen = extoff - (zb->head - zb->buf);
-+ } else {
-+ paylen = zbuf_used(zb);
-+ }
-+ zbuf_init(&pp.payload, zbuf_pulln(zb, paylen), paylen, paylen);
-+ extlen = zbuf_used(zb);
-+ zbuf_init(&pp.extensions, zbuf_pulln(zb, extlen), extlen, extlen);
-+
-+ if (!nifp->afi[afi].network_id) {
-+ info = "nhrp not enabled";
-+ goto drop;
-+ }
-+
-+ nhrp_packet_debug(zb, "Recv");
-+
-+ /* FIXME: Check authentication here. This extension needs to be
-+ * pre-handled. */
-+
-+ /* Figure out if this is local */
-+ target_addr = (packet_types[hdr->type].type == PACKET_REPLY) ? &pp.src_proto : &pp.dst_proto;
-+
-+ if (sockunion_same(&pp.src_proto, &pp.dst_proto))
-+ pp.route_type = NHRP_ROUTE_LOCAL;
-+ else
-+ pp.route_type = nhrp_route_address(pp.ifp, target_addr, &pp.route_prefix, &peer);
-+
-+ switch (pp.route_type) {
-+ case NHRP_ROUTE_LOCAL:
-+ nhrp_packet_debug(zb, "!LOCAL");
-+ if (packet_types[hdr->type].type == PACKET_REPLY) {
-+ reqid = nhrp_reqid_lookup(&nhrp_packet_reqid, htonl(hdr->u.request_id));
-+ if (reqid) {
-+ reqid->cb(reqid, &pp);
-+ break;
-+ } else {
-+ nhrp_packet_debug(zb, "!UNKNOWN-REQID");
-+ /* FIXME: send error-indication */
-+ }
-+ }
-+ case NHRP_ROUTE_OFF_NBMA:
-+ if (packet_types[hdr->type].handler) {
-+ packet_types[hdr->type].handler(&pp);
-+ break;
-+ }
-+ break;
-+ case NHRP_ROUTE_NBMA_NEXTHOP:
-+ nhrp_peer_forward(peer, &pp);
-+ break;
-+ case NHRP_ROUTE_BLACKHOLE:
-+ break;
-+ }
-+
-+drop:
-+ if (info) {
-+ zlog_info("From %s: error: %s",
-+ sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]),
-+ info);
-+ }
-+ if (peer) nhrp_peer_unref(peer);
-+ zbuf_free(zb);
-+}
-diff --git a/nhrpd/nhrp_protocol.h b/nhrpd/nhrp_protocol.h
-new file mode 100644
-index 0000000..a4bc9fa
---- /dev/null
-+++ b/nhrpd/nhrp_protocol.h
-@@ -0,0 +1,128 @@
-+/* nhrp_protocol.h - NHRP protocol definitions
-+ *
-+ * Copyright (c) 2007-2012 Timo Teräs <timo.teras@iki.fi>
-+ *
-+ * This software is licensed under the MIT License.
-+ * See MIT-LICENSE.txt for additional details.
-+ */
-+
-+#ifndef NHRP_PROTOCOL_H
-+#define NHRP_PROTOCOL_H
-+
-+#include <stdint.h>
-+
-+/* NHRP Ethernet protocol number */
-+#define ETH_P_NHRP 0x2001
-+
-+/* NHRP Version */
-+#define NHRP_VERSION_RFC2332 1
-+
-+/* NHRP Packet Types */
-+#define NHRP_PACKET_RESOLUTION_REQUEST 1
-+#define NHRP_PACKET_RESOLUTION_REPLY 2
-+#define NHRP_PACKET_REGISTRATION_REQUEST 3
-+#define NHRP_PACKET_REGISTRATION_REPLY 4
-+#define NHRP_PACKET_PURGE_REQUEST 5
-+#define NHRP_PACKET_PURGE_REPLY 6
-+#define NHRP_PACKET_ERROR_INDICATION 7
-+#define NHRP_PACKET_TRAFFIC_INDICATION 8
-+
-+/* NHRP Extension Types */
-+#define NHRP_EXTENSION_FLAG_COMPULSORY 0x8000
-+#define NHRP_EXTENSION_END 0
-+#define NHRP_EXTENSION_PAYLOAD 0
-+#define NHRP_EXTENSION_RESPONDER_ADDRESS 3
-+#define NHRP_EXTENSION_FORWARD_TRANSIT_NHS 4
-+#define NHRP_EXTENSION_REVERSE_TRANSIT_NHS 5
-+#define NHRP_EXTENSION_AUTHENTICATION 7
-+#define NHRP_EXTENSION_VENDOR 8
-+#define NHRP_EXTENSION_NAT_ADDRESS 9
-+
-+/* NHRP Error Indication Codes */
-+#define NHRP_ERROR_UNRECOGNIZED_EXTENSION 1
-+#define NHRP_ERROR_LOOP_DETECTED 2
-+#define NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE 6
-+#define NHRP_ERROR_PROTOCOL_ERROR 7
-+#define NHRP_ERROR_SDU_SIZE_EXCEEDED 8
-+#define NHRP_ERROR_INVALID_EXTENSION 9
-+#define NHRP_ERROR_INVALID_RESOLUTION_REPLY 10
-+#define NHRP_ERROR_AUTHENTICATION_FAILURE 11
-+#define NHRP_ERROR_HOP_COUNT_EXCEEDED 15
-+
-+/* NHRP CIE Codes */
-+#define NHRP_CODE_SUCCESS 0
-+#define NHRP_CODE_ADMINISTRATIVELY_PROHIBITED 4
-+#define NHRP_CODE_INSUFFICIENT_RESOURCES 5
-+#define NHRP_CODE_NO_BINDING_EXISTS 11
-+#define NHRP_CODE_BINDING_NON_UNIQUE 13
-+#define NHRP_CODE_UNIQUE_ADDRESS_REGISTERED 14
-+
-+/* NHRP Flags for Resolution request/reply */
-+#define NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER 0x8000
-+#define NHRP_FLAG_RESOLUTION_AUTHORATIVE 0x4000
-+#define NHRP_FLAG_RESOLUTION_DESTINATION_STABLE 0x2000
-+#define NHRP_FLAG_RESOLUTION_UNIQUE 0x1000
-+#define NHRP_FLAG_RESOLUTION_SOURCE_STABLE 0x0800
-+#define NHRP_FLAG_RESOLUTION_NAT 0x0002
-+
-+/* NHRP Flags for Registration request/reply */
-+#define NHRP_FLAG_REGISTRATION_UNIQUE 0x8000
-+#define NHRP_FLAG_REGISTRATION_NAT 0x0002
-+
-+/* NHRP Flags for Purge request/reply */
-+#define NHRP_FLAG_PURGE_NO_REPLY 0x8000
-+
-+/* NHRP Authentication extension types (ala Cisco) */
-+#define NHRP_AUTHENTICATION_PLAINTEXT 0x00000001
-+
-+/* NHRP Packet Structures */
-+struct nhrp_packet_header {
-+ /* Fixed header */
-+ uint16_t afnum;
-+ uint16_t protocol_type;
-+ uint8_t snap[5];
-+ uint8_t hop_count;
-+ uint16_t packet_size;
-+ uint16_t checksum;
-+ uint16_t extension_offset;
-+ uint8_t version;
-+ uint8_t type;
-+ uint8_t src_nbma_address_len;
-+ uint8_t src_nbma_subaddress_len;
-+
-+ /* Mandatory header */
-+ uint8_t src_protocol_address_len;
-+ uint8_t dst_protocol_address_len;
-+ uint16_t flags;
-+ union {
-+ uint32_t request_id;
-+ struct {
-+ uint16_t code;
-+ uint16_t offset;
-+ } error;
-+ } u;
-+} __attribute__((packed));
-+
-+struct nhrp_cie_header {
-+ uint8_t code;
-+ uint8_t prefix_length;
-+ uint16_t unused;
-+ uint16_t mtu;
-+ uint16_t holding_time;
-+ uint8_t nbma_address_len;
-+ uint8_t nbma_subaddress_len;
-+ uint8_t protocol_address_len;
-+ uint8_t preference;
-+} __attribute__((packed));
-+
-+struct nhrp_extension_header {
-+ uint16_t type;
-+ uint16_t length;
-+} __attribute__((packed));
-+
-+struct nhrp_cisco_authentication_extension {
-+ uint32_t type;
-+ uint8_t secret[8];
-+} __attribute__((packed));
-+
-+#endif
-diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c
-new file mode 100644
-index 0000000..cc6b5fa
---- /dev/null
-+++ b/nhrpd/nhrp_route.c
-@@ -0,0 +1,345 @@
-+/* NHRP routing functions
-+ * 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 "nhrpd.h"
-+#include "table.h"
-+#include "memory.h"
-+#include "stream.h"
-+#include "log.h"
-+#include "zclient.h"
-+
-+static struct zclient *zclient;
-+static struct route_table *zebra_rib[AFI_MAX];
-+
-+struct route_info {
-+ union sockunion via;
-+ struct interface *ifp;
-+ struct interface *nhrp_ifp;
-+};
-+
-+static void nhrp_zebra_connected(struct zclient *zclient)
-+{
-+ /* No real VRF support yet -- bind only to the default vrf */
-+ zclient_send_requests (zclient, VRF_DEFAULT);
-+}
-+
-+static struct route_node *nhrp_route_update_get(const struct prefix *p, int create)
-+{
-+ struct route_node *rn;
-+ afi_t afi = family2afi(PREFIX_FAMILY(p));
-+
-+ if (!zebra_rib[afi])
-+ return NULL;
-+
-+ if (create) {
-+ rn = route_node_get(zebra_rib[afi], p);
-+ if (!rn->info) {
-+ rn->info = XCALLOC(MTYPE_NHRP_ROUTE, sizeof(struct route_info));
-+ route_lock_node(rn);
-+ }
-+ return rn;
-+ } else {
-+ return route_node_lookup(zebra_rib[afi], p);
-+ }
-+}
-+
-+static void nhrp_route_update_put(struct route_node *rn)
-+{
-+ struct route_info *ri = rn->info;
-+
-+ if (!ri->ifp && !ri->nhrp_ifp && sockunion_family(&ri->via) == AF_UNSPEC) {
-+ XFREE(MTYPE_NHRP_ROUTE, rn->info);
-+ rn->info = NULL;
-+ route_unlock_node(rn);
-+ }
-+ route_unlock_node(rn);
-+}
-+
-+static void nhrp_route_update_zebra(const struct prefix *p, union sockunion *nexthop, struct interface *ifp)
-+{
-+ struct route_node *rn;
-+ struct route_info *ri;
-+
-+ rn = nhrp_route_update_get(p, (sockunion_family(nexthop) != AF_UNSPEC) || ifp);
-+ if (rn) {
-+ ri = rn->info;
-+ ri->via = *nexthop;
-+ ri->ifp = ifp;
-+ nhrp_route_update_put(rn);
-+ }
-+}
-+
-+void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp)
-+{
-+ struct route_node *rn;
-+ struct route_info *ri;
-+
-+ rn = nhrp_route_update_get(p, ifp != NULL);
-+ if (rn) {
-+ ri = rn->info;
-+ ri->nhrp_ifp = ifp;
-+ nhrp_route_update_put(rn);
-+ }
-+}
-+
-+void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix *p, struct interface *ifp, const union sockunion *nexthop, uint32_t mtu)
-+{
-+ struct in_addr *nexthop_ipv4;
-+ int flags = 0;
-+
-+ if (zclient->sock < 0)
-+ return;
-+
-+ switch (type) {
-+ case NHRP_CACHE_NEGATIVE:
-+ SET_FLAG(flags, ZEBRA_FLAG_REJECT);
-+ break;
-+ case NHRP_CACHE_DYNAMIC:
-+ case NHRP_CACHE_NHS:
-+ case NHRP_CACHE_STATIC:
-+ /* Regular route, so these are announced
-+ * to other routing daemons */
-+ break;
-+ default:
-+ SET_FLAG(flags, ZEBRA_FLAG_FIB_OVERRIDE);
-+ break;
-+ }
-+ SET_FLAG(flags, ZEBRA_FLAG_INTERNAL);
-+
-+ if (p->family == AF_INET) {
-+ struct zapi_ipv4 api;
-+
-+ memset(&api, 0, sizeof(api));
-+ api.flags = flags;
-+ api.type = ZEBRA_ROUTE_NHRP;
-+ api.safi = SAFI_UNICAST;
-+
-+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
-+ if (nexthop) {
-+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
-+ nexthop_ipv4 = (struct in_addr *) sockunion_get_addr(nexthop);
-+ api.nexthop_num = 1;
-+ api.nexthop = &nexthop_ipv4;
-+ }
-+ if (ifp) {
-+ SET_FLAG(api.message, ZAPI_MESSAGE_IFINDEX);
-+ api.ifindex_num = 1;
-+ api.ifindex = &ifp->ifindex;
-+ }
-+ if (mtu) {
-+ SET_FLAG(api.message, ZAPI_MESSAGE_MTU);
-+ api.mtu = mtu;
-+ }
-+
-+ if (unlikely(debug_flags & NHRP_DEBUG_ROUTE)) {
-+ char buf[2][INET_ADDRSTRLEN];
-+ zlog_debug("Zebra send: IPv4 route %s %s/%d nexthop %s metric %u"
-+ " count %d dev %s",
-+ add ? "add" : "del",
-+ inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])),
-+ p->prefixlen,
-+ nexthop ? inet_ntop(AF_INET, api.nexthop[0], buf[1], sizeof(buf[1])) : "<onlink>",
-+ api.metric, api.nexthop_num, ifp->name);
-+ }
-+
-+ zapi_ipv4_route(
-+ add ? ZEBRA_IPV4_ROUTE_ADD : ZEBRA_IPV4_ROUTE_DELETE,
-+ zclient, (struct prefix_ipv4 *) p, &api);
-+ }
-+}
-+
-+int nhrp_route_read(int cmd, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id)
-+{
-+ struct stream *s;
-+ struct interface *ifp = NULL;
-+ struct prefix prefix;
-+ union sockunion nexthop_addr;
-+ unsigned char message, nexthop_num, ifindex_num;
-+ unsigned ifindex;
-+ char buf[2][PREFIX_STRLEN];
-+ int i, afaddrlen, added;
-+
-+ s = zclient->ibuf;
-+ memset(&prefix, 0, sizeof(prefix));
-+ sockunion_family(&nexthop_addr) = AF_UNSPEC;
-+
-+ /* Type, flags, message. */
-+ /*type =*/ stream_getc(s);
-+ /*flags =*/ stream_getc(s);
-+ message = stream_getc(s);
-+
-+ /* Prefix */
-+ switch (cmd) {
-+ case ZEBRA_IPV4_ROUTE_ADD:
-+ case ZEBRA_IPV4_ROUTE_DELETE:
-+ prefix.family = AF_INET;
-+ break;
-+ case ZEBRA_IPV6_ROUTE_ADD:
-+ case ZEBRA_IPV6_ROUTE_DELETE:
-+ prefix.family = AF_INET6;
-+ break;
-+ default:
-+ return -1;
-+ }
-+ afaddrlen = family2addrsize(prefix.family);
-+ prefix.prefixlen = stream_getc(s);
-+ stream_get(&prefix.u.val, s, PSIZE(prefix.prefixlen));
-+
-+ /* Nexthop, ifindex, distance, metric. */
-+ if (CHECK_FLAG(message, ZAPI_MESSAGE_NEXTHOP|ZAPI_MESSAGE_IFINDEX)) {
-+ nexthop_num = stream_getc(s);
-+ for (i = 0; i < nexthop_num; i++) {
-+ stream_get(buf[0], s, afaddrlen);
-+ if (i == 0) sockunion_set(&nexthop_addr, prefix.family, (u_char*) buf[0], afaddrlen);
-+ }
-+ ifindex_num = stream_getc(s);
-+ for (i = 0; i < ifindex_num; i++) {
-+ ifindex = stream_getl(s);
-+ if (i == 0 && ifindex != IFINDEX_INTERNAL)
-+ ifp = if_lookup_by_index(ifindex);
-+ }
-+ }
-+ if (CHECK_FLAG(message, ZAPI_MESSAGE_DISTANCE))
-+ /*distance =*/ stream_getc(s);
-+ if (CHECK_FLAG(message, ZAPI_MESSAGE_METRIC))
-+ /*metric =*/ stream_getl(s);
-+
-+ added = (cmd == ZEBRA_IPV4_ROUTE_ADD || cmd == ZEBRA_IPV6_ROUTE_ADD);
-+ debugf(NHRP_DEBUG_ROUTE, "if-route-%s: %s via %s dev %s",
-+ added ? "add" : "del",
-+ prefix2str(&prefix, buf[0], sizeof buf[0]),
-+ sockunion2str(&nexthop_addr, buf[1], sizeof buf[1]),
-+ ifp ? ifp->name : "(none)");
-+
-+ nhrp_route_update_zebra(&prefix, &nexthop_addr, ifp);
-+ nhrp_shortcut_prefix_change(&prefix, !added);
-+
-+ return 0;
-+}
-+
-+int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, union sockunion *via, struct interface **ifp)
-+{
-+ struct route_node *rn;
-+ struct route_info *ri;
-+ struct prefix lookup;
-+ afi_t afi = family2afi(sockunion_family(addr));
-+ char buf[PREFIX_STRLEN];
-+
-+ sockunion2hostprefix(addr, &lookup);
-+
-+ rn = route_node_match(zebra_rib[afi], &lookup);
-+ if (!rn) return 0;
-+
-+ ri = rn->info;
-+ if (ri->nhrp_ifp) {
-+ debugf(NHRP_DEBUG_ROUTE, "lookup %s: nhrp_if=%s",
-+ prefix2str(&lookup, buf, sizeof buf),
-+ ri->nhrp_ifp->name);
-+
-+ if (via) sockunion_family(via) = AF_UNSPEC;
-+ if (ifp) *ifp = ri->nhrp_ifp;
-+ } else {
-+ debugf(NHRP_DEBUG_ROUTE, "lookup %s: zebra route dev %s",
-+ prefix2str(&lookup, buf, sizeof buf),
-+ ri->ifp ? ri->ifp->name : "(none)");
-+
-+ if (via) *via = ri->via;
-+ if (ifp) *ifp = ri->ifp;
-+ }
-+ if (p) *p = rn->p;
-+ route_unlock_node(rn);
-+ return 1;
-+}
-+
-+enum nhrp_route_type nhrp_route_address(struct interface *in_ifp, union sockunion *addr, struct prefix *p, struct nhrp_peer **peer)
-+{
-+ struct interface *ifp = in_ifp;
-+ struct nhrp_interface *nifp;
-+ struct nhrp_cache *c;
-+ union sockunion via[4];
-+ uint32_t network_id = 0;
-+ afi_t afi = family2afi(sockunion_family(addr));
-+ int i;
-+
-+ if (ifp) {
-+ nifp = ifp->info;
-+ network_id = nifp->afi[afi].network_id;
-+
-+ c = nhrp_cache_get(ifp, addr, 0);
-+ if (c && c->cur.type == NHRP_CACHE_LOCAL) {
-+ if (p) memset(p, 0, sizeof(*p));
-+ return NHRP_ROUTE_LOCAL;
-+ }
-+ }
-+
-+ for (i = 0; i < 4; i++) {
-+ if (!nhrp_route_get_nexthop(addr, p, &via[i], &ifp))
-+ return NHRP_ROUTE_BLACKHOLE;
-+ if (ifp) {
-+ /* Departing from nbma network? */
-+ nifp = ifp->info;
-+ if (network_id && network_id != nifp->afi[afi].network_id)
-+ return NHRP_ROUTE_OFF_NBMA;
-+ }
-+ if (sockunion_family(&via[i]) == AF_UNSPEC)
-+ break;
-+ /* Resolve via node, but return the prefix of first match */
-+ addr = &via[i];
-+ p = NULL;
-+ }
-+
-+ if (ifp) {
-+ c = nhrp_cache_get(ifp, addr, 0);
-+ if (c && c->cur.type >= NHRP_CACHE_DYNAMIC) {
-+ if (p) memset(p, 0, sizeof(*p));
-+ if (c->cur.type == NHRP_CACHE_LOCAL)
-+ return NHRP_ROUTE_LOCAL;
-+ if (peer) *peer = nhrp_peer_ref(c->cur.peer);
-+ return NHRP_ROUTE_NBMA_NEXTHOP;
-+ }
-+ }
-+
-+ return NHRP_ROUTE_BLACKHOLE;
-+}
-+
-+void nhrp_zebra_init(void)
-+{
-+ zebra_rib[AFI_IP] = route_table_init();
-+ zebra_rib[AFI_IP6] = route_table_init();
-+
-+ zclient = zclient_new(master);
-+ zclient->zebra_connected = nhrp_zebra_connected;
-+ zclient->interface_add = nhrp_interface_add;
-+ zclient->interface_delete = nhrp_interface_delete;
-+ zclient->interface_up = nhrp_interface_up;
-+ zclient->interface_down = nhrp_interface_down;
-+ zclient->interface_address_add = nhrp_interface_address_add;
-+ zclient->interface_address_delete = nhrp_interface_address_delete;
-+ zclient->ipv4_route_add = nhrp_route_read;
-+ zclient->ipv4_route_delete = nhrp_route_read;
-+ zclient->ipv6_route_add = nhrp_route_read;
-+ zclient->ipv6_route_delete = nhrp_route_read;
-+
-+ zclient_init(zclient, ZEBRA_ROUTE_NHRP);
-+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_KERNEL, VRF_DEFAULT);
-+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_CONNECT, VRF_DEFAULT);
-+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_STATIC, VRF_DEFAULT);
-+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_RIP, VRF_DEFAULT);
-+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_OSPF, VRF_DEFAULT);
-+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_ISIS, VRF_DEFAULT);
-+ zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_BGP, VRF_DEFAULT);
-+}
-+
-+void nhrp_zebra_terminate(void)
-+{
-+ zclient_stop(zclient);
-+ route_table_finish(zebra_rib[AFI_IP]);
-+ route_table_finish(zebra_rib[AFI_IP6]);
-+}
-+
-diff --git a/nhrpd/nhrp_shortcut.c b/nhrpd/nhrp_shortcut.c
-new file mode 100644
-index 0000000..421f288
---- /dev/null
-+++ b/nhrpd/nhrp_shortcut.c
-@@ -0,0 +1,402 @@
-+/* NHRP shortcut related functions
-+ * 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 "nhrpd.h"
-+#include "table.h"
-+#include "memory.h"
-+#include "thread.h"
-+#include "log.h"
-+#include "nhrp_protocol.h"
-+
-+static struct route_table *shortcut_rib[AFI_MAX];
-+
-+static int nhrp_shortcut_do_purge(struct thread *t);
-+static void nhrp_shortcut_delete(struct nhrp_shortcut *s);
-+static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s);
-+
-+static void nhrp_shortcut_check_use(struct nhrp_shortcut *s)
-+{
-+ char buf[PREFIX_STRLEN];
-+
-+ if (s->expiring && s->cache && s->cache->used) {
-+ debugf(NHRP_DEBUG_ROUTE, "Shortcut %s used and expiring",
-+ prefix2str(s->p, buf, sizeof buf));
-+ nhrp_shortcut_send_resolution_req(s);
-+ }
-+}
-+
-+static int nhrp_shortcut_do_expire(struct thread *t)
-+{
-+ struct nhrp_shortcut *s = THREAD_ARG(t);
-+
-+ s->t_timer = NULL;
-+ THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, s->holding_time/3);
-+ s->expiring = 1;
-+ nhrp_shortcut_check_use(s);
-+
-+ return 0;
-+}
-+
-+static void nhrp_shortcut_cache_notify(struct notifier_block *n, unsigned long cmd)
-+{
-+ struct nhrp_shortcut *s = container_of(n, struct nhrp_shortcut, cache_notifier);
-+
-+ switch (cmd) {
-+ case NOTIFY_CACHE_UP:
-+ if (!s->route_installed) {
-+ nhrp_route_announce(1, s->type, s->p, NULL, &s->cache->remote_addr, 0);
-+ s->route_installed = 1;
-+ }
-+ break;
-+ case NOTIFY_CACHE_USED:
-+ nhrp_shortcut_check_use(s);
-+ break;
-+ case NOTIFY_CACHE_DOWN:
-+ case NOTIFY_CACHE_DELETE:
-+ if (s->route_installed) {
-+ nhrp_route_announce(0, NHRP_CACHE_INVALID, s->p, NULL, NULL, 0);
-+ s->route_installed = 0;
-+ }
-+ if (cmd == NOTIFY_CACHE_DELETE)
-+ nhrp_shortcut_delete(s);
-+ break;
-+ }
-+}
-+
-+static void nhrp_shortcut_update_binding(struct nhrp_shortcut *s, enum nhrp_cache_type type, struct nhrp_cache *c, int holding_time)
-+{
-+ s->type = type;
-+ if (c != s->cache) {
-+ if (s->cache) {
-+ nhrp_cache_notify_del(s->cache, &s->cache_notifier);
-+ s->cache = NULL;
-+ }
-+ s->cache = c;
-+ if (s->cache) {
-+ nhrp_cache_notify_add(s->cache, &s->cache_notifier, nhrp_shortcut_cache_notify);
-+ if (s->cache->route_installed) {
-+ /* Force renewal of Zebra announce on prefix change */
-+ s->route_installed = 0;
-+ nhrp_shortcut_cache_notify(&s->cache_notifier, NOTIFY_CACHE_UP);
-+ }
-+ }
-+ if (!s->cache || !s->cache->route_installed)
-+ nhrp_shortcut_cache_notify(&s->cache_notifier, NOTIFY_CACHE_DOWN);
-+ }
-+ if (s->type == NHRP_CACHE_NEGATIVE && !s->route_installed) {
-+ nhrp_route_announce(1, s->type, s->p, NULL, NULL, 0);
-+ s->route_installed = 1;
-+ } else if (s->type == NHRP_CACHE_INVALID && s->route_installed) {
-+ nhrp_route_announce(0, NHRP_CACHE_INVALID, s->p, NULL, NULL, 0);
-+ s->route_installed = 0;
-+ }
-+
-+ THREAD_OFF(s->t_timer);
-+ if (holding_time) {
-+ s->expiring = 0;
-+ s->holding_time = holding_time;
-+ THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_expire, s, 2*holding_time/3);
-+ }
-+}
-+
-+static void nhrp_shortcut_delete(struct nhrp_shortcut *s)
-+{
-+ struct route_node *rn;
-+ afi_t afi = family2afi(PREFIX_FAMILY(s->p));
-+ char buf[PREFIX_STRLEN];
-+
-+ THREAD_OFF(s->t_timer);
-+ nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
-+
-+ debugf(NHRP_DEBUG_ROUTE, "Shortcut %s purged",
-+ prefix2str(s->p, buf, sizeof buf));
-+
-+ nhrp_shortcut_update_binding(s, NHRP_CACHE_INVALID, NULL, 0);
-+
-+ /* Delete node */
-+ rn = route_node_lookup(shortcut_rib[afi], s->p);
-+ if (rn) {
-+ XFREE(MTYPE_NHRP_SHORTCUT, rn->info);
-+ rn->info = NULL;
-+ route_unlock_node(rn);
-+ route_unlock_node(rn);
-+ }
-+}
-+
-+static int nhrp_shortcut_do_purge(struct thread *t)
-+{
-+ struct nhrp_shortcut *s = THREAD_ARG(t);
-+ s->t_timer = NULL;
-+ nhrp_shortcut_delete(s);
-+ return 0;
-+}
-+
-+static struct nhrp_shortcut *nhrp_shortcut_get(struct prefix *p)
-+{
-+ struct nhrp_shortcut *s;
-+ struct route_node *rn;
-+ char buf[PREFIX_STRLEN];
-+ afi_t afi = family2afi(PREFIX_FAMILY(p));
-+
-+ if (!shortcut_rib[afi])
-+ return 0;
-+
-+ rn = route_node_get(shortcut_rib[afi], p);
-+ if (!rn->info) {
-+ s = rn->info = XCALLOC(MTYPE_NHRP_SHORTCUT, sizeof(struct nhrp_shortcut));
-+ s->type = NHRP_CACHE_INVALID;
-+ s->p = &rn->p;
-+
-+ debugf(NHRP_DEBUG_ROUTE, "Shortcut %s created",
-+ prefix2str(s->p, buf, sizeof buf));
-+ } else {
-+ s = rn->info;
-+ route_unlock_node(rn);
-+ }
-+ return s;
-+}
-+
-+static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, void *arg)
-+{
-+ struct nhrp_packet_parser *pp = arg;
-+ struct nhrp_shortcut *s = container_of(reqid, struct nhrp_shortcut, reqid);
-+ struct nhrp_shortcut *ps;
-+ struct nhrp_extension_header *ext;
-+ struct nhrp_cie_header *cie;
-+ struct nhrp_cache *c = NULL;
-+ union sockunion *proto, cie_proto, *nbma, *nbma_natoa, cie_nbma, nat_nbma;
-+ struct prefix prefix, route_prefix;
-+ struct zbuf extpl;
-+ char bufp[PREFIX_STRLEN], buf[3][SU_ADDRSTRLEN];
-+ int holding_time = pp->if_ad->holdtime;
-+
-+ nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
-+ THREAD_OFF(s->t_timer);
-+ THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 1);
-+
-+ if (pp->hdr->type != NHRP_PACKET_RESOLUTION_REPLY) {
-+ if (pp->hdr->type == NHRP_PACKET_ERROR_INDICATION &&
-+ pp->hdr->u.error.code == NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE) {
-+ debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution: Protocol address unreachable");
-+ nhrp_shortcut_update_binding(s, NHRP_CACHE_NEGATIVE, NULL, holding_time);
-+ } else {
-+ debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution failed");
-+ }
-+ return;
-+ }
-+
-+ /* Parse extensions */
-+ memset(&nat_nbma, 0, sizeof nat_nbma);
-+ while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) {
-+ switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
-+ case NHRP_EXTENSION_NAT_ADDRESS:
-+ nhrp_cie_pull(&extpl, pp->hdr, &nat_nbma, &cie_proto);
-+ break;
-+ }
-+ }
-+
-+ /* Minor sanity check */
-+ prefix2sockunion(s->p, &cie_proto);
-+ if (!sockunion_same(&cie_proto, &pp->dst_proto)) {
-+ debugf(NHRP_DEBUG_COMMON, "Shortcut: Warning dst_proto altered from %s to %s",
-+ sockunion2str(&cie_proto, buf[0], sizeof buf[0]),
-+ sockunion2str(&pp->dst_proto, buf[1], sizeof buf[1]));
-+ }
-+
-+ /* One or more CIEs should be given as reply, we support only one */
-+ cie = nhrp_cie_pull(&pp->payload, pp->hdr, &cie_nbma, &cie_proto);
-+ if (!cie || cie->code != NHRP_CODE_SUCCESS) {
-+ debugf(NHRP_DEBUG_COMMON, "Shortcut: CIE code %d", cie ? cie->code : -1);
-+ return;
-+ }
-+
-+ proto = sockunion_family(&cie_proto) != AF_UNSPEC ? &cie_proto : &pp->dst_proto;
-+ if (cie->holding_time)
-+ holding_time = htons(cie->holding_time);
-+
-+ prefix = *s->p;
-+ prefix.prefixlen = cie->prefix_length;
-+
-+ /* Sanity check prefix length */
-+ if (prefix.prefixlen >= 8*prefix_blen(&prefix)) {
-+ prefix.prefixlen = 8*prefix_blen(&prefix);
-+ } else if (nhrp_route_address(NULL, &pp->dst_proto, &route_prefix, NULL) == NHRP_ROUTE_NBMA_NEXTHOP) {
-+ if (prefix.prefixlen < route_prefix.prefixlen)
-+ prefix.prefixlen = route_prefix.prefixlen;
-+ }
-+
-+ debugf(NHRP_DEBUG_COMMON, "Shortcut: %s is at proto %s cie-nbma %s nat-nbma %s cie-holdtime %d",
-+ prefix2str(&prefix, bufp, sizeof bufp),
-+ sockunion2str(proto, buf[0], sizeof buf[0]),
-+ sockunion2str(&cie_nbma, buf[1], sizeof buf[1]),
-+ sockunion2str(&nat_nbma, buf[2], sizeof buf[2]),
-+ htons(cie->holding_time));
-+
-+ /* Update cache entry for the protocol to nbma binding */
-+ if (sockunion_family(&nat_nbma) != AF_UNSPEC) {
-+ nbma = &nat_nbma;
-+ nbma_natoa = &cie_nbma;
-+ } else {
-+ nbma = &cie_nbma;
-+ nbma_natoa = NULL;
-+ }
-+ if (sockunion_family(nbma)) {
-+ c = nhrp_cache_get(pp->ifp, proto, 1);
-+ if (c) {
-+ nhrp_cache_update_binding(
-+ c, NHRP_CACHE_CACHED, holding_time,
-+ nhrp_peer_get(pp->ifp, nbma),
-+ htons(cie->mtu), nbma_natoa);
-+ }
-+ }
-+
-+ /* Update shortcut entry for subnet to protocol gw binding */
-+ if (c && !sockunion_same(proto, &pp->dst_proto)) {
-+ ps = nhrp_shortcut_get(&prefix);
-+ if (ps) {
-+ ps->addr = s->addr;
-+ nhrp_shortcut_update_binding(ps, NHRP_CACHE_CACHED, c, holding_time);
-+ }
-+ }
-+
-+ debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution reply handled");
-+}
-+
-+static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s)
-+{
-+ struct zbuf *zb;
-+ struct nhrp_packet_header *hdr;
-+ struct interface *ifp;
-+ struct nhrp_interface *nifp;
-+ struct nhrp_peer *peer;
-+
-+ if (nhrp_route_address(NULL, &s->addr, NULL, &peer) != NHRP_ROUTE_NBMA_NEXTHOP)
-+ return;
-+
-+ if (s->type == NHRP_CACHE_INVALID || s->type == NHRP_CACHE_NEGATIVE)
-+ s->type = NHRP_CACHE_INCOMPLETE;
-+
-+ ifp = peer->ifp;
-+ nifp = ifp->info;
-+
-+ /* Create request */
-+ zb = zbuf_alloc(1500);
-+ hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REQUEST,
-+ &nifp->nbma, &nifp->afi[family2afi(sockunion_family(&s->addr))].addr, &s->addr);
-+ hdr->u.request_id = htonl(nhrp_reqid_alloc(&nhrp_packet_reqid, &s->reqid, nhrp_shortcut_recv_resolution_rep));
-+ hdr->flags = htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER |
-+ NHRP_FLAG_RESOLUTION_AUTHORATIVE |
-+ NHRP_FLAG_RESOLUTION_SOURCE_STABLE);
-+
-+ /* RFC2332 - One or zero CIEs, if CIE is present contains:
-+ * - Prefix length: widest acceptable prefix we accept (if U set, 0xff)
-+ * - MTU: MTU of the source station
-+ * - Holding Time: Max time to cache the source information
-+ * */
-+ /* FIXME: Send holding time, and MTU */
-+
-+ nhrp_ext_request(zb, hdr, ifp);
-+
-+ /* Cisco NAT detection extension */
-+ hdr->flags |= htons(NHRP_FLAG_RESOLUTION_NAT);
-+ nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
-+
-+ nhrp_packet_complete(zb, hdr);
-+
-+ nhrp_peer_send(peer, zb);
-+ nhrp_peer_unref(peer);
-+ zbuf_free(zb);
-+}
-+
-+void nhrp_shortcut_initiate(union sockunion *addr)
-+{
-+ struct prefix p;
-+ struct nhrp_shortcut *s;
-+
-+ sockunion2hostprefix(addr, &p);
-+ s = nhrp_shortcut_get(&p);
-+ if (s && s->type != NHRP_CACHE_INCOMPLETE) {
-+ s->addr = *addr;
-+ THREAD_OFF(s->t_timer);
-+ THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 30);
-+ nhrp_shortcut_send_resolution_req(s);
-+ }
-+}
-+
-+void nhrp_shortcut_init(void)
-+{
-+ shortcut_rib[AFI_IP] = route_table_init();
-+ shortcut_rib[AFI_IP6] = route_table_init();
-+}
-+
-+void nhrp_shortcut_terminate(void)
-+{
-+ route_table_finish(shortcut_rib[AFI_IP]);
-+ route_table_finish(shortcut_rib[AFI_IP6]);
-+}
-+
-+void nhrp_shortcut_foreach(afi_t afi, void (*cb)(struct nhrp_shortcut *, void *), void *ctx)
-+{
-+ struct route_table *rt = shortcut_rib[afi];
-+ struct route_node *rn;
-+ route_table_iter_t iter;
-+
-+ if (!rt) return;
-+
-+ route_table_iter_init(&iter, rt);
-+ while ((rn = route_table_iter_next(&iter)) != NULL) {
-+ if (rn->info) cb(rn->info, ctx);
-+ }
-+ route_table_iter_cleanup(&iter);
-+}
-+
-+struct purge_ctx {
-+ const struct prefix *p;
-+ int deleted;
-+};
-+
-+void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force)
-+{
-+ THREAD_OFF(s->t_timer);
-+ nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
-+
-+ if (force) {
-+ /* Immediate purge on route with draw or pending shortcut */
-+ THREAD_TIMER_MSEC_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 5);
-+ } else {
-+ /* Soft expire - force immediate renewal, but purge
-+ * in few seconds to make sure stale route is not
-+ * used too long. In practice most purges are caused
-+ * by hub bgp change, but target usually stays same.
-+ * This allows to keep nhrp route up, and to not
-+ * cause temporary rerouting via hubs causing latency
-+ * jitter. */
-+ THREAD_TIMER_MSEC_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 3000);
-+ s->expiring = 1;
-+ nhrp_shortcut_check_use(s);
-+ }
-+}
-+
-+static void nhrp_shortcut_purge_prefix(struct nhrp_shortcut *s, void *ctx)
-+{
-+ struct purge_ctx *pctx = ctx;
-+
-+ if (prefix_match(pctx->p, s->p))
-+ nhrp_shortcut_purge(s, pctx->deleted || !s->cache);
-+}
-+
-+void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted)
-+{
-+ struct purge_ctx pctx = {
-+ .p = p,
-+ .deleted = deleted,
-+ };
-+ nhrp_shortcut_foreach(family2afi(PREFIX_FAMILY(p)), nhrp_shortcut_purge_prefix, &pctx);
-+}
-+
-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);
-+}
-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);
-+}
-diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h
-new file mode 100644
-index 0000000..c8f8816
---- /dev/null
-+++ b/nhrpd/nhrpd.h
-@@ -0,0 +1,440 @@
-+/* NHRP daemon internal structures and function prototypes
-+ * 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.
-+ */
-+
-+#ifndef NHRPD_H
-+#define NHRPD_H
-+
-+#include "list.h"
-+
-+#include "zebra.h"
-+#include "zbuf.h"
-+#include "zclient.h"
-+#include "log.h"
-+
-+#define NHRPD_DEFAULT_HOLDTIME 7200
-+
-+#define NHRP_DEBUG_COMMON (1 << 0)
-+#define NHRP_DEBUG_KERNEL (1 << 1)
-+#define NHRP_DEBUG_IF (1 << 2)
-+#define NHRP_DEBUG_ROUTE (1 << 3)
-+#define NHRP_DEBUG_VICI (1 << 4)
-+#define NHRP_DEBUG_EVENT (1 << 5)
-+#define NHRP_DEBUG_ALL (0xFFFF)
-+
-+#define NHRP_VTY_PORT 2612
-+#define NHRP_DEFAULT_CONFIG "nhrpd.conf"
-+
-+#if defined(__GNUC__) && (__GNUC__ >= 3)
-+#define likely(_x) __builtin_expect(!!(_x), 1)
-+#define unlikely(_x) __builtin_expect(!!(_x), 0)
-+#else
-+#define likely(_x) !!(_x)
-+#define unlikely(_x) !!(_x)
-+#endif
-+
-+extern unsigned int debug_flags;
-+extern struct thread_master *master;
-+
-+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
-+
-+#define debugf(level, ...) \
-+ do { \
-+ if (unlikely(debug_flags & level)) \
-+ zlog_debug(__VA_ARGS__); \
-+ } while(0)
-+
-+#elif defined __GNUC__
-+
-+#define debugf(level, _args...) \
-+ do { \
-+ if (unlikely(debug_flags & level)) \
-+ zlog_debug(_args); \
-+ } while(0)
-+
-+#else
-+
-+static inline void debugf(int level, const char *format, ...) { }
-+
-+#endif
-+
-+enum {
-+ NHRP_OK = 0,
-+ NHRP_ERR_FAIL,
-+ NHRP_ERR_NO_MEMORY,
-+ NHRP_ERR_UNSUPPORTED_INTERFACE,
-+ NHRP_ERR_NHRP_NOT_ENABLED,
-+ NHRP_ERR_ENTRY_EXISTS,
-+ NHRP_ERR_ENTRY_NOT_FOUND,
-+ NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH,
-+};
-+
-+struct notifier_block;
-+
-+typedef void (*notifier_fn_t)(struct notifier_block *, unsigned long);
-+
-+struct notifier_block {
-+ struct list_head notifier_entry;
-+ notifier_fn_t action;
-+};
-+
-+struct notifier_list {
-+ struct list_head notifier_head;
-+};
-+
-+#define NOTIFIER_LIST_INITIALIZER(l) \
-+ { .notifier_head = LIST_INITIALIZER((l)->notifier_head) }
-+
-+static inline void notifier_init(struct notifier_list *l)
-+{
-+ list_init(&l->notifier_head);
-+}
-+
-+static inline void notifier_add(struct notifier_block *n, struct notifier_list *l, notifier_fn_t action)
-+{
-+ n->action = action;
-+ list_add_tail(&n->notifier_entry, &l->notifier_head);
-+}
-+
-+static inline void notifier_del(struct notifier_block *n)
-+{
-+ list_del(&n->notifier_entry);
-+}
-+
-+static inline void notifier_call(struct notifier_list *l, int cmd)
-+{
-+ struct notifier_block *n, *nn;
-+ list_for_each_entry_safe(n, nn, &l->notifier_head, notifier_entry)
-+ n->action(n, cmd);
-+}
-+
-+static inline int notifier_active(struct notifier_list *l)
-+{
-+ return !list_empty(&l->notifier_head);
-+}
-+
-+struct resolver_query {
-+ void (*callback)(struct resolver_query *, int n, union sockunion *);
-+};
-+
-+void resolver_init(void);
-+void resolver_resolve(struct resolver_query *query, int af, const char *hostname, void (*cb)(struct resolver_query *, int, union sockunion *));
-+
-+void nhrp_zebra_init(void);
-+void nhrp_zebra_terminate(void);
-+
-+struct zbuf;
-+struct nhrp_vc;
-+struct nhrp_cache;
-+struct nhrp_nhs;
-+struct nhrp_interface;
-+
-+#define MAX_ID_LENGTH 64
-+#define MAX_CERT_LENGTH 2048
-+
-+enum nhrp_notify_type {
-+ NOTIFY_INTERFACE_UP,
-+ NOTIFY_INTERFACE_DOWN,
-+ NOTIFY_INTERFACE_CHANGED,
-+ NOTIFY_INTERFACE_ADDRESS_CHANGED,
-+ NOTIFY_INTERFACE_NBMA_CHANGED,
-+ NOTIFY_INTERFACE_MTU_CHANGED,
-+
-+ NOTIFY_VC_IPSEC_CHANGED,
-+ NOTIFY_VC_IPSEC_UPDATE_NBMA,
-+
-+ NOTIFY_PEER_UP,
-+ NOTIFY_PEER_DOWN,
-+ NOTIFY_PEER_IFCONFIG_CHANGED,
-+ NOTIFY_PEER_MTU_CHANGED,
-+ NOTIFY_PEER_NBMA_CHANGING,
-+
-+ NOTIFY_CACHE_UP,
-+ NOTIFY_CACHE_DOWN,
-+ NOTIFY_CACHE_DELETE,
-+ NOTIFY_CACHE_USED,
-+ NOTIFY_CACHE_BINDING_CHANGE,
-+};
-+
-+struct nhrp_vc {
-+ struct notifier_list notifier_list;
-+ uint8_t ipsec;
-+ uint8_t updating;
-+ uint8_t abort_migration;
-+
-+ struct nhrp_vc_peer {
-+ union sockunion nbma;
-+ char id[MAX_ID_LENGTH];
-+ uint16_t certlen;
-+ uint8_t cert[MAX_CERT_LENGTH];
-+ } local, remote;
-+};
-+
-+enum nhrp_route_type {
-+ NHRP_ROUTE_BLACKHOLE,
-+ NHRP_ROUTE_LOCAL,
-+ NHRP_ROUTE_NBMA_NEXTHOP,
-+ NHRP_ROUTE_OFF_NBMA,
-+};
-+
-+struct nhrp_peer {
-+ unsigned int ref;
-+ unsigned online : 1;
-+ unsigned requested : 1;
-+ unsigned fallback_requested : 1;
-+ unsigned prio : 1;
-+ struct notifier_list notifier_list;
-+ struct interface *ifp;
-+ struct nhrp_vc *vc;
-+ struct thread *t_fallback;
-+ struct notifier_block vc_notifier, ifp_notifier;
-+};
-+
-+struct nhrp_packet_parser {
-+ struct interface *ifp;
-+ struct nhrp_afi_data *if_ad;
-+ struct nhrp_peer *peer;
-+ struct zbuf *pkt;
-+ struct zbuf payload;
-+ struct zbuf extensions;
-+ struct nhrp_packet_header *hdr;
-+ enum nhrp_route_type route_type;
-+ struct prefix route_prefix;
-+ union sockunion src_nbma, src_proto, dst_proto;
-+};
-+
-+struct nhrp_reqid_pool {
-+ struct hash *reqid_hash;
-+ uint32_t next_request_id;
-+};
-+
-+struct nhrp_reqid {
-+ uint32_t request_id;
-+ void (*cb)(struct nhrp_reqid *, void *);
-+};
-+
-+extern struct nhrp_reqid_pool nhrp_packet_reqid;
-+extern struct nhrp_reqid_pool nhrp_event_reqid;
-+
-+enum nhrp_cache_type {
-+ NHRP_CACHE_INVALID = 0,
-+ NHRP_CACHE_INCOMPLETE,
-+ NHRP_CACHE_NEGATIVE,
-+ NHRP_CACHE_CACHED,
-+ NHRP_CACHE_DYNAMIC,
-+ NHRP_CACHE_NHS,
-+ NHRP_CACHE_STATIC,
-+ NHRP_CACHE_LOCAL,
-+ NHRP_CACHE_NUM_TYPES
-+};
-+
-+extern const char * const nhrp_cache_type_str[];
-+extern unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES];
-+
-+struct nhrp_cache {
-+ struct interface *ifp;
-+ union sockunion remote_addr;
-+
-+ unsigned map : 1;
-+ unsigned used : 1;
-+ unsigned route_installed : 1;
-+ unsigned nhrp_route_installed : 1;
-+
-+ struct notifier_block peer_notifier;
-+ struct notifier_block newpeer_notifier;
-+ struct notifier_list notifier_list;
-+ struct nhrp_reqid eventid;
-+ struct thread *t_timeout;
-+ struct thread *t_auth;
-+
-+ struct {
-+ enum nhrp_cache_type type;
-+ union sockunion remote_nbma_natoa;
-+ struct nhrp_peer *peer;
-+ time_t expires;
-+ uint32_t mtu;
-+ } cur, new;
-+};
-+
-+struct nhrp_shortcut {
-+ struct prefix *p;
-+ union sockunion addr;
-+
-+ struct nhrp_reqid reqid;
-+ struct thread *t_timer;
-+
-+ enum nhrp_cache_type type;
-+ unsigned int holding_time;
-+ unsigned route_installed : 1;
-+ unsigned expiring : 1;
-+
-+ struct nhrp_cache *cache;
-+ struct notifier_block cache_notifier;
-+};
-+
-+struct nhrp_nhs {
-+ struct interface *ifp;
-+ struct list_head nhslist_entry;
-+
-+ unsigned hub : 1;
-+ afi_t afi;
-+ union sockunion proto_addr;
-+ const char *nbma_fqdn; /* IP-address or FQDN */
-+
-+ struct thread *t_resolve;
-+ struct resolver_query dns_resolve;
-+ struct list_head reglist_head;
-+};
-+
-+#define NHRP_IFF_SHORTCUT 0x0001
-+#define NHRP_IFF_REDIRECT 0x0002
-+#define NHRP_IFF_REG_NO_UNIQUE 0x0100
-+
-+struct nhrp_interface {
-+ struct interface *ifp;
-+
-+ unsigned enabled : 1;
-+
-+ char *ipsec_profile, *ipsec_fallback_profile, *source;
-+ union sockunion nbma;
-+ union sockunion nat_nbma;
-+ unsigned int linkidx;
-+ uint32_t grekey;
-+
-+ struct hash *peer_hash;
-+ struct hash *cache_hash;
-+
-+ struct notifier_list notifier_list;
-+
-+ struct interface *nbmaifp;
-+ struct notifier_block nbmanifp_notifier;
-+
-+ struct nhrp_afi_data {
-+ unsigned flags;
-+ unsigned short configured : 1;
-+ union sockunion addr;
-+ uint32_t network_id;
-+ short configured_mtu;
-+ unsigned short mtu;
-+ unsigned int holdtime;
-+ struct list_head nhslist_head;
-+ } afi[AFI_MAX];
-+};
-+
-+int sock_open_unix(const char *path);
-+
-+void nhrp_interface_init(void);
-+void nhrp_interface_update(struct interface *ifp);
-+void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi);
-+
-+int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
-+int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
-+int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
-+int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
-+int nhrp_interface_address_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
-+int nhrp_interface_address_delete(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id);
-+
-+void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn);
-+void nhrp_interface_notify_del(struct interface *ifp, struct notifier_block *n);
-+void nhrp_interface_set_protection(struct interface *ifp, const char *profile, const char *fallback_profile);
-+void nhrp_interface_set_source(struct interface *ifp, const char *ifname);
-+
-+int nhrp_nhs_add(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn);
-+int nhrp_nhs_del(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn);
-+int nhrp_nhs_free(struct nhrp_nhs *nhs);
-+void nhrp_nhs_terminate(void);
-+
-+void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp);
-+void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix *p, struct interface *ifp, const union sockunion *nexthop, uint32_t mtu);
-+int nhrp_route_read(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id);
-+int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, union sockunion *via, struct interface **ifp);
-+enum nhrp_route_type nhrp_route_address(struct interface *in_ifp, union sockunion *addr, struct prefix *p, struct nhrp_peer **peer);
-+
-+void nhrp_config_init(void);
-+
-+void nhrp_shortcut_init(void);
-+void nhrp_shortcut_terminate(void);
-+void nhrp_shortcut_initiate(union sockunion *addr);
-+void nhrp_shortcut_foreach(afi_t afi, void (*cb)(struct nhrp_shortcut *, void *), void *ctx);
-+void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force);
-+void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted);
-+
-+struct nhrp_cache *nhrp_cache_get(struct interface *ifp, union sockunion *remote_addr, int create);
-+void nhrp_cache_foreach(struct interface *ifp, void (*cb)(struct nhrp_cache *, void *), void *ctx);
-+void nhrp_cache_set_used(struct nhrp_cache *, int);
-+int nhrp_cache_update_binding(struct nhrp_cache *, enum nhrp_cache_type type, int holding_time, struct nhrp_peer *p, uint32_t mtu, union sockunion *nbma_natoa);
-+void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *, notifier_fn_t);
-+void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *);
-+
-+void nhrp_vc_init(void);
-+void nhrp_vc_terminate(void);
-+struct nhrp_vc *nhrp_vc_get(const union sockunion *src, const union sockunion *dst, int create);
-+int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc);
-+void nhrp_vc_notify_add(struct nhrp_vc *, struct notifier_block *, notifier_fn_t);
-+void nhrp_vc_notify_del(struct nhrp_vc *, struct notifier_block *);
-+void nhrp_vc_foreach(void (*cb)(struct nhrp_vc *, void *), void *ctx);
-+void nhrp_vc_reset(void);
-+
-+void vici_init(void);
-+void vici_terminate(void);
-+void vici_request_vc(const char *profile, union sockunion *src, union sockunion *dst, int prio);
-+
-+extern const char *nhrp_event_socket_path;
-+
-+void evmgr_init(void);
-+void evmgr_terminate(void);
-+void evmgr_set_socket(const char *socket);
-+void evmgr_notify(const char *name, struct nhrp_cache *c, void (*cb)(struct nhrp_reqid *, void *));
-+
-+struct nhrp_packet_header *nhrp_packet_push(
-+ struct zbuf *zb, uint8_t type,
-+ const union sockunion *src_nbma,
-+ const union sockunion *src_proto,
-+ const union sockunion *dst_proto);
-+void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr);
-+uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len);
-+
-+struct nhrp_packet_header *nhrp_packet_pull(
-+ struct zbuf *zb,
-+ union sockunion *src_nbma,
-+ union sockunion *src_proto,
-+ union sockunion *dst_proto);
-+
-+struct nhrp_cie_header *nhrp_cie_push(
-+ struct zbuf *zb, uint8_t code,
-+ const union sockunion *nbma,
-+ const union sockunion *proto);
-+struct nhrp_cie_header *nhrp_cie_pull(
-+ struct zbuf *zb,
-+ struct nhrp_packet_header *hdr,
-+ union sockunion *nbma,
-+ union sockunion *proto);
-+
-+struct nhrp_extension_header *nhrp_ext_push(struct zbuf *zb, struct nhrp_packet_header *hdr, uint16_t type);
-+void nhrp_ext_complete(struct zbuf *zb, struct nhrp_extension_header *ext);
-+struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb, struct zbuf *payload);
-+void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *);
-+int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp, struct nhrp_extension_header *ext, struct zbuf *extpayload);
-+
-+uint32_t nhrp_reqid_alloc(struct nhrp_reqid_pool *, struct nhrp_reqid *r, void (*cb)(struct nhrp_reqid *, void *));
-+void nhrp_reqid_free(struct nhrp_reqid_pool *, struct nhrp_reqid *r);
-+struct nhrp_reqid *nhrp_reqid_lookup(struct nhrp_reqid_pool *, uint32_t reqid);
-+
-+int nhrp_packet_init(void);
-+
-+struct nhrp_peer *nhrp_peer_get(struct interface *ifp, const union sockunion *remote_nbma);
-+struct nhrp_peer *nhrp_peer_ref(struct nhrp_peer *p);
-+void nhrp_peer_unref(struct nhrp_peer *p);
-+int nhrp_peer_check(struct nhrp_peer *p, int establish);
-+void nhrp_peer_notify_add(struct nhrp_peer *p, struct notifier_block *, notifier_fn_t);
-+void nhrp_peer_notify_del(struct nhrp_peer *p, struct notifier_block *);
-+void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb);
-+void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb);
-+void nhrp_peer_send_indication(struct interface *ifp, uint16_t, struct zbuf *);
-+
-+#endif
-diff --git a/nhrpd/os.h b/nhrpd/os.h
-new file mode 100644
-index 0000000..0fbe8b0
---- /dev/null
-+++ b/nhrpd/os.h
-@@ -0,0 +1,5 @@
-+
-+int os_socket(void);
-+int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr, size_t addrlen);
-+int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr, size_t *addrlen);
-+int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af);
-diff --git a/nhrpd/reqid.c b/nhrpd/reqid.c
-new file mode 100644
-index 0000000..24b3199
---- /dev/null
-+++ b/nhrpd/reqid.c
-@@ -0,0 +1,49 @@
-+#include "zebra.h"
-+#include "hash.h"
-+#include "nhrpd.h"
-+
-+static unsigned int nhrp_reqid_key(void *data)
-+{
-+ struct nhrp_reqid *r = data;
-+ return r->request_id;
-+}
-+
-+static int nhrp_reqid_cmp(const void *data, const void *key)
-+{
-+ const struct nhrp_reqid *a = data, *b = key;
-+ return a->request_id == b->request_id;
-+}
-+
-+uint32_t nhrp_reqid_alloc(struct nhrp_reqid_pool *p, struct nhrp_reqid *r, void (*cb)(struct nhrp_reqid *, void *))
-+{
-+ if (!p->reqid_hash) {
-+ p->reqid_hash = hash_create(nhrp_reqid_key, nhrp_reqid_cmp);
-+ p->next_request_id = 1;
-+ }
-+
-+ if (r->cb != cb) {
-+ r->request_id = p->next_request_id;
-+ if (++p->next_request_id == 0) p->next_request_id = 1;
-+ r->cb = cb;
-+ hash_get(p->reqid_hash, r, hash_alloc_intern);
-+ }
-+ return r->request_id;
-+}
-+
-+void nhrp_reqid_free(struct nhrp_reqid_pool *p, struct nhrp_reqid *r)
-+{
-+ if (r->cb) {
-+ hash_release(p->reqid_hash, r);
-+ r->cb = NULL;
-+ }
-+}
-+
-+struct nhrp_reqid *nhrp_reqid_lookup(struct nhrp_reqid_pool *p, uint32_t reqid)
-+{
-+ struct nhrp_reqid key;
-+ if (!p->reqid_hash) return 0;
-+ key.request_id = reqid;
-+ return hash_lookup(p->reqid_hash, &key);
-+}
-+
-+
-diff --git a/nhrpd/resolver.c b/nhrpd/resolver.c
-new file mode 100644
-index 0000000..07bdb73
---- /dev/null
-+++ b/nhrpd/resolver.c
-@@ -0,0 +1,190 @@
-+/* C-Ares integration to Quagga mainloop
-+ * 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 <ares.h>
-+#include <ares_version.h>
-+
-+#include "vector.h"
-+#include "thread.h"
-+#include "nhrpd.h"
-+
-+struct resolver_state {
-+ ares_channel channel;
-+ struct thread *timeout;
-+ vector read_threads, write_threads;
-+};
-+
-+static struct resolver_state state;
-+
-+#define THREAD_RUNNING ((struct thread *)-1)
-+
-+static void resolver_update_timeouts(struct resolver_state *r);
-+
-+static int resolver_cb_timeout(struct thread *t)
-+{
-+ struct resolver_state *r = THREAD_ARG(t);
-+
-+ r->timeout = THREAD_RUNNING;
-+ ares_process(r->channel, NULL, NULL);
-+ r->timeout = NULL;
-+ resolver_update_timeouts(r);
-+
-+ return 0;
-+}
-+
-+static int resolver_cb_socket_readable(struct thread *t)
-+{
-+ struct resolver_state *r = THREAD_ARG(t);
-+ int fd = THREAD_FD(t);
-+
-+ vector_set_index(r->read_threads, fd, THREAD_RUNNING);
-+ ares_process_fd(r->channel, fd, ARES_SOCKET_BAD);
-+ if (vector_lookup(r->read_threads, fd) == THREAD_RUNNING) {
-+ t = NULL;
-+ THREAD_READ_ON(master, t, resolver_cb_socket_readable, r, fd);
-+ vector_set_index(r->read_threads, fd, t);
-+ }
-+ resolver_update_timeouts(r);
-+
-+ return 0;
-+}
-+
-+static int resolver_cb_socket_writable(struct thread *t)
-+{
-+ struct resolver_state *r = THREAD_ARG(t);
-+ int fd = THREAD_FD(t);
-+
-+ vector_set_index(r->write_threads, fd, THREAD_RUNNING);
-+ ares_process_fd(r->channel, ARES_SOCKET_BAD, fd);
-+ if (vector_lookup(r->write_threads, fd) == THREAD_RUNNING) {
-+ t = NULL;
-+ THREAD_WRITE_ON(master, t, resolver_cb_socket_writable, r, fd);
-+ vector_set_index(r->write_threads, fd, t);
-+ }
-+ resolver_update_timeouts(r);
-+
-+ return 0;
-+}
-+
-+static void resolver_update_timeouts(struct resolver_state *r)
-+{
-+ struct timeval *tv, tvbuf;
-+
-+ if (r->timeout == THREAD_RUNNING) return;
-+
-+ THREAD_OFF(r->timeout);
-+ tv = ares_timeout(r->channel, NULL, &tvbuf);
-+ if (tv) {
-+ unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
-+ THREAD_TIMER_MSEC_ON(master, r->timeout, resolver_cb_timeout, r, timeoutms);
-+ }
-+}
-+
-+static void ares_socket_cb(void *data, ares_socket_t fd, int readable, int writable)
-+{
-+ struct resolver_state *r = (struct resolver_state *) data;
-+ struct thread *t;
-+
-+ if (readable) {
-+ t = vector_lookup_ensure(r->read_threads, fd);
-+ if (!t) {
-+ THREAD_READ_ON(master, t, resolver_cb_socket_readable, r, fd);
-+ vector_set_index(r->read_threads, fd, t);
-+ }
-+ } else {
-+ t = vector_lookup(r->read_threads, fd);
-+ if (t) {
-+ if (t != THREAD_RUNNING) {
-+ THREAD_OFF(t);
-+ }
-+ vector_unset(r->read_threads, fd);
-+ }
-+ }
-+
-+ if (writable) {
-+ t = vector_lookup_ensure(r->write_threads, fd);
-+ if (!t) {
-+ THREAD_READ_ON(master, t, resolver_cb_socket_writable, r, fd);
-+ vector_set_index(r->write_threads, fd, t);
-+ }
-+ } else {
-+ t = vector_lookup(r->write_threads, fd);
-+ if (t) {
-+ if (t != THREAD_RUNNING) {
-+ THREAD_OFF(t);
-+ }
-+ vector_unset(r->write_threads, fd);
-+ }
-+ }
-+}
-+
-+void resolver_init(void)
-+{
-+ struct ares_options ares_opts;
-+
-+ state.read_threads = vector_init(1);
-+ state.write_threads = vector_init(1);
-+
-+ ares_opts = (struct ares_options) {
-+ .sock_state_cb = &ares_socket_cb,
-+ .sock_state_cb_data = &state,
-+ .timeout = 2,
-+ .tries = 3,
-+ };
-+
-+ ares_init_options(&state.channel, &ares_opts,
-+ ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT |
-+ ARES_OPT_TRIES);
-+}
-+
-+
-+static void ares_address_cb(void *arg, int status, int timeouts, struct hostent *he)
-+{
-+ struct resolver_query *query = (struct resolver_query *) arg;
-+ union sockunion addr[16];
-+ size_t i;
-+
-+ if (status != ARES_SUCCESS) {
-+ debugf(NHRP_DEBUG_COMMON, "[%p] Resolving failed", query);
-+ query->callback(query, -1, NULL);
-+ query->callback = NULL;
-+ return;
-+ }
-+
-+ for (i = 0; he->h_addr_list[i] != NULL && i < ZEBRA_NUM_OF(addr); i++) {
-+ memset(&addr[i], 0, sizeof(addr[i]));
-+ addr[i].sa.sa_family = he->h_addrtype;
-+ switch (he->h_addrtype) {
-+ case AF_INET:
-+ memcpy(&addr[i].sin.sin_addr, (uint8_t *) he->h_addr_list[i], he->h_length);
-+ break;
-+ case AF_INET6:
-+ memcpy(&addr[i].sin6.sin6_addr, (uint8_t *) he->h_addr_list[i], he->h_length);
-+ break;
-+ }
-+ }
-+
-+ debugf(NHRP_DEBUG_COMMON, "[%p] Resolved with %d results", query, (int) i);
-+ query->callback(query, i, &addr[0]);
-+ query->callback = NULL;
-+}
-+
-+void resolver_resolve(struct resolver_query *query, int af, const char *hostname, void (*callback)(struct resolver_query *, int, union sockunion *))
-+{
-+ if (query->callback != NULL) {
-+ zlog_err("Trying to resolve '%s', but previous query was not finished yet", hostname);
-+ return;
-+ }
-+
-+ debugf(NHRP_DEBUG_COMMON, "[%p] Resolving '%s'", query, hostname);
-+
-+ query->callback = callback;
-+ ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query);
-+ resolver_update_timeouts(&state);
-+}
-diff --git a/nhrpd/vici.c b/nhrpd/vici.c
-new file mode 100644
-index 0000000..507dd14
---- /dev/null
-+++ b/nhrpd/vici.c
-@@ -0,0 +1,482 @@
-+/* strongSwan VICI protocol implementation for NHRP
-+ * 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 <string.h>
-+#include <sys/socket.h>
-+#include <sys/un.h>
-+
-+#include "thread.h"
-+#include "zbuf.h"
-+#include "log.h"
-+#include "nhrpd.h"
-+
-+#include "vici.h"
-+
-+#define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
-+
-+struct blob {
-+ char *ptr;
-+ int len;
-+};
-+
-+static int blob_equal(const struct blob *b, const char *str)
-+{
-+ if (b->len != (int) strlen(str)) return 0;
-+ return memcmp(b->ptr, str, b->len) == 0;
-+}
-+
-+static int blob2buf(const struct blob *b, char *buf, size_t n)
-+{
-+ if (b->len >= (int) n) return 0;
-+ memcpy(buf, b->ptr, b->len);
-+ buf[b->len] = 0;
-+ return 1;
-+}
-+
-+struct vici_conn {
-+ struct thread *t_reconnect, *t_read, *t_write;
-+ struct zbuf ibuf;
-+ struct zbuf_queue obuf;
-+ int fd;
-+ uint8_t ibuf_data[VICI_MAX_MSGLEN];
-+};
-+
-+struct vici_message_ctx {
-+ const char *sections[8];
-+ int nsections;
-+};
-+
-+static int vici_reconnect(struct thread *t);
-+static void vici_submit_request(struct vici_conn *vici, const char *name, ...);
-+
-+static void vici_zbuf_puts(struct zbuf *obuf, const char *str)
-+{
-+ size_t len = strlen(str);
-+ zbuf_put8(obuf, len);
-+ zbuf_put(obuf, str, len);
-+}
-+
-+static void vici_connection_error(struct vici_conn *vici)
-+{
-+ nhrp_vc_reset();
-+
-+ THREAD_OFF(vici->t_read);
-+ THREAD_OFF(vici->t_write);
-+ zbuf_reset(&vici->ibuf);
-+ zbufq_reset(&vici->obuf);
-+
-+ close(vici->fd);
-+ vici->fd = -1;
-+ THREAD_TIMER_ON(master, vici->t_reconnect, vici_reconnect, vici, 2);
-+}
-+
-+static void vici_parse_message(
-+ struct vici_conn *vici, struct zbuf *msg,
-+ void (*parser)(struct vici_message_ctx *ctx, enum vici_type_t msgtype, const struct blob *key, const struct blob *val),
-+ struct vici_message_ctx *ctx)
-+{
-+ uint8_t *type;
-+ struct blob key;
-+ struct blob val;
-+
-+ while ((type = zbuf_may_pull(msg, uint8_t)) != NULL) {
-+ switch (*type) {
-+ case VICI_SECTION_START:
-+ key.len = zbuf_get8(msg);
-+ key.ptr = zbuf_pulln(msg, key.len);
-+ debugf(NHRP_DEBUG_VICI, "VICI: Section start '%.*s'", key.len, key.ptr);
-+ parser(ctx, *type, &key, NULL);
-+ ctx->nsections++;
-+ break;
-+ case VICI_SECTION_END:
-+ debugf(NHRP_DEBUG_VICI, "VICI: Section end");
-+ parser(ctx, *type, NULL, NULL);
-+ ctx->nsections--;
-+ break;
-+ case VICI_KEY_VALUE:
-+ key.len = zbuf_get8(msg);
-+ key.ptr = zbuf_pulln(msg, key.len);
-+ val.len = zbuf_get_be16(msg);
-+ val.ptr = zbuf_pulln(msg, val.len);
-+ debugf(NHRP_DEBUG_VICI, "VICI: Key '%.*s'='%.*s'", key.len, key.ptr, val.len, val.ptr);
-+ parser(ctx, *type, &key, &val);
-+ break;
-+ case VICI_LIST_START:
-+ key.len = zbuf_get8(msg);
-+ key.ptr = zbuf_pulln(msg, key.len);
-+ debugf(NHRP_DEBUG_VICI, "VICI: List start '%.*s'", key.len, key.ptr);
-+ break;
-+ case VICI_LIST_ITEM:
-+ val.len = zbuf_get_be16(msg);
-+ val.ptr = zbuf_pulln(msg, val.len);
-+ debugf(NHRP_DEBUG_VICI, "VICI: List item: '%.*s'", val.len, val.ptr);
-+ parser(ctx, *type, &key, &val);
-+ break;
-+ case VICI_LIST_END:
-+ debugf(NHRP_DEBUG_VICI, "VICI: List end");
-+ break;
-+ default:
-+ debugf(NHRP_DEBUG_VICI, "VICI: Unsupported message component type %d", *type);
-+ return;
-+ }
-+ }
-+}
-+
-+struct handle_sa_ctx {
-+ struct vici_message_ctx msgctx;
-+ int event;
-+ int child_ok;
-+ int kill_ikesa;
-+ uint32_t child_uniqueid, ike_uniqueid;
-+ struct {
-+ union sockunion host;
-+ struct blob id, cert;
-+ } local, remote;
-+};
-+
-+static void parse_sa_message(
-+ struct vici_message_ctx *ctx,
-+ enum vici_type_t msgtype,
-+ const struct blob *key, const struct blob *val)
-+{
-+ struct handle_sa_ctx *sactx = container_of(ctx, struct handle_sa_ctx, msgctx);
-+ struct nhrp_vc *vc;
-+ char buf[512];
-+
-+ switch (msgtype) {
-+ case VICI_SECTION_START:
-+ if (ctx->nsections == 3) {
-+ /* Begin of child-sa section, reset child vars */
-+ sactx->child_uniqueid = 0;
-+ sactx->child_ok = 0;
-+ }
-+ break;
-+ case VICI_SECTION_END:
-+ if (ctx->nsections == 3) {
-+ /* End of child-sa section, update nhrp_vc */
-+ int up = sactx->child_ok || sactx->event == 1;
-+ if (up) {
-+ vc = nhrp_vc_get(&sactx->local.host, &sactx->remote.host, up);
-+ if (vc) {
-+ blob2buf(&sactx->local.id, vc->local.id, sizeof(vc->local.id));
-+ if (blob2buf(&sactx->local.cert, (char*)vc->local.cert, sizeof(vc->local.cert)))
-+ vc->local.certlen = sactx->local.cert.len;
-+ blob2buf(&sactx->remote.id, vc->remote.id, sizeof(vc->remote.id));
-+ if (blob2buf(&sactx->remote.cert, (char*)vc->remote.cert, sizeof(vc->remote.cert)))
-+ vc->remote.certlen = sactx->remote.cert.len;
-+ sactx->kill_ikesa |= nhrp_vc_ipsec_updown(sactx->child_uniqueid, vc);
-+ }
-+ } else {
-+ nhrp_vc_ipsec_updown(sactx->child_uniqueid, 0);
-+ }
-+ }
-+ break;
-+ default:
-+ switch (key->ptr[0]) {
-+ case 'l':
-+ if (blob_equal(key, "local-host") && ctx->nsections == 1) {
-+ if (blob2buf(val, buf, sizeof(buf)))
-+ str2sockunion(buf, &sactx->local.host);
-+ } else if (blob_equal(key, "local-id") && ctx->nsections == 1) {
-+ sactx->local.id = *val;
-+ } else if (blob_equal(key, "local-cert-data") && ctx->nsections == 1) {
-+ sactx->local.cert = *val;
-+ }
-+ break;
-+ case 'r':
-+ if (blob_equal(key, "remote-host") && ctx->nsections == 1) {
-+ if (blob2buf(val, buf, sizeof(buf)))
-+ str2sockunion(buf, &sactx->remote.host);
-+ } else if (blob_equal(key, "remote-id") && ctx->nsections == 1) {
-+ sactx->remote.id = *val;
-+ } else if (blob_equal(key, "remote-cert-data") && ctx->nsections == 1) {
-+ sactx->remote.cert = *val;
-+ }
-+ break;
-+ case 'u':
-+ if (blob_equal(key, "uniqueid") && blob2buf(val, buf, sizeof(buf))) {
-+ if (ctx->nsections == 3)
-+ sactx->child_uniqueid = strtoul(buf, NULL, 0);
-+ else if (ctx->nsections == 1)
-+ sactx->ike_uniqueid = strtoul(buf, NULL, 0);
-+ }
-+ break;
-+ case 's':
-+ if (blob_equal(key, "state") && ctx->nsections == 3) {
-+ sactx->child_ok =
-+ (sactx->event == 0 &&
-+ (blob_equal(val, "INSTALLED") ||
-+ blob_equal(val, "REKEYED")));
-+ }
-+ break;
-+ }
-+ break;
-+ }
-+}
-+
-+static void vici_recv_sa(struct vici_conn *vici, struct zbuf *msg, int event)
-+{
-+ char buf[32];
-+ struct handle_sa_ctx ctx = {
-+ .event = event,
-+ };
-+
-+ vici_parse_message(vici, msg, parse_sa_message, &ctx.msgctx);
-+
-+ if (ctx.kill_ikesa && ctx.ike_uniqueid) {
-+ debugf(NHRP_DEBUG_COMMON, "VICI: Deleting IKE_SA %u", ctx.ike_uniqueid);
-+ snprintf(buf, sizeof buf, "%u", ctx.ike_uniqueid);
-+ vici_submit_request(
-+ vici, "terminate",
-+ VICI_KEY_VALUE, "ike-id", strlen(buf), buf,
-+ VICI_END);
-+ }
-+}
-+
-+static void vici_recv_message(struct vici_conn *vici, struct zbuf *msg)
-+{
-+ uint32_t msglen;
-+ uint8_t msgtype;
-+ struct blob name;
-+
-+ msglen = zbuf_get_be32(msg);
-+ msgtype = zbuf_get8(msg);
-+ debugf(NHRP_DEBUG_VICI, "VICI: Message %d, %d bytes", msgtype, msglen);
-+
-+ switch (msgtype) {
-+ case VICI_EVENT:
-+ name.len = zbuf_get8(msg);
-+ name.ptr = zbuf_pulln(msg, name.len);
-+
-+ debugf(NHRP_DEBUG_VICI, "VICI: Event '%.*s'", name.len, name.ptr);
-+ if (blob_equal(&name, "list-sa") ||
-+ blob_equal(&name, "child-updown") ||
-+ blob_equal(&name, "child-rekey"))
-+ vici_recv_sa(vici, msg, 0);
-+ else if (blob_equal(&name, "child-state-installed") ||
-+ blob_equal(&name, "child-state-rekeyed"))
-+ vici_recv_sa(vici, msg, 1);
-+ else if (blob_equal(&name, "child-state-destroying"))
-+ vici_recv_sa(vici, msg, 2);
-+ break;
-+ case VICI_EVENT_UNKNOWN:
-+ zlog_err("VICI: StrongSwan does not support mandatory events (unpatched?)");
-+ break;
-+ case VICI_EVENT_CONFIRM:
-+ case VICI_CMD_RESPONSE:
-+ break;
-+ default:
-+ zlog_notice("VICI: Unrecognized message type %d", msgtype);
-+ break;
-+ }
-+}
-+
-+static int vici_read(struct thread *t)
-+{
-+ struct vici_conn *vici = THREAD_ARG(t);
-+ struct zbuf *ibuf = &vici->ibuf;
-+ struct zbuf pktbuf;
-+
-+ vici->t_read = NULL;
-+ if (zbuf_read(ibuf, vici->fd, (size_t) -1) < 0) {
-+ vici_connection_error(vici);
-+ return 0;
-+ }
-+
-+ /* Process all messages in buffer */
-+ do {
-+ uint32_t *hdrlen = zbuf_may_pull(ibuf, uint32_t);
-+ if (!hdrlen)
-+ break;
-+ if (!zbuf_may_pulln(ibuf, ntohl(*hdrlen))) {
-+ zbuf_reset_head(ibuf, hdrlen);
-+ break;
-+ }
-+
-+ /* Handle packet */
-+ zbuf_init(&pktbuf, hdrlen, htonl(*hdrlen)+4, htonl(*hdrlen)+4);
-+ vici_recv_message(vici, &pktbuf);
-+ } while (1);
-+
-+ THREAD_READ_ON(master, vici->t_read, vici_read, vici, vici->fd);
-+ return 0;
-+}
-+
-+static int vici_write(struct thread *t)
-+{
-+ struct vici_conn *vici = THREAD_ARG(t);
-+ int r;
-+
-+ vici->t_write = NULL;
-+ r = zbufq_write(&vici->obuf, vici->fd);
-+ if (r > 0) {
-+ THREAD_WRITE_ON(master, vici->t_write, vici_write, vici, vici->fd);
-+ } else if (r < 0) {
-+ vici_connection_error(vici);
-+ }
-+
-+ return 0;
-+}
-+
-+static void vici_submit(struct vici_conn *vici, struct zbuf *obuf)
-+{
-+ if (vici->fd < 0) {
-+ zbuf_free(obuf);
-+ return;
-+ }
-+
-+ zbufq_queue(&vici->obuf, obuf);
-+ THREAD_WRITE_ON(master, vici->t_write, vici_write, vici, vici->fd);
-+}
-+
-+static void vici_submit_request(struct vici_conn *vici, const char *name, ...)
-+{
-+ struct zbuf *obuf;
-+ uint32_t *hdrlen;
-+ va_list va;
-+ size_t len;
-+ int type;
-+
-+ obuf = zbuf_alloc(256);
-+ if (!obuf) return;
-+
-+ hdrlen = zbuf_push(obuf, uint32_t);
-+ zbuf_put8(obuf, VICI_CMD_REQUEST);
-+ vici_zbuf_puts(obuf, name);
-+
-+ va_start(va, name);
-+ for (type = va_arg(va, int); type != VICI_END; type = va_arg(va, int)) {
-+ zbuf_put8(obuf, type);
-+ switch (type) {
-+ case VICI_KEY_VALUE:
-+ vici_zbuf_puts(obuf, va_arg(va, const char *));
-+ len = va_arg(va, size_t);
-+ zbuf_put_be16(obuf, len);
-+ zbuf_put(obuf, va_arg(va, void *), len);
-+ break;
-+ case VICI_END:
-+ break;
-+ default:
-+ break;
-+ }
-+ }
-+ va_end(va);
-+ *hdrlen = htonl(zbuf_used(obuf) - 4);
-+ vici_submit(vici, obuf);
-+}
-+
-+static void vici_register_event(struct vici_conn *vici, const char *name)
-+{
-+ struct zbuf *obuf;
-+ uint32_t *hdrlen;
-+ uint8_t namelen;
-+
-+ namelen = strlen(name);
-+ obuf = zbuf_alloc(4 + 1 + 1 + namelen);
-+ if (!obuf) return;
-+
-+ hdrlen = zbuf_push(obuf, uint32_t);
-+ zbuf_put8(obuf, VICI_EVENT_REGISTER);
-+ zbuf_put8(obuf, namelen);
-+ zbuf_put(obuf, name, namelen);
-+ *hdrlen = htonl(zbuf_used(obuf) - 4);
-+
-+ vici_submit(vici, obuf);
-+}
-+
-+static int vici_reconnect(struct thread *t)
-+{
-+ struct vici_conn *vici = THREAD_ARG(t);
-+ int fd;
-+
-+ vici->t_reconnect = NULL;
-+ if (vici->fd >= 0) return 0;
-+
-+ fd = sock_open_unix("/var/run/charon.vici");
-+ if (fd < 0) {
-+ zlog_warn("%s: failure connecting VICI socket: %s",
-+ __PRETTY_FUNCTION__, strerror(errno));
-+ THREAD_TIMER_ON(master, vici->t_reconnect, vici_reconnect, vici, 2);
-+ return 0;
-+ }
-+
-+ debugf(NHRP_DEBUG_COMMON, "VICI: Connected");
-+ vici->fd = fd;
-+ THREAD_READ_ON(master, vici->t_read, vici_read, vici, vici->fd);
-+
-+ /* Send event subscribtions */
-+ //vici_register_event(vici, "child-updown");
-+ //vici_register_event(vici, "child-rekey");
-+ vici_register_event(vici, "child-state-installed");
-+ vici_register_event(vici, "child-state-rekeyed");
-+ vici_register_event(vici, "child-state-destroying");
-+ vici_register_event(vici, "list-sa");
-+ vici_submit_request(vici, "list-sas", VICI_END);
-+
-+ return 0;
-+}
-+
-+static struct vici_conn vici_connection;
-+
-+void vici_init(void)
-+{
-+ struct vici_conn *vici = &vici_connection;
-+
-+ vici->fd = -1;
-+ zbuf_init(&vici->ibuf, vici->ibuf_data, sizeof(vici->ibuf_data), 0);
-+ zbufq_init(&vici->obuf);
-+ THREAD_TIMER_MSEC_ON(master, vici->t_reconnect, vici_reconnect, vici, 10);
-+}
-+
-+void vici_terminate(void)
-+{
-+}
-+
-+void vici_request_vc(const char *profile, union sockunion *src, union sockunion *dst, int prio)
-+{
-+ struct vici_conn *vici = &vici_connection;
-+ char buf[2][SU_ADDRSTRLEN];
-+
-+ sockunion2str(src, buf[0], sizeof buf[0]);
-+ sockunion2str(dst, buf[1], sizeof buf[1]);
-+
-+ vici_submit_request(
-+ vici, "initiate",
-+ VICI_KEY_VALUE, "child", strlen(profile), profile,
-+ VICI_KEY_VALUE, "timeout", 2, "-1",
-+ VICI_KEY_VALUE, "async", 1, "1",
-+ VICI_KEY_VALUE, "init-limits", 1, prio ? "0" : "1",
-+ VICI_KEY_VALUE, "my-host", strlen(buf[0]), buf[0],
-+ VICI_KEY_VALUE, "other-host", strlen(buf[1]), buf[1],
-+ VICI_END);
-+}
-+
-+int sock_open_unix(const char *path)
-+{
-+ int ret, fd;
-+ struct sockaddr_un addr;
-+
-+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
-+ if (fd < 0)
-+ return -1;
-+
-+ memset(&addr, 0, sizeof (struct sockaddr_un));
-+ addr.sun_family = AF_UNIX;
-+ strncpy(addr.sun_path, path, strlen (path));
-+
-+ ret = connect(fd, (struct sockaddr *) &addr, sizeof(addr.sun_family) + strlen(addr.sun_path));
-+ if (ret < 0) {
-+ close(fd);
-+ return -1;
-+ }
-+
-+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
-+
-+ return fd;
-+}
-diff --git a/nhrpd/vici.h b/nhrpd/vici.h
-new file mode 100644
-index 0000000..24b900b
---- /dev/null
-+++ b/nhrpd/vici.h
-@@ -0,0 +1,24 @@
-+
-+enum vici_type_t {
-+ VICI_START = 0,
-+ VICI_SECTION_START = 1,
-+ VICI_SECTION_END = 2,
-+ VICI_KEY_VALUE = 3,
-+ VICI_LIST_START = 4,
-+ VICI_LIST_ITEM = 5,
-+ VICI_LIST_END = 6,
-+ VICI_END = 7
-+};
-+
-+enum vici_operation_t {
-+ VICI_CMD_REQUEST = 0,
-+ VICI_CMD_RESPONSE,
-+ VICI_CMD_UNKNOWN,
-+ VICI_EVENT_REGISTER,
-+ VICI_EVENT_UNREGISTER,
-+ VICI_EVENT_CONFIRM,
-+ VICI_EVENT_UNKNOWN,
-+ VICI_EVENT,
-+};
-+
-+#define VICI_MAX_MSGLEN (512*1024)
-diff --git a/nhrpd/zbuf.c b/nhrpd/zbuf.c
-new file mode 100644
-index 0000000..ead7cfd
---- /dev/null
-+++ b/nhrpd/zbuf.c
-@@ -0,0 +1,219 @@
-+/* Stream/packet buffer API implementation
-+ * 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.
-+ */
-+
-+#define _GNU_SOURCE
-+#include <string.h>
-+#include <unistd.h>
-+#include <errno.h>
-+#include "zassert.h"
-+#include "zbuf.h"
-+#include "memory.h"
-+#include "memtypes.h"
-+#include "nhrpd.h"
-+
-+#define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
-+
-+struct zbuf *zbuf_alloc(size_t size)
-+{
-+ struct zbuf *zb;
-+
-+ zb = XMALLOC(MTYPE_STREAM_DATA, sizeof(*zb) + size);
-+ if (!zb)
-+ return NULL;
-+
-+ zbuf_init(zb, zb+1, size, 0);
-+ zb->allocated = 1;
-+
-+ return zb;
-+}
-+
-+void zbuf_init(struct zbuf *zb, void *buf, size_t len, size_t datalen)
-+{
-+ *zb = (struct zbuf) {
-+ .buf = buf,
-+ .end = (uint8_t *)buf + len,
-+ .head = buf,
-+ .tail = (uint8_t *)buf + datalen,
-+ };
-+}
-+
-+void zbuf_free(struct zbuf *zb)
-+{
-+ if (zb->allocated)
-+ XFREE(MTYPE_STREAM_DATA, zb);
-+}
-+
-+void zbuf_reset(struct zbuf *zb)
-+{
-+ zb->head = zb->tail = zb->buf;
-+ zb->error = 0;
-+}
-+
-+void zbuf_reset_head(struct zbuf *zb, void *ptr)
-+{
-+ zassert((void*)zb->buf <= ptr && ptr <= (void*)zb->tail);
-+ zb->head = ptr;
-+}
-+
-+static void zbuf_remove_headroom(struct zbuf *zb)
-+{
-+ ssize_t headroom = zbuf_headroom(zb);
-+ if (!headroom)
-+ return;
-+ memmove(zb->buf, zb->head, zbuf_used(zb));
-+ zb->head -= headroom;
-+ zb->tail -= headroom;
-+}
-+
-+ssize_t zbuf_read(struct zbuf *zb, int fd, size_t maxlen)
-+{
-+ ssize_t r;
-+
-+ if (zb->error)
-+ return -3;
-+
-+ zbuf_remove_headroom(zb);
-+ if (maxlen > zbuf_tailroom(zb))
-+ maxlen = zbuf_tailroom(zb);
-+
-+ r = read(fd, zb->tail, maxlen);
-+ if (r > 0) zb->tail += r;
-+ else if (r == 0) r = -2;
-+ else if (r < 0 && ERRNO_IO_RETRY(errno)) r = 0;
-+
-+ return r;
-+}
-+
-+ssize_t zbuf_write(struct zbuf *zb, int fd)
-+{
-+ ssize_t r;
-+
-+ if (zb->error)
-+ return -3;
-+
-+ r = write(fd, zb->head, zbuf_used(zb));
-+ if (r > 0) {
-+ zb->head += r;
-+ if (zb->head == zb->tail)
-+ zbuf_reset(zb);
-+ }
-+ else if (r == 0) r = -2;
-+ else if (r < 0 && ERRNO_IO_RETRY(errno)) r = 0;
-+
-+ return r;
-+}
-+
-+ssize_t zbuf_recv(struct zbuf *zb, int fd)
-+{
-+ ssize_t r;
-+
-+ if (zb->error)
-+ return -3;
-+
-+ zbuf_remove_headroom(zb);
-+ r = recv(fd, zb->tail, zbuf_tailroom(zb), 0);
-+ if (r > 0) zb->tail += r;
-+ else if (r == 0) r = -2;
-+ else if (r < 0 && ERRNO_IO_RETRY(errno)) r = 0;
-+ return r;
-+}
-+
-+ssize_t zbuf_send(struct zbuf *zb, int fd)
-+{
-+ ssize_t r;
-+
-+ if (zb->error)
-+ return -3;
-+
-+ r = send(fd, zb->head, zbuf_used(zb), 0);
-+ if (r >= 0)
-+ zbuf_reset(zb);
-+
-+ return r;
-+}
-+
-+void *zbuf_may_pull_until(struct zbuf *zb, const char *sep, struct zbuf *msg)
-+{
-+ size_t seplen = strlen(sep), len;
-+ uint8_t *ptr;
-+
-+ ptr = memmem(zb->head, zbuf_used(zb), sep, seplen);
-+ if (!ptr) return NULL;
-+
-+ len = ptr - zb->head + seplen;
-+ zbuf_init(msg, zbuf_pulln(zb, len), len, len);
-+ return msg->head;
-+}
-+
-+void zbufq_init(struct zbuf_queue *zbq)
-+{
-+ *zbq = (struct zbuf_queue) {
-+ .queue_head = LIST_INITIALIZER(zbq->queue_head),
-+ };
-+}
-+
-+void zbufq_reset(struct zbuf_queue *zbq)
-+{
-+ struct zbuf *buf, *bufn;
-+
-+ list_for_each_entry_safe(buf, bufn, &zbq->queue_head, queue_list) {
-+ list_del(&buf->queue_list);
-+ zbuf_free(buf);
-+ }
-+}
-+
-+void zbufq_queue(struct zbuf_queue *zbq, struct zbuf *zb)
-+{
-+ list_add_tail(&zb->queue_list, &zbq->queue_head);
-+}
-+
-+int zbufq_write(struct zbuf_queue *zbq, int fd)
-+{
-+ struct iovec iov[16];
-+ struct zbuf *zb, *zbn;
-+ ssize_t r;
-+ size_t iovcnt = 0;
-+
-+ list_for_each_entry_safe(zb, zbn, &zbq->queue_head, queue_list) {
-+ iov[iovcnt++] = (struct iovec) {
-+ .iov_base = zb->head,
-+ .iov_len = zbuf_used(zb),
-+ };
-+ if (iovcnt >= ZEBRA_NUM_OF(iov))
-+ break;
-+ }
-+
-+ r = writev(fd, iov, iovcnt);
-+ if (r < 0)
-+ return r;
-+
-+ list_for_each_entry_safe(zb, zbn, &zbq->queue_head, queue_list) {
-+ if (r < (ssize_t)zbuf_used(zb)) {
-+ zb->head += r;
-+ return 1;
-+ }
-+
-+ r -= zbuf_used(zb);
-+ list_del(&zb->queue_list);
-+ zbuf_free(zb);
-+ }
-+
-+ return 0;
-+}
-+
-+void zbuf_copy(struct zbuf *zdst, struct zbuf *zsrc, size_t len)
-+{
-+ const void *src;
-+ void *dst;
-+
-+ dst = zbuf_pushn(zdst, len);
-+ src = zbuf_pulln(zsrc, len);
-+ if (!dst || !src) return;
-+ memcpy(dst, src, len);
-+}
-diff --git a/nhrpd/zbuf.h b/nhrpd/zbuf.h
-new file mode 100644
-index 0000000..73d7073
---- /dev/null
-+++ b/nhrpd/zbuf.h
-@@ -0,0 +1,189 @@
-+/* Stream/packet buffer API
-+ * 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.
-+ */
-+
-+#ifndef ZBUF_H
-+#define ZBUF_H
-+
-+#include <stdint.h>
-+#include <string.h>
-+#include <endian.h>
-+#include <sys/types.h>
-+
-+#include "zassert.h"
-+#include "list.h"
-+
-+struct zbuf {
-+ struct list_head queue_list;
-+ unsigned allocated : 1;
-+ unsigned error : 1;
-+ uint8_t *buf, *end;
-+ uint8_t *head, *tail;
-+};
-+
-+struct zbuf_queue {
-+ struct list_head queue_head;
-+};
-+
-+struct zbuf *zbuf_alloc(size_t size);
-+void zbuf_init(struct zbuf *zb, void *buf, size_t len, size_t datalen);
-+void zbuf_free(struct zbuf *zb);
-+
-+static inline size_t zbuf_size(struct zbuf *zb)
-+{
-+ return zb->end - zb->buf;
-+}
-+
-+static inline size_t zbuf_used(struct zbuf *zb)
-+{
-+ return zb->tail - zb->head;
-+}
-+
-+static inline size_t zbuf_tailroom(struct zbuf *zb)
-+{
-+ return zb->end - zb->tail;
-+}
-+
-+static inline size_t zbuf_headroom(struct zbuf *zb)
-+{
-+ return zb->head - zb->buf;
-+}
-+
-+void zbuf_reset(struct zbuf *zb);
-+void zbuf_reset_head(struct zbuf *zb, void *ptr);
-+ssize_t zbuf_read(struct zbuf *zb, int fd, size_t maxlen);
-+ssize_t zbuf_write(struct zbuf *zb, int fd);
-+ssize_t zbuf_recv(struct zbuf *zb, int fd);
-+ssize_t zbuf_send(struct zbuf *zb, int fd);
-+
-+static inline void zbuf_set_rerror(struct zbuf *zb)
-+{
-+ zb->error = 1;
-+ zb->head = zb->tail;
-+}
-+
-+static inline void zbuf_set_werror(struct zbuf *zb)
-+{
-+ zb->error = 1;
-+ zb->tail = zb->end;
-+}
-+
-+static inline void *__zbuf_pull(struct zbuf *zb, size_t size, int error)
-+{
-+ void *head = zb->head;
-+ if (size > zbuf_used(zb)) {
-+ if (error) zbuf_set_rerror(zb);
-+ return NULL;
-+ }
-+ zb->head += size;
-+ return head;
-+}
-+
-+#define zbuf_pull(zb, type) ((type *)__zbuf_pull(zb, sizeof(type), 1))
-+#define zbuf_pulln(zb, sz) ((void *)__zbuf_pull(zb, sz, 1))
-+#define zbuf_may_pull(zb, type) ((type *)__zbuf_pull(zb, sizeof(type), 0))
-+#define zbuf_may_pulln(zb, sz) ((void *)__zbuf_pull(zb, sz, 0))
-+
-+void *zbuf_may_pull_until(struct zbuf *zb, const char *sep, struct zbuf *msg);
-+
-+static inline void zbuf_get(struct zbuf *zb, void *dst, size_t len)
-+{
-+ void *src = zbuf_pulln(zb, len);
-+ if (src) memcpy(dst, src, len);
-+}
-+
-+static inline uint8_t zbuf_get8(struct zbuf *zb)
-+{
-+ uint8_t *src = zbuf_pull(zb, uint8_t);
-+ if (src) return *src;
-+ return 0;
-+}
-+
-+static inline uint32_t zbuf_get32(struct zbuf *zb)
-+{
-+ struct unaligned32 {
-+ uint32_t value;
-+ } __attribute__((packed));
-+
-+ struct unaligned32 *v = zbuf_pull(zb, struct unaligned32);
-+ if (v) return v->value;
-+ return 0;
-+}
-+
-+static inline uint16_t zbuf_get_be16(struct zbuf *zb)
-+{
-+ struct unaligned16 {
-+ uint16_t value;
-+ } __attribute__((packed));
-+
-+ struct unaligned16 *v = zbuf_pull(zb, struct unaligned16);
-+ if (v) return be16toh(v->value);
-+ return 0;
-+}
-+
-+static inline uint32_t zbuf_get_be32(struct zbuf *zb)
-+{
-+ return be32toh(zbuf_get32(zb));
-+}
-+
-+static inline void *__zbuf_push(struct zbuf *zb, size_t size, int error)
-+{
-+ void *tail = zb->tail;
-+ if (size > zbuf_tailroom(zb)) {
-+ if (error) zbuf_set_werror(zb);
-+ return NULL;
-+ }
-+ zb->tail += size;
-+ return tail;
-+}
-+
-+#define zbuf_push(zb, type) ((type *)__zbuf_push(zb, sizeof(type), 1))
-+#define zbuf_pushn(zb, sz) ((void *)__zbuf_push(zb, sz, 1))
-+#define zbuf_may_push(zb, type) ((type *)__zbuf_may_push(zb, sizeof(type), 0))
-+#define zbuf_may_pushn(zb, sz) ((void *)__zbuf_push(zb, sz, 0))
-+
-+static inline void zbuf_put(struct zbuf *zb, const void *src, size_t len)
-+{
-+ void *dst = zbuf_pushn(zb, len);
-+ if (dst) memcpy(dst, src, len);
-+}
-+
-+static inline void zbuf_put8(struct zbuf *zb, uint8_t val)
-+{
-+ uint8_t *dst = zbuf_push(zb, uint8_t);
-+ if (dst) *dst = val;
-+}
-+
-+static inline void zbuf_put_be16(struct zbuf *zb, uint16_t val)
-+{
-+ struct unaligned16 {
-+ uint16_t value;
-+ } __attribute__((packed));
-+
-+ struct unaligned16 *v = zbuf_push(zb, struct unaligned16);
-+ if (v) v->value = htobe16(val);
-+}
-+
-+static inline void zbuf_put_be32(struct zbuf *zb, uint32_t val)
-+{
-+ struct unaligned32 {
-+ uint32_t value;
-+ } __attribute__((packed));
-+
-+ struct unaligned32 *v = zbuf_push(zb, struct unaligned32);
-+ if (v) v->value = htobe32(val);
-+}
-+
-+void zbuf_copy(struct zbuf *zb, struct zbuf *src, size_t len);
-+
-+void zbufq_init(struct zbuf_queue *);
-+void zbufq_reset(struct zbuf_queue *);
-+void zbufq_queue(struct zbuf_queue *, struct zbuf *);
-+int zbufq_write(struct zbuf_queue *, int);
-+
-+#endif
-diff --git a/nhrpd/znl.c b/nhrpd/znl.c
-new file mode 100644
-index 0000000..2216d97
---- /dev/null
-+++ b/nhrpd/znl.c
-@@ -0,0 +1,160 @@
-+/* Netlink helpers for zbuf
-+ * 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 <fcntl.h>
-+#include <errno.h>
-+#include <string.h>
-+#include <unistd.h>
-+#include <sys/types.h>
-+#include <sys/socket.h>
-+#include <linux/netlink.h>
-+#include <linux/rtnetlink.h>
-+
-+#include "znl.h"
-+
-+#define ZNL_ALIGN(len) (((len)+3) & ~3)
-+
-+void *znl_push(struct zbuf *zb, size_t n)
-+{
-+ return zbuf_pushn(zb, ZNL_ALIGN(n));
-+}
-+
-+void *znl_pull(struct zbuf *zb, size_t n)
-+{
-+ return zbuf_pulln(zb, ZNL_ALIGN(n));
-+}
-+
-+struct nlmsghdr *znl_nlmsg_push(struct zbuf *zb, uint16_t type, uint16_t flags)
-+{
-+ struct nlmsghdr *n;
-+
-+ n = znl_push(zb, sizeof(*n));
-+ if (!n) return NULL;
-+
-+ *n = (struct nlmsghdr) {
-+ .nlmsg_type = type,
-+ .nlmsg_flags = flags,
-+ };
-+ return n;
-+}
-+
-+void znl_nlmsg_complete(struct zbuf *zb, struct nlmsghdr *n)
-+{
-+ n->nlmsg_len = zb->tail - (uint8_t*)n;
-+}
-+
-+struct nlmsghdr *znl_nlmsg_pull(struct zbuf *zb, struct zbuf *payload)
-+{
-+ struct nlmsghdr *n;
-+ size_t plen;
-+
-+ n = znl_pull(zb, sizeof(*n));
-+ if (!n) return NULL;
-+
-+ plen = n->nlmsg_len - sizeof(*n);
-+ zbuf_init(payload, znl_pull(zb, plen), plen, plen);
-+ zbuf_may_pulln(zb, ZNL_ALIGN(plen) - plen);
-+
-+ return n;
-+}
-+
-+struct rtattr *znl_rta_push(struct zbuf *zb, uint16_t type, const void *val, size_t len)
-+{
-+ struct rtattr *rta;
-+ uint8_t *dst;
-+
-+ rta = znl_push(zb, ZNL_ALIGN(sizeof(*rta)) + ZNL_ALIGN(len));
-+ if (!rta) return NULL;
-+
-+ *rta = (struct rtattr) {
-+ .rta_type = type,
-+ .rta_len = ZNL_ALIGN(sizeof(*rta)) + len,
-+ };
-+
-+ dst = (uint8_t *)(rta+1);
-+ memcpy(dst, val, len);
-+ memset(dst+len, 0, ZNL_ALIGN(len) - len);
-+
-+ return rta;
-+}
-+
-+struct rtattr *znl_rta_push_u32(struct zbuf *zb, uint16_t type, uint32_t val)
-+{
-+ return znl_rta_push(zb, type, &val, sizeof(val));
-+}
-+
-+struct rtattr *znl_rta_nested_push(struct zbuf *zb, uint16_t type)
-+{
-+ struct rtattr *rta;
-+
-+ rta = znl_push(zb, sizeof(*rta));
-+ if (!rta) return NULL;
-+
-+ *rta = (struct rtattr) {
-+ .rta_type = type,
-+ };
-+ return rta;
-+}
-+
-+void znl_rta_nested_complete(struct zbuf *zb, struct rtattr *rta)
-+{
-+ size_t len = zb->tail - (uint8_t*) rta;
-+ size_t align = ZNL_ALIGN(len) - len;
-+
-+ if (align) {
-+ void *dst = zbuf_pushn(zb, align);
-+ if (dst) memset(dst, 0, align);
-+ }
-+ rta->rta_len = len;
-+}
-+
-+struct rtattr *znl_rta_pull(struct zbuf *zb, struct zbuf *payload)
-+{
-+ struct rtattr *rta;
-+ size_t plen;
-+
-+ rta = znl_pull(zb, sizeof(*rta));
-+ if (!rta) return NULL;
-+
-+ if (rta->rta_len > sizeof(*rta)) {
-+ plen = rta->rta_len - sizeof(*rta);
-+ zbuf_init(payload, znl_pull(zb, plen), plen, plen);
-+ } else {
-+ zbuf_init(payload, NULL, 0, 0);
-+ }
-+
-+ return rta;
-+}
-+
-+int znl_open(int protocol, int groups)
-+{
-+ struct sockaddr_nl addr;
-+ int fd, buf = 128 * 1024;
-+
-+ fd = socket(AF_NETLINK, SOCK_RAW, protocol);
-+ if (fd < 0)
-+ return -1;
-+
-+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
-+ fcntl(fd, F_SETFD, FD_CLOEXEC);
-+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buf, sizeof(buf)) < 0)
-+ goto error;
-+
-+ memset(&addr, 0, sizeof(addr));
-+ addr.nl_family = AF_NETLINK;
-+ addr.nl_groups = groups;
-+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
-+ goto error;
-+
-+ return fd;
-+error:
-+ close(fd);
-+ return -1;
-+}
-+
-diff --git a/nhrpd/znl.h b/nhrpd/znl.h
-new file mode 100644
-index 0000000..2cd630b
---- /dev/null
-+++ b/nhrpd/znl.h
-@@ -0,0 +1,29 @@
-+/* Netlink helpers for zbuf
-+ * 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 "zbuf.h"
-+
-+#define ZNL_BUFFER_SIZE 8192
-+
-+void *znl_push(struct zbuf *zb, size_t n);
-+void *znl_pull(struct zbuf *zb, size_t n);
-+
-+struct nlmsghdr *znl_nlmsg_push(struct zbuf *zb, uint16_t type, uint16_t flags);
-+void znl_nlmsg_complete(struct zbuf *zb, struct nlmsghdr *n);
-+struct nlmsghdr *znl_nlmsg_pull(struct zbuf *zb, struct zbuf *payload);
-+
-+struct rtattr *znl_rta_push(struct zbuf *zb, uint16_t type, const void *val, size_t len);
-+struct rtattr *znl_rta_push_u32(struct zbuf *zb, uint16_t type, uint32_t val);
-+struct rtattr *znl_rta_nested_push(struct zbuf *zb, uint16_t type);
-+void znl_rta_nested_complete(struct zbuf *zb, struct rtattr *rta);
-+
-+struct rtattr *znl_rta_pull(struct zbuf *zb, struct zbuf *payload);
-+
-+int znl_open(int protocol, int groups);
-+
-diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am
-index e44cd49..983103f 100644
---- a/vtysh/Makefile.am
-+++ b/vtysh/Makefile.am
-@@ -24,6 +24,7 @@ vtysh_cmd_FILES = $(top_srcdir)/bgpd/*.c $(top_srcdir)/isisd/*.c \
- $(top_srcdir)/ospfd/*.c $(top_srcdir)/ospf6d/*.c \
- $(top_srcdir)/ripd/*.c $(top_srcdir)/ripngd/*.c \
- $(top_srcdir)/pimd/pim_cmd.c \
-+ $(top_srcdir)/nhrpd/nhrp_vty.c \
- $(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \
- $(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \
- $(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \
-diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
-index 2e2203d..1ee8582 100644
---- a/vtysh/vtysh.c
-+++ b/vtysh/vtysh.c
-@@ -60,6 +60,7 @@ struct vtysh_client
- { .fd = -1, .name = "bgpd", .flag = VTYSH_BGPD, .path = BGP_VTYSH_PATH},
- { .fd = -1, .name = "isisd", .flag = VTYSH_ISISD, .path = ISIS_VTYSH_PATH},
- { .fd = -1, .name = "pimd", .flag = VTYSH_PIMD, .path = PIM_VTYSH_PATH},
-+ { .fd = -1, .name = "nhrpd", .flag = VTYSH_NHRPD, .path = NHRP_VTYSH_PATH},
- };
-
-
-diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
-index 1681a71..02ff7fd 100644
---- a/vtysh/vtysh.h
-+++ b/vtysh/vtysh.h
-@@ -31,9 +31,10 @@
- #define VTYSH_ISISD 0x40
- #define VTYSH_BABELD 0x80
- #define VTYSH_PIMD 0x100
--#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD
-+#define VTYSH_NHRPD 0x200
-+#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD|VTYSH_NHRPD
- #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_BABELD
--#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD
-+#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD|VTYSH_NHRPD
-
- /* vtysh local configuration file. */
- #define VTYSH_DEFAULT_CONFIG "vtysh.conf"
-diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
-index abb9560..b03ccc2 100644
---- a/zebra/zebra_rib.c
-+++ b/zebra/zebra_rib.c
-@@ -72,6 +72,7 @@ static const struct
- [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115},
- [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */},
- [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 95},
-+ [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10},
- /* no entry/default: 150 */
- };
-
-@@ -1423,6 +1424,7 @@ static const u_char meta_queue_map[ZEBRA_ROUTE_MAX] = {
- [ZEBRA_ROUTE_BGP] = 3,
- [ZEBRA_ROUTE_HSLS] = 4,
- [ZEBRA_ROUTE_BABEL] = 2,
-+ [ZEBRA_ROUTE_NHRP] = 2,
- };
-
- /* Look into the RN and queue it into one or more priority queues,
-diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
-index 5762d3f..859b6d7 100644
---- a/zebra/zebra_rnh.c
-+++ b/zebra/zebra_rnh.c
-@@ -223,16 +223,27 @@ zebra_evaluate_rnh_table (vrf_id_t vrfid, int family)
- {
- if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
- continue;
-- if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB))
-+ if (! CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB))
-+ continue;
-+
-+ if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED))
- {
-- if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED))
-+ if (rib->type == ZEBRA_ROUTE_CONNECT)
-+ break;
-+
-+ if (rib->type == ZEBRA_ROUTE_NHRP)
- {
-- if (rib->type == ZEBRA_ROUTE_CONNECT)
-+ struct nexthop *nexthop;
-+ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
-+ if (nexthop->type == NEXTHOP_TYPE_IFINDEX ||
-+ nexthop->type == NEXTHOP_TYPE_IFNAME)
-+ break;
-+ if (nexthop)
- break;
- }
-- else
-- break;
- }
-+ else
-+ break;
- }
- }
-
-diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
-index 38f61e9..c73896b 100644
---- a/zebra/zebra_vty.c
-+++ b/zebra/zebra_vty.c
-@@ -2121,6 +2121,7 @@ vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast)
- || rib->type == ZEBRA_ROUTE_OSPF6
- || rib->type == ZEBRA_ROUTE_BABEL
- || rib->type == ZEBRA_ROUTE_ISIS
-+ || rib->type == ZEBRA_ROUTE_NHRP
- || rib->type == ZEBRA_ROUTE_BGP)
- {
- time_t uptime;
-@@ -2341,6 +2342,7 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib)
- || rib->type == ZEBRA_ROUTE_OSPF6
- || rib->type == ZEBRA_ROUTE_BABEL
- || rib->type == ZEBRA_ROUTE_ISIS
-+ || rib->type == ZEBRA_ROUTE_NHRP
- || rib->type == ZEBRA_ROUTE_BGP)
- {
- time_t uptime;
---
-2.10.2
-
diff --git a/main/quagga-nhrp/APKBUILD b/main/quagga-nhrp/APKBUILD
deleted file mode 100644
index d7ccac4689..0000000000
--- a/main/quagga-nhrp/APKBUILD
+++ /dev/null
@@ -1,111 +0,0 @@
-# Maintainer: Timo Teräs <timo.teras@iki.fi>
-pkgname=quagga-nhrp
-pkgver=1.1.0
-pkgrel=1
-pkgdesc="A free routing daemon replacing Zebra supporting RIP, OSPF, BGP and NHRP."
-url="http://quagga.net/"
-arch="all"
-license="GPL2"
-depends="iproute2"
-makedepends="gawk texinfo perl autoconf automake libtool c-ares-dev
- linux-headers readline-dev ncurses-dev net-snmp-dev libcap-dev"
-provides="quagga=0.99"
-replaces="quagga"
-install="$pkgname.pre-install $pkgname.post-install $pkgname.post-upgrade"
-subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg"
-pkgusers="quagga"
-pkggroups="quagga"
-source="http://download.savannah.gnu.org/releases/quagga/quagga-$pkgver.tar.gz
- dont-hook-core-signals.patch
- 0001-zebra-use-FIB-state-for-nexthop-tracking.patch
- 0002-zebra-fix-nht-validity-checking-to-be-same-as-when-i.patch
- 0003-bgpd-honor-disable-connected-check-option-with-next-.patch
- 0004-bgpd-simplify-ebgp-multihop-and-ttl-security-handlin.patch
- 0005-nhrpd-implement-next-hop-resolution-protocol.patch
- 0006-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch
-
- bgpd.initd
- zebra.initd
- zebra.confd
- "
-
-builddir="$srcdir"/quagga-$pkgver
-
-prepare() {
- default_prepare
- autoreconf
-}
-
-build() {
- cd "$builddir"
- quagga_cv_ipforward_method=proc \
- ./configure \
- --build=$CBUILD \
- --host=$CHOST \
- --prefix=/usr \
- --disable-static \
- --enable-ospf6d \
- --enable-rtadv \
- --enable-user=quagga \
- --enable-group=quagga \
- --enable-vty-group=quagga \
- --enable-vtysh \
- --enable-snmp \
- --enable-multipath=64 \
- --enable-pimd \
- --sysconfdir=/etc/quagga \
- --enable-exampledir=/usr/share/doc/quagga/ \
- --localstatedir=/var/run/quagga \
- || return 1
-
- # add CFLAGS to work around textrel issue
- make CFLAGS+="-fPIC" || return 1
-}
-
-package() {
- cd "$builddir"
- make DESTDIR="$pkgdir" install || return 1
-
- install -o quagga -g quagga -d "$pkgdir"/var/run/quagga
- for i in zebra bgpd; do
- install -Dm755 "$srcdir"/$i.initd "$pkgdir"/etc/init.d/$i
- done
- for i in ripd ospfd ripngd ospf6d nhrpd; do
- ln -s bgpd "$pkgdir"/etc/init.d/$i || return 1
- done
- install -Dm644 "$srcdir/zebra.confd" "$pkgdir"/etc/conf.d/zebra
- install -o quagga -g quagga -d -m755 "$pkgdir"/etc/quagga
-}
-md5sums="daa303871e07ea5856aae6fd79e89722 quagga-1.1.0.tar.gz
-1224ba91ea6b6e81f583bad7813aba98 dont-hook-core-signals.patch
-06f0ae78b129cfc729430c4df9e9f807 0001-zebra-use-FIB-state-for-nexthop-tracking.patch
-6769244333af49b9a47268111ee690b6 0002-zebra-fix-nht-validity-checking-to-be-same-as-when-i.patch
-78450d646cb3e63899a2223ab2dbce97 0003-bgpd-honor-disable-connected-check-option-with-next-.patch
-f8c2e263994a9ca1705127f967d858f0 0004-bgpd-simplify-ebgp-multihop-and-ttl-security-handlin.patch
-92e66d8bf3058e1db660b89ca4fa323f 0005-nhrpd-implement-next-hop-resolution-protocol.patch
-e1a27c077e323e3f915d4c3b142ca56b 0006-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch
-09a77e2e84e71c43f5a449738c026261 bgpd.initd
-916f1dd1a286ee7b862cda4fe56cbf21 zebra.initd
-34e06a1d2bc602ce691abc9ed169dd15 zebra.confd"
-sha256sums="f7a43a9c59bfd3722002210530b2553c8d5cc05bfea5acd56d4f102b9f55dc63 quagga-1.1.0.tar.gz
-4b71588e34ac14f8d6e72e6064b5e4ec302f286ebbe43df94c97411cceb66a23 dont-hook-core-signals.patch
-a9cfc74247612640129a0bd8402e78380e35ce86f54ca4aeb40bfcf2a05c5688 0001-zebra-use-FIB-state-for-nexthop-tracking.patch
-d290a449084d5be89b124b721a539772ab95e56304d0441723a8811fbd5fa692 0002-zebra-fix-nht-validity-checking-to-be-same-as-when-i.patch
-463087091c5fe4ddcc85e476ac56d3bef8c964050b2a45c7b10e670d0c20911b 0003-bgpd-honor-disable-connected-check-option-with-next-.patch
-643688c2b3f05b1b307821d0bad18f1452ccebf00d2a5b262ee6fe14f363b14b 0004-bgpd-simplify-ebgp-multihop-and-ttl-security-handlin.patch
-f7161e557fde61ce61094df564fe48900af390f2e6098b359d45ef4b0f460697 0005-nhrpd-implement-next-hop-resolution-protocol.patch
-4ad0e70d235981c8063395b947a7a677a8d354b130fcf9eb545297e99ce96ddb 0006-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch
-aab037454c6a70cd5cb45e14c47b7dfea358f8d81c7d12418edcf7e58a86c679 bgpd.initd
-c1d7526581927e990e687cbd5d08447eb060f76a439475572785b5b90c60c460 zebra.initd
-f7a52d383f60270a5a8fee5d4ac522c5c0ec2b7c4b5252cff54e260f32d9b323 zebra.confd"
-sha512sums="3b29a90c4f05593714bda3c702fd2c8886ce48fba2fbfb98f55cc04d1025edd5427944e9a9fb7cd630e5e8ccea388b72a8e611ab65c370e760f3f319d03f090f quagga-1.1.0.tar.gz
-5ef5c5e6d70d991b33b13a062e25b6fbde395dceee36aea29384b0640a48d2957ed5f50d416a1f2f770bf69bae2340133e35b1114be7e1fa722eb6d3d021f37a dont-hook-core-signals.patch
-73f23b62a0fdc4a55dc1dab67a7a79cd2968c090dfe264cc6a733c9f0fb844df47e6c876d98211f94e5903c85103020a669bd3dd5bf6cb4f08c1162e030b6b7b 0001-zebra-use-FIB-state-for-nexthop-tracking.patch
-4fb238f07b67d927fc1e5ec5cd184fb7a4247dfcb2e2733496db3277da439d2eb9728c634c40cd87a250b890170120f9b613aeb17aaaf054952d2e7ead81e63c 0002-zebra-fix-nht-validity-checking-to-be-same-as-when-i.patch
-568c0c9ca2fff36c7ddf52908b8df7028b0f4242ae0329f002460ee632bb4d8067f5e2d3c1dbb5e1fcb513fe30674babf16338b923628ccc2f838b5c23f4fb39 0003-bgpd-honor-disable-connected-check-option-with-next-.patch
-de0d14ceb91d6d858b790c276a2bced579696c36c954084892f90d6c36a27bec62cb496b18e11017fad5a1d646149130bf3bcada1f786f2a9939b606491eff15 0004-bgpd-simplify-ebgp-multihop-and-ttl-security-handlin.patch
-9b58fc2be9757676f8ee9c7d766c98b58123f28cace08a3ff0eb7b237d7879c51a8324c8687f488597109fc4e2f30002ac9c1cb0a825bb2604f6723abbeddef6 0005-nhrpd-implement-next-hop-resolution-protocol.patch
-dfa33341119fe51caa7bc33b44256f57361f2e3f8192862cca215b312ceb68e6a8c264dbf2a43d6244e6152bfad110cb0fdbefb065d95dd50389cf613d9720b3 0006-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch
-13b5b57e10df013bd2d931abc49bf76b8c4dee59dbceab22c9f151ccb988b2c5f7167f2909027d5e0f990b59da8de115667b02484aee9a67d347625700f6cacd bgpd.initd
-1638a4a64ffd066b1884f7e5a4243edab68739aabd83bd35ea8c9608af7b8623eece1d59fb08feead84e4386b6d1da4220764ccf5fd7f2a9959a8470d5cce86a zebra.initd
-900972c6f98e561dfacf384111251db262326e8764b8c763a5ef639fa11c7949c03eef5e3bce324a4b1964fe45416d2db74ae1b6bc967f7d4ba48c2eeda017c4 zebra.confd"
diff --git a/main/quagga-nhrp/bgpd.initd b/main/quagga-nhrp/bgpd.initd
deleted file mode 100644
index bd1e4c6c92..0000000000
--- a/main/quagga-nhrp/bgpd.initd
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/sbin/openrc-run
-
-piddir=/var/run/quagga
-pidfile=$piddir/$SVCNAME.pid
-command=/usr/sbin/$SVCNAME
-command_args="-d -f /etc/quagga/$SVCNAME.conf --pid_file $pidfile"
-
-depend() {
- need net zebra
- after firewall opennhrp
-}
-
-start_pre() {
- if ! [ -e /etc/quagga/$SVCNAME.conf ] ; then
- eerror "You need to create /etc/quagga/$SVCNAME.conf first."
- eerror "An example can be found in /usr/share/doc/quagga/$SVCNAME.conf.sample"
- eerror "from quagga-doc package"
- return 1
- fi
- checkpath --owner quagga:quagga --directory $piddir
-}
-
diff --git a/main/quagga-nhrp/dont-hook-core-signals.patch b/main/quagga-nhrp/dont-hook-core-signals.patch
deleted file mode 100644
index 0fa57502d0..0000000000
--- a/main/quagga-nhrp/dont-hook-core-signals.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-diff --git a/lib/sigevent.c b/lib/sigevent.c
-index c80a729..1221c25 100644
---- a/lib/sigevent.c
-+++ b/lib/sigevent.c
-@@ -244,6 +244,7 @@ static void
- trap_default_signals(void)
- {
- static const int core_signals[] = {
-+#if 0
- SIGQUIT,
- SIGILL,
- #ifdef SIGEMT
-@@ -261,6 +262,7 @@ trap_default_signals(void)
- #ifdef SIGXFSZ
- SIGXFSZ,
- #endif
-+#endif
- };
- static const int exit_signals[] = {
- SIGHUP,
diff --git a/main/quagga-nhrp/quagga-nhrp.post-install b/main/quagga-nhrp/quagga-nhrp.post-install
deleted file mode 100644
index b4bcef1e7b..0000000000
--- a/main/quagga-nhrp/quagga-nhrp.post-install
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-# early quaggas had an entry in ld.so.conf. We dont need it anymore
-if [ -r /etc/ld.so.conf ]; then
- sed -i -e '/usr\/lib\/quagga/d' /etc/ld.so.conf
- # remove if zero sized
- [ -s /etc/ld.so.conf ] || rm /etc/ld.so.conf
-fi
-exit 0
diff --git a/main/quagga-nhrp/quagga-nhrp.post-upgrade b/main/quagga-nhrp/quagga-nhrp.post-upgrade
deleted file mode 120000
index 1dcbc73fb9..0000000000
--- a/main/quagga-nhrp/quagga-nhrp.post-upgrade
+++ /dev/null
@@ -1 +0,0 @@
-quagga-nhrp.post-install \ No newline at end of file
diff --git a/main/quagga-nhrp/quagga-nhrp.pre-install b/main/quagga-nhrp/quagga-nhrp.pre-install
deleted file mode 100644
index 53569ceb6e..0000000000
--- a/main/quagga-nhrp/quagga-nhrp.pre-install
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-
-addgroup -S quagga 2>/dev/null
-adduser -S -D -h /var/run/quagga -s /sbin/nologin -G quagga -g quagga quagga 2>/dev/null
-
-exit 0
diff --git a/main/quagga-nhrp/zebra.confd b/main/quagga-nhrp/zebra.confd
deleted file mode 100644
index d2deb114e5..0000000000
--- a/main/quagga-nhrp/zebra.confd
+++ /dev/null
@@ -1,7 +0,0 @@
-# Additional command-line parameters to run zebra with:
-# -k, --keep_kernel Don't delete old routes which installed by zebra.
-# -A, --vty_addr addr Set vty's bind address
-# -P, --vty_port port Set vty's port number
-# -r, --retain When program terminates, retain added route by zebra.
-# -s, --nl-bufsize Set netlink receive buffer size
-ZEBRA_OPTS=""
diff --git a/main/quagga-nhrp/zebra.initd b/main/quagga-nhrp/zebra.initd
deleted file mode 100644
index 6ab297e74f..0000000000
--- a/main/quagga-nhrp/zebra.initd
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/sbin/openrc-run
-
-piddir=/var/run/quagga
-pidfile=$piddir/$SVCNAME.pid
-command=/usr/sbin/$SVCNAME
-command_args="-d -f /etc/quagga/$SVCNAME.conf --pid_file $pidfile"
-
-depend() {
- need net
- after firewall opennhrp
-}
-
-cleanup() {
- ebegin "Cleaning up stale zebra routes..."
- ip route flush proto zebra
- eend $?
-}
-
-checkconfig() {
- if ! [ -e /etc/quagga/$SVCNAME.conf ] ; then
- eerror "You need to create /etc/quagga/$SVCNAME.conf first."
- eerror "An example can be found in /usr/share/doc/quagga/$SVCNAME.conf.sample"
- eerror "from quagga-doc package"
- return 1
- fi
- return 0
-}
-
-start_pre() {
- checkconfig || return 1
- checkpath --owner quagga:quagga --directory $piddir
- cleanup
-}
-
diff --git a/main/quagga-nhrp/0006-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch b/main/quagga/1001-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch
index f38522950a..f38522950a 100644
--- a/main/quagga-nhrp/0006-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch
+++ b/main/quagga/1001-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch
diff --git a/main/quagga/APKBUILD b/main/quagga/APKBUILD
index 7e8e631a01..cd6940024b 100644
--- a/main/quagga/APKBUILD
+++ b/main/quagga/APKBUILD
@@ -1,18 +1,22 @@
# Maintainer: Natanael Copa <ncopa@alpinelinux.org>
pkgname=quagga
-pkgver=1.1.1
+pkgver=1.2.0
pkgrel=0
-pkgdesc="A free routing daemon replacing Zebra supporting RIP, OSPF and BGP."
+pkgdesc="A free routing daemon replacing Zebra supporting RIP, OSPF, BGP and NHRP"
url="http://quagga.net/"
arch="all"
license="GPL2"
depends="iproute2"
-makedepends="linux-headers readline-dev ncurses-dev gawk texinfo perl net-snmp-dev"
+replaces="quagga-nhrp"
+provides="quagga-nhrp=$pkgver"
+makedepends="linux-headers readline-dev ncurses-dev c-ares-dev net-snmp-dev
+ gawk texinfo perl"
install="$pkgname.pre-install $pkgname.post-install $pkgname.post-upgrade"
subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg"
pkgusers="quagga"
pkggroups="quagga"
source="http://download.savannah.gnu.org/releases/quagga/quagga-$pkgver.tar.gz
+ 1001-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch
dont-hook-core-signals.patch
bgpd.initd
@@ -60,23 +64,14 @@ package() {
for i in zebra bgpd; do
install -Dm755 "$srcdir"/$i.initd "$pkgdir"/etc/init.d/$i
done
- for i in ripd ospfd ripngd ospf6d; do
+ for i in ripd ospfd ripngd ospf6d nhrpd; do
ln -s bgpd "$pkgdir"/etc/init.d/$i || return 1
done
install -Dm644 "$srcdir/zebra.confd" "$pkgdir"/etc/conf.d/zebra
install -o quagga -g quagga -d -m755 "$pkgdir"/etc/quagga
}
-md5sums="fb7e4f50638886b5701422bc2279a9af quagga-1.1.1.tar.gz
-1224ba91ea6b6e81f583bad7813aba98 dont-hook-core-signals.patch
-09a77e2e84e71c43f5a449738c026261 bgpd.initd
-916f1dd1a286ee7b862cda4fe56cbf21 zebra.initd
-34e06a1d2bc602ce691abc9ed169dd15 zebra.confd"
-sha256sums="b5a94e5bdad3062e04595a5692b8cc435f0a85102f75dfdca0a06d093b4ef63f quagga-1.1.1.tar.gz
-4b71588e34ac14f8d6e72e6064b5e4ec302f286ebbe43df94c97411cceb66a23 dont-hook-core-signals.patch
-aab037454c6a70cd5cb45e14c47b7dfea358f8d81c7d12418edcf7e58a86c679 bgpd.initd
-c1d7526581927e990e687cbd5d08447eb060f76a439475572785b5b90c60c460 zebra.initd
-f7a52d383f60270a5a8fee5d4ac522c5c0ec2b7c4b5252cff54e260f32d9b323 zebra.confd"
-sha512sums="51eb64ada07b42c663705cedf56be5b8b54143a5543b472e3dc7c703a4ab0542f39cfbeed64d1c33ceee6a15ea8d25ef84616fa40b6bf9cc32023f7241c18c58 quagga-1.1.1.tar.gz
+sha512sums="0ad28b828c2e52ad486189bd68e21041fe7c4affada7e3f12ba83a95f3532e0b012ba6b4271f9d0de63d4f5b15e80c706977b3afa54b57a3a5ec02400227d5ae quagga-1.2.0.tar.gz
+dfa33341119fe51caa7bc33b44256f57361f2e3f8192862cca215b312ceb68e6a8c264dbf2a43d6244e6152bfad110cb0fdbefb065d95dd50389cf613d9720b3 1001-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch
5ef5c5e6d70d991b33b13a062e25b6fbde395dceee36aea29384b0640a48d2957ed5f50d416a1f2f770bf69bae2340133e35b1114be7e1fa722eb6d3d021f37a dont-hook-core-signals.patch
13b5b57e10df013bd2d931abc49bf76b8c4dee59dbceab22c9f151ccb988b2c5f7167f2909027d5e0f990b59da8de115667b02484aee9a67d347625700f6cacd bgpd.initd
1638a4a64ffd066b1884f7e5a4243edab68739aabd83bd35ea8c9608af7b8623eece1d59fb08feead84e4386b6d1da4220764ccf5fd7f2a9959a8470d5cce86a zebra.initd