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