]> git.proxmox.com Git - mirror_frr.git/blob - zebra/zebra_dplane.c
Merge pull request #4145 from pguibert6WIND/bfd_converge_up
[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/libfrr.h"
21 #include "lib/debug.h"
22 #include "lib/frratomic.h"
23 #include "lib/frr_pthread.h"
24 #include "lib/memory.h"
25 #include "lib/queue.h"
26 #include "lib/zebra.h"
27 #include "zebra/zebra_router.h"
28 #include "zebra/zebra_memory.h"
29 #include "zebra/zebra_router.h"
30 #include "zebra/zebra_dplane.h"
31 #include "zebra/rt.h"
32 #include "zebra/debug.h"
33
34 /* Memory type for context blocks */
35 DEFINE_MTYPE(ZEBRA, DP_CTX, "Zebra DPlane Ctx")
36 DEFINE_MTYPE(ZEBRA, DP_PROV, "Zebra DPlane Provider")
37
38 #ifndef AOK
39 # define AOK 0
40 #endif
41
42 /* Enable test dataplane provider */
43 /*#define DPLANE_TEST_PROVIDER 1 */
44
45 /* Default value for max queued incoming updates */
46 const uint32_t DPLANE_DEFAULT_MAX_QUEUED = 200;
47
48 /* Default value for new work per cycle */
49 const uint32_t DPLANE_DEFAULT_NEW_WORK = 100;
50
51 /* Validation check macro for context blocks */
52 /* #define DPLANE_DEBUG 1 */
53
54 #ifdef DPLANE_DEBUG
55
56 # define DPLANE_CTX_VALID(p) \
57 assert((p) != NULL)
58
59 #else
60
61 # define DPLANE_CTX_VALID(p)
62
63 #endif /* DPLANE_DEBUG */
64
65 /*
66 * Route information captured for route updates.
67 */
68 struct dplane_route_info {
69
70 /* Dest and (optional) source prefixes */
71 struct prefix zd_dest;
72 struct prefix zd_src;
73
74 afi_t zd_afi;
75 safi_t zd_safi;
76
77 int zd_type;
78 int zd_old_type;
79
80 route_tag_t zd_tag;
81 route_tag_t zd_old_tag;
82 uint32_t zd_metric;
83 uint32_t zd_old_metric;
84
85 uint16_t zd_instance;
86 uint16_t zd_old_instance;
87
88 uint8_t zd_distance;
89 uint8_t zd_old_distance;
90
91 uint32_t zd_mtu;
92 uint32_t zd_nexthop_mtu;
93
94 /* Nexthops */
95 struct nexthop_group zd_ng;
96
97 /* "Previous" nexthops, used only in route updates without netlink */
98 struct nexthop_group zd_old_ng;
99
100 /* TODO -- use fixed array of nexthops, to avoid mallocs? */
101
102 };
103
104 /*
105 * Pseudowire info for the dataplane
106 */
107 struct dplane_pw_info {
108 char ifname[IF_NAMESIZE];
109 ifindex_t ifindex;
110 int type;
111 int af;
112 int status;
113 uint32_t flags;
114 union g_addr dest;
115 mpls_label_t local_label;
116 mpls_label_t remote_label;
117
118 /* Nexthops */
119 struct nexthop_group nhg;
120
121 union pw_protocol_fields fields;
122 };
123
124 /*
125 * Interface/prefix info for the dataplane
126 */
127 struct dplane_intf_info {
128
129 char ifname[INTERFACE_NAMSIZ];
130 ifindex_t ifindex;
131
132 uint32_t metric;
133 uint32_t flags;
134
135 #define DPLANE_INTF_CONNECTED (1 << 0) /* Connected peer, p2p */
136 #define DPLANE_INTF_SECONDARY (1 << 1)
137 #define DPLANE_INTF_BROADCAST (1 << 2)
138 #define DPLANE_INTF_HAS_DEST (1 << 3)
139 #define DPLANE_INTF_HAS_LABEL (1 << 4)
140
141 /* Interface address/prefix */
142 struct prefix prefix;
143
144 /* Dest address, for p2p, or broadcast prefix */
145 struct prefix dest_prefix;
146
147 char *label;
148 char label_buf[32];
149 };
150
151 /*
152 * The context block used to exchange info about route updates across
153 * the boundary between the zebra main context (and pthread) and the
154 * dataplane layer (and pthread).
155 */
156 struct zebra_dplane_ctx {
157
158 /* Operation code */
159 enum dplane_op_e zd_op;
160
161 /* Status on return */
162 enum zebra_dplane_result zd_status;
163
164 /* Dplane provider id */
165 uint32_t zd_provider;
166
167 /* Flags - used by providers, e.g. */
168 int zd_flags;
169
170 bool zd_is_update;
171
172 uint32_t zd_seq;
173 uint32_t zd_old_seq;
174
175 /* TODO -- internal/sub-operation status? */
176 enum zebra_dplane_result zd_remote_status;
177 enum zebra_dplane_result zd_kernel_status;
178
179 vrf_id_t zd_vrf_id;
180 uint32_t zd_table_id;
181
182 /* Support info for different kinds of updates */
183 union {
184 struct dplane_route_info rinfo;
185 zebra_lsp_t lsp;
186 struct dplane_pw_info pw;
187 struct dplane_intf_info intf;
188 } u;
189
190 /* Namespace info, used especially for netlink kernel communication */
191 struct zebra_dplane_info zd_ns_info;
192
193 /* Embedded list linkage */
194 TAILQ_ENTRY(zebra_dplane_ctx) zd_q_entries;
195 };
196
197 /* Flag that can be set by a pre-kernel provider as a signal that an update
198 * should bypass the kernel.
199 */
200 #define DPLANE_CTX_FLAG_NO_KERNEL 0x01
201
202
203 /*
204 * Registration block for one dataplane provider.
205 */
206 struct zebra_dplane_provider {
207 /* Name */
208 char dp_name[DPLANE_PROVIDER_NAMELEN + 1];
209
210 /* Priority, for ordering among providers */
211 uint8_t dp_priority;
212
213 /* Id value */
214 uint32_t dp_id;
215
216 /* Mutex */
217 pthread_mutex_t dp_mutex;
218
219 /* Plugin-provided extra data */
220 void *dp_data;
221
222 /* Flags */
223 int dp_flags;
224
225 int (*dp_fp)(struct zebra_dplane_provider *prov);
226
227 int (*dp_fini)(struct zebra_dplane_provider *prov, bool early_p);
228
229 _Atomic uint32_t dp_in_counter;
230 _Atomic uint32_t dp_in_queued;
231 _Atomic uint32_t dp_in_max;
232 _Atomic uint32_t dp_out_counter;
233 _Atomic uint32_t dp_out_queued;
234 _Atomic uint32_t dp_out_max;
235 _Atomic uint32_t dp_error_counter;
236
237 /* Queue of contexts inbound to the provider */
238 struct dplane_ctx_q dp_ctx_in_q;
239
240 /* Queue of completed contexts outbound from the provider back
241 * towards the dataplane module.
242 */
243 struct dplane_ctx_q dp_ctx_out_q;
244
245 /* Embedded list linkage for provider objects */
246 TAILQ_ENTRY(zebra_dplane_provider) dp_prov_link;
247 };
248
249 /*
250 * Globals
251 */
252 static struct zebra_dplane_globals {
253 /* Mutex to control access to dataplane components */
254 pthread_mutex_t dg_mutex;
255
256 /* Results callback registered by zebra 'core' */
257 int (*dg_results_cb)(struct dplane_ctx_q *ctxlist);
258
259 /* Sentinel for beginning of shutdown */
260 volatile bool dg_is_shutdown;
261
262 /* Sentinel for end of shutdown */
263 volatile bool dg_run;
264
265 /* Route-update context queue inbound to the dataplane */
266 TAILQ_HEAD(zdg_ctx_q, zebra_dplane_ctx) dg_route_ctx_q;
267
268 /* Ordered list of providers */
269 TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider) dg_providers_q;
270
271 /* Counter used to assign internal ids to providers */
272 uint32_t dg_provider_id;
273
274 /* Limit number of pending, unprocessed updates */
275 _Atomic uint32_t dg_max_queued_updates;
276
277 /* Control whether system route notifications should be produced. */
278 bool dg_sys_route_notifs;
279
280 /* Limit number of new updates dequeued at once, to pace an
281 * incoming burst.
282 */
283 uint32_t dg_updates_per_cycle;
284
285 _Atomic uint32_t dg_routes_in;
286 _Atomic uint32_t dg_routes_queued;
287 _Atomic uint32_t dg_routes_queued_max;
288 _Atomic uint32_t dg_route_errors;
289 _Atomic uint32_t dg_other_errors;
290
291 _Atomic uint32_t dg_lsps_in;
292 _Atomic uint32_t dg_lsp_errors;
293
294 _Atomic uint32_t dg_pws_in;
295 _Atomic uint32_t dg_pw_errors;
296
297 _Atomic uint32_t dg_intf_addrs_in;
298 _Atomic uint32_t dg_intf_addr_errors;
299
300 _Atomic uint32_t dg_update_yields;
301
302 /* Dataplane pthread */
303 struct frr_pthread *dg_pthread;
304
305 /* Event-delivery context 'master' for the dplane */
306 struct thread_master *dg_master;
307
308 /* Event/'thread' pointer for queued updates */
309 struct thread *dg_t_update;
310
311 /* Event pointer for pending shutdown check loop */
312 struct thread *dg_t_shutdown_check;
313
314 } zdplane_info;
315
316 /*
317 * Lock and unlock for interactions with the zebra 'core' pthread
318 */
319 #define DPLANE_LOCK() pthread_mutex_lock(&zdplane_info.dg_mutex)
320 #define DPLANE_UNLOCK() pthread_mutex_unlock(&zdplane_info.dg_mutex)
321
322
323 /*
324 * Lock and unlock for individual providers
325 */
326 #define DPLANE_PROV_LOCK(p) pthread_mutex_lock(&((p)->dp_mutex))
327 #define DPLANE_PROV_UNLOCK(p) pthread_mutex_unlock(&((p)->dp_mutex))
328
329 /* Prototypes */
330 static int dplane_thread_loop(struct thread *event);
331 static void dplane_info_from_zns(struct zebra_dplane_info *ns_info,
332 struct zebra_ns *zns);
333 static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp,
334 enum dplane_op_e op);
335 static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw,
336 enum dplane_op_e op);
337 static enum zebra_dplane_result intf_addr_update_internal(
338 const struct interface *ifp, const struct connected *ifc,
339 enum dplane_op_e op);
340
341 /*
342 * Public APIs
343 */
344
345 /* Obtain thread_master for dataplane thread */
346 struct thread_master *dplane_get_thread_master(void)
347 {
348 return zdplane_info.dg_master;
349 }
350
351 /*
352 * Allocate a dataplane update context
353 */
354 static struct zebra_dplane_ctx *dplane_ctx_alloc(void)
355 {
356 struct zebra_dplane_ctx *p;
357
358 /* TODO -- just alloc'ing memory, but would like to maintain
359 * a pool
360 */
361 p = XCALLOC(MTYPE_DP_CTX, sizeof(struct zebra_dplane_ctx));
362
363 return p;
364 }
365
366 /* Enable system route notifications */
367 void dplane_enable_sys_route_notifs(void)
368 {
369 zdplane_info.dg_sys_route_notifs = true;
370 }
371
372 /*
373 * Free a dataplane results context.
374 */
375 static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
376 {
377 if (pctx == NULL)
378 return;
379
380 DPLANE_CTX_VALID(*pctx);
381
382 /* TODO -- just freeing memory, but would like to maintain
383 * a pool
384 */
385
386 /* Some internal allocations may need to be freed, depending on
387 * the type of info captured in the ctx.
388 */
389 switch ((*pctx)->zd_op) {
390 case DPLANE_OP_ROUTE_INSTALL:
391 case DPLANE_OP_ROUTE_UPDATE:
392 case DPLANE_OP_ROUTE_DELETE:
393 case DPLANE_OP_SYS_ROUTE_ADD:
394 case DPLANE_OP_SYS_ROUTE_DELETE:
395
396 /* Free allocated nexthops */
397 if ((*pctx)->u.rinfo.zd_ng.nexthop) {
398 /* This deals with recursive nexthops too */
399 nexthops_free((*pctx)->u.rinfo.zd_ng.nexthop);
400
401 (*pctx)->u.rinfo.zd_ng.nexthop = NULL;
402 }
403
404 if ((*pctx)->u.rinfo.zd_old_ng.nexthop) {
405 /* This deals with recursive nexthops too */
406 nexthops_free((*pctx)->u.rinfo.zd_old_ng.nexthop);
407
408 (*pctx)->u.rinfo.zd_old_ng.nexthop = NULL;
409 }
410
411 break;
412
413 case DPLANE_OP_LSP_INSTALL:
414 case DPLANE_OP_LSP_UPDATE:
415 case DPLANE_OP_LSP_DELETE:
416 {
417 zebra_nhlfe_t *nhlfe, *next;
418
419 /* Free allocated NHLFEs */
420 for (nhlfe = (*pctx)->u.lsp.nhlfe_list; nhlfe; nhlfe = next) {
421 next = nhlfe->next;
422
423 zebra_mpls_nhlfe_del(nhlfe);
424 }
425
426 /* Clear pointers in lsp struct, in case we're cacheing
427 * free context structs.
428 */
429 (*pctx)->u.lsp.nhlfe_list = NULL;
430 (*pctx)->u.lsp.best_nhlfe = NULL;
431
432 break;
433 }
434
435 case DPLANE_OP_PW_INSTALL:
436 case DPLANE_OP_PW_UNINSTALL:
437 /* Free allocated nexthops */
438 if ((*pctx)->u.pw.nhg.nexthop) {
439 /* This deals with recursive nexthops too */
440 nexthops_free((*pctx)->u.pw.nhg.nexthop);
441
442 (*pctx)->u.pw.nhg.nexthop = NULL;
443 }
444 break;
445
446 case DPLANE_OP_ADDR_INSTALL:
447 case DPLANE_OP_ADDR_UNINSTALL:
448 /* Maybe free label string, if allocated */
449 if ((*pctx)->u.intf.label != NULL &&
450 (*pctx)->u.intf.label != (*pctx)->u.intf.label_buf) {
451 free((*pctx)->u.intf.label);
452 (*pctx)->u.intf.label = NULL;
453 }
454 break;
455
456 case DPLANE_OP_NONE:
457 break;
458 }
459
460 XFREE(MTYPE_DP_CTX, *pctx);
461 *pctx = NULL;
462 }
463
464 /*
465 * Return a context block to the dplane module after processing
466 */
467 void dplane_ctx_fini(struct zebra_dplane_ctx **pctx)
468 {
469 /* TODO -- maintain pool; for now, just free */
470 dplane_ctx_free(pctx);
471 }
472
473 /* Enqueue a context block */
474 void dplane_ctx_enqueue_tail(struct dplane_ctx_q *q,
475 const struct zebra_dplane_ctx *ctx)
476 {
477 TAILQ_INSERT_TAIL(q, (struct zebra_dplane_ctx *)ctx, zd_q_entries);
478 }
479
480 /* Append a list of context blocks to another list */
481 void dplane_ctx_list_append(struct dplane_ctx_q *to_list,
482 struct dplane_ctx_q *from_list)
483 {
484 if (TAILQ_FIRST(from_list)) {
485 TAILQ_CONCAT(to_list, from_list, zd_q_entries);
486
487 /* And clear 'from' list */
488 TAILQ_INIT(from_list);
489 }
490 }
491
492 /* Dequeue a context block from the head of a list */
493 struct zebra_dplane_ctx *dplane_ctx_dequeue(struct dplane_ctx_q *q)
494 {
495 struct zebra_dplane_ctx *ctx = TAILQ_FIRST(q);
496
497 if (ctx)
498 TAILQ_REMOVE(q, ctx, zd_q_entries);
499
500 return ctx;
501 }
502
503 /*
504 * Accessors for information from the context object
505 */
506 enum zebra_dplane_result dplane_ctx_get_status(
507 const struct zebra_dplane_ctx *ctx)
508 {
509 DPLANE_CTX_VALID(ctx);
510
511 return ctx->zd_status;
512 }
513
514 void dplane_ctx_set_status(struct zebra_dplane_ctx *ctx,
515 enum zebra_dplane_result status)
516 {
517 DPLANE_CTX_VALID(ctx);
518
519 ctx->zd_status = status;
520 }
521
522 /* Retrieve last/current provider id */
523 uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx *ctx)
524 {
525 DPLANE_CTX_VALID(ctx);
526 return ctx->zd_provider;
527 }
528
529 /* Providers run before the kernel can control whether a kernel
530 * update should be done.
531 */
532 void dplane_ctx_set_skip_kernel(struct zebra_dplane_ctx *ctx)
533 {
534 DPLANE_CTX_VALID(ctx);
535
536 SET_FLAG(ctx->zd_flags, DPLANE_CTX_FLAG_NO_KERNEL);
537 }
538
539 bool dplane_ctx_is_skip_kernel(const struct zebra_dplane_ctx *ctx)
540 {
541 DPLANE_CTX_VALID(ctx);
542
543 return CHECK_FLAG(ctx->zd_flags, DPLANE_CTX_FLAG_NO_KERNEL);
544 }
545
546 enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx)
547 {
548 DPLANE_CTX_VALID(ctx);
549
550 return ctx->zd_op;
551 }
552
553 const char *dplane_op2str(enum dplane_op_e op)
554 {
555 const char *ret = "UNKNOWN";
556
557 switch (op) {
558 case DPLANE_OP_NONE:
559 ret = "NONE";
560 break;
561
562 /* Route update */
563 case DPLANE_OP_ROUTE_INSTALL:
564 ret = "ROUTE_INSTALL";
565 break;
566 case DPLANE_OP_ROUTE_UPDATE:
567 ret = "ROUTE_UPDATE";
568 break;
569 case DPLANE_OP_ROUTE_DELETE:
570 ret = "ROUTE_DELETE";
571 break;
572
573 case DPLANE_OP_LSP_INSTALL:
574 ret = "LSP_INSTALL";
575 break;
576 case DPLANE_OP_LSP_UPDATE:
577 ret = "LSP_UPDATE";
578 break;
579 case DPLANE_OP_LSP_DELETE:
580 ret = "LSP_DELETE";
581 break;
582
583 case DPLANE_OP_PW_INSTALL:
584 ret = "PW_INSTALL";
585 break;
586 case DPLANE_OP_PW_UNINSTALL:
587 ret = "PW_UNINSTALL";
588 break;
589
590 case DPLANE_OP_SYS_ROUTE_ADD:
591 ret = "SYS_ROUTE_ADD";
592 break;
593 case DPLANE_OP_SYS_ROUTE_DELETE:
594 ret = "SYS_ROUTE_DEL";
595 break;
596
597 case DPLANE_OP_ADDR_INSTALL:
598 ret = "ADDR_INSTALL";
599 break;
600 case DPLANE_OP_ADDR_UNINSTALL:
601 ret = "ADDR_UNINSTALL";
602 break;
603
604 }
605
606 return ret;
607 }
608
609 const char *dplane_res2str(enum zebra_dplane_result res)
610 {
611 const char *ret = "<Unknown>";
612
613 switch (res) {
614 case ZEBRA_DPLANE_REQUEST_FAILURE:
615 ret = "FAILURE";
616 break;
617 case ZEBRA_DPLANE_REQUEST_QUEUED:
618 ret = "QUEUED";
619 break;
620 case ZEBRA_DPLANE_REQUEST_SUCCESS:
621 ret = "SUCCESS";
622 break;
623 }
624
625 return ret;
626 }
627
628 const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx)
629 {
630 DPLANE_CTX_VALID(ctx);
631
632 return &(ctx->u.rinfo.zd_dest);
633 }
634
635 /* Source prefix is a little special - return NULL for "no src prefix" */
636 const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx)
637 {
638 DPLANE_CTX_VALID(ctx);
639
640 if (ctx->u.rinfo.zd_src.prefixlen == 0 &&
641 IN6_IS_ADDR_UNSPECIFIED(&(ctx->u.rinfo.zd_src.u.prefix6))) {
642 return NULL;
643 } else {
644 return &(ctx->u.rinfo.zd_src);
645 }
646 }
647
648 bool dplane_ctx_is_update(const struct zebra_dplane_ctx *ctx)
649 {
650 DPLANE_CTX_VALID(ctx);
651
652 return ctx->zd_is_update;
653 }
654
655 uint32_t dplane_ctx_get_seq(const struct zebra_dplane_ctx *ctx)
656 {
657 DPLANE_CTX_VALID(ctx);
658
659 return ctx->zd_seq;
660 }
661
662 uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx)
663 {
664 DPLANE_CTX_VALID(ctx);
665
666 return ctx->zd_old_seq;
667 }
668
669 vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx)
670 {
671 DPLANE_CTX_VALID(ctx);
672
673 return ctx->zd_vrf_id;
674 }
675
676 int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx)
677 {
678 DPLANE_CTX_VALID(ctx);
679
680 return ctx->u.rinfo.zd_type;
681 }
682
683 int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx)
684 {
685 DPLANE_CTX_VALID(ctx);
686
687 return ctx->u.rinfo.zd_old_type;
688 }
689
690 afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx)
691 {
692 DPLANE_CTX_VALID(ctx);
693
694 return ctx->u.rinfo.zd_afi;
695 }
696
697 safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx)
698 {
699 DPLANE_CTX_VALID(ctx);
700
701 return ctx->u.rinfo.zd_safi;
702 }
703
704 uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx)
705 {
706 DPLANE_CTX_VALID(ctx);
707
708 return ctx->zd_table_id;
709 }
710
711 route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx)
712 {
713 DPLANE_CTX_VALID(ctx);
714
715 return ctx->u.rinfo.zd_tag;
716 }
717
718 route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx)
719 {
720 DPLANE_CTX_VALID(ctx);
721
722 return ctx->u.rinfo.zd_old_tag;
723 }
724
725 uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx)
726 {
727 DPLANE_CTX_VALID(ctx);
728
729 return ctx->u.rinfo.zd_instance;
730 }
731
732 uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx)
733 {
734 DPLANE_CTX_VALID(ctx);
735
736 return ctx->u.rinfo.zd_old_instance;
737 }
738
739 uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx)
740 {
741 DPLANE_CTX_VALID(ctx);
742
743 return ctx->u.rinfo.zd_metric;
744 }
745
746 uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx *ctx)
747 {
748 DPLANE_CTX_VALID(ctx);
749
750 return ctx->u.rinfo.zd_old_metric;
751 }
752
753 uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx *ctx)
754 {
755 DPLANE_CTX_VALID(ctx);
756
757 return ctx->u.rinfo.zd_mtu;
758 }
759
760 uint32_t dplane_ctx_get_nh_mtu(const struct zebra_dplane_ctx *ctx)
761 {
762 DPLANE_CTX_VALID(ctx);
763
764 return ctx->u.rinfo.zd_nexthop_mtu;
765 }
766
767 uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx)
768 {
769 DPLANE_CTX_VALID(ctx);
770
771 return ctx->u.rinfo.zd_distance;
772 }
773
774 uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx)
775 {
776 DPLANE_CTX_VALID(ctx);
777
778 return ctx->u.rinfo.zd_old_distance;
779 }
780
781 const struct nexthop_group *dplane_ctx_get_ng(
782 const struct zebra_dplane_ctx *ctx)
783 {
784 DPLANE_CTX_VALID(ctx);
785
786 return &(ctx->u.rinfo.zd_ng);
787 }
788
789 const struct nexthop_group *dplane_ctx_get_old_ng(
790 const struct zebra_dplane_ctx *ctx)
791 {
792 DPLANE_CTX_VALID(ctx);
793
794 return &(ctx->u.rinfo.zd_old_ng);
795 }
796
797 const struct zebra_dplane_info *dplane_ctx_get_ns(
798 const struct zebra_dplane_ctx *ctx)
799 {
800 DPLANE_CTX_VALID(ctx);
801
802 return &(ctx->zd_ns_info);
803 }
804
805 /* Accessors for LSP information */
806
807 mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx)
808 {
809 DPLANE_CTX_VALID(ctx);
810
811 return ctx->u.lsp.ile.in_label;
812 }
813
814 uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx)
815 {
816 DPLANE_CTX_VALID(ctx);
817
818 return ctx->u.lsp.addr_family;
819 }
820
821 uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx)
822 {
823 DPLANE_CTX_VALID(ctx);
824
825 return ctx->u.lsp.flags;
826 }
827
828 const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx)
829 {
830 DPLANE_CTX_VALID(ctx);
831
832 return ctx->u.lsp.nhlfe_list;
833 }
834
835 const zebra_nhlfe_t *
836 dplane_ctx_get_best_nhlfe(const struct zebra_dplane_ctx *ctx)
837 {
838 DPLANE_CTX_VALID(ctx);
839
840 return ctx->u.lsp.best_nhlfe;
841 }
842
843 uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx)
844 {
845 DPLANE_CTX_VALID(ctx);
846
847 return ctx->u.lsp.num_ecmp;
848 }
849
850 const char *dplane_ctx_get_pw_ifname(const struct zebra_dplane_ctx *ctx)
851 {
852 DPLANE_CTX_VALID(ctx);
853
854 return ctx->u.pw.ifname;
855 }
856
857 mpls_label_t dplane_ctx_get_pw_local_label(const struct zebra_dplane_ctx *ctx)
858 {
859 DPLANE_CTX_VALID(ctx);
860
861 return ctx->u.pw.local_label;
862 }
863
864 mpls_label_t dplane_ctx_get_pw_remote_label(const struct zebra_dplane_ctx *ctx)
865 {
866 DPLANE_CTX_VALID(ctx);
867
868 return ctx->u.pw.remote_label;
869 }
870
871 int dplane_ctx_get_pw_type(const struct zebra_dplane_ctx *ctx)
872 {
873 DPLANE_CTX_VALID(ctx);
874
875 return ctx->u.pw.type;
876 }
877
878 int dplane_ctx_get_pw_af(const struct zebra_dplane_ctx *ctx)
879 {
880 DPLANE_CTX_VALID(ctx);
881
882 return ctx->u.pw.af;
883 }
884
885 uint32_t dplane_ctx_get_pw_flags(const struct zebra_dplane_ctx *ctx)
886 {
887 DPLANE_CTX_VALID(ctx);
888
889 return ctx->u.pw.flags;
890 }
891
892 int dplane_ctx_get_pw_status(const struct zebra_dplane_ctx *ctx)
893 {
894 DPLANE_CTX_VALID(ctx);
895
896 return ctx->u.pw.status;
897 }
898
899 const union g_addr *dplane_ctx_get_pw_dest(
900 const struct zebra_dplane_ctx *ctx)
901 {
902 DPLANE_CTX_VALID(ctx);
903
904 return &(ctx->u.pw.dest);
905 }
906
907 const union pw_protocol_fields *dplane_ctx_get_pw_proto(
908 const struct zebra_dplane_ctx *ctx)
909 {
910 DPLANE_CTX_VALID(ctx);
911
912 return &(ctx->u.pw.fields);
913 }
914
915 const struct nexthop_group *
916 dplane_ctx_get_pw_nhg(const struct zebra_dplane_ctx *ctx)
917 {
918 DPLANE_CTX_VALID(ctx);
919
920 return &(ctx->u.pw.nhg);
921 }
922
923 /* Accessors for interface information */
924 const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx)
925 {
926 DPLANE_CTX_VALID(ctx);
927
928 return ctx->u.intf.ifname;
929 }
930
931 ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx)
932 {
933 DPLANE_CTX_VALID(ctx);
934
935 return ctx->u.intf.ifindex;
936 }
937
938 uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx)
939 {
940 DPLANE_CTX_VALID(ctx);
941
942 return ctx->u.intf.metric;
943 }
944
945 /* Is interface addr p2p? */
946 bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx)
947 {
948 DPLANE_CTX_VALID(ctx);
949
950 return (ctx->u.intf.flags & DPLANE_INTF_CONNECTED);
951 }
952
953 bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx)
954 {
955 DPLANE_CTX_VALID(ctx);
956
957 return (ctx->u.intf.flags & DPLANE_INTF_SECONDARY);
958 }
959
960 bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx)
961 {
962 DPLANE_CTX_VALID(ctx);
963
964 return (ctx->u.intf.flags & DPLANE_INTF_BROADCAST);
965 }
966
967 const struct prefix *dplane_ctx_get_intf_addr(
968 const struct zebra_dplane_ctx *ctx)
969 {
970 DPLANE_CTX_VALID(ctx);
971
972 return &(ctx->u.intf.prefix);
973 }
974
975 bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx)
976 {
977 DPLANE_CTX_VALID(ctx);
978
979 return (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST);
980 }
981
982 const struct prefix *dplane_ctx_get_intf_dest(
983 const struct zebra_dplane_ctx *ctx)
984 {
985 DPLANE_CTX_VALID(ctx);
986
987 if (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST)
988 return &(ctx->u.intf.dest_prefix);
989 else
990 return NULL;
991 }
992
993 bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx)
994 {
995 DPLANE_CTX_VALID(ctx);
996
997 return (ctx->u.intf.flags & DPLANE_INTF_HAS_LABEL);
998 }
999
1000 const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx)
1001 {
1002 DPLANE_CTX_VALID(ctx);
1003
1004 return ctx->u.intf.label;
1005 }
1006
1007 /*
1008 * End of dplane context accessors
1009 */
1010
1011
1012 /*
1013 * Retrieve the limit on the number of pending, unprocessed updates.
1014 */
1015 uint32_t dplane_get_in_queue_limit(void)
1016 {
1017 return atomic_load_explicit(&zdplane_info.dg_max_queued_updates,
1018 memory_order_relaxed);
1019 }
1020
1021 /*
1022 * Configure limit on the number of pending, queued updates.
1023 */
1024 void dplane_set_in_queue_limit(uint32_t limit, bool set)
1025 {
1026 /* Reset to default on 'unset' */
1027 if (!set)
1028 limit = DPLANE_DEFAULT_MAX_QUEUED;
1029
1030 atomic_store_explicit(&zdplane_info.dg_max_queued_updates, limit,
1031 memory_order_relaxed);
1032 }
1033
1034 /*
1035 * Retrieve the current queue depth of incoming, unprocessed updates
1036 */
1037 uint32_t dplane_get_in_queue_len(void)
1038 {
1039 return atomic_load_explicit(&zdplane_info.dg_routes_queued,
1040 memory_order_seq_cst);
1041 }
1042
1043 /*
1044 * Common dataplane context init with zebra namespace info.
1045 */
1046 static int dplane_ctx_ns_init(struct zebra_dplane_ctx *ctx,
1047 struct zebra_ns *zns,
1048 bool is_update)
1049 {
1050 dplane_info_from_zns(&(ctx->zd_ns_info), zns);
1051
1052 #if defined(HAVE_NETLINK)
1053 /* Increment message counter after copying to context struct - may need
1054 * two messages in some 'update' cases.
1055 */
1056 if (is_update)
1057 zns->netlink_dplane.seq += 2;
1058 else
1059 zns->netlink_dplane.seq++;
1060 #endif /* HAVE_NETLINK */
1061
1062 return AOK;
1063 }
1064
1065 /*
1066 * Initialize a context block for a route update from zebra data structs.
1067 */
1068 static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx,
1069 enum dplane_op_e op,
1070 struct route_node *rn,
1071 struct route_entry *re)
1072 {
1073 int ret = EINVAL;
1074 const struct route_table *table = NULL;
1075 const rib_table_info_t *info;
1076 const struct prefix *p, *src_p;
1077 struct zebra_ns *zns;
1078 struct zebra_vrf *zvrf;
1079 struct nexthop *nexthop;
1080
1081 if (!ctx || !rn || !re)
1082 goto done;
1083
1084 ctx->zd_op = op;
1085 ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
1086
1087 ctx->u.rinfo.zd_type = re->type;
1088 ctx->u.rinfo.zd_old_type = re->type;
1089
1090 /* Prefixes: dest, and optional source */
1091 srcdest_rnode_prefixes(rn, &p, &src_p);
1092
1093 prefix_copy(&(ctx->u.rinfo.zd_dest), p);
1094
1095 if (src_p)
1096 prefix_copy(&(ctx->u.rinfo.zd_src), src_p);
1097 else
1098 memset(&(ctx->u.rinfo.zd_src), 0, sizeof(ctx->u.rinfo.zd_src));
1099
1100 ctx->zd_table_id = re->table;
1101
1102 ctx->u.rinfo.zd_metric = re->metric;
1103 ctx->u.rinfo.zd_old_metric = re->metric;
1104 ctx->zd_vrf_id = re->vrf_id;
1105 ctx->u.rinfo.zd_mtu = re->mtu;
1106 ctx->u.rinfo.zd_nexthop_mtu = re->nexthop_mtu;
1107 ctx->u.rinfo.zd_instance = re->instance;
1108 ctx->u.rinfo.zd_tag = re->tag;
1109 ctx->u.rinfo.zd_old_tag = re->tag;
1110 ctx->u.rinfo.zd_distance = re->distance;
1111
1112 table = srcdest_rnode_table(rn);
1113 info = table->info;
1114
1115 ctx->u.rinfo.zd_afi = info->afi;
1116 ctx->u.rinfo.zd_safi = info->safi;
1117
1118 /* Copy nexthops; recursive info is included too */
1119 copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), re->ng.nexthop, NULL);
1120
1121 /* Ensure that the dplane's nexthops flags are clear. */
1122 for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop))
1123 UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
1124
1125 /* Don't need some info when capturing a system notification */
1126 if (op == DPLANE_OP_SYS_ROUTE_ADD ||
1127 op == DPLANE_OP_SYS_ROUTE_DELETE) {
1128 ret = AOK;
1129 goto done;
1130 }
1131
1132 /* Extract ns info - can't use pointers to 'core' structs */
1133 zvrf = vrf_info_lookup(re->vrf_id);
1134 zns = zvrf->zns;
1135 dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_ROUTE_UPDATE));
1136
1137 /* Trying out the sequence number idea, so we can try to detect
1138 * when a result is stale.
1139 */
1140 re->dplane_sequence = zebra_router_get_next_sequence();
1141 ctx->zd_seq = re->dplane_sequence;
1142
1143 ret = AOK;
1144
1145 done:
1146 return ret;
1147 }
1148
1149 /*
1150 * Capture information for an LSP update in a dplane context.
1151 */
1152 static int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx,
1153 enum dplane_op_e op,
1154 zebra_lsp_t *lsp)
1155 {
1156 int ret = AOK;
1157 zebra_nhlfe_t *nhlfe, *new_nhlfe;
1158
1159 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1160 zlog_debug("init dplane ctx %s: in-label %u ecmp# %d",
1161 dplane_op2str(op), lsp->ile.in_label,
1162 lsp->num_ecmp);
1163
1164 ctx->zd_op = op;
1165 ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
1166
1167 /* Capture namespace info */
1168 dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
1169 (op == DPLANE_OP_LSP_UPDATE));
1170
1171 memset(&ctx->u.lsp, 0, sizeof(ctx->u.lsp));
1172
1173 ctx->u.lsp.ile = lsp->ile;
1174 ctx->u.lsp.addr_family = lsp->addr_family;
1175 ctx->u.lsp.num_ecmp = lsp->num_ecmp;
1176 ctx->u.lsp.flags = lsp->flags;
1177
1178 /* Copy source LSP's nhlfes, and capture 'best' nhlfe */
1179 for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) {
1180 /* Not sure if this is meaningful... */
1181 if (nhlfe->nexthop == NULL)
1182 continue;
1183
1184 new_nhlfe =
1185 zebra_mpls_lsp_add_nhlfe(
1186 &(ctx->u.lsp),
1187 nhlfe->type,
1188 nhlfe->nexthop->type,
1189 &(nhlfe->nexthop->gate),
1190 nhlfe->nexthop->ifindex,
1191 nhlfe->nexthop->nh_label->label[0]);
1192
1193 if (new_nhlfe == NULL || new_nhlfe->nexthop == NULL) {
1194 ret = ENOMEM;
1195 break;
1196 }
1197
1198 /* Need to copy flags too */
1199 new_nhlfe->flags = nhlfe->flags;
1200 new_nhlfe->nexthop->flags = nhlfe->nexthop->flags;
1201
1202 if (nhlfe == lsp->best_nhlfe)
1203 ctx->u.lsp.best_nhlfe = new_nhlfe;
1204 }
1205
1206 /* On error the ctx will be cleaned-up, so we don't need to
1207 * deal with any allocated nhlfe or nexthop structs here.
1208 */
1209
1210 return ret;
1211 }
1212
1213 /*
1214 * Capture information for an LSP update in a dplane context.
1215 */
1216 static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
1217 enum dplane_op_e op,
1218 struct zebra_pw *pw)
1219 {
1220 struct prefix p;
1221 afi_t afi;
1222 struct route_table *table;
1223 struct route_node *rn;
1224 struct route_entry *re;
1225
1226 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
1227 zlog_debug("init dplane ctx %s: pw '%s', loc %u, rem %u",
1228 dplane_op2str(op), pw->ifname, pw->local_label,
1229 pw->remote_label);
1230
1231 ctx->zd_op = op;
1232 ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
1233
1234 /* Capture namespace info: no netlink support as of 12/18,
1235 * but just in case...
1236 */
1237 dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT), false);
1238
1239 memset(&ctx->u.pw, 0, sizeof(ctx->u.pw));
1240
1241 /* This name appears to be c-string, so we use string copy. */
1242 strlcpy(ctx->u.pw.ifname, pw->ifname, sizeof(ctx->u.pw.ifname));
1243
1244 ctx->zd_vrf_id = pw->vrf_id;
1245 ctx->u.pw.ifindex = pw->ifindex;
1246 ctx->u.pw.type = pw->type;
1247 ctx->u.pw.af = pw->af;
1248 ctx->u.pw.local_label = pw->local_label;
1249 ctx->u.pw.remote_label = pw->remote_label;
1250 ctx->u.pw.flags = pw->flags;
1251
1252 ctx->u.pw.dest = pw->nexthop;
1253
1254 ctx->u.pw.fields = pw->data;
1255
1256 /* Capture nexthop info for the pw destination. We need to look
1257 * up and use zebra datastructs, but we're running in the zebra
1258 * pthread here so that should be ok.
1259 */
1260 memcpy(&p.u, &pw->nexthop, sizeof(pw->nexthop));
1261 p.family = pw->af;
1262 p.prefixlen = ((pw->af == AF_INET) ?
1263 IPV4_MAX_PREFIXLEN : IPV6_MAX_PREFIXLEN);
1264
1265 afi = (pw->af == AF_INET) ? AFI_IP : AFI_IP6;
1266 table = zebra_vrf_table(afi, SAFI_UNICAST, pw->vrf_id);
1267 if (table) {
1268 rn = route_node_match(table, &p);
1269 if (rn) {
1270 RNODE_FOREACH_RE(rn, re) {
1271 if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED))
1272 break;
1273 }
1274
1275 if (re)
1276 copy_nexthops(&(ctx->u.pw.nhg.nexthop),
1277 re->ng.nexthop, NULL);
1278
1279 route_unlock_node(rn);
1280 }
1281 }
1282
1283 return AOK;
1284 }
1285
1286 /*
1287 * Enqueue a new route update,
1288 * and ensure an event is active for the dataplane pthread.
1289 */
1290 static int dplane_route_enqueue(struct zebra_dplane_ctx *ctx)
1291 {
1292 int ret = EINVAL;
1293 uint32_t high, curr;
1294
1295 /* Enqueue for processing by the dataplane pthread */
1296 DPLANE_LOCK();
1297 {
1298 TAILQ_INSERT_TAIL(&zdplane_info.dg_route_ctx_q, ctx,
1299 zd_q_entries);
1300 }
1301 DPLANE_UNLOCK();
1302
1303 curr = atomic_add_fetch_explicit(
1304 #ifdef __clang__
1305 /* TODO -- issue with the clang atomic/intrinsics currently;
1306 * casting away the 'Atomic'-ness of the variable works.
1307 */
1308 (uint32_t *)&(zdplane_info.dg_routes_queued),
1309 #else
1310 &(zdplane_info.dg_routes_queued),
1311 #endif
1312 1, memory_order_seq_cst);
1313
1314 /* Maybe update high-water counter also */
1315 high = atomic_load_explicit(&zdplane_info.dg_routes_queued_max,
1316 memory_order_seq_cst);
1317 while (high < curr) {
1318 if (atomic_compare_exchange_weak_explicit(
1319 &zdplane_info.dg_routes_queued_max,
1320 &high, curr,
1321 memory_order_seq_cst,
1322 memory_order_seq_cst))
1323 break;
1324 }
1325
1326 /* Ensure that an event for the dataplane thread is active */
1327 ret = dplane_provider_work_ready();
1328
1329 return ret;
1330 }
1331
1332 /*
1333 * Utility that prepares a route update and enqueues it for processing
1334 */
1335 static enum zebra_dplane_result
1336 dplane_route_update_internal(struct route_node *rn,
1337 struct route_entry *re,
1338 struct route_entry *old_re,
1339 enum dplane_op_e op)
1340 {
1341 enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
1342 int ret = EINVAL;
1343 struct zebra_dplane_ctx *ctx = NULL;
1344
1345 /* Obtain context block */
1346 ctx = dplane_ctx_alloc();
1347 if (ctx == NULL) {
1348 ret = ENOMEM;
1349 goto done;
1350 }
1351
1352 /* Init context with info from zebra data structs */
1353 ret = dplane_ctx_route_init(ctx, op, rn, re);
1354 if (ret == AOK) {
1355 /* Capture some extra info for update case
1356 * where there's a different 'old' route.
1357 */
1358 if ((op == DPLANE_OP_ROUTE_UPDATE) &&
1359 old_re && (old_re != re)) {
1360 ctx->zd_is_update = true;
1361
1362 old_re->dplane_sequence =
1363 zebra_router_get_next_sequence();
1364 ctx->zd_old_seq = old_re->dplane_sequence;
1365
1366 ctx->u.rinfo.zd_old_tag = old_re->tag;
1367 ctx->u.rinfo.zd_old_type = old_re->type;
1368 ctx->u.rinfo.zd_old_instance = old_re->instance;
1369 ctx->u.rinfo.zd_old_distance = old_re->distance;
1370 ctx->u.rinfo.zd_old_metric = old_re->metric;
1371
1372 #ifndef HAVE_NETLINK
1373 /* For bsd, capture previous re's nexthops too, sigh.
1374 * We'll need these to do per-nexthop deletes.
1375 */
1376 copy_nexthops(&(ctx->u.rinfo.zd_old_ng.nexthop),
1377 old_re->ng.nexthop, NULL);
1378 #endif /* !HAVE_NETLINK */
1379 }
1380
1381 /* Enqueue context for processing */
1382 ret = dplane_route_enqueue(ctx);
1383 }
1384
1385 done:
1386 /* Update counter */
1387 atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1,
1388 memory_order_relaxed);
1389
1390 if (ret == AOK)
1391 result = ZEBRA_DPLANE_REQUEST_QUEUED;
1392 else {
1393 atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, 1,
1394 memory_order_relaxed);
1395 if (ctx)
1396 dplane_ctx_free(&ctx);
1397 }
1398
1399 return result;
1400 }
1401
1402 /*
1403 * Enqueue a route 'add' for the dataplane.
1404 */
1405 enum zebra_dplane_result dplane_route_add(struct route_node *rn,
1406 struct route_entry *re)
1407 {
1408 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1409
1410 if (rn == NULL || re == NULL)
1411 goto done;
1412
1413 ret = dplane_route_update_internal(rn, re, NULL,
1414 DPLANE_OP_ROUTE_INSTALL);
1415
1416 done:
1417 return ret;
1418 }
1419
1420 /*
1421 * Enqueue a route update for the dataplane.
1422 */
1423 enum zebra_dplane_result dplane_route_update(struct route_node *rn,
1424 struct route_entry *re,
1425 struct route_entry *old_re)
1426 {
1427 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1428
1429 if (rn == NULL || re == NULL)
1430 goto done;
1431
1432 ret = dplane_route_update_internal(rn, re, old_re,
1433 DPLANE_OP_ROUTE_UPDATE);
1434 done:
1435 return ret;
1436 }
1437
1438 /*
1439 * Enqueue a route removal for the dataplane.
1440 */
1441 enum zebra_dplane_result dplane_route_delete(struct route_node *rn,
1442 struct route_entry *re)
1443 {
1444 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1445
1446 if (rn == NULL || re == NULL)
1447 goto done;
1448
1449 ret = dplane_route_update_internal(rn, re, NULL,
1450 DPLANE_OP_ROUTE_DELETE);
1451
1452 done:
1453 return ret;
1454 }
1455
1456 /*
1457 * Notify the dplane when system/connected routes change.
1458 */
1459 enum zebra_dplane_result dplane_sys_route_add(struct route_node *rn,
1460 struct route_entry *re)
1461 {
1462 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1463
1464 /* Ignore this event unless a provider plugin has requested it. */
1465 if (!zdplane_info.dg_sys_route_notifs) {
1466 ret = ZEBRA_DPLANE_REQUEST_SUCCESS;
1467 goto done;
1468 }
1469
1470 if (rn == NULL || re == NULL)
1471 goto done;
1472
1473 ret = dplane_route_update_internal(rn, re, NULL,
1474 DPLANE_OP_SYS_ROUTE_ADD);
1475
1476 done:
1477 return ret;
1478 }
1479
1480 /*
1481 * Notify the dplane when system/connected routes are deleted.
1482 */
1483 enum zebra_dplane_result dplane_sys_route_del(struct route_node *rn,
1484 struct route_entry *re)
1485 {
1486 enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
1487
1488 /* Ignore this event unless a provider plugin has requested it. */
1489 if (!zdplane_info.dg_sys_route_notifs) {
1490 ret = ZEBRA_DPLANE_REQUEST_SUCCESS;
1491 goto done;
1492 }
1493
1494 if (rn == NULL || re == NULL)
1495 goto done;
1496
1497 ret = dplane_route_update_internal(rn, re, NULL,
1498 DPLANE_OP_SYS_ROUTE_DELETE);
1499
1500 done:
1501 return ret;
1502 }
1503
1504 /*
1505 * Enqueue LSP add for the dataplane.
1506 */
1507 enum zebra_dplane_result dplane_lsp_add(zebra_lsp_t *lsp)
1508 {
1509 enum zebra_dplane_result ret =
1510 lsp_update_internal(lsp, DPLANE_OP_LSP_INSTALL);
1511
1512 return ret;
1513 }
1514
1515 /*
1516 * Enqueue LSP update for the dataplane.
1517 */
1518 enum zebra_dplane_result dplane_lsp_update(zebra_lsp_t *lsp)
1519 {
1520 enum zebra_dplane_result ret =
1521 lsp_update_internal(lsp, DPLANE_OP_LSP_UPDATE);
1522
1523 return ret;
1524 }
1525
1526 /*
1527 * Enqueue LSP delete for the dataplane.
1528 */
1529 enum zebra_dplane_result dplane_lsp_delete(zebra_lsp_t *lsp)
1530 {
1531 enum zebra_dplane_result ret =
1532 lsp_update_internal(lsp, DPLANE_OP_LSP_DELETE);
1533
1534 return ret;
1535 }
1536
1537 /*
1538 * Enqueue pseudowire install for the dataplane.
1539 */
1540 enum zebra_dplane_result dplane_pw_install(struct zebra_pw *pw)
1541 {
1542 return pw_update_internal(pw, DPLANE_OP_PW_INSTALL);
1543 }
1544
1545 /*
1546 * Enqueue pseudowire un-install for the dataplane.
1547 */
1548 enum zebra_dplane_result dplane_pw_uninstall(struct zebra_pw *pw)
1549 {
1550 return pw_update_internal(pw, DPLANE_OP_PW_UNINSTALL);
1551 }
1552
1553 /*
1554 * Common internal LSP update utility
1555 */
1556 static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp,
1557 enum dplane_op_e op)
1558 {
1559 enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
1560 int ret = EINVAL;
1561 struct zebra_dplane_ctx *ctx = NULL;
1562
1563 /* Obtain context block */
1564 ctx = dplane_ctx_alloc();
1565 if (ctx == NULL) {
1566 ret = ENOMEM;
1567 goto done;
1568 }
1569
1570 ret = dplane_ctx_lsp_init(ctx, op, lsp);
1571 if (ret != AOK)
1572 goto done;
1573
1574 ret = dplane_route_enqueue(ctx);
1575
1576 done:
1577 /* Update counter */
1578 atomic_fetch_add_explicit(&zdplane_info.dg_lsps_in, 1,
1579 memory_order_relaxed);
1580
1581 if (ret == AOK)
1582 result = ZEBRA_DPLANE_REQUEST_QUEUED;
1583 else {
1584 atomic_fetch_add_explicit(&zdplane_info.dg_lsp_errors, 1,
1585 memory_order_relaxed);
1586 if (ctx)
1587 dplane_ctx_free(&ctx);
1588 }
1589
1590 return result;
1591 }
1592
1593 /*
1594 * Internal, common handler for pseudowire updates.
1595 */
1596 static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw,
1597 enum dplane_op_e op)
1598 {
1599 enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
1600 int ret;
1601 struct zebra_dplane_ctx *ctx = NULL;
1602
1603 ctx = dplane_ctx_alloc();
1604 if (ctx == NULL) {
1605 ret = ENOMEM;
1606 goto done;
1607 }
1608
1609 ret = dplane_ctx_pw_init(ctx, op, pw);
1610 if (ret != AOK)
1611 goto done;
1612
1613 ret = dplane_route_enqueue(ctx);
1614
1615 done:
1616 /* Update counter */
1617 atomic_fetch_add_explicit(&zdplane_info.dg_pws_in, 1,
1618 memory_order_relaxed);
1619
1620 if (ret == AOK)
1621 result = ZEBRA_DPLANE_REQUEST_QUEUED;
1622 else {
1623 atomic_fetch_add_explicit(&zdplane_info.dg_pw_errors, 1,
1624 memory_order_relaxed);
1625 if (ctx)
1626 dplane_ctx_free(&ctx);
1627 }
1628
1629 return result;
1630 }
1631
1632 /*
1633 * Enqueue interface address add for the dataplane.
1634 */
1635 enum zebra_dplane_result dplane_intf_addr_set(const struct interface *ifp,
1636 const struct connected *ifc)
1637 {
1638 #if !defined(HAVE_NETLINK) && defined(HAVE_STRUCT_IFALIASREQ)
1639 /* Extra checks for this OS path. */
1640
1641 /* Don't configure PtP addresses on broadcast ifs or reverse */
1642 if (!(ifp->flags & IFF_POINTOPOINT) != !CONNECTED_PEER(ifc)) {
1643 if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_DPLANE)
1644 zlog_debug("Failed to set intf addr: mismatch p2p and connected");
1645
1646 return ZEBRA_DPLANE_REQUEST_FAILURE;
1647 }
1648
1649 /* Ensure that no existing installed v4 route conflicts with
1650 * the new interface prefix. This check must be done in the
1651 * zebra pthread context, and any route delete (if needed)
1652 * is enqueued before the interface address programming attempt.
1653 */
1654 if (ifc->address->family == AF_INET) {
1655 struct prefix_ipv4 *p;
1656
1657 p = (struct prefix_ipv4 *)ifc->address;
1658 rib_lookup_and_pushup(p, ifp->vrf_id);
1659 }
1660 #endif
1661
1662 return intf_addr_update_internal(ifp, ifc, DPLANE_OP_ADDR_INSTALL);
1663 }
1664
1665 /*
1666 * Enqueue interface address remove/uninstall for the dataplane.
1667 */
1668 enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp,
1669 const struct connected *ifc)
1670 {
1671 return intf_addr_update_internal(ifp, ifc, DPLANE_OP_ADDR_UNINSTALL);
1672 }
1673
1674 static enum zebra_dplane_result intf_addr_update_internal(
1675 const struct interface *ifp, const struct connected *ifc,
1676 enum dplane_op_e op)
1677 {
1678 enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
1679 int ret = EINVAL;
1680 struct zebra_dplane_ctx *ctx = NULL;
1681 struct zebra_ns *zns;
1682
1683 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
1684 char addr_str[PREFIX_STRLEN];
1685
1686 prefix2str(ifc->address, addr_str, sizeof(addr_str));
1687
1688 zlog_debug("init intf ctx %s: idx %d, addr %u:%s",
1689 dplane_op2str(op), ifp->ifindex, ifp->vrf_id,
1690 addr_str);
1691 }
1692
1693 ctx = dplane_ctx_alloc();
1694 if (ctx == NULL) {
1695 ret = ENOMEM;
1696 goto done;
1697 }
1698
1699 ctx->zd_op = op;
1700 ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
1701 ctx->zd_vrf_id = ifp->vrf_id;
1702
1703 zns = zebra_ns_lookup(ifp->vrf_id);
1704 dplane_ctx_ns_init(ctx, zns, false);
1705
1706 /* Init the interface-addr-specific area */
1707 memset(&ctx->u.intf, 0, sizeof(ctx->u.intf));
1708
1709 strncpy(ctx->u.intf.ifname, ifp->name, sizeof(ctx->u.intf.ifname));
1710 ctx->u.intf.ifindex = ifp->ifindex;
1711 ctx->u.intf.prefix = *(ifc->address);
1712
1713 if (if_is_broadcast(ifp))
1714 ctx->u.intf.flags |= DPLANE_INTF_BROADCAST;
1715
1716 if (CONNECTED_PEER(ifc)) {
1717 ctx->u.intf.dest_prefix = *(ifc->destination);
1718 ctx->u.intf.flags |=
1719 (DPLANE_INTF_CONNECTED | DPLANE_INTF_HAS_DEST);
1720 } else if (ifc->destination) {
1721 ctx->u.intf.dest_prefix = *(ifc->destination);
1722 ctx->u.intf.flags |= DPLANE_INTF_HAS_DEST;
1723 }
1724
1725 if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY))
1726 ctx->u.intf.flags |= DPLANE_INTF_SECONDARY;
1727
1728 if (ifc->label) {
1729 size_t len;
1730
1731 ctx->u.intf.flags |= DPLANE_INTF_HAS_LABEL;
1732
1733 /* Use embedded buffer if it's adequate; else allocate. */
1734 len = strlen(ifc->label);
1735
1736 if (len < sizeof(ctx->u.intf.label_buf)) {
1737 strncpy(ctx->u.intf.label_buf, ifc->label,
1738 sizeof(ctx->u.intf.label_buf));
1739 ctx->u.intf.label = ctx->u.intf.label_buf;
1740 } else {
1741 ctx->u.intf.label = strdup(ifc->label);
1742 }
1743 }
1744
1745 ret = dplane_route_enqueue(ctx);
1746
1747 done:
1748
1749 /* Increment counter */
1750 atomic_fetch_add_explicit(&zdplane_info.dg_intf_addrs_in, 1,
1751 memory_order_relaxed);
1752
1753 if (ret == AOK)
1754 result = ZEBRA_DPLANE_REQUEST_QUEUED;
1755 else {
1756 /* Error counter */
1757 atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors,
1758 1, memory_order_relaxed);
1759 if (ctx)
1760 dplane_ctx_free(&ctx);
1761 }
1762
1763 return result;
1764 }
1765
1766 /*
1767 * Handler for 'show dplane'
1768 */
1769 int dplane_show_helper(struct vty *vty, bool detailed)
1770 {
1771 uint64_t queued, queue_max, limit, errs, incoming, yields,
1772 other_errs;
1773
1774 /* Using atomics because counters are being changed in different
1775 * pthread contexts.
1776 */
1777 incoming = atomic_load_explicit(&zdplane_info.dg_routes_in,
1778 memory_order_relaxed);
1779 limit = atomic_load_explicit(&zdplane_info.dg_max_queued_updates,
1780 memory_order_relaxed);
1781 queued = atomic_load_explicit(&zdplane_info.dg_routes_queued,
1782 memory_order_relaxed);
1783 queue_max = atomic_load_explicit(&zdplane_info.dg_routes_queued_max,
1784 memory_order_relaxed);
1785 errs = atomic_load_explicit(&zdplane_info.dg_route_errors,
1786 memory_order_relaxed);
1787 yields = atomic_load_explicit(&zdplane_info.dg_update_yields,
1788 memory_order_relaxed);
1789 other_errs = atomic_load_explicit(&zdplane_info.dg_other_errors,
1790 memory_order_relaxed);
1791
1792 vty_out(vty, "Zebra dataplane:\nRoute updates: %"PRIu64"\n",
1793 incoming);
1794 vty_out(vty, "Route update errors: %"PRIu64"\n", errs);
1795 vty_out(vty, "Other errors : %"PRIu64"\n", other_errs);
1796 vty_out(vty, "Route update queue limit: %"PRIu64"\n", limit);
1797 vty_out(vty, "Route update queue depth: %"PRIu64"\n", queued);
1798 vty_out(vty, "Route update queue max: %"PRIu64"\n", queue_max);
1799 vty_out(vty, "Dplane update yields: %"PRIu64"\n", yields);
1800
1801 return CMD_SUCCESS;
1802 }
1803
1804 /*
1805 * Handler for 'show dplane providers'
1806 */
1807 int dplane_show_provs_helper(struct vty *vty, bool detailed)
1808 {
1809 struct zebra_dplane_provider *prov;
1810 uint64_t in, in_max, out, out_max;
1811
1812 vty_out(vty, "Zebra dataplane providers:\n");
1813
1814 DPLANE_LOCK();
1815 prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
1816 DPLANE_UNLOCK();
1817
1818 /* Show counters, useful info from each registered provider */
1819 while (prov) {
1820
1821 in = atomic_load_explicit(&prov->dp_in_counter,
1822 memory_order_relaxed);
1823 in_max = atomic_load_explicit(&prov->dp_in_max,
1824 memory_order_relaxed);
1825 out = atomic_load_explicit(&prov->dp_out_counter,
1826 memory_order_relaxed);
1827 out_max = atomic_load_explicit(&prov->dp_out_max,
1828 memory_order_relaxed);
1829
1830 vty_out(vty, "%s (%u): in: %"PRIu64", q_max: %"PRIu64", "
1831 "out: %"PRIu64", q_max: %"PRIu64"\n",
1832 prov->dp_name, prov->dp_id, in, in_max, out, out_max);
1833
1834 DPLANE_LOCK();
1835 prov = TAILQ_NEXT(prov, dp_prov_link);
1836 DPLANE_UNLOCK();
1837 }
1838
1839 return CMD_SUCCESS;
1840 }
1841
1842 /*
1843 * Provider registration
1844 */
1845 int dplane_provider_register(const char *name,
1846 enum dplane_provider_prio prio,
1847 int flags,
1848 int (*fp)(struct zebra_dplane_provider *),
1849 int (*fini_fp)(struct zebra_dplane_provider *,
1850 bool early),
1851 void *data,
1852 struct zebra_dplane_provider **prov_p)
1853 {
1854 int ret = 0;
1855 struct zebra_dplane_provider *p = NULL, *last;
1856
1857 /* Validate */
1858 if (fp == NULL) {
1859 ret = EINVAL;
1860 goto done;
1861 }
1862
1863 if (prio <= DPLANE_PRIO_NONE ||
1864 prio > DPLANE_PRIO_LAST) {
1865 ret = EINVAL;
1866 goto done;
1867 }
1868
1869 /* Allocate and init new provider struct */
1870 p = XCALLOC(MTYPE_DP_PROV, sizeof(struct zebra_dplane_provider));
1871
1872 pthread_mutex_init(&(p->dp_mutex), NULL);
1873 TAILQ_INIT(&(p->dp_ctx_in_q));
1874 TAILQ_INIT(&(p->dp_ctx_out_q));
1875
1876 p->dp_priority = prio;
1877 p->dp_fp = fp;
1878 p->dp_fini = fini_fp;
1879 p->dp_data = data;
1880
1881 /* Lock - the dplane pthread may be running */
1882 DPLANE_LOCK();
1883
1884 p->dp_id = ++zdplane_info.dg_provider_id;
1885
1886 if (name)
1887 strlcpy(p->dp_name, name, DPLANE_PROVIDER_NAMELEN);
1888 else
1889 snprintf(p->dp_name, DPLANE_PROVIDER_NAMELEN,
1890 "provider-%u", p->dp_id);
1891
1892 /* Insert into list ordered by priority */
1893 TAILQ_FOREACH(last, &zdplane_info.dg_providers_q, dp_prov_link) {
1894 if (last->dp_priority > p->dp_priority)
1895 break;
1896 }
1897
1898 if (last)
1899 TAILQ_INSERT_BEFORE(last, p, dp_prov_link);
1900 else
1901 TAILQ_INSERT_TAIL(&zdplane_info.dg_providers_q, p,
1902 dp_prov_link);
1903
1904 /* And unlock */
1905 DPLANE_UNLOCK();
1906
1907 if (IS_ZEBRA_DEBUG_DPLANE)
1908 zlog_debug("dplane: registered new provider '%s' (%u), prio %d",
1909 p->dp_name, p->dp_id, p->dp_priority);
1910
1911 done:
1912 if (prov_p)
1913 *prov_p = p;
1914
1915 return ret;
1916 }
1917
1918 /* Accessors for provider attributes */
1919 const char *dplane_provider_get_name(const struct zebra_dplane_provider *prov)
1920 {
1921 return prov->dp_name;
1922 }
1923
1924 uint32_t dplane_provider_get_id(const struct zebra_dplane_provider *prov)
1925 {
1926 return prov->dp_id;
1927 }
1928
1929 void *dplane_provider_get_data(const struct zebra_dplane_provider *prov)
1930 {
1931 return prov->dp_data;
1932 }
1933
1934 int dplane_provider_get_work_limit(const struct zebra_dplane_provider *prov)
1935 {
1936 return zdplane_info.dg_updates_per_cycle;
1937 }
1938
1939 /* Lock/unlock a provider's mutex - iff the provider was registered with
1940 * the THREADED flag.
1941 */
1942 void dplane_provider_lock(struct zebra_dplane_provider *prov)
1943 {
1944 if (dplane_provider_is_threaded(prov))
1945 DPLANE_PROV_LOCK(prov);
1946 }
1947
1948 void dplane_provider_unlock(struct zebra_dplane_provider *prov)
1949 {
1950 if (dplane_provider_is_threaded(prov))
1951 DPLANE_PROV_UNLOCK(prov);
1952 }
1953
1954 /*
1955 * Dequeue and maintain associated counter
1956 */
1957 struct zebra_dplane_ctx *dplane_provider_dequeue_in_ctx(
1958 struct zebra_dplane_provider *prov)
1959 {
1960 struct zebra_dplane_ctx *ctx = NULL;
1961
1962 dplane_provider_lock(prov);
1963
1964 ctx = TAILQ_FIRST(&(prov->dp_ctx_in_q));
1965 if (ctx) {
1966 TAILQ_REMOVE(&(prov->dp_ctx_in_q), ctx, zd_q_entries);
1967
1968 atomic_fetch_sub_explicit(&prov->dp_in_queued, 1,
1969 memory_order_relaxed);
1970 }
1971
1972 dplane_provider_unlock(prov);
1973
1974 return ctx;
1975 }
1976
1977 /*
1978 * Dequeue work to a list, return count
1979 */
1980 int dplane_provider_dequeue_in_list(struct zebra_dplane_provider *prov,
1981 struct dplane_ctx_q *listp)
1982 {
1983 int limit, ret;
1984 struct zebra_dplane_ctx *ctx;
1985
1986 limit = zdplane_info.dg_updates_per_cycle;
1987
1988 dplane_provider_lock(prov);
1989
1990 for (ret = 0; ret < limit; ret++) {
1991 ctx = TAILQ_FIRST(&(prov->dp_ctx_in_q));
1992 if (ctx) {
1993 TAILQ_REMOVE(&(prov->dp_ctx_in_q), ctx, zd_q_entries);
1994
1995 TAILQ_INSERT_TAIL(listp, ctx, zd_q_entries);
1996 } else {
1997 break;
1998 }
1999 }
2000
2001 if (ret > 0)
2002 atomic_fetch_sub_explicit(&prov->dp_in_queued, ret,
2003 memory_order_relaxed);
2004
2005 dplane_provider_unlock(prov);
2006
2007 return ret;
2008 }
2009
2010 /*
2011 * Enqueue and maintain associated counter
2012 */
2013 void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider *prov,
2014 struct zebra_dplane_ctx *ctx)
2015 {
2016 dplane_provider_lock(prov);
2017
2018 TAILQ_INSERT_TAIL(&(prov->dp_ctx_out_q), ctx,
2019 zd_q_entries);
2020
2021 dplane_provider_unlock(prov);
2022
2023 atomic_fetch_add_explicit(&(prov->dp_out_counter), 1,
2024 memory_order_relaxed);
2025 }
2026
2027 /*
2028 * Accessor for provider object
2029 */
2030 bool dplane_provider_is_threaded(const struct zebra_dplane_provider *prov)
2031 {
2032 return (prov->dp_flags & DPLANE_PROV_FLAG_THREADED);
2033 }
2034
2035 /*
2036 * Internal helper that copies information from a zebra ns object; this is
2037 * called in the zebra main pthread context as part of dplane ctx init.
2038 */
2039 static void dplane_info_from_zns(struct zebra_dplane_info *ns_info,
2040 struct zebra_ns *zns)
2041 {
2042 ns_info->ns_id = zns->ns_id;
2043
2044 #if defined(HAVE_NETLINK)
2045 ns_info->is_cmd = true;
2046 ns_info->nls = zns->netlink_dplane;
2047 #endif /* NETLINK */
2048 }
2049
2050 /*
2051 * Provider api to signal that work/events are available
2052 * for the dataplane pthread.
2053 */
2054 int dplane_provider_work_ready(void)
2055 {
2056 /* Note that during zebra startup, we may be offered work before
2057 * the dataplane pthread (and thread-master) are ready. We want to
2058 * enqueue the work, but the event-scheduling machinery may not be
2059 * available.
2060 */
2061 if (zdplane_info.dg_run) {
2062 thread_add_event(zdplane_info.dg_master,
2063 dplane_thread_loop, NULL, 0,
2064 &zdplane_info.dg_t_update);
2065 }
2066
2067 return AOK;
2068 }
2069
2070 /*
2071 * Kernel dataplane provider
2072 */
2073
2074 /*
2075 * Handler for kernel LSP updates
2076 */
2077 static enum zebra_dplane_result
2078 kernel_dplane_lsp_update(struct zebra_dplane_ctx *ctx)
2079 {
2080 enum zebra_dplane_result res;
2081
2082 /* Call into the synchronous kernel-facing code here */
2083 res = kernel_lsp_update(ctx);
2084
2085 if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
2086 atomic_fetch_add_explicit(
2087 &zdplane_info.dg_lsp_errors, 1,
2088 memory_order_relaxed);
2089
2090 return res;
2091 }
2092
2093 /*
2094 * Handler for kernel pseudowire updates
2095 */
2096 static enum zebra_dplane_result
2097 kernel_dplane_pw_update(struct zebra_dplane_ctx *ctx)
2098 {
2099 enum zebra_dplane_result res;
2100
2101 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2102 zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u",
2103 dplane_ctx_get_pw_ifname(ctx),
2104 dplane_op2str(ctx->zd_op),
2105 dplane_ctx_get_pw_af(ctx),
2106 dplane_ctx_get_pw_local_label(ctx),
2107 dplane_ctx_get_pw_remote_label(ctx));
2108
2109 res = kernel_pw_update(ctx);
2110
2111 if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
2112 atomic_fetch_add_explicit(
2113 &zdplane_info.dg_pw_errors, 1,
2114 memory_order_relaxed);
2115
2116 return res;
2117 }
2118
2119 /*
2120 * Handler for kernel route updates
2121 */
2122 static enum zebra_dplane_result
2123 kernel_dplane_route_update(struct zebra_dplane_ctx *ctx)
2124 {
2125 enum zebra_dplane_result res;
2126
2127 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
2128 char dest_str[PREFIX_STRLEN];
2129
2130 prefix2str(dplane_ctx_get_dest(ctx),
2131 dest_str, sizeof(dest_str));
2132
2133 zlog_debug("%u:%s Dplane route update ctx %p op %s",
2134 dplane_ctx_get_vrf(ctx), dest_str,
2135 ctx, dplane_op2str(dplane_ctx_get_op(ctx)));
2136 }
2137
2138 /* Call into the synchronous kernel-facing code here */
2139 res = kernel_route_update(ctx);
2140
2141 if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
2142 atomic_fetch_add_explicit(
2143 &zdplane_info.dg_route_errors, 1,
2144 memory_order_relaxed);
2145
2146 return res;
2147 }
2148
2149 /*
2150 * Handler for kernel-facing interface address updates
2151 */
2152 static enum zebra_dplane_result
2153 kernel_dplane_address_update(struct zebra_dplane_ctx *ctx)
2154 {
2155 enum zebra_dplane_result res;
2156
2157
2158 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
2159 char dest_str[PREFIX_STRLEN];
2160
2161 prefix2str(dplane_ctx_get_intf_addr(ctx), dest_str,
2162 sizeof(dest_str));
2163
2164 zlog_debug("Dplane intf %s, idx %u, addr %s",
2165 dplane_op2str(dplane_ctx_get_op(ctx)),
2166 dplane_ctx_get_ifindex(ctx), dest_str);
2167 }
2168
2169 res = kernel_address_update_ctx(ctx);
2170
2171 if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
2172 atomic_fetch_add_explicit(&zdplane_info.dg_intf_addr_errors,
2173 1, memory_order_relaxed);
2174
2175 return res;
2176 }
2177
2178 /*
2179 * Kernel provider callback
2180 */
2181 static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
2182 {
2183 enum zebra_dplane_result res;
2184 struct zebra_dplane_ctx *ctx;
2185 int counter, limit;
2186
2187 limit = dplane_provider_get_work_limit(prov);
2188
2189 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2190 zlog_debug("dplane provider '%s': processing",
2191 dplane_provider_get_name(prov));
2192
2193 for (counter = 0; counter < limit; counter++) {
2194
2195 ctx = dplane_provider_dequeue_in_ctx(prov);
2196 if (ctx == NULL)
2197 break;
2198
2199 /* A previous provider plugin may have asked to skip the
2200 * kernel update.
2201 */
2202 if (dplane_ctx_is_skip_kernel(ctx)) {
2203 res = ZEBRA_DPLANE_REQUEST_SUCCESS;
2204 goto skip_one;
2205 }
2206
2207 /* Dispatch to appropriate kernel-facing apis */
2208 switch (dplane_ctx_get_op(ctx)) {
2209
2210 case DPLANE_OP_ROUTE_INSTALL:
2211 case DPLANE_OP_ROUTE_UPDATE:
2212 case DPLANE_OP_ROUTE_DELETE:
2213 res = kernel_dplane_route_update(ctx);
2214 break;
2215
2216 case DPLANE_OP_LSP_INSTALL:
2217 case DPLANE_OP_LSP_UPDATE:
2218 case DPLANE_OP_LSP_DELETE:
2219 res = kernel_dplane_lsp_update(ctx);
2220 break;
2221
2222 case DPLANE_OP_PW_INSTALL:
2223 case DPLANE_OP_PW_UNINSTALL:
2224 res = kernel_dplane_pw_update(ctx);
2225 break;
2226
2227 case DPLANE_OP_ADDR_INSTALL:
2228 case DPLANE_OP_ADDR_UNINSTALL:
2229 res = kernel_dplane_address_update(ctx);
2230 break;
2231
2232 /* Ignore system 'notifications' - the kernel already knows */
2233 case DPLANE_OP_SYS_ROUTE_ADD:
2234 case DPLANE_OP_SYS_ROUTE_DELETE:
2235 res = ZEBRA_DPLANE_REQUEST_SUCCESS;
2236 break;
2237
2238 default:
2239 atomic_fetch_add_explicit(
2240 &zdplane_info.dg_other_errors, 1,
2241 memory_order_relaxed);
2242
2243 res = ZEBRA_DPLANE_REQUEST_FAILURE;
2244 break;
2245 }
2246
2247 skip_one:
2248 dplane_ctx_set_status(ctx, res);
2249
2250 dplane_provider_enqueue_out_ctx(prov, ctx);
2251 }
2252
2253 /* Ensure that we'll run the work loop again if there's still
2254 * more work to do.
2255 */
2256 if (counter >= limit) {
2257 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2258 zlog_debug("dplane provider '%s' reached max updates %d",
2259 dplane_provider_get_name(prov), counter);
2260
2261 atomic_fetch_add_explicit(&zdplane_info.dg_update_yields,
2262 1, memory_order_relaxed);
2263
2264 dplane_provider_work_ready();
2265 }
2266
2267 return 0;
2268 }
2269
2270 #if DPLANE_TEST_PROVIDER
2271
2272 /*
2273 * Test dataplane provider plugin
2274 */
2275
2276 /*
2277 * Test provider process callback
2278 */
2279 static int test_dplane_process_func(struct zebra_dplane_provider *prov)
2280 {
2281 struct zebra_dplane_ctx *ctx;
2282 int counter, limit;
2283
2284 /* Just moving from 'in' queue to 'out' queue */
2285
2286 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2287 zlog_debug("dplane provider '%s': processing",
2288 dplane_provider_get_name(prov));
2289
2290 limit = dplane_provider_get_work_limit(prov);
2291
2292 for (counter = 0; counter < limit; counter++) {
2293
2294 ctx = dplane_provider_dequeue_in_ctx(prov);
2295 if (ctx == NULL)
2296 break;
2297
2298 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2299 zlog_debug("dplane provider '%s': op %s",
2300 dplane_provider_get_name(prov),
2301 dplane_op2str(dplane_ctx_get_op(ctx)));
2302
2303 dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
2304
2305 dplane_provider_enqueue_out_ctx(prov, ctx);
2306 }
2307
2308 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2309 zlog_debug("dplane provider '%s': processed %d",
2310 dplane_provider_get_name(prov), counter);
2311
2312 /* Ensure that we'll run the work loop again if there's still
2313 * more work to do.
2314 */
2315 if (counter >= limit)
2316 dplane_provider_work_ready();
2317
2318 return 0;
2319 }
2320
2321 /*
2322 * Test provider shutdown/fini callback
2323 */
2324 static int test_dplane_shutdown_func(struct zebra_dplane_provider *prov,
2325 bool early)
2326 {
2327 if (IS_ZEBRA_DEBUG_DPLANE)
2328 zlog_debug("dplane provider '%s': %sshutdown",
2329 dplane_provider_get_name(prov),
2330 early ? "early " : "");
2331
2332 return 0;
2333 }
2334 #endif /* DPLANE_TEST_PROVIDER */
2335
2336 /*
2337 * Register default kernel provider
2338 */
2339 static void dplane_provider_init(void)
2340 {
2341 int ret;
2342
2343 ret = dplane_provider_register("Kernel",
2344 DPLANE_PRIO_KERNEL,
2345 DPLANE_PROV_FLAGS_DEFAULT,
2346 kernel_dplane_process_func,
2347 NULL,
2348 NULL, NULL);
2349
2350 if (ret != AOK)
2351 zlog_err("Unable to register kernel dplane provider: %d",
2352 ret);
2353
2354 #if DPLANE_TEST_PROVIDER
2355 /* Optional test provider ... */
2356 ret = dplane_provider_register("Test",
2357 DPLANE_PRIO_PRE_KERNEL,
2358 DPLANE_PROV_FLAGS_DEFAULT,
2359 test_dplane_process_func,
2360 test_dplane_shutdown_func,
2361 NULL /* data */, NULL);
2362
2363 if (ret != AOK)
2364 zlog_err("Unable to register test dplane provider: %d",
2365 ret);
2366 #endif /* DPLANE_TEST_PROVIDER */
2367 }
2368
2369 /* Indicates zebra shutdown/exit is in progress. Some operations may be
2370 * simplified or skipped during shutdown processing.
2371 */
2372 bool dplane_is_in_shutdown(void)
2373 {
2374 return zdplane_info.dg_is_shutdown;
2375 }
2376
2377 /*
2378 * Early or pre-shutdown, de-init notification api. This runs pretty
2379 * early during zebra shutdown, as a signal to stop new work and prepare
2380 * for updates generated by shutdown/cleanup activity, as zebra tries to
2381 * remove everything it's responsible for.
2382 * NB: This runs in the main zebra pthread context.
2383 */
2384 void zebra_dplane_pre_finish(void)
2385 {
2386 if (IS_ZEBRA_DEBUG_DPLANE)
2387 zlog_debug("Zebra dataplane pre-fini called");
2388
2389 zdplane_info.dg_is_shutdown = true;
2390
2391 /* TODO -- Notify provider(s) of pending shutdown */
2392 }
2393
2394 /*
2395 * Utility to determine whether work remains enqueued within the dplane;
2396 * used during system shutdown processing.
2397 */
2398 static bool dplane_work_pending(void)
2399 {
2400 bool ret = false;
2401 struct zebra_dplane_ctx *ctx;
2402 struct zebra_dplane_provider *prov;
2403
2404 /* TODO -- just checking incoming/pending work for now, must check
2405 * providers
2406 */
2407 DPLANE_LOCK();
2408 {
2409 ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q);
2410 prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
2411 }
2412 DPLANE_UNLOCK();
2413
2414 if (ctx != NULL) {
2415 ret = true;
2416 goto done;
2417 }
2418
2419 while (prov) {
2420
2421 dplane_provider_lock(prov);
2422
2423 ctx = TAILQ_FIRST(&(prov->dp_ctx_in_q));
2424 if (ctx == NULL)
2425 ctx = TAILQ_FIRST(&(prov->dp_ctx_out_q));
2426
2427 dplane_provider_unlock(prov);
2428
2429 if (ctx != NULL)
2430 break;
2431
2432 DPLANE_LOCK();
2433 prov = TAILQ_NEXT(prov, dp_prov_link);
2434 DPLANE_UNLOCK();
2435 }
2436
2437 if (ctx != NULL)
2438 ret = true;
2439
2440 done:
2441 return ret;
2442 }
2443
2444 /*
2445 * Shutdown-time intermediate callback, used to determine when all pending
2446 * in-flight updates are done. If there's still work to do, reschedules itself.
2447 * If all work is done, schedules an event to the main zebra thread for
2448 * final zebra shutdown.
2449 * This runs in the dplane pthread context.
2450 */
2451 static int dplane_check_shutdown_status(struct thread *event)
2452 {
2453 if (IS_ZEBRA_DEBUG_DPLANE)
2454 zlog_debug("Zebra dataplane shutdown status check called");
2455
2456 if (dplane_work_pending()) {
2457 /* Reschedule dplane check on a short timer */
2458 thread_add_timer_msec(zdplane_info.dg_master,
2459 dplane_check_shutdown_status,
2460 NULL, 100,
2461 &zdplane_info.dg_t_shutdown_check);
2462
2463 /* TODO - give up and stop waiting after a short time? */
2464
2465 } else {
2466 /* We appear to be done - schedule a final callback event
2467 * for the zebra main pthread.
2468 */
2469 thread_add_event(zrouter.master, zebra_finalize, NULL, 0, NULL);
2470 }
2471
2472 return 0;
2473 }
2474
2475 /*
2476 * Shutdown, de-init api. This runs pretty late during shutdown,
2477 * after zebra has tried to free/remove/uninstall all routes during shutdown.
2478 * At this point, dplane work may still remain to be done, so we can't just
2479 * blindly terminate. If there's still work to do, we'll periodically check
2480 * and when done, we'll enqueue a task to the zebra main thread for final
2481 * termination processing.
2482 *
2483 * NB: This runs in the main zebra thread context.
2484 */
2485 void zebra_dplane_finish(void)
2486 {
2487 if (IS_ZEBRA_DEBUG_DPLANE)
2488 zlog_debug("Zebra dataplane fini called");
2489
2490 thread_add_event(zdplane_info.dg_master,
2491 dplane_check_shutdown_status, NULL, 0,
2492 &zdplane_info.dg_t_shutdown_check);
2493 }
2494
2495 /*
2496 * Main dataplane pthread event loop. The thread takes new incoming work
2497 * and offers it to the first provider. It then iterates through the
2498 * providers, taking complete work from each one and offering it
2499 * to the next in order. At each step, a limited number of updates are
2500 * processed during a cycle in order to provide some fairness.
2501 *
2502 * This loop through the providers is only run once, so that the dataplane
2503 * pthread can look for other pending work - such as i/o work on behalf of
2504 * providers.
2505 */
2506 static int dplane_thread_loop(struct thread *event)
2507 {
2508 struct dplane_ctx_q work_list;
2509 struct dplane_ctx_q error_list;
2510 struct zebra_dplane_provider *prov;
2511 struct zebra_dplane_ctx *ctx, *tctx;
2512 int limit, counter, error_counter;
2513 uint64_t curr, high;
2514
2515 /* Capture work limit per cycle */
2516 limit = zdplane_info.dg_updates_per_cycle;
2517
2518 /* Init temporary lists used to move contexts among providers */
2519 TAILQ_INIT(&work_list);
2520 TAILQ_INIT(&error_list);
2521 error_counter = 0;
2522
2523 /* Check for zebra shutdown */
2524 if (!zdplane_info.dg_run)
2525 goto done;
2526
2527 /* Dequeue some incoming work from zebra (if any) onto the temporary
2528 * working list.
2529 */
2530 DPLANE_LOCK();
2531
2532 /* Locate initial registered provider */
2533 prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
2534
2535 /* Move new work from incoming list to temp list */
2536 for (counter = 0; counter < limit; counter++) {
2537 ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q);
2538 if (ctx) {
2539 TAILQ_REMOVE(&zdplane_info.dg_route_ctx_q, ctx,
2540 zd_q_entries);
2541
2542 ctx->zd_provider = prov->dp_id;
2543
2544 TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries);
2545 } else {
2546 break;
2547 }
2548 }
2549
2550 DPLANE_UNLOCK();
2551
2552 atomic_fetch_sub_explicit(&zdplane_info.dg_routes_queued, counter,
2553 memory_order_relaxed);
2554
2555 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2556 zlog_debug("dplane: incoming new work counter: %d", counter);
2557
2558 /* Iterate through the registered providers, offering new incoming
2559 * work. If the provider has outgoing work in its queue, take that
2560 * work for the next provider
2561 */
2562 while (prov) {
2563
2564 /* At each iteration, the temporary work list has 'counter'
2565 * items.
2566 */
2567 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2568 zlog_debug("dplane enqueues %d new work to provider '%s'",
2569 counter, dplane_provider_get_name(prov));
2570
2571 /* Capture current provider id in each context; check for
2572 * error status.
2573 */
2574 TAILQ_FOREACH_SAFE(ctx, &work_list, zd_q_entries, tctx) {
2575 if (dplane_ctx_get_status(ctx) ==
2576 ZEBRA_DPLANE_REQUEST_SUCCESS) {
2577 ctx->zd_provider = prov->dp_id;
2578 } else {
2579 /*
2580 * TODO -- improve error-handling: recirc
2581 * errors backwards so that providers can
2582 * 'undo' their work (if they want to)
2583 */
2584
2585 /* Move to error list; will be returned
2586 * zebra main.
2587 */
2588 TAILQ_REMOVE(&work_list, ctx, zd_q_entries);
2589 TAILQ_INSERT_TAIL(&error_list,
2590 ctx, zd_q_entries);
2591 error_counter++;
2592 }
2593 }
2594
2595 /* Enqueue new work to the provider */
2596 dplane_provider_lock(prov);
2597
2598 if (TAILQ_FIRST(&work_list))
2599 TAILQ_CONCAT(&(prov->dp_ctx_in_q), &work_list,
2600 zd_q_entries);
2601
2602 atomic_fetch_add_explicit(&prov->dp_in_counter, counter,
2603 memory_order_relaxed);
2604 atomic_fetch_add_explicit(&prov->dp_in_queued, counter,
2605 memory_order_relaxed);
2606 curr = atomic_load_explicit(&prov->dp_in_queued,
2607 memory_order_relaxed);
2608 high = atomic_load_explicit(&prov->dp_in_max,
2609 memory_order_relaxed);
2610 if (curr > high)
2611 atomic_store_explicit(&prov->dp_in_max, curr,
2612 memory_order_relaxed);
2613
2614 dplane_provider_unlock(prov);
2615
2616 /* Reset the temp list (though the 'concat' may have done this
2617 * already), and the counter
2618 */
2619 TAILQ_INIT(&work_list);
2620 counter = 0;
2621
2622 /* Call into the provider code. Note that this is
2623 * unconditional: we offer to do work even if we don't enqueue
2624 * any _new_ work.
2625 */
2626 (*prov->dp_fp)(prov);
2627
2628 /* Check for zebra shutdown */
2629 if (!zdplane_info.dg_run)
2630 break;
2631
2632 /* Dequeue completed work from the provider */
2633 dplane_provider_lock(prov);
2634
2635 while (counter < limit) {
2636 ctx = TAILQ_FIRST(&(prov->dp_ctx_out_q));
2637 if (ctx) {
2638 TAILQ_REMOVE(&(prov->dp_ctx_out_q), ctx,
2639 zd_q_entries);
2640
2641 TAILQ_INSERT_TAIL(&work_list,
2642 ctx, zd_q_entries);
2643 counter++;
2644 } else
2645 break;
2646 }
2647
2648 dplane_provider_unlock(prov);
2649
2650 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2651 zlog_debug("dplane dequeues %d completed work from provider %s",
2652 counter, dplane_provider_get_name(prov));
2653
2654 /* Locate next provider */
2655 DPLANE_LOCK();
2656 prov = TAILQ_NEXT(prov, dp_prov_link);
2657 DPLANE_UNLOCK();
2658 }
2659
2660 /* After all providers have been serviced, enqueue any completed
2661 * work and any errors back to zebra so it can process the results.
2662 */
2663 if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
2664 zlog_debug("dplane has %d completed, %d errors, for zebra main",
2665 counter, error_counter);
2666
2667 /*
2668 * Hand lists through the api to zebra main,
2669 * to reduce the number of lock/unlock cycles
2670 */
2671
2672 /* Call through to zebra main */
2673 (zdplane_info.dg_results_cb)(&error_list);
2674
2675 TAILQ_INIT(&error_list);
2676
2677
2678 /* Call through to zebra main */
2679 (zdplane_info.dg_results_cb)(&work_list);
2680
2681 TAILQ_INIT(&work_list);
2682
2683 done:
2684 return 0;
2685 }
2686
2687 /*
2688 * Final phase of shutdown, after all work enqueued to dplane has been
2689 * processed. This is called from the zebra main pthread context.
2690 */
2691 void zebra_dplane_shutdown(void)
2692 {
2693 if (IS_ZEBRA_DEBUG_DPLANE)
2694 zlog_debug("Zebra dataplane shutdown called");
2695
2696 /* Stop dplane thread, if it's running */
2697
2698 zdplane_info.dg_run = false;
2699
2700 THREAD_OFF(zdplane_info.dg_t_update);
2701
2702 frr_pthread_stop(zdplane_info.dg_pthread, NULL);
2703
2704 /* Destroy pthread */
2705 frr_pthread_destroy(zdplane_info.dg_pthread);
2706 zdplane_info.dg_pthread = NULL;
2707 zdplane_info.dg_master = NULL;
2708
2709 /* TODO -- Notify provider(s) of final shutdown */
2710
2711 /* TODO -- Clean-up provider objects */
2712
2713 /* TODO -- Clean queue(s), free memory */
2714 }
2715
2716 /*
2717 * Initialize the dataplane module during startup, internal/private version
2718 */
2719 static void zebra_dplane_init_internal(void)
2720 {
2721 memset(&zdplane_info, 0, sizeof(zdplane_info));
2722
2723 pthread_mutex_init(&zdplane_info.dg_mutex, NULL);
2724
2725 TAILQ_INIT(&zdplane_info.dg_route_ctx_q);
2726 TAILQ_INIT(&zdplane_info.dg_providers_q);
2727
2728 zdplane_info.dg_updates_per_cycle = DPLANE_DEFAULT_NEW_WORK;
2729
2730 zdplane_info.dg_max_queued_updates = DPLANE_DEFAULT_MAX_QUEUED;
2731
2732 /* Register default kernel 'provider' during init */
2733 dplane_provider_init();
2734 }
2735
2736 /*
2737 * Start the dataplane pthread. This step needs to be run later than the
2738 * 'init' step, in case zebra has fork-ed.
2739 */
2740 void zebra_dplane_start(void)
2741 {
2742 /* Start dataplane pthread */
2743
2744 struct frr_pthread_attr pattr = {
2745 .start = frr_pthread_attr_default.start,
2746 .stop = frr_pthread_attr_default.stop
2747 };
2748
2749 zdplane_info.dg_pthread = frr_pthread_new(&pattr, "Zebra dplane thread",
2750 "Zebra dplane");
2751
2752 zdplane_info.dg_master = zdplane_info.dg_pthread->master;
2753
2754 zdplane_info.dg_run = true;
2755
2756 /* Enqueue an initial event for the dataplane pthread */
2757 thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0,
2758 &zdplane_info.dg_t_update);
2759
2760 frr_pthread_run(zdplane_info.dg_pthread, NULL);
2761 }
2762
2763 /*
2764 * Initialize the dataplane module at startup; called by zebra rib_init()
2765 */
2766 void zebra_dplane_init(int (*results_fp)(struct dplane_ctx_q *))
2767 {
2768 zebra_dplane_init_internal();
2769 zdplane_info.dg_results_cb = results_fp;
2770 }