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