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