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