diff options
author | Timo Teräs <timo.teras@iki.fi> | 2016-10-19 13:15:07 +0300 |
---|---|---|
committer | Timo Teräs <timo.teras@iki.fi> | 2016-10-19 16:10:27 +0300 |
commit | 2ed71552e2efad2ec88d241bf447464d0873e550 (patch) | |
tree | 90c9840701559ea20d04bbf5a1b5ba7f4023bd7e /main/quagga-nhrp | |
parent | ca712c8127101721b2bb51eaccd4ed6b3491c78e (diff) | |
download | aports-2ed71552e2efad2ec88d241bf447464d0873e550.tar.bz2 aports-2ed71552e2efad2ec88d241bf447464d0873e550.tar.xz |
main/quagga-nhrp: upgrade to 1.1.0 + nhrp patch
Diffstat (limited to 'main/quagga-nhrp')
8 files changed, 9124 insertions, 38 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 new file mode 100644 index 0000000000..d9cee06948 --- /dev/null +++ b/main/quagga-nhrp/0001-zebra-use-FIB-state-for-nexthop-tracking.patch @@ -0,0 +1,28 @@ +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 new file mode 100644 index 0000000000..88d338184f --- /dev/null +++ b/main/quagga-nhrp/0002-zebra-fix-nht-validity-checking-to-be-same-as-when-i.patch @@ -0,0 +1,33 @@ +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 new file mode 100644 index 0000000000..9e338fa074 --- /dev/null +++ b/main/quagga-nhrp/0003-bgpd-honor-disable-connected-check-option-with-next-.patch @@ -0,0 +1,28 @@ +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 new file mode 100644 index 0000000000..58429fd03e --- /dev/null +++ b/main/quagga-nhrp/0004-bgpd-simplify-ebgp-multihop-and-ttl-security-handlin.patch @@ -0,0 +1,652 @@ +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 new file mode 100644 index 0000000000..f2f7696175 --- /dev/null +++ b/main/quagga-nhrp/0005-nhrpd-implement-next-hop-resolution-protocol.patch @@ -0,0 +1,8276 @@ +From a7ca28fd70cee2dda0ee34883cce901af2a1e4bf 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 | 19 +- + zebra/zebra_vty.c | 2 + + 44 files changed, 7776 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..98ca6d0 100644 +--- a/zebra/zebra_rnh.c ++++ b/zebra/zebra_rnh.c +@@ -223,15 +223,24 @@ 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; + } + } + } +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.1 + diff --git a/main/quagga-nhrp/0006-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch b/main/quagga-nhrp/0006-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch new file mode 100644 index 0000000000..f38522950a --- /dev/null +++ b/main/quagga-nhrp/0006-bgpd-allow-using-ebgp-multihop-for-ibgp-connections.patch @@ -0,0 +1,69 @@ +From 43b861e1322c492970dcb83db880c27427b13b51 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi> +Date: Wed, 19 Oct 2016 13:45:13 +0300 +Subject: [PATCH] bgpd: allow using ebgp-multihop for ibgp connections +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Allow setting ebgp-multihop for iBGP. It will basically only set +TTL for iBGP connections as the other features of ebgp-multihop +affect only eBGP. This improves compatibility with other +implementations that allow setting TTL for all connections. +Enabling this has similar reasoning as for enabling gtsm for ibgp +(see commit 5d804b439a4138c77). + +Signed-off-by: Timo Teräs <timo.teras@iki.fi> +--- + bgpd/bgp_vty.c | 3 --- + bgpd/bgpd.c | 3 --- + bgpd/bgpd.h | 5 ++--- + 3 files changed, 2 insertions(+), 9 deletions(-) + +diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c +index 8eeaff9..332ce21 100644 +--- a/bgpd/bgp_vty.c ++++ b/bgpd/bgp_vty.c +@@ -276,9 +276,6 @@ bgp_vty_return (struct vty *vty, int ret) + case BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK: + str = "ebgp-multihop and ttl-security cannot be configured together"; + break; +- case BGP_ERR_NO_IBGP_WITH_TTLHACK: +- str = "ttl-security only allowed for EBGP peers"; +- break; + } + if (str) + { +diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c +index 56e4322..68ddfc4 100644 +--- a/bgpd/bgpd.c ++++ b/bgpd/bgpd.c +@@ -2865,9 +2865,6 @@ peer_ebgp_multihop_set (struct peer *peer, int ttl) + struct listnode *node, *nnode; + struct peer *peer1; + +- if (peer->sort == BGP_PEER_IBGP) +- return BGP_ERR_NO_IBGP_WITH_TTLHACK; +- + if (peer->gtsm_hops != 0) + return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; + +diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h +index a4c608d..dbdcfa1 100644 +--- a/bgpd/bgpd.h ++++ b/bgpd/bgpd.h +@@ -831,9 +831,8 @@ enum bgp_clear_type + #define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS -28 + #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_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS -32 +-#define BGP_ERR_MAX -33 ++#define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS -31 ++#define BGP_ERR_MAX -32 + + extern struct bgp_master *bm; + +-- +2.10.1 + diff --git a/main/quagga-nhrp/APKBUILD b/main/quagga-nhrp/APKBUILD index d03771114a..59cf47cb1c 100644 --- a/main/quagga-nhrp/APKBUILD +++ b/main/quagga-nhrp/APKBUILD @@ -1,10 +1,9 @@ # Maintainer: Timo Teräs <timo.teras@iki.fi> pkgname=quagga-nhrp -pkgver=0.99.24.1_git20151103 -_commitid=dfd18986b0f7503dd2a22b42b69e44d3fdc7a99e -pkgrel=1 +pkgver=1.1.0 +pkgrel=0 pkgdesc="A free routing daemon replacing Zebra supporting RIP, OSPF, BGP and NHRP." -url="http://git.alpinelinux.org/cgit/user/tteras/quagga/commit/?h=nhrp" +url="http://quagga.net/" arch="all" license="GPL2" depends="iproute2" @@ -16,27 +15,29 @@ install="$pkgname.pre-install $pkgname.post-install $pkgname.post-upgrade" subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg" pkgusers="quagga" pkggroups="quagga" -source="quagga-nhrp-$pkgver-$pkgrel.tar.bz2::http://git.alpinelinux.org/cgit/user/tteras/quagga/snapshot/quagga-$_commitid.tar.bz2 - bgpd-route-selection-crash.patch +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-$_commitid +builddir="$srcdir"/quagga-$pkgver + prepare() { - cd "$_builddir" - for i in $source; do - case $i in - *.patch) msg $i; patch -p1 -i "$srcdir"/$i || return 1;; - esac - done - ./bootstrap.sh || return 1 + default_prepare + autoreconf } build() { - cd "$_builddir" + cd "$builddir" quagga_cv_ipforward_method=proc \ ./configure \ --build=$CBUILD \ @@ -62,7 +63,7 @@ build() { } package() { - cd "$_builddir" + cd "$builddir" make DESTDIR="$pkgdir" install || return 1 install -o quagga -g quagga -d "$pkgdir"/var/run/quagga @@ -75,21 +76,36 @@ package() { install -Dm644 "$srcdir/zebra.confd" "$pkgdir"/etc/conf.d/zebra install -o quagga -g quagga -d -m755 "$pkgdir"/etc/quagga } -md5sums="5fbd694c92a44ee125c77daa7ee20ce8 quagga-nhrp-0.99.24.1_git20151103-1.tar.bz2 -10c9c745f2f9fdd1d81a4100d44e3313 bgpd-route-selection-crash.patch +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 +b4d5c743d457b66ea284db8eb7ff4762 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="5a9251bd59716bda2d5c8ad658542a21e15a48caf3445235e5bb5113abe5577f quagga-nhrp-0.99.24.1_git20151103-1.tar.bz2 -d8d65cc092cf7644b059d4c1b789b223482b0f50ba2cc891da4d71fe083f8cc0 bgpd-route-selection-crash.patch +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 +6669ebf4398c45c63e97c2caf2b9c76aaeaa29ca88df99835a5bab4dba5a5b52 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="b0eff1263b1abc68bbf3539f4abf18138e3e8e91e2c7a4677120ec83535ed9cbb0c7c17dd2e948ce8f3ffc25b9a075e50efacc659febd229149b0a17d3abfd88 quagga-nhrp-0.99.24.1_git20151103-1.tar.bz2 -3317554cc2470f12eb6694f2ada187be4ccc45976ebf09aa487634bbd7a4820a917f3c202bb9d4736771adf33e5eafa45f7bb8dadd2e9872d5fe7885261714b5 bgpd-route-selection-crash.patch +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 +f731ea78847ce7eca459ebdeb57787380d369f095a3aff4d40aa95da017854f696b8a6e9820371e24948240580d5d691339d79fbb86007c8ea253dabf6eb1036 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-route-selection-crash.patch b/main/quagga-nhrp/bgpd-route-selection-crash.patch deleted file mode 100644 index 473e4e564b..0000000000 --- a/main/quagga-nhrp/bgpd-route-selection-crash.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c -index 34ba1ab..7ade22f 100644 ---- a/bgpd/bgp_route.c -+++ b/bgpd/bgp_route.c -@@ -553,6 +553,11 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist, - return 0; - - /* 13. Neighbor address comparision. */ -+ if (new->peer->su_remote == NULL) -+ return 0; -+ if (exist->peer->su_remote == NULL) -+ return 1; -+ - ret = sockunion_cmp (new->peer->su_remote, exist->peer->su_remote); - - if (ret == 1) |