]> git.proxmox.com Git - mirror_frr.git/blame - nhrpd/nhrp_cache.c
Merge pull request #2267 from donaldsharp/flim_flam
[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
819dc8bb
DL
18DEFINE_MTYPE_STATIC(NHRPD, NHRP_CACHE, "NHRP cache entry")
19
2fb975da
TT
20unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES];
21
996c9314
LB
22const char *const nhrp_cache_type_str[] = {
23 [NHRP_CACHE_INVALID] = "invalid",
24 [NHRP_CACHE_INCOMPLETE] = "incomplete",
25 [NHRP_CACHE_NEGATIVE] = "negative",
26 [NHRP_CACHE_CACHED] = "cached",
27 [NHRP_CACHE_DYNAMIC] = "dynamic",
28 [NHRP_CACHE_NHS] = "nhs",
29 [NHRP_CACHE_STATIC] = "static",
30 [NHRP_CACHE_LOCAL] = "local",
2fb975da
TT
31};
32
33static unsigned int nhrp_cache_protocol_key(void *peer_data)
34{
35 struct nhrp_cache *p = peer_data;
36 return sockunion_hash(&p->remote_addr);
37}
38
39static int nhrp_cache_protocol_cmp(const void *cache_data, const void *key_data)
40{
41 const struct nhrp_cache *a = cache_data;
42 const struct nhrp_cache *b = key_data;
43 return sockunion_same(&a->remote_addr, &b->remote_addr);
44}
45
46static void *nhrp_cache_alloc(void *data)
47{
48 struct nhrp_cache *p, *key = data;
49
50 p = XMALLOC(MTYPE_NHRP_CACHE, sizeof(struct nhrp_cache));
51 if (p) {
996c9314 52 *p = (struct nhrp_cache){
2fb975da
TT
53 .cur.type = NHRP_CACHE_INVALID,
54 .new.type = NHRP_CACHE_INVALID,
55 .remote_addr = key->remote_addr,
56 .ifp = key->ifp,
996c9314
LB
57 .notifier_list =
58 NOTIFIER_LIST_INITIALIZER(&p->notifier_list),
2fb975da
TT
59 };
60 nhrp_cache_counts[p->cur.type]++;
61 }
62
63 return p;
64}
65
66static void nhrp_cache_free(struct nhrp_cache *c)
67{
68 struct nhrp_interface *nifp = c->ifp->info;
69
70 zassert(c->cur.type == NHRP_CACHE_INVALID && c->cur.peer == NULL);
71 zassert(c->new.type == NHRP_CACHE_INVALID && c->new.peer == NULL);
72 nhrp_cache_counts[c->cur.type]--;
73 notifier_call(&c->notifier_list, NOTIFY_CACHE_DELETE);
74 zassert(!notifier_active(&c->notifier_list));
75 hash_release(nifp->cache_hash, c);
76 XFREE(MTYPE_NHRP_CACHE, c);
77}
78
996c9314
LB
79struct nhrp_cache *nhrp_cache_get(struct interface *ifp,
80 union sockunion *remote_addr, int create)
2fb975da
TT
81{
82 struct nhrp_interface *nifp = ifp->info;
83 struct nhrp_cache key;
84
85 if (!nifp->cache_hash) {
996c9314
LB
86 nifp->cache_hash =
87 hash_create(nhrp_cache_protocol_key,
88 nhrp_cache_protocol_cmp, "NHRP Cache");
2fb975da
TT
89 if (!nifp->cache_hash)
90 return NULL;
91 }
92
93 key.remote_addr = *remote_addr;
94 key.ifp = ifp;
95
996c9314
LB
96 return hash_get(nifp->cache_hash, &key,
97 create ? nhrp_cache_alloc : NULL);
2fb975da
TT
98}
99
100static int nhrp_cache_do_free(struct thread *t)
101{
102 struct nhrp_cache *c = THREAD_ARG(t);
103 c->t_timeout = NULL;
104 nhrp_cache_free(c);
105 return 0;
106}
107
108static int nhrp_cache_do_timeout(struct thread *t)
109{
110 struct nhrp_cache *c = THREAD_ARG(t);
111 c->t_timeout = NULL;
112 if (c->cur.type != NHRP_CACHE_INVALID)
113 nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
114 return 0;
115}
116
117static void nhrp_cache_update_route(struct nhrp_cache *c)
118{
119 struct prefix pfx;
120 struct nhrp_peer *p = c->cur.peer;
121
122 sockunion2hostprefix(&c->remote_addr, &pfx);
123
124 if (p && nhrp_peer_check(p, 1)) {
996c9314
LB
125 netlink_update_binding(p->ifp, &c->remote_addr,
126 &p->vc->remote.nbma);
127 nhrp_route_announce(1, c->cur.type, &pfx, c->ifp, NULL,
128 c->cur.mtu);
2fb975da
TT
129 if (c->cur.type >= NHRP_CACHE_DYNAMIC) {
130 nhrp_route_update_nhrp(&pfx, c->ifp);
131 c->nhrp_route_installed = 1;
132 } else if (c->nhrp_route_installed) {
133 nhrp_route_update_nhrp(&pfx, NULL);
134 c->nhrp_route_installed = 0;
135 }
136 if (!c->route_installed) {
137 notifier_call(&c->notifier_list, NOTIFY_CACHE_UP);
138 c->route_installed = 1;
139 }
140 } else {
141 if (c->nhrp_route_installed) {
142 nhrp_route_update_nhrp(&pfx, NULL);
143 c->nhrp_route_installed = 0;
144 }
145 if (c->route_installed) {
146 sockunion2hostprefix(&c->remote_addr, &pfx);
147 notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
996c9314
LB
148 nhrp_route_announce(0, c->cur.type, &pfx, NULL, NULL,
149 0);
2fb975da
TT
150 c->route_installed = 0;
151 }
152 }
153}
154
996c9314
LB
155static void nhrp_cache_peer_notifier(struct notifier_block *n,
156 unsigned long cmd)
2fb975da 157{
996c9314
LB
158 struct nhrp_cache *c =
159 container_of(n, struct nhrp_cache, peer_notifier);
2fb975da
TT
160
161 switch (cmd) {
162 case NOTIFY_PEER_UP:
163 nhrp_cache_update_route(c);
164 break;
165 case NOTIFY_PEER_DOWN:
166 case NOTIFY_PEER_IFCONFIG_CHANGED:
167 notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN);
168 nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
169 break;
170 case NOTIFY_PEER_NBMA_CHANGING:
171 if (c->cur.type == NHRP_CACHE_DYNAMIC)
172 c->cur.peer->vc->abort_migration = 1;
173 break;
174 }
175}
176
177static void nhrp_cache_reset_new(struct nhrp_cache *c)
178{
179 THREAD_OFF(c->t_auth);
180 if (list_hashed(&c->newpeer_notifier.notifier_entry))
181 nhrp_peer_notify_del(c->new.peer, &c->newpeer_notifier);
182 nhrp_peer_unref(c->new.peer);
183 memset(&c->new, 0, sizeof(c->new));
184 c->new.type = NHRP_CACHE_INVALID;
185}
186
187static void nhrp_cache_update_timers(struct nhrp_cache *c)
188{
189 THREAD_OFF(c->t_timeout);
190
191 switch (c->cur.type) {
192 case NHRP_CACHE_INVALID:
193 if (!c->t_auth)
996c9314
LB
194 thread_add_timer_msec(master, nhrp_cache_do_free, c, 10,
195 &c->t_timeout);
2fb975da
TT
196 break;
197 default:
198 if (c->cur.expires)
ffa2c898
QY
199 thread_add_timer(master, nhrp_cache_do_timeout, c,
200 c->cur.expires - monotime(NULL),
201 &c->t_timeout);
2fb975da
TT
202 break;
203 }
204}
205
206static void nhrp_cache_authorize_binding(struct nhrp_reqid *r, void *arg)
207{
208 struct nhrp_cache *c = container_of(r, struct nhrp_cache, eventid);
209 char buf[SU_ADDRSTRLEN];
210
996c9314
LB
211 debugf(NHRP_DEBUG_COMMON, "cache: %s %s: %s", c->ifp->name,
212 sockunion2str(&c->remote_addr, buf, sizeof buf),
213 (const char *)arg);
2fb975da
TT
214
215 nhrp_reqid_free(&nhrp_event_reqid, r);
216
217 if (arg && strcmp(arg, "accept") == 0) {
218 if (c->cur.peer) {
996c9314
LB
219 netlink_update_binding(c->cur.peer->ifp,
220 &c->remote_addr, NULL);
2fb975da
TT
221 nhrp_peer_notify_del(c->cur.peer, &c->peer_notifier);
222 nhrp_peer_unref(c->cur.peer);
223 }
224 nhrp_cache_counts[c->cur.type]--;
225 nhrp_cache_counts[c->new.type]++;
226 c->cur = c->new;
227 c->cur.peer = nhrp_peer_ref(c->cur.peer);
228 nhrp_cache_reset_new(c);
229 if (c->cur.peer)
996c9314
LB
230 nhrp_peer_notify_add(c->cur.peer, &c->peer_notifier,
231 nhrp_cache_peer_notifier);
2fb975da
TT
232 nhrp_cache_update_route(c);
233 notifier_call(&c->notifier_list, NOTIFY_CACHE_BINDING_CHANGE);
234 } else {
235 nhrp_cache_reset_new(c);
236 }
237
238 nhrp_cache_update_timers(c);
239}
240
241static int nhrp_cache_do_auth_timeout(struct thread *t)
242{
243 struct nhrp_cache *c = THREAD_ARG(t);
244 c->t_auth = NULL;
996c9314 245 nhrp_cache_authorize_binding(&c->eventid, (void *)"timeout");
2fb975da
TT
246 return 0;
247}
248
996c9314
LB
249static void nhrp_cache_newpeer_notifier(struct notifier_block *n,
250 unsigned long cmd)
2fb975da 251{
996c9314
LB
252 struct nhrp_cache *c =
253 container_of(n, struct nhrp_cache, newpeer_notifier);
2fb975da
TT
254
255 switch (cmd) {
256 case NOTIFY_PEER_UP:
257 if (nhrp_peer_check(c->new.peer, 1)) {
996c9314
LB
258 evmgr_notify("authorize-binding", c,
259 nhrp_cache_authorize_binding);
260 thread_add_timer(master, nhrp_cache_do_auth_timeout, c,
261 10, &c->t_auth);
2fb975da
TT
262 }
263 break;
264 case NOTIFY_PEER_DOWN:
265 case NOTIFY_PEER_IFCONFIG_CHANGED:
266 nhrp_cache_reset_new(c);
267 break;
268 }
269}
270
996c9314
LB
271int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type,
272 int holding_time, struct nhrp_peer *p,
273 uint32_t mtu, union sockunion *nbma_oa)
2fb975da
TT
274{
275 if (c->cur.type > type || c->new.type > type) {
276 nhrp_peer_unref(p);
277 return 0;
278 }
279
280 /* Sanitize MTU */
281 switch (sockunion_family(&c->remote_addr)) {
282 case AF_INET:
283 if (mtu < 576 || mtu >= 1500)
284 mtu = 0;
285 /* Opennhrp announces nbma mtu, but we use protocol mtu.
286 * This heuristic tries to fix up it. */
996c9314
LB
287 if (mtu > 1420)
288 mtu = (mtu & -16) - 80;
2fb975da
TT
289 break;
290 default:
291 mtu = 0;
292 break;
293 }
294
295 nhrp_cache_reset_new(c);
296 if (c->cur.type == type && c->cur.peer == p && c->cur.mtu == mtu) {
996c9314
LB
297 if (holding_time > 0)
298 c->cur.expires = monotime(NULL) + holding_time;
299 if (nbma_oa)
300 c->cur.remote_nbma_natoa = *nbma_oa;
301 else
302 memset(&c->cur.remote_nbma_natoa, 0,
303 sizeof c->cur.remote_nbma_natoa);
2fb975da
TT
304 nhrp_peer_unref(p);
305 } else {
306 c->new.type = type;
307 c->new.peer = p;
308 c->new.mtu = mtu;
996c9314
LB
309 if (nbma_oa)
310 c->new.remote_nbma_natoa = *nbma_oa;
2fb975da
TT
311
312 if (holding_time > 0)
819dc8bb 313 c->new.expires = monotime(NULL) + holding_time;
2fb975da 314 else if (holding_time < 0)
45c8b07a 315 nhrp_cache_reset_new(c);
2fb975da 316
996c9314
LB
317 if (c->new.type == NHRP_CACHE_INVALID
318 || c->new.type >= NHRP_CACHE_STATIC || c->map) {
319 nhrp_cache_authorize_binding(&c->eventid,
320 (void *)"accept");
2fb975da 321 } else {
996c9314
LB
322 nhrp_peer_notify_add(c->new.peer, &c->newpeer_notifier,
323 nhrp_cache_newpeer_notifier);
324 nhrp_cache_newpeer_notifier(&c->newpeer_notifier,
325 NOTIFY_PEER_UP);
326 thread_add_timer(master, nhrp_cache_do_auth_timeout, c,
327 60, &c->t_auth);
2fb975da
TT
328 }
329 }
330 nhrp_cache_update_timers(c);
331
332 return 1;
333}
334
335void nhrp_cache_set_used(struct nhrp_cache *c, int used)
336{
337 c->used = used;
338 if (c->used)
339 notifier_call(&c->notifier_list, NOTIFY_CACHE_USED);
340}
341
342struct nhrp_cache_iterator_ctx {
343 void (*cb)(struct nhrp_cache *, void *);
344 void *ctx;
345};
346
347static void nhrp_cache_iterator(struct hash_backet *b, void *ctx)
348{
349 struct nhrp_cache_iterator_ctx *ic = ctx;
350 ic->cb(b->data, ic->ctx);
351}
352
996c9314
LB
353void nhrp_cache_foreach(struct interface *ifp,
354 void (*cb)(struct nhrp_cache *, void *), void *ctx)
2fb975da
TT
355{
356 struct nhrp_interface *nifp = ifp->info;
357 struct nhrp_cache_iterator_ctx ic = {
996c9314 358 .cb = cb, .ctx = ctx,
2fb975da
TT
359 };
360
361 if (nifp->cache_hash)
362 hash_iterate(nifp->cache_hash, nhrp_cache_iterator, &ic);
363}
364
996c9314
LB
365void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *n,
366 notifier_fn_t fn)
2fb975da
TT
367{
368 notifier_add(n, &c->notifier_list, fn);
369}
370
371void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *n)
372{
373 notifier_del(n);
374}