]> git.proxmox.com Git - mirror_frr.git/blob - nhrpd/nhrp_shortcut.c
Merge pull request #7353 from AnuradhaKaruppiah/mh-cleanup-fix-1
[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 char buf[PREFIX_STRLEN];
32
33 if (s->expiring && s->cache && s->cache->used) {
34 debugf(NHRP_DEBUG_ROUTE, "Shortcut %s used and expiring",
35 prefix2str(s->p, buf, sizeof(buf)));
36 nhrp_shortcut_send_resolution_req(s);
37 }
38 }
39
40 static int nhrp_shortcut_do_expire(struct thread *t)
41 {
42 struct nhrp_shortcut *s = THREAD_ARG(t);
43
44 s->t_timer = NULL;
45 thread_add_timer(master, nhrp_shortcut_do_purge, s, s->holding_time / 3,
46 &s->t_timer);
47 s->expiring = 1;
48 nhrp_shortcut_check_use(s);
49
50 return 0;
51 }
52
53 static void nhrp_shortcut_cache_notify(struct notifier_block *n,
54 unsigned long cmd)
55 {
56 char buf[PREFIX_STRLEN];
57
58 struct nhrp_shortcut *s =
59 container_of(n, struct nhrp_shortcut, cache_notifier);
60
61 switch (cmd) {
62 case NOTIFY_CACHE_UP:
63 if (!s->route_installed) {
64 debugf(NHRP_DEBUG_ROUTE,
65 "Shortcut: route install %s nh (unspec) dev %s",
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);
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 char buf[2][PREFIX_STRLEN];
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 %s ht %u cur nbma %s dev %s",
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);
119 nhrp_shortcut_cache_notify(&s->cache_notifier,
120 NOTIFY_CACHE_UP);
121 }
122 }
123 if (!s->cache || !s->cache->route_installed) {
124 debugf(NHRP_DEBUG_ROUTE,
125 "Shortcut: notify cache down because cache?%s or ri?%s",
126 s->cache ? "yes" : "no",
127 s->cache ? (s->cache->route_installed ? "yes"
128 : "no")
129 : "n/a");
130 nhrp_shortcut_cache_notify(&s->cache_notifier,
131 NOTIFY_CACHE_DOWN);
132 }
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;
146 thread_add_timer(master, nhrp_shortcut_do_expire, s,
147 2 * holding_time / 3, &s->t_timer);
148 }
149 }
150
151 static 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",
161 prefix2str(s->p, buf, sizeof(buf)));
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);
169 rn->info = NULL;
170 route_unlock_node(rn);
171 route_unlock_node(rn);
172 }
173 }
174
175 static 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
183 static 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) {
195 s = rn->info = XCALLOC(MTYPE_NHRP_SHORTCUT,
196 sizeof(struct nhrp_shortcut));
197 s->type = NHRP_CACHE_INVALID;
198 s->p = &rn->p;
199
200 debugf(NHRP_DEBUG_ROUTE, "Shortcut %s created",
201 prefix2str(s->p, buf, sizeof(buf)));
202 } else {
203 s = rn->info;
204 route_unlock_node(rn);
205 }
206 return s;
207 }
208
209 static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid,
210 void *arg)
211 {
212 struct nhrp_packet_parser *pp = arg;
213 struct nhrp_shortcut *s =
214 container_of(reqid, struct nhrp_shortcut, reqid);
215 struct nhrp_shortcut *ps;
216 struct nhrp_extension_header *ext;
217 struct nhrp_cie_header *cie;
218 struct nhrp_cache *c = NULL;
219 union sockunion *proto, cie_proto, *nbma, cie_nbma, nat_nbma;
220 struct prefix prefix, route_prefix;
221 struct zbuf extpl;
222 char bufp[PREFIX_STRLEN], buf[4][SU_ADDRSTRLEN];
223 int holding_time = pp->if_ad->holdtime;
224
225 nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
226 THREAD_OFF(s->t_timer);
227 thread_add_timer(master, nhrp_shortcut_do_purge, s, 1, &s->t_timer);
228
229 if (pp->hdr->type != NHRP_PACKET_RESOLUTION_REPLY) {
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);
237 } else {
238 debugf(NHRP_DEBUG_COMMON,
239 "Shortcut: Resolution failed");
240 }
241 return;
242 }
243
244 /* Parse extensions */
245 memset(&nat_nbma, 0, sizeof(nat_nbma));
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)) {
257 debugf(NHRP_DEBUG_COMMON,
258 "Shortcut: Warning dst_proto altered from %s to %s",
259 sockunion2str(&cie_proto, buf[0], sizeof(buf[0])),
260 sockunion2str(&pp->dst_proto, buf[1], sizeof(buf[1])));
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) {
266 debugf(NHRP_DEBUG_COMMON, "Shortcut: CIE code %d",
267 cie ? cie->code : -1);
268 return;
269 }
270
271 proto = sockunion_family(&cie_proto) != AF_UNSPEC ? &cie_proto
272 : &pp->dst_proto;
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 */
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) {
285 if (prefix.prefixlen < route_prefix.prefixlen)
286 prefix.prefixlen = route_prefix.prefixlen;
287 }
288
289 debugf(NHRP_DEBUG_COMMON,
290 "Shortcut: %s is at proto %s dst_proto %s cie-nbma %s nat-nbma %s cie-holdtime %d",
291 prefix2str(&prefix, bufp, sizeof(bufp)),
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])),
296 htons(cie->holding_time));
297
298 /* Update cache entry for the protocol to nbma binding */
299 if (sockunion_family(&nat_nbma) != AF_UNSPEC)
300 nbma = &nat_nbma;
301 else
302 nbma = &cie_nbma;
303
304 if (sockunion_family(nbma)) {
305 c = nhrp_cache_get(pp->ifp, proto, 1);
306 if (c) {
307 debugf(NHRP_DEBUG_COMMON,
308 "Shortcut: cache found, update binding");
309 nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC,
310 holding_time,
311 nhrp_peer_get(pp->ifp, nbma),
312 htons(cie->mtu), nbma);
313 } else {
314 debugf(NHRP_DEBUG_COMMON,
315 "Shortcut: no cache for nbma %s", buf[2]);
316 }
317 }
318
319 /* Update shortcut entry for subnet to protocol gw binding */
320 if (c) {
321 ps = nhrp_shortcut_get(&prefix);
322 if (ps) {
323 ps->addr = s->addr;
324 debugf(NHRP_DEBUG_COMMON,
325 "Shortcut: calling update_binding");
326 nhrp_shortcut_update_binding(ps, NHRP_CACHE_DYNAMIC, c,
327 holding_time);
328 } else {
329 debugf(NHRP_DEBUG_COMMON,
330 "Shortcut: proto diff but no ps");
331 }
332 } else {
333 debugf(NHRP_DEBUG_COMMON,
334 "NO Shortcut because c NULL?%s or same proto?%s",
335 c ? "no" : "yes",
336 proto && pp && sockunion_same(proto, &pp->dst_proto)
337 ? "yes"
338 : "no");
339 }
340
341 debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution reply handled");
342 }
343
344 static 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;
350 struct nhrp_afi_data *if_ad;
351 struct nhrp_peer *peer;
352 struct nhrp_cie_header *cie;
353
354 if (nhrp_route_address(NULL, &s->addr, NULL, &peer)
355 != NHRP_ROUTE_NBMA_NEXTHOP)
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);
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);
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 * */
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,
389 "Shortcut res_req: set cie ht to %u and mtu to %u. shortcut ht is %u",
390 ntohs(cie->holding_time), ntohs(cie->mtu), s->holding_time);
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
405 void 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);
415 thread_add_timer(master, nhrp_shortcut_do_purge, s, 30,
416 &s->t_timer);
417 nhrp_shortcut_send_resolution_req(s);
418 }
419 }
420
421 void nhrp_shortcut_init(void)
422 {
423 shortcut_rib[AFI_IP] = route_table_init();
424 shortcut_rib[AFI_IP6] = route_table_init();
425 }
426
427 void nhrp_shortcut_terminate(void)
428 {
429 route_table_finish(shortcut_rib[AFI_IP]);
430 route_table_finish(shortcut_rib[AFI_IP6]);
431 }
432
433 void nhrp_shortcut_foreach(afi_t afi,
434 void (*cb)(struct nhrp_shortcut *, void *),
435 void *ctx)
436 {
437 struct route_table *rt = shortcut_rib[afi];
438 struct route_node *rn;
439 route_table_iter_t iter;
440
441 if (!rt)
442 return;
443
444 route_table_iter_init(&iter, rt);
445 while ((rn = route_table_iter_next(&iter)) != NULL) {
446 if (rn->info)
447 cb(rn->info, ctx);
448 }
449 route_table_iter_cleanup(&iter);
450 }
451
452 struct purge_ctx {
453 const struct prefix *p;
454 int deleted;
455 };
456
457 void 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 */
464 thread_add_timer_msec(master, nhrp_shortcut_do_purge, s, 5,
465 &s->t_timer);
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. */
474 thread_add_timer_msec(master, nhrp_shortcut_do_purge, s, 3000,
475 &s->t_timer);
476 s->expiring = 1;
477 nhrp_shortcut_check_use(s);
478 }
479 }
480
481 static 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
489 void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted)
490 {
491 struct purge_ctx pctx = {
492 .p = p, .deleted = deleted,
493 };
494 nhrp_shortcut_foreach(family2afi(PREFIX_FAMILY(p)),
495 nhrp_shortcut_purge_prefix, &pctx);
496 }