]> git.proxmox.com Git - mirror_frr.git/blame - zebra/zebra_dplane.c
zebra: start sketching dataplane provider api
[mirror_frr.git] / zebra / zebra_dplane.c
CommitLineData
ea1c14f6
MS
1/*
2 * Zebra dataplane layer.
3 * Copyright (c) 2018 Volta Networks, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
7cdb1a84
MS
20#include "lib/zebra.h"
21#include "lib/memory.h"
22#include "lib/frr_pthread.h"
23#include "lib/queue.h"
24#include "zebra/zebra_memory.h"
25#include "zebra/zserv.h"
26#include "zebra/zebra_dplane.h"
27#include "zebra/rt.h"
28#include "zebra/debug.h"
29
30/* Memory type for context blocks */
31DEFINE_MTYPE(ZEBRA, DP_CTX, "Zebra DPlane Ctx")
b8e0423d 32DEFINE_MTYPE(ZEBRA, DP_PROV, "Zebra DPlane Provider")
7cdb1a84
MS
33
34#ifndef AOK
35# define AOK 0
36#endif
37
38/* Validation value for context blocks */
39const uint32_t DPLANE_CTX_MAGIC = 0xb97a557f;
40
41/* Validation check macro for context blocks */
42/* #define DPLANE_DEBUG 1 */
43
44#ifdef DPLANE_DEBUG
45
46# define DPLANE_CTX_VALID(p) \
47 (assert((p) && ((p)->zd_magic == DPLANE_CTX_MAGIC)))
48
49#else
50
51# define DPLANE_CTX_VALID(p) \
52 if ((p) && ((p)->zd_magic == DPLANE_CTX_MAGIC)) { ; }
53
54#endif /* DPLANE_DEBUG */
55
56/*
57 * The context block used to exchange info about route updates across
58 * the boundary between the zebra main context (and pthread) and the
59 * dataplane layer (and pthread).
60 */
61struct zebra_dplane_ctx_s {
62
63 /* Operation code */
64 dplane_op_e zd_op;
65
66 /* Status on return */
67 enum zebra_dplane_result zd_status;
68
69 /* TODO -- internal/sub-operation status? */
70 enum zebra_dplane_status zd_remote_status;
71 enum zebra_dplane_status zd_kernel_status;
72
73 /* Dest and (optional) source prefixes */
74 struct prefix zd_dest;
75 struct prefix zd_src;
76
77 bool zd_is_update;
78
79 uint32_t zd_seq;
80 uint32_t zd_old_seq;
81 vrf_id_t zd_vrf_id;
82 uint32_t zd_table_id;
83
84 int zd_type;
85 int zd_old_type;
86
87 afi_t zd_afi;
88 safi_t zd_safi;
89
90 route_tag_t zd_tag;
91 route_tag_t zd_old_tag;
92 uint32_t zd_metric;
93 uint16_t zd_instance;
94 uint16_t zd_old_instance;
95
96 uint8_t zd_distance;
97 uint8_t zd_old_distance;
98
99 uint32_t zd_mtu;
100 uint32_t zd_nexthop_mtu;
101
102 /* Namespace info */
103 struct zebra_dplane_info zd_ns_info;
104
105 /* Nexthops */
106 struct nexthop_group zd_ng;
107
b8e0423d
MS
108 /* TODO -- use fixed array of nexthops, to avoid mallocs? */
109
7cdb1a84
MS
110 /* Embedded list linkage */
111 TAILQ_ENTRY(zebra_dplane_ctx_s) zd_q_entries;
112
113 /* Magic validation value */
114 uint32_t zd_magic;
115};
116
117/*
118 * Registration block for one dataplane provider.
119 */
120struct zebra_dplane_provider_s {
121 /* Name */
122 char dp_name[DPLANE_PROVIDER_NAMELEN + 1];
123
124 /* Priority, for ordering among providers */
125 uint8_t dp_priority;
126
127 /* Id value */
128 uint32_t dp_id;
129
130 /* Event pointer for use by the dplane thread */
b8e0423d
MS
131// struct thread *dp_t_event;
132
133 dplane_provider_process_fp dp_fp;
7cdb1a84
MS
134
135 /* Embedded list linkage */
b8e0423d 136 TAILQ_ENTRY(zebra_dplane_provider_s) dp_q_providers;
7cdb1a84
MS
137
138};
139
140/*
141 * Globals
142 */
143static struct zebra_dplane_globals_s {
144 /* Mutex to control access to dataplane components */
145 pthread_mutex_t dg_mutex;
146
147 /* Results callback registered by zebra 'core' */
148 dplane_results_fp dg_results_cb;
149
150 /* Route-update context queue inbound to the dataplane */
151 TAILQ_HEAD(zdg_ctx_q, zebra_dplane_ctx_s) dg_route_ctx_q;
152
153 /* Ordered list of providers */
154 TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider_s) dg_providers_q;
155
b8e0423d
MS
156 /* Counter used to assign ids to providers */
157 uint32_t dg_provider_id;
158
7cdb1a84
MS
159 /* Event-delivery context 'master' for the dplane */
160 struct thread_master *dg_master;
161
162 /* Event/'thread' pointer for queued updates */
163 struct thread *dg_t_update;
164
165} zdplane_g;
166
167/*
168 * Lock and unlock for interactions with the zebra 'core'
169 */
170#define DPLANE_LOCK() pthread_mutex_lock(&zdplane_g.dg_mutex)
171
172#define DPLANE_UNLOCK() pthread_mutex_unlock(&zdplane_g.dg_mutex)
173
174/* Prototypes */
175static int dplane_route_process(struct thread *event);
176
177/*
178 * Public APIs
179 */
180
181/*
b8e0423d 182 * Allocate a dataplane update context
7cdb1a84
MS
183 */
184dplane_ctx_h dplane_ctx_alloc(void)
185{
186 struct zebra_dplane_ctx_s *p;
187
b8e0423d
MS
188 /* TODO -- just alloc'ing memory, but would like to maintain
189 * a pool
190 */
191
7cdb1a84
MS
192 p = XCALLOC(MTYPE_DP_CTX, sizeof(struct zebra_dplane_ctx_s));
193 if (p) {
194 p->zd_magic = DPLANE_CTX_MAGIC;
195 }
196
197 return (p);
198}
199
200/*
b8e0423d 201 * Free a dataplane results context.
7cdb1a84
MS
202 */
203static void dplane_ctx_free(dplane_ctx_h *pctx)
204{
205 if (pctx) {
206 DPLANE_CTX_VALID(*pctx);
207
b8e0423d
MS
208 /* TODO -- just freeing memory, but would like to maintain
209 * a pool
210 */
211
7cdb1a84
MS
212 /* Free embedded nexthops */
213 if ((*pctx)->zd_ng.nexthop) {
214 /* This deals with recursive nexthops too */
215 nexthops_free((*pctx)->zd_ng.nexthop);
216 }
217
218 /* Clear validation value */
219 (*pctx)->zd_magic = 0;
220
221 XFREE(MTYPE_DP_CTX, *pctx);
222 *pctx = NULL;
223 }
224}
225
226/*
227 * Return a context block to the dplane module after processing
228 */
229void dplane_ctx_fini(dplane_ctx_h *pctx)
230{
231 /* TODO -- enqueue for next provider; for now, just free */
232 dplane_ctx_free(pctx);
233}
234
235/* Enqueue a context block */
236void dplane_ctx_enqueue_tail(struct dplane_ctx_q_s *q, dplane_ctx_h ctx)
237{
238 TAILQ_INSERT_TAIL(q, ctx, zd_q_entries);
239}
240
241/* Dequeue a context block from the head of a list */
242void dplane_ctx_dequeue(struct dplane_ctx_q_s *q, dplane_ctx_h *ctxp)
243{
244 dplane_ctx_h ctx = TAILQ_FIRST(q);
245 if (ctx) {
246 TAILQ_REMOVE(q, ctx, zd_q_entries);
247 }
248
249 *ctxp = ctx;
250}
251
252/*
253 * Accessors for information from the context object
254 */
255enum zebra_dplane_result dplane_ctx_get_status(const dplane_ctx_h ctx)
256{
257 DPLANE_CTX_VALID(ctx);
258
259 return (ctx->zd_status);
260}
261
262dplane_op_e dplane_ctx_get_op(const dplane_ctx_h ctx)
263{
264 DPLANE_CTX_VALID(ctx);
265
266 return (ctx->zd_op);
267}
268
269const char *dplane_op2str(dplane_op_e op)
270{
271 const char *ret = "UNKNOWN";
272
273 switch(op) {
274 case DPLANE_OP_NONE:
275 ret = "NONE";
276 break;
277
278 /* Route update */
279 case DPLANE_OP_ROUTE_INSTALL:
280 ret = "ROUTE_INSTALL";
281 break;
282 case DPLANE_OP_ROUTE_UPDATE:
283 ret = "ROUTE_UPDATE";
284 break;
285 case DPLANE_OP_ROUTE_DELETE:
286 ret = "ROUTE_DELETE";
287 break;
288
289 };
290
291 return (ret);
292}
293
294const struct prefix *dplane_ctx_get_dest(const dplane_ctx_h ctx)
295{
296 DPLANE_CTX_VALID(ctx);
297
298 return (&(ctx->zd_dest));
299}
300
301/* Source prefix is a little special - use convention like prefix-len of zero
302 * and all-zeroes address means "no src prefix"? or ... return NULL in that case?
303 */
304const struct prefix *dplane_ctx_get_src(const dplane_ctx_h ctx)
305{
306 DPLANE_CTX_VALID(ctx);
307
308 if (ctx->zd_src.prefixlen == 0 &&
309 IN6_IS_ADDR_UNSPECIFIED(&(ctx->zd_src.u.prefix6))) {
310 return (NULL);
311 } else {
312 return (&(ctx->zd_src));
313 }
314}
315
316bool dplane_ctx_is_update(const dplane_ctx_h ctx)
317{
318 DPLANE_CTX_VALID(ctx);
319
320 return (ctx->zd_is_update);
321}
322
323uint32_t dplane_ctx_get_seq(const dplane_ctx_h ctx)
324{
325 DPLANE_CTX_VALID(ctx);
326
327 return (ctx->zd_seq);
328}
329
330uint32_t dplane_ctx_get_old_seq(const dplane_ctx_h ctx)
331{
332 DPLANE_CTX_VALID(ctx);
333
334 return (ctx->zd_old_seq);
335}
336
337vrf_id_t dplane_ctx_get_vrf(const dplane_ctx_h ctx)
338{
339 DPLANE_CTX_VALID(ctx);
340
341 return (ctx->zd_vrf_id);
342}
343
344int dplane_ctx_get_type(const dplane_ctx_h ctx)
345{
346 DPLANE_CTX_VALID(ctx);
347
348 return (ctx->zd_type);
349}
350
351int dplane_ctx_get_old_type(const dplane_ctx_h ctx)
352{
353 DPLANE_CTX_VALID(ctx);
354
355 return (ctx->zd_old_type);
356}
357
358afi_t dplane_ctx_get_afi(const dplane_ctx_h ctx)
359{
360 DPLANE_CTX_VALID(ctx);
361
362 return (ctx->zd_afi);
363}
364
365safi_t dplane_ctx_get_safi(const dplane_ctx_h ctx)
366{
367 DPLANE_CTX_VALID(ctx);
368
369 return (ctx->zd_safi);
370}
371
372uint32_t dplane_ctx_get_table(const dplane_ctx_h ctx)
373{
374 DPLANE_CTX_VALID(ctx);
375
376 return (ctx->zd_table_id);
377}
378
379route_tag_t dplane_ctx_get_tag(const dplane_ctx_h ctx)
380{
381 DPLANE_CTX_VALID(ctx);
382
383 return (ctx->zd_tag);
384}
385
386route_tag_t dplane_ctx_get_old_tag(const dplane_ctx_h ctx)
387{
388 DPLANE_CTX_VALID(ctx);
389
390 return (ctx->zd_old_tag);
391}
392
393uint16_t dplane_ctx_get_instance(const dplane_ctx_h ctx)
394{
395 DPLANE_CTX_VALID(ctx);
396
397 return (ctx->zd_instance);
398}
399
400uint16_t dplane_ctx_get_old_instance(const dplane_ctx_h ctx)
401{
402 DPLANE_CTX_VALID(ctx);
403
404 return (ctx->zd_instance);
405}
406
407uint32_t dplane_ctx_get_metric(const dplane_ctx_h ctx)
408{
409 DPLANE_CTX_VALID(ctx);
410
411 return (ctx->zd_metric);
412}
413
414uint32_t dplane_ctx_get_mtu(const dplane_ctx_h ctx)
415{
416 DPLANE_CTX_VALID(ctx);
417
418 return (ctx->zd_mtu);
419}
420
421uint32_t dplane_ctx_get_nh_mtu(const dplane_ctx_h ctx)
422{
423 DPLANE_CTX_VALID(ctx);
424
425 return (ctx->zd_nexthop_mtu);
426}
427
428uint8_t dplane_ctx_get_distance(const dplane_ctx_h ctx)
429{
430 DPLANE_CTX_VALID(ctx);
431
432 return (ctx->zd_distance);
433}
434
435uint8_t dplane_ctx_get_old_distance(const dplane_ctx_h ctx)
436{
437 DPLANE_CTX_VALID(ctx);
438
439 return (ctx->zd_old_distance);
440}
441
442const struct nexthop_group *dplane_ctx_get_ng(const dplane_ctx_h ctx)
443{
444 DPLANE_CTX_VALID(ctx);
445
446 return (&(ctx->zd_ng));
447}
448
449const struct zebra_dplane_info *dplane_ctx_get_ns(const dplane_ctx_h ctx)
450{
451 DPLANE_CTX_VALID(ctx);
452
453 return (&(ctx->zd_ns_info));
454}
455
456/*
457 * End of dplane context accessors
458 */
459
460/*
461 * Initialize a context block for a route update from zebra data structs.
462 */
463static int dplane_ctx_route_init(dplane_ctx_h ctx,
464 dplane_op_e op,
465 struct route_node *rn,
466 struct route_entry *re)
467{
468 int ret = EINVAL;
469 const struct route_table *table = NULL;
470 const rib_table_info_t *info;
471 const struct prefix *p, *src_p;
472 struct zebra_ns *zns;
473 struct zebra_vrf *zvrf;
474
475 if (!ctx || !rn || !re) {
476 goto done;
477 }
478
479 ctx->zd_op = op;
480
481 ctx->zd_type = re->type;
ae299285 482 ctx->zd_old_type = re->type;
7cdb1a84
MS
483
484 /* Prefixes: dest, and optional source */
485 srcdest_rnode_prefixes(rn, &p, &src_p);
486
487 prefix_copy(&(ctx->zd_dest), p);
488
489 if (src_p) {
490 prefix_copy(&(ctx->zd_src), src_p);
491 } else {
492 memset(&(ctx->zd_src), 0, sizeof(ctx->zd_src));
493 }
494
495 ctx->zd_table_id = re->table;
496
497 ctx->zd_metric = re->metric;
498 ctx->zd_vrf_id = re->vrf_id;
499 ctx->zd_mtu = re->mtu;
500 ctx->zd_nexthop_mtu = re->nexthop_mtu;
501 ctx->zd_instance = re->instance;
502 ctx->zd_tag = re->tag;
ae299285 503 ctx->zd_old_tag = re->tag;
7cdb1a84
MS
504 ctx->zd_distance = re->distance;
505
506 table = srcdest_rnode_table(rn);
507 info = table->info;
508
509 ctx->zd_afi = info->afi;
510 ctx->zd_safi = info->safi;
511
512 /* Extract ns info - can't use pointers to 'core' structs */
513 zvrf = vrf_info_lookup(re->vrf_id);
514 zns = zvrf->zns;
515
516 zebra_dplane_info_from_zns(&(ctx->zd_ns_info), zns, true /*is_cmd*/);
517
518#if defined(HAVE_NETLINK)
519 /* Increment message counter after copying to context struct - may need
520 * two messages in some 'update' cases.
521 */
522 if (op == DPLANE_OP_ROUTE_UPDATE) {
523 zns->netlink_cmd.seq += 2;
524 } else {
525 zns->netlink_cmd.seq++;
526 }
527#endif /* NETLINK*/
528
529 /* Copy nexthops; recursive info is included too */
530 copy_nexthops(&(ctx->zd_ng.nexthop), re->ng.nexthop, NULL);
531
b8e0423d
MS
532 /* TODO -- maybe use array of nexthops to avoid allocs? */
533
7cdb1a84
MS
534 /* Trying out the sequence number idea, so we can try to detect
535 * when a result is stale.
536 */
537 re->dplane_sequence++;
538 ctx->zd_seq = re->dplane_sequence;
539
540 ret = AOK;
541
542done:
543 return ret;
544}
545
546/*
547 * Enqueue a new route update,
548 * and ensure an event is active for the dataplane thread.
549 */
550static int dplane_route_enqueue(dplane_ctx_h ctx)
551{
552 int ret = EINVAL;
553
554 /* Enqueue for processing by the dataplane thread */
555 DPLANE_LOCK();
556 {
557 TAILQ_INSERT_TAIL(&zdplane_g.dg_route_ctx_q, ctx, zd_q_entries);
558 }
559 DPLANE_UNLOCK();
560
561 /* Ensure that an event for the dataplane thread is active */
562 thread_add_event(zdplane_g.dg_master, dplane_route_process, NULL, 0,
563 &zdplane_g.dg_t_update);
564
565 ret = AOK;
566
567 return (ret);
568}
569
570/*
571 * Attempt to dequeue a route-update block
572 */
573static dplane_ctx_h dplane_route_dequeue(void)
574{
575 dplane_ctx_h ctx = NULL;
576
577 DPLANE_LOCK();
578 {
579 ctx = TAILQ_FIRST(&zdplane_g.dg_route_ctx_q);
580 if (ctx) {
581 TAILQ_REMOVE(&zdplane_g.dg_route_ctx_q,
582 ctx, zd_q_entries);
583 }
584 }
585 DPLANE_UNLOCK();
586
587 return (ctx);
588}
589
590/*
591 * Utility that prepares a route update and enqueues it for processing
592 */
655d681a
MS
593static enum zebra_dplane_result
594dplane_route_update_internal(struct route_node *rn,
595 struct route_entry *re,
596 struct route_entry *old_re,
597 dplane_op_e op)
7cdb1a84 598{
655d681a 599 enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
7cdb1a84
MS
600 int ret = EINVAL;
601 dplane_ctx_h ctx = NULL;
602
603 /* Obtain context block */
604 ctx = dplane_ctx_alloc();
605 if (ctx == NULL) {
606 ret = ENOMEM;
607 goto done;
608 }
609
610 /* Init context with info from zebra data structs */
611 ret = dplane_ctx_route_init(ctx, op, rn, re);
612 if (ret == AOK) {
613 /* Capture some extra info for update case
614 * where there's a different 'old' route.
615 */
b8e0423d
MS
616 if ((op == DPLANE_OP_ROUTE_UPDATE) &&
617 old_re && (old_re != re)) {
7cdb1a84
MS
618 ctx->zd_is_update = true;
619
620 old_re->dplane_sequence++;
621 ctx->zd_old_seq = old_re->dplane_sequence;
622
623 ctx->zd_old_tag = old_re->tag;
624 ctx->zd_old_type = old_re->type;
625 ctx->zd_old_instance = old_re->instance;
626 ctx->zd_old_distance = old_re->distance;
627 }
628
629 /* Enqueue context for processing */
630 ret = dplane_route_enqueue(ctx);
631 }
632
633done:
655d681a
MS
634 if (ret == AOK) {
635 result = ZEBRA_DPLANE_REQUEST_QUEUED;
636 } else if (ctx) {
7cdb1a84 637 dplane_ctx_free(&ctx);
655d681a 638 }
7cdb1a84 639
655d681a 640 return (result);
7cdb1a84
MS
641}
642
643/*
644 * Enqueue a route 'add' for the dataplane.
645 */
655d681a
MS
646enum zebra_dplane_result dplane_route_add(struct route_node *rn,
647 struct route_entry *re)
7cdb1a84 648{
655d681a 649 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
7cdb1a84
MS
650
651 if (rn == NULL || re == NULL) {
652 goto done;
653 }
654
655 ret = dplane_route_update_internal(rn, re, NULL,
656 DPLANE_OP_ROUTE_INSTALL);
657
658done:
7cdb1a84
MS
659 return (ret);
660}
661
662/*
663 * Enqueue a route update for the dataplane.
664 */
655d681a
MS
665enum zebra_dplane_result dplane_route_update(struct route_node *rn,
666 struct route_entry *re,
667 struct route_entry *old_re)
7cdb1a84 668{
655d681a 669 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
7cdb1a84
MS
670
671 if (rn == NULL || re == NULL) {
672 goto done;
673 }
674
675 ret = dplane_route_update_internal(rn, re, old_re,
676 DPLANE_OP_ROUTE_UPDATE);
7cdb1a84 677done:
7cdb1a84
MS
678 return (ret);
679}
680
681/*
682 * Enqueue a route removal for the dataplane.
683 */
655d681a
MS
684enum zebra_dplane_result dplane_route_delete(struct route_node *rn,
685 struct route_entry *re)
7cdb1a84 686{
655d681a 687 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
7cdb1a84
MS
688
689 if (rn == NULL || re == NULL) {
690 goto done;
691 }
692
693 ret = dplane_route_update_internal(rn, re, NULL,
694 DPLANE_OP_ROUTE_DELETE);
695
696done:
7cdb1a84
MS
697 return (ret);
698}
699
700/*
701 * Event handler function for routing updates
702 */
703static int dplane_route_process(struct thread *event)
704{
705 enum zebra_dplane_result res;
706 dplane_ctx_h ctx;
707
708 while (1) {
709 /* TODO -- limit number of updates per cycle? */
710 ctx = dplane_route_dequeue();
711 if (ctx == NULL) {
712 break;
713 }
714
b8e0423d
MS
715 /* TODO -- support series of providers */
716
7cdb1a84
MS
717 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
718 char dest_str[PREFIX_STRLEN];
719
720 prefix2str(dplane_ctx_get_dest(ctx),
721 dest_str, sizeof(dest_str));
722
723 zlog_debug("%u:%s Dplane update ctx %p op %s",
724 dplane_ctx_get_vrf(ctx), dest_str,
725 ctx, dplane_op2str(dplane_ctx_get_op(ctx)));
726 }
727
b8e0423d 728 /* Initially, just doing kernel-facing update here */
7cdb1a84
MS
729 res = kernel_route_update(ctx);
730
731 ctx->zd_status = res;
732
7cdb1a84
MS
733 /* Enqueue result to zebra main context */
734 (*zdplane_g.dg_results_cb)(ctx);
735
736 ctx = NULL;
737 }
738
739 return (0);
740}
741
b8e0423d
MS
742/*
743 * Provider registration
744 */
745int dplane_provider_register(const char *name,
746 dplane_provider_prio_e prio,
747 dplane_provider_process_fp fp)
748{
749 int ret = 0;
750 struct zebra_dplane_provider_s *p, *last;
751
752 /* Validate */
753 if (fp == NULL) {
754 ret = EINVAL;
755 goto done;
756 }
757
758 if (prio <= DPLANE_PRIO_NONE ||
759 prio >= DPLANE_PRIO_LAST) {
760 ret = EINVAL;
761 goto done;
762 }
763
764 /* Allocate and init new provider struct */
765 p = XCALLOC(MTYPE_DP_PROV, sizeof(struct zebra_dplane_provider_s));
766 if (p == NULL) {
767 ret = ENOMEM;
768 goto done;
769 }
770
771 strncpy(p->dp_name, name, DPLANE_PROVIDER_NAMELEN);
772 p->dp_name[DPLANE_PROVIDER_NAMELEN] = '\0';
773
774 p->dp_priority = prio;
775 p->dp_fp = fp;
776
777 p->dp_id = ++zdplane_g.dg_provider_id;
778
779 /* Insert into list ordered by priority */
780 TAILQ_FOREACH(last, &zdplane_g.dg_providers_q, dp_q_providers) {
781 if (last->dp_priority > p->dp_priority) {
782 break;
783 }
784 }
785
786 if (last) {
787 TAILQ_INSERT_BEFORE(last, p, dp_q_providers);
788 } else {
789 TAILQ_INSERT_TAIL(&zdplane_g.dg_providers_q, p, dp_q_providers);
790 }
791
792done:
793 return (ret);
794}
795
7cdb1a84
MS
796/*
797 * Zebra registers a results callback with the dataplane system
798 */
799int dplane_results_register(dplane_results_fp fp)
800{
801 zdplane_g.dg_results_cb = fp;
802 return (AOK);
803}
804
805/*
806 * Initialize the dataplane module during startup, internal/private version
807 */
808static void zebra_dplane_init_internal(struct zebra_t *zebra)
809{
810 memset(&zdplane_g, 0, sizeof(zdplane_g));
811
812 pthread_mutex_init(&zdplane_g.dg_mutex, NULL);
813
814 TAILQ_INIT(&zdplane_g.dg_route_ctx_q);
815 TAILQ_INIT(&zdplane_g.dg_providers_q);
816
b8e0423d
MS
817 /* TODO -- register kernel 'provider' during init */
818
7cdb1a84
MS
819 /* TODO -- using zebra core event thread temporarily */
820 zdplane_g.dg_master = zebra->master;
821
822 return;
823}
824
825/*
b8e0423d 826 * Initialize the dataplane module at startup; called by zebra rib_init()
7cdb1a84
MS
827 */
828void zebra_dplane_init(void)
829{
830 zebra_dplane_init_internal(&zebrad);
831}