]> git.proxmox.com Git - mirror_frr.git/blame - nhrpd/nhrp_cache.c
Merge pull request #8589 from idryzhov/bgp-cli-nb-fixes
[mirror_frr.git] / nhrpd / nhrp_cache.c
CommitLineData
2fb975da
TT
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
bf8d3d6a
DL
18DEFINE_MTYPE_STATIC(NHRPD, NHRP_CACHE, "NHRP cache entry");
19DEFINE_MTYPE_STATIC(NHRPD, NHRP_CACHE_CONFIG, "NHRP cache config entry");
819dc8bb 20
2fb975da
TT
21unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES];
22
996c9314
LB
23const 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",
2fb975da
TT
32};
33
d8b87afe 34static unsigned int nhrp_cache_protocol_key(const void *peer_data)
2fb975da 35{
d8b87afe 36 const struct nhrp_cache *p = peer_data;
2fb975da
TT
37 return sockunion_hash(&p->remote_addr);
38}
39
74df8d6d
DS
40static bool nhrp_cache_protocol_cmp(const void *cache_data,
41 const void *key_data)
2fb975da
TT
42{
43 const struct nhrp_cache *a = cache_data;
44 const struct nhrp_cache *b = key_data;
74df8d6d 45
2fb975da
TT
46 return sockunion_same(&a->remote_addr, &b->remote_addr);
47}
48
49static 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));
0ce1ca80
DS
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]++;
2fb975da
TT
64
65 return p;
66}
67
68static void nhrp_cache_free(struct nhrp_cache *c)
69{
70 struct nhrp_interface *nifp = c->ifp->info;
71
ee72f0a0 72 debugf(NHRP_DEBUG_COMMON, "Deleting cache entry");
2fb975da
TT
73 nhrp_cache_counts[c->cur.type]--;
74 notifier_call(&c->notifier_list, NOTIFY_CACHE_DELETE);
642ac49d 75 assert(!notifier_active(&c->notifier_list));
2fb975da 76 hash_release(nifp->cache_hash, c);
ee72f0a0
RD
77 THREAD_OFF(c->t_timeout);
78 THREAD_OFF(c->t_auth);
2fb975da
TT
79 XFREE(MTYPE_NHRP_CACHE, c);
80}
81
fef2ed13
PG
82static 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
88static 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
101static 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
114void 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
122struct 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
ee72f0a0
RD
144static 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
152static 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
160void 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
996c9314
LB
179struct nhrp_cache *nhrp_cache_get(struct interface *ifp,
180 union sockunion *remote_addr, int create)
2fb975da
TT
181{
182 struct nhrp_interface *nifp = ifp->info;
183 struct nhrp_cache key;
184
185 if (!nifp->cache_hash) {
996c9314
LB
186 nifp->cache_hash =
187 hash_create(nhrp_cache_protocol_key,
188 nhrp_cache_protocol_cmp, "NHRP Cache");
2fb975da
TT
189 if (!nifp->cache_hash)
190 return NULL;
191 }
192
193 key.remote_addr = *remote_addr;
194 key.ifp = ifp;
195
996c9314
LB
196 return hash_get(nifp->cache_hash, &key,
197 create ? nhrp_cache_alloc : NULL);
2fb975da
TT
198}
199
200static int nhrp_cache_do_free(struct thread *t)
201{
202 struct nhrp_cache *c = THREAD_ARG(t);
ee72f0a0 203
2fb975da
TT
204 c->t_timeout = NULL;
205 nhrp_cache_free(c);
206 return 0;
207}
208
209static int nhrp_cache_do_timeout(struct thread *t)
210{
211 struct nhrp_cache *c = THREAD_ARG(t);
ee72f0a0 212
2fb975da
TT
213 c->t_timeout = NULL;
214 if (c->cur.type != NHRP_CACHE_INVALID)
611915ae
AL
215 nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL,
216 NULL);
2fb975da
TT
217 return 0;
218}
219
220static void nhrp_cache_update_route(struct nhrp_cache *c)
221{
222 struct prefix pfx;
223 struct nhrp_peer *p = c->cur.peer;
912c556b 224 struct nhrp_interface *nifp;
2fb975da 225
0154d8ce
DS
226 if (!sockunion2hostprefix(&c->remote_addr, &pfx))
227 return;
2fb975da
TT
228
229 if (p && nhrp_peer_check(p, 1)) {
912c556b
GN
230 if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) {
231 /* remote_nbma_natoa is already set. Therefore, binding
232 * should be updated to this value and not vc's remote
233 * nbma.
234 */
235 debugf(NHRP_DEBUG_COMMON,
b6c48481
DS
236 "cache (remote_nbma_natoa set): Update binding for %pSU dev %s from (deleted) peer.vc.nbma %pSU to %pSU",
237 &c->remote_addr, p->ifp->name,
238 &p->vc->remote.nbma, &c->cur.remote_nbma_natoa);
912c556b
GN
239
240 netlink_update_binding(p->ifp, &c->remote_addr,
241 &c->cur.remote_nbma_natoa);
242 } else {
5f36c26c 243 /* update binding to peer->vc->remote->nbma */
912c556b 244 debugf(NHRP_DEBUG_COMMON,
b6c48481
DS
245 "cache (remote_nbma_natoa unspec): Update binding for %pSU dev %s from (deleted) to peer.vc.nbma %pSU",
246 &c->remote_addr, p->ifp->name,
247 &p->vc->remote.nbma);
912c556b
GN
248
249 netlink_update_binding(p->ifp, &c->remote_addr,
250 &p->vc->remote.nbma);
251 }
252
996c9314
LB
253 nhrp_route_announce(1, c->cur.type, &pfx, c->ifp, NULL,
254 c->cur.mtu);
2fb975da
TT
255 if (c->cur.type >= NHRP_CACHE_DYNAMIC) {
256 nhrp_route_update_nhrp(&pfx, c->ifp);
257 c->nhrp_route_installed = 1;
258 } else if (c->nhrp_route_installed) {
259 nhrp_route_update_nhrp(&pfx, NULL);
260 c->nhrp_route_installed = 0;
261 }
262 if (!c->route_installed) {
263 notifier_call(&c->notifier_list, NOTIFY_CACHE_UP);
264 c->route_installed = 1;
265 }
266 } else {
5f36c26c 267 /* debug the reason for peer check fail */
912c556b
GN
268 if (p) {
269 nifp = p->ifp->info;
270 debugf(NHRP_DEBUG_COMMON,
5f36c26c 271 "cache (peer check failed: online?%d requested?%d ipsec?%d)",
912c556b
GN
272 p->online, p->requested,
273 nifp->ipsec_profile ? 1 : 0);
274 } else
275 debugf(NHRP_DEBUG_COMMON,
276 "cache (peer check failed: no p)");
277
2fb975da
TT
278 if (c->nhrp_route_installed) {
279 nhrp_route_update_nhrp(&pfx, NULL);
280 c->nhrp_route_installed = 0;
281 }
282 if (c->route_installed) {
0154d8ce 283 assert(sockunion2hostprefix(&c->remote_addr, &pfx));
2fb975da 284 notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
996c9314
LB
285 nhrp_route_announce(0, c->cur.type, &pfx, NULL, NULL,
286 0);
2fb975da
TT
287 c->route_installed = 0;
288 }
289 }
290}
291
996c9314
LB
292static void nhrp_cache_peer_notifier(struct notifier_block *n,
293 unsigned long cmd)
2fb975da 294{
996c9314
LB
295 struct nhrp_cache *c =
296 container_of(n, struct nhrp_cache, peer_notifier);
2fb975da
TT
297
298 switch (cmd) {
299 case NOTIFY_PEER_UP:
300 nhrp_cache_update_route(c);
301 break;
302 case NOTIFY_PEER_DOWN:
303 case NOTIFY_PEER_IFCONFIG_CHANGED:
304 notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
611915ae
AL
305 nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL,
306 NULL);
2fb975da
TT
307 break;
308 case NOTIFY_PEER_NBMA_CHANGING:
309 if (c->cur.type == NHRP_CACHE_DYNAMIC)
310 c->cur.peer->vc->abort_migration = 1;
311 break;
312 }
313}
314
315static void nhrp_cache_reset_new(struct nhrp_cache *c)
316{
317 THREAD_OFF(c->t_auth);
318 if (list_hashed(&c->newpeer_notifier.notifier_entry))
319 nhrp_peer_notify_del(c->new.peer, &c->newpeer_notifier);
320 nhrp_peer_unref(c->new.peer);
321 memset(&c->new, 0, sizeof(c->new));
322 c->new.type = NHRP_CACHE_INVALID;
323}
324
325static void nhrp_cache_update_timers(struct nhrp_cache *c)
326{
327 THREAD_OFF(c->t_timeout);
328
329 switch (c->cur.type) {
330 case NHRP_CACHE_INVALID:
331 if (!c->t_auth)
996c9314
LB
332 thread_add_timer_msec(master, nhrp_cache_do_free, c, 10,
333 &c->t_timeout);
2fb975da
TT
334 break;
335 default:
336 if (c->cur.expires)
ffa2c898
QY
337 thread_add_timer(master, nhrp_cache_do_timeout, c,
338 c->cur.expires - monotime(NULL),
339 &c->t_timeout);
2fb975da
TT
340 break;
341 }
342}
343
344static void nhrp_cache_authorize_binding(struct nhrp_reqid *r, void *arg)
345{
346 struct nhrp_cache *c = container_of(r, struct nhrp_cache, eventid);
912c556b 347 char buf[3][SU_ADDRSTRLEN];
2fb975da 348
b6c48481
DS
349 debugf(NHRP_DEBUG_COMMON, "cache: %s %pSU: %s", c->ifp->name,
350 &c->remote_addr, (const char *)arg);
2fb975da
TT
351
352 nhrp_reqid_free(&nhrp_event_reqid, r);
353
354 if (arg && strcmp(arg, "accept") == 0) {
355 if (c->cur.peer) {
996c9314
LB
356 netlink_update_binding(c->cur.peer->ifp,
357 &c->remote_addr, NULL);
2fb975da
TT
358 nhrp_peer_notify_del(c->cur.peer, &c->peer_notifier);
359 nhrp_peer_unref(c->cur.peer);
360 }
361 nhrp_cache_counts[c->cur.type]--;
362 nhrp_cache_counts[c->new.type]++;
363 c->cur = c->new;
364 c->cur.peer = nhrp_peer_ref(c->cur.peer);
365 nhrp_cache_reset_new(c);
366 if (c->cur.peer)
996c9314
LB
367 nhrp_peer_notify_add(c->cur.peer, &c->peer_notifier,
368 nhrp_cache_peer_notifier);
912c556b
GN
369
370 if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) {
371 debugf(NHRP_DEBUG_COMMON,
b6c48481
DS
372 "cache: update binding for %pSU dev %s from (deleted) peer.vc.nbma %s to %pSU",
373 &c->remote_addr, c->ifp->name,
912c556b
GN
374 (c->cur.peer ? sockunion2str(
375 &c->cur.peer->vc->remote.nbma, buf[1],
5f36c26c 376 sizeof(buf[1]))
912c556b 377 : "(no peer)"),
b6c48481 378 &c->cur.remote_nbma_natoa);
912c556b 379
5f36c26c
GN
380 if (c->cur.peer)
381 netlink_update_binding(
382 c->cur.peer->ifp, &c->remote_addr,
383 &c->cur.remote_nbma_natoa);
912c556b
GN
384 }
385
2fb975da
TT
386 nhrp_cache_update_route(c);
387 notifier_call(&c->notifier_list, NOTIFY_CACHE_BINDING_CHANGE);
388 } else {
389 nhrp_cache_reset_new(c);
390 }
391
392 nhrp_cache_update_timers(c);
393}
394
395static int nhrp_cache_do_auth_timeout(struct thread *t)
396{
397 struct nhrp_cache *c = THREAD_ARG(t);
398 c->t_auth = NULL;
996c9314 399 nhrp_cache_authorize_binding(&c->eventid, (void *)"timeout");
2fb975da
TT
400 return 0;
401}
402
996c9314
LB
403static void nhrp_cache_newpeer_notifier(struct notifier_block *n,
404 unsigned long cmd)
2fb975da 405{
996c9314
LB
406 struct nhrp_cache *c =
407 container_of(n, struct nhrp_cache, newpeer_notifier);
2fb975da
TT
408
409 switch (cmd) {
410 case NOTIFY_PEER_UP:
411 if (nhrp_peer_check(c->new.peer, 1)) {
996c9314
LB
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);
2fb975da
TT
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
996c9314
LB
425int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type,
426 int holding_time, struct nhrp_peer *p,
611915ae
AL
427 uint32_t mtu, union sockunion *nbma_oa,
428 union sockunion *nbma_claimed)
2fb975da 429{
912c556b 430 char buf[2][SU_ADDRSTRLEN];
5f36c26c 431
2fb975da
TT
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. */
996c9314
LB
444 if (mtu > 1420)
445 mtu = (mtu & -16) - 80;
2fb975da
TT
446 break;
447 default:
448 mtu = 0;
449 break;
450 }
451
912c556b
GN
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
2fb975da
TT
456 nhrp_cache_reset_new(c);
457 if (c->cur.type == type && c->cur.peer == p && c->cur.mtu == mtu) {
912c556b 458 debugf(NHRP_DEBUG_COMMON,
5f36c26c 459 "cache: same type %u, updating expiry and changing nbma addr from %s to %s",
912c556b 460 type, buf[0], nbma_oa ? buf[1] : "(NULL)");
996c9314
LB
461 if (holding_time > 0)
462 c->cur.expires = monotime(NULL) + holding_time;
5f36c26c 463
996c9314
LB
464 if (nbma_oa)
465 c->cur.remote_nbma_natoa = *nbma_oa;
466 else
467 memset(&c->cur.remote_nbma_natoa, 0,
0d6f7fd6 468 sizeof(c->cur.remote_nbma_natoa));
5f36c26c 469
85365e51
AL
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
2fb975da
TT
476 nhrp_peer_unref(p);
477 } else {
912c556b 478 debugf(NHRP_DEBUG_COMMON,
5f36c26c 479 "cache: new type %u/%u, or peer %s, or mtu %u/%u, nbma %s --> %s (map %d)",
912c556b
GN
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);
2fb975da
TT
483 c->new.type = type;
484 c->new.peer = p;
485 c->new.mtu = mtu;
bb58f442 486 c->new.holding_time = holding_time;
996c9314
LB
487 if (nbma_oa)
488 c->new.remote_nbma_natoa = *nbma_oa;
2fb975da 489
85365e51
AL
490 if (nbma_claimed)
491 c->new.remote_nbma_claimed = *nbma_claimed;
492
2fb975da 493 if (holding_time > 0)
819dc8bb 494 c->new.expires = monotime(NULL) + holding_time;
2fb975da 495 else if (holding_time < 0)
45c8b07a 496 nhrp_cache_reset_new(c);
2fb975da 497
996c9314
LB
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");
2fb975da 502 } else {
996c9314
LB
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);
2fb975da
TT
509 }
510 }
511 nhrp_cache_update_timers(c);
512
513 return 1;
514}
515
516void 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
523struct nhrp_cache_iterator_ctx {
524 void (*cb)(struct nhrp_cache *, void *);
525 void *ctx;
526};
527
fef2ed13
PG
528struct nhrp_cache_config_iterator_ctx {
529 void (*cb)(struct nhrp_cache_config *, void *);
530 void *ctx;
531};
532
e3b78da8 533static void nhrp_cache_iterator(struct hash_bucket *b, void *ctx)
2fb975da
TT
534{
535 struct nhrp_cache_iterator_ctx *ic = ctx;
536 ic->cb(b->data, ic->ctx);
537}
538
fef2ed13
PG
539static 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
996c9314
LB
545void nhrp_cache_foreach(struct interface *ifp,
546 void (*cb)(struct nhrp_cache *, void *), void *ctx)
2fb975da
TT
547{
548 struct nhrp_interface *nifp = ifp->info;
549 struct nhrp_cache_iterator_ctx ic = {
996c9314 550 .cb = cb, .ctx = ctx,
2fb975da
TT
551 };
552
553 if (nifp->cache_hash)
554 hash_iterate(nifp->cache_hash, nhrp_cache_iterator, &ic);
555}
556
fef2ed13
PG
557void 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
996c9314
LB
569void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *n,
570 notifier_fn_t fn)
2fb975da
TT
571{
572 notifier_add(n, &c->notifier_list, fn);
573}
574
575void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *n)
576{
577 notifier_del(n);
578}