]> git.proxmox.com Git - mirror_frr.git/blame - zebra/dplane_fpm_nl.c
zebra: CLI commands for new FPM interface
[mirror_frr.git] / zebra / dplane_fpm_nl.c
CommitLineData
d35f447d
RZ
1/*
2 * Zebra dataplane plugin for Forwarding Plane Manager (FPM) using netlink.
3 *
4 * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
5 * Rafael Zalamena
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; see the file COPYING; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <arpa/inet.h>
23
24#include <sys/types.h>
25#include <sys/socket.h>
26
27#include <errno.h>
28#include <string.h>
29
30#include "config.h" /* Include this explicitly */
31#include "lib/zebra.h"
32#include "lib/libfrr.h"
3bdd7fca 33#include "lib/command.h"
d35f447d
RZ
34#include "lib/memory.h"
35#include "lib/network.h"
36#include "lib/ns.h"
37#include "lib/frr_pthread.h"
bda10adf 38#include "zebra/interface.h"
d35f447d 39#include "zebra/zebra_dplane.h"
018e77bc 40#include "zebra/zebra_router.h"
bda10adf 41#include "zebra/zebra_vxlan_private.h"
d35f447d
RZ
42#include "zebra/kernel_netlink.h"
43#include "zebra/rt_netlink.h"
44#include "zebra/debug.h"
45
46#define SOUTHBOUND_DEFAULT_ADDR INADDR_LOOPBACK
47#define SOUTHBOUND_DEFAULT_PORT 2620
48
49static const char *prov_name = "dplane_fpm_nl";
50
51struct fpm_nl_ctx {
52 /* data plane connection. */
53 int socket;
3bdd7fca 54 bool disabled;
d35f447d 55 bool connecting;
018e77bc 56 bool rib_complete;
bda10adf 57 bool rmac_complete;
d35f447d
RZ
58 struct sockaddr_storage addr;
59
60 /* data plane buffers. */
61 struct stream *ibuf;
62 struct stream *obuf;
63 pthread_mutex_t obuf_mutex;
64
65 /* data plane events. */
66 struct frr_pthread *fthread;
67 struct thread *t_connect;
68 struct thread *t_read;
69 struct thread *t_write;
3bdd7fca 70 struct thread *t_event;
018e77bc
RZ
71
72 /* zebra events. */
73 struct thread *t_ribreset;
74 struct thread *t_ribwalk;
bda10adf
RZ
75 struct thread *t_rmacreset;
76 struct thread *t_rmacwalk;
3bdd7fca
RZ
77} * gfnc;
78
79enum fpm_nl_events {
80 /* Ask for FPM to reconnect the external server. */
81 FNE_RECONNECT,
82 /* Disable FPM. */
83 FNE_DISABLE,
d35f447d
RZ
84};
85
018e77bc
RZ
86/*
87 * Prototypes.
88 */
3bdd7fca 89static int fpm_process_event(struct thread *t);
018e77bc
RZ
90static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx);
91static int fpm_rib_send(struct thread *t);
92static int fpm_rib_reset(struct thread *t);
bda10adf
RZ
93static int fpm_rmac_send(struct thread *t);
94static int fpm_rmac_reset(struct thread *t);
018e77bc 95
3bdd7fca
RZ
96/*
97 * CLI.
98 */
99DEFUN(fpm_set_address, fpm_set_address_cmd,
100 "fpm address <A.B.C.D|X:X::X:X> [port (1-65535)]",
101 "Forwarding Plane Manager configuration\n"
102 "FPM remote listening server address\n"
103 "Remote IPv4 FPM server\n"
104 "Remote IPv6 FPM server\n"
105 "FPM remote listening server port\n"
106 "Remote FPM server port\n")
107{
108 struct sockaddr_in *sin;
109 struct sockaddr_in6 *sin6;
110 uint16_t port = 0;
111 uint8_t naddr[INET6_BUFSIZ];
112
113 if (argc == 5)
114 port = strtol(argv[4]->arg, NULL, 10);
115
116 /* Handle IPv4 addresses. */
117 if (inet_pton(AF_INET, argv[2]->arg, naddr) == 1) {
118 sin = (struct sockaddr_in *)&gfnc->addr;
119
120 memset(sin, 0, sizeof(*sin));
121 sin->sin_family = AF_INET;
122 sin->sin_port =
123 port ? htons(port) : htons(SOUTHBOUND_DEFAULT_PORT);
124#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
125 sin->sin_len = sizeof(*sin);
126#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
127 memcpy(&sin->sin_addr, naddr, sizeof(sin->sin_addr));
128
129 goto ask_reconnect;
130 }
131
132 /* Handle IPv6 addresses. */
133 if (inet_pton(AF_INET6, argv[2]->arg, naddr) != 1) {
134 vty_out(vty, "%% Invalid address: %s\n", argv[2]->arg);
135 return CMD_WARNING;
136 }
137
138 sin6 = (struct sockaddr_in6 *)&gfnc->addr;
139 memset(sin6, 0, sizeof(*sin6));
140 sin6->sin6_family = AF_INET6;
141 sin6->sin6_port = port ? htons(port) : htons(SOUTHBOUND_DEFAULT_PORT);
142#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
143 sin6->sin6_len = sizeof(*sin6);
144#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
145 memcpy(&sin6->sin6_addr, naddr, sizeof(sin6->sin6_addr));
146
147ask_reconnect:
148 thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc,
149 FNE_RECONNECT, &gfnc->t_event);
150 return CMD_SUCCESS;
151}
152
153DEFUN(no_fpm_set_address, no_fpm_set_address_cmd,
154 "no fpm address [<A.B.C.D|X:X::X:X> [port <1-65535>]]",
155 NO_STR
156 "Forwarding Plane Manager configuration\n"
157 "FPM remote listening server address\n"
158 "Remote IPv4 FPM server\n"
159 "Remote IPv6 FPM server\n"
160 "FPM remote listening server port\n"
161 "Remote FPM server port\n")
162{
163 thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc,
164 FNE_DISABLE, &gfnc->t_event);
165 return CMD_SUCCESS;
166}
167
168static int fpm_write_config(struct vty *vty)
169{
170 struct sockaddr_in *sin;
171 struct sockaddr_in6 *sin6;
172 int written = 0;
173 char addrstr[INET6_ADDRSTRLEN];
174
175 if (gfnc->disabled)
176 return written;
177
178 switch (gfnc->addr.ss_family) {
179 case AF_INET:
180 written = 1;
181 sin = (struct sockaddr_in *)&gfnc->addr;
182 inet_ntop(AF_INET, &sin->sin_addr, addrstr, sizeof(addrstr));
183 vty_out(vty, "fpm address %s", addrstr);
184 if (sin->sin_port != htons(SOUTHBOUND_DEFAULT_PORT))
185 vty_out(vty, " port %d", ntohs(sin->sin_port));
186
187 vty_out(vty, "\n");
188 break;
189 case AF_INET6:
190 written = 1;
191 sin6 = (struct sockaddr_in6 *)&gfnc->addr;
192 inet_ntop(AF_INET, &sin6->sin6_addr, addrstr, sizeof(addrstr));
193 vty_out(vty, "fpm address %s", addrstr);
194 if (sin6->sin6_port != htons(SOUTHBOUND_DEFAULT_PORT))
195 vty_out(vty, " port %d", ntohs(sin6->sin6_port));
196
197 vty_out(vty, "\n");
198 break;
199
200 default:
201 break;
202 }
203
204 return written;
205}
206
207struct cmd_node fpm_node = {
208 .node = VTY_NODE,
209 .prompt = "",
210 .vtysh = 1,
211};
212
d35f447d
RZ
213/*
214 * FPM functions.
215 */
216static int fpm_connect(struct thread *t);
217
218static void fpm_reconnect(struct fpm_nl_ctx *fnc)
219{
220 /* Grab the lock to empty the stream and stop the zebra thread. */
221 frr_mutex_lock_autounlock(&fnc->obuf_mutex);
222
3bdd7fca
RZ
223 /* Avoid calling close on `-1`. */
224 if (fnc->socket != -1) {
225 close(fnc->socket);
226 fnc->socket = -1;
227 }
228
d35f447d
RZ
229 stream_reset(fnc->ibuf);
230 stream_reset(fnc->obuf);
231 THREAD_OFF(fnc->t_read);
232 THREAD_OFF(fnc->t_write);
018e77bc
RZ
233
234 if (fnc->t_ribreset)
235 thread_cancel_async(zrouter.master, &fnc->t_ribreset, NULL);
236 if (fnc->t_ribwalk)
237 thread_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL);
bda10adf
RZ
238 if (fnc->t_rmacreset)
239 thread_cancel_async(zrouter.master, &fnc->t_rmacreset, NULL);
240 if (fnc->t_rmacwalk)
241 thread_cancel_async(zrouter.master, &fnc->t_rmacwalk, NULL);
018e77bc 242
3bdd7fca
RZ
243 /* FPM is disabled, don't attempt to connect. */
244 if (fnc->disabled)
245 return;
246
d35f447d
RZ
247 thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 3,
248 &fnc->t_connect);
249}
250
251static int fpm_read(struct thread *t)
252{
253 struct fpm_nl_ctx *fnc = THREAD_ARG(t);
254 ssize_t rv;
255
256 /* Let's ignore the input at the moment. */
257 rv = stream_read_try(fnc->ibuf, fnc->socket,
258 STREAM_WRITEABLE(fnc->ibuf));
259 if (rv == 0) {
260 zlog_debug("%s: connection closed", __func__);
261 fpm_reconnect(fnc);
262 return 0;
263 }
264 if (rv == -1) {
265 if (errno == EAGAIN || errno == EWOULDBLOCK
266 || errno == EINTR)
267 return 0;
268
269 zlog_debug("%s: connection failure: %s", __func__,
270 strerror(errno));
271 fpm_reconnect(fnc);
272 return 0;
273 }
274 stream_reset(fnc->ibuf);
275
276 thread_add_read(fnc->fthread->master, fpm_read, fnc, fnc->socket,
277 &fnc->t_read);
278
279 return 0;
280}
281
282static int fpm_write(struct thread *t)
283{
284 struct fpm_nl_ctx *fnc = THREAD_ARG(t);
285 socklen_t statuslen;
286 ssize_t bwritten;
287 int rv, status;
288 size_t btotal;
289
290 if (fnc->connecting == true) {
291 status = 0;
292 statuslen = sizeof(status);
293
294 rv = getsockopt(fnc->socket, SOL_SOCKET, SO_ERROR, &status,
295 &statuslen);
296 if (rv == -1 || status != 0) {
297 if (rv != -1)
298 zlog_debug("%s: connection failed: %s",
299 __func__, strerror(status));
300 else
301 zlog_debug("%s: SO_ERROR failed: %s", __func__,
302 strerror(status));
303
304 fpm_reconnect(fnc);
305 return 0;
306 }
307
308 fnc->connecting = false;
018e77bc
RZ
309
310 /* Ask zebra main thread to start walking the RIB table. */
311 thread_add_timer(zrouter.master, fpm_rib_send, fnc, 0,
312 &fnc->t_ribwalk);
bda10adf
RZ
313 thread_add_timer(zrouter.master, fpm_rmac_send, fnc, 0,
314 &fnc->t_rmacwalk);
d35f447d
RZ
315 }
316
317 frr_mutex_lock_autounlock(&fnc->obuf_mutex);
318
319 while (true) {
320 /* Stream is empty: reset pointers and return. */
321 if (STREAM_READABLE(fnc->obuf) == 0) {
322 stream_reset(fnc->obuf);
323 break;
324 }
325
326 /* Try to write all at once. */
327 btotal = stream_get_endp(fnc->obuf) -
328 stream_get_getp(fnc->obuf);
329 bwritten = write(fnc->socket, stream_pnt(fnc->obuf), btotal);
330 if (bwritten == 0) {
331 zlog_debug("%s: connection closed", __func__);
332 break;
333 }
334 if (bwritten == -1) {
335 if (errno == EAGAIN || errno == EWOULDBLOCK
336 || errno == EINTR)
337 break;
338
339 zlog_debug("%s: connection failure: %s", __func__,
340 strerror(errno));
341 fpm_reconnect(fnc);
342 break;
343 }
344
345 stream_forward_getp(fnc->obuf, (size_t)bwritten);
346 }
347
348 /* Stream is not empty yet, we must schedule more writes. */
349 if (STREAM_READABLE(fnc->obuf)) {
350 thread_add_write(fnc->fthread->master, fpm_write, fnc,
351 fnc->socket, &fnc->t_write);
352 return 0;
353 }
354
355 return 0;
356}
357
358static int fpm_connect(struct thread *t)
359{
360 struct fpm_nl_ctx *fnc = THREAD_ARG(t);
3bdd7fca
RZ
361 struct sockaddr_in *sin = (struct sockaddr_in *)&fnc->addr;
362 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&fnc->addr;
363 socklen_t slen;
d35f447d
RZ
364 int rv, sock;
365 char addrstr[INET6_ADDRSTRLEN];
366
3bdd7fca 367 sock = socket(fnc->addr.ss_family, SOCK_STREAM, 0);
d35f447d
RZ
368 if (sock == -1) {
369 zlog_err("%s: fpm connection failed: %s", __func__,
370 strerror(errno));
371 thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 3,
372 &fnc->t_connect);
373 return 0;
374 }
375
376 set_nonblocking(sock);
377
3bdd7fca
RZ
378 if (fnc->addr.ss_family == AF_INET) {
379 inet_ntop(AF_INET, &sin->sin_addr, addrstr, sizeof(addrstr));
380 slen = sizeof(*sin);
381 } else {
382 inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr, sizeof(addrstr));
383 slen = sizeof(*sin6);
384 }
d35f447d 385
d35f447d
RZ
386 zlog_debug("%s: attempting to connect to %s:%d", __func__, addrstr,
387 ntohs(sin->sin_port));
388
3bdd7fca 389 rv = connect(sock, (struct sockaddr *)&fnc->addr, slen);
d35f447d
RZ
390 if (rv == -1 && errno != EINPROGRESS) {
391 close(sock);
392 zlog_warn("%s: fpm connection failed: %s", __func__,
393 strerror(errno));
394 thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 3,
395 &fnc->t_connect);
396 return 0;
397 }
398
399 fnc->connecting = (errno == EINPROGRESS);
400 fnc->socket = sock;
401 thread_add_read(fnc->fthread->master, fpm_read, fnc, sock,
402 &fnc->t_read);
403 thread_add_write(fnc->fthread->master, fpm_write, fnc, sock,
404 &fnc->t_write);
405
018e77bc
RZ
406 /* Mark all routes as unsent. */
407 thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0,
408 &fnc->t_ribreset);
bda10adf
RZ
409 thread_add_timer(zrouter.master, fpm_rmac_reset, fnc, 0,
410 &fnc->t_rmacreset);
018e77bc 411
d35f447d
RZ
412 return 0;
413}
414
415/**
416 * Encode data plane operation context into netlink and enqueue it in the FPM
417 * output buffer.
418 *
419 * @param fnc the netlink FPM context.
420 * @param ctx the data plane operation context data.
421 * @return 0 on success or -1 on not enough space.
422 */
423static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx)
424{
425 uint8_t nl_buf[NL_PKT_BUF_SIZE];
426 size_t nl_buf_len;
427 ssize_t rv;
428
429 nl_buf_len = 0;
430
431 frr_mutex_lock_autounlock(&fnc->obuf_mutex);
432
433 switch (dplane_ctx_get_op(ctx)) {
434 case DPLANE_OP_ROUTE_UPDATE:
435 case DPLANE_OP_ROUTE_DELETE:
436 rv = netlink_route_multipath(RTM_DELROUTE, ctx, nl_buf,
437 sizeof(nl_buf));
438 if (rv <= 0) {
439 zlog_debug("%s: netlink_route_multipath failed",
440 __func__);
441 return 0;
442 }
443
444 nl_buf_len = (size_t)rv;
445 if (STREAM_WRITEABLE(fnc->obuf) < nl_buf_len) {
446 zlog_debug("%s: not enough output buffer (%ld vs %lu)",
447 __func__, STREAM_WRITEABLE(fnc->obuf),
448 nl_buf_len);
449 return -1;
450 }
451
452 /* UPDATE operations need a INSTALL, otherwise just quit. */
453 if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE)
454 break;
455
456 /* FALL THROUGH */
457 case DPLANE_OP_ROUTE_INSTALL:
458 rv = netlink_route_multipath(RTM_NEWROUTE, ctx,
459 &nl_buf[nl_buf_len],
460 sizeof(nl_buf) - nl_buf_len);
461 if (rv <= 0) {
462 zlog_debug("%s: netlink_route_multipath failed",
463 __func__);
464 return 0;
465 }
466
467 nl_buf_len += (size_t)rv;
468 if (STREAM_WRITEABLE(fnc->obuf) < nl_buf_len) {
469 zlog_debug("%s: not enough output buffer (%ld vs %lu)",
470 __func__, STREAM_WRITEABLE(fnc->obuf),
471 nl_buf_len);
472 return -1;
473 }
474 break;
475
bda10adf
RZ
476 case DPLANE_OP_MAC_INSTALL:
477 case DPLANE_OP_MAC_DELETE:
478 rv = netlink_macfdb_update_ctx(ctx, nl_buf, sizeof(nl_buf));
479 if (rv <= 0) {
480 zlog_debug("%s: netlink_macfdb_update_ctx failed",
481 __func__);
482 return 0;
483 }
484
485 nl_buf_len = (size_t)rv;
486 if (STREAM_WRITEABLE(fnc->obuf) < nl_buf_len) {
487 zlog_debug("%s: not enough output buffer (%ld vs %lu)",
488 __func__, STREAM_WRITEABLE(fnc->obuf),
489 nl_buf_len);
490 return -1;
491 }
492 break;
493
d35f447d
RZ
494 case DPLANE_OP_NH_INSTALL:
495 case DPLANE_OP_NH_UPDATE:
496 case DPLANE_OP_NH_DELETE:
497 case DPLANE_OP_LSP_INSTALL:
498 case DPLANE_OP_LSP_UPDATE:
499 case DPLANE_OP_LSP_DELETE:
500 case DPLANE_OP_PW_INSTALL:
501 case DPLANE_OP_PW_UNINSTALL:
502 case DPLANE_OP_ADDR_INSTALL:
503 case DPLANE_OP_ADDR_UNINSTALL:
d35f447d
RZ
504 case DPLANE_OP_NEIGH_INSTALL:
505 case DPLANE_OP_NEIGH_UPDATE:
506 case DPLANE_OP_NEIGH_DELETE:
507 case DPLANE_OP_VTEP_ADD:
508 case DPLANE_OP_VTEP_DELETE:
509 case DPLANE_OP_SYS_ROUTE_ADD:
510 case DPLANE_OP_SYS_ROUTE_DELETE:
511 case DPLANE_OP_ROUTE_NOTIFY:
512 case DPLANE_OP_LSP_NOTIFY:
513 case DPLANE_OP_NONE:
514 break;
515
516 default:
517 zlog_debug("%s: unhandled data plane message (%d) %s",
518 __func__, dplane_ctx_get_op(ctx),
519 dplane_op2str(dplane_ctx_get_op(ctx)));
520 break;
521 }
522
523 /* Skip empty enqueues. */
524 if (nl_buf_len == 0)
525 return 0;
526
527 /*
528 * FPM header:
529 * {
530 * version: 1 byte (always 1),
531 * type: 1 byte (1 for netlink, 2 protobuf),
532 * len: 2 bytes (network order),
533 * }
534 */
535 stream_putc(fnc->obuf, 1);
536 stream_putc(fnc->obuf, 1);
537 assert(nl_buf_len < UINT16_MAX);
538 stream_putw(fnc->obuf, nl_buf_len + 4);
539
540 /* Write current data. */
541 stream_write(fnc->obuf, nl_buf, (size_t)nl_buf_len);
542
543 /* Tell the thread to start writing. */
544 thread_add_write(fnc->fthread->master, fpm_write, fnc, fnc->socket,
545 &fnc->t_write);
546
547 return 0;
548}
549
018e77bc
RZ
550/**
551 * Send all RIB installed routes to the connected data plane.
552 */
553static int fpm_rib_send(struct thread *t)
554{
555 struct fpm_nl_ctx *fnc = THREAD_ARG(t);
556 rib_dest_t *dest;
557 struct route_node *rn;
558 struct route_table *rt;
559 struct zebra_dplane_ctx *ctx;
560 rib_tables_iter_t rt_iter;
561
562 /* Allocate temporary context for all transactions. */
563 ctx = dplane_ctx_alloc();
564
565 rt_iter.state = RIB_TABLES_ITER_S_INIT;
566 while ((rt = rib_tables_iter_next(&rt_iter))) {
567 for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
568 dest = rib_dest_from_rnode(rn);
569 /* Skip bad route entries. */
570 if (dest == NULL || dest->selected_fib == NULL) {
571 continue;
572 }
573
574 /* Check for already sent routes. */
575 if (CHECK_FLAG(dest->flags, RIB_DEST_UPDATE_FPM)) {
576 continue;
577 }
578
579 /* Enqueue route install. */
580 dplane_ctx_reset(ctx);
581 dplane_ctx_route_init(ctx, DPLANE_OP_ROUTE_INSTALL, rn,
582 dest->selected_fib);
583 if (fpm_nl_enqueue(fnc, ctx) == -1) {
584 /* Free the temporary allocated context. */
585 dplane_ctx_fini(&ctx);
586
587 zlog_debug("%s: buffer full, come back later",
588 __func__);
589 thread_add_timer(zrouter.master, fpm_rib_send,
590 fnc, 1, &fnc->t_ribwalk);
591 return 0;
592 }
593
594 /* Mark as sent. */
595 SET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM);
596 }
597 }
598
599 /* Free the temporary allocated context. */
600 dplane_ctx_fini(&ctx);
601
602 /* All RIB routes sent! */
603 fnc->rib_complete = true;
604
605 return 0;
606}
607
bda10adf
RZ
608/*
609 * The next three functions will handle RMAC enqueue.
610 */
611struct fpm_rmac_arg {
612 struct zebra_dplane_ctx *ctx;
613 struct fpm_nl_ctx *fnc;
614 zebra_l3vni_t *zl3vni;
615};
616
617static void fpm_enqueue_rmac_table(struct hash_backet *backet, void *arg)
618{
619 struct fpm_rmac_arg *fra = arg;
620 zebra_mac_t *zrmac = backet->data;
621 struct zebra_if *zif = fra->zl3vni->vxlan_if->info;
622 const struct zebra_l2info_vxlan *vxl = &zif->l2info.vxl;
623 struct zebra_if *br_zif;
624 vlanid_t vid;
625 bool sticky;
626
627 /* Entry already sent. */
628 if (CHECK_FLAG(zrmac->flags, ZEBRA_MAC_FPM_SENT))
629 return;
630
631 sticky = !!CHECK_FLAG(zrmac->flags,
632 (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW));
633 br_zif = (struct zebra_if *)(zif->brslave_info.br_if->info);
634 vid = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) ? vxl->access_vlan : 0;
635
636 dplane_ctx_reset(fra->ctx);
637 dplane_ctx_set_op(fra->ctx, DPLANE_OP_MAC_INSTALL);
638 dplane_mac_init(fra->ctx, fra->zl3vni->vxlan_if,
639 zif->brslave_info.br_if, vid, &zrmac->macaddr,
640 zrmac->fwd_info.r_vtep_ip, sticky);
641 if (fpm_nl_enqueue(fra->fnc, fra->ctx) == -1) {
642 zlog_debug("%s: buffer full, come back later",
643 __func__);
644 thread_add_timer(zrouter.master, fpm_rmac_send,
645 fra->fnc, 1, &fra->fnc->t_rmacwalk);
646 }
647}
648
649static void fpm_enqueue_l3vni_table(struct hash_backet *backet, void *arg)
650{
651 struct fpm_rmac_arg *fra = arg;
652 zebra_l3vni_t *zl3vni = backet->data;
653
654 fra->zl3vni = zl3vni;
655 hash_iterate(zl3vni->rmac_table, fpm_enqueue_rmac_table, zl3vni);
656}
657
658static int fpm_rmac_send(struct thread *t)
659{
660 struct fpm_rmac_arg fra;
661
662 fra.fnc = THREAD_ARG(t);
663 fra.ctx = dplane_ctx_alloc();
664 hash_iterate(zrouter.l3vni_table, fpm_enqueue_l3vni_table, &fra);
665 dplane_ctx_fini(&fra.ctx);
666
667 return 0;
668}
669
018e77bc
RZ
670/**
671 * Resets the RIB FPM flags so we send all routes again.
672 */
673static int fpm_rib_reset(struct thread *t)
674{
675 struct fpm_nl_ctx *fnc = THREAD_ARG(t);
676 rib_dest_t *dest;
677 struct route_node *rn;
678 struct route_table *rt;
679 rib_tables_iter_t rt_iter;
680
681 fnc->rib_complete = false;
682
683 rt_iter.state = RIB_TABLES_ITER_S_INIT;
684 while ((rt = rib_tables_iter_next(&rt_iter))) {
685 for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
686 dest = rib_dest_from_rnode(rn);
687 /* Skip bad route entries. */
688 if (dest == NULL)
689 continue;
690
691 UNSET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM);
692 }
693 }
694
695 return 0;
696}
697
bda10adf
RZ
698/*
699 * The next three function will handle RMAC table reset.
700 */
701static void fpm_unset_rmac_table(struct hash_backet *backet, void *arg)
702{
703 zebra_mac_t *zrmac = backet->data;
704
705 UNSET_FLAG(zrmac->flags, ZEBRA_MAC_FPM_SENT);
706}
707
708static void fpm_unset_l3vni_table(struct hash_backet *backet, void *arg)
709{
710 zebra_l3vni_t *zl3vni = backet->data;
711
712 hash_iterate(zl3vni->rmac_table, fpm_unset_rmac_table, zl3vni);
713}
714
715static int fpm_rmac_reset(struct thread *t)
716{
717 hash_iterate(zrouter.l3vni_table, fpm_unset_l3vni_table, NULL);
718
719 return 0;
720}
721
3bdd7fca
RZ
722/**
723 * Handles external (e.g. CLI, data plane or others) events.
724 */
725static int fpm_process_event(struct thread *t)
726{
727 struct fpm_nl_ctx *fnc = THREAD_ARG(t);
728 int event = THREAD_VAL(t);
729
730 switch (event) {
731 case FNE_DISABLE:
732 zlog_debug("%s: manual FPM disable event", __func__);
733 fnc->disabled = true;
734
735 /* Call reconnect to disable timers and clean up context. */
736 fpm_reconnect(fnc);
737 break;
738
739 case FNE_RECONNECT:
740 zlog_debug("%s: manual FPM reconnect event", __func__);
741 fnc->disabled = false;
742 fpm_reconnect(fnc);
743 break;
744
745 default:
746 zlog_debug("%s: unhandled event %d", __func__, event);
747 break;
748 }
749
750 return 0;
751}
752
d35f447d
RZ
753/*
754 * Data plane functions.
755 */
756static int fpm_nl_start(struct zebra_dplane_provider *prov)
757{
758 struct fpm_nl_ctx *fnc;
759
760 fnc = dplane_provider_get_data(prov);
761 fnc->fthread = frr_pthread_new(NULL, prov_name, prov_name);
762 assert(frr_pthread_run(fnc->fthread, NULL) == 0);
763 fnc->ibuf = stream_new(NL_PKT_BUF_SIZE);
764 fnc->obuf = stream_new(NL_PKT_BUF_SIZE * 128);
765 pthread_mutex_init(&fnc->obuf_mutex, NULL);
766 fnc->socket = -1;
3bdd7fca 767 fnc->disabled = true;
d35f447d
RZ
768
769 return 0;
770}
771
772static int fpm_nl_finish(struct zebra_dplane_provider *prov, bool early)
773{
774 struct fpm_nl_ctx *fnc;
775
776 fnc = dplane_provider_get_data(prov);
777 stream_free(fnc->ibuf);
778 stream_free(fnc->obuf);
779 close(fnc->socket);
780
781 return 0;
782}
783
784static int fpm_nl_process(struct zebra_dplane_provider *prov)
785{
786 struct zebra_dplane_ctx *ctx;
787 struct fpm_nl_ctx *fnc;
788 int counter, limit;
789
790 fnc = dplane_provider_get_data(prov);
791 limit = dplane_provider_get_work_limit(prov);
792 for (counter = 0; counter < limit; counter++) {
793 ctx = dplane_provider_dequeue_in_ctx(prov);
794 if (ctx == NULL)
795 break;
796
797 /*
798 * Skip all notifications if not connected, we'll walk the RIB
799 * anyway.
800 */
801 if (fnc->socket != -1 && fnc->connecting == false)
802 fpm_nl_enqueue(fnc, ctx);
803
804 dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
805 dplane_provider_enqueue_out_ctx(prov, ctx);
806 }
807
808 return 0;
809}
810
811static int fpm_nl_new(struct thread_master *tm)
812{
813 struct zebra_dplane_provider *prov = NULL;
d35f447d
RZ
814 int rv;
815
3bdd7fca 816 gfnc = calloc(1, sizeof(*gfnc));
d35f447d
RZ
817 rv = dplane_provider_register(prov_name, DPLANE_PRIO_POSTPROCESS,
818 DPLANE_PROV_FLAG_THREADED, fpm_nl_start,
3bdd7fca 819 fpm_nl_process, fpm_nl_finish, gfnc,
d35f447d
RZ
820 &prov);
821
822 if (IS_ZEBRA_DEBUG_DPLANE)
823 zlog_debug("%s register status: %d", prov_name, rv);
824
3bdd7fca
RZ
825 install_node(&fpm_node, fpm_write_config);
826 install_element(CONFIG_NODE, &fpm_set_address_cmd);
827 install_element(CONFIG_NODE, &no_fpm_set_address_cmd);
828
d35f447d
RZ
829 return 0;
830}
831
832static int fpm_nl_init(void)
833{
834 hook_register(frr_late_init, fpm_nl_new);
835 return 0;
836}
837
838FRR_MODULE_SETUP(
839 .name = "dplane_fpm_nl",
840 .version = "0.0.1",
841 .description = "Data plane plugin for FPM using netlink.",
842 .init = fpm_nl_init,
843 )