]> git.proxmox.com Git - mirror_frr.git/blame - zebra/dplane_fpm_nl.c
zebra: adapt and export rmac netlink functions
[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"
33#include "lib/memory.h"
34#include "lib/network.h"
35#include "lib/ns.h"
36#include "lib/frr_pthread.h"
37#include "zebra/zebra_dplane.h"
018e77bc 38#include "zebra/zebra_router.h"
d35f447d
RZ
39#include "zebra/kernel_netlink.h"
40#include "zebra/rt_netlink.h"
41#include "zebra/debug.h"
42
43#define SOUTHBOUND_DEFAULT_ADDR INADDR_LOOPBACK
44#define SOUTHBOUND_DEFAULT_PORT 2620
45
46static const char *prov_name = "dplane_fpm_nl";
47
48struct fpm_nl_ctx {
49 /* data plane connection. */
50 int socket;
51 bool connecting;
018e77bc 52 bool rib_complete;
d35f447d
RZ
53 struct sockaddr_storage addr;
54
55 /* data plane buffers. */
56 struct stream *ibuf;
57 struct stream *obuf;
58 pthread_mutex_t obuf_mutex;
59
60 /* data plane events. */
61 struct frr_pthread *fthread;
62 struct thread *t_connect;
63 struct thread *t_read;
64 struct thread *t_write;
018e77bc
RZ
65
66 /* zebra events. */
67 struct thread *t_ribreset;
68 struct thread *t_ribwalk;
d35f447d
RZ
69};
70
018e77bc
RZ
71/*
72 * Prototypes.
73 */
74static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx);
75static int fpm_rib_send(struct thread *t);
76static int fpm_rib_reset(struct thread *t);
77
d35f447d
RZ
78/*
79 * FPM functions.
80 */
81static int fpm_connect(struct thread *t);
82
83static void fpm_reconnect(struct fpm_nl_ctx *fnc)
84{
85 /* Grab the lock to empty the stream and stop the zebra thread. */
86 frr_mutex_lock_autounlock(&fnc->obuf_mutex);
87
88 close(fnc->socket);
89 fnc->socket = -1;
90 stream_reset(fnc->ibuf);
91 stream_reset(fnc->obuf);
92 THREAD_OFF(fnc->t_read);
93 THREAD_OFF(fnc->t_write);
018e77bc
RZ
94
95 if (fnc->t_ribreset)
96 thread_cancel_async(zrouter.master, &fnc->t_ribreset, NULL);
97 if (fnc->t_ribwalk)
98 thread_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL);
99
d35f447d
RZ
100 thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 3,
101 &fnc->t_connect);
102}
103
104static int fpm_read(struct thread *t)
105{
106 struct fpm_nl_ctx *fnc = THREAD_ARG(t);
107 ssize_t rv;
108
109 /* Let's ignore the input at the moment. */
110 rv = stream_read_try(fnc->ibuf, fnc->socket,
111 STREAM_WRITEABLE(fnc->ibuf));
112 if (rv == 0) {
113 zlog_debug("%s: connection closed", __func__);
114 fpm_reconnect(fnc);
115 return 0;
116 }
117 if (rv == -1) {
118 if (errno == EAGAIN || errno == EWOULDBLOCK
119 || errno == EINTR)
120 return 0;
121
122 zlog_debug("%s: connection failure: %s", __func__,
123 strerror(errno));
124 fpm_reconnect(fnc);
125 return 0;
126 }
127 stream_reset(fnc->ibuf);
128
129 thread_add_read(fnc->fthread->master, fpm_read, fnc, fnc->socket,
130 &fnc->t_read);
131
132 return 0;
133}
134
135static int fpm_write(struct thread *t)
136{
137 struct fpm_nl_ctx *fnc = THREAD_ARG(t);
138 socklen_t statuslen;
139 ssize_t bwritten;
140 int rv, status;
141 size_t btotal;
142
143 if (fnc->connecting == true) {
144 status = 0;
145 statuslen = sizeof(status);
146
147 rv = getsockopt(fnc->socket, SOL_SOCKET, SO_ERROR, &status,
148 &statuslen);
149 if (rv == -1 || status != 0) {
150 if (rv != -1)
151 zlog_debug("%s: connection failed: %s",
152 __func__, strerror(status));
153 else
154 zlog_debug("%s: SO_ERROR failed: %s", __func__,
155 strerror(status));
156
157 fpm_reconnect(fnc);
158 return 0;
159 }
160
161 fnc->connecting = false;
018e77bc
RZ
162
163 /* Ask zebra main thread to start walking the RIB table. */
164 thread_add_timer(zrouter.master, fpm_rib_send, fnc, 0,
165 &fnc->t_ribwalk);
d35f447d
RZ
166 }
167
168 frr_mutex_lock_autounlock(&fnc->obuf_mutex);
169
170 while (true) {
171 /* Stream is empty: reset pointers and return. */
172 if (STREAM_READABLE(fnc->obuf) == 0) {
173 stream_reset(fnc->obuf);
174 break;
175 }
176
177 /* Try to write all at once. */
178 btotal = stream_get_endp(fnc->obuf) -
179 stream_get_getp(fnc->obuf);
180 bwritten = write(fnc->socket, stream_pnt(fnc->obuf), btotal);
181 if (bwritten == 0) {
182 zlog_debug("%s: connection closed", __func__);
183 break;
184 }
185 if (bwritten == -1) {
186 if (errno == EAGAIN || errno == EWOULDBLOCK
187 || errno == EINTR)
188 break;
189
190 zlog_debug("%s: connection failure: %s", __func__,
191 strerror(errno));
192 fpm_reconnect(fnc);
193 break;
194 }
195
196 stream_forward_getp(fnc->obuf, (size_t)bwritten);
197 }
198
199 /* Stream is not empty yet, we must schedule more writes. */
200 if (STREAM_READABLE(fnc->obuf)) {
201 thread_add_write(fnc->fthread->master, fpm_write, fnc,
202 fnc->socket, &fnc->t_write);
203 return 0;
204 }
205
206 return 0;
207}
208
209static int fpm_connect(struct thread *t)
210{
211 struct fpm_nl_ctx *fnc = THREAD_ARG(t);
212 struct sockaddr_in *sin;
213 int rv, sock;
214 char addrstr[INET6_ADDRSTRLEN];
215
216 sock = socket(AF_INET, SOCK_STREAM, 0);
217 if (sock == -1) {
218 zlog_err("%s: fpm connection failed: %s", __func__,
219 strerror(errno));
220 thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 3,
221 &fnc->t_connect);
222 return 0;
223 }
224
225 set_nonblocking(sock);
226
227 sin = (struct sockaddr_in *)&fnc->addr;
228 memset(sin, 0, sizeof(*sin));
229 sin->sin_family = AF_INET;
230 sin->sin_addr.s_addr = htonl(SOUTHBOUND_DEFAULT_ADDR);
231 sin->sin_port = htons(SOUTHBOUND_DEFAULT_PORT);
232#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
233 sin->sin_len = sizeof(sin);
234#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
235
236 inet_ntop(AF_INET, &sin->sin_addr, addrstr, sizeof(addrstr));
237 zlog_debug("%s: attempting to connect to %s:%d", __func__, addrstr,
238 ntohs(sin->sin_port));
239
240 rv = connect(sock, (struct sockaddr *)sin, sizeof(*sin));
241 if (rv == -1 && errno != EINPROGRESS) {
242 close(sock);
243 zlog_warn("%s: fpm connection failed: %s", __func__,
244 strerror(errno));
245 thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 3,
246 &fnc->t_connect);
247 return 0;
248 }
249
250 fnc->connecting = (errno == EINPROGRESS);
251 fnc->socket = sock;
252 thread_add_read(fnc->fthread->master, fpm_read, fnc, sock,
253 &fnc->t_read);
254 thread_add_write(fnc->fthread->master, fpm_write, fnc, sock,
255 &fnc->t_write);
256
018e77bc
RZ
257 /* Mark all routes as unsent. */
258 thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0,
259 &fnc->t_ribreset);
260
d35f447d
RZ
261 return 0;
262}
263
264/**
265 * Encode data plane operation context into netlink and enqueue it in the FPM
266 * output buffer.
267 *
268 * @param fnc the netlink FPM context.
269 * @param ctx the data plane operation context data.
270 * @return 0 on success or -1 on not enough space.
271 */
272static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx)
273{
274 uint8_t nl_buf[NL_PKT_BUF_SIZE];
275 size_t nl_buf_len;
276 ssize_t rv;
277
278 nl_buf_len = 0;
279
280 frr_mutex_lock_autounlock(&fnc->obuf_mutex);
281
282 switch (dplane_ctx_get_op(ctx)) {
283 case DPLANE_OP_ROUTE_UPDATE:
284 case DPLANE_OP_ROUTE_DELETE:
285 rv = netlink_route_multipath(RTM_DELROUTE, ctx, nl_buf,
286 sizeof(nl_buf));
287 if (rv <= 0) {
288 zlog_debug("%s: netlink_route_multipath failed",
289 __func__);
290 return 0;
291 }
292
293 nl_buf_len = (size_t)rv;
294 if (STREAM_WRITEABLE(fnc->obuf) < nl_buf_len) {
295 zlog_debug("%s: not enough output buffer (%ld vs %lu)",
296 __func__, STREAM_WRITEABLE(fnc->obuf),
297 nl_buf_len);
298 return -1;
299 }
300
301 /* UPDATE operations need a INSTALL, otherwise just quit. */
302 if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE)
303 break;
304
305 /* FALL THROUGH */
306 case DPLANE_OP_ROUTE_INSTALL:
307 rv = netlink_route_multipath(RTM_NEWROUTE, ctx,
308 &nl_buf[nl_buf_len],
309 sizeof(nl_buf) - nl_buf_len);
310 if (rv <= 0) {
311 zlog_debug("%s: netlink_route_multipath failed",
312 __func__);
313 return 0;
314 }
315
316 nl_buf_len += (size_t)rv;
317 if (STREAM_WRITEABLE(fnc->obuf) < nl_buf_len) {
318 zlog_debug("%s: not enough output buffer (%ld vs %lu)",
319 __func__, STREAM_WRITEABLE(fnc->obuf),
320 nl_buf_len);
321 return -1;
322 }
323 break;
324
325 case DPLANE_OP_NH_INSTALL:
326 case DPLANE_OP_NH_UPDATE:
327 case DPLANE_OP_NH_DELETE:
328 case DPLANE_OP_LSP_INSTALL:
329 case DPLANE_OP_LSP_UPDATE:
330 case DPLANE_OP_LSP_DELETE:
331 case DPLANE_OP_PW_INSTALL:
332 case DPLANE_OP_PW_UNINSTALL:
333 case DPLANE_OP_ADDR_INSTALL:
334 case DPLANE_OP_ADDR_UNINSTALL:
335 case DPLANE_OP_MAC_INSTALL:
336 case DPLANE_OP_MAC_DELETE:
337 case DPLANE_OP_NEIGH_INSTALL:
338 case DPLANE_OP_NEIGH_UPDATE:
339 case DPLANE_OP_NEIGH_DELETE:
340 case DPLANE_OP_VTEP_ADD:
341 case DPLANE_OP_VTEP_DELETE:
342 case DPLANE_OP_SYS_ROUTE_ADD:
343 case DPLANE_OP_SYS_ROUTE_DELETE:
344 case DPLANE_OP_ROUTE_NOTIFY:
345 case DPLANE_OP_LSP_NOTIFY:
346 case DPLANE_OP_NONE:
347 break;
348
349 default:
350 zlog_debug("%s: unhandled data plane message (%d) %s",
351 __func__, dplane_ctx_get_op(ctx),
352 dplane_op2str(dplane_ctx_get_op(ctx)));
353 break;
354 }
355
356 /* Skip empty enqueues. */
357 if (nl_buf_len == 0)
358 return 0;
359
360 /*
361 * FPM header:
362 * {
363 * version: 1 byte (always 1),
364 * type: 1 byte (1 for netlink, 2 protobuf),
365 * len: 2 bytes (network order),
366 * }
367 */
368 stream_putc(fnc->obuf, 1);
369 stream_putc(fnc->obuf, 1);
370 assert(nl_buf_len < UINT16_MAX);
371 stream_putw(fnc->obuf, nl_buf_len + 4);
372
373 /* Write current data. */
374 stream_write(fnc->obuf, nl_buf, (size_t)nl_buf_len);
375
376 /* Tell the thread to start writing. */
377 thread_add_write(fnc->fthread->master, fpm_write, fnc, fnc->socket,
378 &fnc->t_write);
379
380 return 0;
381}
382
018e77bc
RZ
383/**
384 * Send all RIB installed routes to the connected data plane.
385 */
386static int fpm_rib_send(struct thread *t)
387{
388 struct fpm_nl_ctx *fnc = THREAD_ARG(t);
389 rib_dest_t *dest;
390 struct route_node *rn;
391 struct route_table *rt;
392 struct zebra_dplane_ctx *ctx;
393 rib_tables_iter_t rt_iter;
394
395 /* Allocate temporary context for all transactions. */
396 ctx = dplane_ctx_alloc();
397
398 rt_iter.state = RIB_TABLES_ITER_S_INIT;
399 while ((rt = rib_tables_iter_next(&rt_iter))) {
400 for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
401 dest = rib_dest_from_rnode(rn);
402 /* Skip bad route entries. */
403 if (dest == NULL || dest->selected_fib == NULL) {
404 continue;
405 }
406
407 /* Check for already sent routes. */
408 if (CHECK_FLAG(dest->flags, RIB_DEST_UPDATE_FPM)) {
409 continue;
410 }
411
412 /* Enqueue route install. */
413 dplane_ctx_reset(ctx);
414 dplane_ctx_route_init(ctx, DPLANE_OP_ROUTE_INSTALL, rn,
415 dest->selected_fib);
416 if (fpm_nl_enqueue(fnc, ctx) == -1) {
417 /* Free the temporary allocated context. */
418 dplane_ctx_fini(&ctx);
419
420 zlog_debug("%s: buffer full, come back later",
421 __func__);
422 thread_add_timer(zrouter.master, fpm_rib_send,
423 fnc, 1, &fnc->t_ribwalk);
424 return 0;
425 }
426
427 /* Mark as sent. */
428 SET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM);
429 }
430 }
431
432 /* Free the temporary allocated context. */
433 dplane_ctx_fini(&ctx);
434
435 /* All RIB routes sent! */
436 fnc->rib_complete = true;
437
438 return 0;
439}
440
441/**
442 * Resets the RIB FPM flags so we send all routes again.
443 */
444static int fpm_rib_reset(struct thread *t)
445{
446 struct fpm_nl_ctx *fnc = THREAD_ARG(t);
447 rib_dest_t *dest;
448 struct route_node *rn;
449 struct route_table *rt;
450 rib_tables_iter_t rt_iter;
451
452 fnc->rib_complete = false;
453
454 rt_iter.state = RIB_TABLES_ITER_S_INIT;
455 while ((rt = rib_tables_iter_next(&rt_iter))) {
456 for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
457 dest = rib_dest_from_rnode(rn);
458 /* Skip bad route entries. */
459 if (dest == NULL)
460 continue;
461
462 UNSET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM);
463 }
464 }
465
466 return 0;
467}
468
d35f447d
RZ
469/*
470 * Data plane functions.
471 */
472static int fpm_nl_start(struct zebra_dplane_provider *prov)
473{
474 struct fpm_nl_ctx *fnc;
475
476 fnc = dplane_provider_get_data(prov);
477 fnc->fthread = frr_pthread_new(NULL, prov_name, prov_name);
478 assert(frr_pthread_run(fnc->fthread, NULL) == 0);
479 fnc->ibuf = stream_new(NL_PKT_BUF_SIZE);
480 fnc->obuf = stream_new(NL_PKT_BUF_SIZE * 128);
481 pthread_mutex_init(&fnc->obuf_mutex, NULL);
482 fnc->socket = -1;
483
484 thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 1,
485 &fnc->t_connect);
486
487 return 0;
488}
489
490static int fpm_nl_finish(struct zebra_dplane_provider *prov, bool early)
491{
492 struct fpm_nl_ctx *fnc;
493
494 fnc = dplane_provider_get_data(prov);
495 stream_free(fnc->ibuf);
496 stream_free(fnc->obuf);
497 close(fnc->socket);
498
499 return 0;
500}
501
502static int fpm_nl_process(struct zebra_dplane_provider *prov)
503{
504 struct zebra_dplane_ctx *ctx;
505 struct fpm_nl_ctx *fnc;
506 int counter, limit;
507
508 fnc = dplane_provider_get_data(prov);
509 limit = dplane_provider_get_work_limit(prov);
510 for (counter = 0; counter < limit; counter++) {
511 ctx = dplane_provider_dequeue_in_ctx(prov);
512 if (ctx == NULL)
513 break;
514
515 /*
516 * Skip all notifications if not connected, we'll walk the RIB
517 * anyway.
518 */
519 if (fnc->socket != -1 && fnc->connecting == false)
520 fpm_nl_enqueue(fnc, ctx);
521
522 dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
523 dplane_provider_enqueue_out_ctx(prov, ctx);
524 }
525
526 return 0;
527}
528
529static int fpm_nl_new(struct thread_master *tm)
530{
531 struct zebra_dplane_provider *prov = NULL;
532 struct fpm_nl_ctx *fnc;
533 int rv;
534
535 fnc = calloc(1, sizeof(*fnc));
536 rv = dplane_provider_register(prov_name, DPLANE_PRIO_POSTPROCESS,
537 DPLANE_PROV_FLAG_THREADED, fpm_nl_start,
538 fpm_nl_process, fpm_nl_finish, fnc,
539 &prov);
540
541 if (IS_ZEBRA_DEBUG_DPLANE)
542 zlog_debug("%s register status: %d", prov_name, rv);
543
544 return 0;
545}
546
547static int fpm_nl_init(void)
548{
549 hook_register(frr_late_init, fpm_nl_new);
550 return 0;
551}
552
553FRR_MODULE_SETUP(
554 .name = "dplane_fpm_nl",
555 .version = "0.0.1",
556 .description = "Data plane plugin for FPM using netlink.",
557 .init = fpm_nl_init,
558 )