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.
19 #include "nhrp_protocol.h"
21 DEFINE_MTYPE_STATIC(NHRPD
, NHRP_SHORTCUT
, "NHRP shortcut")
23 static struct route_table
*shortcut_rib
[AFI_MAX
];
25 static int nhrp_shortcut_do_purge(struct thread
*t
);
26 static void nhrp_shortcut_delete(struct nhrp_shortcut
*s
);
27 static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut
*s
);
29 static void nhrp_shortcut_check_use(struct nhrp_shortcut
*s
)
31 char buf
[PREFIX_STRLEN
];
33 if (s
->expiring
&& s
->cache
&& s
->cache
->used
) {
34 debugf(NHRP_DEBUG_ROUTE
, "Shortcut %s used and expiring",
35 prefix2str(s
->p
, buf
, sizeof(buf
)));
36 nhrp_shortcut_send_resolution_req(s
);
40 static int nhrp_shortcut_do_expire(struct thread
*t
)
42 struct nhrp_shortcut
*s
= THREAD_ARG(t
);
45 thread_add_timer(master
, nhrp_shortcut_do_purge
, s
, s
->holding_time
/ 3,
48 nhrp_shortcut_check_use(s
);
53 static void nhrp_shortcut_cache_notify(struct notifier_block
*n
,
56 char buf
[PREFIX_STRLEN
];
58 struct nhrp_shortcut
*s
=
59 container_of(n
, struct nhrp_shortcut
, cache_notifier
);
63 if (!s
->route_installed
) {
64 debugf(NHRP_DEBUG_ROUTE
,
65 "Shortcut: route install %s nh (unspec) dev %s",
66 prefix2str(s
->p
, buf
, sizeof(buf
)),
69 nhrp_route_announce(1, s
->type
, s
->p
, s
->cache
->ifp
,
71 s
->route_installed
= 1;
74 case NOTIFY_CACHE_USED
:
75 nhrp_shortcut_check_use(s
);
77 case NOTIFY_CACHE_DOWN
:
78 case NOTIFY_CACHE_DELETE
:
79 if (s
->route_installed
) {
80 nhrp_route_announce(0, NHRP_CACHE_INVALID
, s
->p
, NULL
,
82 s
->route_installed
= 0;
84 if (cmd
== NOTIFY_CACHE_DELETE
)
85 nhrp_shortcut_delete(s
);
90 static void nhrp_shortcut_update_binding(struct nhrp_shortcut
*s
,
91 enum nhrp_cache_type type
,
92 struct nhrp_cache
*c
, int holding_time
)
94 char buf
[2][PREFIX_STRLEN
];
99 nhrp_cache_notify_del(s
->cache
, &s
->cache_notifier
);
104 nhrp_cache_notify_add(s
->cache
, &s
->cache_notifier
,
105 nhrp_shortcut_cache_notify
);
106 if (s
->cache
->route_installed
) {
107 /* Force renewal of Zebra announce on prefix
109 s
->route_installed
= 0;
110 debugf(NHRP_DEBUG_ROUTE
,
111 "Shortcut: forcing renewal of zebra announce on prefix change peer %s ht %u cur nbma %s dev %s",
112 sockunion2str(&s
->cache
->remote_addr
,
113 buf
[0], sizeof(buf
[0])),
116 &s
->cache
->cur
.remote_nbma_natoa
,
117 buf
[1], sizeof(buf
[1])),
118 s
->cache
->ifp
->name
);
119 nhrp_shortcut_cache_notify(&s
->cache_notifier
,
123 if (!s
->cache
|| !s
->cache
->route_installed
) {
124 debugf(NHRP_DEBUG_ROUTE
,
125 "Shortcut: notify cache down because cache?%s or ri?%s",
126 s
->cache
? "yes" : "no",
127 s
->cache
? (s
->cache
->route_installed
? "yes"
130 nhrp_shortcut_cache_notify(&s
->cache_notifier
,
134 if (s
->type
== NHRP_CACHE_NEGATIVE
&& !s
->route_installed
) {
135 nhrp_route_announce(1, s
->type
, s
->p
, NULL
, NULL
, 0);
136 s
->route_installed
= 1;
137 } else if (s
->type
== NHRP_CACHE_INVALID
&& s
->route_installed
) {
138 nhrp_route_announce(0, NHRP_CACHE_INVALID
, s
->p
, NULL
, NULL
, 0);
139 s
->route_installed
= 0;
142 THREAD_OFF(s
->t_timer
);
145 s
->holding_time
= holding_time
;
146 thread_add_timer(master
, nhrp_shortcut_do_expire
, s
,
147 2 * holding_time
/ 3, &s
->t_timer
);
151 static void nhrp_shortcut_delete(struct nhrp_shortcut
*s
)
153 struct route_node
*rn
;
154 afi_t afi
= family2afi(PREFIX_FAMILY(s
->p
));
155 char buf
[PREFIX_STRLEN
];
157 THREAD_OFF(s
->t_timer
);
158 nhrp_reqid_free(&nhrp_packet_reqid
, &s
->reqid
);
160 debugf(NHRP_DEBUG_ROUTE
, "Shortcut %s purged",
161 prefix2str(s
->p
, buf
, sizeof(buf
)));
163 nhrp_shortcut_update_binding(s
, NHRP_CACHE_INVALID
, NULL
, 0);
166 rn
= route_node_lookup(shortcut_rib
[afi
], s
->p
);
168 XFREE(MTYPE_NHRP_SHORTCUT
, rn
->info
);
170 route_unlock_node(rn
);
171 route_unlock_node(rn
);
175 static int nhrp_shortcut_do_purge(struct thread
*t
)
177 struct nhrp_shortcut
*s
= THREAD_ARG(t
);
179 nhrp_shortcut_delete(s
);
183 static struct nhrp_shortcut
*nhrp_shortcut_get(struct prefix
*p
)
185 struct nhrp_shortcut
*s
;
186 struct route_node
*rn
;
187 char buf
[PREFIX_STRLEN
];
188 afi_t afi
= family2afi(PREFIX_FAMILY(p
));
190 if (!shortcut_rib
[afi
])
193 rn
= route_node_get(shortcut_rib
[afi
], p
);
195 s
= rn
->info
= XCALLOC(MTYPE_NHRP_SHORTCUT
,
196 sizeof(struct nhrp_shortcut
));
197 s
->type
= NHRP_CACHE_INVALID
;
200 debugf(NHRP_DEBUG_ROUTE
, "Shortcut %s created",
201 prefix2str(s
->p
, buf
, sizeof(buf
)));
204 route_unlock_node(rn
);
209 static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid
*reqid
,
212 struct nhrp_packet_parser
*pp
= arg
;
213 struct nhrp_shortcut
*s
=
214 container_of(reqid
, struct nhrp_shortcut
, reqid
);
215 struct nhrp_shortcut
*ps
;
216 struct nhrp_extension_header
*ext
;
217 struct nhrp_cie_header
*cie
;
218 struct nhrp_cache
*c
= NULL
;
219 union sockunion
*proto
, cie_proto
, *nbma
, cie_nbma
, nat_nbma
;
220 struct prefix prefix
, route_prefix
;
222 char bufp
[PREFIX_STRLEN
], buf
[4][SU_ADDRSTRLEN
];
223 int holding_time
= pp
->if_ad
->holdtime
;
225 nhrp_reqid_free(&nhrp_packet_reqid
, &s
->reqid
);
226 THREAD_OFF(s
->t_timer
);
227 thread_add_timer(master
, nhrp_shortcut_do_purge
, s
, 1, &s
->t_timer
);
229 if (pp
->hdr
->type
!= NHRP_PACKET_RESOLUTION_REPLY
) {
230 if (pp
->hdr
->type
== NHRP_PACKET_ERROR_INDICATION
231 && pp
->hdr
->u
.error
.code
232 == NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE
) {
233 debugf(NHRP_DEBUG_COMMON
,
234 "Shortcut: Resolution: Protocol address unreachable");
235 nhrp_shortcut_update_binding(s
, NHRP_CACHE_NEGATIVE
,
238 debugf(NHRP_DEBUG_COMMON
,
239 "Shortcut: Resolution failed");
244 /* Parse extensions */
245 memset(&nat_nbma
, 0, sizeof(nat_nbma
));
246 while ((ext
= nhrp_ext_pull(&pp
->extensions
, &extpl
)) != NULL
) {
247 switch (htons(ext
->type
) & ~NHRP_EXTENSION_FLAG_COMPULSORY
) {
248 case NHRP_EXTENSION_NAT_ADDRESS
:
249 nhrp_cie_pull(&extpl
, pp
->hdr
, &nat_nbma
, &cie_proto
);
254 /* Minor sanity check */
255 prefix2sockunion(s
->p
, &cie_proto
);
256 if (!sockunion_same(&cie_proto
, &pp
->dst_proto
)) {
257 debugf(NHRP_DEBUG_COMMON
,
258 "Shortcut: Warning dst_proto altered from %s to %s",
259 sockunion2str(&cie_proto
, buf
[0], sizeof(buf
[0])),
260 sockunion2str(&pp
->dst_proto
, buf
[1], sizeof(buf
[1])));
263 /* One or more CIEs should be given as reply, we support only one */
264 cie
= nhrp_cie_pull(&pp
->payload
, pp
->hdr
, &cie_nbma
, &cie_proto
);
265 if (!cie
|| cie
->code
!= NHRP_CODE_SUCCESS
) {
266 debugf(NHRP_DEBUG_COMMON
, "Shortcut: CIE code %d",
267 cie
? cie
->code
: -1);
271 proto
= sockunion_family(&cie_proto
) != AF_UNSPEC
? &cie_proto
273 if (cie
->holding_time
)
274 holding_time
= htons(cie
->holding_time
);
277 prefix
.prefixlen
= cie
->prefix_length
;
279 /* Sanity check prefix length */
280 if (prefix
.prefixlen
>= 8 * prefix_blen(&prefix
)
281 || prefix
.prefixlen
== 0) {
282 prefix
.prefixlen
= 8 * prefix_blen(&prefix
);
283 } else if (nhrp_route_address(NULL
, &pp
->dst_proto
, &route_prefix
, NULL
)
284 == NHRP_ROUTE_NBMA_NEXTHOP
) {
285 if (prefix
.prefixlen
< route_prefix
.prefixlen
)
286 prefix
.prefixlen
= route_prefix
.prefixlen
;
289 debugf(NHRP_DEBUG_COMMON
,
290 "Shortcut: %s is at proto %s dst_proto %s cie-nbma %s nat-nbma %s cie-holdtime %d",
291 prefix2str(&prefix
, bufp
, sizeof(bufp
)),
292 sockunion2str(proto
, buf
[0], sizeof(buf
[0])),
293 sockunion2str(&pp
->dst_proto
, buf
[1], sizeof(buf
[1])),
294 sockunion2str(&cie_nbma
, buf
[2], sizeof(buf
[2])),
295 sockunion2str(&nat_nbma
, buf
[3], sizeof(buf
[3])),
296 htons(cie
->holding_time
));
298 /* Update cache entry for the protocol to nbma binding */
299 if (sockunion_family(&nat_nbma
) != AF_UNSPEC
)
304 if (sockunion_family(nbma
)) {
305 c
= nhrp_cache_get(pp
->ifp
, proto
, 1);
307 debugf(NHRP_DEBUG_COMMON
,
308 "Shortcut: cache found, update binding");
309 nhrp_cache_update_binding(c
, NHRP_CACHE_DYNAMIC
,
311 nhrp_peer_get(pp
->ifp
, nbma
),
312 htons(cie
->mtu
), nbma
);
314 debugf(NHRP_DEBUG_COMMON
,
315 "Shortcut: no cache for nbma %s", buf
[2]);
319 /* Update shortcut entry for subnet to protocol gw binding */
321 ps
= nhrp_shortcut_get(&prefix
);
324 debugf(NHRP_DEBUG_COMMON
,
325 "Shortcut: calling update_binding");
326 nhrp_shortcut_update_binding(ps
, NHRP_CACHE_DYNAMIC
, c
,
329 debugf(NHRP_DEBUG_COMMON
,
330 "Shortcut: proto diff but no ps");
333 debugf(NHRP_DEBUG_COMMON
,
334 "NO Shortcut because c NULL?%s or same proto?%s",
336 proto
&& pp
&& sockunion_same(proto
, &pp
->dst_proto
)
341 debugf(NHRP_DEBUG_COMMON
, "Shortcut: Resolution reply handled");
344 static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut
*s
)
347 struct nhrp_packet_header
*hdr
;
348 struct interface
*ifp
;
349 struct nhrp_interface
*nifp
;
350 struct nhrp_afi_data
*if_ad
;
351 struct nhrp_peer
*peer
;
352 struct nhrp_cie_header
*cie
;
354 if (nhrp_route_address(NULL
, &s
->addr
, NULL
, &peer
)
355 != NHRP_ROUTE_NBMA_NEXTHOP
)
358 if (s
->type
== NHRP_CACHE_INVALID
|| s
->type
== NHRP_CACHE_NEGATIVE
)
359 s
->type
= NHRP_CACHE_INCOMPLETE
;
365 zb
= zbuf_alloc(1500);
366 hdr
= nhrp_packet_push(
367 zb
, NHRP_PACKET_RESOLUTION_REQUEST
, &nifp
->nbma
,
368 &nifp
->afi
[family2afi(sockunion_family(&s
->addr
))].addr
,
371 htonl(nhrp_reqid_alloc(&nhrp_packet_reqid
, &s
->reqid
,
372 nhrp_shortcut_recv_resolution_rep
));
373 hdr
->flags
= htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER
374 | NHRP_FLAG_RESOLUTION_AUTHORATIVE
375 | NHRP_FLAG_RESOLUTION_SOURCE_STABLE
);
377 /* RFC2332 - One or zero CIEs, if CIE is present contains:
378 * - Prefix length: widest acceptable prefix we accept (if U set, 0xff)
379 * - MTU: MTU of the source station
380 * - Holding Time: Max time to cache the source information
382 /* FIXME: push CIE for each local protocol address */
383 cie
= nhrp_cie_push(zb
, NHRP_CODE_SUCCESS
, NULL
, NULL
);
384 cie
->prefix_length
= 0xff;
385 if_ad
= &nifp
->afi
[family2afi(sockunion_family(&s
->addr
))];
386 cie
->holding_time
= htons(if_ad
->holdtime
);
387 cie
->mtu
= htons(if_ad
->mtu
);
388 debugf(NHRP_DEBUG_COMMON
,
389 "Shortcut res_req: set cie ht to %u and mtu to %u. shortcut ht is %u",
390 ntohs(cie
->holding_time
), ntohs(cie
->mtu
), s
->holding_time
);
392 nhrp_ext_request(zb
, hdr
, ifp
);
394 /* Cisco NAT detection extension */
395 hdr
->flags
|= htons(NHRP_FLAG_RESOLUTION_NAT
);
396 nhrp_ext_push(zb
, hdr
, NHRP_EXTENSION_NAT_ADDRESS
);
398 nhrp_packet_complete(zb
, hdr
);
400 nhrp_peer_send(peer
, zb
);
401 nhrp_peer_unref(peer
);
405 void nhrp_shortcut_initiate(union sockunion
*addr
)
408 struct nhrp_shortcut
*s
;
410 sockunion2hostprefix(addr
, &p
);
411 s
= nhrp_shortcut_get(&p
);
412 if (s
&& s
->type
!= NHRP_CACHE_INCOMPLETE
) {
414 THREAD_OFF(s
->t_timer
);
415 thread_add_timer(master
, nhrp_shortcut_do_purge
, s
, 30,
417 nhrp_shortcut_send_resolution_req(s
);
421 void nhrp_shortcut_init(void)
423 shortcut_rib
[AFI_IP
] = route_table_init();
424 shortcut_rib
[AFI_IP6
] = route_table_init();
427 void nhrp_shortcut_terminate(void)
429 route_table_finish(shortcut_rib
[AFI_IP
]);
430 route_table_finish(shortcut_rib
[AFI_IP6
]);
433 void nhrp_shortcut_foreach(afi_t afi
,
434 void (*cb
)(struct nhrp_shortcut
*, void *),
437 struct route_table
*rt
= shortcut_rib
[afi
];
438 struct route_node
*rn
;
439 route_table_iter_t iter
;
444 route_table_iter_init(&iter
, rt
);
445 while ((rn
= route_table_iter_next(&iter
)) != NULL
) {
449 route_table_iter_cleanup(&iter
);
453 const struct prefix
*p
;
457 void nhrp_shortcut_purge(struct nhrp_shortcut
*s
, int force
)
459 THREAD_OFF(s
->t_timer
);
460 nhrp_reqid_free(&nhrp_packet_reqid
, &s
->reqid
);
463 /* Immediate purge on route with draw or pending shortcut */
464 thread_add_timer_msec(master
, nhrp_shortcut_do_purge
, s
, 5,
467 /* Soft expire - force immediate renewal, but purge
468 * in few seconds to make sure stale route is not
469 * used too long. In practice most purges are caused
470 * by hub bgp change, but target usually stays same.
471 * This allows to keep nhrp route up, and to not
472 * cause temporary rerouting via hubs causing latency
474 thread_add_timer_msec(master
, nhrp_shortcut_do_purge
, s
, 3000,
477 nhrp_shortcut_check_use(s
);
481 static void nhrp_shortcut_purge_prefix(struct nhrp_shortcut
*s
, void *ctx
)
483 struct purge_ctx
*pctx
= ctx
;
485 if (prefix_match(pctx
->p
, s
->p
))
486 nhrp_shortcut_purge(s
, pctx
->deleted
|| !s
->cache
);
489 void nhrp_shortcut_prefix_change(const struct prefix
*p
, int deleted
)
491 struct purge_ctx pctx
= {
492 .p
= p
, .deleted
= deleted
,
494 nhrp_shortcut_foreach(family2afi(PREFIX_FAMILY(p
)),
495 nhrp_shortcut_purge_prefix
, &pctx
);