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