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