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