]>
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 | ||
bf8d3d6a DL |
53 | DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CHUNK, "BGP Label Chunk"); |
54 | DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO item"); | |
55 | DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CB, "BGP Dynamic Label Assignment"); | |
56 | DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CBQ, "BGP Dynamic Label Callback"); | |
955bfd98 | 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 | ||
960b9a53 | 83 | DECLARE_LIST(lp_fifo, struct lp_fifo, fifo); |
41397f2e | 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); | |
75eeda93 | 641 | vty_json(vty, json); |
e3ea6503 PR |
642 | } else { |
643 | vty_out(vty, "Labelpool Summary\n"); | |
644 | vty_out(vty, "-----------------\n"); | |
645 | vty_out(vty, "%-13s %d\n", | |
646 | "Ledger:", skiplist_count(lp->ledger)); | |
647 | vty_out(vty, "%-13s %d\n", "InUse:", skiplist_count(lp->inuse)); | |
648 | vty_out(vty, "%-13s %zu\n", | |
649 | "Requests:", lp_fifo_count(&lp->requests)); | |
650 | vty_out(vty, "%-13s %d\n", | |
651 | "LabelChunks:", listcount(lp->chunks)); | |
652 | vty_out(vty, "%-13s %d\n", "Pending:", lp->pending_count); | |
653 | vty_out(vty, "%-13s %d\n", "Reconnects:", lp->reconnect_count); | |
654 | } | |
655 | return CMD_SUCCESS; | |
656 | } | |
657 | ||
658 | DEFUN(show_bgp_labelpool_ledger, show_bgp_labelpool_ledger_cmd, | |
659 | "show bgp labelpool ledger [json]", | |
660 | SHOW_STR BGP_STR | |
661 | "BGP Labelpool information\n" | |
662 | "BGP Labelpool ledger\n" JSON_STR) | |
663 | { | |
664 | bool uj = use_json(argc, argv); | |
665 | json_object *json = NULL, *json_elem = NULL; | |
666 | struct lp_lcb *lcb = NULL; | |
992dd67e | 667 | struct bgp_dest *dest; |
e3ea6503 PR |
668 | void *cursor = NULL; |
669 | const struct prefix *p; | |
670 | int rc, count; | |
671 | ||
672 | if (!lp) { | |
673 | if (uj) | |
674 | vty_out(vty, "{}\n"); | |
675 | else | |
676 | vty_out(vty, "No existing BGP labelpool\n"); | |
677 | return (CMD_WARNING); | |
678 | } | |
679 | ||
680 | if (uj) { | |
681 | count = skiplist_count(lp->ledger); | |
682 | if (!count) { | |
683 | vty_out(vty, "{}\n"); | |
684 | return CMD_SUCCESS; | |
685 | } | |
686 | json = json_object_new_array(); | |
687 | } else { | |
688 | vty_out(vty, "Prefix Label\n"); | |
689 | vty_out(vty, "---------------------------\n"); | |
690 | } | |
691 | ||
992dd67e | 692 | for (rc = skiplist_next(lp->ledger, (void **)&dest, (void **)&lcb, |
e3ea6503 | 693 | &cursor); |
992dd67e | 694 | !rc; rc = skiplist_next(lp->ledger, (void **)&dest, (void **)&lcb, |
e3ea6503 PR |
695 | &cursor)) { |
696 | if (uj) { | |
697 | json_elem = json_object_new_object(); | |
698 | json_object_array_add(json, json_elem); | |
699 | } | |
700 | switch (lcb->type) { | |
701 | case LP_TYPE_BGP_LU: | |
992dd67e | 702 | if (!CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) |
e3ea6503 PR |
703 | if (uj) { |
704 | json_object_string_add( | |
705 | json_elem, "prefix", "INVALID"); | |
706 | json_object_int_add(json_elem, "label", | |
707 | lcb->label); | |
708 | } else | |
709 | vty_out(vty, "%-18s %u\n", | |
710 | "INVALID", lcb->label); | |
711 | else { | |
712 | char buf[PREFIX2STR_BUFFER]; | |
992dd67e | 713 | p = bgp_dest_get_prefix(dest); |
e3ea6503 PR |
714 | prefix2str(p, buf, sizeof(buf)); |
715 | if (uj) { | |
716 | json_object_string_add(json_elem, | |
717 | "prefix", buf); | |
718 | json_object_int_add(json_elem, "label", | |
719 | lcb->label); | |
720 | } else | |
721 | vty_out(vty, "%-18s %u\n", buf, | |
722 | lcb->label); | |
723 | } | |
724 | break; | |
725 | case LP_TYPE_VRF: | |
726 | if (uj) { | |
727 | json_object_string_add(json_elem, "prefix", | |
728 | "VRF"); | |
729 | json_object_int_add(json_elem, "label", | |
730 | lcb->label); | |
731 | } else | |
732 | vty_out(vty, "%-18s %u\n", "VRF", | |
733 | lcb->label); | |
734 | ||
735 | break; | |
736 | } | |
737 | } | |
c48349e3 | 738 | if (uj) |
75eeda93 | 739 | vty_json(vty, json); |
e3ea6503 PR |
740 | return CMD_SUCCESS; |
741 | } | |
742 | ||
743 | DEFUN(show_bgp_labelpool_inuse, show_bgp_labelpool_inuse_cmd, | |
744 | "show bgp labelpool inuse [json]", | |
745 | SHOW_STR BGP_STR | |
746 | "BGP Labelpool information\n" | |
747 | "BGP Labelpool inuse\n" JSON_STR) | |
748 | { | |
749 | bool uj = use_json(argc, argv); | |
750 | json_object *json = NULL, *json_elem = NULL; | |
992dd67e | 751 | struct bgp_dest *dest; |
e3ea6503 PR |
752 | mpls_label_t label; |
753 | struct lp_lcb *lcb; | |
754 | void *cursor = NULL; | |
755 | const struct prefix *p; | |
756 | int rc, count; | |
757 | ||
758 | if (!lp) { | |
759 | vty_out(vty, "No existing BGP labelpool\n"); | |
760 | return (CMD_WARNING); | |
761 | } | |
762 | if (!lp) { | |
763 | if (uj) | |
764 | vty_out(vty, "{}\n"); | |
765 | else | |
766 | vty_out(vty, "No existing BGP labelpool\n"); | |
767 | return (CMD_WARNING); | |
768 | } | |
769 | ||
770 | if (uj) { | |
771 | count = skiplist_count(lp->inuse); | |
772 | if (!count) { | |
773 | vty_out(vty, "{}\n"); | |
774 | return CMD_SUCCESS; | |
775 | } | |
776 | json = json_object_new_array(); | |
777 | } else { | |
778 | vty_out(vty, "Prefix Label\n"); | |
779 | vty_out(vty, "---------------------------\n"); | |
780 | } | |
992dd67e | 781 | for (rc = skiplist_next(lp->inuse, (void **)&label, (void **)&dest, |
e3ea6503 | 782 | &cursor); |
992dd67e PR |
783 | !rc; rc = skiplist_next(lp->ledger, (void **)&label, |
784 | (void **)&dest, &cursor)) { | |
785 | if (skiplist_search(lp->ledger, dest, (void **)&lcb)) | |
e3ea6503 PR |
786 | continue; |
787 | ||
788 | if (uj) { | |
789 | json_elem = json_object_new_object(); | |
790 | json_object_array_add(json, json_elem); | |
791 | } | |
792 | ||
793 | switch (lcb->type) { | |
794 | case LP_TYPE_BGP_LU: | |
992dd67e | 795 | if (!CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) |
e3ea6503 PR |
796 | if (uj) { |
797 | json_object_string_add( | |
798 | json_elem, "prefix", "INVALID"); | |
799 | json_object_int_add(json_elem, "label", | |
800 | label); | |
801 | } else | |
802 | vty_out(vty, "INVALID %u\n", | |
803 | label); | |
804 | else { | |
805 | char buf[PREFIX2STR_BUFFER]; | |
992dd67e | 806 | p = bgp_dest_get_prefix(dest); |
e3ea6503 PR |
807 | prefix2str(p, buf, sizeof(buf)); |
808 | if (uj) { | |
809 | json_object_string_add(json_elem, | |
810 | "prefix", buf); | |
811 | json_object_int_add(json_elem, "label", | |
812 | label); | |
813 | } else | |
814 | vty_out(vty, "%-18s %u\n", buf, | |
815 | label); | |
816 | } | |
817 | break; | |
818 | case LP_TYPE_VRF: | |
819 | if (uj) { | |
820 | json_object_string_add(json_elem, "prefix", | |
821 | "VRF"); | |
822 | json_object_int_add(json_elem, "label", label); | |
823 | } else | |
824 | vty_out(vty, "%-18s %u\n", "VRF", | |
825 | label); | |
826 | break; | |
827 | } | |
828 | } | |
c48349e3 | 829 | if (uj) |
75eeda93 | 830 | vty_json(vty, json); |
e3ea6503 PR |
831 | return CMD_SUCCESS; |
832 | } | |
833 | ||
834 | DEFUN(show_bgp_labelpool_requests, show_bgp_labelpool_requests_cmd, | |
835 | "show bgp labelpool requests [json]", | |
836 | SHOW_STR BGP_STR | |
837 | "BGP Labelpool information\n" | |
838 | "BGP Labelpool requests\n" JSON_STR) | |
839 | { | |
840 | bool uj = use_json(argc, argv); | |
841 | json_object *json = NULL, *json_elem = NULL; | |
992dd67e | 842 | struct bgp_dest *dest; |
e3ea6503 PR |
843 | const struct prefix *p; |
844 | char buf[PREFIX2STR_BUFFER]; | |
845 | struct lp_fifo *item, *next; | |
846 | int count; | |
847 | ||
848 | if (!lp) { | |
849 | if (uj) | |
850 | vty_out(vty, "{}\n"); | |
851 | else | |
852 | vty_out(vty, "No existing BGP labelpool\n"); | |
853 | return (CMD_WARNING); | |
854 | } | |
855 | ||
856 | if (uj) { | |
857 | count = lp_fifo_count(&lp->requests); | |
858 | if (!count) { | |
859 | vty_out(vty, "{}\n"); | |
860 | return CMD_SUCCESS; | |
861 | } | |
862 | json = json_object_new_array(); | |
863 | } else { | |
864 | vty_out(vty, "Prefix \n"); | |
865 | vty_out(vty, "----------------\n"); | |
866 | } | |
867 | ||
868 | for (item = lp_fifo_first(&lp->requests); item; item = next) { | |
869 | next = lp_fifo_next_safe(&lp->requests, item); | |
992dd67e | 870 | dest = item->lcb.labelid; |
e3ea6503 PR |
871 | if (uj) { |
872 | json_elem = json_object_new_object(); | |
873 | json_object_array_add(json, json_elem); | |
874 | } | |
875 | switch (item->lcb.type) { | |
876 | case LP_TYPE_BGP_LU: | |
992dd67e PR |
877 | if (!CHECK_FLAG(dest->flags, |
878 | BGP_NODE_LABEL_REQUESTED)) { | |
e3ea6503 PR |
879 | if (uj) |
880 | json_object_string_add( | |
881 | json_elem, "prefix", "INVALID"); | |
882 | else | |
883 | vty_out(vty, "INVALID\n"); | |
884 | } else { | |
992dd67e | 885 | p = bgp_dest_get_prefix(dest); |
e3ea6503 PR |
886 | prefix2str(p, buf, sizeof(buf)); |
887 | if (uj) | |
888 | json_object_string_add(json_elem, | |
889 | "prefix", buf); | |
890 | else | |
891 | vty_out(vty, "%-18s\n", buf); | |
892 | } | |
893 | break; | |
894 | case LP_TYPE_VRF: | |
895 | if (uj) | |
896 | json_object_string_add(json_elem, "prefix", | |
897 | "VRF"); | |
898 | else | |
899 | vty_out(vty, "VRF\n"); | |
900 | break; | |
901 | } | |
902 | } | |
c48349e3 | 903 | if (uj) |
75eeda93 | 904 | vty_json(vty, json); |
e3ea6503 PR |
905 | return CMD_SUCCESS; |
906 | } | |
907 | ||
908 | DEFUN(show_bgp_labelpool_chunks, show_bgp_labelpool_chunks_cmd, | |
909 | "show bgp labelpool chunks [json]", | |
910 | SHOW_STR BGP_STR | |
911 | "BGP Labelpool information\n" | |
912 | "BGP Labelpool chunks\n" JSON_STR) | |
913 | { | |
914 | bool uj = use_json(argc, argv); | |
915 | json_object *json = NULL, *json_elem; | |
916 | struct listnode *node; | |
917 | struct lp_chunk *chunk; | |
918 | int count; | |
919 | ||
920 | if (!lp) { | |
921 | if (uj) | |
922 | vty_out(vty, "{}\n"); | |
923 | else | |
924 | vty_out(vty, "No existing BGP labelpool\n"); | |
925 | return (CMD_WARNING); | |
926 | } | |
927 | ||
928 | if (uj) { | |
929 | count = listcount(lp->chunks); | |
930 | if (!count) { | |
931 | vty_out(vty, "{}\n"); | |
932 | return CMD_SUCCESS; | |
933 | } | |
934 | json = json_object_new_array(); | |
935 | } else { | |
936 | vty_out(vty, "First Last\n"); | |
937 | vty_out(vty, "--------------\n"); | |
938 | } | |
939 | ||
940 | for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) { | |
941 | if (uj) { | |
942 | json_elem = json_object_new_object(); | |
943 | json_object_array_add(json, json_elem); | |
944 | json_object_int_add(json_elem, "first", chunk->first); | |
945 | json_object_int_add(json_elem, "last", chunk->last); | |
946 | } else | |
947 | vty_out(vty, "%-10u %-10u\n", chunk->first, | |
948 | chunk->last); | |
949 | } | |
c48349e3 | 950 | if (uj) |
75eeda93 | 951 | vty_json(vty, json); |
e3ea6503 PR |
952 | return CMD_SUCCESS; |
953 | } | |
954 | ||
955 | void bgp_lp_vty_init(void) | |
956 | { | |
957 | install_element(VIEW_NODE, &show_bgp_labelpool_summary_cmd); | |
958 | install_element(VIEW_NODE, &show_bgp_labelpool_ledger_cmd); | |
959 | install_element(VIEW_NODE, &show_bgp_labelpool_inuse_cmd); | |
960 | install_element(VIEW_NODE, &show_bgp_labelpool_requests_cmd); | |
961 | install_element(VIEW_NODE, &show_bgp_labelpool_chunks_cmd); | |
962 | } |