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