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