2 * Copyright (c) 2014-2015 Timo Teräs
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.
18 DEFINE_MTYPE_STATIC(NHRPD
, NHRP_CACHE
, "NHRP cache entry")
20 unsigned long nhrp_cache_counts
[NHRP_CACHE_NUM_TYPES
];
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",
33 static unsigned int nhrp_cache_protocol_key(void *peer_data
)
35 struct nhrp_cache
*p
= peer_data
;
36 return sockunion_hash(&p
->remote_addr
);
39 static int nhrp_cache_protocol_cmp(const void *cache_data
, const void *key_data
)
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
);
46 static void *nhrp_cache_alloc(void *data
)
48 struct nhrp_cache
*p
, *key
= data
;
50 p
= XMALLOC(MTYPE_NHRP_CACHE
, sizeof(struct nhrp_cache
));
52 *p
= (struct nhrp_cache
) {
53 .cur
.type
= NHRP_CACHE_INVALID
,
54 .new.type
= NHRP_CACHE_INVALID
,
55 .remote_addr
= key
->remote_addr
,
57 .notifier_list
= NOTIFIER_LIST_INITIALIZER(&p
->notifier_list
),
59 nhrp_cache_counts
[p
->cur
.type
]++;
65 static void nhrp_cache_free(struct nhrp_cache
*c
)
67 struct nhrp_interface
*nifp
= c
->ifp
->info
;
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
);
78 struct nhrp_cache
*nhrp_cache_get(struct interface
*ifp
, union sockunion
*remote_addr
, int create
)
80 struct nhrp_interface
*nifp
= ifp
->info
;
81 struct nhrp_cache key
;
83 if (!nifp
->cache_hash
) {
84 nifp
->cache_hash
= hash_create(nhrp_cache_protocol_key
,
85 nhrp_cache_protocol_cmp
,
87 if (!nifp
->cache_hash
)
91 key
.remote_addr
= *remote_addr
;
94 return hash_get(nifp
->cache_hash
, &key
, create
? nhrp_cache_alloc
: NULL
);
97 static int nhrp_cache_do_free(struct thread
*t
)
99 struct nhrp_cache
*c
= THREAD_ARG(t
);
105 static int nhrp_cache_do_timeout(struct thread
*t
)
107 struct nhrp_cache
*c
= THREAD_ARG(t
);
109 if (c
->cur
.type
!= NHRP_CACHE_INVALID
)
110 nhrp_cache_update_binding(c
, c
->cur
.type
, -1, NULL
, 0, NULL
);
114 static void nhrp_cache_update_route(struct nhrp_cache
*c
)
117 struct nhrp_peer
*p
= c
->cur
.peer
;
119 sockunion2hostprefix(&c
->remote_addr
, &pfx
);
121 if (p
&& nhrp_peer_check(p
, 1)) {
122 netlink_update_binding(p
->ifp
, &c
->remote_addr
, &p
->vc
->remote
.nbma
);
123 nhrp_route_announce(1, c
->cur
.type
, &pfx
, c
->ifp
, NULL
, c
->cur
.mtu
);
124 if (c
->cur
.type
>= NHRP_CACHE_DYNAMIC
) {
125 nhrp_route_update_nhrp(&pfx
, c
->ifp
);
126 c
->nhrp_route_installed
= 1;
127 } else if (c
->nhrp_route_installed
) {
128 nhrp_route_update_nhrp(&pfx
, NULL
);
129 c
->nhrp_route_installed
= 0;
131 if (!c
->route_installed
) {
132 notifier_call(&c
->notifier_list
, NOTIFY_CACHE_UP
);
133 c
->route_installed
= 1;
136 if (c
->nhrp_route_installed
) {
137 nhrp_route_update_nhrp(&pfx
, NULL
);
138 c
->nhrp_route_installed
= 0;
140 if (c
->route_installed
) {
141 sockunion2hostprefix(&c
->remote_addr
, &pfx
);
142 notifier_call(&c
->notifier_list
, NOTIFY_CACHE_DOWN
);
143 nhrp_route_announce(0, c
->cur
.type
, &pfx
, NULL
, NULL
, 0);
144 c
->route_installed
= 0;
149 static void nhrp_cache_peer_notifier(struct notifier_block
*n
, unsigned long cmd
)
151 struct nhrp_cache
*c
= container_of(n
, struct nhrp_cache
, peer_notifier
);
155 nhrp_cache_update_route(c
);
157 case NOTIFY_PEER_DOWN
:
158 case NOTIFY_PEER_IFCONFIG_CHANGED
:
159 notifier_call(&c
->notifier_list
, NOTIFY_CACHE_DOWN
);
160 nhrp_cache_update_binding(c
, c
->cur
.type
, -1, NULL
, 0, NULL
);
162 case NOTIFY_PEER_NBMA_CHANGING
:
163 if (c
->cur
.type
== NHRP_CACHE_DYNAMIC
)
164 c
->cur
.peer
->vc
->abort_migration
= 1;
169 static void nhrp_cache_reset_new(struct nhrp_cache
*c
)
171 THREAD_OFF(c
->t_auth
);
172 if (list_hashed(&c
->newpeer_notifier
.notifier_entry
))
173 nhrp_peer_notify_del(c
->new.peer
, &c
->newpeer_notifier
);
174 nhrp_peer_unref(c
->new.peer
);
175 memset(&c
->new, 0, sizeof(c
->new));
176 c
->new.type
= NHRP_CACHE_INVALID
;
179 static void nhrp_cache_update_timers(struct nhrp_cache
*c
)
181 THREAD_OFF(c
->t_timeout
);
183 switch (c
->cur
.type
) {
184 case NHRP_CACHE_INVALID
:
186 thread_add_timer_msec(master
, nhrp_cache_do_free
, c
,
191 thread_add_timer(master
, nhrp_cache_do_timeout
, c
,
192 c
->cur
.expires
- monotime(NULL
),
198 static void nhrp_cache_authorize_binding(struct nhrp_reqid
*r
, void *arg
)
200 struct nhrp_cache
*c
= container_of(r
, struct nhrp_cache
, eventid
);
201 char buf
[SU_ADDRSTRLEN
];
203 debugf(NHRP_DEBUG_COMMON
, "cache: %s %s: %s",
204 c
->ifp
->name
, sockunion2str(&c
->remote_addr
, buf
, sizeof buf
),
207 nhrp_reqid_free(&nhrp_event_reqid
, r
);
209 if (arg
&& strcmp(arg
, "accept") == 0) {
211 netlink_update_binding(c
->cur
.peer
->ifp
, &c
->remote_addr
, NULL
);
212 nhrp_peer_notify_del(c
->cur
.peer
, &c
->peer_notifier
);
213 nhrp_peer_unref(c
->cur
.peer
);
215 nhrp_cache_counts
[c
->cur
.type
]--;
216 nhrp_cache_counts
[c
->new.type
]++;
218 c
->cur
.peer
= nhrp_peer_ref(c
->cur
.peer
);
219 nhrp_cache_reset_new(c
);
221 nhrp_peer_notify_add(c
->cur
.peer
, &c
->peer_notifier
, nhrp_cache_peer_notifier
);
222 nhrp_cache_update_route(c
);
223 notifier_call(&c
->notifier_list
, NOTIFY_CACHE_BINDING_CHANGE
);
225 nhrp_cache_reset_new(c
);
228 nhrp_cache_update_timers(c
);
231 static int nhrp_cache_do_auth_timeout(struct thread
*t
)
233 struct nhrp_cache
*c
= THREAD_ARG(t
);
235 nhrp_cache_authorize_binding(&c
->eventid
, (void *) "timeout");
239 static void nhrp_cache_newpeer_notifier(struct notifier_block
*n
, unsigned long cmd
)
241 struct nhrp_cache
*c
= container_of(n
, struct nhrp_cache
, newpeer_notifier
);
245 if (nhrp_peer_check(c
->new.peer
, 1)) {
246 evmgr_notify("authorize-binding", c
, nhrp_cache_authorize_binding
);
247 thread_add_timer(master
, nhrp_cache_do_auth_timeout
,
251 case NOTIFY_PEER_DOWN
:
252 case NOTIFY_PEER_IFCONFIG_CHANGED
:
253 nhrp_cache_reset_new(c
);
258 int 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
)
260 if (c
->cur
.type
> type
|| c
->new.type
> type
) {
266 switch (sockunion_family(&c
->remote_addr
)) {
268 if (mtu
< 576 || mtu
>= 1500)
270 /* Opennhrp announces nbma mtu, but we use protocol mtu.
271 * This heuristic tries to fix up it. */
272 if (mtu
> 1420) mtu
= (mtu
& -16) - 80;
279 nhrp_cache_reset_new(c
);
280 if (c
->cur
.type
== type
&& c
->cur
.peer
== p
&& c
->cur
.mtu
== mtu
) {
281 if (holding_time
> 0) c
->cur
.expires
= monotime(NULL
) + holding_time
;
282 if (nbma_oa
) c
->cur
.remote_nbma_natoa
= *nbma_oa
;
283 else memset(&c
->cur
.remote_nbma_natoa
, 0, sizeof c
->cur
.remote_nbma_natoa
);
289 if (nbma_oa
) c
->new.remote_nbma_natoa
= *nbma_oa
;
291 if (holding_time
> 0)
292 c
->new.expires
= monotime(NULL
) + holding_time
;
293 else if (holding_time
< 0)
294 nhrp_cache_reset_new(c
);
296 if (c
->new.type
== NHRP_CACHE_INVALID
||
297 c
->new.type
>= NHRP_CACHE_STATIC
||
299 nhrp_cache_authorize_binding(&c
->eventid
, (void *) "accept");
301 nhrp_peer_notify_add(c
->new.peer
, &c
->newpeer_notifier
, nhrp_cache_newpeer_notifier
);
302 nhrp_cache_newpeer_notifier(&c
->newpeer_notifier
, NOTIFY_PEER_UP
);
303 thread_add_timer(master
, nhrp_cache_do_auth_timeout
,
307 nhrp_cache_update_timers(c
);
312 void nhrp_cache_set_used(struct nhrp_cache
*c
, int used
)
316 notifier_call(&c
->notifier_list
, NOTIFY_CACHE_USED
);
319 struct nhrp_cache_iterator_ctx
{
320 void (*cb
)(struct nhrp_cache
*, void *);
324 static void nhrp_cache_iterator(struct hash_backet
*b
, void *ctx
)
326 struct nhrp_cache_iterator_ctx
*ic
= ctx
;
327 ic
->cb(b
->data
, ic
->ctx
);
330 void nhrp_cache_foreach(struct interface
*ifp
, void (*cb
)(struct nhrp_cache
*, void *), void *ctx
)
332 struct nhrp_interface
*nifp
= ifp
->info
;
333 struct nhrp_cache_iterator_ctx ic
= {
338 if (nifp
->cache_hash
)
339 hash_iterate(nifp
->cache_hash
, nhrp_cache_iterator
, &ic
);
342 void nhrp_cache_notify_add(struct nhrp_cache
*c
, struct notifier_block
*n
, notifier_fn_t fn
)
344 notifier_add(n
, &c
->notifier_list
, fn
);
347 void nhrp_cache_notify_del(struct nhrp_cache
*c
, struct notifier_block
*n
)