mirror of
https://github.com/rtbrick/bngblaster.git
synced 2024-05-06 15:54:57 +00:00
BNG Blaster 0.8 WIP - 2
This commit is contained in:
+1
-1
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -45,14 +45,6 @@
|
||||
#include "lwip/api.h"
|
||||
#endif
|
||||
|
||||
/* Experimental NETMAP Support */
|
||||
#ifdef BNGBLASTER_NETMAP
|
||||
#define LIBNETMAP_NOTHREADSAFE
|
||||
#include <net/netmap.h>
|
||||
#define NETMAP_WITH_LIBS
|
||||
#include <net/netmap_user.h>
|
||||
#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,
|
||||
|
||||
@@ -25,7 +25,7 @@ bbl_interface_rate_job(timer_s *timer) {
|
||||
* @brief This functions locks the interface
|
||||
* creating the file "/run/lock/bngblaster_<interface>.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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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_ {
|
||||
|
||||
Reference in New Issue
Block a user