]>
Commit | Line | Data |
---|---|---|
955bfd98 PZ |
1 | /* |
2 | * BGP Label Pool - Manage label chunk allocations from zebra asynchronously | |
3 | * | |
4 | * Copyright (C) 2018 LabN Consulting, L.L.C. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the Free | |
8 | * Software Foundation; either version 2 of the License, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along | |
17 | * with this program; see the file COPYING; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #include <zebra.h> | |
22 | ||
23 | #include "log.h" | |
24 | #include "memory.h" | |
25 | #include "stream.h" | |
26 | #include "mpls.h" | |
27 | #include "vty.h" | |
955bfd98 PZ |
28 | #include "linklist.h" |
29 | #include "skiplist.h" | |
30 | #include "workqueue.h" | |
31 | #include "zclient.h" | |
0e3b6a92 | 32 | #include "mpls.h" |
955bfd98 PZ |
33 | |
34 | #include "bgpd/bgpd.h" | |
35 | #include "bgpd/bgp_labelpool.h" | |
36 | #include "bgpd/bgp_debug.h" | |
14454c9f | 37 | #include "bgpd/bgp_errors.h" |
668cfa98 | 38 | #include "bgpd/bgp_route.h" |
955bfd98 PZ |
39 | |
40 | /* | |
41 | * Definitions and external declarations. | |
42 | */ | |
43 | extern struct zclient *zclient; | |
44 | ||
45 | /* | |
46 | * Remember where pool data are kept | |
47 | */ | |
48 | static struct labelpool *lp; | |
49 | ||
50 | /* request this many labels at a time from zebra */ | |
51 | #define LP_CHUNK_SIZE 50 | |
52 | ||
53 | DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CHUNK, "BGP Label Chunk") | |
41397f2e | 54 | DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO item") |
955bfd98 PZ |
55 | DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CB, "BGP Dynamic Label Assignment") |
56 | DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CBQ, "BGP Dynamic Label Callback") | |
57 | ||
955bfd98 PZ |
58 | struct lp_chunk { |
59 | uint32_t first; | |
60 | uint32_t last; | |
61 | }; | |
62 | ||
63 | /* | |
64 | * label control block | |
65 | */ | |
66 | struct lp_lcb { | |
67 | mpls_label_t label; /* MPLS_LABEL_NONE = not allocated */ | |
68 | int type; | |
69 | void *labelid; /* unique ID */ | |
70 | /* | |
71 | * callback for label allocation and loss | |
72 | * | |
73 | * allocated: false = lost | |
74 | */ | |
75 | int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc); | |
76 | }; | |
77 | ||
955bfd98 | 78 | struct lp_fifo { |
41397f2e | 79 | struct lp_fifo_item fifo; |
955bfd98 PZ |
80 | struct lp_lcb lcb; |
81 | }; | |
82 | ||
41397f2e DL |
83 | DECLARE_LIST(lp_fifo, struct lp_fifo, fifo) |
84 | ||
955bfd98 PZ |
85 | struct lp_cbq_item { |
86 | int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc); | |
87 | int type; | |
88 | mpls_label_t label; | |
89 | void *labelid; | |
90 | bool allocated; /* false = lost */ | |
91 | }; | |
92 | ||
93 | static wq_item_status lp_cbq_docallback(struct work_queue *wq, void *data) | |
94 | { | |
95 | struct lp_cbq_item *lcbq = data; | |
96 | int rc; | |
97 | int debug = BGP_DEBUG(labelpool, LABELPOOL); | |
98 | ||
99 | if (debug) | |
100 | zlog_debug("%s: calling callback with labelid=%p label=%u allocated=%d", | |
101 | __func__, lcbq->labelid, lcbq->label, lcbq->allocated); | |
102 | ||
103 | if (lcbq->label == MPLS_LABEL_NONE) { | |
104 | /* shouldn't happen */ | |
e50f7cfd | 105 | flog_err(EC_BGP_LABEL, "%s: error: label==MPLS_LABEL_NONE", |
1c50c1c0 | 106 | __func__); |
955bfd98 PZ |
107 | return WQ_SUCCESS; |
108 | } | |
109 | ||
110 | rc = (*(lcbq->cbfunc))(lcbq->label, lcbq->labelid, lcbq->allocated); | |
111 | ||
112 | if (lcbq->allocated && rc) { | |
113 | /* | |
114 | * Callback rejected allocation. This situation could arise | |
115 | * if there was a label request followed by the requestor | |
116 | * deciding it didn't need the assignment (e.g., config | |
117 | * change) while the reply to the original request (with | |
118 | * label) was in the work queue. | |
119 | */ | |
120 | if (debug) | |
121 | zlog_debug("%s: callback rejected allocation, releasing labelid=%p label=%u", | |
122 | __func__, lcbq->labelid, lcbq->label); | |
123 | ||
124 | uintptr_t lbl = lcbq->label; | |
125 | void *labelid; | |
126 | struct lp_lcb *lcb; | |
127 | ||
128 | /* | |
129 | * If the rejected label was marked inuse by this labelid, | |
130 | * release the label back to the pool. | |
131 | * | |
132 | * Further, if the rejected label was still assigned to | |
133 | * this labelid in the LCB, delete the LCB. | |
134 | */ | |
135 | if (!skiplist_search(lp->inuse, (void *)lbl, &labelid)) { | |
136 | if (labelid == lcbq->labelid) { | |
137 | if (!skiplist_search(lp->ledger, labelid, | |
138 | (void **)&lcb)) { | |
139 | if (lcbq->label == lcb->label) | |
140 | skiplist_delete(lp->ledger, | |
141 | labelid, NULL); | |
142 | } | |
143 | skiplist_delete(lp->inuse, (void *)lbl, NULL); | |
144 | } | |
145 | } | |
146 | } | |
147 | ||
148 | return WQ_SUCCESS; | |
149 | } | |
150 | ||
151 | static void lp_cbq_item_free(struct work_queue *wq, void *data) | |
152 | { | |
153 | XFREE(MTYPE_BGP_LABEL_CBQ, data); | |
154 | } | |
155 | ||
156 | static void lp_lcb_free(void *goner) | |
157 | { | |
0a22ddfb | 158 | XFREE(MTYPE_BGP_LABEL_CB, goner); |
955bfd98 PZ |
159 | } |
160 | ||
161 | static void lp_chunk_free(void *goner) | |
162 | { | |
0a22ddfb | 163 | XFREE(MTYPE_BGP_LABEL_CHUNK, goner); |
955bfd98 PZ |
164 | } |
165 | ||
166 | void bgp_lp_init(struct thread_master *master, struct labelpool *pool) | |
167 | { | |
168 | if (BGP_DEBUG(labelpool, LABELPOOL)) | |
169 | zlog_debug("%s: entry", __func__); | |
170 | ||
171 | lp = pool; /* Set module pointer to pool data */ | |
172 | ||
173 | lp->ledger = skiplist_new(0, NULL, lp_lcb_free); | |
174 | lp->inuse = skiplist_new(0, NULL, NULL); | |
175 | lp->chunks = list_new(); | |
176 | lp->chunks->del = lp_chunk_free; | |
41397f2e | 177 | lp_fifo_init(&lp->requests); |
955bfd98 | 178 | lp->callback_q = work_queue_new(master, "label callbacks"); |
955bfd98 PZ |
179 | |
180 | lp->callback_q->spec.workfunc = lp_cbq_docallback; | |
181 | lp->callback_q->spec.del_item_data = lp_cbq_item_free; | |
182 | lp->callback_q->spec.max_retries = 0; | |
183 | } | |
184 | ||
992dd67e | 185 | /* check if a label callback was for a BGP LU node, and if so, unlock it */ |
ea63ff6b EDP |
186 | static void check_bgp_lu_cb_unlock(struct lp_lcb *lcb) |
187 | { | |
188 | if (lcb->type == LP_TYPE_BGP_LU) | |
992dd67e | 189 | bgp_dest_unlock_node(lcb->labelid); |
ea63ff6b EDP |
190 | } |
191 | ||
992dd67e | 192 | /* check if a label callback was for a BGP LU node, and if so, lock it */ |
ea63ff6b EDP |
193 | static void check_bgp_lu_cb_lock(struct lp_lcb *lcb) |
194 | { | |
195 | if (lcb->type == LP_TYPE_BGP_LU) | |
992dd67e | 196 | bgp_dest_lock_node(lcb->labelid); |
ea63ff6b EDP |
197 | } |
198 | ||
955bfd98 PZ |
199 | void bgp_lp_finish(void) |
200 | { | |
201 | struct lp_fifo *lf; | |
ea63ff6b | 202 | struct work_queue_item *item, *titem; |
955bfd98 PZ |
203 | |
204 | if (!lp) | |
205 | return; | |
206 | ||
207 | skiplist_free(lp->ledger); | |
208 | lp->ledger = NULL; | |
209 | ||
210 | skiplist_free(lp->inuse); | |
211 | lp->inuse = NULL; | |
212 | ||
6a154c88 | 213 | list_delete(&lp->chunks); |
955bfd98 | 214 | |
ea63ff6b EDP |
215 | while ((lf = lp_fifo_pop(&lp->requests))) { |
216 | check_bgp_lu_cb_unlock(&lf->lcb); | |
955bfd98 | 217 | XFREE(MTYPE_BGP_LABEL_FIFO, lf); |
ea63ff6b | 218 | } |
41397f2e | 219 | lp_fifo_fini(&lp->requests); |
955bfd98 | 220 | |
ea63ff6b EDP |
221 | /* we must unlock path infos for LU callbacks; but we cannot do that |
222 | * in the deletion callback of the workqueue, as that is also called | |
223 | * to remove an element from the queue after it has been run, resulting | |
224 | * in a double unlock. Hence we need to iterate over our queues and | |
225 | * lists and manually perform the unlocking (ugh) | |
226 | */ | |
227 | STAILQ_FOREACH_SAFE (item, &lp->callback_q->items, wq, titem) | |
228 | check_bgp_lu_cb_unlock(item->data); | |
229 | ||
955bfd98 PZ |
230 | work_queue_free_and_null(&lp->callback_q); |
231 | ||
232 | lp = NULL; | |
233 | } | |
234 | ||
235 | static mpls_label_t get_label_from_pool(void *labelid) | |
236 | { | |
237 | struct listnode *node; | |
238 | struct lp_chunk *chunk; | |
239 | int debug = BGP_DEBUG(labelpool, LABELPOOL); | |
240 | ||
241 | /* | |
242 | * Find a free label | |
243 | * Linear search is not efficient but should be executed infrequently. | |
244 | */ | |
245 | for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) { | |
246 | uintptr_t lbl; | |
247 | ||
248 | if (debug) | |
249 | zlog_debug("%s: chunk first=%u last=%u", | |
250 | __func__, chunk->first, chunk->last); | |
251 | ||
252 | for (lbl = chunk->first; lbl <= chunk->last; ++lbl) { | |
253 | /* labelid is key to all-request "ledger" list */ | |
254 | if (!skiplist_insert(lp->inuse, (void *)lbl, labelid)) { | |
255 | /* | |
256 | * Success | |
257 | */ | |
258 | return lbl; | |
259 | } | |
260 | } | |
261 | } | |
262 | return MPLS_LABEL_NONE; | |
263 | } | |
264 | ||
265 | /* | |
266 | * Success indicated by value of "label" field in returned LCB | |
267 | */ | |
268 | static struct lp_lcb *lcb_alloc( | |
269 | int type, | |
270 | void *labelid, | |
271 | int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated)) | |
272 | { | |
273 | /* | |
274 | * Set up label control block | |
275 | */ | |
276 | struct lp_lcb *new = XCALLOC(MTYPE_BGP_LABEL_CB, | |
277 | sizeof(struct lp_lcb)); | |
278 | ||
279 | new->label = get_label_from_pool(labelid); | |
280 | new->type = type; | |
281 | new->labelid = labelid; | |
282 | new->cbfunc = cbfunc; | |
283 | ||
284 | return new; | |
285 | } | |
286 | ||
287 | /* | |
288 | * Callers who need labels must supply a type, labelid, and callback. | |
289 | * The type is a value defined in bgp_labelpool.h (add types as needed). | |
290 | * The callback is for asynchronous notification of label allocation. | |
291 | * The labelid is passed as an argument to the callback. It should be unique | |
292 | * to the requested label instance. | |
293 | * | |
294 | * If zebra is not connected, callbacks with labels will be delayed | |
295 | * until connection is established. If zebra connection is lost after | |
296 | * labels have been assigned, existing assignments via this labelpool | |
297 | * module will continue until reconnection. | |
298 | * | |
299 | * When connection to zebra is reestablished, previous label assignments | |
300 | * will be invalidated (via callbacks having the "allocated" parameter unset) | |
301 | * and new labels will be automatically reassigned by this labelpool module | |
302 | * (that is, a requestor does not need to call lp_get() again if it is | |
303 | * notified via callback that its label has been lost: it will eventually | |
304 | * get another callback with a new label assignment). | |
305 | * | |
306 | * Prior requests for a given labelid are detected so that requests and | |
307 | * assignments are not duplicated. | |
308 | */ | |
309 | void bgp_lp_get( | |
310 | int type, | |
311 | void *labelid, | |
312 | int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated)) | |
313 | { | |
314 | struct lp_lcb *lcb; | |
315 | int requested = 0; | |
316 | int debug = BGP_DEBUG(labelpool, LABELPOOL); | |
317 | ||
318 | if (debug) | |
319 | zlog_debug("%s: labelid=%p", __func__, labelid); | |
320 | ||
321 | /* | |
322 | * Have we seen this request before? | |
323 | */ | |
324 | if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) { | |
325 | requested = 1; | |
326 | } else { | |
327 | lcb = lcb_alloc(type, labelid, cbfunc); | |
328 | if (debug) | |
329 | zlog_debug("%s: inserting lcb=%p label=%u", | |
330 | __func__, lcb, lcb->label); | |
331 | int rc = skiplist_insert(lp->ledger, labelid, lcb); | |
332 | ||
333 | if (rc) { | |
334 | /* shouldn't happen */ | |
e50f7cfd | 335 | flog_err(EC_BGP_LABEL, |
1c50c1c0 QY |
336 | "%s: can't insert new LCB into ledger list", |
337 | __func__); | |
955bfd98 PZ |
338 | XFREE(MTYPE_BGP_LABEL_CB, lcb); |
339 | return; | |
340 | } | |
341 | } | |
342 | ||
343 | if (lcb->label != MPLS_LABEL_NONE) { | |
344 | /* | |
345 | * Fast path: we filled the request from local pool (or | |
346 | * this is a duplicate request that we filled already). | |
347 | * Enqueue response work item with new label. | |
348 | */ | |
349 | struct lp_cbq_item *q; | |
350 | ||
351 | q = XCALLOC(MTYPE_BGP_LABEL_CBQ, sizeof(struct lp_cbq_item)); | |
352 | ||
353 | q->cbfunc = lcb->cbfunc; | |
354 | q->type = lcb->type; | |
355 | q->label = lcb->label; | |
356 | q->labelid = lcb->labelid; | |
357 | q->allocated = true; | |
358 | ||
992dd67e | 359 | /* if this is a LU request, lock node before queueing */ |
ea63ff6b EDP |
360 | check_bgp_lu_cb_lock(lcb); |
361 | ||
955bfd98 PZ |
362 | work_queue_add(lp->callback_q, q); |
363 | ||
364 | return; | |
365 | } | |
366 | ||
367 | if (requested) | |
368 | return; | |
369 | ||
370 | if (debug) | |
371 | zlog_debug("%s: slow path. lcb=%p label=%u", | |
372 | __func__, lcb, lcb->label); | |
373 | ||
374 | /* | |
375 | * Slow path: we are out of labels in the local pool, | |
376 | * so remember the request and also get another chunk from | |
377 | * the label manager. | |
378 | * | |
379 | * We track number of outstanding label requests: don't | |
380 | * need to get a chunk for each one. | |
381 | */ | |
382 | ||
383 | struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO, | |
384 | sizeof(struct lp_fifo)); | |
385 | ||
386 | lf->lcb = *lcb; | |
992dd67e | 387 | /* if this is a LU request, lock node before queueing */ |
ea63ff6b EDP |
388 | check_bgp_lu_cb_lock(lcb); |
389 | ||
41397f2e | 390 | lp_fifo_add_tail(&lp->requests, lf); |
955bfd98 | 391 | |
41397f2e | 392 | if (lp_fifo_count(&lp->requests) > lp->pending_count) { |
ea63ff6b | 393 | if (!zclient || zclient->sock < 0) |
955bfd98 | 394 | return; |
7cfdb485 DS |
395 | if (zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE, |
396 | MPLS_LABEL_BASE_ANY) | |
3466e2a1 | 397 | != ZCLIENT_SEND_FAILURE) |
ea63ff6b | 398 | lp->pending_count += LP_CHUNK_SIZE; |
955bfd98 PZ |
399 | } |
400 | } | |
401 | ||
402 | void bgp_lp_release( | |
403 | int type, | |
404 | void *labelid, | |
405 | mpls_label_t label) | |
406 | { | |
407 | struct lp_lcb *lcb; | |
408 | ||
409 | if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) { | |
410 | if (label == lcb->label && type == lcb->type) { | |
411 | uintptr_t lbl = label; | |
412 | ||
413 | /* no longer in use */ | |
414 | skiplist_delete(lp->inuse, (void *)lbl, NULL); | |
415 | ||
416 | /* no longer requested */ | |
417 | skiplist_delete(lp->ledger, labelid, NULL); | |
418 | } | |
419 | } | |
420 | } | |
421 | ||
422 | /* | |
423 | * zebra response giving us a chunk of labels | |
424 | */ | |
425 | void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last) | |
426 | { | |
427 | struct lp_chunk *chunk; | |
428 | int debug = BGP_DEBUG(labelpool, LABELPOOL); | |
429 | struct lp_fifo *lf; | |
430 | ||
431 | if (last < first) { | |
e50f7cfd | 432 | flog_err(EC_BGP_LABEL, |
1c50c1c0 QY |
433 | "%s: zebra label chunk invalid: first=%u, last=%u", |
434 | __func__, first, last); | |
955bfd98 PZ |
435 | return; |
436 | } | |
437 | ||
438 | chunk = XCALLOC(MTYPE_BGP_LABEL_CHUNK, sizeof(struct lp_chunk)); | |
439 | ||
440 | chunk->first = first; | |
441 | chunk->last = last; | |
442 | ||
443 | listnode_add(lp->chunks, chunk); | |
444 | ||
445 | lp->pending_count -= (last - first + 1); | |
446 | ||
447 | if (debug) { | |
41397f2e DL |
448 | zlog_debug("%s: %zu pending requests", __func__, |
449 | lp_fifo_count(&lp->requests)); | |
955bfd98 PZ |
450 | } |
451 | ||
41397f2e | 452 | while ((lf = lp_fifo_first(&lp->requests))) { |
955bfd98 PZ |
453 | |
454 | struct lp_lcb *lcb; | |
455 | void *labelid = lf->lcb.labelid; | |
456 | ||
457 | if (skiplist_search(lp->ledger, labelid, (void **)&lcb)) { | |
458 | /* request no longer in effect */ | |
459 | ||
460 | if (debug) { | |
461 | zlog_debug("%s: labelid %p: request no longer in effect", | |
462 | __func__, labelid); | |
463 | } | |
992dd67e | 464 | /* if this was a BGP_LU request, unlock node |
331bd0eb PR |
465 | */ |
466 | check_bgp_lu_cb_unlock(lcb); | |
955bfd98 PZ |
467 | goto finishedrequest; |
468 | } | |
469 | ||
470 | /* have LCB */ | |
471 | if (lcb->label != MPLS_LABEL_NONE) { | |
472 | /* request already has a label */ | |
473 | if (debug) { | |
474 | zlog_debug("%s: labelid %p: request already has a label: %u=0x%x, lcb=%p", | |
475 | __func__, labelid, | |
476 | lcb->label, lcb->label, lcb); | |
477 | } | |
992dd67e | 478 | /* if this was a BGP_LU request, unlock node |
ea63ff6b EDP |
479 | */ |
480 | check_bgp_lu_cb_unlock(lcb); | |
481 | ||
955bfd98 PZ |
482 | goto finishedrequest; |
483 | } | |
484 | ||
485 | lcb->label = get_label_from_pool(lcb->labelid); | |
486 | ||
487 | if (lcb->label == MPLS_LABEL_NONE) { | |
488 | /* | |
489 | * Out of labels in local pool, await next chunk | |
490 | */ | |
491 | if (debug) { | |
492 | zlog_debug("%s: out of labels, await more", | |
493 | __func__); | |
494 | } | |
495 | break; | |
496 | } | |
497 | ||
498 | /* | |
499 | * we filled the request from local pool. | |
500 | * Enqueue response work item with new label. | |
501 | */ | |
502 | struct lp_cbq_item *q = XCALLOC(MTYPE_BGP_LABEL_CBQ, | |
503 | sizeof(struct lp_cbq_item)); | |
504 | ||
505 | q->cbfunc = lcb->cbfunc; | |
506 | q->type = lcb->type; | |
507 | q->label = lcb->label; | |
508 | q->labelid = lcb->labelid; | |
509 | q->allocated = true; | |
510 | ||
511 | if (debug) | |
512 | zlog_debug("%s: assigning label %u to labelid %p", | |
513 | __func__, q->label, q->labelid); | |
514 | ||
515 | work_queue_add(lp->callback_q, q); | |
516 | ||
517 | finishedrequest: | |
41397f2e | 518 | lp_fifo_del(&lp->requests, lf); |
955bfd98 PZ |
519 | XFREE(MTYPE_BGP_LABEL_FIFO, lf); |
520 | } | |
521 | } | |
522 | ||
523 | /* | |
524 | * continue using allocated labels until zebra returns | |
525 | */ | |
526 | void bgp_lp_event_zebra_down(void) | |
527 | { | |
528 | /* rats. */ | |
529 | } | |
530 | ||
531 | /* | |
532 | * Inform owners of previously-allocated labels that their labels | |
533 | * are not valid. Request chunk from zebra large enough to satisfy | |
534 | * previously-allocated labels plus any outstanding requests. | |
535 | */ | |
536 | void bgp_lp_event_zebra_up(void) | |
537 | { | |
538 | int labels_needed; | |
539 | int chunks_needed; | |
540 | void *labelid; | |
541 | struct lp_lcb *lcb; | |
f533be73 | 542 | int lm_init_ok; |
955bfd98 | 543 | |
e3ea6503 | 544 | lp->reconnect_count++; |
955bfd98 PZ |
545 | /* |
546 | * Get label chunk allocation request dispatched to zebra | |
547 | */ | |
41397f2e | 548 | labels_needed = lp_fifo_count(&lp->requests) + |
955bfd98 PZ |
549 | skiplist_count(lp->inuse); |
550 | ||
551 | /* round up */ | |
552 | chunks_needed = (labels_needed / LP_CHUNK_SIZE) + 1; | |
553 | labels_needed = chunks_needed * LP_CHUNK_SIZE; | |
554 | ||
f533be73 | 555 | lm_init_ok = lm_label_manager_connect(zclient, 1) == 0; |
556 | ||
ea63ff6b | 557 | if (!lm_init_ok) { |
f533be73 | 558 | zlog_err("%s: label manager connection error", __func__); |
ea63ff6b EDP |
559 | return; |
560 | } | |
f533be73 | 561 | |
0e3b6a92 EDP |
562 | zclient_send_get_label_chunk(zclient, 0, labels_needed, |
563 | MPLS_LABEL_BASE_ANY); | |
955bfd98 PZ |
564 | lp->pending_count = labels_needed; |
565 | ||
566 | /* | |
567 | * Invalidate current list of chunks | |
568 | */ | |
569 | list_delete_all_node(lp->chunks); | |
570 | ||
571 | /* | |
572 | * Invalidate any existing labels and requeue them as requests | |
573 | */ | |
574 | while (!skiplist_first(lp->inuse, NULL, &labelid)) { | |
575 | ||
576 | /* | |
577 | * Get LCB | |
578 | */ | |
579 | if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) { | |
580 | ||
581 | if (lcb->label != MPLS_LABEL_NONE) { | |
582 | /* | |
583 | * invalidate | |
584 | */ | |
585 | struct lp_cbq_item *q; | |
586 | ||
587 | q = XCALLOC(MTYPE_BGP_LABEL_CBQ, | |
588 | sizeof(struct lp_cbq_item)); | |
589 | q->cbfunc = lcb->cbfunc; | |
590 | q->type = lcb->type; | |
591 | q->label = lcb->label; | |
592 | q->labelid = lcb->labelid; | |
593 | q->allocated = false; | |
ea63ff6b | 594 | check_bgp_lu_cb_lock(lcb); |
955bfd98 PZ |
595 | work_queue_add(lp->callback_q, q); |
596 | ||
597 | lcb->label = MPLS_LABEL_NONE; | |
598 | } | |
599 | ||
600 | /* | |
601 | * request queue | |
602 | */ | |
603 | struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO, | |
604 | sizeof(struct lp_fifo)); | |
605 | ||
606 | lf->lcb = *lcb; | |
ea63ff6b | 607 | check_bgp_lu_cb_lock(lcb); |
41397f2e | 608 | lp_fifo_add_tail(&lp->requests, lf); |
955bfd98 PZ |
609 | } |
610 | ||
611 | skiplist_delete_first(lp->inuse); | |
612 | } | |
613 | } | |
e3ea6503 PR |
614 | |
615 | DEFUN(show_bgp_labelpool_summary, show_bgp_labelpool_summary_cmd, | |
616 | "show bgp labelpool summary [json]", | |
617 | SHOW_STR BGP_STR | |
618 | "BGP Labelpool information\n" | |
619 | "BGP Labelpool summary\n" JSON_STR) | |
620 | { | |
621 | bool uj = use_json(argc, argv); | |
622 | json_object *json = NULL; | |
623 | ||
624 | if (!lp) { | |
625 | if (uj) | |
626 | vty_out(vty, "{}\n"); | |
627 | else | |
628 | vty_out(vty, "No existing BGP labelpool\n"); | |
629 | return (CMD_WARNING); | |
630 | } | |
631 | ||
632 | if (uj) { | |
633 | json = json_object_new_object(); | |
634 | json_object_int_add(json, "Ledger", skiplist_count(lp->ledger)); | |
635 | json_object_int_add(json, "InUse", skiplist_count(lp->inuse)); | |
636 | json_object_int_add(json, "Requests", | |
637 | lp_fifo_count(&lp->requests)); | |
638 | json_object_int_add(json, "LabelChunks", listcount(lp->chunks)); | |
639 | json_object_int_add(json, "Pending", lp->pending_count); | |
640 | json_object_int_add(json, "Reconnects", lp->reconnect_count); | |
641 | vty_out(vty, "%s\n", | |
642 | json_object_to_json_string_ext( | |
643 | json, JSON_C_TO_STRING_PRETTY)); | |
644 | json_object_free(json); | |
645 | } else { | |
646 | vty_out(vty, "Labelpool Summary\n"); | |
647 | vty_out(vty, "-----------------\n"); | |
648 | vty_out(vty, "%-13s %d\n", | |
649 | "Ledger:", skiplist_count(lp->ledger)); | |
650 | vty_out(vty, "%-13s %d\n", "InUse:", skiplist_count(lp->inuse)); | |
651 | vty_out(vty, "%-13s %zu\n", | |
652 | "Requests:", lp_fifo_count(&lp->requests)); | |
653 | vty_out(vty, "%-13s %d\n", | |
654 | "LabelChunks:", listcount(lp->chunks)); | |
655 | vty_out(vty, "%-13s %d\n", "Pending:", lp->pending_count); | |
656 | vty_out(vty, "%-13s %d\n", "Reconnects:", lp->reconnect_count); | |
657 | } | |
658 | return CMD_SUCCESS; | |
659 | } | |
660 | ||
661 | DEFUN(show_bgp_labelpool_ledger, show_bgp_labelpool_ledger_cmd, | |
662 | "show bgp labelpool ledger [json]", | |
663 | SHOW_STR BGP_STR | |
664 | "BGP Labelpool information\n" | |
665 | "BGP Labelpool ledger\n" JSON_STR) | |
666 | { | |
667 | bool uj = use_json(argc, argv); | |
668 | json_object *json = NULL, *json_elem = NULL; | |
669 | struct lp_lcb *lcb = NULL; | |
992dd67e | 670 | struct bgp_dest *dest; |
e3ea6503 PR |
671 | void *cursor = NULL; |
672 | const struct prefix *p; | |
673 | int rc, count; | |
674 | ||
675 | if (!lp) { | |
676 | if (uj) | |
677 | vty_out(vty, "{}\n"); | |
678 | else | |
679 | vty_out(vty, "No existing BGP labelpool\n"); | |
680 | return (CMD_WARNING); | |
681 | } | |
682 | ||
683 | if (uj) { | |
684 | count = skiplist_count(lp->ledger); | |
685 | if (!count) { | |
686 | vty_out(vty, "{}\n"); | |
687 | return CMD_SUCCESS; | |
688 | } | |
689 | json = json_object_new_array(); | |
690 | } else { | |
691 | vty_out(vty, "Prefix Label\n"); | |
692 | vty_out(vty, "---------------------------\n"); | |
693 | } | |
694 | ||
992dd67e | 695 | for (rc = skiplist_next(lp->ledger, (void **)&dest, (void **)&lcb, |
e3ea6503 | 696 | &cursor); |
992dd67e | 697 | !rc; rc = skiplist_next(lp->ledger, (void **)&dest, (void **)&lcb, |
e3ea6503 PR |
698 | &cursor)) { |
699 | if (uj) { | |
700 | json_elem = json_object_new_object(); | |
701 | json_object_array_add(json, json_elem); | |
702 | } | |
703 | switch (lcb->type) { | |
704 | case LP_TYPE_BGP_LU: | |
992dd67e | 705 | if (!CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) |
e3ea6503 PR |
706 | if (uj) { |
707 | json_object_string_add( | |
708 | json_elem, "prefix", "INVALID"); | |
709 | json_object_int_add(json_elem, "label", | |
710 | lcb->label); | |
711 | } else | |
712 | vty_out(vty, "%-18s %u\n", | |
713 | "INVALID", lcb->label); | |
714 | else { | |
715 | char buf[PREFIX2STR_BUFFER]; | |
992dd67e | 716 | p = bgp_dest_get_prefix(dest); |
e3ea6503 PR |
717 | prefix2str(p, buf, sizeof(buf)); |
718 | if (uj) { | |
719 | json_object_string_add(json_elem, | |
720 | "prefix", buf); | |
721 | json_object_int_add(json_elem, "label", | |
722 | lcb->label); | |
723 | } else | |
724 | vty_out(vty, "%-18s %u\n", buf, | |
725 | lcb->label); | |
726 | } | |
727 | break; | |
728 | case LP_TYPE_VRF: | |
729 | if (uj) { | |
730 | json_object_string_add(json_elem, "prefix", | |
731 | "VRF"); | |
732 | json_object_int_add(json_elem, "label", | |
733 | lcb->label); | |
734 | } else | |
735 | vty_out(vty, "%-18s %u\n", "VRF", | |
736 | lcb->label); | |
737 | ||
738 | break; | |
739 | } | |
740 | } | |
741 | if (uj) { | |
742 | vty_out(vty, "%s\n", | |
743 | json_object_to_json_string_ext( | |
744 | json, JSON_C_TO_STRING_PRETTY)); | |
745 | json_object_free(json); | |
746 | } | |
747 | return CMD_SUCCESS; | |
748 | } | |
749 | ||
750 | DEFUN(show_bgp_labelpool_inuse, show_bgp_labelpool_inuse_cmd, | |
751 | "show bgp labelpool inuse [json]", | |
752 | SHOW_STR BGP_STR | |
753 | "BGP Labelpool information\n" | |
754 | "BGP Labelpool inuse\n" JSON_STR) | |
755 | { | |
756 | bool uj = use_json(argc, argv); | |
757 | json_object *json = NULL, *json_elem = NULL; | |
992dd67e | 758 | struct bgp_dest *dest; |
e3ea6503 PR |
759 | mpls_label_t label; |
760 | struct lp_lcb *lcb; | |
761 | void *cursor = NULL; | |
762 | const struct prefix *p; | |
763 | int rc, count; | |
764 | ||
765 | if (!lp) { | |
766 | vty_out(vty, "No existing BGP labelpool\n"); | |
767 | return (CMD_WARNING); | |
768 | } | |
769 | if (!lp) { | |
770 | if (uj) | |
771 | vty_out(vty, "{}\n"); | |
772 | else | |
773 | vty_out(vty, "No existing BGP labelpool\n"); | |
774 | return (CMD_WARNING); | |
775 | } | |
776 | ||
777 | if (uj) { | |
778 | count = skiplist_count(lp->inuse); | |
779 | if (!count) { | |
780 | vty_out(vty, "{}\n"); | |
781 | return CMD_SUCCESS; | |
782 | } | |
783 | json = json_object_new_array(); | |
784 | } else { | |
785 | vty_out(vty, "Prefix Label\n"); | |
786 | vty_out(vty, "---------------------------\n"); | |
787 | } | |
992dd67e | 788 | for (rc = skiplist_next(lp->inuse, (void **)&label, (void **)&dest, |
e3ea6503 | 789 | &cursor); |
992dd67e PR |
790 | !rc; rc = skiplist_next(lp->ledger, (void **)&label, |
791 | (void **)&dest, &cursor)) { | |
792 | if (skiplist_search(lp->ledger, dest, (void **)&lcb)) | |
e3ea6503 PR |
793 | continue; |
794 | ||
795 | if (uj) { | |
796 | json_elem = json_object_new_object(); | |
797 | json_object_array_add(json, json_elem); | |
798 | } | |
799 | ||
800 | switch (lcb->type) { | |
801 | case LP_TYPE_BGP_LU: | |
992dd67e | 802 | if (!CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) |
e3ea6503 PR |
803 | if (uj) { |
804 | json_object_string_add( | |
805 | json_elem, "prefix", "INVALID"); | |
806 | json_object_int_add(json_elem, "label", | |
807 | label); | |
808 | } else | |
809 | vty_out(vty, "INVALID %u\n", | |
810 | label); | |
811 | else { | |
812 | char buf[PREFIX2STR_BUFFER]; | |
992dd67e | 813 | p = bgp_dest_get_prefix(dest); |
e3ea6503 PR |
814 | prefix2str(p, buf, sizeof(buf)); |
815 | if (uj) { | |
816 | json_object_string_add(json_elem, | |
817 | "prefix", buf); | |
818 | json_object_int_add(json_elem, "label", | |
819 | label); | |
820 | } else | |
821 | vty_out(vty, "%-18s %u\n", buf, | |
822 | label); | |
823 | } | |
824 | break; | |
825 | case LP_TYPE_VRF: | |
826 | if (uj) { | |
827 | json_object_string_add(json_elem, "prefix", | |
828 | "VRF"); | |
829 | json_object_int_add(json_elem, "label", label); | |
830 | } else | |
831 | vty_out(vty, "%-18s %u\n", "VRF", | |
832 | label); | |
833 | break; | |
834 | } | |
835 | } | |
836 | if (uj) { | |
837 | vty_out(vty, "%s\n", | |
838 | json_object_to_json_string_ext( | |
839 | json, JSON_C_TO_STRING_PRETTY)); | |
840 | json_object_free(json); | |
841 | } | |
842 | return CMD_SUCCESS; | |
843 | } | |
844 | ||
845 | DEFUN(show_bgp_labelpool_requests, show_bgp_labelpool_requests_cmd, | |
846 | "show bgp labelpool requests [json]", | |
847 | SHOW_STR BGP_STR | |
848 | "BGP Labelpool information\n" | |
849 | "BGP Labelpool requests\n" JSON_STR) | |
850 | { | |
851 | bool uj = use_json(argc, argv); | |
852 | json_object *json = NULL, *json_elem = NULL; | |
992dd67e | 853 | struct bgp_dest *dest; |
e3ea6503 PR |
854 | const struct prefix *p; |
855 | char buf[PREFIX2STR_BUFFER]; | |
856 | struct lp_fifo *item, *next; | |
857 | int count; | |
858 | ||
859 | if (!lp) { | |
860 | if (uj) | |
861 | vty_out(vty, "{}\n"); | |
862 | else | |
863 | vty_out(vty, "No existing BGP labelpool\n"); | |
864 | return (CMD_WARNING); | |
865 | } | |
866 | ||
867 | if (uj) { | |
868 | count = lp_fifo_count(&lp->requests); | |
869 | if (!count) { | |
870 | vty_out(vty, "{}\n"); | |
871 | return CMD_SUCCESS; | |
872 | } | |
873 | json = json_object_new_array(); | |
874 | } else { | |
875 | vty_out(vty, "Prefix \n"); | |
876 | vty_out(vty, "----------------\n"); | |
877 | } | |
878 | ||
879 | for (item = lp_fifo_first(&lp->requests); item; item = next) { | |
880 | next = lp_fifo_next_safe(&lp->requests, item); | |
992dd67e | 881 | dest = item->lcb.labelid; |
e3ea6503 PR |
882 | if (uj) { |
883 | json_elem = json_object_new_object(); | |
884 | json_object_array_add(json, json_elem); | |
885 | } | |
886 | switch (item->lcb.type) { | |
887 | case LP_TYPE_BGP_LU: | |
992dd67e PR |
888 | if (!CHECK_FLAG(dest->flags, |
889 | BGP_NODE_LABEL_REQUESTED)) { | |
e3ea6503 PR |
890 | if (uj) |
891 | json_object_string_add( | |
892 | json_elem, "prefix", "INVALID"); | |
893 | else | |
894 | vty_out(vty, "INVALID\n"); | |
895 | } else { | |
992dd67e | 896 | p = bgp_dest_get_prefix(dest); |
e3ea6503 PR |
897 | prefix2str(p, buf, sizeof(buf)); |
898 | if (uj) | |
899 | json_object_string_add(json_elem, | |
900 | "prefix", buf); | |
901 | else | |
902 | vty_out(vty, "%-18s\n", buf); | |
903 | } | |
904 | break; | |
905 | case LP_TYPE_VRF: | |
906 | if (uj) | |
907 | json_object_string_add(json_elem, "prefix", | |
908 | "VRF"); | |
909 | else | |
910 | vty_out(vty, "VRF\n"); | |
911 | break; | |
912 | } | |
913 | } | |
914 | if (uj) { | |
915 | vty_out(vty, "%s\n", | |
916 | json_object_to_json_string_ext( | |
917 | json, JSON_C_TO_STRING_PRETTY)); | |
918 | json_object_free(json); | |
919 | } | |
920 | return CMD_SUCCESS; | |
921 | } | |
922 | ||
923 | DEFUN(show_bgp_labelpool_chunks, show_bgp_labelpool_chunks_cmd, | |
924 | "show bgp labelpool chunks [json]", | |
925 | SHOW_STR BGP_STR | |
926 | "BGP Labelpool information\n" | |
927 | "BGP Labelpool chunks\n" JSON_STR) | |
928 | { | |
929 | bool uj = use_json(argc, argv); | |
930 | json_object *json = NULL, *json_elem; | |
931 | struct listnode *node; | |
932 | struct lp_chunk *chunk; | |
933 | int count; | |
934 | ||
935 | if (!lp) { | |
936 | if (uj) | |
937 | vty_out(vty, "{}\n"); | |
938 | else | |
939 | vty_out(vty, "No existing BGP labelpool\n"); | |
940 | return (CMD_WARNING); | |
941 | } | |
942 | ||
943 | if (uj) { | |
944 | count = listcount(lp->chunks); | |
945 | if (!count) { | |
946 | vty_out(vty, "{}\n"); | |
947 | return CMD_SUCCESS; | |
948 | } | |
949 | json = json_object_new_array(); | |
950 | } else { | |
951 | vty_out(vty, "First Last\n"); | |
952 | vty_out(vty, "--------------\n"); | |
953 | } | |
954 | ||
955 | for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) { | |
956 | if (uj) { | |
957 | json_elem = json_object_new_object(); | |
958 | json_object_array_add(json, json_elem); | |
959 | json_object_int_add(json_elem, "first", chunk->first); | |
960 | json_object_int_add(json_elem, "last", chunk->last); | |
961 | } else | |
962 | vty_out(vty, "%-10u %-10u\n", chunk->first, | |
963 | chunk->last); | |
964 | } | |
965 | if (uj) { | |
966 | vty_out(vty, "%s\n", | |
967 | json_object_to_json_string_ext( | |
968 | json, JSON_C_TO_STRING_PRETTY)); | |
969 | json_object_free(json); | |
970 | } | |
971 | return CMD_SUCCESS; | |
972 | } | |
973 | ||
974 | void bgp_lp_vty_init(void) | |
975 | { | |
976 | install_element(VIEW_NODE, &show_bgp_labelpool_summary_cmd); | |
977 | install_element(VIEW_NODE, &show_bgp_labelpool_ledger_cmd); | |
978 | install_element(VIEW_NODE, &show_bgp_labelpool_inuse_cmd); | |
979 | install_element(VIEW_NODE, &show_bgp_labelpool_requests_cmd); | |
980 | install_element(VIEW_NODE, &show_bgp_labelpool_chunks_cmd); | |
981 | } |