]> git.proxmox.com Git - mirror_frr.git/blame - nhrpd/nhrp_shortcut.c
Merge pull request #7353 from AnuradhaKaruppiah/mh-cleanup-fix-1
[mirror_frr.git] / nhrpd / nhrp_shortcut.c
CommitLineData
2fb975da
TT
1/* NHRP shortcut related functions
2 * Copyright (c) 2014-2015 Timo Teräs
3 *
4 * This file is free software: you may copy, redistribute and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
b45ac5f5
DL
10#ifdef HAVE_CONFIG_H
11#include "config.h"
12#endif
13
2fb975da
TT
14#include "nhrpd.h"
15#include "table.h"
16#include "memory.h"
17#include "thread.h"
18#include "log.h"
19#include "nhrp_protocol.h"
20
819dc8bb
DL
21DEFINE_MTYPE_STATIC(NHRPD, NHRP_SHORTCUT, "NHRP shortcut")
22
2fb975da
TT
23static struct route_table *shortcut_rib[AFI_MAX];
24
25static int nhrp_shortcut_do_purge(struct thread *t);
26static void nhrp_shortcut_delete(struct nhrp_shortcut *s);
27static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s);
28
29static void nhrp_shortcut_check_use(struct nhrp_shortcut *s)
30{
31 char buf[PREFIX_STRLEN];
32
33 if (s->expiring && s->cache && s->cache->used) {
34 debugf(NHRP_DEBUG_ROUTE, "Shortcut %s used and expiring",
0d6f7fd6 35 prefix2str(s->p, buf, sizeof(buf)));
2fb975da
TT
36 nhrp_shortcut_send_resolution_req(s);
37 }
38}
39
40static int nhrp_shortcut_do_expire(struct thread *t)
41{
42 struct nhrp_shortcut *s = THREAD_ARG(t);
43
44 s->t_timer = NULL;
996c9314
LB
45 thread_add_timer(master, nhrp_shortcut_do_purge, s, s->holding_time / 3,
46 &s->t_timer);
2fb975da
TT
47 s->expiring = 1;
48 nhrp_shortcut_check_use(s);
49
50 return 0;
51}
52
996c9314
LB
53static void nhrp_shortcut_cache_notify(struct notifier_block *n,
54 unsigned long cmd)
2fb975da 55{
912c556b
GN
56 char buf[PREFIX_STRLEN];
57
996c9314
LB
58 struct nhrp_shortcut *s =
59 container_of(n, struct nhrp_shortcut, cache_notifier);
2fb975da
TT
60
61 switch (cmd) {
62 case NOTIFY_CACHE_UP:
63 if (!s->route_installed) {
912c556b 64 debugf(NHRP_DEBUG_ROUTE,
5f36c26c 65 "Shortcut: route install %s nh (unspec) dev %s",
912c556b
GN
66 prefix2str(s->p, buf, sizeof(buf)),
67 s->cache->ifp->name);
68
69 nhrp_route_announce(1, s->type, s->p, s->cache->ifp,
70 NULL, 0);
2fb975da
TT
71 s->route_installed = 1;
72 }
73 break;
74 case NOTIFY_CACHE_USED:
75 nhrp_shortcut_check_use(s);
76 break;
77 case NOTIFY_CACHE_DOWN:
78 case NOTIFY_CACHE_DELETE:
79 if (s->route_installed) {
996c9314
LB
80 nhrp_route_announce(0, NHRP_CACHE_INVALID, s->p, NULL,
81 NULL, 0);
2fb975da
TT
82 s->route_installed = 0;
83 }
84 if (cmd == NOTIFY_CACHE_DELETE)
85 nhrp_shortcut_delete(s);
86 break;
87 }
88}
89
996c9314
LB
90static void nhrp_shortcut_update_binding(struct nhrp_shortcut *s,
91 enum nhrp_cache_type type,
92 struct nhrp_cache *c, int holding_time)
2fb975da 93{
912c556b
GN
94 char buf[2][PREFIX_STRLEN];
95
2fb975da
TT
96 s->type = type;
97 if (c != s->cache) {
98 if (s->cache) {
99 nhrp_cache_notify_del(s->cache, &s->cache_notifier);
100 s->cache = NULL;
101 }
102 s->cache = c;
103 if (s->cache) {
996c9314
LB
104 nhrp_cache_notify_add(s->cache, &s->cache_notifier,
105 nhrp_shortcut_cache_notify);
2fb975da 106 if (s->cache->route_installed) {
996c9314
LB
107 /* Force renewal of Zebra announce on prefix
108 * change */
2fb975da 109 s->route_installed = 0;
912c556b 110 debugf(NHRP_DEBUG_ROUTE,
5f36c26c 111 "Shortcut: forcing renewal of zebra announce on prefix change peer %s ht %u cur nbma %s dev %s",
912c556b
GN
112 sockunion2str(&s->cache->remote_addr,
113 buf[0], sizeof(buf[0])),
114 holding_time,
115 sockunion2str(
116 &s->cache->cur.remote_nbma_natoa,
117 buf[1], sizeof(buf[1])),
118 s->cache->ifp->name);
996c9314
LB
119 nhrp_shortcut_cache_notify(&s->cache_notifier,
120 NOTIFY_CACHE_UP);
2fb975da
TT
121 }
122 }
912c556b
GN
123 if (!s->cache || !s->cache->route_installed) {
124 debugf(NHRP_DEBUG_ROUTE,
5f36c26c 125 "Shortcut: notify cache down because cache?%s or ri?%s",
912c556b
GN
126 s->cache ? "yes" : "no",
127 s->cache ? (s->cache->route_installed ? "yes"
128 : "no")
129 : "n/a");
996c9314
LB
130 nhrp_shortcut_cache_notify(&s->cache_notifier,
131 NOTIFY_CACHE_DOWN);
912c556b 132 }
2fb975da
TT
133 }
134 if (s->type == NHRP_CACHE_NEGATIVE && !s->route_installed) {
135 nhrp_route_announce(1, s->type, s->p, NULL, NULL, 0);
136 s->route_installed = 1;
137 } else if (s->type == NHRP_CACHE_INVALID && s->route_installed) {
138 nhrp_route_announce(0, NHRP_CACHE_INVALID, s->p, NULL, NULL, 0);
139 s->route_installed = 0;
140 }
141
142 THREAD_OFF(s->t_timer);
143 if (holding_time) {
144 s->expiring = 0;
145 s->holding_time = holding_time;
ffa2c898
QY
146 thread_add_timer(master, nhrp_shortcut_do_expire, s,
147 2 * holding_time / 3, &s->t_timer);
2fb975da
TT
148 }
149}
150
151static void nhrp_shortcut_delete(struct nhrp_shortcut *s)
152{
153 struct route_node *rn;
154 afi_t afi = family2afi(PREFIX_FAMILY(s->p));
155 char buf[PREFIX_STRLEN];
156
157 THREAD_OFF(s->t_timer);
158 nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
159
160 debugf(NHRP_DEBUG_ROUTE, "Shortcut %s purged",
0d6f7fd6 161 prefix2str(s->p, buf, sizeof(buf)));
2fb975da
TT
162
163 nhrp_shortcut_update_binding(s, NHRP_CACHE_INVALID, NULL, 0);
164
165 /* Delete node */
166 rn = route_node_lookup(shortcut_rib[afi], s->p);
167 if (rn) {
168 XFREE(MTYPE_NHRP_SHORTCUT, rn->info);
912c556b 169 rn->info = NULL;
2fb975da
TT
170 route_unlock_node(rn);
171 route_unlock_node(rn);
172 }
173}
174
175static int nhrp_shortcut_do_purge(struct thread *t)
176{
177 struct nhrp_shortcut *s = THREAD_ARG(t);
178 s->t_timer = NULL;
179 nhrp_shortcut_delete(s);
180 return 0;
181}
182
183static struct nhrp_shortcut *nhrp_shortcut_get(struct prefix *p)
184{
185 struct nhrp_shortcut *s;
186 struct route_node *rn;
187 char buf[PREFIX_STRLEN];
188 afi_t afi = family2afi(PREFIX_FAMILY(p));
189
190 if (!shortcut_rib[afi])
191 return 0;
192
193 rn = route_node_get(shortcut_rib[afi], p);
194 if (!rn->info) {
996c9314
LB
195 s = rn->info = XCALLOC(MTYPE_NHRP_SHORTCUT,
196 sizeof(struct nhrp_shortcut));
2fb975da
TT
197 s->type = NHRP_CACHE_INVALID;
198 s->p = &rn->p;
199
200 debugf(NHRP_DEBUG_ROUTE, "Shortcut %s created",
0d6f7fd6 201 prefix2str(s->p, buf, sizeof(buf)));
2fb975da
TT
202 } else {
203 s = rn->info;
204 route_unlock_node(rn);
205 }
206 return s;
207}
208
996c9314
LB
209static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid,
210 void *arg)
2fb975da
TT
211{
212 struct nhrp_packet_parser *pp = arg;
996c9314
LB
213 struct nhrp_shortcut *s =
214 container_of(reqid, struct nhrp_shortcut, reqid);
2fb975da
TT
215 struct nhrp_shortcut *ps;
216 struct nhrp_extension_header *ext;
217 struct nhrp_cie_header *cie;
218 struct nhrp_cache *c = NULL;
912c556b 219 union sockunion *proto, cie_proto, *nbma, cie_nbma, nat_nbma;
2fb975da
TT
220 struct prefix prefix, route_prefix;
221 struct zbuf extpl;
912c556b 222 char bufp[PREFIX_STRLEN], buf[4][SU_ADDRSTRLEN];
2fb975da
TT
223 int holding_time = pp->if_ad->holdtime;
224
225 nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
226 THREAD_OFF(s->t_timer);
ffa2c898 227 thread_add_timer(master, nhrp_shortcut_do_purge, s, 1, &s->t_timer);
2fb975da
TT
228
229 if (pp->hdr->type != NHRP_PACKET_RESOLUTION_REPLY) {
996c9314
LB
230 if (pp->hdr->type == NHRP_PACKET_ERROR_INDICATION
231 && pp->hdr->u.error.code
232 == NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE) {
233 debugf(NHRP_DEBUG_COMMON,
234 "Shortcut: Resolution: Protocol address unreachable");
235 nhrp_shortcut_update_binding(s, NHRP_CACHE_NEGATIVE,
236 NULL, holding_time);
2fb975da 237 } else {
996c9314
LB
238 debugf(NHRP_DEBUG_COMMON,
239 "Shortcut: Resolution failed");
2fb975da
TT
240 }
241 return;
242 }
243
244 /* Parse extensions */
0d6f7fd6 245 memset(&nat_nbma, 0, sizeof(nat_nbma));
2fb975da
TT
246 while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) {
247 switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
248 case NHRP_EXTENSION_NAT_ADDRESS:
249 nhrp_cie_pull(&extpl, pp->hdr, &nat_nbma, &cie_proto);
250 break;
251 }
252 }
253
254 /* Minor sanity check */
255 prefix2sockunion(s->p, &cie_proto);
256 if (!sockunion_same(&cie_proto, &pp->dst_proto)) {
996c9314
LB
257 debugf(NHRP_DEBUG_COMMON,
258 "Shortcut: Warning dst_proto altered from %s to %s",
0d6f7fd6
DA
259 sockunion2str(&cie_proto, buf[0], sizeof(buf[0])),
260 sockunion2str(&pp->dst_proto, buf[1], sizeof(buf[1])));
2fb975da
TT
261 }
262
263 /* One or more CIEs should be given as reply, we support only one */
264 cie = nhrp_cie_pull(&pp->payload, pp->hdr, &cie_nbma, &cie_proto);
265 if (!cie || cie->code != NHRP_CODE_SUCCESS) {
996c9314
LB
266 debugf(NHRP_DEBUG_COMMON, "Shortcut: CIE code %d",
267 cie ? cie->code : -1);
2fb975da
TT
268 return;
269 }
270
996c9314
LB
271 proto = sockunion_family(&cie_proto) != AF_UNSPEC ? &cie_proto
272 : &pp->dst_proto;
2fb975da
TT
273 if (cie->holding_time)
274 holding_time = htons(cie->holding_time);
275
276 prefix = *s->p;
277 prefix.prefixlen = cie->prefix_length;
278
279 /* Sanity check prefix length */
996c9314
LB
280 if (prefix.prefixlen >= 8 * prefix_blen(&prefix)
281 || prefix.prefixlen == 0) {
282 prefix.prefixlen = 8 * prefix_blen(&prefix);
283 } else if (nhrp_route_address(NULL, &pp->dst_proto, &route_prefix, NULL)
284 == NHRP_ROUTE_NBMA_NEXTHOP) {
2fb975da
TT
285 if (prefix.prefixlen < route_prefix.prefixlen)
286 prefix.prefixlen = route_prefix.prefixlen;
287 }
288
996c9314 289 debugf(NHRP_DEBUG_COMMON,
31384f92 290 "Shortcut: %s is at proto %s dst_proto %s cie-nbma %s nat-nbma %s cie-holdtime %d",
0d6f7fd6 291 prefix2str(&prefix, bufp, sizeof(bufp)),
912c556b
GN
292 sockunion2str(proto, buf[0], sizeof(buf[0])),
293 sockunion2str(&pp->dst_proto, buf[1], sizeof(buf[1])),
294 sockunion2str(&cie_nbma, buf[2], sizeof(buf[2])),
295 sockunion2str(&nat_nbma, buf[3], sizeof(buf[3])),
996c9314 296 htons(cie->holding_time));
2fb975da
TT
297
298 /* Update cache entry for the protocol to nbma binding */
912c556b 299 if (sockunion_family(&nat_nbma) != AF_UNSPEC)
2fb975da 300 nbma = &nat_nbma;
912c556b 301 else
2fb975da 302 nbma = &cie_nbma;
912c556b 303
2fb975da
TT
304 if (sockunion_family(nbma)) {
305 c = nhrp_cache_get(pp->ifp, proto, 1);
306 if (c) {
912c556b
GN
307 debugf(NHRP_DEBUG_COMMON,
308 "Shortcut: cache found, update binding");
309 nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC,
996c9314
LB
310 holding_time,
311 nhrp_peer_get(pp->ifp, nbma),
912c556b
GN
312 htons(cie->mtu), nbma);
313 } else {
314 debugf(NHRP_DEBUG_COMMON,
315 "Shortcut: no cache for nbma %s", buf[2]);
2fb975da
TT
316 }
317 }
318
319 /* Update shortcut entry for subnet to protocol gw binding */
912c556b 320 if (c) {
2fb975da
TT
321 ps = nhrp_shortcut_get(&prefix);
322 if (ps) {
323 ps->addr = s->addr;
912c556b 324 debugf(NHRP_DEBUG_COMMON,
5f36c26c 325 "Shortcut: calling update_binding");
912c556b 326 nhrp_shortcut_update_binding(ps, NHRP_CACHE_DYNAMIC, c,
996c9314 327 holding_time);
912c556b
GN
328 } else {
329 debugf(NHRP_DEBUG_COMMON,
330 "Shortcut: proto diff but no ps");
2fb975da 331 }
912c556b
GN
332 } else {
333 debugf(NHRP_DEBUG_COMMON,
5f36c26c 334 "NO Shortcut because c NULL?%s or same proto?%s",
912c556b
GN
335 c ? "no" : "yes",
336 proto && pp && sockunion_same(proto, &pp->dst_proto)
337 ? "yes"
338 : "no");
2fb975da
TT
339 }
340
341 debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution reply handled");
342}
343
344static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s)
345{
346 struct zbuf *zb;
347 struct nhrp_packet_header *hdr;
348 struct interface *ifp;
349 struct nhrp_interface *nifp;
47d40757 350 struct nhrp_afi_data *if_ad;
2fb975da 351 struct nhrp_peer *peer;
47d40757 352 struct nhrp_cie_header *cie;
2fb975da 353
996c9314
LB
354 if (nhrp_route_address(NULL, &s->addr, NULL, &peer)
355 != NHRP_ROUTE_NBMA_NEXTHOP)
2fb975da
TT
356 return;
357
358 if (s->type == NHRP_CACHE_INVALID || s->type == NHRP_CACHE_NEGATIVE)
359 s->type = NHRP_CACHE_INCOMPLETE;
360
361 ifp = peer->ifp;
362 nifp = ifp->info;
363
364 /* Create request */
365 zb = zbuf_alloc(1500);
996c9314
LB
366 hdr = nhrp_packet_push(
367 zb, NHRP_PACKET_RESOLUTION_REQUEST, &nifp->nbma,
368 &nifp->afi[family2afi(sockunion_family(&s->addr))].addr,
369 &s->addr);
370 hdr->u.request_id =
371 htonl(nhrp_reqid_alloc(&nhrp_packet_reqid, &s->reqid,
372 nhrp_shortcut_recv_resolution_rep));
373 hdr->flags = htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER
374 | NHRP_FLAG_RESOLUTION_AUTHORATIVE
375 | NHRP_FLAG_RESOLUTION_SOURCE_STABLE);
2fb975da
TT
376
377 /* RFC2332 - One or zero CIEs, if CIE is present contains:
378 * - Prefix length: widest acceptable prefix we accept (if U set, 0xff)
379 * - MTU: MTU of the source station
380 * - Holding Time: Max time to cache the source information
381 * */
47d40757
GN
382 /* FIXME: push CIE for each local protocol address */
383 cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL);
384 cie->prefix_length = 0xff;
385 if_ad = &nifp->afi[family2afi(sockunion_family(&s->addr))];
386 cie->holding_time = htons(if_ad->holdtime);
387 cie->mtu = htons(if_ad->mtu);
388 debugf(NHRP_DEBUG_COMMON,
5f36c26c 389 "Shortcut res_req: set cie ht to %u and mtu to %u. shortcut ht is %u",
47d40757 390 ntohs(cie->holding_time), ntohs(cie->mtu), s->holding_time);
2fb975da
TT
391
392 nhrp_ext_request(zb, hdr, ifp);
393
394 /* Cisco NAT detection extension */
395 hdr->flags |= htons(NHRP_FLAG_RESOLUTION_NAT);
396 nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
397
398 nhrp_packet_complete(zb, hdr);
399
400 nhrp_peer_send(peer, zb);
401 nhrp_peer_unref(peer);
402 zbuf_free(zb);
403}
404
405void nhrp_shortcut_initiate(union sockunion *addr)
406{
407 struct prefix p;
408 struct nhrp_shortcut *s;
409
410 sockunion2hostprefix(addr, &p);
411 s = nhrp_shortcut_get(&p);
412 if (s && s->type != NHRP_CACHE_INCOMPLETE) {
413 s->addr = *addr;
414 THREAD_OFF(s->t_timer);
ffa2c898
QY
415 thread_add_timer(master, nhrp_shortcut_do_purge, s, 30,
416 &s->t_timer);
2fb975da
TT
417 nhrp_shortcut_send_resolution_req(s);
418 }
419}
420
421void nhrp_shortcut_init(void)
422{
423 shortcut_rib[AFI_IP] = route_table_init();
424 shortcut_rib[AFI_IP6] = route_table_init();
425}
426
427void nhrp_shortcut_terminate(void)
428{
429 route_table_finish(shortcut_rib[AFI_IP]);
430 route_table_finish(shortcut_rib[AFI_IP6]);
431}
432
996c9314
LB
433void nhrp_shortcut_foreach(afi_t afi,
434 void (*cb)(struct nhrp_shortcut *, void *),
435 void *ctx)
2fb975da
TT
436{
437 struct route_table *rt = shortcut_rib[afi];
438 struct route_node *rn;
439 route_table_iter_t iter;
440
996c9314
LB
441 if (!rt)
442 return;
2fb975da
TT
443
444 route_table_iter_init(&iter, rt);
445 while ((rn = route_table_iter_next(&iter)) != NULL) {
996c9314
LB
446 if (rn->info)
447 cb(rn->info, ctx);
2fb975da
TT
448 }
449 route_table_iter_cleanup(&iter);
450}
451
452struct purge_ctx {
453 const struct prefix *p;
454 int deleted;
455};
456
457void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force)
458{
459 THREAD_OFF(s->t_timer);
460 nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
461
462 if (force) {
463 /* Immediate purge on route with draw or pending shortcut */
ffa2c898
QY
464 thread_add_timer_msec(master, nhrp_shortcut_do_purge, s, 5,
465 &s->t_timer);
2fb975da
TT
466 } else {
467 /* Soft expire - force immediate renewal, but purge
468 * in few seconds to make sure stale route is not
469 * used too long. In practice most purges are caused
470 * by hub bgp change, but target usually stays same.
471 * This allows to keep nhrp route up, and to not
472 * cause temporary rerouting via hubs causing latency
473 * jitter. */
ffa2c898
QY
474 thread_add_timer_msec(master, nhrp_shortcut_do_purge, s, 3000,
475 &s->t_timer);
2fb975da
TT
476 s->expiring = 1;
477 nhrp_shortcut_check_use(s);
478 }
479}
480
481static void nhrp_shortcut_purge_prefix(struct nhrp_shortcut *s, void *ctx)
482{
483 struct purge_ctx *pctx = ctx;
484
485 if (prefix_match(pctx->p, s->p))
486 nhrp_shortcut_purge(s, pctx->deleted || !s->cache);
487}
488
489void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted)
490{
491 struct purge_ctx pctx = {
996c9314 492 .p = p, .deleted = deleted,
2fb975da 493 };
996c9314
LB
494 nhrp_shortcut_foreach(family2afi(PREFIX_FAMILY(p)),
495 nhrp_shortcut_purge_prefix, &pctx);
2fb975da 496}