From a6fc065d21bee339da8dbbaab9eebadbafc111b7 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Wed, 24 Aug 2022 21:54:09 +0000 Subject: [PATCH] BNG Blaster 0.8 WIP - 2 --- CMakeLists.txt | 2 +- code/bngblaster/CMakeLists.txt | 19 ++- code/bngblaster/src/bbl.c | 4 +- code/bngblaster/src/bbl.h | 3 + code/bngblaster/src/bbl_a10nsp.c | 2 +- code/bngblaster/src/bbl_a10nsp.h | 2 +- code/bngblaster/src/bbl_access.c | 4 +- code/bngblaster/src/bbl_access.h | 2 +- code/bngblaster/src/bbl_config.c | 85 +++++++--- code/bngblaster/src/bbl_config.h | 12 +- code/bngblaster/src/bbl_ctx.h | 1 + code/bngblaster/src/bbl_def.h | 17 +- code/bngblaster/src/bbl_interface.c | 61 +------- code/bngblaster/src/bbl_interface.h | 36 +---- code/bngblaster/src/bbl_io.c | 19 +-- code/bngblaster/src/bbl_io.h | 1 + code/bngblaster/src/bbl_io_netmap.c | 197 ------------------------ code/bngblaster/src/bbl_io_netmap.h | 22 --- code/bngblaster/src/bbl_lag.h | 5 +- code/bngblaster/src/bbl_network.c | 2 +- code/bngblaster/src/bbl_network.h | 2 +- code/bngblaster/src/bbl_tx.c | 3 + code/bngblaster/src/bbl_txq.c | 51 +++++- code/bngblaster/src/bbl_txq.h | 31 ++-- code/bngblaster/src/io/io.c | 50 ++++++ code/bngblaster/src/io/io.h | 24 +++ code/bngblaster/src/io/io_def.h | 101 ++++++++++++ code/bngblaster/src/io/io_interface.c | 130 ++++++++++++++++ code/bngblaster/src/io/io_interface.h | 15 ++ code/bngblaster/src/io/io_packet_mmap.c | 194 +++++++++++++++++++++++ code/bngblaster/src/io/io_packet_mmap.h | 13 ++ code/bngblaster/src/io/io_raw.c | 110 +++++++++++++ code/bngblaster/src/io/io_raw.h | 12 ++ code/bngblaster/src/io/io_socket.c | 142 +++++++++++++++++ code/bngblaster/src/io/io_socket.h | 15 ++ code/bngblaster/src/io/io_thread.c | 192 +++++++++++++++++++++++ code/bngblaster/src/io/io_thread.h | 21 +++ code/common/src/common.h | 14 +- 38 files changed, 1223 insertions(+), 393 deletions(-) delete mode 100644 code/bngblaster/src/bbl_io_netmap.c delete mode 100644 code/bngblaster/src/bbl_io_netmap.h create mode 100644 code/bngblaster/src/io/io.c create mode 100644 code/bngblaster/src/io/io.h create mode 100644 code/bngblaster/src/io/io_def.h create mode 100644 code/bngblaster/src/io/io_interface.c create mode 100644 code/bngblaster/src/io/io_interface.h create mode 100644 code/bngblaster/src/io/io_packet_mmap.c create mode 100644 code/bngblaster/src/io/io_packet_mmap.h create mode 100644 code/bngblaster/src/io/io_raw.c create mode 100644 code/bngblaster/src/io/io_raw.h create mode 100644 code/bngblaster/src/io/io_socket.c create mode 100644 code/bngblaster/src/io/io_socket.h create mode 100644 code/bngblaster/src/io/io_thread.c create mode 100644 code/bngblaster/src/io/io_thread.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f4d70833..0fec9198 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ endif() message("Compiler: ${CMAKE_C_COMPILER_ID} Version: ${CMAKE_C_COMPILER_VERSION}") option(BNGBLASTER_TESTS "Build unit tests (requires cmocka)" OFF) -option(BNGBLASTER_NETMAP "Build with netmap support" OFF) +option(BNGBLASTER_DPDK "Build with dpdk support" OFF) set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) diff --git a/code/bngblaster/CMakeLists.txt b/code/bngblaster/CMakeLists.txt index 29c2739a..2c737f99 100644 --- a/code/bngblaster/CMakeLists.txt +++ b/code/bngblaster/CMakeLists.txt @@ -41,10 +41,21 @@ target_include_directories(bngblaster PRIVATE ${LWIP_INCLUDE_DIRS}) target_compile_definitions(bngblaster PRIVATE ${LWIP_DEFINITIONS} ${LWIP_MBEDTLS_DEFINITIONS}) target_link_libraries(bngblaster ${LWIP_SANITIZER_LIBS} lwipcore lwipcontribportunix) -# Add experimental netmap support -if(BNGBLASTER_NETMAP) - add_definitions(-DBNGBLASTER_NETMAP) - target_link_libraries(bngblaster netmap) +# Add DPDK support +if(BNGBLASTER_DPDK) + message(STATUS "Build bngblaster with DPDK support") + find_package(PkgConfig REQUIRED) + if (PKG_CONFIG_FOUND) + pkg_check_modules(DPDK "libdpdk") + if (DPDK_FOUND) + message(STATUS "Found DPDK via pkg-config") + add_definitions(-DBNGBLASTER_DPDK) + add_definitions(${DPDK_CFLAGS}) + set(DPDK_LIBS -Wl,--whole-archive ${DPDK_LIBRARIES} -lpthread -lnuma -ldl -Wl,--no-whole-archive) + include_directories(${DPDK_INCLUDE_DIR}) + target_link_libraries(bngblaster ${DPDK_LIBS}) + endif() + endif() endif() if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0) diff --git a/code/bngblaster/src/bbl.c b/code/bngblaster/src/bbl.c index f8dd13dd..5bf3462e 100644 --- a/code/bngblaster/src/bbl.c +++ b/code/bngblaster/src/bbl.c @@ -230,8 +230,8 @@ bbl_print_version (void) printf(" SHA: %s\n", GIT_SHA); } printf("IO Modes: packet_mmap_raw (default), packet_mmap, raw"); -#ifdef BNGBLASTER_NETMAP - printf(", netmap"); +#ifdef BNGBLASTER_DPDK + printf(", dpdk"); #endif printf("\n"); } diff --git a/code/bngblaster/src/bbl.h b/code/bngblaster/src/bbl.h index d11bbd6f..dd42467c 100644 --- a/code/bngblaster/src/bbl.h +++ b/code/bngblaster/src/bbl.h @@ -22,6 +22,7 @@ #include "bbl_def.h" #include "bbl_protocols.h" +#include "io/io_def.h" #include "bgp/bgp_def.h" #include "isis/isis_def.h" @@ -39,6 +40,8 @@ #include "bbl_a10nsp.h" #include "bbl_li.h" #include "bbl_tcp.h" + +#include "io/io.h" #include "bgp/bgp.h" #include "isis/isis.h" diff --git a/code/bngblaster/src/bbl_a10nsp.c b/code/bngblaster/src/bbl_a10nsp.c index d4da5d37..437be799 100644 --- a/code/bngblaster/src/bbl_a10nsp.c +++ b/code/bngblaster/src/bbl_a10nsp.c @@ -63,7 +63,7 @@ bbl_a10nsp_interfaces_add() CIRCLEQ_INSERT_TAIL(&g_ctx->a10nsp_interface_qhead, a10nsp_interface, a10nsp_interface_qnode); /* Init TXQ */ - a10nsp_interface->txq = calloc(1, sizeof(bbl_txq_t)); + a10nsp_interface->txq = calloc(1, sizeof(bbl_txq_s)); bbl_txq_init(a10nsp_interface->txq, BBL_TXQ_DEFAULT_SIZE); /* Init ethernet */ diff --git a/code/bngblaster/src/bbl_a10nsp.h b/code/bngblaster/src/bbl_a10nsp.h index 692f8edd..3e591916 100644 --- a/code/bngblaster/src/bbl_a10nsp.h +++ b/code/bngblaster/src/bbl_a10nsp.h @@ -22,7 +22,7 @@ typedef struct bbl_a10nsp_interface_ { bbl_interface_s *interface; - bbl_txq_t *txq; + bbl_txq_s *txq; uint8_t mac[ETH_ADDR_LEN]; bool qinq; diff --git a/code/bngblaster/src/bbl_access.c b/code/bngblaster/src/bbl_access.c index 7f6bd4f8..471ee24b 100644 --- a/code/bngblaster/src/bbl_access.c +++ b/code/bngblaster/src/bbl_access.c @@ -71,7 +71,7 @@ bbl_access_interfaces_add() CIRCLEQ_INSERT_TAIL(&g_ctx->access_interface_qhead, access_interface, access_interface_qnode); /* Init TXQ */ - access_interface->txq = calloc(1, sizeof(bbl_txq_t)); + access_interface->txq = calloc(1, sizeof(bbl_txq_s)); bbl_txq_init(access_interface->txq, BBL_TXQ_DEFAULT_SIZE); /* TX list init */ @@ -254,7 +254,7 @@ bbl_access_igmp_zapping(timer_s *timer) } } - if(!ctx->zapping && group->state < IGMP_GROUP_ACTIVE) { + if(!g_ctx->zapping && group->state < IGMP_GROUP_ACTIVE) { return; } diff --git a/code/bngblaster/src/bbl_access.h b/code/bngblaster/src/bbl_access.h index 8509d65a..e7c13cd3 100644 --- a/code/bngblaster/src/bbl_access.h +++ b/code/bngblaster/src/bbl_access.h @@ -13,7 +13,7 @@ typedef struct bbl_access_interface_ { bbl_interface_s *interface; - bbl_txq_t *txq; + bbl_txq_s *txq; uint8_t mac[ETH_ADDR_LEN]; uint32_t send_requests; diff --git a/code/bngblaster/src/bbl_config.c b/code/bngblaster/src/bbl_config.c index c4178cee..20296e11 100644 --- a/code/bngblaster/src/bbl_config.c +++ b/code/bngblaster/src/bbl_config.c @@ -317,12 +317,66 @@ json_parse_link(json_t *link, bbl_link_config_s *link_config) return false; } } - + value = json_object_get(link, "qdisc-bypass"); + if (json_is_number(value)) { + link_config->qdisc_bypass = json_number_value(value); + } else { + link_config->qdisc_bypass = g_ctx->config.qdisc_bypass; + } + if (json_unpack(link, "{s:s}", "io-mode", &s) == 0) { + if (strcmp(s, "packet_mmap_raw") == 0) { + link_config->io_mode = IO_MODE_PACKET_MMAP_RAW; + } else if (strcmp(s, "packet_mmap") == 0) { + link_config->io_mode = IO_MODE_PACKET_MMAP; + } else if (strcmp(s, "raw") == 0) { + link_config->io_mode = IO_MODE_RAW; +#if BNGBLASTER_DPDK + } else if (strcmp(s, "dpdk") == 0) { + link_config->io_mode = IO_MODE_DPDK; +#endif + } else { + fprintf(stderr, "Config error: Invalid value for links->io-mode\n"); + return false; + } + } else { + link_config->io_mode = g_ctx->config.io_mode; + } + value = json_object_get(link, "io-slots"); + if (json_is_number(value)) { + link_config->io_slots = json_number_value(value); + } else { + link_config->io_slots = g_ctx->config.io_slots; + } + value = json_object_get(link, "io-stream-max-ppi"); + if (json_is_number(value)) { + link_config->io_stream_max_ppi = json_number_value(value); + } else { + link_config->io_stream_max_ppi = g_ctx->config.io_stream_max_ppi; + } + value = json_object_get(link, "tx-interval"); + if (json_is_number(value)) { + link_config->tx_interval = json_number_value(value) * MSEC; + } else { + link_config->tx_interval = g_ctx->config.tx_interval; + } + value = json_object_get(link, "rx-interval"); + if (json_is_number(value)) { + link_config->rx_interval = json_number_value(value) * MSEC; + } else { + link_config->rx_interval = g_ctx->config.rx_interval; + } + value = json_object_get(link, "tx-threads"); + if (value) { + link_config->tx_threads = json_number_value(value); + } + value = json_object_get(link, "rx-threads"); + if (value) { + link_config->rx_threads = json_number_value(value); + } value = json_object_get(link, "lag-id"); if (value) { link_config->lag_id = json_number_value(value); } - value = json_object_get(link, "lacp-priority"); if (value) { link_config->lacp_priority = json_number_value(value); @@ -335,25 +389,6 @@ json_parse_link(json_t *link, bbl_link_config_s *link_config) } else { link_config->lacp_interval = 1000; } - - if (json_unpack(link, "{s:s}", "io-mode", &s) == 0) { - if (strcmp(s, "packet_mmap_raw") == 0) { - link_config->io_mode = IO_MODE_PACKET_MMAP_RAW; -#if BNGBLASTER_NETMAP - } else if (strcmp(s, "netmap") == 0) { - link_config->io_mode = IO_MODE_NETMAP; -#endif - } else if (strcmp(s, "packet_mmap") == 0) { - link_config->io_mode = IO_MODE_PACKET_MMAP; - } else if (strcmp(s, "raw") == 0) { - link_config->io_mode = IO_MODE_RAW; - } else { - fprintf(stderr, "Config error: Invalid value for links->io-mode\n"); - return false; - } - } else { - link_config->io_mode = g_ctx->config.io_mode; - } return true; } @@ -2036,14 +2071,14 @@ json_parse_config(json_t *root) if (json_unpack(section, "{s:s}", "io-mode", &s) == 0) { if (strcmp(s, "packet_mmap_raw") == 0) { g_ctx->config.io_mode = IO_MODE_PACKET_MMAP_RAW; -#if BNGBLASTER_NETMAP - } else if (strcmp(s, "netmap") == 0) { - g_ctx->config.io_mode = IO_MODE_NETMAP; -#endif } else if (strcmp(s, "packet_mmap") == 0) { g_ctx->config.io_mode = IO_MODE_PACKET_MMAP; } else if (strcmp(s, "raw") == 0) { g_ctx->config.io_mode = IO_MODE_RAW; +#if BNGBLASTER_DPDK + } else if (strcmp(s, "dpdk") == 0) { + g_ctx->config.io_mode = IO_MODE_DPDK; +#endif } else { fprintf(stderr, "Config error: Invalid value for interfaces->io-mode\n"); return false; diff --git a/code/bngblaster/src/bbl_config.h b/code/bngblaster/src/bbl_config.h index 7db73dda..76b8f1d9 100644 --- a/code/bngblaster/src/bbl_config.h +++ b/code/bngblaster/src/bbl_config.h @@ -123,8 +123,18 @@ typedef struct bbl_link_config_ char *interface; char *description; uint8_t mac[ETH_ADDR_LEN]; - bbl_io_mode_t io_mode; + io_mode_t io_mode; + uint16_t io_slots; + uint16_t io_stream_max_ppi; /* Traffic stream max packets per interval */ + + bool qdisc_bypass; + + uint64_t tx_interval; /* TX interval in nsec */ + uint64_t rx_interval; /* RX interval in nsec */ + + uint8_t tx_threads; + uint8_t rx_threads; uint8_t lag_id; uint32_t lacp_priority; diff --git a/code/bngblaster/src/bbl_ctx.h b/code/bngblaster/src/bbl_ctx.h index 1d17e50f..0c1bd967 100644 --- a/code/bngblaster/src/bbl_ctx.h +++ b/code/bngblaster/src/bbl_ctx.h @@ -87,6 +87,7 @@ typedef struct bbl_ctx_ char *ctrl_socket_path; void *stream_thread; /* single linked list of threads */ + io_thread_s *io_threads; /* single linked list of threads */ bool tcp; diff --git a/code/bngblaster/src/bbl_def.h b/code/bngblaster/src/bbl_def.h index a1108749..ba8268ae 100644 --- a/code/bngblaster/src/bbl_def.h +++ b/code/bngblaster/src/bbl_def.h @@ -45,14 +45,6 @@ #include "lwip/api.h" #endif -/* Experimental NETMAP Support */ -#ifdef BNGBLASTER_NETMAP -#define LIBNETMAP_NOTHREADSAFE -#include -#define NETMAP_WITH_LIBS -#include -#endif - #define IO_BUFFER_LEN 9216 #define SCRATCHPAD_LEN 4096 #define CHALLENGE_LEN 16 @@ -108,11 +100,10 @@ #define DATA_TRAFFIC_MAX_LEN 1920 typedef enum { - IO_MODE_PACKET_MMAP_RAW = 0, /* RX packet_mmap ring / TX raw sockets */ - IO_MODE_PACKET_MMAP, /* RX/TX packet_mmap ring */ - IO_MODE_RAW, /* RX/TX raw sockets */ - IO_MODE_NETMAP /* RX/TX netmap ring */ -} __attribute__ ((__packed__)) bbl_io_mode_t; + INTERFACE_DISABLED = 0, + INTERFACE_UP, + INTERFACE_DOWN +} __attribute__ ((__packed__)) bbl_interface_state_type_t; typedef enum { ACCESS_TYPE_PPPOE = 0, diff --git a/code/bngblaster/src/bbl_interface.c b/code/bngblaster/src/bbl_interface.c index c2cb9cd9..f9a0b947 100644 --- a/code/bngblaster/src/bbl_interface.c +++ b/code/bngblaster/src/bbl_interface.c @@ -25,7 +25,7 @@ bbl_interface_rate_job(timer_s *timer) { * @brief This functions locks the interface * creating the file "/run/lock/bngblaster_.lock". * - * @param interface interface + * @param interface_name interface name * @return false if failed to lock (e.g. in use) */ static bool @@ -84,45 +84,11 @@ bbl_interface_unlock_all() } } -static bool -bbl_interface_set_kernel_info(bbl_interface_s *interface) -{ - struct ifreq ifr = {0}; - - if(interface->io.mode == IO_MODE_DPDK) { - /* This will not work for DPDK bound interfaces. */ - return true; - } - - int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface->name); - if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) { - LOG(ERROR, "Getting MAC address error %s (%d) for interface %s\n", - strerror(errno), errno, interface->name); - close(fd); - return false; - } - memcpy(&interface->mac, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN); - - memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface->name); - if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) { - LOG(ERROR, "Get interface index error %s (%d) for interface %s\n", - strerror(errno), errno, interface->name); - close(fd); - return false; - } - interface->ifindex = ifr.ifr_ifindex; - - close(fd); - return true; -} - /** * bbl_add_interface * * @param interface interface name - * @param link_config optional link configuration + * @param link_config link configuration * @return interface */ static bbl_interface_s * @@ -143,32 +109,17 @@ bbl_interface_add(char *interface_name, bbl_link_config_s *link_config) } CIRCLEQ_INSERT_TAIL(&g_ctx->interface_qhead, interface, interface_qnode); - if(!bbl_interface_set_kernel_info(interface)) { - return NULL; - } - interface->config = link_config; - interface->io.rx_buf = malloc(IO_BUFFER_LEN); - interface->io.tx_buf = malloc(IO_BUFFER_LEN); - interface->io.mode = link_config->io_mode; - if(*(uint64_t*)link_config->mac & 0xffffffffffff00) { - memcpy(interface->mac, link_config->mac, ETH_ADDR_LEN); - } if(!bbl_lag_interface_add(interface, link_config)) { return NULL; } - - /* The BNG Blaster supports multiple IO modes where packet_mmap is - * selected per default. */ - if(!bbl_io_add_interface(interface)) { + if(!io_interface_init(interface)) { return NULL; } - /* - * Timer to compute periodic rates. - */ - timer_add_periodic(&g_ctx->timer_root, &interface->rate_job, "Rate Computation", 1, 0, interface, - &bbl_interface_rate_job); + /* Timer to compute periodic rates. */ + timer_add_periodic(&g_ctx->timer_root, &interface->rate_job, "Rate Computation", + 1, 0, interface, &bbl_interface_rate_job); return interface; } diff --git a/code/bngblaster/src/bbl_interface.h b/code/bngblaster/src/bbl_interface.h index 02a10bc3..6150803d 100644 --- a/code/bngblaster/src/bbl_interface.h +++ b/code/bngblaster/src/bbl_interface.h @@ -21,40 +21,18 @@ typedef struct bbl_interface_ bbl_access_interface_s *access; bbl_network_interface_s *network; + bbl_interface_state_type_t state; uint8_t mac[ETH_ADDR_LEN]; - uint32_t send_requests; CIRCLEQ_ENTRY(bbl_interface_) interface_qnode; CIRCLEQ_ENTRY(bbl_interface_) interface_lag_qnode; struct { - bbl_io_mode_t mode; - - int fd_tx; - int fd_rx; - - struct tpacket_req req_tx; - struct tpacket_req req_rx; - struct sockaddr_ll addr; - - uint8_t *rx_buf; /* RX buffer */ - uint16_t rx_len; - uint8_t *tx_buf; /* TX buffer */ - uint16_t tx_len; - - uint8_t *ring_tx; /* TX ring buffer */ - uint8_t *ring_rx; /* RX ring buffer */ - uint16_t cursor_tx; /* slot # inside the ring buffer */ - uint16_t cursor_rx; /* slot # inside the ring buffer */ - uint16_t queued_tx; - - bool pollout; - bool ctrl; /* control traffic */ - -#ifdef BNGBLASTER_NETMAP - struct nm_desc *port; -#endif + io_handle_s *rx; + io_handle_s *tx; + uint8_t *sp; + bool ctrl; } io; uint32_t ifindex; /* interface index */ @@ -83,10 +61,6 @@ typedef struct bbl_interface_ struct timer_ *tx_job; struct timer_ *rx_job; struct timer_ *rate_job; - - struct timespec tx_timestamp; /* user space timestamps */ - struct timespec rx_timestamp; /* user space timestamps */ - } bbl_interface_s; void diff --git a/code/bngblaster/src/bbl_io.c b/code/bngblaster/src/bbl_io.c index b3564568..b018f2ff 100644 --- a/code/bngblaster/src/bbl_io.c +++ b/code/bngblaster/src/bbl_io.c @@ -10,9 +10,6 @@ #include "bbl_pcap.h" #include "bbl_rx.h" #include "bbl_tx.h" -#ifdef BNGBLASTER_NETMAP -#include "bbl_io_netmap.h" -#endif void bbl_io_packet_mmap_rx_job(timer_s *timer) @@ -308,13 +305,8 @@ bbl_io_send(bbl_interface_s *interface, uint8_t *packet, uint16_t packet_len) case IO_MODE_PACKET_MMAP: result = bbl_io_packet_mmap_send(interface, packet, packet_len); break; - case IO_MODE_NETMAP: -#ifdef BNGBLASTER_NETMAP - result = bbl_io_netmap_send(interface, packet, packet_len); -#else - result = false; -#endif - break; + default: + return false; } if(result) { @@ -376,13 +368,6 @@ bbl_io_add_interface(bbl_interface_s *interface) int qdisc_bypass = 1; int slots = g_ctx->config.io_slots; - -#ifdef BNGBLASTER_NETMAP - if(interface->io.mode == IO_MODE_NETMAP) { - return bbl_io_netmap_add_interface(ctx, interface); - } -#endif - /* * Open RAW socket for all ethertypes. * https://man7.org/linux/man-pages/man7/packet.7.html diff --git a/code/bngblaster/src/bbl_io.h b/code/bngblaster/src/bbl_io.h index eda2251a..f59c4575 100644 --- a/code/bngblaster/src/bbl_io.h +++ b/code/bngblaster/src/bbl_io.h @@ -21,6 +21,7 @@ #ifndef __BBL_IO_H__ #define __BBL_IO_H__ + bool bbl_io_send(bbl_interface_s *interface, uint8_t *packet, uint16_t packet_len); diff --git a/code/bngblaster/src/bbl_io_netmap.c b/code/bngblaster/src/bbl_io_netmap.c deleted file mode 100644 index e9e86a93..00000000 --- a/code/bngblaster/src/bbl_io_netmap.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * BNG Blaster (BBL) - Netmap - * - * Christian Giese, October 2020 - * - * Copyright (C) 2020-2022, RtBrick, Inc. - * SPDX-License-Identifier: BSD-3-Clause - */ -#include "bbl.h" -#include "bbl_pcap.h" -#include "bbl_rx.h" -#include "bbl_tx.h" - -#ifdef BNGBLASTER_NETMAP - -void -bbl_io_netmap_rx_job (timer_s *timer) -{ - bbl_interface_s *interface; - bbl_ctx_s *ctx; - - struct netmap_ring *ring; - unsigned int i; - - uint8_t *eth_start; - uint16_t eth_len; - - bbl_ethernet_header_t *eth; - protocol_error_t decode_result; - - interface = timer->data; - if (!interface) { - return; - } - ctx = interface->ctx; - - /* Get RX timestamp */ - clock_gettime(CLOCK_MONOTONIC, &interface->rx_timestamp); - - ring = NETMAP_RXRING(interface->io.port->nifp, 0); - while (!nm_ring_empty(ring)) { - - i = ring->cur; - eth_start = (uint8_t*)NETMAP_BUF(ring, ring->slot[i].buf_idx); - eth_len = ring->slot[i].len; - interface->stats.packets_rx++; - interface->stats.bytes_rx += eth_len; - interface->io.ctrl = true; - decode_result = decode_ethernet(eth_start, eth_len, interface->ctx->sp_rx, SCRATCHPAD_LEN, ð); - if(decode_result == PROTOCOL_SUCCESS) { -#if 0 - /* Copy RX timestamp */ - eth->timestamp.tv_sec = ring->ts.tv_sec; - eth->timestamp.tv_nsec = ring->ts.tv_usec * 1000; -#endif - /* Copy RX timestamp */ - eth->timestamp.tv_sec = interface->rx_timestamp.tv_sec; - eth->timestamp.tv_nsec = interface->rx_timestamp.tv_nsec; - bbl_rx_handler(interface, eth); - } else if (decode_result == UNKNOWN_PROTOCOL) { - interface->stats.unknown++; - } else { - interface->stats.decode_error++; - } - - /* Dump the packet into pcap file. */ - if (g_ctx->pcap.write_buf && (interface->io.ctrl || g_ctx->pcap.include_streams)) { - pcapng_push_packet_header(ctx, &interface->rx_timestamp, eth_start, eth_len, - interface->pcap_index, PCAPNG_EPB_FLAGS_INBOUND); - } - - ring->head = ring->cur = nm_ring_next(ring, i); - } - pcapng_fflush(ctx); - ioctl(interface->io.port->fd, NIOCRXSYNC, NULL); -} - -void -bbl_io_netmap_tx_job (timer_s *timer) -{ - bbl_interface_s *interface; - bbl_ctx_s *ctx; - - struct netmap_ring *ring; - unsigned int i; - - uint8_t *buf; - uint16_t len; - uint16_t packets = 0; - - protocol_error_t tx_result = IGNORED; - - interface = timer->data; - if (!interface) { - return; - } - ctx = interface->ctx; - - /* Get TX timestamp */ - clock_gettime(CLOCK_MONOTONIC, &interface->tx_timestamp); - - ring = NETMAP_TXRING(interface->io.port->nifp, 0); - while(tx_result != EMPTY) { - /* Check if this slot available for writing. */ - if(nm_ring_empty(ring)) { - interface->stats.no_buffer++; - break; - } - i = ring->cur; - buf = (uint8_t*)NETMAP_BUF(ring, ring->slot[i].buf_idx); - - tx_result = bbl_tx(ctx, interface, buf, &len); - if(tx_result == PROTOCOL_SUCCESS) { - packets++; - interface->stats.packets_tx++; - interface->stats.bytes_tx += len; - ring->slot[i].len = len; - ring->head = ring->cur = nm_ring_next(ring, i); - /* Dump the packet into pcap file. */ - if(g_ctx->pcap.write_buf && (interface->io.ctrl || g_ctx->pcap.include_streams)) { - pcapng_push_packet_header(ctx, &interface->tx_timestamp, - buf, len, interface->pcap_index, - PCAPNG_EPB_FLAGS_OUTBOUND); - } - } - } - if(packets) { - pcapng_fflush(ctx); - ioctl(interface->io.port->fd, NIOCTXSYNC, NULL); - } -} - -/** - * bbl_io_netmap_send - * - * Send single packet trough given interface. - * - * @param interface interface. - * @param packet packet to be send - * @param packet_len packet length - */ -bool -bbl_io_netmap_send (bbl_interface_s *interface, uint8_t *packet, uint16_t packet_len) { - struct netmap_ring *ring; - unsigned int i; - uint8_t *buf; - ring = NETMAP_TXRING(interface->io.port->nifp, 0); - if (nm_ring_empty(ring)) { - interface->stats.no_buffer++; - return false; - } - i = ring->cur; - buf = (uint8_t*)NETMAP_BUF(ring, ring->slot[i].buf_idx); - memcpy(buf, packet, packet_len); - ring->slot[i].len = packet_len; - ring->head = ring->cur = nm_ring_next(ring, i); - return true; -} - -/** - * bbl_io_netmap_add_interface - * - * @param ctx global context - * @param interface interface. - */ -bool -bbl_io_netmap_add_interface(bbl_interface_s *interface) { - char timer_name[128]; - char netmap_port[128]; - - snprintf(netmap_port, sizeof(netmap_port), "netmap:%s", interface->name); - - /* - * Open netmap port. - */ - interface->io.port = nm_open(netmap_port, NULL, NETMAP_NO_TX_POLL, NULL); - if (interface->io.port == NULL) { - if (!errno) { - LOG(ERROR, "Failed to nm_open(%s): not a netmap port\n", netmap_port); - } else { - LOG(ERROR, "Failed to nm_open(%s): %s\n", netmap_port, strerror(errno)); - } - return false; - } - - /* - * Add an periodic timer for polling I/O. - */ - snprintf(timer_name, sizeof(timer_name), "%s TX", interface->name); - timer_add_periodic(&g_ctx->timer_root, &interface->tx_job, timer_name, 0, g_ctx->config.tx_interval, interface, &bbl_io_netmap_tx_job); - snprintf(timer_name, sizeof(timer_name), "%s RX", interface->name); - timer_add_periodic(&g_ctx->timer_root, &interface->rx_job, timer_name, 0, g_ctx->config.rx_interval, interface, &bbl_io_netmap_rx_job); - - return true; -} - -#endif diff --git a/code/bngblaster/src/bbl_io_netmap.h b/code/bngblaster/src/bbl_io_netmap.h deleted file mode 100644 index fb41c5d2..00000000 --- a/code/bngblaster/src/bbl_io_netmap.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * BNG Blaster (BBL) - Netmap - * - * Christian Giese, October 2020 - * - * Netmap is a an framework for very fast packet I/O from userspace. - * https://github.com/luigirizzo/netmap - * - * Copyright (C) 2020-2022, RtBrick, Inc. - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef __BBL_NETMAP_H__ -#define __BBL_NETMAP_H__ - -bool -bbl_io_netmap_send(bbl_interface_s *interface, uint8_t *packet, uint16_t packet_len); - -bool -bbl_io_netmap_add_interface(bbl_interface_s *interface); - -#endif \ No newline at end of file diff --git a/code/bngblaster/src/bbl_lag.h b/code/bngblaster/src/bbl_lag.h index bcbee169..5cdb38c7 100644 --- a/code/bngblaster/src/bbl_lag.h +++ b/code/bngblaster/src/bbl_lag.h @@ -19,7 +19,7 @@ typedef struct bbl_lag_ CIRCLEQ_HEAD(interface_, bbl_interface_ ) interface_qhead; /* list of interfaces */ CIRCLEQ_ENTRY(bbl_lag_) lag_qnode; - bool up; + bbl_interface_state_type_t state; struct { uint64_t packets_tx; @@ -32,6 +32,9 @@ typedef struct bbl_lag_ bbl_rate_s rate_bytes_rx; } stats; + struct timer_ *lacp_timer; + + struct timer_ *tx_job; struct timer_ *rate_job; } bbl_lag_s; diff --git a/code/bngblaster/src/bbl_network.c b/code/bngblaster/src/bbl_network.c index 5756d174..4ba2d25f 100644 --- a/code/bngblaster/src/bbl_network.c +++ b/code/bngblaster/src/bbl_network.c @@ -93,7 +93,7 @@ bbl_network_interfaces_add() CIRCLEQ_INSERT_TAIL(&g_ctx->network_interface_qhead, network_interface, network_interface_qnode); /* Init TXQ */ - network_interface->txq = calloc(1, sizeof(bbl_txq_t)); + network_interface->txq = calloc(1, sizeof(bbl_txq_s)); bbl_txq_init(network_interface->txq, BBL_TXQ_DEFAULT_SIZE); /* Init ethernet */ diff --git a/code/bngblaster/src/bbl_network.h b/code/bngblaster/src/bbl_network.h index 62f63987..9a922c4c 100644 --- a/code/bngblaster/src/bbl_network.h +++ b/code/bngblaster/src/bbl_network.h @@ -15,7 +15,7 @@ typedef struct bbl_network_interface_ char *name; bbl_interface_s *interface; struct bbl_network_interface_ *next; - bbl_txq_t *txq; + bbl_txq_s *txq; uint16_t vlan; bbl_mpls_t tx_label; diff --git a/code/bngblaster/src/bbl_tx.c b/code/bngblaster/src/bbl_tx.c index bc1da48e..7dc7831d 100644 --- a/code/bngblaster/src/bbl_tx.c +++ b/code/bngblaster/src/bbl_tx.c @@ -1812,6 +1812,9 @@ bbl_tx(bbl_interface_s *interface, uint8_t *buf, uint16_t *len) return bbl_tx_encode_interface_packet(interface, buf, len); } + if(interface->state != INTERFACE_UP) { + return EMPTY; + } if(interface->access) { access_interface = interface->access; diff --git a/code/bngblaster/src/bbl_txq.c b/code/bngblaster/src/bbl_txq.c index d2183a26..cba1c078 100644 --- a/code/bngblaster/src/bbl_txq.c +++ b/code/bngblaster/src/bbl_txq.c @@ -11,7 +11,8 @@ #include "bbl_session.h" bool -bbl_txq_init(bbl_txq_t *txq, uint16_t size) { +bbl_txq_init(bbl_txq_s *txq, uint16_t size) +{ txq->ring = malloc(size * sizeof(bbl_txq_slot_t)); if(!txq->ring) { return false; @@ -24,7 +25,8 @@ bbl_txq_init(bbl_txq_t *txq, uint16_t size) { } bool -bbl_txq_is_empty(bbl_txq_t *txq) { +bbl_txq_is_empty(bbl_txq_s *txq) +{ if(txq->read == txq->write) { return true; } @@ -32,7 +34,8 @@ bbl_txq_is_empty(bbl_txq_t *txq) { } bool -bbl_txq_is_full(bbl_txq_t *txq) { +bbl_txq_is_full(bbl_txq_s *txq) +{ if(txq->read == txq->next) { return true; } @@ -48,7 +51,8 @@ bbl_txq_is_full(bbl_txq_t *txq) { * @return number of bytes copied */ uint16_t -bbl_txq_from_buffer(bbl_txq_t *txq, uint8_t *buf) { +bbl_txq_from_buffer(bbl_txq_s *txq, uint8_t *buf) +{ bbl_txq_slot_t *slot; if(txq->read == txq->write) { @@ -73,7 +77,8 @@ bbl_txq_from_buffer(bbl_txq_t *txq, uint8_t *buf) { * @return bbl_txq_result_t */ bbl_txq_result_t -bbl_txq_to_buffer(bbl_txq_t *txq, bbl_ethernet_header_t *eth) { +bbl_txq_to_buffer(bbl_txq_s *txq, bbl_ethernet_header_t *eth) +{ bbl_txq_slot_t *slot; if(txq->read == txq->next) { @@ -92,4 +97,40 @@ bbl_txq_to_buffer(bbl_txq_t *txq, bbl_ethernet_header_t *eth) { txq->stats.encode_error++; return BBL_TXQ_ENCODE_ERROR; } +} + +bbl_txq_slot_t * +bbl_txq_read_slot(bbl_txq_s *txq) { + if(txq->read == txq->write) { + return NULL; + } + return txq->ring + txq->read; +} + +void +bbl_txq_read_next(bbl_txq_s *txq) +{ + txq->read++; + if(txq->read == txq->size) { + txq->read = 0; + } +} + +bbl_txq_slot_t * +bbl_txq_write_slot(bbl_txq_s *txq) { + if(txq->read == txq->next) { + txq->stats.full++; + return NULL; + } + + return txq->ring + txq->write; +} + +void +bbl_txq_write_next(bbl_txq_s *txq) +{ + txq->write = txq->next++; + if(txq->next == txq->size) { + txq->next = 0; + } } \ No newline at end of file diff --git a/code/bngblaster/src/bbl_txq.h b/code/bngblaster/src/bbl_txq.h index ad28e9b5..b6d0f214 100644 --- a/code/bngblaster/src/bbl_txq.h +++ b/code/bngblaster/src/bbl_txq.h @@ -14,7 +14,7 @@ #define __BBL_TXQ_H__ #define BBL_TXQ_DEFAULT_SIZE 4096 -#define BBL_TXQ_BUFFER_LEN 4092 +#define BBL_TXQ_BUFFER_LEN 4074 typedef enum bbl_ring_result_ { BBL_TXQ_OK = 0, @@ -23,35 +23,44 @@ typedef enum bbl_ring_result_ { } bbl_txq_result_t; typedef struct bbl_txq_slot_ { + struct timespec timestamp; + uint16_t vlan_tci; + uint16_t vlan_tpid; uint16_t packet_len; uint8_t packet[BBL_TXQ_BUFFER_LEN]; } bbl_txq_slot_t; typedef struct bbl_txq_ { bbl_txq_slot_t *ring; /* ring buffer */ - uint16_t size; /* number of send slots */ - uint16_t read; /* current read slot */ - uint16_t write; /* current write slot */ - uint16_t next; /* next write slot */ + uint16_t size; /* number of send slots */ + + char _pad0 __attribute__((__aligned__(CACHE_LINE_SIZE))); /* empty cache line */ + + volatile uint16_t write; /* current write slot */ + volatile uint16_t next; /* next write slot */ struct { uint32_t full; uint32_t encode_error; } stats; -} bbl_txq_t; + + char _pad1 __attribute__((__aligned__(CACHE_LINE_SIZE))); /* empty cache line */ + + volatile uint16_t read; /* current read slot */ +} bbl_txq_s; bool -bbl_txq_init(bbl_txq_t *txq, uint16_t slots); +bbl_txq_init(bbl_txq_s *txq, uint16_t slots); bool -bbl_txq_is_empty(bbl_txq_t *txq); +bbl_txq_is_empty(bbl_txq_s *txq); bool -bbl_txq_is_full(bbl_txq_t *txq); +bbl_txq_is_full(bbl_txq_s *txq); uint16_t -bbl_txq_from_buffer(bbl_txq_t *txq, uint8_t *buf); +bbl_txq_from_buffer(bbl_txq_s *txq, uint8_t *buf); bbl_txq_result_t -bbl_txq_to_buffer(bbl_txq_t *txq, bbl_ethernet_header_t *eth); +bbl_txq_to_buffer(bbl_txq_s *txq, bbl_ethernet_header_t *eth); #endif \ No newline at end of file diff --git a/code/bngblaster/src/io/io.c b/code/bngblaster/src/io/io.c new file mode 100644 index 00000000..f602278c --- /dev/null +++ b/code/bngblaster/src/io/io.c @@ -0,0 +1,50 @@ +/* + * BNG Blaster (BBL) - IO + * + * Christian Giese, July 2022 + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "io.h" + +/** + * bbl_io_send + * + * Send single packet trough given interface. + * + * @param interface interface. + * @param packet packet to be send + * @param packet_len packet length + */ +bool +bbl_io_send(bbl_interface_t *interface, uint8_t *packet, uint16_t packet_len) { + bbl_ctx_t *ctx = interface->ctx; + bool result = false; + + switch (interface->io.mode) { + case IO_MODE_PACKET_MMAP_RAW: + case IO_MODE_RAW: + result = bbl_io_raw_send(interface, packet, packet_len); + break; + case IO_MODE_PACKET_MMAP: + result = bbl_io_packet_mmap_send(interface, packet, packet_len); + break; + default: + return false; + } + + if(result) { + interface->stats.packets_tx++; + interface->stats.bytes_tx += packet_len; + /* Dump the packet into pcap file. */ + if(g_ctx->pcap.write_buf && (interface->io.ctrl || ctx->pcap.include_streams)) { + pcapng_push_packet_header(ctx, &interface->tx_timestamp, + packet, packet_len, interface->pcap_index, + PCAPNG_EPB_FLAGS_OUTBOUND); + pcapng_fflush(ctx); + } + } + return result; +} \ No newline at end of file diff --git a/code/bngblaster/src/io/io.h b/code/bngblaster/src/io/io.h new file mode 100644 index 00000000..71bc5b45 --- /dev/null +++ b/code/bngblaster/src/io/io.h @@ -0,0 +1,24 @@ +/* + * BNG Blaster (BBL) - IO + * + * Christian Giese, August 2022 + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __BBL_IO_H__ +#define __BBL_IO_H__ + +#include "../bbl.h" +#include "../bbl_pcap.h" +#include "../bbl_rx.h" +#include "../bbl_tx.h" + +#include "io_def.h" +#include "io_socket.h" +#include "io_interface.h" + +#include "io_raw.h" +#include "io_packet_mmap.h" + +#endif \ No newline at end of file diff --git a/code/bngblaster/src/io/io_def.h b/code/bngblaster/src/io/io_def.h new file mode 100644 index 00000000..ee0a7ab9 --- /dev/null +++ b/code/bngblaster/src/io/io_def.h @@ -0,0 +1,101 @@ +/* + * BNG Blaster (BBL) - IO Definitions + * + * Christian Giese, August 2022 + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __BBL_IO_DEF_H__ +#define __BBL_IO_DEF_H__ + +typedef struct io_handle_ io_handle_s; +typedef struct io_thread_ io_thread_s; + +typedef enum io_result_ { + IO_SUCCESS, + IO_REDIRECT, + IO_ERROR, + IO_DECODE_ERROR, + IO_ENCODE_ERROR, + IO_FULL, + IO_EMPTY +} __attribute__ ((__packed__)) io_result_t; + +typedef enum { + IO_DISABLED = 0, + IO_INGRESS = 1, + IO_EGRESS = 2, + IO_DUPLEX = 3 +} __attribute__ ((__packed__)) io_direction_t; + +typedef enum { + IO_MODE_DISABLED = 0, + IO_MODE_PACKET_MMAP_RAW, /* packet_mmap ring (RX) and raw sockets (TX) */ + IO_MODE_PACKET_MMAP, /* packet_mmap ring */ + IO_MODE_RAW, /* raw sockets */ + IO_MODE_DPDK, /* DPDK */ + IO_MODE_AF_XDP /* AF_XDP */ +} __attribute__ ((__packed__)) io_mode_t; + +typedef struct io_handle_ { + io_mode_t mode; + io_direction_t direction; + + int id; + int fd; + int fanout_id; + int fanout_type; + struct tpacket_req req; + struct sockaddr_ll addr; + + uint8_t *ring; /* ring buffer */ + uint16_t cursor; /* ring buffer cursor */ + uint16_t queued; + bool polled; + + bbl_interface_s *interface; + bbl_txq_s *txq; + + uint8_t *sp; + uint8_t *buf; + uint16_t buf_len; + + bbl_ethernet_header_t *eth; + + struct timespec timestamp; /* user space timestamps */ + + io_thread_s *thread; + + struct { + uint64_t packets; + uint64_t bytes; + + uint64_t stream_packets; + uint64_t stream_bytes; + } stats; + + struct io_handle_ *next; +} io_handle_s; + +typedef void (*io_thread_start_fn)(void *arg); + +typedef struct io_thread_ { + pthread_t thread; + pthread_mutex_t mutex; + volatile bool active; + volatile bool stopped; + + io_thread_start_fn start_fn; + + struct timer_root_ timer_root; + struct timer_ *ctrl_timer; + struct timer_ *io_timer; + + struct timer_ *main_rx_job; /* main loop RX job */ + + io_handle_s *io; + struct io_thread_ *next; +} io_thread_s; + +#endif \ No newline at end of file diff --git a/code/bngblaster/src/io/io_interface.c b/code/bngblaster/src/io/io_interface.c new file mode 100644 index 00000000..2b015865 --- /dev/null +++ b/code/bngblaster/src/io/io_interface.c @@ -0,0 +1,130 @@ +/* + * BNG Blaster (BBL) - IO Interface Functions + * + * Christian Giese, July 2022 + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "io.h" + +static bool +set_kernel_info(bbl_interface_s *interface) +{ + struct ifreq ifr = {0}; + + int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface->name); + if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) { + LOG(ERROR, "Getting MAC address error %s (%d) for interface %s\n", + strerror(errno), errno, interface->name); + close(fd); + return false; + } + memcpy(&interface->mac, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN); + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface->name); + if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) { + LOG(ERROR, "Get interface index error %s (%d) for interface %s\n", + strerror(errno), errno, interface->name); + close(fd); + return false; + } + interface->ifindex = ifr.ifr_ifindex; + + close(fd); + return true; +} + +/* Set interface in promisc mode. */ +static bool +set_promisc(bbl_interface_s *interface) { + /* Taken and adapted from: + * https://stackoverflow.com/questions/41678219/how-to-properly-put-network-interface-into-promiscuous-mode-on-linux + * This prevents the ioctl get flags / set flags race condition. */ + struct packet_mreq mreq = {0}; + int sfd; + + /* This socket is only opened, but not closed. Closing the socket would reset + * its flags - effectively removing the just added promisc mode. + * We want to keep the interface in promisc mode until the end of the program. */ + if ((sfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) { + LOG_NOARG(ERROR, "Unable to open control socket for promiscuous mode activation\n"); + return -1; + } + + mreq.mr_type = PACKET_MR_PROMISC; + mreq.mr_ifindex = interface->ifindex; + if(setsockopt(sfd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0) { + LOG(ERROR, "Failed to put interface %s in promiscuous mode\n", interface->name); + return false; + } + return true; +} + +static bool +io_interface_init_rx(bbl_interface_s *interface) +{ + bbl_link_config_s *config = interface->config; + io_handle_s *io; + + uint8_t count = 1; + if(config->rx_threads) { + count = config->rx_threads; + } + + while(count) { + io = calloc(1, sizeof(io_handle_s)); + if(!io) return false; + io->id = count--; + io->mode = config->io_mode; + if(io->mode == IO_MODE_PACKET_MMAP_RAW) { + io->mode = IO_MODE_PACKET_MMAP; + } + io->direction = IO_INGRESS; + io->next = interface->io.rx; + interface->io.rx = io->next; + io->sp = malloc(SCRATCHPAD_LEN); + io->interface = interface; + switch(io->mode) { + case IO_MODE_PACKET_MMAP: + if(!io_packet_mmap_init(io)) { + return false; + } + break; + case IO_MODE_RAW: + if(!io_raw_init(io)) { + return false; + } + break; + default: + return false; + } + } + return true; +} + +/** + * io_interface_init + * + * @param interface interface. + */ +bool +io_interface_init(bbl_interface_s *interface) +{ + bbl_link_config_s *config = interface->config; + + if(config->io_mode != IO_MODE_DPDK) { + set_kernel_info(interface); + set_promisc(interface); + } + if(*(uint64_t*)config->mac & 0xffffffffffff00) { + memcpy(interface->mac, config->mac, ETH_ADDR_LEN); + } + interface->io.sp = malloc(SCRATCHPAD_LEN); + + /* RX */ + + +} \ No newline at end of file diff --git a/code/bngblaster/src/io/io_interface.h b/code/bngblaster/src/io/io_interface.h new file mode 100644 index 00000000..6753945e --- /dev/null +++ b/code/bngblaster/src/io/io_interface.h @@ -0,0 +1,15 @@ +/* + * BNG Blaster (BBL) - IO Interface Functions + * + * Christian Giese, August 2022 + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __BBL_IO_INTERFACE_H__ +#define __BBL_IO_INTERFACE_H__ + +bool +io_interface_init(bbl_interface_s *interface); + +#endif \ No newline at end of file diff --git a/code/bngblaster/src/io/io_packet_mmap.c b/code/bngblaster/src/io/io_packet_mmap.c new file mode 100644 index 00000000..b352d7d7 --- /dev/null +++ b/code/bngblaster/src/io/io_packet_mmap.c @@ -0,0 +1,194 @@ +/* + * BNG Blaster (BBL) - IO PACKET_MMAP + * + * Christian Giese, July 2022 + * + * PACKET_MMAP provides a size configurable circular buffer mapped in user space + * that can be used to either send or receive packets. This way reading packets + * just needs to wait for them, most of the time there is no need to issue a single + * system call. Concerning transmission, multiple packets can be sent through one + * system call to get the highest bandwidth. By using a shared buffer between the + * kernel and the user also has the benefit of minimizing packet copies. + * + * https://www.kernel.org/doc/Documentation/networking/packet_mmap.txt + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "io.h" + +static void +poll_kernel(io_handle_s *io, short events) +{ + struct pollfd fds[1] = {0}; + fds[0].fd = io->fd; + fds[0].events = events; + fds[0].revents = 0; + if (poll(fds, 1, 0) == -1) { + LOG(IO, "Failed to poll interface %s", + io->interface->name); + } +} + +void +io_packet_mmap_rx_job(timer_s *timer) +{ + io_handle_s *io = timer->data; + bbl_interface_s *interface = io->interface; + + uint8_t *frame_ptr; + struct tpacket2_hdr *tphdr; + + bbl_ethernet_header_t *eth; + uint16_t vlan; + + protocol_error_t decode_result; + bool pcap = false; + + assert(io->mode == IO_MODE_PACKET_MMAP); + assert(io->direction == IO_INGRESS); + assert(io->thread == NULL); + + frame_ptr = io->ring + (io->cursor * io->req.tp_frame_size); + tphdr = (struct tpacket2_hdr*)frame_ptr; + if(!(tphdr->tp_status & TP_STATUS_USER)) { + /* If no buffer is available poll kernel */ + poll_kernel(io, POLLIN); + interface->stats.poll_rx++; + return; + } + + /* Get RX timestamp */ + clock_gettime(CLOCK_MONOTONIC, &io->timestamp); + while(tphdr->tp_status & TP_STATUS_USER) { + io->buf = (uint8_t*)tphdr + tphdr->tp_mac; + io->buf_len = tphdr->tp_len; + interface->stats.packets_rx++; + interface->stats.bytes_rx += io->buf_len; + interface->io.ctrl = true; + decode_result = decode_ethernet(io->buf, io->buf_len, io->sp, SCRATCHPAD_LEN, ð); + if(decode_result == PROTOCOL_SUCCESS) { + vlan = tphdr->tp_vlan_tci & ETH_VLAN_ID_MAX; + if(vlan && eth->vlan_outer != vlan) { + /* The outer VLAN is stripped from header */ + eth->vlan_inner = eth->vlan_outer; + eth->vlan_inner_priority = eth->vlan_outer_priority; + eth->vlan_outer = vlan; + eth->vlan_outer_priority = tphdr->tp_vlan_tci >> 13; + if(tphdr->tp_vlan_tpid == ETH_TYPE_QINQ) { + eth->qinq = true; + } + } + /* Copy RX timestamp */ + //eth->timestamp.tv_sec = tphdr->tp_sec; /* ktime/hw timestamp */ + //eth->timestamp.tv_nsec = tphdr->tp_nsec; /* ktime/hw timestamp */ + eth->timestamp.tv_sec = io->timestamp.tv_sec; + eth->timestamp.tv_nsec = io->timestamp.tv_nsec; + bbl_rx_handler(interface, eth); + } else if (decode_result == UNKNOWN_PROTOCOL) { + interface->stats.unknown++; + } else { + interface->stats.decode_error++; + } + /* Dump the packet into pcap file. */ + if (g_ctx->pcap.write_buf && (interface->io.ctrl || g_ctx->pcap.include_streams)) { + pcap = true; + pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len, + interface->pcap_index, PCAPNG_EPB_FLAGS_INBOUND); + } + /* Return ownership back to kernel. */ + tphdr->tp_status = TP_STATUS_KERNEL; + /* Get next packet. */ + io->cursor = (io->cursor + 1) % io->req.tp_frame_nr; + frame_ptr = io->ring + (io->cursor * io->req.tp_frame_size); + tphdr = (struct tpacket2_hdr*)frame_ptr; + } + if(pcap) { + pcapng_fflush(); + } +} + +void +bbl_io_packet_mmap_tx_job(timer_s *timer) +{ + io_handle_s *io = timer->data; + bbl_interface_s *interface = io->interface; + protocol_error_t tx_result = IGNORED; + + struct tpacket2_hdr* tphdr; + + uint8_t *frame_ptr; + bool pcap = false; + + assert(io->mode == IO_MODE_PACKET_MMAP); + assert(io->direction == IO_EGRESS); + assert(io->thread == NULL); + + frame_ptr = io->ring + (io->cursor * io->req.tp_frame_size); + tphdr = (struct tpacket2_hdr *)frame_ptr; + if (tphdr->tp_status != TP_STATUS_AVAILABLE) { + if(io->polled) { + /* We already polled kernel. */ + return; + } + /* If no buffer is available poll kernel. */ + poll_kernel(io, POLLOUT); + io->polled = true; + interface->stats.poll_tx++; + } else { + io->polled = false; + + /* Get TX timestamp */ + clock_gettime(CLOCK_MONOTONIC, &io->timestamp); + + while(tx_result != EMPTY) { + /* Check if this slot available for writing. */ + if (tphdr->tp_status != TP_STATUS_AVAILABLE) { + interface->stats.no_buffer++; + break; + } + io->buf = frame_ptr + TPACKET2_HDRLEN - sizeof(struct sockaddr_ll); + tx_result = bbl_tx(interface, io->buf, &io->buf_len); + if (tx_result == PROTOCOL_SUCCESS) { + io->queued++; + tphdr->tp_len = io->buf_len; + tphdr->tp_status = TP_STATUS_SEND_REQUEST; + interface->stats.packets_tx++; + interface->stats.bytes_tx += io->buf_len; + /* Dump the packet into pcap file. */ + if(g_ctx->pcap.write_buf && (interface->io.ctrl || g_ctx->pcap.include_streams)) { + pcap = true; + pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len, + interface->pcap_index, PCAPNG_EPB_FLAGS_OUTBOUND); + } + /* Request send from kernel. */ + tphdr->tp_status = TP_STATUS_SEND_REQUEST; + /* Get next slot. */ + io->cursor = (io->cursor + 1) % io->req.tp_frame_nr; + frame_ptr = io->ring + (io->cursor * io->req.tp_frame_size); + tphdr = (struct tpacket2_hdr *)frame_ptr; + } + } + if(pcap) { + pcapng_fflush(); + } + } + + if(io->queued) { + /* Notify kernel. */ + if (sendto(io->fd, NULL, 0, 0, NULL, 0) == -1) { + LOG(IO, "Sendto failed with errno: %i\n", errno); + interface->stats.sendto_failed++; + } else { + io->queued = 0; + } + } +} + +bool +io_packet_mmap_init(io_handle_s *io) { + bbl_interface_s *interface = io->interface; + bbl_link_config_s *config = interface->config; + + +} diff --git a/code/bngblaster/src/io/io_packet_mmap.h b/code/bngblaster/src/io/io_packet_mmap.h new file mode 100644 index 00000000..1c4d09a3 --- /dev/null +++ b/code/bngblaster/src/io/io_packet_mmap.h @@ -0,0 +1,13 @@ +/* + * BNG Blaster (BBL) - IO PACKET_MMAP + * + * Christian Giese, July 2022 + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __BBL_IO_PACKET_MMAP_H__ +#define __BBL_IO_PACKET_MMAP_H__ + + +#endif \ No newline at end of file diff --git a/code/bngblaster/src/io/io_raw.c b/code/bngblaster/src/io/io_raw.c new file mode 100644 index 00000000..2bcd025a --- /dev/null +++ b/code/bngblaster/src/io/io_raw.c @@ -0,0 +1,110 @@ +/* + * BNG Blaster (BBL) - IO RAW + * + * Christian Giese, July 2022 + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "io.h" + +/** + * This job is for RAW RX in main thread! + */ +void +io_raw_rx_job(timer_s *timer) +{ + io_handle_s *io = timer->data; + bbl_interface_s *interface = io->interface; + + struct sockaddr saddr; + int saddr_size = sizeof(saddr); + + bbl_ethernet_header_t *eth; + + protocol_error_t decode_result; + + bool pcap = false; + + assert(io->mode == IO_MODE_RAW); + assert(io->direction == IO_INGRESS); + assert(io->thread == NULL); + + /* Get RX timestamp */ + clock_gettime(CLOCK_MONOTONIC, &io->timestamp); + while(true) { + io->buf_len = recvfrom(io->fd, io->buf, IO_BUFFER_LEN, 0, &saddr , (socklen_t*)&saddr_size); + if(io->buf_len < 14 || io->buf_len > IO_BUFFER_LEN) { + break; + } + io->stats.packets++; + io->stats.bytes += io->buf_len; + interface->io.ctrl = true; + decode_result = io_decode_ethernet(io->buf, io->buf_len, io->sp, SCRATCHPAD_LEN, ð); + if(decode_result == PROTOCOL_SUCCESS) { + /* Copy RX timestamp */ + eth->timestamp.tv_sec = io->timestamp.tv_sec; + eth->timestamp.tv_nsec = io->timestamp.tv_nsec; + bbl_rx_handler(interface, eth); + } else if (decode_result == UNKNOWN_PROTOCOL) { + interface->stats.unknown++; + } else { + interface->stats.decode_error++; + } + /* Dump the packet into pcap file. */ + if (g_ctx->pcap.write_buf && (interface->io.ctrl || g_ctx->pcap.include_streams)) { + pcap = true; + pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len, + interface->pcap_index, PCAPNG_EPB_FLAGS_INBOUND); + } + } + if(pcap) { + pcapng_fflush(); + } +} + +/** + * This job is for RAW TX in main thread! + */ +void +io_raw_tx_job(timer_s *timer) +{ + io_handle_s *io = timer->data; + bbl_interface_s *interface = io->interface; + protocol_error_t tx_result = IGNORED; + + bool pcap = false; + + assert(io->mode == IO_MODE_RAW); + assert(io->direction == IO_EGRESS); + assert(io->thread == NULL); + + /* Get TX timestamp */ + clock_gettime(CLOCK_MONOTONIC, &io->timestamp); + while(tx_result != EMPTY) { + /* If sendto fails, the failed packet remains in TX buffer to be retried + * in the next interval. */ + if(!io->buf_len) { + tx_result = bbl_tx(interface, io->buf, &io->buf_len); + } + if(tx_result == PROTOCOL_SUCCESS) { + if (sendto(io->fd, io->buf, io->buf_len, 0, (struct sockaddr*)&io->addr, sizeof(struct sockaddr_ll)) <0 ) { + LOG(IO, "Sendto failed with errno: %i\n", errno); + interface->stats.sendto_failed++; + return; + } + interface->stats.packets_tx++; + interface->stats.bytes_tx += io->buf_len; + /* Dump the packet into pcap file. */ + if(g_ctx->pcap.write_buf && (interface->io.ctrl || g_ctx->pcap.include_streams)) { + pcap = true; + pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len, + interface->pcap_index, PCAPNG_EPB_FLAGS_OUTBOUND); + } + } + io->buf_len = 0; + } + if(pcap) { + pcapng_fflush(); + } +} \ No newline at end of file diff --git a/code/bngblaster/src/io/io_raw.h b/code/bngblaster/src/io/io_raw.h new file mode 100644 index 00000000..439a09b1 --- /dev/null +++ b/code/bngblaster/src/io/io_raw.h @@ -0,0 +1,12 @@ +/* + * BNG Blaster (BBL) - IO RAW + * + * Christian Giese, July 2022 + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __BBL_IO_RAW_H__ +#define __BBL_IO_RAW_H__ + +#endif \ No newline at end of file diff --git a/code/bngblaster/src/io/io_socket.c b/code/bngblaster/src/io/io_socket.c new file mode 100644 index 00000000..5e3623a5 --- /dev/null +++ b/code/bngblaster/src/io/io_socket.c @@ -0,0 +1,142 @@ +/* + * BNG Blaster (BBL) - IO Linux Socket Functions + * + * Christian Giese, August 2022 + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "io.h" + +/* Bypass TC_QDISC, such that the kernel is hammered 30% less with + * processing packets. Only for the TX FD. */ +static bool +set_qdisc_bypass(io_handle_s *io) { + /* PACKET_QDISC_BYPASS (since Linux 3.14) + * By default, packets sent through packet sockets pass through + * the kernel's qdisc (traffic control) layer, which is fine for + * the vast majority of use cases. For traffic generator appli‐ + * ances using packet sockets that intend to brute-force flood + * the network—for example, to test devices under load in a simi‐ + * lar fashion to pktgen—this layer can be bypassed by setting + * this integer option to 1. A side effect is that packet + * buffering in the qdisc layer is avoided, which will lead to + * increased drops when network device transmit queues are busy; + * therefore, use at your own risk. */ + int qdisc_bypass = 1; + if (setsockopt(io->fd, SOL_PACKET, PACKET_QDISC_BYPASS, &qdisc_bypass, sizeof(qdisc_bypass)) == -1) { + LOG(ERROR, "Failed to set qdisc bypass for interface %s - %s (%d)\n", + io->interface->name, strerror(errno), errno); + return false; + } + return true; +} + +/* Set fanout group. */ +static bool +set_fanout(io_handle_s *io) { + if(io->direction == IO_INGRESS && io->fanout_id) { + int fanout_arg = (io->fanout_id | (io->fanout_type << 16)); + if (setsockopt(io->fd, SOL_PACKET, PACKET_FANOUT, &fanout_arg, sizeof(fanout_arg)) == -1) { + LOG(ERROR, "Failed to set fanout group for interface %s - %s (%d)\n", + io->interface->name, strerror(errno), errno); + return false; + } + } + return true; +} + +/* Set packet version (TPACKET_V1, TPACKET_V2 or TPACKET_V3). */ +static bool +set_packet_version(io_handle_s *io, int version) { + if ((setsockopt(io->fd, SOL_PACKET, PACKET_VERSION, &version, sizeof(version))) == -1) { + LOG(ERROR, "Failed to set packet version error for interface %s - %s (%d)\n", + io->interface->name, strerror(errno), errno); + return false; + } + return true; +} + +/* Setup ringbuffer. */ +static void * +set_ring(io_handle_s *io, int slots) { + /* The following are conditions that are checked in packet_set_ring: + * - tp_block_size must be a multiple of PAGE_SIZE (1) + * - tp_frame_size must be greater than TPACKET_HDRLEN (obvious) + * - tp_frame_size must be a multiple of TPACKET_ALIGNMENT + * - tp_frame_nr must be exactly frames_per_block*tp_block_nr + * Note that tp_block_size should be chosen to be a power of two + * or there will be a waste of memory. */ + size_t ring_size = 0; + int flag = 0; + + if(io->direction == IO_EGRESS) { + flag = PACKET_RX_RING; + } else { + flag = PACKET_TX_RING; + } + io->req.tp_block_size = sysconf(_SC_PAGESIZE); /* 4096 */ + io->req.tp_frame_size =io->req.tp_block_size/2; /* 2048 */ + io->req.tp_block_nr = slots/2; + io->req.tp_frame_nr = slots; + + ring_size = io->req.tp_block_nr * io->req.tp_block_size; + + LOG(DEBUG, "Setup %d byte packet_mmap ringbuffer (%d slots) for interface %s\n", + ring_size, slots, io->interface->name); + if (setsockopt(io->fd, SOL_PACKET, flag, &io->req, sizeof(struct tpacket_req)) == -1) { + LOG(ERROR, "Allocating ringbuffer error for interface %s - %s (%d)\n", + io->interface->name, strerror(errno), errno); + return NULL; + } + return mmap(0, ring_size, PROT_READ|PROT_WRITE, MAP_SHARED, io->fd, 0); +} + + +bool +io_socket_open(io_handle_s *io) { + + bbl_interface_s *interface = io->interface; + + assert(io->mode == IO_MODE_PACKET_MMAP || io->mode == IO_MODE_RAW); + + int protocol = 0; + if(io->direction == IO_INGRESS) { + protocol = htobe16(ETH_P_ALL); + } + + /* Open RAW socket for all ethertypes. + * https://man7.org/linux/man-pages/man7/packet.7.html */ + io->fd = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK, protocol); + if (io->fd == -1) { + LOG(ERROR, "Failed to open socket for interface %s - %s (%d)\n", + io->interface->name, strerror(errno), errno); + return false; + } + /* Limit socket to the given interface index. */ + io->addr.sll_family = PF_PACKET; + io->addr.sll_ifindex = io->interface->ifindex; + io->addr.sll_protocol = protocol; + if (bind(io->fd, (struct sockaddr*)&io->addr, sizeof(io->addr)) == -1) { + LOG(ERROR, "Failed to bind socket for interface %s - %s (%d)\n", + io->interface->name, strerror(errno), errno); + return false; + } + if(io->direction == IO_EGRESS && interface->config->qdisc_bypass) { + if(!set_qdisc_bypass(io)) { + return false; + } + } + if(io->mode == IO_MODE_PACKET_MMAP) { + if(!set_packet_version(io, TPACKET_V2)) { + return false; + } + if(!set_ring(io, interface->config->io_slots)) { + return false; + } + if(!set_fanout(io)) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/code/bngblaster/src/io/io_socket.h b/code/bngblaster/src/io/io_socket.h new file mode 100644 index 00000000..24435f24 --- /dev/null +++ b/code/bngblaster/src/io/io_socket.h @@ -0,0 +1,15 @@ +/* + * BNG Blaster (BBL) - IO Linux Socket Functions + * + * Christian Giese, August 2022 + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __BBL_IO_SOCKET_H__ +#define __BBL_IO_SOCKET_H__ + +bool +io_socket_open(io_handle_s *io); + +#endif \ No newline at end of file diff --git a/code/bngblaster/src/io/io_thread.c b/code/bngblaster/src/io/io_thread.c new file mode 100644 index 00000000..3d9d1c61 --- /dev/null +++ b/code/bngblaster/src/io/io_thread.c @@ -0,0 +1,192 @@ +/* + * BNG Blaster (BBL) - IO RX Threads + * + * Christian Giese, July 2022 + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "io.h" + +void +io_thread_rx_job(timer_s *timer) +{ + io_handle_s *io = timer->data; + bbl_txq_s *txq = io->txq; + bbl_txq_slot_t *slot; + bbl_interface_s *interface = io->interface; + + bbl_ethernet_header_t *eth; + uint16_t vlan; + + protocol_error_t decode_result; + bool pcap = false; + + while(slot = bbl_txq_read_slot(txq)) { + decode_result = decode_ethernet(slot->packet, slot->packet_len, interface->io.sp, SCRATCHPAD_LEN, ð); + if(decode_result == PROTOCOL_SUCCESS) { + vlan = slot->vlan_tci & ETH_VLAN_ID_MAX; + if(vlan && eth->vlan_outer != vlan) { + /* Restore outer VLAN */ + eth->vlan_inner = eth->vlan_outer; + eth->vlan_inner_priority = eth->vlan_outer_priority; + eth->vlan_outer = vlan; + eth->vlan_outer_priority = slot->vlan_tci >> 13; + if(slot->vlan_tpid == ETH_TYPE_QINQ) { + eth->qinq = true; + } + } + /* Copy RX timestamp */ + eth->timestamp.tv_sec = slot->timestamp.tv_sec; + eth->timestamp.tv_nsec = slot->timestamp.tv_nsec; + bbl_rx_handler(interface, eth); + } else if (decode_result == UNKNOWN_PROTOCOL) { + interface->stats.unknown++; + } else { + interface->stats.decode_error++; + } + /* Dump the packet into pcap file. */ + if (g_ctx->pcap.write_buf && (interface->io.ctrl || g_ctx->pcap.include_streams)) { + pcap = true; + pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len, + interface->pcap_index, PCAPNG_EPB_FLAGS_INBOUND); + } + bbl_txq_read_next(txq); + } + if(pcap) { + pcapng_fflush(); + } +} + +void +io_thread_tx_job(timer_s *timer) +{ + io_handle_s *io = timer->data; + bbl_txq_s *txq = io->txq; + bbl_txq_slot_t *slot; + bbl_interface_s *interface = io->interface; + + protocol_error_t tx_result = IGNORED; + + bool pcap = false; + + /* Get TX timestamp */ + struct timespec timestamp; + clock_gettime(CLOCK_MONOTONIC, ×tamp); + + while(slot = bbl_txq_write_slot(txq)) { + tx_result = bbl_tx(interface, slot->packet, &slot->packet_len); + if(tx_result == PROTOCOL_SUCCESS) { + interface->stats.packets_tx++; + interface->stats.bytes_tx += slot->packet_len; + /* Dump the packet into pcap file. */ + if(g_ctx->pcap.write_buf && (interface->io.ctrl || g_ctx->pcap.include_streams)) { + pcap = true; + pcapng_push_packet_header(×tamp, slot->packet, slot->packet_len, + interface->pcap_index, PCAPNG_EPB_FLAGS_OUTBOUND); + } + bbl_txq_write_next(txq); + } else if(tx_result == EMPTY) { + break; + } + } + if(pcap) { + pcapng_fflush(); + } +} + +void * +io_thread_timer(void *thread_data) +{ + io_thread_s *thread = thread_data; + pthread_mutex_lock(&thread->mutex); + timer_smear_all_buckets(&thread->timer_root); + pthread_mutex_unlock(&thread->mutex); + while(thread->active) { + timer_walk(&thread->timer_root); + } + thread->stopped = true; + return NULL; +} + +bool +io_thread_init(io_handle_s *io) +{ + bbl_interface_s *interface = io->interface; + bbl_link_config_s *config = interface->config; + io_thread_s *thread; + + uint16_t slots = config->io_slots; + + thread = calloc(1, sizeof(io_thread_s)); + thread->next = g_ctx->io_threads; + g_ctx->io_threads = thread; + + io->thread = thread; + io->fanout_id = interface->ifindex; + io->fanout_type = PACKET_FANOUT_HASH; + + + /* Init thread mutex */ + if (pthread_mutex_init(&thread->mutex, NULL) != 0) { + LOG_NOARG(ERROR, "Failed to init mutex\n"); + return false; + } + + /* Init thread timer root */ + timer_init_root(&thread->timer_root); + + if(io->direction == IO_INGRESS) { + + if(slots < (UINT16_MAX/2)) { + slots = slots*2; + } + + + /** Start job reading from RX thread TXQ. */ + timer_add_periodic(&g_ctx->timer_root, &thread->main_rx_job, "RX", + 0, config->rx_interval, + io, &io_thread_rx_job); + } else if(!interface->tx_job) { + /** Start job writing to first TX thread TXQ */ + timer_add_periodic(&g_ctx->timer_root, &interface->rx_job, "TX", + 0, config->rx_interval, + io, &io_thread_tx_job); + } + + /* Init TXQ */ + io->txq = calloc(1, sizeof(bbl_txq_s)); + if(!(io->txq && bbl_txq_init(io->txq, slots))) { + return false; + } + + return true; +} + +void +io_thread_start_all() +{ + io_thread_s *thread = g_ctx->io_threads; + while(thread) { + pthread_create(&thread->thread, NULL, thread->start_fn, (void *)thread); + thread = thread->next; + } +} + +void +io_thread_stop_all() +{ + io_thread_s *thread = g_ctx->io_threads; + while(thread) { + pthread_mutex_lock(&thread->mutex); + thread->active = false; + pthread_mutex_unlock(&thread->mutex); + thread = thread->next; + + } + /* Wait for threads to be stopped */ + thread = g_ctx->io_threads; + while(thread) { + pthread_join(thread->thread, NULL); + } +} \ No newline at end of file diff --git a/code/bngblaster/src/io/io_thread.h b/code/bngblaster/src/io/io_thread.h new file mode 100644 index 00000000..3255df54 --- /dev/null +++ b/code/bngblaster/src/io/io_thread.h @@ -0,0 +1,21 @@ +/* + * BNG Blaster (BBL) - IO RX Threads + * + * Christian Giese, July 2022 + * + * Copyright (C) 2020-2022, RtBrick, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef __BBL_IO_THREAD_H__ +#define __BBL_IO_THREAD_H__ + +bool +io_thread_init(io_handle_s *io); + +void +io_thread_start_all(); + +void +io_thread_stop_all(); + +#endif \ No newline at end of file diff --git a/code/common/src/common.h b/code/common/src/common.h index db9b7a76..6595bdc8 100644 --- a/code/common/src/common.h +++ b/code/common/src/common.h @@ -36,10 +36,22 @@ #define ISO_STR_LEN 64 #define SUB_STR_LEN 256 +#define CACHE_LINE_SIZE 64 + /* Macro Definitions */ +#define UNUSED(x) (void)x + +#ifndef likely +#define likely(x) __builtin_expect((x),1) +#endif /* likely */ + +#ifndef unlikely +#define unlikely(x) __builtin_expect((x),0) +#endif /* unlikely */ + +#define POWEROF2(x) ((((x)-1) & (x)) == 0) /* true if x is a power of 2 */ #define BITS_TO_BYTES(_len) ((_len+7) >> 3) -#define UNUSED(x) (void)x /* Key-Value structure. */ typedef struct keyval_ {