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