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 DEFINE_MTYPE_STATIC(NHRPD
, NHRP_SHORTCUT
, "NHRP shortcut")
19 static struct route_table
*shortcut_rib
[AFI_MAX
];
21 static int nhrp_shortcut_do_purge(struct thread
*t
);
22 static void nhrp_shortcut_delete(struct nhrp_shortcut
*s
);
23 static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut
*s
);
25 static void nhrp_shortcut_check_use(struct nhrp_shortcut
*s
)
27 char buf
[PREFIX_STRLEN
];
29 if (s
->expiring
&& s
->cache
&& s
->cache
->used
) {
30 debugf(NHRP_DEBUG_ROUTE
, "Shortcut %s used and expiring",
31 prefix2str(s
->p
, buf
, sizeof buf
));
32 nhrp_shortcut_send_resolution_req(s
);
36 static int nhrp_shortcut_do_expire(struct thread
*t
)
38 struct nhrp_shortcut
*s
= THREAD_ARG(t
);
41 THREAD_TIMER_ON(master
, s
->t_timer
, nhrp_shortcut_do_purge
, s
, s
->holding_time
/3);
43 nhrp_shortcut_check_use(s
);
48 static void nhrp_shortcut_cache_notify(struct notifier_block
*n
, unsigned long cmd
)
50 struct nhrp_shortcut
*s
= container_of(n
, struct nhrp_shortcut
, cache_notifier
);
54 if (!s
->route_installed
) {
55 nhrp_route_announce(1, s
->type
, s
->p
, NULL
, &s
->cache
->remote_addr
, 0);
56 s
->route_installed
= 1;
59 case NOTIFY_CACHE_USED
:
60 nhrp_shortcut_check_use(s
);
62 case NOTIFY_CACHE_DOWN
:
63 case NOTIFY_CACHE_DELETE
:
64 if (s
->route_installed
) {
65 nhrp_route_announce(0, NHRP_CACHE_INVALID
, s
->p
, NULL
, NULL
, 0);
66 s
->route_installed
= 0;
68 if (cmd
== NOTIFY_CACHE_DELETE
)
69 nhrp_shortcut_delete(s
);
74 static void nhrp_shortcut_update_binding(struct nhrp_shortcut
*s
, enum nhrp_cache_type type
, struct nhrp_cache
*c
, int holding_time
)
79 nhrp_cache_notify_del(s
->cache
, &s
->cache_notifier
);
84 nhrp_cache_notify_add(s
->cache
, &s
->cache_notifier
, nhrp_shortcut_cache_notify
);
85 if (s
->cache
->route_installed
) {
86 /* Force renewal of Zebra announce on prefix change */
87 s
->route_installed
= 0;
88 nhrp_shortcut_cache_notify(&s
->cache_notifier
, NOTIFY_CACHE_UP
);
91 if (!s
->cache
|| !s
->cache
->route_installed
)
92 nhrp_shortcut_cache_notify(&s
->cache_notifier
, NOTIFY_CACHE_DOWN
);
94 if (s
->type
== NHRP_CACHE_NEGATIVE
&& !s
->route_installed
) {
95 nhrp_route_announce(1, s
->type
, s
->p
, NULL
, NULL
, 0);
96 s
->route_installed
= 1;
97 } else if (s
->type
== NHRP_CACHE_INVALID
&& s
->route_installed
) {
98 nhrp_route_announce(0, NHRP_CACHE_INVALID
, s
->p
, NULL
, NULL
, 0);
99 s
->route_installed
= 0;
102 THREAD_OFF(s
->t_timer
);
105 s
->holding_time
= holding_time
;
106 THREAD_TIMER_ON(master
, s
->t_timer
, nhrp_shortcut_do_expire
, s
, 2*holding_time
/3);
110 static void nhrp_shortcut_delete(struct nhrp_shortcut
*s
)
112 struct route_node
*rn
;
113 afi_t afi
= family2afi(PREFIX_FAMILY(s
->p
));
114 char buf
[PREFIX_STRLEN
];
116 THREAD_OFF(s
->t_timer
);
117 nhrp_reqid_free(&nhrp_packet_reqid
, &s
->reqid
);
119 debugf(NHRP_DEBUG_ROUTE
, "Shortcut %s purged",
120 prefix2str(s
->p
, buf
, sizeof buf
));
122 nhrp_shortcut_update_binding(s
, NHRP_CACHE_INVALID
, NULL
, 0);
125 rn
= route_node_lookup(shortcut_rib
[afi
], s
->p
);
127 XFREE(MTYPE_NHRP_SHORTCUT
, rn
->info
);
129 route_unlock_node(rn
);
130 route_unlock_node(rn
);
134 static int nhrp_shortcut_do_purge(struct thread
*t
)
136 struct nhrp_shortcut
*s
= THREAD_ARG(t
);
138 nhrp_shortcut_delete(s
);
142 static struct nhrp_shortcut
*nhrp_shortcut_get(struct prefix
*p
)
144 struct nhrp_shortcut
*s
;
145 struct route_node
*rn
;
146 char buf
[PREFIX_STRLEN
];
147 afi_t afi
= family2afi(PREFIX_FAMILY(p
));
149 if (!shortcut_rib
[afi
])
152 rn
= route_node_get(shortcut_rib
[afi
], p
);
154 s
= rn
->info
= XCALLOC(MTYPE_NHRP_SHORTCUT
, sizeof(struct nhrp_shortcut
));
155 s
->type
= NHRP_CACHE_INVALID
;
158 debugf(NHRP_DEBUG_ROUTE
, "Shortcut %s created",
159 prefix2str(s
->p
, buf
, sizeof buf
));
162 route_unlock_node(rn
);
167 static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid
*reqid
, void *arg
)
169 struct nhrp_packet_parser
*pp
= arg
;
170 struct nhrp_shortcut
*s
= container_of(reqid
, struct nhrp_shortcut
, reqid
);
171 struct nhrp_shortcut
*ps
;
172 struct nhrp_extension_header
*ext
;
173 struct nhrp_cie_header
*cie
;
174 struct nhrp_cache
*c
= NULL
;
175 union sockunion
*proto
, cie_proto
, *nbma
, *nbma_natoa
, cie_nbma
, nat_nbma
;
176 struct prefix prefix
, route_prefix
;
178 char bufp
[PREFIX_STRLEN
], buf
[3][SU_ADDRSTRLEN
];
179 int holding_time
= pp
->if_ad
->holdtime
;
181 nhrp_reqid_free(&nhrp_packet_reqid
, &s
->reqid
);
182 THREAD_OFF(s
->t_timer
);
183 THREAD_TIMER_ON(master
, s
->t_timer
, nhrp_shortcut_do_purge
, s
, 1);
185 if (pp
->hdr
->type
!= NHRP_PACKET_RESOLUTION_REPLY
) {
186 if (pp
->hdr
->type
== NHRP_PACKET_ERROR_INDICATION
&&
187 pp
->hdr
->u
.error
.code
== NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE
) {
188 debugf(NHRP_DEBUG_COMMON
, "Shortcut: Resolution: Protocol address unreachable");
189 nhrp_shortcut_update_binding(s
, NHRP_CACHE_NEGATIVE
, NULL
, holding_time
);
191 debugf(NHRP_DEBUG_COMMON
, "Shortcut: Resolution failed");
196 /* Parse extensions */
197 memset(&nat_nbma
, 0, sizeof nat_nbma
);
198 while ((ext
= nhrp_ext_pull(&pp
->extensions
, &extpl
)) != NULL
) {
199 switch (htons(ext
->type
) & ~NHRP_EXTENSION_FLAG_COMPULSORY
) {
200 case NHRP_EXTENSION_NAT_ADDRESS
:
201 nhrp_cie_pull(&extpl
, pp
->hdr
, &nat_nbma
, &cie_proto
);
206 /* Minor sanity check */
207 prefix2sockunion(s
->p
, &cie_proto
);
208 if (!sockunion_same(&cie_proto
, &pp
->dst_proto
)) {
209 debugf(NHRP_DEBUG_COMMON
, "Shortcut: Warning dst_proto altered from %s to %s",
210 sockunion2str(&cie_proto
, buf
[0], sizeof buf
[0]),
211 sockunion2str(&pp
->dst_proto
, buf
[1], sizeof buf
[1]));
214 /* One or more CIEs should be given as reply, we support only one */
215 cie
= nhrp_cie_pull(&pp
->payload
, pp
->hdr
, &cie_nbma
, &cie_proto
);
216 if (!cie
|| cie
->code
!= NHRP_CODE_SUCCESS
) {
217 debugf(NHRP_DEBUG_COMMON
, "Shortcut: CIE code %d", cie
? cie
->code
: -1);
221 proto
= sockunion_family(&cie_proto
) != AF_UNSPEC
? &cie_proto
: &pp
->dst_proto
;
222 if (cie
->holding_time
)
223 holding_time
= htons(cie
->holding_time
);
226 prefix
.prefixlen
= cie
->prefix_length
;
228 /* Sanity check prefix length */
229 if (prefix
.prefixlen
>= 8*prefix_blen(&prefix
)) {
230 prefix
.prefixlen
= 8*prefix_blen(&prefix
);
231 } else if (nhrp_route_address(NULL
, &pp
->dst_proto
, &route_prefix
, NULL
) == NHRP_ROUTE_NBMA_NEXTHOP
) {
232 if (prefix
.prefixlen
< route_prefix
.prefixlen
)
233 prefix
.prefixlen
= route_prefix
.prefixlen
;
236 debugf(NHRP_DEBUG_COMMON
, "Shortcut: %s is at proto %s cie-nbma %s nat-nbma %s cie-holdtime %d",
237 prefix2str(&prefix
, bufp
, sizeof bufp
),
238 sockunion2str(proto
, buf
[0], sizeof buf
[0]),
239 sockunion2str(&cie_nbma
, buf
[1], sizeof buf
[1]),
240 sockunion2str(&nat_nbma
, buf
[2], sizeof buf
[2]),
241 htons(cie
->holding_time
));
243 /* Update cache entry for the protocol to nbma binding */
244 if (sockunion_family(&nat_nbma
) != AF_UNSPEC
) {
246 nbma_natoa
= &cie_nbma
;
251 if (sockunion_family(nbma
)) {
252 c
= nhrp_cache_get(pp
->ifp
, proto
, 1);
254 nhrp_cache_update_binding(
255 c
, NHRP_CACHE_CACHED
, holding_time
,
256 nhrp_peer_get(pp
->ifp
, nbma
),
257 htons(cie
->mtu
), nbma_natoa
);
261 /* Update shortcut entry for subnet to protocol gw binding */
262 if (c
&& !sockunion_same(proto
, &pp
->dst_proto
)) {
263 ps
= nhrp_shortcut_get(&prefix
);
266 nhrp_shortcut_update_binding(ps
, NHRP_CACHE_CACHED
, c
, holding_time
);
270 debugf(NHRP_DEBUG_COMMON
, "Shortcut: Resolution reply handled");
273 static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut
*s
)
276 struct nhrp_packet_header
*hdr
;
277 struct interface
*ifp
;
278 struct nhrp_interface
*nifp
;
279 struct nhrp_peer
*peer
;
281 if (nhrp_route_address(NULL
, &s
->addr
, NULL
, &peer
) != NHRP_ROUTE_NBMA_NEXTHOP
)
284 if (s
->type
== NHRP_CACHE_INVALID
|| s
->type
== NHRP_CACHE_NEGATIVE
)
285 s
->type
= NHRP_CACHE_INCOMPLETE
;
291 zb
= zbuf_alloc(1500);
292 hdr
= nhrp_packet_push(zb
, NHRP_PACKET_RESOLUTION_REQUEST
,
293 &nifp
->nbma
, &nifp
->afi
[family2afi(sockunion_family(&s
->addr
))].addr
, &s
->addr
);
294 hdr
->u
.request_id
= htonl(nhrp_reqid_alloc(&nhrp_packet_reqid
, &s
->reqid
, nhrp_shortcut_recv_resolution_rep
));
295 hdr
->flags
= htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER
|
296 NHRP_FLAG_RESOLUTION_AUTHORATIVE
|
297 NHRP_FLAG_RESOLUTION_SOURCE_STABLE
);
299 /* RFC2332 - One or zero CIEs, if CIE is present contains:
300 * - Prefix length: widest acceptable prefix we accept (if U set, 0xff)
301 * - MTU: MTU of the source station
302 * - Holding Time: Max time to cache the source information
304 /* FIXME: Send holding time, and MTU */
306 nhrp_ext_request(zb
, hdr
, ifp
);
308 /* Cisco NAT detection extension */
309 hdr
->flags
|= htons(NHRP_FLAG_RESOLUTION_NAT
);
310 nhrp_ext_push(zb
, hdr
, NHRP_EXTENSION_NAT_ADDRESS
);
312 nhrp_packet_complete(zb
, hdr
);
314 nhrp_peer_send(peer
, zb
);
315 nhrp_peer_unref(peer
);
319 void nhrp_shortcut_initiate(union sockunion
*addr
)
322 struct nhrp_shortcut
*s
;
324 sockunion2hostprefix(addr
, &p
);
325 s
= nhrp_shortcut_get(&p
);
326 if (s
&& s
->type
!= NHRP_CACHE_INCOMPLETE
) {
328 THREAD_OFF(s
->t_timer
);
329 THREAD_TIMER_ON(master
, s
->t_timer
, nhrp_shortcut_do_purge
, s
, 30);
330 nhrp_shortcut_send_resolution_req(s
);
334 void nhrp_shortcut_init(void)
336 shortcut_rib
[AFI_IP
] = route_table_init();
337 shortcut_rib
[AFI_IP6
] = route_table_init();
340 void nhrp_shortcut_terminate(void)
342 route_table_finish(shortcut_rib
[AFI_IP
]);
343 route_table_finish(shortcut_rib
[AFI_IP6
]);
346 void nhrp_shortcut_foreach(afi_t afi
, void (*cb
)(struct nhrp_shortcut
*, void *), void *ctx
)
348 struct route_table
*rt
= shortcut_rib
[afi
];
349 struct route_node
*rn
;
350 route_table_iter_t iter
;
354 route_table_iter_init(&iter
, rt
);
355 while ((rn
= route_table_iter_next(&iter
)) != NULL
) {
356 if (rn
->info
) cb(rn
->info
, ctx
);
358 route_table_iter_cleanup(&iter
);
362 const struct prefix
*p
;
366 void nhrp_shortcut_purge(struct nhrp_shortcut
*s
, int force
)
368 THREAD_OFF(s
->t_timer
);
369 nhrp_reqid_free(&nhrp_packet_reqid
, &s
->reqid
);
372 /* Immediate purge on route with draw or pending shortcut */
373 THREAD_TIMER_MSEC_ON(master
, s
->t_timer
, nhrp_shortcut_do_purge
, s
, 5);
375 /* Soft expire - force immediate renewal, but purge
376 * in few seconds to make sure stale route is not
377 * used too long. In practice most purges are caused
378 * by hub bgp change, but target usually stays same.
379 * This allows to keep nhrp route up, and to not
380 * cause temporary rerouting via hubs causing latency
382 THREAD_TIMER_MSEC_ON(master
, s
->t_timer
, nhrp_shortcut_do_purge
, s
, 3000);
384 nhrp_shortcut_check_use(s
);
388 static void nhrp_shortcut_purge_prefix(struct nhrp_shortcut
*s
, void *ctx
)
390 struct purge_ctx
*pctx
= ctx
;
392 if (prefix_match(pctx
->p
, s
->p
))
393 nhrp_shortcut_purge(s
, pctx
->deleted
|| !s
->cache
);
396 void nhrp_shortcut_prefix_change(const struct prefix
*p
, int deleted
)
398 struct purge_ctx pctx
= {
402 nhrp_shortcut_foreach(family2afi(PREFIX_FAMILY(p
)), nhrp_shortcut_purge_prefix
, &pctx
);