diff options
author | Timo Teräs <timo.teras@iki.fi> | 2017-02-09 12:51:13 +0000 |
---|---|---|
committer | Timo Teräs <timo.teras@iki.fi> | 2017-02-09 14:01:04 +0000 |
commit | 1d287b67101639d947830eda2412ea84980f68ae (patch) | |
tree | 02aaf96ceccf06c5a2fedc491c2ccfc5bb2aa7c0 /main | |
parent | 9e5dd61600d687fa3525d6084874883629b5fb25 (diff) | |
download | aports-1d287b67101639d947830eda2412ea84980f68ae.tar.bz2 aports-1d287b67101639d947830eda2412ea84980f68ae.tar.xz |
main/quagga: upgrade to 1.2.0
- nhrpd added
- replace and provde quagga-nhrp now that it's upstreamed
- remove quagga-nhrp as obsolete
Diffstat (limited to 'main')
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(®->reglist_entry); -+ list_add_tail(®->reglist_entry, &nhs->reglist_head); -+ nhrp_peer_notify_add(reg->peer, ®->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 |