mirror of
https://github.com/rtbrick/bngblaster.git
synced 2024-05-06 15:54:57 +00:00
Fix N:1 VLAN Support
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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, ð);
|
||||
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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user