diff --git a/Makefile.am b/Makefile.am index f56e1b8e0bac..eb07e4ee8454 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 dplaneserver/subdir.am include grpc/subdir.am include tools/subdir.am @@ -272,6 +273,7 @@ EXTRA_DIST += \ doc/user/Makefile \ eigrpd/Makefile \ fpm/Makefile \ + dplaneserver/Makefile \ grpc/Makefile \ isisd/Makefile \ ldpd/Makefile \ diff --git a/dplaneserver/Makefile b/dplaneserver/Makefile new file mode 100644 index 000000000000..4bdb6eb8fade --- /dev/null +++ b/dplaneserver/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. dplaneserver/dplaneserver +%: ALWAYS + @$(MAKE) -s -C .. dplaneserver/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/dplaneserver/dplaneserver.c b/dplaneserver/dplaneserver.c new file mode 100644 index 000000000000..2efb26c2b135 --- /dev/null +++ b/dplaneserver/dplaneserver.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Server socket program to simulate fpm using protobuf + * Copyright (C) 2023 Alibaba, Inc. + * Hongyu Li + */ +#include "dplaneserver.h" +#include "fpm/fpm.pb-c.h" + +#define FPM_HEADER_SIZE 4 +DEFINE_MGROUP(DPLANESERVER, "dplaneserver"); +DEFINE_MTYPE(DPLANESERVER, DPLANE_BUFFER, "dplaneserver communication"); +struct Dplaneserver_data dplaneserver_data = { .bufSize = 2048, + .messageBuffer = NULL, + .pos = 0, + .server_socket = 0, + .connection_socket = 0, + .connected = false, + .server_up = false }; +enum fpm_msg_op fm_op; + +void process_fpm_msg(struct 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) { + fm_op = msg->type; + switch (fm_op) { + case FPM_OP_ROUTE_INSTALL: + if (!msg->add_route) { + zlog_err("%s: ROUTE_INSTALL info doesn't exist", + __func__); + break; + } + process_route_install_msg(msg->add_route); + break; + /* Un-handled at this time */ + case FPM_OP_ROUTE_DELETE: + break; + } + fpm__message__free_unpacked(msg, NULL); + } else { + zlog_err("%s: unpack fpm message failed", __func__); + return; + } +} + +int dplaneserver_init(void) +{ + struct sockaddr_in server_addr; + + dplaneserver_data.server_socket = socket(PF_INET, SOCK_STREAM, + IPPROTO_TCP); + if (dplaneserver_data.server_socket < 0) { + zlog_err("%s: Can not open socket", __func__); + return -1; + } + + if (sockopt_reuseaddr(dplaneserver_data.server_socket) == -1) { + zlog_err("%s: Can not set socket opt", __func__); + return -1; + } + + // bind port + memset(&server_addr, 0, sizeof(server_addr)); + if (is_ipv6) + server_addr.sin_family = AF_INET6; + else + 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(dplaneserver_data.server_socket, + (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { + zlog_err("%s: bing socket to address failed", __func__); + return -1; + } + + if (listen(dplaneserver_data.server_socket, 10) == -1) { + zlog_err("%s: listen socket failed", __func__); + return -1; + } + + dplaneserver_data.server_up = true; + dplaneserver_data.messageBuffer = XMALLOC(MTYPE_DPLANE_BUFFER, + dplaneserver_data.bufSize); + + if (IS_DPLANE_SERVER_DEBUG) + zlog_debug("%s: connect socket successfully", __func__); + return 0; +} + +void dplaneserver_exit(void) +{ + XFREE(MTYPE_DPLANE_BUFFER, dplaneserver_data.messageBuffer); + if (dplaneserver_data.connected) + close(dplaneserver_data.connection_socket); + if (dplaneserver_data.server_up) + close(dplaneserver_data.server_socket); +} + +int dplaneserver_read_data(void) +{ + struct fpm_msg_hdr_t *fpm_hdr; + size_t msg_len; + size_t start = 0, left; + ssize_t read_len; + + read_len = read(dplaneserver_data.connection_socket, + dplaneserver_data.messageBuffer + dplaneserver_data.pos, + dplaneserver_data.bufSize - dplaneserver_data.pos); + + if (read_len == 0) { + if (IS_DPLANE_SERVER_DEBUG) + zlog_debug("%s: socket connection closed", __func__); + return -2; + } + if (read_len < 0) { + zlog_err("%s: socket connection read error", __func__); + return -1; + } + dplaneserver_data.pos += (uint32_t)read_len; + while (true) { + fpm_hdr = (struct fpm_msg_hdr_t *)(dplaneserver_data.messageBuffer + + start); + left = dplaneserver_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)) { + zlog_err("%s: fpm message header check failed"); + return -1; + } + process_fpm_msg(fpm_hdr); + start += msg_len; + } + /* update msg buffer*/ + memmove(dplaneserver_data.messageBuffer, + dplaneserver_data.messageBuffer + start, + dplaneserver_data.pos - start); + dplaneserver_data.pos = dplaneserver_data.pos - (uint32_t)start; + return 0; +} + +int dplaneserver_poll(void) +{ + struct pollfd poll_fd_set[MAX_CLIENTS + 1]; + + memset(poll_fd_set, 0, sizeof(poll_fd_set)); + poll_fd_set[0].fd = dplaneserver_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) { + zlog_err("%s: failed to poll socket", __func__); + return -1; + } + if (poll_fd_set[0].revents & POLLIN) { + struct sockaddr_in client_addr; + int i; + socklen_t addr_len = sizeof(client_addr); + int client_fd = accept(dplaneserver_data.server_socket, + (struct sockaddr *)&client_addr, + &addr_len); + + if (client_fd == -1) { + zlog_err("%s: failed to accept client connection", + __func__); + continue; + } + // add new connection to poll fd set + for (i = 1; i <= MAX_CLIENTS; i++) { + if (poll_fd_set[i].fd == 0) { + if (IS_DPLANE_SERVER_DEBUG) + zlog_debug("%s: a new client has connected", + __func__); + poll_fd_set[i].fd = client_fd; + poll_fd_set[i].events = POLLIN; + dplaneserver_data.connection_socket = + client_fd; + dplaneserver_data.connected = true; + break; + } + } + if (i > MAX_CLIENTS) { + close(client_fd); + continue; + } + } + // check for events on client sockets + for (int i = 1; i <= MAX_CLIENTS; i++) { + if (poll_fd_set[i].fd == 0) + continue; + if (poll_fd_set[i].revents & POLLIN) { + int res = dplaneserver_read_data(); + /* if func return -1 or -2 it means errors occur*/ + if (res) + return res; + } + if (poll_fd_set[i].revents & + (POLLERR | POLLHUP | POLLNVAL)) { + if (IS_DPLANE_SERVER_DEBUG) + zlog_debug("%s: socket POLLERR | POLLHUP | POLLNVAL event happened", + __func__); + close(poll_fd_set[i].fd); + poll_fd_set[i].fd = 0; + } + } + } +} + +void process_route_install_msg(Fpm__AddRoute *msg) +{ + char buf[4096] = { 0 }; + + if (!msg->key) { + zlog_err("%s: ROUTE_INSTALL route key doesn't exist", __func__); + return; + } + if (!msg->key->prefix) { + zlog_err("%s: ROUTE_INSTALL prefix doesn't exist", __func__); + return; + } + if (IS_DPLANE_SERVER_DEBUG) + zlog_debug("%s: msg address family:%d", + __func__, msg->address_family); + if (msg->address_family == AF_INET) { + inet_ntop(AF_INET, msg->key->prefix->bytes.data, buf, + sizeof(buf)); + if (IS_DPLANE_SERVER_DEBUG) + zlog_debug("%s: key ipv4 prefix:%pI4", __func__, buf); + } else if (msg->address_family == AF_INET6) { + inet_ntop(AF_INET6, msg->key->prefix->bytes.data, buf, + sizeof(buf)); + if (IS_DPLANE_SERVER_DEBUG) + zlog_debug("%s: key ipv6 prefix:%pI6", __func__, buf); + } else { + zlog_err("%s: not ipv4 or ipv6 address family", __func__); + return; + } + if (IS_DPLANE_SERVER_DEBUG) + zlog_debug("%s: key length:%d", __func__, msg->key->prefix->length); + + json_object *json = json_object_new_object(); + + json_object_int_add(json, "vrfId", (int64_t)(msg->vrf_id)); + json_object_int_add(json, "addressFamily", + (int64_t)(msg->address_family)); + json_object_int_add(json, "metric", (int64_t)(msg->metric)); + json_object_int_add(json, "subAddressFamily", + (int64_t)(msg->sub_address_family)); + json_object_int_add(json, "hasRouteType", + (int64_t)(msg->has_route_type)); + json_object_int_add(json, "routeType", (int64_t)(msg->route_type)); + if (msg->key) { + json_object_string_add(json, "prefix", buf); + json_object_int_add(json, "prefixLength", + (int64_t)(msg->key->prefix->length)); + } + if (output_file_path) { + FILE *fp = fopen(output_file_path, "a+"); + + if (!fp) { + zlog_err("%s open output json file failed:%s", __func__, + output_file_path); + } else { + fprintf(fp, "%s\n", + json_object_to_json_string_ext(json, + JSON_C_TO_STRING_PRETTY)); + fclose(fp); + } + } else { + zlog_err("%s output json file doesn't exist", __func__); + } + json_object_free(json); +} diff --git a/dplaneserver/dplaneserver.h b/dplaneserver/dplaneserver.h new file mode 100644 index 000000000000..a91d578b294a --- /dev/null +++ b/dplaneserver/dplaneserver.h @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Server socket program to simulate fpm using protobuf + * Copyright (C) 2023 Alibaba, Inc. + * Hongyu Li + */ +#ifndef _DPLANESERVER_H +#define _DPLANESERVER_H + +#ifdef HAVE_CONFIG_H +#include "config.h" /* Include this explicitly */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "zlog.h" +#include "fpm/fpm.h" +#include "lib/json.h" +#include "fpm/fpm.pb-c.h" +#include "lib/memory.h" +#include "lib/sockunion.h" + +extern char *output_file_path; +extern struct Dplaneserver_data dplaneserver_data; +extern bool is_ipv6; +extern bool debug_mode; + +#define MAX_CLIENTS 10 +#define BUFFER_SIZE 1024 +#define DPLANE_SERVER_DEBUG 0x01 +#define IS_DPLANE_SERVER_DEBUG (debug_mode & DPLANE_SERVER_DEBUG) + +#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 + +DECLARE_MTYPE(DPLANE_BUFFER); + +struct Dplaneserver_data { + unsigned int bufSize; + char *messageBuffer; + unsigned int pos; + int server_socket; + int connection_socket; + bool connected; + bool server_up; +}; + +enum fpm_msg_op { + FPM_OP_NONE = 0, + + /* Route update */ + FPM_OP_ROUTE_INSTALL, + FPM_OP_ROUTE_DELETE, +}; + +int dplaneserver_init(void); +void dplaneserver_exit(void); +int dplaneserver_poll(void); +int dplaneserver_read_data(void); +void process_route_install_msg(Fpm__AddRoute *msg); + +#endif diff --git a/dplaneserver/main.c b/dplaneserver/main.c new file mode 100644 index 000000000000..49964a6834fb --- /dev/null +++ b/dplaneserver/main.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Server socket program to simulate fpm using protobuf + * Copyright (C) 2023 Alibaba, Inc. + * Hongyu Li + */ +#include "dplaneserver.h" +#include "zlog.h" +struct option longopts[] = {{ "help", no_argument, NULL, 'h' }, + { "debug", no_argument, NULL, 'd' }, + { "file", required_argument, NULL, 'f' }, + { 0 }}; +char *output_file_path; +bool is_ipv6; +bool debug_mode; + +void usage(const char *progname, int exit_code) +{ + printf("Usage : %s [OPTION...]\n" + "-f --file \n" + "-d --debug\n" + "-i --ipv6\n" + "-h --help\n", + progname); + exit(exit_code); +} +int main(int argc, char **argv) +{ + while (1) { + int opt; + + opt = getopt_long(argc, argv, "f:dhi", 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("dplaneserver", 1); + break; + case 'i': + is_ipv6 = true; + break; + default: + usage("dplaneserver", 1); + break; + } + } + if (debug_mode) + zlog_aux_init("DPLANESERVER", LOG_DEBUG); + else + zlog_aux_init("DPLANESERVER", LOG_INFO); + + if (output_file_path == NULL) { + zlog_err("%s: output file path not specified", __func__); + usage("dplaneserver", 1); + } else if (access(output_file_path, F_OK) == -1) { + zlog_err("%s: output file path does not exist", __func__); + usage("dplaneserver", 1); + } else { + if (IS_DPLANE_SERVER_DEBUG) + zlog_debug("%s: output file path: %s", __func__, output_file_path); + } + + while (1) { + int step1 = dplaneserver_init(); + int step2 = dplaneserver_poll(); + + if (step2 | step1) { + if (step2 == -2) { + if (IS_DPLANE_SERVER_DEBUG) + zlog_debug("%s: fpm connection closed", + __func__); + } else { + zlog_err("%s: socket errors occur", __func__); + } + dplaneserver_exit(); + return 0; + } + } +} diff --git a/dplaneserver/subdir.am b/dplaneserver/subdir.am new file mode 100644 index 000000000000..806a979527a3 --- /dev/null +++ b/dplaneserver/subdir.am @@ -0,0 +1,23 @@ +sbin_PROGRAMS += dplaneserver/dplaneserver + +dplaneserver_dplaneserver_CFLAGS = -g $(AM_CPPFLAGS) $(PROTOBUF_C_CFLAGS) -std=gnu11 +dplaneserver_dplaneserver_LDADD = lib/libfrr.la $(LIBCAP) $(UST_LIBS) $(PROTOBUF_C_LIBS) + +dplaneserver_dplaneserver_SOURCES = \ + dplaneserver/main.c \ + dplaneserver/dplaneserver.h \ + dplaneserver/dplaneserver.c \ + qpb/qpb.c \ + qpb/qpb_allocator.c \ + fpm/fpm.h \ + fpm/fpm_pb.h \ + fpm/fpm_pb.c \ + qpb/linear_allocator.h \ + qpb/qpb.h \ + qpb/qpb_allocator.h \ + # end + +nodist_dplaneserver_dplaneserver_SOURCES = \ + fpm/fpm.pb-c.c \ + qpb/qpb.pb-c.c \ + # end