diff --git a/Makefile.am b/Makefile.am index f56e1b8e0bac..f08e08784014 100644 --- a/Makefile.am +++ b/Makefile.am @@ -181,6 +181,7 @@ include zebra/subdir.am include watchfrr/subdir.am include qpb/subdir.am include fpm/subdir.am +include fpmpbserver/subdir.am include grpc/subdir.am include tools/subdir.am @@ -272,6 +273,7 @@ EXTRA_DIST += \ doc/user/Makefile \ eigrpd/Makefile \ fpm/Makefile \ + fpmpbserver/Makefile \ grpc/Makefile \ isisd/Makefile \ ldpd/Makefile \ diff --git a/doc/developer/fpm.rst b/doc/developer/fpm.rst index 56d33671d2dd..373d27b2554b 100644 --- a/doc/developer/fpm.rst +++ b/doc/developer/fpm.rst @@ -37,6 +37,7 @@ advantages over Netlink: * ``fpm`` * ``dplane_fpm_nl`` + * ``dplane_fpm_pb`` fpm ^^^ @@ -55,6 +56,13 @@ netlink functions to translate route event snapshots into formatted binary data. +dplane_fpm_pb +^^^^^^^^^^^^^ + +It has the same implementation as ``dplane_fpm_nl``, using ``zebra``'s data +plane framework as a plugin. It supports transmitting data in protobuf format. + + Protocol Specification ---------------------- diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 32de3e908f10..1beccaa3d4e9 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -1124,13 +1124,13 @@ as well. We refer to the component that programs the forwarding plane The relevant zebra code kicks in when zebra is configured with the :option:`--enable-fpm` flag and started with the module (``-M fpm`` -or ``-M dplane_fpm_nl``). +or ``-M dplane_fpm_nl`` or ``-M dplane_fpm_pb``). .. note:: The ``fpm`` implementation attempts to connect to ``127.0.0.1`` port ``2620`` - by default without configurations. The ``dplane_fpm_nl`` only attempts to - connect to a server if configured. + by default without configurations. The ``dplane_fpm_nl`` or ``dplane_fpm_pb`` + only attempts to connect to a server if configured. Zebra periodically attempts to connect to the well-known FPM port (``2620``). Once the connection is up, zebra starts sending messages containing routes @@ -1144,6 +1144,7 @@ with the module load-time option. The modules accept the following options: - ``fpm``: ``netlink`` and ``protobuf``. - ``dplane_fpm_nl``: none, it only implements netlink. +- ``dplane_fpm_pb``: none, it only implements protobuf. The zebra FPM interface uses replace semantics. That is, if a 'route add' message for a prefix is followed by another 'route add' message, @@ -1208,7 +1209,7 @@ FPM Commands optional Forwarding Plane Manager (FPM) component. -``dplane_fpm_nl`` implementation +``dplane_fpm_nl`` or ``dplane_fpm_pb`` implementation -------------------------------- .. clicmd:: fpm address [port (1-65535)] diff --git a/fpmpbserver/Makefile b/fpmpbserver/Makefile new file mode 100644 index 000000000000..1bb087204a73 --- /dev/null +++ b/fpmpbserver/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. fpmpbserver/fpmpbserver +%: ALWAYS + @$(MAKE) -s -C .. fpmpbserver/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/fpmpbserver/fpm.h b/fpmpbserver/fpm.h new file mode 100644 index 000000000000..e960ff1d56aa --- /dev/null +++ b/fpmpbserver/fpm.h @@ -0,0 +1,278 @@ +/* + * Public definitions pertaining to the Forwarding Plane Manager component. + * + * Permission is granted to use, copy, modify and/or distribute this + * software under either one of the licenses below. + * + * Note that if you use other files from the Quagga tree directly or + * indirectly, then the licenses in those files still apply. + * + * Please retain both licenses below when modifying this code in the + * Quagga tree. + * + * Copyright (C) 2012 by Open Source Routing. + * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC") + */ + +/* + * License Option 1: GPL + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * License Option 2: ISC License + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice appear + * in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FPM_H +#define _FPM_H + +/* + * The Forwarding Plane Manager (FPM) is an optional component that + * may be used in scenarios where the router has a forwarding path + * that is distinct from the kernel, commonly a hardware-based fast + * path. It is responsible for programming forwarding information + * (such as routes and nexthops) in the fast path. + * + * In Quagga, the Routing Information Base is maintained in the + * 'zebra' infrastructure daemon. Routing protocols communicate their + * best routes to zebra, and zebra computes the best route across + * protocols for each prefix. This latter information comprises the + * bulk of the Forwarding Information Base. + * + * This header file defines a point-to-point interface using which + * zebra can update the FPM about changes in routes. The communication + * takes place over a stream socket. The FPM listens on a well-known + * TCP port, and zebra initiates the connection. + * + * All messages sent over the connection start with a short FPM + * header, fpm_msg_hdr_t. In the case of route add/delete messages, + * the header is followed by a netlink message. Zebra should send a + * complete copy of the forwarding table(s) to the FPM, including + * routes that it may have picked up from the kernel. + * + * The FPM interface uses replace semantics. That is, if a 'route add' + * message for a prefix is followed by another 'route add' message, the + * information in the second message is complete by itself, and replaces + * the information sent in the first message. + * + * If the connection to the FPM goes down for some reason, the client + * (zebra) should send the FPM a complete copy of the forwarding + * table(s) when it reconnects. + */ + +#include +#include +#include +#include +#define FPM_DEFAULT_PORT 2620 + +/* + * Largest message that can be sent to or received from the FPM. + */ +#define FPM_MAX_MSG_LEN 4096 + +/* + * Header that precedes each fpm message to/from the FPM. + */ +typedef struct fpm_msg_hdr_t_ { + /* + * Protocol version. + */ + uint8_t version; + + /* + * Type of message, see below. + */ + uint8_t msg_type; + + /* + * Length of entire message, including the header, in network byte + * order. + * + * Note that msg_len is rounded up to make sure that message is at + * the desired alignment. This means that some payloads may need + * padding at the end. + */ + uint16_t msg_len; +} fpm_msg_hdr_t; + +/* + * The current version of the FPM protocol is 1. + */ +#define FPM_PROTO_VERSION 1 + +typedef enum fpm_msg_type_e_ { + FPM_MSG_TYPE_NONE = 0, + + /* + * Indicates that the payload is a completely formed netlink + * message. + * + * XXX Netlink cares about the alignment of messages. When any + * FPM_MSG_TYPE_NETLINK messages are sent over a channel, then all + * messages should be sized such that netlink alignment is + * maintained. + */ + FPM_MSG_TYPE_NETLINK = 1, + FPM_MSG_TYPE_PROTOBUF = 2, +} fpm_msg_type_e; + +/* + * The FPM message header is aligned to the same boundary as netlink + * messages (4). This means that a netlink message does not need + * padding when encapsulated in an FPM message. + */ +#define FPM_MSG_ALIGNTO 4 + +/* + * fpm_msg_align + * + * Round up the given length to the desired alignment. + */ +static inline size_t fpm_msg_align(size_t len) +{ + return (len + FPM_MSG_ALIGNTO - 1) & ~(FPM_MSG_ALIGNTO - 1); +} + +/* + * The (rounded up) size of the FPM message header. This ensures that + * the message payload always starts at an aligned address. + */ +#define FPM_MSG_HDR_LEN (fpm_msg_align(sizeof(fpm_msg_hdr_t))) + +/* + * fpm_data_len_to_msg_len + * + * The length value that should be placed in the msg_len field of the + * header for a *payload* of size 'data_len'. + */ +static inline size_t fpm_data_len_to_msg_len(size_t data_len) +{ + return fpm_msg_align(data_len) + FPM_MSG_HDR_LEN; +} + +/* + * fpm_msg_data + * + * Pointer to the payload of the given fpm header. + */ +static inline void *fpm_msg_data(fpm_msg_hdr_t *hdr) +{ + return ((char *)hdr) + FPM_MSG_HDR_LEN; +} + +/* + * fpm_msg_len + */ +static inline size_t fpm_msg_len(const fpm_msg_hdr_t *hdr) +{ + return ntohs(hdr->msg_len); +} + +/* + * fpm_msg_data_len + */ +static inline size_t fpm_msg_data_len(const fpm_msg_hdr_t *hdr) +{ + return (fpm_msg_len(hdr) - FPM_MSG_HDR_LEN); +} + +/* + * fpm_msg_next + * + * Move to the next message in a buffer. + */ +static inline fpm_msg_hdr_t *fpm_msg_next(fpm_msg_hdr_t *hdr, size_t *len) +{ + size_t msg_len; + + msg_len = fpm_msg_len(hdr); + + if (len) { + if (*len < msg_len) { + assert(0); + return NULL; + } + *len -= msg_len; + } + + return reinterpret_cast( + static_cast(((char *)hdr) + msg_len)); +} + +/* + * fpm_msg_hdr_ok + * + * Returns TRUE if a message header looks well-formed. + */ +static inline int fpm_msg_hdr_ok(const fpm_msg_hdr_t *hdr) +{ + size_t msg_len; + + if (hdr->msg_type == FPM_MSG_TYPE_NONE) + return 0; + + msg_len = fpm_msg_len(hdr); + + if (msg_len < FPM_MSG_HDR_LEN || msg_len > FPM_MAX_MSG_LEN) + return 0; + + /* + * Netlink messages must be aligned properly. + */ + if (hdr->msg_type == FPM_MSG_TYPE_NETLINK && + fpm_msg_align(msg_len) != msg_len) + return 0; + + return 1; +} + +/* + * fpm_msg_ok + * + * Returns true if a message looks well-formed. + * + * @param len The length in bytes from 'hdr' to the end of the buffer. + */ +static inline int fpm_msg_ok(const fpm_msg_hdr_t *hdr, size_t len) +{ + if (len < FPM_MSG_HDR_LEN) + return 0; + + if (!fpm_msg_hdr_ok(hdr)) + return 0; + + if (fpm_msg_len(hdr) > len) + return 0; + + return 1; +} + +#endif /* _FPM_H */ diff --git a/fpmpbserver/fpmpbserver.cpp b/fpmpbserver/fpmpbserver.cpp new file mode 100644 index 000000000000..abb45cc72cc4 --- /dev/null +++ b/fpmpbserver/fpmpbserver.cpp @@ -0,0 +1,277 @@ + +#include "fpmpbserver.h" +#include "fpm/fpm.pb-c.h" + +#define FPM_HEADER_SIZE 4 +struct Fpmpbserver_data fpmpbserver_data = {.bufSize = 2048, + .messageBuffer = NULL, + .pos = 0, + .server_socket = 0, + .connection_socket = 0, + .connected = false, + .server_up = false}; +char *output_file_path = NULL; + +void process_fpm_msg(fpm_msg_hdr_t *fpm_hdr) +{ + size_t msg_len = fpm_msg_len(fpm_hdr); + Fpm__Message *msg; + msg = fpm__message__unpack(NULL, msg_len - FPM_HEADER_SIZE, + (uint8_t *)fpm_msg_data(fpm_hdr)); + if (!msg) { + zlog_info("[process_fpm_msg] unpack error!"); + return; + } else { + zlog_info("[process_fpm_msg] msg_type:%d", msg->type); + if (!msg->add_route) { + zlog_info("[process_fpm_msg] add_route not exist"); + return; + } + zlog_info( + "[process_fpm_msg] msg add_route data:\nvrf_id:%d\naddress_family:%d\nmetric:%d\nsub_address_family:%d\nhas_route_type:%d\nroute_type:%d\n", + msg->add_route->vrf_id, msg->add_route->address_family, + msg->add_route->metric, + msg->add_route->sub_address_family, + msg->add_route->has_route_type, + msg->add_route->route_type); + if (!msg->add_route->key) { + zlog_info("[process_fpm_msg] key not exist"); + return; + } else { + zlog_info("[process_fpm_msg] key exist"); + } + char buf[100] = {0}; + inet_ntop(AF_INET, msg->add_route->key->prefix->bytes.data, buf, + sizeof(buf)); + zlog_info("[process_fpm_msg] key prefix:%s", buf); + zlog_info("[process_fpm_msg] key length:%d", + msg->add_route->key->prefix->length); + + json_object *json = json_object_new_object(); + json_object_int_add(json, "msg_type", int64_t(msg->type)); + if (msg->add_route) { + json_object_int_add(json, "vrf_id", + int64_t(msg->add_route->vrf_id)); + json_object_int_add( + json, "address_family", + int64_t(msg->add_route->address_family)); + json_object_int_add(json, "metric", + int64_t(msg->add_route->metric)); + json_object_int_add( + json, "sub_address_family", + int64_t(msg->add_route->sub_address_family)); + json_object_int_add( + json, "has_route_type", + int64_t(msg->add_route->has_route_type)); + json_object_int_add( + json, "route_type", + int64_t(msg->add_route->route_type)); + if (msg->add_route->key) { + json_object_string_add(json, "prefix", buf); + json_object_int_add( + json, "prefix_length", + int64_t(msg->add_route->key->prefix + ->length)); + } + } + if (output_file_path) { + FILE *fp = fopen(output_file_path, "a+"); + if (!fp) { + zlog_info( + "[process_fpm_msg] open file error file path :%s", + output_file_path); + } else { + fprintf(fp, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + fclose(fp); + } + } + json_object_free(json); + } +} + +int fpmpbserver_init() +{ + fpmpbserver_data.server_socket = + socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fpmpbserver_data.server_socket < 0) { + throw system_error(make_error_code(errc::bad_message), + "Failed to create socket"); + } + + int opt = 1; + if (setsockopt(fpmpbserver_data.server_socket, SOL_SOCKET, SO_REUSEADDR, + &opt, sizeof(opt)) == -1) { + throw system_error(make_error_code(errc::bad_message), + "Failed to set socket option"); + } + + // bind port + sockaddr_in server_addr{}; + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(FPM_DEFAULT_PORT); + server_addr.sin_addr.s_addr = FPM_DEFAULT_IP; + + if (bind(fpmpbserver_data.server_socket, (sockaddr *)&server_addr, + sizeof(server_addr)) == -1) { + throw system_error(make_error_code(errc::bad_message), + "Failed to bind port"); + } + + if (listen(fpmpbserver_data.server_socket, 10) == -1) { + throw system_error(make_error_code(errc::bad_message), + "Failed to listen on socket"); + } + + fpmpbserver_data.server_up = true; + fpmpbserver_data.messageBuffer = new char[fpmpbserver_data.bufSize]; + return 0; +} + +int fpmpbserver_exit() +{ + delete[] fpmpbserver_data.messageBuffer; + if (fpmpbserver_data.connected) + close(fpmpbserver_data.connection_socket); + if (fpmpbserver_data.server_up) + close(fpmpbserver_data.server_socket); + return 0; +} + + +int fpmpbserver_read_data() +{ + + fpm_msg_hdr_t *fpm_hdr; + size_t msg_len; + size_t start = 0, left; + ssize_t read; + + + read = ::read(fpmpbserver_data.connection_socket, + fpmpbserver_data.messageBuffer + fpmpbserver_data.pos, + fpmpbserver_data.bufSize - fpmpbserver_data.pos); + if (read == 0) + throw FpmConnectionClosedException(); + if (read < 0) + throw system_error(make_error_code(errc::bad_message), + "read connnected socket error"); + fpmpbserver_data.pos += (uint32_t)read; + + while (true) { + fpm_hdr = reinterpret_cast(static_cast( + fpmpbserver_data.messageBuffer + start)); + + + left = fpmpbserver_data.pos - start; + + if (left < FPM_MSG_HDR_LEN) { + break; + } + + /* fpm_msg_len includes header size */ + msg_len = fpm_msg_len(fpm_hdr); + if (left < msg_len) { + break; + } + + if (!fpm_msg_ok(fpm_hdr, left)) { + throw system_error(make_error_code(errc::bad_message), + "Malformed FPM message received"); + } + + + process_fpm_msg(fpm_hdr); + + start += msg_len; + } + + memmove(fpmpbserver_data.messageBuffer, + fpmpbserver_data.messageBuffer + start, + fpmpbserver_data.pos - start); + fpmpbserver_data.pos = fpmpbserver_data.pos - (uint32_t)start; + return 0; +} + +int fpmpbserver_poll(void) +{ + pollfd poll_fd_set[MAX_CLIENTS + 1]; + memset(poll_fd_set, 0, sizeof(poll_fd_set)); + poll_fd_set[0].fd = fpmpbserver_data.server_socket; + poll_fd_set[0].events = POLLIN; + + + while (true) { + + // poll for events + int nready = poll(poll_fd_set, MAX_CLIENTS + 1, -1); + + if (nready == -1) { + + throw system_error(make_error_code(errc::bad_message), + "Failed to poll socket"); + + return -1; + } + if (poll_fd_set[0].revents & POLLIN) { + sockaddr_in client_addr{}; + socklen_t addr_len = sizeof(client_addr); + int client_fd = + accept(fpmpbserver_data.server_socket, + (sockaddr *)&client_addr, &addr_len); + if (client_fd == -1) { + throw system_error( + make_error_code(errc::bad_message), + "Failed to accept client connection"); + continue; + } + + // add new connection to poll fd set + int i; + for (i = 1; i <= MAX_CLIENTS; i++) { + if (poll_fd_set[i].fd == 0) { + zlog_info("has connected"); + poll_fd_set[i].fd = client_fd; + poll_fd_set[i].events = POLLIN; + fpmpbserver_data.connection_socket = + client_fd; + fpmpbserver_data.connected = true; + break; + } + } + if (i > MAX_CLIENTS) { + throw system_error( + make_error_code(errc::bad_message), + "Too many clients"); + + close(client_fd); + continue; + } + + } + + + // check for events on client sockets + for (int i = 1; i <= MAX_CLIENTS; i++) { + zlog_info("check sockets i:%d", i); + + if (poll_fd_set[i].fd == 0) + continue; + + if (poll_fd_set[i].revents & POLLIN) { + fpmpbserver_read_data(); + } + + if (poll_fd_set[i].revents & + (POLLERR | POLLHUP | POLLNVAL)) { + throw system_error( + make_error_code(errc::bad_message), + "socket POLLERR | POLLHUP | POLLNVAL event happened"); + close(poll_fd_set[i].fd); + poll_fd_set[i].fd = 0; + } + } + zlog_info("end of loop"); + } +} diff --git a/fpmpbserver/fpmpbserver.h b/fpmpbserver/fpmpbserver.h new file mode 100644 index 000000000000..3fe7f4f5befb --- /dev/null +++ b/fpmpbserver/fpmpbserver.h @@ -0,0 +1,55 @@ +#ifndef _FPMPBSERVER_H +#define _FPMPBSERVER_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "zlog.h" +#include "libfrr.h" +#include "fpmpbserver/fpm.h" +#include "lib/json.h" +#define MAX_CLIENTS 10 +#define BUFFER_SIZE 1024 + +#define FPM_DEFAULT_PORT 2620 +#ifndef FPM_DEFAULT_IP +#define FPM_DEFAULT_IP (htonl(INADDR_LOOPBACK)) +#endif +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */ +#endif + + +using namespace std; +extern char *output_file_path; + +struct Fpmpbserver_data { + unsigned int bufSize; + char *messageBuffer; + unsigned int pos; + int server_socket; + int connection_socket; + bool connected; + bool server_up; +}; + + +class FpmConnectionClosedException : public std::exception +{ +}; +extern struct Fpmpbserver_data fpmpbserver_data; + + +int fpmpbserver_init(); +int fpmpbserver_exit(); +int fpmpbserver_poll(void); +int fpmpbserver_read_data(); + +#endif \ No newline at end of file diff --git a/fpmpbserver/main.cpp b/fpmpbserver/main.cpp new file mode 100644 index 000000000000..9f5f5143c083 --- /dev/null +++ b/fpmpbserver/main.cpp @@ -0,0 +1,74 @@ +#include "fpmpbserver.h" +#include "zlog.h" +struct option longopts[] = {{"help", no_argument, NULL, 'h'}, + {"debug", no_argument, NULL, 'd'}, + {"file", required_argument, NULL, 'f'}, + {0}}; + +void usage(const char *progname, int exit_code) +{ + printf("Usage : %s [OPTION...]\n\ + -f --file \n\ + -d --debug\n\ + -h --help\n", + progname); + exit(exit_code); +} +int main(int argc, char **argv) +{ + bool debug_mode = false; + while (1) { + int opt; + opt = getopt_long(argc, argv, "f:dh", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + case 'f': + output_file_path = optarg; + break; + case 'd': + debug_mode = true; + break; + case 'h': + usage("fpmpbserver", 1); + break; + default: + usage("fpmpbserver", 1); + break; + } + } + + if (debug_mode) + zlog_aux_init("FPMPBSERVER", LOG_DEBUG); + else + zlog_aux_init("FPMPBSERVER", LOG_INFO); + + if (output_file_path == NULL) { + zlog_err("output file path not specified"); + usage("fpmpbserver", 1); + } else if (access(output_file_path, F_OK) == -1) { + zlog_err("output file path does not exist"); + usage("fpmpbserver", 1); + } else + zlog_debug("output file path: %s", output_file_path); + + + while (1) { + try { + fpmpbserver_init(); + fpmpbserver_poll(); + } catch (FpmConnectionClosedException &e) { + zlog_info("fpm connection closed"); + fpmpbserver_exit(); + } catch (const exception &e) { + zlog_err("exception: %s had been thrown in daemon", + e.what()); + fpmpbserver_exit(); + return 0; + } + } +} diff --git a/fpmpbserver/subdir.am b/fpmpbserver/subdir.am new file mode 100644 index 000000000000..e2ed6e301db8 --- /dev/null +++ b/fpmpbserver/subdir.am @@ -0,0 +1,21 @@ +sbin_PROGRAMS += fpmpbserver/fpmpbserver + +fpmpbserver_fpmpbserver_CXXFLAGS = -g $(AM_CPPFLAGS) $(PROTOBUF_C_CFLAGS) +fpmpbserver_fpmpbserver_LDADD = lib/libfrr.la $(LIBCAP) $(UST_LIBS) fpm/libfrrfpm_pb.la qpb/libfrr_pb.la $(PROTOBUF_C_LIBS) + +fpmpbserver_fpmpbserver_SOURCES = \ + fpmpbserver/main.cpp \ + fpmpbserver/fpmpbserver.h \ + fpmpbserver/fpmpbserver.cpp \ + # end + +CLEANFILES += \ + fpm/fpm.pb-c.c \ + fpm/fpm.pb-c.h \ + # end + +EXTRA_DIST += fpm/fpm.proto + + + + diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 4d935b9538c5..eae010fb423a 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -1099,6 +1099,26 @@ def has_type(self, rtype): def has_mpls(self): return self.net.hasmpls + def startFpmPBServer(self): + "Starts FRR FPM Simulator for this router." + dir_path = f"{self.logdir}/{self.name}" + log_path = f"{dir_path}/FpmProtoBufServer.log" + json_path = f"{dir_path}/output.json" + self.cmd(f"mkdir -p {dir_path}") + run_cmd = f"/usr/lib/frr/fpmpbserver -f {json_path} -d > {log_path} 2>&1 &" + file_cmd = f"touch {json_path}" + self.cmd_raises(file_cmd, warn=False) + try: + self.cmd_raises(run_cmd, warn=False) + except subprocess.CalledProcessError as error: + self.logger.error( + '%s: Failed to launch "%s" daemon (%d) using: %s:', + self, + "FPM ProtoBuf Server", + error.returncode, + error.cmd, + ) + class TopoSwitch(TopoGear): """ diff --git a/tests/topotests/zebra_fpm_pb/__init__.py b/tests/topotests/zebra_fpm_pb/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/zebra_fpm_pb/r1/bgpd.conf b/tests/topotests/zebra_fpm_pb/r1/bgpd.conf new file mode 100644 index 000000000000..0918796e9503 --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r1/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 101 + bgp router-id 10.254.254.1 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r1-eth0 interface peer-group r2g + neighbor r1-eth0 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/zebra_fpm_pb/r1/ref.json b/tests/topotests/zebra_fpm_pb/r1/ref.json new file mode 100644 index 000000000000..c40fd9975a3e --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r1/ref.json @@ -0,0 +1,46 @@ +[ + { + "msg_type":1, + "vrf_id":0, + "address_family":10, + "metric":0, + "sub_address_family":1, + "has_route_type":1, + "route_type":1, + "prefix":"32.1.13.184", + "prefix_length":64 + }, + { + "msg_type":1, + "vrf_id":0, + "address_family":2, + "metric":0, + "sub_address_family":1, + "has_route_type":1, + "route_type":1, + "prefix":"10.0.3.2", + "prefix_length":32 + }, + { + "msg_type":1, + "vrf_id":0, + "address_family":2, + "metric":0, + "sub_address_family":1, + "has_route_type":1, + "route_type":1, + "prefix":"10.254.254.2", + "prefix_length":32 + }, + { + "msg_type":1, + "vrf_id":0, + "address_family":10, + "metric":0, + "sub_address_family":1, + "has_route_type":1, + "route_type":1, + "prefix":"32.1.13.184", + "prefix_length":64 + } + ] \ No newline at end of file diff --git a/tests/topotests/zebra_fpm_pb/r1/zebra.conf b/tests/topotests/zebra_fpm_pb/r1/zebra.conf new file mode 100644 index 000000000000..7fe5eb218fc1 --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r1/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/zebra_fpm_pb/r2/bgpd.conf b/tests/topotests/zebra_fpm_pb/r2/bgpd.conf new file mode 100644 index 000000000000..55d48560e7ff --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r2/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 102 + bgp router-id 10.254.254.2 + no bgp ebgp-requires-policy + neighbor r2g peer-group + neighbor r2g remote-as external + neighbor r2g bfd + neighbor r2-eth0 interface peer-group r2g + neighbor r2-eth0 timers 3 10 + ! + address-family ipv4 unicast + redistribute connected + exit-address-family + ! + address-family ipv6 unicast + redistribute connected + neighbor r2g activate + exit-address-family +! diff --git a/tests/topotests/zebra_fpm_pb/r2/zebra.conf b/tests/topotests/zebra_fpm_pb/r2/zebra.conf new file mode 100644 index 000000000000..5fe582b9f90d --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r2/zebra.conf @@ -0,0 +1,15 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ipv6 address 2001:db8:1::2/64 +! +interface r2-eth1 + ip address 10.0.3.2/32 +! +interface r2-eth2 + ipv6 address 2001:db8:4::2/64 +! diff --git a/tests/topotests/zebra_fpm_pb/r3/zebra.conf b/tests/topotests/zebra_fpm_pb/r3/zebra.conf new file mode 100644 index 000000000000..96fd08c72901 --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r3/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 10.0.3.1/24 +! diff --git a/tests/topotests/zebra_fpm_pb/r4/zebra.conf b/tests/topotests/zebra_fpm_pb/r4/zebra.conf new file mode 100644 index 000000000000..e4f8fd8514a0 --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/r4/zebra.conf @@ -0,0 +1,6 @@ +interface lo + ip address 10.254.254.4/32 +! +interface r4-eth0 + ipv6 address 2001:db8:4::1/64 +! diff --git a/tests/topotests/zebra_fpm_pb/test_zebra_fpm_pb.py b/tests/topotests/zebra_fpm_pb/test_zebra_fpm_pb.py new file mode 100644 index 000000000000..08ed09c13948 --- /dev/null +++ b/tests/topotests/zebra_fpm_pb/test_zebra_fpm_pb.py @@ -0,0 +1,127 @@ +# +# Copyright (c) 2017 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +