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