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