2 * Label Manager for FRR
4 * Copyright (C) 2017 by Bingen Eguzkitza,
7 * This file is part of FRRouting (FRR)
9 * FRR is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2, or (at your option) any
14 * FRR is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; see the file COPYING; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 #include <sys/types.h>
30 #include "lib/memory.h"
32 #include "lib/network.h"
33 #include "lib/stream.h"
34 #include "lib/zclient.h"
35 #include "lib/libfrr.h"
37 //#include "zebra/zserv.h"
38 #include "zebra/zebra_router.h"
39 #include "zebra/label_manager.h"
40 #include "zebra/zebra_errors.h"
41 #include "zebra/zapi_msg.h"
42 #include "zebra/debug.h"
44 #define CONNECTION_DELAY 5
46 struct label_manager lbl_mgr
;
48 DEFINE_MGROUP(LBL_MGR
, "Label Manager");
49 DEFINE_MTYPE_STATIC(LBL_MGR
, LM_CHUNK
, "Label Manager Chunk");
51 /* define hooks for the basic API, so that it can be specialized or served
55 DEFINE_HOOK(lm_client_connect
,
56 (uint8_t proto
, uint16_t instance
, vrf_id_t vrf_id
),
57 (proto
, instance
, vrf_id
));
58 DEFINE_HOOK(lm_client_disconnect
, (uint8_t proto
, uint16_t instance
),
60 DEFINE_HOOK(lm_get_chunk
,
61 (struct label_manager_chunk
* *lmc
, uint8_t proto
,
62 uint16_t instance
, uint8_t keep
, uint32_t size
, uint32_t base
,
64 (lmc
, proto
, instance
, keep
, size
, base
, vrf_id
));
65 DEFINE_HOOK(lm_release_chunk
,
66 (uint8_t proto
, uint16_t instance
, uint32_t start
, uint32_t end
),
67 (proto
, instance
, start
, end
));
68 DEFINE_HOOK(lm_cbs_inited
, (), ());
70 /* define wrappers to be called in zapi_msg.c (as hooks must be called in
71 * source file where they were defined)
73 void lm_client_connect_call(uint8_t proto
, uint16_t instance
, vrf_id_t vrf_id
)
75 hook_call(lm_client_connect
, proto
, instance
, vrf_id
);
77 void lm_get_chunk_call(struct label_manager_chunk
**lmc
, uint8_t proto
,
78 uint16_t instance
, uint8_t keep
, uint32_t size
,
79 uint32_t base
, vrf_id_t vrf_id
)
81 hook_call(lm_get_chunk
, lmc
, proto
, instance
, keep
, size
, base
, vrf_id
);
83 void lm_release_chunk_call(uint8_t proto
, uint16_t instance
, uint32_t start
,
86 hook_call(lm_release_chunk
, proto
, instance
, start
, end
);
89 /* forward declarations of the static functions to be used for some hooks */
90 static int label_manager_connect(uint8_t proto
, uint16_t instance
,
92 static int label_manager_disconnect(uint8_t proto
, uint16_t instance
);
93 static int label_manager_get_chunk(struct label_manager_chunk
**lmc
,
94 uint8_t proto
, uint16_t instance
,
95 uint8_t keep
, uint32_t size
, uint32_t base
,
98 void delete_label_chunk(void *val
)
100 XFREE(MTYPE_LM_CHUNK
, val
);
104 * Release label chunks from a client.
106 * Called on client disconnection or reconnection. It only releases chunks
107 * with empty keep value.
109 * @param proto Daemon protocol of client, to identify the owner
110 * @param instance Instance, to identify the owner
111 * @return Number of chunks released
113 int release_daemon_label_chunks(uint8_t proto
, unsigned short instance
)
115 struct listnode
*node
;
116 struct label_manager_chunk
*lmc
;
120 if (IS_ZEBRA_DEBUG_PACKET
)
121 zlog_debug("%s: Releasing chunks for client proto %s, instance %d",
122 __func__
, zebra_route_string(proto
), instance
);
124 for (ALL_LIST_ELEMENTS_RO(lbl_mgr
.lc_list
, node
, lmc
)) {
125 if (lmc
->proto
== proto
&& lmc
->instance
== instance
127 ret
= release_label_chunk(lmc
->proto
, lmc
->instance
,
128 lmc
->start
, lmc
->end
);
134 if (IS_ZEBRA_DEBUG_PACKET
)
135 zlog_debug("%s: Released %d label chunks", __func__
, count
);
140 int lm_client_disconnect_cb(struct zserv
*client
)
142 uint8_t proto
= client
->proto
;
143 uint16_t instance
= client
->instance
;
145 hook_call(lm_client_disconnect
, proto
, instance
);
149 void lm_hooks_register(void)
151 hook_register(lm_client_connect
, label_manager_connect
);
152 hook_register(lm_client_disconnect
, label_manager_disconnect
);
153 hook_register(lm_get_chunk
, label_manager_get_chunk
);
154 hook_register(lm_release_chunk
, release_label_chunk
);
156 void lm_hooks_unregister(void)
158 hook_unregister(lm_client_connect
, label_manager_connect
);
159 hook_unregister(lm_client_disconnect
, label_manager_disconnect
);
160 hook_unregister(lm_get_chunk
, label_manager_get_chunk
);
161 hook_unregister(lm_release_chunk
, release_label_chunk
);
165 * Init label manager (or proxy to an external one)
167 void label_manager_init(void)
169 lbl_mgr
.lc_list
= list_new();
170 lbl_mgr
.lc_list
->del
= delete_label_chunk
;
171 hook_register(zserv_client_close
, lm_client_disconnect_cb
);
173 /* register default hooks for the label manager actions */
176 /* notify any external module that we are done */
177 hook_call(lm_cbs_inited
);
180 /* alloc and fill a label chunk */
181 struct label_manager_chunk
*create_label_chunk(uint8_t proto
,
182 unsigned short instance
,
183 uint8_t keep
, uint32_t start
,
186 /* alloc chunk, fill it and return it */
187 struct label_manager_chunk
*lmc
=
188 XCALLOC(MTYPE_LM_CHUNK
, sizeof(struct label_manager_chunk
));
193 lmc
->instance
= instance
;
199 /* attempt to get a specific label chunk */
200 static struct label_manager_chunk
*
201 assign_specific_label_chunk(uint8_t proto
, unsigned short instance
,
202 uint8_t keep
, uint32_t size
, uint32_t base
)
204 struct label_manager_chunk
*lmc
;
205 struct listnode
*node
, *next
= NULL
;
206 struct listnode
*first_node
= NULL
;
207 struct listnode
*last_node
= NULL
;
208 struct listnode
*insert_node
= NULL
;
210 /* precompute last label from base and size */
211 uint32_t end
= base
+ size
- 1;
214 if ((base
< MPLS_LABEL_UNRESERVED_MIN
)
215 || (end
> MPLS_LABEL_UNRESERVED_MAX
)) {
216 zlog_err("Invalid LM request arguments: base: %u, size: %u",
221 /* Scan the existing chunks to see if the requested range of labels
222 * falls inside any of such chunks */
223 for (ALL_LIST_ELEMENTS_RO(lbl_mgr
.lc_list
, node
, lmc
)) {
225 /* skip chunks for labels < base */
229 /* requested range is not covered by any existing, free chunk.
230 * Therefore, need to insert a chunk */
231 if ((end
< lmc
->start
) && !first_node
) {
239 /* if chunk is used, cannot honor request */
240 if (lmc
->proto
!= NO_PROTO
)
243 if (end
< lmc
->end
) {
249 /* insert chunk between existing chunks */
251 lmc
= create_label_chunk(proto
, instance
, keep
, base
, end
);
252 listnode_add_before(lbl_mgr
.lc_list
, insert_node
, lmc
);
257 /* get node past the last one, if there */
259 last_node
= listnextnode(last_node
);
261 /* delete node coming after the above chunk whose labels are
262 * included in the previous one */
263 for (node
= first_node
; node
&& (node
!= last_node
);
265 struct label_manager_chunk
*death
;
267 next
= listnextnode(node
);
268 death
= listgetdata(node
);
269 list_delete_node(lbl_mgr
.lc_list
, node
);
270 delete_label_chunk(death
);
273 lmc
= create_label_chunk(proto
, instance
, keep
, base
, end
);
275 listnode_add_before(lbl_mgr
.lc_list
, last_node
, lmc
);
277 listnode_add(lbl_mgr
.lc_list
, lmc
);
281 /* create a new chunk past all the existing ones and link at
283 lmc
= create_label_chunk(proto
, instance
, keep
, base
, end
);
284 listnode_add(lbl_mgr
.lc_list
, lmc
);
290 * Core function, assigns label chunks
292 * It first searches through the list to check if there's one available
293 * (previously released). Otherwise it creates and assigns a new one
295 * @param proto Daemon protocol of client, to identify the owner
296 * @param instance Instance, to identify the owner
297 * @param keep If set, avoid garbage collection
298 * @param size Size of the label chunk
299 * @param base Desired starting label of the chunk; if MPLS_LABEL_BASE_ANY it does not apply
300 * @return Pointer to the assigned label chunk, or NULL if the request could not be satisfied
302 struct label_manager_chunk
*assign_label_chunk(uint8_t proto
,
303 unsigned short instance
,
304 uint8_t keep
, uint32_t size
,
307 struct label_manager_chunk
*lmc
;
308 struct listnode
*node
;
309 uint32_t prev_end
= 0;
311 /* handle chunks request with a specific base label */
312 if (base
!= MPLS_LABEL_BASE_ANY
)
313 return assign_specific_label_chunk(proto
, instance
, keep
, size
,
316 /* appease scan-build, who gets confused by the use of macros */
317 assert(lbl_mgr
.lc_list
);
319 /* first check if there's one available */
320 for (ALL_LIST_ELEMENTS_RO(lbl_mgr
.lc_list
, node
, lmc
)) {
321 if (lmc
->proto
== NO_PROTO
322 && lmc
->end
- lmc
->start
+ 1 == size
) {
324 lmc
->instance
= instance
;
328 /* check if we hadve a "hole" behind us that we can squeeze into
330 if ((lmc
->start
> prev_end
)
331 && (lmc
->start
- prev_end
>= size
)) {
332 lmc
= create_label_chunk(proto
, instance
, keep
,
333 prev_end
+ 1, prev_end
+ size
);
334 listnode_add_before(lbl_mgr
.lc_list
, node
, lmc
);
339 /* otherwise create a new one */
342 if (list_isempty(lbl_mgr
.lc_list
))
343 start_free
= MPLS_LABEL_UNRESERVED_MIN
;
345 start_free
= ((struct label_manager_chunk
*)listgetdata(
346 listtail(lbl_mgr
.lc_list
)))
350 if (start_free
> MPLS_LABEL_UNRESERVED_MAX
- size
+ 1) {
351 flog_err(EC_ZEBRA_LM_EXHAUSTED_LABELS
,
352 "Reached max labels. Start: %u, size: %u", start_free
,
357 /* create chunk and link at tail */
358 lmc
= create_label_chunk(proto
, instance
, keep
, start_free
,
359 start_free
+ size
- 1);
360 listnode_add(lbl_mgr
.lc_list
, lmc
);
365 * Core function, release no longer used label chunks
367 * @param proto Daemon protocol of client, to identify the owner
368 * @param instance Instance, to identify the owner
369 * @param start First label of the chunk
370 * @param end Last label of the chunk
371 * @return 0 on success, -1 otherwise
373 int release_label_chunk(uint8_t proto
, unsigned short instance
, uint32_t start
,
376 struct listnode
*node
;
377 struct label_manager_chunk
*lmc
;
380 /* check that size matches */
381 if (IS_ZEBRA_DEBUG_PACKET
)
382 zlog_debug("Releasing label chunk: %u - %u", start
, end
);
383 /* find chunk and disown */
384 for (ALL_LIST_ELEMENTS_RO(lbl_mgr
.lc_list
, node
, lmc
)) {
385 if (lmc
->start
!= start
)
389 if (lmc
->proto
!= proto
|| lmc
->instance
!= instance
) {
390 flog_err(EC_ZEBRA_LM_DAEMON_MISMATCH
,
391 "%s: Daemon mismatch!!", __func__
);
394 lmc
->proto
= NO_PROTO
;
401 flog_err(EC_ZEBRA_LM_UNRELEASED_CHUNK
,
402 "%s: Label chunk not released!!", __func__
);
407 /* default functions to be called on hooks */
408 static int label_manager_connect(uint8_t proto
, uint16_t instance
,
412 * Release previous labels of same protocol and instance.
413 * This is done in case it restarted from an unexpected shutdown.
415 release_daemon_label_chunks(proto
, instance
);
416 return lm_client_connect_response(proto
, instance
, vrf_id
, 0);
418 static int label_manager_disconnect(uint8_t proto
, uint16_t instance
)
420 release_daemon_label_chunks(proto
, instance
);
423 static int label_manager_get_chunk(struct label_manager_chunk
**lmc
,
424 uint8_t proto
, uint16_t instance
,
425 uint8_t keep
, uint32_t size
, uint32_t base
,
428 *lmc
= assign_label_chunk(proto
, instance
, keep
, size
, base
);
429 return lm_get_chunk_response(*lmc
, proto
, instance
, vrf_id
);
432 /* Respond to a connect request */
433 int lm_client_connect_response(uint8_t proto
, uint16_t instance
,
434 vrf_id_t vrf_id
, uint8_t result
)
436 struct zserv
*client
= zserv_find_client(proto
, instance
);
438 zlog_err("%s: could not find client for daemon %s instance %u",
439 __func__
, zebra_route_string(proto
), instance
);
442 return zsend_label_manager_connect_response(client
, vrf_id
, result
);
445 /* Respond to a get_chunk request */
446 int lm_get_chunk_response(struct label_manager_chunk
*lmc
, uint8_t proto
,
447 uint16_t instance
, vrf_id_t vrf_id
)
450 flog_err(EC_ZEBRA_LM_CANNOT_ASSIGN_CHUNK
,
451 "Unable to assign Label Chunk to %s instance %u",
452 zebra_route_string(proto
), instance
);
453 else if (IS_ZEBRA_DEBUG_PACKET
)
454 zlog_debug("Assigned Label Chunk %u - %u to %s instance %u",
455 lmc
->start
, lmc
->end
, zebra_route_string(proto
),
458 struct zserv
*client
= zserv_find_client(proto
, instance
);
460 zlog_err("%s: could not find client for daemon %s instance %u",
461 __func__
, zebra_route_string(proto
), instance
);
464 return zsend_assign_label_chunk_response(client
, vrf_id
, proto
,
468 void label_manager_close(void)
470 list_delete(&lbl_mgr
.lc_list
);