nhrpd: Implement retrying resolution request

In the event that a resolution request is sent and
and resolution reply is never received, resolution
requests will continue to be sent until either the
newly created shortcut has been purged or a resolution
reply is finally received.

NHRPD_DEFAULT_PURGE_TIME and NHRPD_PURGE_EXPIRE are values
that were previously hardcoded and  moved into macros for
the sake of readability.

Signed-off-by: Joshua Muthii <jmuthii@labn.net>
This commit is contained in:
Joshua Muthii 2024-09-01 09:28:16 -04:00
parent 5989d1cf78
commit 97a03685e8
2 changed files with 77 additions and 24 deletions

View file

@ -21,14 +21,16 @@ static struct route_table *shortcut_rib[AFI_MAX];
static void nhrp_shortcut_do_purge(struct event *t); static void nhrp_shortcut_do_purge(struct event *t);
static void nhrp_shortcut_delete(struct nhrp_shortcut *s, static void nhrp_shortcut_delete(struct nhrp_shortcut *s,
void *arg __attribute__((__unused__))); void *arg __attribute__((__unused__)));
static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s); static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s,
bool retry);
static void nhrp_shortcut_retry_resolution_req(struct event *t);
static void nhrp_shortcut_check_use(struct nhrp_shortcut *s) static void nhrp_shortcut_check_use(struct nhrp_shortcut *s)
{ {
if (s->expiring && s->cache && s->cache->used) { if (s->expiring && s->cache && s->cache->used) {
debugf(NHRP_DEBUG_ROUTE, "Shortcut %pFX used and expiring", debugf(NHRP_DEBUG_ROUTE, "Shortcut %pFX used and expiring",
s->p); s->p);
nhrp_shortcut_send_resolution_req(s); nhrp_shortcut_send_resolution_req(s, false);
} }
} }
@ -37,7 +39,7 @@ static void nhrp_shortcut_do_expire(struct event *t)
struct nhrp_shortcut *s = EVENT_ARG(t); struct nhrp_shortcut *s = EVENT_ARG(t);
event_add_timer(master, nhrp_shortcut_do_purge, s, s->holding_time / 3, event_add_timer(master, nhrp_shortcut_do_purge, s, s->holding_time / 3,
&s->t_timer); &s->t_shortcut_purge);
s->expiring = 1; s->expiring = 1;
nhrp_shortcut_check_use(s); nhrp_shortcut_check_use(s);
} }
@ -124,12 +126,12 @@ static void nhrp_shortcut_update_binding(struct nhrp_shortcut *s,
s->route_installed = 0; s->route_installed = 0;
} }
EVENT_OFF(s->t_timer); EVENT_OFF(s->t_shortcut_purge);
if (holding_time) { if (holding_time) {
s->expiring = 0; s->expiring = 0;
s->holding_time = holding_time; s->holding_time = holding_time;
event_add_timer(master, nhrp_shortcut_do_expire, s, event_add_timer(master, nhrp_shortcut_do_expire, s,
2 * holding_time / 3, &s->t_timer); 2 * holding_time / 3, &s->t_shortcut_purge);
} }
} }
@ -139,7 +141,8 @@ static void nhrp_shortcut_delete(struct nhrp_shortcut *s,
struct route_node *rn; struct route_node *rn;
afi_t afi = family2afi(PREFIX_FAMILY(s->p)); afi_t afi = family2afi(PREFIX_FAMILY(s->p));
EVENT_OFF(s->t_timer); EVENT_OFF(s->t_shortcut_purge);
EVENT_OFF(s->t_retry_resolution);
nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
debugf(NHRP_DEBUG_ROUTE, "Shortcut %pFX purged", s->p); debugf(NHRP_DEBUG_ROUTE, "Shortcut %pFX purged", s->p);
@ -159,7 +162,8 @@ static void nhrp_shortcut_delete(struct nhrp_shortcut *s,
static void nhrp_shortcut_do_purge(struct event *t) static void nhrp_shortcut_do_purge(struct event *t)
{ {
struct nhrp_shortcut *s = EVENT_ARG(t); struct nhrp_shortcut *s = EVENT_ARG(t);
s->t_timer = NULL; s->t_shortcut_purge = NULL;
EVENT_OFF(s->t_retry_resolution);
nhrp_shortcut_delete(s, NULL); nhrp_shortcut_delete(s, NULL);
} }
@ -206,8 +210,10 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid,
int holding_time = pp->if_ad->holdtime; int holding_time = pp->if_ad->holdtime;
nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
EVENT_OFF(s->t_timer); EVENT_OFF(s->t_shortcut_purge);
event_add_timer(master, nhrp_shortcut_do_purge, s, 1, &s->t_timer); EVENT_OFF(s->t_retry_resolution);
event_add_timer(master, nhrp_shortcut_do_purge, s, 1,
&s->t_shortcut_purge);
if (pp->hdr->type != NHRP_PACKET_RESOLUTION_REPLY) { if (pp->hdr->type != NHRP_PACKET_RESOLUTION_REPLY) {
if (pp->hdr->type == NHRP_PACKET_ERROR_INDICATION if (pp->hdr->type == NHRP_PACKET_ERROR_INDICATION
@ -374,7 +380,8 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid,
debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution reply handled"); debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution reply handled");
} }
static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s) static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s,
bool retry)
{ {
struct zbuf *zb; struct zbuf *zb;
struct nhrp_packet_header *hdr; struct nhrp_packet_header *hdr;
@ -389,6 +396,22 @@ static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s)
!= NHRP_ROUTE_NBMA_NEXTHOP) != NHRP_ROUTE_NBMA_NEXTHOP)
return; return;
/*Retry interval for NHRP resolution request
* will start at 1 second and will be doubled every time
* another resolution request is sent, until it is
* eventually upper-bounded by the purge time of
* the shortcut.
*/
if (!retry)
s->retry_interval = 1;
event_add_timer(master, nhrp_shortcut_retry_resolution_req, s,
s->retry_interval, &s->t_retry_resolution);
if (s->retry_interval != (NHRPD_DEFAULT_PURGE_TIME / 4))
s->retry_interval = ((s->retry_interval * 2) <
(NHRPD_DEFAULT_PURGE_TIME / 4))
? (s->retry_interval * 2)
: (NHRPD_DEFAULT_PURGE_TIME / 4);
if (s->type == NHRP_CACHE_INVALID || s->type == NHRP_CACHE_NEGATIVE) if (s->type == NHRP_CACHE_INVALID || s->type == NHRP_CACHE_NEGATIVE)
s->type = NHRP_CACHE_INCOMPLETE; s->type = NHRP_CACHE_INCOMPLETE;
@ -401,9 +424,23 @@ static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s)
zb, NHRP_PACKET_RESOLUTION_REQUEST, &nifp->nbma, zb, NHRP_PACKET_RESOLUTION_REQUEST, &nifp->nbma,
&nifp->afi[family2afi(sockunion_family(&s->addr))].addr, &nifp->afi[family2afi(sockunion_family(&s->addr))].addr,
&s->addr); &s->addr);
hdr->u.request_id =
htonl(nhrp_reqid_alloc(&nhrp_packet_reqid, &s->reqid, /* RFC2332 - The value is taken from a 32 bit counter that is incremented
* each time a new "request" is transmitted. The same value MUST
* be used when resending a "request", i.e., when a "reply" has not been
* received for a "request" and a retry is sent after an
* appropriate interval
*/
if (!retry)
hdr->u.request_id = htonl(
nhrp_reqid_alloc(&nhrp_packet_reqid, &s->reqid,
nhrp_shortcut_recv_resolution_rep)); nhrp_shortcut_recv_resolution_rep));
else
/* Just pull request_id from existing incomplete
* shortcut in the case of a retry
*/
hdr->u.request_id = htonl(s->reqid.request_id);
hdr->flags = htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER hdr->flags = htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER
| NHRP_FLAG_RESOLUTION_AUTHORATIVE | NHRP_FLAG_RESOLUTION_AUTHORATIVE
| NHRP_FLAG_RESOLUTION_SOURCE_STABLE); | NHRP_FLAG_RESOLUTION_SOURCE_STABLE);
@ -412,7 +449,7 @@ static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s)
* - Prefix length: widest acceptable prefix we accept (if U set, 0xff) * - Prefix length: widest acceptable prefix we accept (if U set, 0xff)
* - MTU: MTU of the source station * - MTU: MTU of the source station
* - Holding Time: Max time to cache the source information * - Holding Time: Max time to cache the source information
* */ */
/* FIXME: push CIE for each local protocol address */ /* FIXME: push CIE for each local protocol address */
cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL); cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL);
if_ad = &nifp->afi[family2afi(sockunion_family(&s->addr))]; if_ad = &nifp->afi[family2afi(sockunion_family(&s->addr))];
@ -456,13 +493,25 @@ void nhrp_shortcut_initiate(union sockunion *addr)
s = nhrp_shortcut_get(&p); s = nhrp_shortcut_get(&p);
if (s && s->type != NHRP_CACHE_INCOMPLETE) { if (s && s->type != NHRP_CACHE_INCOMPLETE) {
s->addr = *addr; s->addr = *addr;
EVENT_OFF(s->t_timer); EVENT_OFF(s->t_shortcut_purge);
event_add_timer(master, nhrp_shortcut_do_purge, s, 30, EVENT_OFF(s->t_retry_resolution);
&s->t_timer);
nhrp_shortcut_send_resolution_req(s); event_add_timer(master, nhrp_shortcut_do_purge, s,
NHRPD_DEFAULT_PURGE_TIME, &s->t_shortcut_purge);
nhrp_shortcut_send_resolution_req(s, false);
} }
} }
static void nhrp_shortcut_retry_resolution_req(struct event *t)
{
struct nhrp_shortcut *s = EVENT_ARG(t);
EVENT_OFF(s->t_retry_resolution);
debugf(NHRP_DEBUG_COMMON, "Shortcut: Retrying Resolution Request");
nhrp_shortcut_send_resolution_req(s, true);
}
void nhrp_shortcut_init(void) void nhrp_shortcut_init(void)
{ {
shortcut_rib[AFI_IP] = route_table_init(); shortcut_rib[AFI_IP] = route_table_init();
@ -503,13 +552,14 @@ struct purge_ctx {
void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force) void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force)
{ {
EVENT_OFF(s->t_timer); EVENT_OFF(s->t_shortcut_purge);
EVENT_OFF(s->t_retry_resolution);
nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
if (force) { if (force) {
/* Immediate purge on route with draw or pending shortcut */ /* Immediate purge on route with draw or pending shortcut */
event_add_timer_msec(master, nhrp_shortcut_do_purge, s, 5, event_add_timer_msec(master, nhrp_shortcut_do_purge, s, 5,
&s->t_timer); &s->t_shortcut_purge);
} else { } else {
/* Soft expire - force immediate renewal, but purge /* Soft expire - force immediate renewal, but purge
* in few seconds to make sure stale route is not * in few seconds to make sure stale route is not
@ -518,8 +568,8 @@ void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force)
* This allows to keep nhrp route up, and to not * This allows to keep nhrp route up, and to not
* cause temporary rerouting via hubs causing latency * cause temporary rerouting via hubs causing latency
* jitter. */ * jitter. */
event_add_timer_msec(master, nhrp_shortcut_do_purge, s, 3000, event_add_timer_msec(master, nhrp_shortcut_do_purge, s,
&s->t_timer); NHRPD_PURGE_EXPIRE, &s->t_shortcut_purge);
s->expiring = 1; s->expiring = 1;
nhrp_shortcut_check_use(s); nhrp_shortcut_check_use(s);
} }

View file

@ -15,7 +15,8 @@
DECLARE_MGROUP(NHRPD); DECLARE_MGROUP(NHRPD);
#define NHRPD_DEFAULT_HOLDTIME 7200 #define NHRPD_DEFAULT_HOLDTIME 7200
#define NHRPD_DEFAULT_PURGE_TIME 30
#define NHRPD_PURGE_EXPIRE 3000
#define NHRP_DEFAULT_CONFIG "nhrpd.conf" #define NHRP_DEFAULT_CONFIG "nhrpd.conf"
extern struct event_loop *master; extern struct event_loop *master;
@ -250,10 +251,12 @@ struct nhrp_shortcut {
union sockunion addr; union sockunion addr;
struct nhrp_reqid reqid; struct nhrp_reqid reqid;
struct event *t_timer; struct event *t_shortcut_purge;
struct event *t_retry_resolution;
enum nhrp_cache_type type; enum nhrp_cache_type type;
unsigned int holding_time; unsigned int holding_time;
unsigned int retry_interval;
unsigned route_installed : 1; unsigned route_installed : 1;
unsigned expiring : 1; unsigned expiring : 1;