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