Fix N:1 VLAN Support

This commit is contained in:
Christian Giese
2021-03-22 10:49:09 +01:00
parent b2f98e3ff2
commit 5a67810758
11 changed files with 473 additions and 102 deletions
+101 -46
View File
@@ -20,9 +20,10 @@
#include "bbl_interactive.h"
#include "bbl_ctrl.h"
#include "bbl_logging.h"
#include "bbl_packet_mmap.h"
#include "bbl_io_packet_mmap.h"
#include "bbl_io_raw.h"
#ifdef BNGBLASTER_NETMAP
#include "bbl_netmap.h"
#include "bbl_io_netmap.h"
#endif
/* Global Variables */
@@ -174,7 +175,6 @@ bbl_add_interface (bbl_ctx_s *ctx, char *interface_name, int slots)
{
bbl_interface_s *interface;
struct ifreq ifr;
bool result;
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
@@ -188,7 +188,6 @@ bbl_add_interface (bbl_ctx_s *ctx, char *interface_name, int slots)
interface->ctx = ctx;
CIRCLEQ_INSERT_TAIL(&ctx->interface_qhead, interface, interface_qnode);
interface->ifindex = ctx->ifindex++;
interface->pcap_index = ctx->pcap.index;
ctx->pcap.index++;
@@ -209,28 +208,54 @@ bbl_add_interface (bbl_ctx_s *ctx, char *interface_name, int slots)
return NULL;
}
memcpy(&interface->mac, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN);
LOG(NORMAL, "Getting MAC address %s for interface %s\n",
format_mac_address(interface->mac), interface->name);
/*
* Obtain the interface index.
*/
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);
return false;
}
interface->ifindex = ifr.ifr_ifindex;
/* The BNG Blaster supports multiple IO modes where packet_mmap is
* selected per default. */
switch (ctx->config.io_mode) {
case IO_MODE_PACKET_MMAP:
LOG(NORMAL, "Add packet_mmap interface %s\n", interface->name);
result = bbl_packet_mmap_add_interface(ctx, interface, slots);
if(bbl_io_packet_mmap_add_interface(ctx, interface, slots)) {
LOG(NORMAL, "Interface %s added (mode: packet_mmap, index: %u mac: %s)\n",
interface->name, interface->ifindex, format_mac_address(interface->mac));
} else {
LOG(ERROR, "Failed to add packet_mmap interface %s\n", interface->name);
return NULL;
}
break;
case IO_MODE_RAW:
if(bbl_io_raw_add_interface(ctx, interface, slots)) {
LOG(NORMAL, "Interface %s added (mode: raw, index: %u mac: %s)\n",
interface->name, interface->ifindex, format_mac_address(interface->mac));
} else {
LOG(ERROR, "Failed to add raw socket interface %s\n", interface->name);
return NULL;
}
break;
#ifdef BNGBLASTER_NETMAP
case IO_MODE_NETMAP:
LOG(NORMAL, "Add netmap interface %s\n", interface->name);
result = bbl_netmap_add_interface(ctx, interface, slots);
if(bbl_io_netmap_add_interface(ctx, interface, slots)) {
LOG(NORMAL, "Interface %s added (mode: packet_mmap, index: %u mac: %s)\n",
interface->name, interface->ifindex, format_mac_address(interface->mac));
} else {
LOG(ERROR, "Failed to add netmap interface %s\n", interface->name);
return NULL;
}
break;
#endif
default:
result = false;
break;
}
if(!result) {
LOG(ERROR, "Failed to add interface %s\n", interface->name);
return NULL;
LOG(ERROR, "Failed to add interface %s because of unsupported io-mode\n", interface->name);
return NULL;
}
/*
@@ -257,7 +282,7 @@ bbl_add_access_interfaces (bbl_ctx_s *ctx) {
while(access_config) {
for(i = 0; i < ctx->op.access_if_count; i++) {
if(ctx->op.access_if[i]->name) {
if (strncmp(ctx->op.access_if[i]->name, access_config->interface, IFNAMSIZ) == 0) {
if (strcmp(ctx->op.access_if[i]->name, access_config->interface) == 0) {
/* Interface already added! */
access_config->access_if = ctx->op.access_if[i];
goto Next;
@@ -327,7 +352,7 @@ bbl_print_version (void)
printf(" SHA: %s\n", GIT_SHA);
}
printf("IO Modes: packet_mmap");
printf("IO Modes: packet_mmap (default), raw");
#ifdef BNGBLASTER_NETMAP
printf(", netmap");
#endif
@@ -355,6 +380,9 @@ bbl_init_sessions (bbl_ctx_s *ctx)
bbl_session_s *session;
bbl_access_config_s *access_config;
dict_insert_result result;
vlan_session_key_t key = {0};
uint32_t i = 1; /* BNG Blaster internal session identifier */
char *s;
@@ -377,37 +405,50 @@ bbl_init_sessions (bbl_ctx_s *ctx)
* and outer VLAN's, we loop first over all configurations and
* second over VLAN ranges as per configration. */
while(i <= ctx->config.sessions) {
if(access_config->exhausted) goto Next;
if(access_config->access_outer_vlan == 0) {
/* The outer VLAN is initial 0 */
access_config->access_outer_vlan = access_config->access_outer_vlan_min;
access_config->access_inner_vlan = access_config->access_inner_vlan_min;
} else {
if(ctx->config.iterate_outer_vlan) {
/* Iterate over outer VLAN first and inner VLAN second */
access_config->access_outer_vlan++;
if(access_config->access_outer_vlan > access_config->access_outer_vlan_max) {
access_config->access_outer_vlan = access_config->access_outer_vlan_min;
access_config->access_inner_vlan++;
}
if(access_config->vlan_mode == VLAN_MODE_N1) {
if(access_config->access_outer_vlan_min) {
access_config->access_outer_vlan = access_config->access_outer_vlan_min;
} else {
/* Iterate over inner VLAN first and outer VLAN second (default) */
access_config->access_inner_vlan++;
if(access_config->access_inner_vlan > access_config->access_inner_vlan_max) {
access_config->access_inner_vlan = access_config->access_inner_vlan_min;
access_config->access_outer_vlan = access_config->access_outer_vlan_max;
}
if(access_config->access_inner_vlan_min) {
access_config->access_inner_vlan = access_config->access_inner_vlan_min;
} else {
access_config->access_inner_vlan = access_config->access_inner_vlan_max;
}
} else {
if(access_config->exhausted) goto Next;
if(access_config->access_outer_vlan == 0) {
/* The outer VLAN is initial 0 */
access_config->access_outer_vlan = access_config->access_outer_vlan_min;
access_config->access_inner_vlan = access_config->access_inner_vlan_min;
} else {
if(ctx->config.iterate_outer_vlan) {
/* Iterate over outer VLAN first and inner VLAN second */
access_config->access_outer_vlan++;
if(access_config->access_outer_vlan > access_config->access_outer_vlan_max) {
access_config->access_outer_vlan = access_config->access_outer_vlan_min;
access_config->access_inner_vlan++;
}
} else {
/* Iterate over inner VLAN first and outer VLAN second (default) */
access_config->access_inner_vlan++;
if(access_config->access_inner_vlan > access_config->access_inner_vlan_max) {
access_config->access_inner_vlan = access_config->access_inner_vlan_min;
access_config->access_outer_vlan++;
}
}
}
}
if(access_config->access_outer_vlan == 0) {
/* This is required to handle untagged interafaces */
access_config->exhausted = true;
}
if(access_config->access_outer_vlan > access_config->access_outer_vlan_max ||
access_config->access_inner_vlan > access_config->access_inner_vlan_max) {
/* VLAN range exhausted */
access_config->exhausted = true;
goto Next;
if(access_config->access_outer_vlan == 0) {
/* This is required to handle untagged interafaces */
access_config->exhausted = true;
}
if(access_config->access_outer_vlan > access_config->access_outer_vlan_max ||
access_config->access_inner_vlan > access_config->access_inner_vlan_max) {
/* VLAN range exhausted */
access_config->exhausted = true;
goto Next;
}
}
t++;
access_config->sessions++;
@@ -509,6 +550,20 @@ bbl_init_sessions (bbl_ctx_s *ctx)
}
/* Add session to list */
ctx->session_list[i-1] = session;
if(access_config->vlan_mode == VLAN_MODE_11) {
/* Add 1:1 sessions to VLAN/session dictionary */
key.ifindex = access_config->access_if->ifindex;
key.outer_vlan_id = session->outer_vlan_id;
key.inner_vlan_id = session->inner_vlan_id;
result = dict_insert(ctx->vlan_session_dict, &key);
if (!result.inserted) {
free(session);
return NULL;
}
*result.datum_ptr = session;
}
LOG(DEBUG, "Session %u created (%s.%u:%u)\n", i, access_config->interface, access_config->access_outer_vlan, access_config->access_inner_vlan);
i++;
Next:
+16 -5
View File
@@ -103,7 +103,8 @@ typedef struct bbl_rate_
typedef enum {
IO_MODE_PACKET_MMAP = 0,
IO_MODE_NETMAP
IO_MODE_NETMAP,
IO_MODE_RAW,
} __attribute__ ((__packed__)) bbl_io_mode_t;
typedef enum {
@@ -111,6 +112,10 @@ typedef enum {
ACCESS_TYPE_IPOE
} __attribute__ ((__packed__)) bbl_access_type_t;
typedef enum {
VLAN_MODE_11 = 0, /* VLAN mode 1:1 */
VLAN_MODE_N1 /* VLAN mode N:1 */
} __attribute__ ((__packed__)) bbl_vlan_mode_t;
typedef enum {
IGMP_GROUP_IDLE = 0,
IGMP_GROUP_LEAVING,
@@ -285,9 +290,10 @@ typedef struct bbl_access_config_
uint32_t sessions; /* per access config session counter */
struct bbl_interface_ *access_if;
char interface[IFNAMSIZ];
char *interface;
bbl_access_type_t access_type; /* pppoe or ipoe */
bbl_vlan_mode_t vlan_mode; /* 1:1 (default) or N:1 */
uint16_t access_outer_vlan;
uint16_t access_outer_vlan_min;
@@ -368,7 +374,8 @@ typedef struct bbl_ctx_
CIRCLEQ_HEAD(bbl_ctx__, bbl_interface_ ) interface_qhead; /* list of interfaces */
bbl_session_s **session_list; /* list for sessions */
dict *vlan_session_dict; /* hashtable for 1:1 vlan sessions */
dict *l2tp_session_dict; /* hashtable for L2TP sessions */
dict *li_flow_dict; /* hashtable for LI flows */
@@ -379,8 +386,6 @@ typedef struct bbl_ctx_
int ctrl_socket;
char *ctrl_socket_path;
uint8_t ifindex;
/* Operational state */
struct {
uint8_t access_if_count;
@@ -576,6 +581,12 @@ typedef enum {
BBL_PPP_MAX
} __attribute__ ((__packed__)) ppp_state_t;
typedef struct vlan_session_key_ {
uint32_t ifindex;
uint16_t outer_vlan_id;
uint16_t inner_vlan_id;
} __attribute__ ((__packed__)) vlan_session_key_t;
#define BBL_SESSION_HASHTABLE_SIZE 128993 /* is a prime number */
#define BBL_LI_HASHTABLE_SIZE 32771 /* is a prime number */
+14 -4
View File
@@ -33,8 +33,18 @@ json_parse_access_interface (bbl_ctx_s *ctx, json_t *access_interface, bbl_acces
return false;
}
}
if (json_unpack(access_interface, "{s:s}", "vlan-mode", &s) == 0) {
if (strcmp(s, "1:1") == 0) {
access_config->vlan_mode = VLAN_MODE_11;
} else if (strcmp(s, "N:1") == 0) {
access_config->vlan_mode = VLAN_MODE_N1;
} else {
fprintf(stderr, "JSON config error: Invalid value for access->vlan-mode\n");
return false;
}
}
if (json_unpack(access_interface, "{s:s}", "interface", &s) == 0) {
snprintf(access_config->interface, IFNAMSIZ, "%s", s);
access_config->interface = strdup(s);
} else {
fprintf(stderr, "JSON config error: Missing value for access->interface\n");
return false;
@@ -570,12 +580,12 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) {
ctx->config.qdisc_bypass = json_boolean_value(value);
}
if (json_unpack(section, "{s:s}", "io-mode", &s) == 0) {
if (strcmp(s, "default") == 0) {
ctx->config.io_mode = IO_MODE_PACKET_MMAP;
} else if (strcmp(s, "packet_mmap") == 0) {
if (strcmp(s, "packet_mmap") == 0) {
ctx->config.io_mode = IO_MODE_PACKET_MMAP;
} else if (strcmp(s, "netmap") == 0) {
ctx->config.io_mode = IO_MODE_NETMAP;
} else if (strcmp(s, "raw") == 0) {
ctx->config.io_mode = IO_MODE_RAW;
} else {
fprintf(stderr, "Config error: Invalid value for interfaces->io-mode\n");
return false;
+51
View File
@@ -983,6 +983,10 @@ bbl_ctrl_socket_job (timer_s *timer) {
const char *command = NULL;
uint32_t session_id = 0;
vlan_session_key_t key = {0};
bbl_session_s *session;
void **search;
while(true) {
fd = accept(ctx->ctrl_socket, 0, 0);
if(fd < 0) {
@@ -1024,6 +1028,53 @@ bbl_ctrl_socket_job (timer_s *timer) {
bbl_ctrl_status(fd, "error", 400, "invalid session-id");
goto Close;
}
} else {
/* Deprecated!
* For backward compatibility with version 0.4.X, we still
* support per session commands using VLAN index instead of
* new session-id. */
value = json_object_get(arguments, "ifindex");
if (value) {
if(json_is_number(value)) {
key.ifindex = json_number_value(value);
} else {
bbl_ctrl_status(fd, "error", 400, "invalid ifindex");
goto Close;
}
} else {
/* Use first interface as default. */
if(ctx->op.access_if[0]) {
key.ifindex = ctx->op.access_if[0]->ifindex;
}
}
value = json_object_get(arguments, "outer-vlan");
if (value) {
if(json_is_number(value)) {
key.outer_vlan_id = json_number_value(value);
} else {
bbl_ctrl_status(fd, "error", 400, "invalid outer-vlan");
goto Close;
}
}
value = json_object_get(arguments, "inner-vlan");
if (value) {
if(json_is_number(value)) {
key.inner_vlan_id = json_number_value(value);
} else {
bbl_ctrl_status(fd, "error", 400, "invalid inner-vlan");
goto Close;
}
}
if(key.outer_vlan_id) {
search = dict_search(ctx->vlan_session_dict, &key);
if(search) {
session = *search;
session_id = session->session_id;
} else {
bbl_ctrl_status(fd, "warning", 404, "session not found");
goto Close;
}
}
}
}
for(i = 0; true; i++) {
+1 -3
View File
@@ -11,7 +11,6 @@
extern volatile bool g_teardown;
#if 0
int
bbl_compare_session (void *key1, void *key2)
{
@@ -31,7 +30,6 @@ bbl_session_hash (const void* k)
return hash;
}
#endif
int
bbl_compare_key32 (void *key1, void *key2)
@@ -78,9 +76,9 @@ bbl_ctx_add (void)
CIRCLEQ_INIT(&ctx->interface_qhead);
ctx->flow_id = 1;
ctx->ifindex = 1;
/* Initialize hash table dictionaries. */
ctx->vlan_session_dict = hashtable2_dict_new((dict_compare_func)bbl_compare_session, bbl_session_hash, BBL_SESSION_HASHTABLE_SIZE);
ctx->l2tp_session_dict = hashtable2_dict_new((dict_compare_func)bbl_compare_key32, bbl_key32_hash, BBL_SESSION_HASHTABLE_SIZE);
ctx->li_flow_dict = hashtable2_dict_new((dict_compare_func)bbl_compare_key32, bbl_key32_hash, BBL_LI_HASHTABLE_SIZE);
+11 -11
View File
@@ -9,17 +9,17 @@
#ifdef BNGBLASTER_NETMAP
#include "bbl.h"
#include "bbl_netmap.h"
#include "bbl_io_netmap.h"
#include "bbl_pcap.h"
#include "bbl_rx.h"
#include "bbl_tx.h"
void
bbl_netmap_rx_job (timer_s *timer)
bbl_io_netmap_rx_job (timer_s *timer)
{
bbl_interface_s *interface;
bbl_ctx_s *ctx;
bbl_io_ctx_netmap *io_ctx;
bbl_io_netmap_ctx *io_ctx;
struct netmap_ring *ring;
unsigned int i;
@@ -79,11 +79,11 @@ bbl_netmap_rx_job (timer_s *timer)
}
void
bbl_netmap_tx_job (timer_s *timer)
bbl_io_netmap_tx_job (timer_s *timer)
{
bbl_interface_s *interface;
bbl_ctx_s *ctx;
bbl_io_ctx_netmap *io_ctx;
bbl_io_netmap_ctx *io_ctx;
bool send = false;
struct netmap_ring *ring;
@@ -135,15 +135,15 @@ bbl_netmap_tx_job (timer_s *timer)
}
/**
* bbl_netmap_add_interface
* bbl_io_netmap_add_interface
*
* @param ctx global context
* @param interface interface.
* @param slots ring buffer size (currently not used)
*/
bool
bbl_netmap_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int slots) {
bbl_io_ctx_netmap *io_ctx;
bbl_io_netmap_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int slots) {
bbl_io_netmap_ctx *io_ctx;
char timer_name[128];
char netmap_port[128];
@@ -151,7 +151,7 @@ bbl_netmap_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int slots)
snprintf(netmap_port, sizeof(netmap_port), "netmap:%s", interface->name);
io_ctx = calloc(1, sizeof(bbl_io_ctx_netmap));
io_ctx = calloc(1, sizeof(bbl_io_netmap_ctx));
interface->io_mode = IO_MODE_NETMAP;
interface->io_ctx = io_ctx;
@@ -172,9 +172,9 @@ bbl_netmap_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int slots)
* Add an periodic timer for polling I/O.
*/
snprintf(timer_name, sizeof(timer_name), "%s TX", interface->name);
timer_add_periodic(&ctx->timer_root, &interface->tx_job, timer_name, 0, ctx->config.tx_interval * MSEC, interface, bbl_netmap_tx_job);
timer_add_periodic(&ctx->timer_root, &interface->tx_job, timer_name, 0, ctx->config.tx_interval * MSEC, interface, bbl_io_netmap_tx_job);
snprintf(timer_name, sizeof(timer_name), "%s RX", interface->name);
timer_add_periodic(&ctx->timer_root, &interface->rx_job, timer_name, 0, ctx->config.rx_interval * MSEC, interface, bbl_netmap_rx_job);
timer_add_periodic(&ctx->timer_root, &interface->rx_job, timer_name, 0, ctx->config.rx_interval * MSEC, interface, bbl_io_netmap_rx_job);
return true;
}
+3 -3
View File
@@ -17,12 +17,12 @@
#define NETMAP_WITH_LIBS
#include <net/netmap_user.h>
typedef struct bbl_io_ctx_netmap_
typedef struct bbl_io_netmap_ctx_
{
struct nm_desc *port;
} bbl_io_ctx_netmap;
} bbl_io_netmap_ctx;
bool
bbl_netmap_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int slots);
bbl_io_netmap_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int slots);
#endif
@@ -7,17 +7,17 @@
*/
#include "bbl.h"
#include "bbl_packet_mmap.h"
#include "bbl_io_packet_mmap.h"
#include "bbl_pcap.h"
#include "bbl_rx.h"
#include "bbl_tx.h"
void
bbl_packet_mmap_rx_job (timer_s *timer)
bbl_io_packet_mmap_rx_job (timer_s *timer)
{
bbl_interface_s *interface;
bbl_ctx_s *ctx;
bbl_io_ctx_packet_mmap *io_ctx;
bbl_io_packet_mmap_ctx *io_ctx;
struct pollfd fds[1] = {0};
uint8_t *frame_ptr;
@@ -96,11 +96,11 @@ bbl_packet_mmap_rx_job (timer_s *timer)
}
void
bbl_packet_mmap_tx_job (timer_s *timer)
bbl_io_packet_mmap_tx_job (timer_s *timer)
{
bbl_interface_s *interface;
bbl_ctx_s *ctx;
bbl_io_ctx_packet_mmap *io_ctx;
bbl_io_packet_mmap_ctx *io_ctx;
struct tpacket2_hdr* tphdr;
struct pollfd fds[1] = {0};
@@ -173,21 +173,21 @@ bbl_packet_mmap_tx_job (timer_s *timer)
}
/**
* bbl_packet_mmap_add_interface
* bbl_io_packet_mmap_add_interface
*
* @param ctx global context
* @param interface interface.
* @param slots ring buffer size
*/
bool
bbl_packet_mmap_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int slots) {
bbl_io_ctx_packet_mmap *io_ctx;
bbl_io_packet_mmap_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int slots) {
bbl_io_packet_mmap_ctx *io_ctx;
size_t ring_size;
char timer_name[32];
struct ifreq ifr;
int version, qdisc_bypass;
io_ctx = calloc(1, sizeof(bbl_io_ctx_packet_mmap));
io_ctx = calloc(1, sizeof(bbl_io_packet_mmap_ctx));
interface->io_mode = IO_MODE_PACKET_MMAP;
interface->io_ctx = io_ctx;
@@ -221,26 +221,17 @@ bbl_packet_mmap_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int sl
}
/*
* Limit packet capture to a given interface.
* Obtain the interface index and bind the socket to the interface.
* Limit socket to the given interface index.
*/
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface->name);
if (ioctl(io_ctx->fd_tx, SIOCGIFINDEX, &ifr) == -1) {
LOG(ERROR, "Get interface index error %s (%d) for interface %s\n",
strerror(errno), errno, interface->name);
return false;
}
io_ctx->addr.sll_family = AF_PACKET;
io_ctx->addr.sll_ifindex = ifr.ifr_ifindex;
io_ctx->addr.sll_protocol = htobe16(ETH_P_ALL);
io_ctx->addr.sll_family = PF_PACKET;
io_ctx->addr.sll_ifindex = interface->ifindex;
io_ctx->addr.sll_protocol = 0;
if (bind(io_ctx->fd_tx, (struct sockaddr*)&io_ctx->addr, sizeof(io_ctx->addr)) == -1) {
LOG(ERROR, "bind() TX error %s (%d) for interface %s\n",
strerror(errno), errno, interface->name);
return false;
}
io_ctx->addr.sll_protocol = htobe16(ETH_P_ALL);
if (bind(io_ctx->fd_rx, (struct sockaddr*)&io_ctx->addr, sizeof(io_ctx->addr)) == -1) {
LOG(ERROR, "bind() RX error %s (%d) for interface %s\n",
strerror(errno), errno, interface->name);
@@ -333,9 +324,9 @@ bbl_packet_mmap_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int sl
* Add an periodic timer for polling I/O.
*/
snprintf(timer_name, sizeof(timer_name), "%s TX", interface->name);
timer_add_periodic(&ctx->timer_root, &interface->tx_job, timer_name, 0, ctx->config.tx_interval * MSEC, interface, bbl_packet_mmap_tx_job);
timer_add_periodic(&ctx->timer_root, &interface->tx_job, timer_name, 0, ctx->config.tx_interval * MSEC, interface, bbl_io_packet_mmap_tx_job);
snprintf(timer_name, sizeof(timer_name), "%s RX", interface->name);
timer_add_periodic(&ctx->timer_root, &interface->rx_job, timer_name, 0, ctx->config.rx_interval * MSEC, interface, bbl_packet_mmap_rx_job);
timer_add_periodic(&ctx->timer_root, &interface->rx_job, timer_name, 0, ctx->config.rx_interval * MSEC, interface, bbl_io_packet_mmap_rx_job);
return true;
}
@@ -15,10 +15,10 @@
* Copyright (C) 2020-2021, RtBrick, Inc.
*/
#ifndef __BBL_PACKET_MMAP_H__
#define __BBL_PACKET_MMAP_H__
#ifndef __BBL_IO_PACKET_MMAP_H__
#define __BBL_IO_PACKET_MMAP_H__
typedef struct bbl_io_ctx_packet_mmap_
typedef struct bbl_io_packet_mmap_ctx_
{
int fd_tx;
int fd_rx;
@@ -32,9 +32,9 @@ typedef struct bbl_io_ctx_packet_mmap_
uint16_t cursor_tx; /* slot # inside the ringbuffer */
uint16_t cursor_rx; /* slot # inside the ringbuffer */
} bbl_io_ctx_packet_mmap;
} bbl_io_packet_mmap_ctx;
bool
bbl_packet_mmap_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int slots);
bbl_io_packet_mmap_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int slots);
#endif
+230
View File
@@ -0,0 +1,230 @@
/*
* BNG Blaster (BBL) - RAW Sockets
*
* Christian Giese, October 2021
*
* Copyright (C) 2020-2021, RtBrick, Inc.
*/
#include "bbl.h"
#include "bbl_io_raw.h"
#include "bbl_pcap.h"
#include "bbl_rx.h"
#include "bbl_tx.h"
void
bbl_io_raw_rx_job (timer_s *timer)
{
bbl_interface_s *interface;
bbl_ctx_s *ctx;
bbl_io_raw_ctx *io_ctx;
struct sockaddr saddr;
int saddr_size = sizeof(saddr);
int rx_len;
uint8_t *eth_start;
uint eth_len;
bbl_ethernet_header_t *eth;
protocol_error_t decode_result;
interface = timer->data;
if (!interface) {
return;
}
ctx = interface->ctx;
io_ctx = interface->io_ctx;
/* Get RX timestamp */
clock_gettime(CLOCK_REALTIME, &interface->rx_timestamp);
while (true) {
rx_len = recvfrom(io_ctx->fd_rx, io_ctx->buf, SCRATCHPAD_LEN , 0, &saddr , (socklen_t*)&saddr_size);
if(rx_len < 14) {
break;
}
interface->stats.packets_rx++;
eth_start = io_ctx->buf;
eth_len = rx_len;
/*
* Dump the packet into pcap file.
*/
if (ctx->pcap.write_buf) {
pcapng_push_packet_header(ctx, &interface->rx_timestamp, eth_start, eth_len,
interface->pcap_index, PCAPNG_EPB_FLAGS_INBOUND);
}
decode_result = decode_ethernet(eth_start, eth_len, interface->ctx->sp_rx, SCRATCHPAD_LEN, &eth);
if(decode_result == PROTOCOL_SUCCESS) {
/* Copy RX timestamp */
eth->rx_sec = interface->rx_timestamp.tv_sec;
eth->rx_nsec = interface->rx_timestamp.tv_nsec;
if(interface->access) {
bbl_rx_handler_access(eth, interface);
} else {
bbl_rx_handler_network(eth, interface);
}
} else if (decode_result == UNKNOWN_PROTOCOL) {
interface->stats.packets_rx_drop_unknown++;
} else {
interface->stats.packets_rx_drop_decode_error++;
}
}
pcapng_fflush(ctx);
}
void
bbl_io_raw_tx_job (timer_s *timer)
{
bbl_interface_s *interface;
bbl_ctx_s *ctx;
bbl_io_raw_ctx *io_ctx;
uint16_t len;
protocol_error_t tx_result = IGNORED;
interface = timer->data;
if (!interface) {
return;
}
ctx = interface->ctx;
io_ctx = interface->io_ctx;
/* Get TX timestamp */
clock_gettime(CLOCK_REALTIME, &interface->tx_timestamp);
while(tx_result != EMPTY) {
tx_result = bbl_tx(ctx, interface, io_ctx->buf, &len);
if (tx_result == PROTOCOL_SUCCESS) {
if (sendto(io_ctx->fd_rx, io_ctx->buf, len, 0, (struct sockaddr*)&io_ctx->addr, sizeof(struct sockaddr_ll)) <0 ) {
LOG(IO, "Sendto failed with errno: %i\n", errno);
interface->stats.sendto_failed++;
return;
}
interface->stats.packets_tx++;
/* Dump the packet into pcap file. */
if (ctx->pcap.write_buf) {
pcapng_push_packet_header(ctx, &interface->tx_timestamp,
io_ctx->buf, len, interface->pcap_index,
PCAPNG_EPB_FLAGS_OUTBOUND);
}
}
}
pcapng_fflush(ctx);
}
/**
* bbl_io_raw_add_interface
*
* @param ctx global context
* @param interface interface.
* @param slots ring buffer size
*/
bool
bbl_io_raw_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int slots) {
bbl_io_raw_ctx *io_ctx;
char timer_name[32];
struct ifreq ifr;
int qdisc_bypass;
io_ctx = calloc(1, sizeof(bbl_io_raw_ctx));
io_ctx->buf = malloc(BBL_IO_RAW_BUFFER_LEN);
interface->io_mode = IO_MODE_RAW;
interface->io_ctx = io_ctx;
UNUSED(slots);
/*
* Open RAW socket for all Ethertypes.
* https://man7.org/linux/man-pages/man7/packet.7.html
*/
io_ctx->fd_tx = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, 0);
if (io_ctx->fd_tx == -1) {
LOG(ERROR, "socket() TX error %s (%d) for interface %s\n", strerror(errno), errno, interface->name);
return false;
}
io_ctx->fd_rx = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, htobe16(ETH_P_ALL));
if (io_ctx->fd_rx == -1) {
LOG(ERROR, "socket() RX error %s (%d) for interface %s\n", strerror(errno), errno, interface->name);
return false;
}
/*
* Limit socket to the given interface index.
*/
io_ctx->addr.sll_family = AF_PACKET;
io_ctx->addr.sll_ifindex = interface->ifindex;
io_ctx->addr.sll_protocol = 0;
if (bind(io_ctx->fd_tx, (struct sockaddr*)&io_ctx->addr, sizeof(io_ctx->addr)) == -1) {
LOG(ERROR, "bind() TX error %s (%d) for interface %s\n",
strerror(errno), errno, interface->name);
return false;
}
io_ctx->addr.sll_protocol = htobe16(ETH_P_ALL);
if (bind(io_ctx->fd_rx, (struct sockaddr*)&io_ctx->addr, sizeof(io_ctx->addr)) == -1) {
LOG(ERROR, "bind() RX error %s (%d) for interface %s\n",
strerror(errno), errno, interface->name);
return false;
}
/*
* Set the interface to promiscuous mode. Only for the RX FD.
*/
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface->name);
if (ioctl(io_ctx->fd_rx, SIOCGIFFLAGS, &ifr) == -1) {
LOG(ERROR, "Getting socket flags error %s (%d) when setting promiscuous mode for interface %s\n",
strerror(errno), errno, interface->name);
return false;
}
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl(io_ctx->fd_rx, SIOCSIFFLAGS, ifr) == -1){
LOG(ERROR, "Setting socket flags error %s (%d) when setting promiscuous mode for interface %s\n",
strerror(errno), errno, interface->name);
return false;
}
/*
* Bypass TC_QDISC, such that the kernel is hammered 30% less with processing packets. Only for the TX FD.
*
* 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.
*/
if(ctx->config.qdisc_bypass) {
qdisc_bypass = 1;
if (setsockopt(io_ctx->fd_tx, SOL_PACKET, PACKET_QDISC_BYPASS, &qdisc_bypass, sizeof(qdisc_bypass)) == -1) {
LOG(ERROR, "Setting qdisc bypass error %s (%d) for interface %s\n", strerror(errno), errno, interface->name);
return false;
}
}
struct timeval read_timeout;
read_timeout.tv_sec = 0;
read_timeout.tv_usec = 10;
setsockopt(io_ctx->fd_rx, SOL_SOCKET, SO_RCVTIMEO, &read_timeout, sizeof(read_timeout));
/*
* Add an periodic timer for polling I/O.
*/
snprintf(timer_name, sizeof(timer_name), "%s TX", interface->name);
timer_add_periodic(&ctx->timer_root, &interface->tx_job, timer_name, 0, ctx->config.tx_interval * MSEC, interface, bbl_io_raw_tx_job);
snprintf(timer_name, sizeof(timer_name), "%s RX", interface->name);
timer_add_periodic(&ctx->timer_root, &interface->rx_job, timer_name, 0, ctx->config.rx_interval * MSEC, interface, bbl_io_raw_rx_job);
return true;
}
+25
View File
@@ -0,0 +1,25 @@
/*
* BNG Blaster (BBL) - RAW Sockets
*
* Christian Giese, October 2021
*
* Copyright (C) 2020-2021, RtBrick, Inc.
*/
#ifndef __BBL_IO_RAW_H__
#define __BBL_IO_RAW_H__
#define BBL_IO_RAW_BUFFER_LEN 2048
typedef struct bbl_io_raw_ctx_
{
int fd_tx;
int fd_rx;
struct sockaddr_ll addr;
uint8_t *buf;
} bbl_io_raw_ctx;
bool
bbl_io_raw_add_interface(bbl_ctx_s *ctx, bbl_interface_s *interface, int slots);
#endif