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