]> git.proxmox.com Git - mirror_frr.git/blame - zebra/label_manager.c
Merge pull request #13588 from sri-mohan1/srib-ldpd1
[mirror_frr.git] / zebra / label_manager.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
fea12efb 2/*
3 * Label Manager for FRR
4 *
5 * Copyright (C) 2017 by Bingen Eguzkitza,
6 * Volta Networks Inc.
7 *
8678d638 8 * This file is part of FRRouting (FRR)
fea12efb 9 */
10
43e52561 11#include <zebra.h>
fea12efb 12#include <stdio.h>
13#include <string.h>
14#include <sys/types.h>
15
fea12efb 16#include "lib/log.h"
17#include "lib/memory.h"
18#include "lib/mpls.h"
19#include "lib/network.h"
20#include "lib/stream.h"
21#include "lib/zclient.h"
689f5a8c 22#include "lib/libfrr.h"
fea12efb 23
3801e764
DS
24//#include "zebra/zserv.h"
25#include "zebra/zebra_router.h"
43e52561
QY
26#include "zebra/label_manager.h"
27#include "zebra/zebra_errors.h"
e11d7c96 28#include "zebra/zapi_msg.h"
8f86bb06 29#include "zebra/debug.h"
fea12efb 30
31#define CONNECTION_DELAY 5
32
33struct label_manager lbl_mgr;
34
35DEFINE_MGROUP(LBL_MGR, "Label Manager");
36DEFINE_MTYPE_STATIC(LBL_MGR, LM_CHUNK, "Label Manager Chunk");
37
e11d7c96
EDP
38/* define hooks for the basic API, so that it can be specialized or served
39 * externally
fea12efb 40 */
fea12efb 41
4cebdb9b
MS
42DEFINE_HOOK(lm_client_connect, (struct zserv *client, vrf_id_t vrf_id),
43 (client, vrf_id));
44DEFINE_HOOK(lm_client_disconnect, (struct zserv *client), (client));
e11d7c96 45DEFINE_HOOK(lm_get_chunk,
4cebdb9b
MS
46 (struct label_manager_chunk * *lmc, struct zserv *client,
47 uint8_t keep, uint32_t size, uint32_t base, vrf_id_t vrf_id),
48 (lmc, client, keep, size, base, vrf_id));
e11d7c96 49DEFINE_HOOK(lm_release_chunk,
4cebdb9b
MS
50 (struct zserv *client, uint32_t start, uint32_t end),
51 (client, start, end));
e11d7c96
EDP
52DEFINE_HOOK(lm_cbs_inited, (), ());
53
54/* define wrappers to be called in zapi_msg.c (as hooks must be called in
55 * source file where they were defined)
56 */
4cebdb9b 57void lm_client_connect_call(struct zserv *client, vrf_id_t vrf_id)
fea12efb 58{
4cebdb9b 59 hook_call(lm_client_connect, client, vrf_id);
fea12efb 60}
4cebdb9b
MS
61void lm_get_chunk_call(struct label_manager_chunk **lmc, struct zserv *client,
62 uint8_t keep, uint32_t size, uint32_t base,
63 vrf_id_t vrf_id)
5c7ef8dc 64{
4cebdb9b 65 hook_call(lm_get_chunk, lmc, client, keep, size, base, vrf_id);
5c7ef8dc 66}
4cebdb9b 67void lm_release_chunk_call(struct zserv *client, uint32_t start, uint32_t end)
5c7ef8dc 68{
4cebdb9b 69 hook_call(lm_release_chunk, client, start, end);
fea12efb 70}
71
e11d7c96 72/* forward declarations of the static functions to be used for some hooks */
4cebdb9b
MS
73static int label_manager_connect(struct zserv *client, vrf_id_t vrf_id);
74static int label_manager_disconnect(struct zserv *client);
e11d7c96 75static int label_manager_get_chunk(struct label_manager_chunk **lmc,
4cebdb9b
MS
76 struct zserv *client, uint8_t keep,
77 uint32_t size, uint32_t base,
e11d7c96 78 vrf_id_t vrf_id);
4cebdb9b
MS
79static int label_manager_release_label_chunk(struct zserv *client,
80 uint32_t start, uint32_t end);
fea12efb 81
507d2737 82void delete_label_chunk(void *val)
fea12efb 83{
e11d7c96 84 XFREE(MTYPE_LM_CHUNK, val);
fea12efb 85}
86
453844ab
QY
87/**
88 * Release label chunks from a client.
89 *
90 * Called on client disconnection or reconnection. It only releases chunks
91 * with empty keep value.
92 *
93 * @param proto Daemon protocol of client, to identify the owner
94 * @param instance Instance, to identify the owner
95 * @return Number of chunks released
96 */
4cebdb9b 97int release_daemon_label_chunks(struct zserv *client)
453844ab 98{
453844ab
QY
99 struct listnode *node;
100 struct label_manager_chunk *lmc;
101 int count = 0;
102 int ret;
103
8f86bb06 104 if (IS_ZEBRA_DEBUG_PACKET)
4cebdb9b
MS
105 zlog_debug("%s: Releasing chunks for client proto %s, instance %d, session %u",
106 __func__, zebra_route_string(client->proto),
107 client->instance, client->session_id);
e11d7c96 108
453844ab 109 for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) {
4cebdb9b
MS
110 if (lmc->proto == client->proto &&
111 lmc->instance == client->instance &&
112 lmc->session_id == client->session_id && lmc->keep == 0) {
453844ab 113 ret = release_label_chunk(lmc->proto, lmc->instance,
4cebdb9b 114 lmc->session_id,
453844ab
QY
115 lmc->start, lmc->end);
116 if (ret == 0)
117 count++;
118 }
119 }
120
8f86bb06
DS
121 if (IS_ZEBRA_DEBUG_PACKET)
122 zlog_debug("%s: Released %d label chunks", __func__, count);
453844ab
QY
123
124 return count;
125}
126
e11d7c96
EDP
127int lm_client_disconnect_cb(struct zserv *client)
128{
4cebdb9b 129 hook_call(lm_client_disconnect, client);
e11d7c96
EDP
130 return 0;
131}
132
133void lm_hooks_register(void)
134{
135 hook_register(lm_client_connect, label_manager_connect);
136 hook_register(lm_client_disconnect, label_manager_disconnect);
137 hook_register(lm_get_chunk, label_manager_get_chunk);
4cebdb9b 138 hook_register(lm_release_chunk, label_manager_release_label_chunk);
e11d7c96
EDP
139}
140void lm_hooks_unregister(void)
141{
142 hook_unregister(lm_client_connect, label_manager_connect);
143 hook_unregister(lm_client_disconnect, label_manager_disconnect);
144 hook_unregister(lm_get_chunk, label_manager_get_chunk);
4cebdb9b 145 hook_unregister(lm_release_chunk, label_manager_release_label_chunk);
e11d7c96
EDP
146}
147
fea12efb 148/**
149 * Init label manager (or proxy to an external one)
150 */
e11d7c96 151void label_manager_init(void)
fea12efb 152{
e11d7c96
EDP
153 lbl_mgr.lc_list = list_new();
154 lbl_mgr.lc_list->del = delete_label_chunk;
155 hook_register(zserv_client_close, lm_client_disconnect_cb);
1002497a 156
e11d7c96
EDP
157 /* register default hooks for the label manager actions */
158 lm_hooks_register();
453844ab 159
e11d7c96
EDP
160 /* notify any external module that we are done */
161 hook_call(lm_cbs_inited);
fea12efb 162}
163
0e3b6a92 164/* alloc and fill a label chunk */
507d2737
PR
165struct label_manager_chunk *
166create_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id,
167 uint8_t keep, uint32_t start, uint32_t end)
0e3b6a92
EDP
168{
169 /* alloc chunk, fill it and return it */
170 struct label_manager_chunk *lmc =
171 XCALLOC(MTYPE_LM_CHUNK, sizeof(struct label_manager_chunk));
172
173 lmc->start = start;
174 lmc->end = end;
175 lmc->proto = proto;
176 lmc->instance = instance;
4cebdb9b 177 lmc->session_id = session_id;
0e3b6a92
EDP
178 lmc->keep = keep;
179
180 return lmc;
181}
182
183/* attempt to get a specific label chunk */
e11d7c96 184static struct label_manager_chunk *
0e3b6a92 185assign_specific_label_chunk(uint8_t proto, unsigned short instance,
4cebdb9b
MS
186 uint32_t session_id, uint8_t keep, uint32_t size,
187 uint32_t base)
0e3b6a92
EDP
188{
189 struct label_manager_chunk *lmc;
190 struct listnode *node, *next = NULL;
191 struct listnode *first_node = NULL;
192 struct listnode *last_node = NULL;
193 struct listnode *insert_node = NULL;
194
195 /* precompute last label from base and size */
196 uint32_t end = base + size - 1;
197
198 /* sanities */
199 if ((base < MPLS_LABEL_UNRESERVED_MIN)
200 || (end > MPLS_LABEL_UNRESERVED_MAX)) {
201 zlog_err("Invalid LM request arguments: base: %u, size: %u",
202 base, size);
203 return NULL;
204 }
205
206 /* Scan the existing chunks to see if the requested range of labels
207 * falls inside any of such chunks */
208 for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) {
209
210 /* skip chunks for labels < base */
211 if (base > lmc->end)
212 continue;
213
214 /* requested range is not covered by any existing, free chunk.
215 * Therefore, need to insert a chunk */
216 if ((end < lmc->start) && !first_node) {
217 insert_node = node;
218 break;
219 }
220
221 if (!first_node)
222 first_node = node;
223
224 /* if chunk is used, cannot honor request */
225 if (lmc->proto != NO_PROTO)
226 return NULL;
227
7658c2e5 228 if (end <= lmc->end) {
0e3b6a92
EDP
229 last_node = node;
230 break;
231 }
232 }
233
234 /* insert chunk between existing chunks */
235 if (insert_node) {
4cebdb9b
MS
236 lmc = create_label_chunk(proto, instance, session_id, keep,
237 base, end);
0e3b6a92
EDP
238 listnode_add_before(lbl_mgr.lc_list, insert_node, lmc);
239 return lmc;
240 }
241
242 if (first_node) {
243 /* get node past the last one, if there */
244 if (last_node)
245 last_node = listnextnode(last_node);
246
247 /* delete node coming after the above chunk whose labels are
248 * included in the previous one */
249 for (node = first_node; node && (node != last_node);
250 node = next) {
7feb884d
DS
251 struct label_manager_chunk *death;
252
0e3b6a92 253 next = listnextnode(node);
7feb884d 254 death = listgetdata(node);
0e3b6a92 255 list_delete_node(lbl_mgr.lc_list, node);
7feb884d 256 delete_label_chunk(death);
0e3b6a92
EDP
257 }
258
4cebdb9b
MS
259 lmc = create_label_chunk(proto, instance, session_id, keep,
260 base, end);
0e3b6a92
EDP
261 if (last_node)
262 listnode_add_before(lbl_mgr.lc_list, last_node, lmc);
263 else
264 listnode_add(lbl_mgr.lc_list, lmc);
265
266 return lmc;
267 } else {
268 /* create a new chunk past all the existing ones and link at
269 * tail */
4cebdb9b
MS
270 lmc = create_label_chunk(proto, instance, session_id, keep,
271 base, end);
0e3b6a92
EDP
272 listnode_add(lbl_mgr.lc_list, lmc);
273 return lmc;
274 }
275}
276
fea12efb 277/**
0e3b6a92 278 * Core function, assigns label chunks
fea12efb 279 *
280 * It first searches through the list to check if there's one available
281 * (previously released). Otherwise it creates and assigns a new one
282 *
283 * @param proto Daemon protocol of client, to identify the owner
284 * @param instance Instance, to identify the owner
285 * @param keep If set, avoid garbage collection
0e3b6a92
EDP
286 * @param size Size of the label chunk
287 * @param base Desired starting label of the chunk; if MPLS_LABEL_BASE_ANY it does not apply
288 * @return Pointer to the assigned label chunk, or NULL if the request could not be satisfied
fea12efb 289 */
507d2737
PR
290struct label_manager_chunk *
291assign_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id,
292 uint8_t keep, uint32_t size, uint32_t base)
fea12efb 293{
294 struct label_manager_chunk *lmc;
295 struct listnode *node;
3c844979 296 uint32_t prev_end = MPLS_LABEL_UNRESERVED_MIN;
0e3b6a92
EDP
297
298 /* handle chunks request with a specific base label */
299 if (base != MPLS_LABEL_BASE_ANY)
4cebdb9b
MS
300 return assign_specific_label_chunk(proto, instance, session_id,
301 keep, size, base);
0e3b6a92
EDP
302
303 /* appease scan-build, who gets confused by the use of macros */
304 assert(lbl_mgr.lc_list);
fea12efb 305
fea12efb 306 /* first check if there's one available */
307 for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) {
d62a17ae 308 if (lmc->proto == NO_PROTO
309 && lmc->end - lmc->start + 1 == size) {
fea12efb 310 lmc->proto = proto;
311 lmc->instance = instance;
4cebdb9b 312 lmc->session_id = session_id;
fea12efb 313 lmc->keep = keep;
314 return lmc;
315 }
0e3b6a92
EDP
316 /* check if we hadve a "hole" behind us that we can squeeze into
317 */
18998228 318 if ((lmc->start > prev_end) && (lmc->start - prev_end > size)) {
4cebdb9b
MS
319 lmc = create_label_chunk(proto, instance, session_id,
320 keep, prev_end + 1,
321 prev_end + size);
0e3b6a92
EDP
322 listnode_add_before(lbl_mgr.lc_list, node, lmc);
323 return lmc;
324 }
325 prev_end = lmc->end;
fea12efb 326 }
327 /* otherwise create a new one */
0e3b6a92 328 uint32_t start_free;
fea12efb 329
330 if (list_isempty(lbl_mgr.lc_list))
0e3b6a92 331 start_free = MPLS_LABEL_UNRESERVED_MIN;
fea12efb 332 else
0e3b6a92 333 start_free = ((struct label_manager_chunk *)listgetdata(
d62a17ae 334 listtail(lbl_mgr.lc_list)))
335 ->end
336 + 1;
0e3b6a92
EDP
337
338 if (start_free > MPLS_LABEL_UNRESERVED_MAX - size + 1) {
e914ccbe 339 flog_err(EC_ZEBRA_LM_EXHAUSTED_LABELS,
0e3b6a92 340 "Reached max labels. Start: %u, size: %u", start_free,
1c50c1c0 341 size);
fea12efb 342 return NULL;
343 }
fea12efb 344
0e3b6a92 345 /* create chunk and link at tail */
4cebdb9b 346 lmc = create_label_chunk(proto, instance, session_id, keep, start_free,
0e3b6a92
EDP
347 start_free + size - 1);
348 listnode_add(lbl_mgr.lc_list, lmc);
fea12efb 349 return lmc;
350}
351
4cebdb9b
MS
352/**
353 * Release label chunks from a client.
354 *
355 * Called on client disconnection or reconnection. It only releases chunks
356 * with empty keep value.
357 *
358 * @param client Client zapi session
359 * @param start First label of the chunk
360 * @param end Last label of the chunk
361 * @return 0 on success
362 */
363static int label_manager_release_label_chunk(struct zserv *client,
364 uint32_t start, uint32_t end)
365{
366 return release_label_chunk(client->proto, client->instance,
367 client->session_id, start, end);
368}
369
fea12efb 370/**
0e3b6a92 371 * Core function, release no longer used label chunks
fea12efb 372 *
373 * @param proto Daemon protocol of client, to identify the owner
374 * @param instance Instance, to identify the owner
d3d9639d 375 * @param session_id Zclient session ID, to identify the zclient session
fea12efb 376 * @param start First label of the chunk
377 * @param end Last label of the chunk
378 * @return 0 on success, -1 otherwise
379 */
507d2737 380int release_label_chunk(uint8_t proto, unsigned short instance,
4cebdb9b 381 uint32_t session_id, uint32_t start, uint32_t end)
fea12efb 382{
383 struct listnode *node;
384 struct label_manager_chunk *lmc;
385 int ret = -1;
386
387 /* check that size matches */
8f86bb06
DS
388 if (IS_ZEBRA_DEBUG_PACKET)
389 zlog_debug("Releasing label chunk: %u - %u", start, end);
fea12efb 390 /* find chunk and disown */
391 for (ALL_LIST_ELEMENTS_RO(lbl_mgr.lc_list, node, lmc)) {
392 if (lmc->start != start)
393 continue;
394 if (lmc->end != end)
395 continue;
4cebdb9b
MS
396 if (lmc->proto != proto || lmc->instance != instance ||
397 lmc->session_id != session_id) {
e914ccbe 398 flog_err(EC_ZEBRA_LM_DAEMON_MISMATCH,
1c50c1c0 399 "%s: Daemon mismatch!!", __func__);
fea12efb 400 continue;
401 }
402 lmc->proto = NO_PROTO;
403 lmc->instance = 0;
4cebdb9b 404 lmc->session_id = 0;
fea12efb 405 lmc->keep = 0;
406 ret = 0;
407 break;
408 }
409 if (ret != 0)
e914ccbe 410 flog_err(EC_ZEBRA_LM_UNRELEASED_CHUNK,
1c50c1c0 411 "%s: Label chunk not released!!", __func__);
fea12efb 412
413 return ret;
414}
415
e11d7c96 416/* default functions to be called on hooks */
4cebdb9b 417static int label_manager_connect(struct zserv *client, vrf_id_t vrf_id)
e11d7c96
EDP
418{
419 /*
420 * Release previous labels of same protocol and instance.
421 * This is done in case it restarted from an unexpected shutdown.
422 */
4cebdb9b
MS
423 release_daemon_label_chunks(client);
424 return zsend_label_manager_connect_response(client, vrf_id, 0);
e11d7c96 425}
4cebdb9b 426static int label_manager_disconnect(struct zserv *client)
e11d7c96 427{
4cebdb9b 428 release_daemon_label_chunks(client);
e11d7c96
EDP
429 return 0;
430}
431static int label_manager_get_chunk(struct label_manager_chunk **lmc,
4cebdb9b
MS
432 struct zserv *client, uint8_t keep,
433 uint32_t size, uint32_t base,
e11d7c96
EDP
434 vrf_id_t vrf_id)
435{
4cebdb9b
MS
436 *lmc = assign_label_chunk(client->proto, client->instance,
437 client->session_id, keep, size, base);
438 return lm_get_chunk_response(*lmc, client, vrf_id);
e11d7c96
EDP
439}
440
441/* Respond to a connect request */
442int lm_client_connect_response(uint8_t proto, uint16_t instance,
4cebdb9b
MS
443 uint32_t session_id, vrf_id_t vrf_id,
444 uint8_t result)
e11d7c96 445{
4cebdb9b
MS
446 struct zserv *client = zserv_find_client_session(proto, instance,
447 session_id);
e11d7c96 448 if (!client) {
4cebdb9b
MS
449 zlog_err("%s: could not find client for daemon %s instance %u session %u",
450 __func__, zebra_route_string(proto), instance,
451 session_id);
e11d7c96
EDP
452 return 1;
453 }
454 return zsend_label_manager_connect_response(client, vrf_id, result);
455}
456
457/* Respond to a get_chunk request */
4cebdb9b
MS
458int lm_get_chunk_response(struct label_manager_chunk *lmc, struct zserv *client,
459 vrf_id_t vrf_id)
e11d7c96 460{
19358322
EDP
461 if (!lmc)
462 flog_err(EC_ZEBRA_LM_CANNOT_ASSIGN_CHUNK,
463 "Unable to assign Label Chunk to %s instance %u",
4cebdb9b 464 zebra_route_string(client->proto), client->instance);
19358322
EDP
465 else if (IS_ZEBRA_DEBUG_PACKET)
466 zlog_debug("Assigned Label Chunk %u - %u to %s instance %u",
4cebdb9b
MS
467 lmc->start, lmc->end,
468 zebra_route_string(client->proto), client->instance);
19358322 469
4cebdb9b 470 return zsend_assign_label_chunk_response(client, vrf_id, lmc);
e11d7c96 471}
fea12efb 472
4d762f26 473void label_manager_close(void)
fea12efb 474{
6a154c88 475 list_delete(&lbl_mgr.lc_list);
fea12efb 476}