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
, (struct zserv
*client
, vrf_id_t vrf_id
),
57 DEFINE_HOOK(lm_client_disconnect
, (struct zserv
*client
), (client
));
58 DEFINE_HOOK(lm_get_chunk
,
59 (struct label_manager_chunk
* *lmc
, struct zserv
*client
,
60 uint8_t keep
, uint32_t size
, uint32_t base
, vrf_id_t vrf_id
),
61 (lmc
, client
, keep
, size
, base
, vrf_id
));
62 DEFINE_HOOK(lm_release_chunk
,
63 (struct zserv
*client
, uint32_t start
, uint32_t end
),
64 (client
, start
, end
));
65 DEFINE_HOOK(lm_cbs_inited
, (), ());
67 /* define wrappers to be called in zapi_msg.c (as hooks must be called in
68 * source file where they were defined)
70 void lm_client_connect_call(struct zserv
*client
, vrf_id_t vrf_id
)
72 hook_call(lm_client_connect
, client
, vrf_id
);
74 void lm_get_chunk_call(struct label_manager_chunk
**lmc
, struct zserv
*client
,
75 uint8_t keep
, uint32_t size
, uint32_t base
,
78 hook_call(lm_get_chunk
, lmc
, client
, keep
, size
, base
, vrf_id
);
80 void lm_release_chunk_call(struct zserv
*client
, uint32_t start
, uint32_t end
)
82 hook_call(lm_release_chunk
, client
, start
, end
);
85 /* forward declarations of the static functions to be used for some hooks */
86 static int label_manager_connect(struct zserv
*client
, vrf_id_t vrf_id
);
87 static int label_manager_disconnect(struct zserv
*client
);
88 static int label_manager_get_chunk(struct label_manager_chunk
**lmc
,
89 struct zserv
*client
, uint8_t keep
,
90 uint32_t size
, uint32_t base
,
92 static int label_manager_release_label_chunk(struct zserv
*client
,
93 uint32_t start
, uint32_t end
);
95 void delete_label_chunk(void *val
)
97 XFREE(MTYPE_LM_CHUNK
, val
);
101 * Release label chunks from a client.
103 * Called on client disconnection or reconnection. It only releases chunks
104 * with empty keep value.
106 * @param proto Daemon protocol of client, to identify the owner
107 * @param instance Instance, to identify the owner
108 * @return Number of chunks released
110 int release_daemon_label_chunks(struct zserv
*client
)
112 struct listnode
*node
;
113 struct label_manager_chunk
*lmc
;
117 if (IS_ZEBRA_DEBUG_PACKET
)
118 zlog_debug("%s: Releasing chunks for client proto %s, instance %d, session %u",
119 __func__
, zebra_route_string(client
->proto
),
120 client
->instance
, client
->session_id
);
122 for (ALL_LIST_ELEMENTS_RO(lbl_mgr
.lc_list
, node
, lmc
)) {
123 if (lmc
->proto
== client
->proto
&&
124 lmc
->instance
== client
->instance
&&
125 lmc
->session_id
== client
->session_id
&& lmc
->keep
== 0) {
126 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 hook_call(lm_client_disconnect
, client
);
146 void lm_hooks_register(void)
148 hook_register(lm_client_connect
, label_manager_connect
);
149 hook_register(lm_client_disconnect
, label_manager_disconnect
);
150 hook_register(lm_get_chunk
, label_manager_get_chunk
);
151 hook_register(lm_release_chunk
, label_manager_release_label_chunk
);
153 void lm_hooks_unregister(void)
155 hook_unregister(lm_client_connect
, label_manager_connect
);
156 hook_unregister(lm_client_disconnect
, label_manager_disconnect
);
157 hook_unregister(lm_get_chunk
, label_manager_get_chunk
);
158 hook_unregister(lm_release_chunk
, label_manager_release_label_chunk
);
162 * Init label manager (or proxy to an external one)
164 void label_manager_init(void)
166 lbl_mgr
.lc_list
= list_new();
167 lbl_mgr
.lc_list
->del
= delete_label_chunk
;
168 hook_register(zserv_client_close
, lm_client_disconnect_cb
);
170 /* register default hooks for the label manager actions */
173 /* notify any external module that we are done */
174 hook_call(lm_cbs_inited
);
177 /* alloc and fill a label chunk */
178 struct label_manager_chunk
*
179 create_label_chunk(uint8_t proto
, unsigned short instance
, uint32_t session_id
,
180 uint8_t keep
, uint32_t start
, uint32_t end
)
182 /* alloc chunk, fill it and return it */
183 struct label_manager_chunk
*lmc
=
184 XCALLOC(MTYPE_LM_CHUNK
, sizeof(struct label_manager_chunk
));
189 lmc
->instance
= instance
;
190 lmc
->session_id
= session_id
;
196 /* attempt to get a specific label chunk */
197 static struct label_manager_chunk
*
198 assign_specific_label_chunk(uint8_t proto
, unsigned short instance
,
199 uint32_t session_id
, uint8_t keep
, uint32_t size
,
202 struct label_manager_chunk
*lmc
;
203 struct listnode
*node
, *next
= NULL
;
204 struct listnode
*first_node
= NULL
;
205 struct listnode
*last_node
= NULL
;
206 struct listnode
*insert_node
= NULL
;
208 /* precompute last label from base and size */
209 uint32_t end
= base
+ size
- 1;
212 if ((base
< MPLS_LABEL_UNRESERVED_MIN
)
213 || (end
> MPLS_LABEL_UNRESERVED_MAX
)) {
214 zlog_err("Invalid LM request arguments: base: %u, size: %u",
219 /* Scan the existing chunks to see if the requested range of labels
220 * falls inside any of such chunks */
221 for (ALL_LIST_ELEMENTS_RO(lbl_mgr
.lc_list
, node
, lmc
)) {
223 /* skip chunks for labels < base */
227 /* requested range is not covered by any existing, free chunk.
228 * Therefore, need to insert a chunk */
229 if ((end
< lmc
->start
) && !first_node
) {
237 /* if chunk is used, cannot honor request */
238 if (lmc
->proto
!= NO_PROTO
)
241 if (end
<= lmc
->end
) {
247 /* insert chunk between existing chunks */
249 lmc
= create_label_chunk(proto
, instance
, session_id
, keep
,
251 listnode_add_before(lbl_mgr
.lc_list
, insert_node
, lmc
);
256 /* get node past the last one, if there */
258 last_node
= listnextnode(last_node
);
260 /* delete node coming after the above chunk whose labels are
261 * included in the previous one */
262 for (node
= first_node
; node
&& (node
!= last_node
);
264 struct label_manager_chunk
*death
;
266 next
= listnextnode(node
);
267 death
= listgetdata(node
);
268 list_delete_node(lbl_mgr
.lc_list
, node
);
269 delete_label_chunk(death
);
272 lmc
= create_label_chunk(proto
, instance
, session_id
, keep
,
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
, session_id
, keep
,
285 listnode_add(lbl_mgr
.lc_list
, lmc
);
291 * Core function, assigns label chunks
293 * It first searches through the list to check if there's one available
294 * (previously released). Otherwise it creates and assigns a new one
296 * @param proto Daemon protocol of client, to identify the owner
297 * @param instance Instance, to identify the owner
298 * @param keep If set, avoid garbage collection
299 * @param size Size of the label chunk
300 * @param base Desired starting label of the chunk; if MPLS_LABEL_BASE_ANY it does not apply
301 * @return Pointer to the assigned label chunk, or NULL if the request could not be satisfied
303 struct label_manager_chunk
*
304 assign_label_chunk(uint8_t proto
, unsigned short instance
, uint32_t session_id
,
305 uint8_t keep
, uint32_t size
, uint32_t base
)
307 struct label_manager_chunk
*lmc
;
308 struct listnode
*node
;
309 uint32_t prev_end
= MPLS_LABEL_UNRESERVED_MIN
;
311 /* handle chunks request with a specific base label */
312 if (base
!= MPLS_LABEL_BASE_ANY
)
313 return assign_specific_label_chunk(proto
, instance
, session_id
,
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
;
325 lmc
->session_id
= session_id
;
329 /* check if we hadve a "hole" behind us that we can squeeze into
331 if ((lmc
->start
> prev_end
) && (lmc
->start
- prev_end
> size
)) {
332 lmc
= create_label_chunk(proto
, instance
, session_id
,
335 listnode_add_before(lbl_mgr
.lc_list
, node
, lmc
);
340 /* otherwise create a new one */
343 if (list_isempty(lbl_mgr
.lc_list
))
344 start_free
= MPLS_LABEL_UNRESERVED_MIN
;
346 start_free
= ((struct label_manager_chunk
*)listgetdata(
347 listtail(lbl_mgr
.lc_list
)))
351 if (start_free
> MPLS_LABEL_UNRESERVED_MAX
- size
+ 1) {
352 flog_err(EC_ZEBRA_LM_EXHAUSTED_LABELS
,
353 "Reached max labels. Start: %u, size: %u", start_free
,
358 /* create chunk and link at tail */
359 lmc
= create_label_chunk(proto
, instance
, session_id
, keep
, start_free
,
360 start_free
+ size
- 1);
361 listnode_add(lbl_mgr
.lc_list
, lmc
);
366 * Release label chunks from a client.
368 * Called on client disconnection or reconnection. It only releases chunks
369 * with empty keep value.
371 * @param client Client zapi session
372 * @param start First label of the chunk
373 * @param end Last label of the chunk
374 * @return 0 on success
376 static int label_manager_release_label_chunk(struct zserv
*client
,
377 uint32_t start
, uint32_t end
)
379 return release_label_chunk(client
->proto
, client
->instance
,
380 client
->session_id
, start
, end
);
384 * Core function, release no longer used label chunks
386 * @param proto Daemon protocol of client, to identify the owner
387 * @param instance Instance, to identify the owner
388 * @param session_id Zclient session ID, to identify the zclient session
389 * @param start First label of the chunk
390 * @param end Last label of the chunk
391 * @return 0 on success, -1 otherwise
393 int release_label_chunk(uint8_t proto
, unsigned short instance
,
394 uint32_t session_id
, uint32_t start
, uint32_t end
)
396 struct listnode
*node
;
397 struct label_manager_chunk
*lmc
;
400 /* check that size matches */
401 if (IS_ZEBRA_DEBUG_PACKET
)
402 zlog_debug("Releasing label chunk: %u - %u", start
, end
);
403 /* find chunk and disown */
404 for (ALL_LIST_ELEMENTS_RO(lbl_mgr
.lc_list
, node
, lmc
)) {
405 if (lmc
->start
!= start
)
409 if (lmc
->proto
!= proto
|| lmc
->instance
!= instance
||
410 lmc
->session_id
!= session_id
) {
411 flog_err(EC_ZEBRA_LM_DAEMON_MISMATCH
,
412 "%s: Daemon mismatch!!", __func__
);
415 lmc
->proto
= NO_PROTO
;
423 flog_err(EC_ZEBRA_LM_UNRELEASED_CHUNK
,
424 "%s: Label chunk not released!!", __func__
);
429 /* default functions to be called on hooks */
430 static int label_manager_connect(struct zserv
*client
, vrf_id_t vrf_id
)
433 * Release previous labels of same protocol and instance.
434 * This is done in case it restarted from an unexpected shutdown.
436 release_daemon_label_chunks(client
);
437 return zsend_label_manager_connect_response(client
, vrf_id
, 0);
439 static int label_manager_disconnect(struct zserv
*client
)
441 release_daemon_label_chunks(client
);
444 static int label_manager_get_chunk(struct label_manager_chunk
**lmc
,
445 struct zserv
*client
, uint8_t keep
,
446 uint32_t size
, uint32_t base
,
449 *lmc
= assign_label_chunk(client
->proto
, client
->instance
,
450 client
->session_id
, keep
, size
, base
);
451 return lm_get_chunk_response(*lmc
, client
, vrf_id
);
454 /* Respond to a connect request */
455 int lm_client_connect_response(uint8_t proto
, uint16_t instance
,
456 uint32_t session_id
, vrf_id_t vrf_id
,
459 struct zserv
*client
= zserv_find_client_session(proto
, instance
,
462 zlog_err("%s: could not find client for daemon %s instance %u session %u",
463 __func__
, zebra_route_string(proto
), instance
,
467 return zsend_label_manager_connect_response(client
, vrf_id
, result
);
470 /* Respond to a get_chunk request */
471 int lm_get_chunk_response(struct label_manager_chunk
*lmc
, struct zserv
*client
,
475 flog_err(EC_ZEBRA_LM_CANNOT_ASSIGN_CHUNK
,
476 "Unable to assign Label Chunk to %s instance %u",
477 zebra_route_string(client
->proto
), client
->instance
);
478 else if (IS_ZEBRA_DEBUG_PACKET
)
479 zlog_debug("Assigned Label Chunk %u - %u to %s instance %u",
480 lmc
->start
, lmc
->end
,
481 zebra_route_string(client
->proto
), client
->instance
);
483 return zsend_assign_label_chunk_response(client
, vrf_id
, lmc
);
486 void label_manager_close(void)
488 list_delete(&lbl_mgr
.lc_list
);