1 /* NHRP shortcut related functions
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.
15 #include "nhrp_protocol.h"
17 static struct route_table *shortcut_rib[AFI_MAX];
19 static int nhrp_shortcut_do_purge(struct thread *t);
20 static void nhrp_shortcut_delete(struct nhrp_shortcut *s);
21 static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s);
23 static void nhrp_shortcut_check_use(struct nhrp_shortcut *s)
25 char buf[PREFIX_STRLEN];
27 if (s->expiring && s->cache && s->cache->used) {
28 debugf(NHRP_DEBUG_ROUTE, "Shortcut %s used and expiring",
29 prefix2str(s->p, buf, sizeof buf));
30 nhrp_shortcut_send_resolution_req(s);
34 static int nhrp_shortcut_do_expire(struct thread *t)
36 struct nhrp_shortcut *s = THREAD_ARG(t);
39 THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, s->holding_time/3);
41 nhrp_shortcut_check_use(s);
46 static void nhrp_shortcut_cache_notify(struct notifier_block *n, unsigned long cmd)
48 struct nhrp_shortcut *s = container_of(n, struct nhrp_shortcut, cache_notifier);
52 if (!s->route_installed) {
53 nhrp_route_announce(1, s->type, s->p, NULL, &s->cache->remote_addr, 0);
54 s->route_installed = 1;
57 case NOTIFY_CACHE_USED:
58 nhrp_shortcut_check_use(s);
60 case NOTIFY_CACHE_DOWN:
61 case NOTIFY_CACHE_DELETE:
62 if (s->route_installed) {
63 nhrp_route_announce(0, NHRP_CACHE_INVALID, s->p, NULL, NULL, 0);
64 s->route_installed = 0;
66 if (cmd == NOTIFY_CACHE_DELETE)
67 nhrp_shortcut_delete(s);
72 static void nhrp_shortcut_update_binding(struct nhrp_shortcut *s, enum nhrp_cache_type type, struct nhrp_cache *c, int holding_time)
77 nhrp_cache_notify_del(s->cache, &s->cache_notifier);
82 nhrp_cache_notify_add(s->cache, &s->cache_notifier, nhrp_shortcut_cache_notify);
83 if (s->cache->route_installed) {
84 /* Force renewal of Zebra announce on prefix change */
85 s->route_installed = 0;
86 nhrp_shortcut_cache_notify(&s->cache_notifier, NOTIFY_CACHE_UP);
89 if (!s->cache || !s->cache->route_installed)
90 nhrp_shortcut_cache_notify(&s->cache_notifier, NOTIFY_CACHE_DOWN);
92 if (s->type == NHRP_CACHE_NEGATIVE && !s->route_installed) {
93 nhrp_route_announce(1, s->type, s->p, NULL, NULL, 0);
94 s->route_installed = 1;
95 } else if (s->type == NHRP_CACHE_INVALID && s->route_installed) {
96 nhrp_route_announce(0, NHRP_CACHE_INVALID, s->p, NULL, NULL, 0);
97 s->route_installed = 0;
100 THREAD_OFF(s->t_timer);
103 s->holding_time = holding_time;
104 THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_expire, s, 2*holding_time/3);
108 static void nhrp_shortcut_delete(struct nhrp_shortcut *s)
110 struct route_node *rn;
111 afi_t afi = family2afi(PREFIX_FAMILY(s->p));
112 char buf[PREFIX_STRLEN];
114 THREAD_OFF(s->t_timer);
115 nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
117 debugf(NHRP_DEBUG_ROUTE, "Shortcut %s purged",
118 prefix2str(s->p, buf, sizeof buf));
120 nhrp_shortcut_update_binding(s, NHRP_CACHE_INVALID, NULL, 0);
123 rn = route_node_lookup(shortcut_rib[afi], s->p);
125 XFREE(MTYPE_NHRP_SHORTCUT, rn->info);
127 route_unlock_node(rn);
128 route_unlock_node(rn);
132 static int nhrp_shortcut_do_purge(struct thread *t)
134 struct nhrp_shortcut *s = THREAD_ARG(t);
136 nhrp_shortcut_delete(s);
140 static struct nhrp_shortcut *nhrp_shortcut_get(struct prefix *p)
142 struct nhrp_shortcut *s;
143 struct route_node *rn;
144 char buf[PREFIX_STRLEN];
145 afi_t afi = family2afi(PREFIX_FAMILY(p));
147 if (!shortcut_rib[afi])
150 rn = route_node_get(shortcut_rib[afi], p);
152 s = rn->info = XCALLOC(MTYPE_NHRP_SHORTCUT, sizeof(struct nhrp_shortcut));
153 s->type = NHRP_CACHE_INVALID;
156 debugf(NHRP_DEBUG_ROUTE, "Shortcut %s created",
157 prefix2str(s->p, buf, sizeof buf));
160 route_unlock_node(rn);
165 static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, void *arg)
167 struct nhrp_packet_parser *pp = arg;
168 struct nhrp_shortcut *s = container_of(reqid, struct nhrp_shortcut, reqid);
169 struct nhrp_shortcut *ps;
170 struct nhrp_extension_header *ext;
171 struct nhrp_cie_header *cie;
172 struct nhrp_cache *c = NULL;
173 union sockunion *proto, cie_proto, *nbma, *nbma_natoa, cie_nbma, nat_nbma;
174 struct prefix prefix, route_prefix;
176 char bufp[PREFIX_STRLEN], buf[3][SU_ADDRSTRLEN];
177 int holding_time = pp->if_ad->holdtime;
179 nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
180 THREAD_OFF(s->t_timer);
181 THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 1);
183 if (pp->hdr->type != NHRP_PACKET_RESOLUTION_REPLY) {
184 if (pp->hdr->type == NHRP_PACKET_ERROR_INDICATION &&
185 pp->hdr->u.error.code == NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE) {
186 debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution: Protocol address unreachable");
187 nhrp_shortcut_update_binding(s, NHRP_CACHE_NEGATIVE, NULL, holding_time);
189 debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution failed");
194 /* Parse extensions */
195 memset(&nat_nbma, 0, sizeof nat_nbma);
196 while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) {
197 switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
198 case NHRP_EXTENSION_NAT_ADDRESS:
199 nhrp_cie_pull(&extpl, pp->hdr, &nat_nbma, &cie_proto);
204 /* Minor sanity check */
205 prefix2sockunion(s->p, &cie_proto);
206 if (!sockunion_same(&cie_proto, &pp->dst_proto)) {
207 debugf(NHRP_DEBUG_COMMON, "Shortcut: Warning dst_proto altered from %s to %s",
208 sockunion2str(&cie_proto, buf[0], sizeof buf[0]),
209 sockunion2str(&pp->dst_proto, buf[1], sizeof buf[1]));
212 /* One or more CIEs should be given as reply, we support only one */
213 cie = nhrp_cie_pull(&pp->payload, pp->hdr, &cie_nbma, &cie_proto);
214 if (!cie || cie->code != NHRP_CODE_SUCCESS) {
215 debugf(NHRP_DEBUG_COMMON, "Shortcut: CIE code %d", cie ? cie->code : -1);
219 proto = sockunion_family(&cie_proto) != AF_UNSPEC ? &cie_proto : &pp->dst_proto;
220 if (cie->holding_time)
221 holding_time = htons(cie->holding_time);
224 prefix.prefixlen = cie->prefix_length;
226 /* Sanity check prefix length */
227 if (prefix.prefixlen >= 8*prefix_blen(&prefix) || prefix.prefixlen == 0) {
228 prefix.prefixlen = 8*prefix_blen(&prefix);
229 } else if (nhrp_route_address(NULL, &pp->dst_proto, &route_prefix, NULL) == NHRP_ROUTE_NBMA_NEXTHOP) {
230 if (prefix.prefixlen < route_prefix.prefixlen)
231 prefix.prefixlen = route_prefix.prefixlen;
234 debugf(NHRP_DEBUG_COMMON, "Shortcut: %s is at proto %s cie-nbma %s nat-nbma %s cie-holdtime %d",
235 prefix2str(&prefix, bufp, sizeof bufp),
236 sockunion2str(proto, buf[0], sizeof buf[0]),
237 sockunion2str(&cie_nbma, buf[1], sizeof buf[1]),
238 sockunion2str(&nat_nbma, buf[2], sizeof buf[2]),
239 htons(cie->holding_time));
241 /* Update cache entry for the protocol to nbma binding */
242 if (sockunion_family(&nat_nbma) != AF_UNSPEC) {
244 nbma_natoa = &cie_nbma;
249 if (sockunion_family(nbma)) {
250 c = nhrp_cache_get(pp->ifp, proto, 1);
252 nhrp_cache_update_binding(
253 c, NHRP_CACHE_CACHED, holding_time,
254 nhrp_peer_get(pp->ifp, nbma),
255 htons(cie->mtu), nbma_natoa);
259 /* Update shortcut entry for subnet to protocol gw binding */
260 if (c && !sockunion_same(proto, &pp->dst_proto)) {
261 ps = nhrp_shortcut_get(&prefix);
264 nhrp_shortcut_update_binding(ps, NHRP_CACHE_CACHED, c, holding_time);
268 debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution reply handled");
271 static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s)
274 struct nhrp_packet_header *hdr;
275 struct interface *ifp;
276 struct nhrp_interface *nifp;
277 struct nhrp_peer *peer;
279 if (nhrp_route_address(NULL, &s->addr, NULL, &peer) != NHRP_ROUTE_NBMA_NEXTHOP)
282 if (s->type == NHRP_CACHE_INVALID || s->type == NHRP_CACHE_NEGATIVE)
283 s->type = NHRP_CACHE_INCOMPLETE;
289 zb = zbuf_alloc(1500);
290 hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REQUEST,
291 &nifp->nbma, &nifp->afi[family2afi(sockunion_family(&s->addr))].addr, &s->addr);
292 hdr->u.request_id = htonl(nhrp_reqid_alloc(&nhrp_packet_reqid, &s->reqid, nhrp_shortcut_recv_resolution_rep));
293 hdr->flags = htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER |
294 NHRP_FLAG_RESOLUTION_AUTHORATIVE |
295 NHRP_FLAG_RESOLUTION_SOURCE_STABLE);
297 /* RFC2332 - One or zero CIEs, if CIE is present contains:
298 * - Prefix length: widest acceptable prefix we accept (if U set, 0xff)
299 * - MTU: MTU of the source station
300 * - Holding Time: Max time to cache the source information
302 /* FIXME: Send holding time, and MTU */
304 nhrp_ext_request(zb, hdr, ifp);
306 /* Cisco NAT detection extension */
307 hdr->flags |= htons(NHRP_FLAG_RESOLUTION_NAT);
308 nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
310 nhrp_packet_complete(zb, hdr);
312 nhrp_peer_send(peer, zb);
313 nhrp_peer_unref(peer);
317 void nhrp_shortcut_initiate(union sockunion *addr)
320 struct nhrp_shortcut *s;
322 sockunion2hostprefix(addr, &p);
323 s = nhrp_shortcut_get(&p);
324 if (s && s->type != NHRP_CACHE_INCOMPLETE) {
326 THREAD_OFF(s->t_timer);
327 THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 30);
328 nhrp_shortcut_send_resolution_req(s);
332 void nhrp_shortcut_init(void)
334 shortcut_rib[AFI_IP] = route_table_init();
335 shortcut_rib[AFI_IP6] = route_table_init();
338 void nhrp_shortcut_terminate(void)
340 route_table_finish(shortcut_rib[AFI_IP]);
341 route_table_finish(shortcut_rib[AFI_IP6]);
344 void nhrp_shortcut_foreach(afi_t afi, void (*cb)(struct nhrp_shortcut *, void *), void *ctx)
346 struct route_table *rt = shortcut_rib[afi];
347 struct route_node *rn;
348 route_table_iter_t iter;
352 route_table_iter_init(&iter, rt);
353 while ((rn = route_table_iter_next(&iter)) != NULL) {
354 if (rn->info) cb(rn->info, ctx);
356 route_table_iter_cleanup(&iter);
360 const struct prefix *p;
364 void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force)
366 THREAD_OFF(s->t_timer);
367 nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid);
370 /* Immediate purge on route with draw or pending shortcut */
371 THREAD_TIMER_MSEC_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 5);
373 /* Soft expire - force immediate renewal, but purge
374 * in few seconds to make sure stale route is not
375 * used too long. In practice most purges are caused
376 * by hub bgp change, but target usually stays same.
377 * This allows to keep nhrp route up, and to not
378 * cause temporary rerouting via hubs causing latency
380 THREAD_TIMER_MSEC_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 3000);
382 nhrp_shortcut_check_use(s);
386 static void nhrp_shortcut_purge_prefix(struct nhrp_shortcut *s, void *ctx)
388 struct purge_ctx *pctx = ctx;
390 if (prefix_match(pctx->p, s->p))
391 nhrp_shortcut_purge(s, pctx->deleted || !s->cache);
394 void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted)
396 struct purge_ctx pctx = {
400 nhrp_shortcut_foreach(family2afi(PREFIX_FAMILY(p)), nhrp_shortcut_purge_prefix, &pctx);