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