diff --git a/nhrpd/nhrp_shortcut.c b/nhrpd/nhrp_shortcut.c index 9b47d974c340..55d61a90b8dd 100644 --- a/nhrpd/nhrp_shortcut.c +++ b/nhrpd/nhrp_shortcut.c @@ -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_delete(struct nhrp_shortcut *s, 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) { if (s->expiring && s->cache && s->cache->used) { debugf(NHRP_DEBUG_ROUTE, "Shortcut %pFX used and expiring", 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); event_add_timer(master, nhrp_shortcut_do_purge, s, s->holding_time / 3, - &s->t_timer); + &s->t_shortcut_purge); s->expiring = 1; nhrp_shortcut_check_use(s); } @@ -124,12 +126,12 @@ static void nhrp_shortcut_update_binding(struct nhrp_shortcut *s, s->route_installed = 0; } - EVENT_OFF(s->t_timer); + EVENT_OFF(s->t_shortcut_purge); if (holding_time) { s->expiring = 0; s->holding_time = holding_time; 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; 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); 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) { 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); } @@ -206,8 +210,10 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, int holding_time = pp->if_ad->holdtime; nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); - EVENT_OFF(s->t_timer); - event_add_timer(master, nhrp_shortcut_do_purge, s, 1, &s->t_timer); + EVENT_OFF(s->t_shortcut_purge); + 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_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"); } -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 nhrp_packet_header *hdr; @@ -389,6 +396,22 @@ static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s) != NHRP_ROUTE_NBMA_NEXTHOP) 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) 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, &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)); + + /* 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)); + 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 | NHRP_FLAG_RESOLUTION_AUTHORATIVE | 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) * - MTU: MTU of the source station * - Holding Time: Max time to cache the source information - * */ + */ /* FIXME: push CIE for each local protocol address */ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL); 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); if (s && s->type != NHRP_CACHE_INCOMPLETE) { s->addr = *addr; - EVENT_OFF(s->t_timer); - event_add_timer(master, nhrp_shortcut_do_purge, s, 30, - &s->t_timer); - nhrp_shortcut_send_resolution_req(s); + EVENT_OFF(s->t_shortcut_purge); + EVENT_OFF(s->t_retry_resolution); + + 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) { shortcut_rib[AFI_IP] = route_table_init(); @@ -503,13 +552,14 @@ struct purge_ctx { 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); if (force) { /* Immediate purge on route with draw or pending shortcut */ event_add_timer_msec(master, nhrp_shortcut_do_purge, s, 5, - &s->t_timer); + &s->t_shortcut_purge); } else { /* Soft expire - force immediate renewal, but purge * 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 * cause temporary rerouting via hubs causing latency * jitter. */ - event_add_timer_msec(master, nhrp_shortcut_do_purge, s, 3000, - &s->t_timer); + event_add_timer_msec(master, nhrp_shortcut_do_purge, s, + NHRPD_PURGE_EXPIRE, &s->t_shortcut_purge); s->expiring = 1; nhrp_shortcut_check_use(s); } diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h index 344e1d51780f..6cab7b3a1d89 100644 --- a/nhrpd/nhrpd.h +++ b/nhrpd/nhrpd.h @@ -15,7 +15,8 @@ DECLARE_MGROUP(NHRPD); #define NHRPD_DEFAULT_HOLDTIME 7200 - +#define NHRPD_DEFAULT_PURGE_TIME 30 +#define NHRPD_PURGE_EXPIRE 3000 #define NHRP_DEFAULT_CONFIG "nhrpd.conf" extern struct event_loop *master; @@ -250,10 +251,12 @@ struct nhrp_shortcut { union sockunion addr; struct nhrp_reqid reqid; - struct event *t_timer; + struct event *t_shortcut_purge; + struct event *t_retry_resolution; enum nhrp_cache_type type; unsigned int holding_time; + unsigned int retry_interval; unsigned route_installed : 1; unsigned expiring : 1;