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