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