]> git.proxmox.com Git - mirror_frr.git/blob - nhrpd/nhrp_cache.c
*: auto-convert to SPDX License IDs
[mirror_frr.git] / nhrpd / nhrp_cache.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* NHRP cache
3 * Copyright (c) 2014-2015 Timo Teräs
4 */
5
6 #include "zebra.h"
7 #include "memory.h"
8 #include "thread.h"
9 #include "hash.h"
10 #include "nhrpd.h"
11
12 #include "netlink.h"
13
14 DEFINE_MTYPE_STATIC(NHRPD, NHRP_CACHE, "NHRP cache entry");
15 DEFINE_MTYPE_STATIC(NHRPD, NHRP_CACHE_CONFIG, "NHRP cache config entry");
16
17 unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES];
18
19 const char *const nhrp_cache_type_str[] = {
20 [NHRP_CACHE_INVALID] = "invalid",
21 [NHRP_CACHE_INCOMPLETE] = "incomplete",
22 [NHRP_CACHE_NEGATIVE] = "negative",
23 [NHRP_CACHE_CACHED] = "cached",
24 [NHRP_CACHE_DYNAMIC] = "dynamic",
25 [NHRP_CACHE_NHS] = "nhs",
26 [NHRP_CACHE_STATIC] = "static",
27 [NHRP_CACHE_LOCAL] = "local",
28 };
29
30 static unsigned int nhrp_cache_protocol_key(const void *peer_data)
31 {
32 const struct nhrp_cache *p = peer_data;
33 return sockunion_hash(&p->remote_addr);
34 }
35
36 static bool nhrp_cache_protocol_cmp(const void *cache_data,
37 const void *key_data)
38 {
39 const struct nhrp_cache *a = cache_data;
40 const struct nhrp_cache *b = key_data;
41
42 return sockunion_same(&a->remote_addr, &b->remote_addr);
43 }
44
45 static void *nhrp_cache_alloc(void *data)
46 {
47 struct nhrp_cache *p, *key = data;
48
49 p = XMALLOC(MTYPE_NHRP_CACHE, sizeof(struct nhrp_cache));
50
51 *p = (struct nhrp_cache){
52 .cur.type = NHRP_CACHE_INVALID,
53 .new.type = NHRP_CACHE_INVALID,
54 .remote_addr = key->remote_addr,
55 .ifp = key->ifp,
56 .notifier_list =
57 NOTIFIER_LIST_INITIALIZER(&p->notifier_list),
58 };
59 nhrp_cache_counts[p->cur.type]++;
60
61 return p;
62 }
63
64 static void nhrp_cache_free(struct nhrp_cache *c)
65 {
66 struct nhrp_interface *nifp = c->ifp->info;
67
68 debugf(NHRP_DEBUG_COMMON, "Deleting cache entry");
69 nhrp_cache_counts[c->cur.type]--;
70 notifier_call(&c->notifier_list, NOTIFY_CACHE_DELETE);
71 assert(!notifier_active(&c->notifier_list));
72 hash_release(nifp->cache_hash, c);
73 THREAD_OFF(c->t_timeout);
74 THREAD_OFF(c->t_auth);
75 XFREE(MTYPE_NHRP_CACHE, c);
76 }
77
78 static unsigned int nhrp_cache_config_protocol_key(const void *peer_data)
79 {
80 const struct nhrp_cache_config *p = peer_data;
81 return sockunion_hash(&p->remote_addr);
82 }
83
84 static bool nhrp_cache_config_protocol_cmp(const void *cache_data,
85 const void *key_data)
86 {
87 const struct nhrp_cache_config *a = cache_data;
88 const struct nhrp_cache_config *b = key_data;
89
90 if (!sockunion_same(&a->remote_addr, &b->remote_addr))
91 return false;
92 if (a->ifp != b->ifp)
93 return false;
94 return true;
95 }
96
97 static void *nhrp_cache_config_alloc(void *data)
98 {
99 struct nhrp_cache_config *p, *key = data;
100
101 p = XCALLOC(MTYPE_NHRP_CACHE_CONFIG, sizeof(struct nhrp_cache_config));
102
103 *p = (struct nhrp_cache_config){
104 .remote_addr = key->remote_addr,
105 .ifp = key->ifp,
106 };
107 return p;
108 }
109
110 void nhrp_cache_config_free(struct nhrp_cache_config *c)
111 {
112 struct nhrp_interface *nifp = c->ifp->info;
113
114 hash_release(nifp->cache_config_hash, c);
115 XFREE(MTYPE_NHRP_CACHE_CONFIG, c);
116 }
117
118 struct nhrp_cache_config *nhrp_cache_config_get(struct interface *ifp,
119 union sockunion *remote_addr,
120 int create)
121 {
122 struct nhrp_interface *nifp = ifp->info;
123 struct nhrp_cache_config key;
124
125 if (!nifp->cache_config_hash) {
126 nifp->cache_config_hash =
127 hash_create(nhrp_cache_config_protocol_key,
128 nhrp_cache_config_protocol_cmp,
129 "NHRP Config Cache");
130 if (!nifp->cache_config_hash)
131 return NULL;
132 }
133 key.remote_addr = *remote_addr;
134 key.ifp = ifp;
135
136 return hash_get(nifp->cache_config_hash, &key,
137 create ? nhrp_cache_config_alloc : NULL);
138 }
139
140 static void do_nhrp_cache_free(struct hash_bucket *hb,
141 void *arg __attribute__((__unused__)))
142 {
143 struct nhrp_cache *c = hb->data;
144
145 nhrp_cache_free(c);
146 }
147
148 static void do_nhrp_cache_config_free(struct hash_bucket *hb,
149 void *arg __attribute__((__unused__)))
150 {
151 struct nhrp_cache_config *cc = hb->data;
152
153 nhrp_cache_config_free(cc);
154 }
155
156 void nhrp_cache_interface_del(struct interface *ifp)
157 {
158 struct nhrp_interface *nifp = ifp->info;
159
160 debugf(NHRP_DEBUG_COMMON, "Cleaning up undeleted cache entries (%lu)",
161 nifp->cache_hash ? nifp->cache_hash->count : 0);
162
163 if (nifp->cache_hash) {
164 hash_iterate(nifp->cache_hash, do_nhrp_cache_free, NULL);
165 hash_free(nifp->cache_hash);
166 }
167
168 if (nifp->cache_config_hash) {
169 hash_iterate(nifp->cache_config_hash, do_nhrp_cache_config_free,
170 NULL);
171 hash_free(nifp->cache_config_hash);
172 }
173 }
174
175 struct nhrp_cache *nhrp_cache_get(struct interface *ifp,
176 union sockunion *remote_addr, int create)
177 {
178 struct nhrp_interface *nifp = ifp->info;
179 struct nhrp_cache key;
180
181 if (!nifp->cache_hash) {
182 nifp->cache_hash =
183 hash_create(nhrp_cache_protocol_key,
184 nhrp_cache_protocol_cmp, "NHRP Cache");
185 if (!nifp->cache_hash)
186 return NULL;
187 }
188
189 key.remote_addr = *remote_addr;
190 key.ifp = ifp;
191
192 return hash_get(nifp->cache_hash, &key,
193 create ? nhrp_cache_alloc : NULL);
194 }
195
196 static void nhrp_cache_do_free(struct thread *t)
197 {
198 struct nhrp_cache *c = THREAD_ARG(t);
199
200 c->t_timeout = NULL;
201 nhrp_cache_free(c);
202 }
203
204 static void nhrp_cache_do_timeout(struct thread *t)
205 {
206 struct nhrp_cache *c = THREAD_ARG(t);
207
208 c->t_timeout = NULL;
209 if (c->cur.type != NHRP_CACHE_INVALID)
210 nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL,
211 NULL);
212 }
213
214 static void nhrp_cache_update_route(struct nhrp_cache *c)
215 {
216 struct prefix pfx;
217 struct nhrp_peer *p = c->cur.peer;
218 struct nhrp_interface *nifp;
219
220 if (!sockunion2hostprefix(&c->remote_addr, &pfx))
221 return;
222
223 if (p && nhrp_peer_check(p, 1)) {
224 if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) {
225 /* remote_nbma_natoa is already set. Therefore, binding
226 * should be updated to this value and not vc's remote
227 * nbma.
228 */
229 debugf(NHRP_DEBUG_COMMON,
230 "cache (remote_nbma_natoa set): Update binding for %pSU dev %s from (deleted) peer.vc.nbma %pSU to %pSU",
231 &c->remote_addr, p->ifp->name,
232 &p->vc->remote.nbma, &c->cur.remote_nbma_natoa);
233
234 netlink_update_binding(p->ifp, &c->remote_addr,
235 &c->cur.remote_nbma_natoa);
236 } else {
237 /* update binding to peer->vc->remote->nbma */
238 debugf(NHRP_DEBUG_COMMON,
239 "cache (remote_nbma_natoa unspec): Update binding for %pSU dev %s from (deleted) to peer.vc.nbma %pSU",
240 &c->remote_addr, p->ifp->name,
241 &p->vc->remote.nbma);
242
243 netlink_update_binding(p->ifp, &c->remote_addr,
244 &p->vc->remote.nbma);
245 }
246
247 nhrp_route_announce(1, c->cur.type, &pfx, c->ifp, NULL,
248 c->cur.mtu);
249 if (c->cur.type >= NHRP_CACHE_DYNAMIC) {
250 nhrp_route_update_nhrp(&pfx, c->ifp);
251 c->nhrp_route_installed = 1;
252 } else if (c->nhrp_route_installed) {
253 nhrp_route_update_nhrp(&pfx, NULL);
254 c->nhrp_route_installed = 0;
255 }
256 if (!c->route_installed) {
257 notifier_call(&c->notifier_list, NOTIFY_CACHE_UP);
258 c->route_installed = 1;
259 }
260 } else {
261 /* debug the reason for peer check fail */
262 if (p) {
263 nifp = p->ifp->info;
264 debugf(NHRP_DEBUG_COMMON,
265 "cache (peer check failed: online?%d requested?%d ipsec?%d)",
266 p->online, p->requested,
267 nifp->ipsec_profile ? 1 : 0);
268 } else
269 debugf(NHRP_DEBUG_COMMON,
270 "cache (peer check failed: no p)");
271
272 if (c->nhrp_route_installed) {
273 nhrp_route_update_nhrp(&pfx, NULL);
274 c->nhrp_route_installed = 0;
275 }
276 if (c->route_installed) {
277 assert(sockunion2hostprefix(&c->remote_addr, &pfx));
278 notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
279 nhrp_route_announce(0, c->cur.type, &pfx, NULL, NULL,
280 0);
281 c->route_installed = 0;
282 }
283 }
284 }
285
286 static void nhrp_cache_peer_notifier(struct notifier_block *n,
287 unsigned long cmd)
288 {
289 struct nhrp_cache *c =
290 container_of(n, struct nhrp_cache, peer_notifier);
291
292 switch (cmd) {
293 case NOTIFY_PEER_UP:
294 nhrp_cache_update_route(c);
295 break;
296 case NOTIFY_PEER_DOWN:
297 case NOTIFY_PEER_IFCONFIG_CHANGED:
298 notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
299 nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL,
300 NULL);
301 break;
302 case NOTIFY_PEER_NBMA_CHANGING:
303 if (c->cur.type == NHRP_CACHE_DYNAMIC)
304 c->cur.peer->vc->abort_migration = 1;
305 break;
306 }
307 }
308
309 static void nhrp_cache_reset_new(struct nhrp_cache *c)
310 {
311 THREAD_OFF(c->t_auth);
312 if (notifier_list_anywhere(&c->newpeer_notifier))
313 nhrp_peer_notify_del(c->new.peer, &c->newpeer_notifier);
314 nhrp_peer_unref(c->new.peer);
315 memset(&c->new, 0, sizeof(c->new));
316 c->new.type = NHRP_CACHE_INVALID;
317 }
318
319 static void nhrp_cache_update_timers(struct nhrp_cache *c)
320 {
321 THREAD_OFF(c->t_timeout);
322
323 switch (c->cur.type) {
324 case NHRP_CACHE_INVALID:
325 if (!c->t_auth)
326 thread_add_timer_msec(master, nhrp_cache_do_free, c, 10,
327 &c->t_timeout);
328 break;
329 case NHRP_CACHE_INCOMPLETE:
330 case NHRP_CACHE_NEGATIVE:
331 case NHRP_CACHE_CACHED:
332 case NHRP_CACHE_DYNAMIC:
333 case NHRP_CACHE_NHS:
334 case NHRP_CACHE_STATIC:
335 case NHRP_CACHE_LOCAL:
336 case NHRP_CACHE_NUM_TYPES:
337 if (c->cur.expires)
338 thread_add_timer(master, nhrp_cache_do_timeout, c,
339 c->cur.expires - monotime(NULL),
340 &c->t_timeout);
341 break;
342 }
343 }
344
345 static void nhrp_cache_authorize_binding(struct nhrp_reqid *r, void *arg)
346 {
347 struct nhrp_cache *c = container_of(r, struct nhrp_cache, eventid);
348 char buf[3][SU_ADDRSTRLEN];
349
350 debugf(NHRP_DEBUG_COMMON, "cache: %s %pSU: %s", c->ifp->name,
351 &c->remote_addr, (const char *)arg);
352
353 nhrp_reqid_free(&nhrp_event_reqid, r);
354
355 if (arg && strcmp(arg, "accept") == 0) {
356 if (c->cur.peer) {
357 netlink_update_binding(c->cur.peer->ifp,
358 &c->remote_addr, NULL);
359 nhrp_peer_notify_del(c->cur.peer, &c->peer_notifier);
360 nhrp_peer_unref(c->cur.peer);
361 }
362 nhrp_cache_counts[c->cur.type]--;
363 nhrp_cache_counts[c->new.type]++;
364 c->cur = c->new;
365 c->cur.peer = nhrp_peer_ref(c->cur.peer);
366 nhrp_cache_reset_new(c);
367 if (c->cur.peer)
368 nhrp_peer_notify_add(c->cur.peer, &c->peer_notifier,
369 nhrp_cache_peer_notifier);
370
371 if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) {
372 debugf(NHRP_DEBUG_COMMON,
373 "cache: update binding for %pSU dev %s from (deleted) peer.vc.nbma %s to %pSU",
374 &c->remote_addr, c->ifp->name,
375 (c->cur.peer ? sockunion2str(
376 &c->cur.peer->vc->remote.nbma, buf[1],
377 sizeof(buf[1]))
378 : "(no peer)"),
379 &c->cur.remote_nbma_natoa);
380
381 if (c->cur.peer)
382 netlink_update_binding(
383 c->cur.peer->ifp, &c->remote_addr,
384 &c->cur.remote_nbma_natoa);
385 }
386
387 nhrp_cache_update_route(c);
388 notifier_call(&c->notifier_list, NOTIFY_CACHE_BINDING_CHANGE);
389 } else {
390 nhrp_cache_reset_new(c);
391 }
392
393 nhrp_cache_update_timers(c);
394 }
395
396 static void nhrp_cache_do_auth_timeout(struct thread *t)
397 {
398 struct nhrp_cache *c = THREAD_ARG(t);
399 c->t_auth = NULL;
400 nhrp_cache_authorize_binding(&c->eventid, (void *)"timeout");
401 }
402
403 static void nhrp_cache_newpeer_notifier(struct notifier_block *n,
404 unsigned long cmd)
405 {
406 struct nhrp_cache *c =
407 container_of(n, struct nhrp_cache, newpeer_notifier);
408
409 switch (cmd) {
410 case NOTIFY_PEER_UP:
411 if (nhrp_peer_check(c->new.peer, 1)) {
412 evmgr_notify("authorize-binding", c,
413 nhrp_cache_authorize_binding);
414 thread_add_timer(master, nhrp_cache_do_auth_timeout, c,
415 10, &c->t_auth);
416 }
417 break;
418 case NOTIFY_PEER_DOWN:
419 case NOTIFY_PEER_IFCONFIG_CHANGED:
420 nhrp_cache_reset_new(c);
421 break;
422 }
423 }
424
425 int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type,
426 int holding_time, struct nhrp_peer *p,
427 uint32_t mtu, union sockunion *nbma_oa,
428 union sockunion *nbma_claimed)
429 {
430 char buf[2][SU_ADDRSTRLEN];
431
432 if (c->cur.type > type || c->new.type > type) {
433 nhrp_peer_unref(p);
434 return 0;
435 }
436
437 /* Sanitize MTU */
438 switch (sockunion_family(&c->remote_addr)) {
439 case AF_INET:
440 if (mtu < 576 || mtu >= 1500)
441 mtu = 0;
442 /* Opennhrp announces nbma mtu, but we use protocol mtu.
443 * This heuristic tries to fix up it. */
444 if (mtu > 1420)
445 mtu = (mtu & -16) - 80;
446 break;
447 default:
448 mtu = 0;
449 break;
450 }
451
452 sockunion2str(&c->cur.remote_nbma_natoa, buf[0], sizeof(buf[0]));
453 if (nbma_oa)
454 sockunion2str(nbma_oa, buf[1], sizeof(buf[1]));
455
456 nhrp_cache_reset_new(c);
457 if (c->cur.type == type && c->cur.peer == p && c->cur.mtu == mtu) {
458 debugf(NHRP_DEBUG_COMMON,
459 "cache: same type %u, updating expiry and changing nbma addr from %s to %s",
460 type, buf[0], nbma_oa ? buf[1] : "(NULL)");
461 if (holding_time > 0)
462 c->cur.expires = monotime(NULL) + holding_time;
463
464 if (nbma_oa)
465 c->cur.remote_nbma_natoa = *nbma_oa;
466 else
467 memset(&c->cur.remote_nbma_natoa, 0,
468 sizeof(c->cur.remote_nbma_natoa));
469
470 if (nbma_claimed)
471 c->cur.remote_nbma_claimed = *nbma_claimed;
472 else
473 memset(&c->cur.remote_nbma_claimed, 0,
474 sizeof(c->cur.remote_nbma_claimed));
475
476 nhrp_peer_unref(p);
477 } else {
478 debugf(NHRP_DEBUG_COMMON,
479 "cache: new type %u/%u, or peer %s, or mtu %u/%u, nbma %s --> %s (map %d)",
480 c->cur.type, type, (c->cur.peer == p) ? "same" : "diff",
481 c->cur.mtu, mtu, buf[0], nbma_oa ? buf[1] : "(NULL)",
482 c->map);
483 c->new.type = type;
484 c->new.peer = p;
485 c->new.mtu = mtu;
486 c->new.holding_time = holding_time;
487 if (nbma_oa)
488 c->new.remote_nbma_natoa = *nbma_oa;
489
490 if (nbma_claimed)
491 c->new.remote_nbma_claimed = *nbma_claimed;
492
493 if (holding_time > 0)
494 c->new.expires = monotime(NULL) + holding_time;
495 else if (holding_time < 0)
496 nhrp_cache_reset_new(c);
497
498 if (c->new.type == NHRP_CACHE_INVALID
499 || c->new.type >= NHRP_CACHE_STATIC || c->map) {
500 nhrp_cache_authorize_binding(&c->eventid,
501 (void *)"accept");
502 } else {
503 nhrp_peer_notify_add(c->new.peer, &c->newpeer_notifier,
504 nhrp_cache_newpeer_notifier);
505 nhrp_cache_newpeer_notifier(&c->newpeer_notifier,
506 NOTIFY_PEER_UP);
507 thread_add_timer(master, nhrp_cache_do_auth_timeout, c,
508 60, &c->t_auth);
509 }
510 }
511 nhrp_cache_update_timers(c);
512
513 return 1;
514 }
515
516 void nhrp_cache_set_used(struct nhrp_cache *c, int used)
517 {
518 c->used = used;
519 if (c->used)
520 notifier_call(&c->notifier_list, NOTIFY_CACHE_USED);
521 }
522
523 struct nhrp_cache_iterator_ctx {
524 void (*cb)(struct nhrp_cache *, void *);
525 void *ctx;
526 };
527
528 struct nhrp_cache_config_iterator_ctx {
529 void (*cb)(struct nhrp_cache_config *, void *);
530 void *ctx;
531 };
532
533 static void nhrp_cache_iterator(struct hash_bucket *b, void *ctx)
534 {
535 struct nhrp_cache_iterator_ctx *ic = ctx;
536 ic->cb(b->data, ic->ctx);
537 }
538
539 static void nhrp_cache_config_iterator(struct hash_bucket *b, void *ctx)
540 {
541 struct nhrp_cache_config_iterator_ctx *ic = ctx;
542 ic->cb(b->data, ic->ctx);
543 }
544
545 void nhrp_cache_foreach(struct interface *ifp,
546 void (*cb)(struct nhrp_cache *, void *), void *ctx)
547 {
548 struct nhrp_interface *nifp = ifp->info;
549 struct nhrp_cache_iterator_ctx ic = {
550 .cb = cb, .ctx = ctx,
551 };
552
553 if (nifp->cache_hash)
554 hash_iterate(nifp->cache_hash, nhrp_cache_iterator, &ic);
555 }
556
557 void nhrp_cache_config_foreach(struct interface *ifp,
558 void (*cb)(struct nhrp_cache_config *, void *), void *ctx)
559 {
560 struct nhrp_interface *nifp = ifp->info;
561 struct nhrp_cache_config_iterator_ctx ic = {
562 .cb = cb, .ctx = ctx,
563 };
564
565 if (nifp->cache_config_hash)
566 hash_iterate(nifp->cache_config_hash, nhrp_cache_config_iterator, &ic);
567 }
568
569 void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *n,
570 notifier_fn_t fn)
571 {
572 notifier_add(n, &c->notifier_list, fn);
573 }
574
575 void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *n)
576 {
577 notifier_del(n, &c->notifier_list);
578 }