From 5a67810758c33dbd0b0e4fbb59104a28fb103e07 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Fri, 19 Mar 2021 18:13:06 +0100 Subject: [PATCH] Fix N:1 VLAN Support --- src/bbl.c | 147 +++++++---- src/bbl.h | 21 +- src/bbl_config.c | 18 +- src/bbl_ctrl.c | 51 ++++ src/bbl_ctx.c | 4 +- src/{bbl_netmap.c => bbl_io_netmap.c} | 22 +- src/{bbl_netmap.h => bbl_io_netmap.h} | 6 +- ...bbl_packet_mmap.c => bbl_io_packet_mmap.c} | 41 ++-- ...bbl_packet_mmap.h => bbl_io_packet_mmap.h} | 10 +- src/bbl_io_raw.c | 230 ++++++++++++++++++ src/bbl_io_raw.h | 25 ++ 11 files changed, 473 insertions(+), 102 deletions(-) rename src/{bbl_netmap.c => bbl_io_netmap.c} (89%) rename src/{bbl_netmap.h => bbl_io_netmap.h} (74%) rename src/{bbl_packet_mmap.c => bbl_io_packet_mmap.c} (91%) rename src/{bbl_packet_mmap.h => bbl_io_packet_mmap.h} (81%) create mode 100644 src/bbl_io_raw.c create mode 100644 src/bbl_io_raw.h diff --git a/src/bbl.c b/src/bbl.c index f5ba6a0a..499864ad 100644 --- a/src/bbl.c +++ b/src/bbl.c @@ -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: diff --git a/src/bbl.h b/src/bbl.h index b0f3b2bc..1c02f66a 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -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 */ diff --git a/src/bbl_config.c b/src/bbl_config.c index c882c060..a5e62f11 100644 --- a/src/bbl_config.c +++ b/src/bbl_config.c @@ -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; diff --git a/src/bbl_ctrl.c b/src/bbl_ctrl.c index 2606cbbc..6a1c5015 100644 --- a/src/bbl_ctrl.c +++ b/src/bbl_ctrl.c @@ -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++) { diff --git a/src/bbl_ctx.c b/src/bbl_ctx.c index bac1b01e..94493beb 100644 --- a/src/bbl_ctx.c +++ b/src/bbl_ctx.c @@ -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); diff --git a/src/bbl_netmap.c b/src/bbl_io_netmap.c similarity index 89% rename from src/bbl_netmap.c rename to src/bbl_io_netmap.c index ba272b4c..c765769c 100644 --- a/src/bbl_netmap.c +++ b/src/bbl_io_netmap.c @@ -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; } diff --git a/src/bbl_netmap.h b/src/bbl_io_netmap.h similarity index 74% rename from src/bbl_netmap.h rename to src/bbl_io_netmap.h index 0c1c9ff1..51282a3d 100644 --- a/src/bbl_netmap.h +++ b/src/bbl_io_netmap.h @@ -17,12 +17,12 @@ #define NETMAP_WITH_LIBS #include -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 \ No newline at end of file diff --git a/src/bbl_packet_mmap.c b/src/bbl_io_packet_mmap.c similarity index 91% rename from src/bbl_packet_mmap.c rename to src/bbl_io_packet_mmap.c index 265958ad..df692eb4 100644 --- a/src/bbl_packet_mmap.c +++ b/src/bbl_io_packet_mmap.c @@ -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; } \ No newline at end of file diff --git a/src/bbl_packet_mmap.h b/src/bbl_io_packet_mmap.h similarity index 81% rename from src/bbl_packet_mmap.h rename to src/bbl_io_packet_mmap.h index 801b3754..034ba483 100644 --- a/src/bbl_packet_mmap.h +++ b/src/bbl_io_packet_mmap.h @@ -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 \ No newline at end of file diff --git a/src/bbl_io_raw.c b/src/bbl_io_raw.c new file mode 100644 index 00000000..5d0cb4e7 --- /dev/null +++ b/src/bbl_io_raw.c @@ -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; +} \ No newline at end of file diff --git a/src/bbl_io_raw.h b/src/bbl_io_raw.h new file mode 100644 index 00000000..b00e7a0c --- /dev/null +++ b/src/bbl_io_raw.h @@ -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 \ No newline at end of file