Skip to content

Commit

Permalink
fragmentation-reassembly
Browse files Browse the repository at this point in the history
  • Loading branch information
GIC-de committed Oct 10, 2024
1 parent 1e24846 commit 33a21f8
Show file tree
Hide file tree
Showing 13 changed files with 268 additions and 6 deletions.
2 changes: 2 additions & 0 deletions code/bngblaster/src/bbl.c
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,8 @@ main(int argc, char *argv[])
if(igmp_group_count) g_ctx->config.igmp_group_count = atoi(igmp_group_count);
if(igmp_zap_interval) g_ctx->config.igmp_zap_interval = atoi(igmp_zap_interval);

bbl_fragment_init();

#ifdef BNGBLASTER_DPDK
/* Init DPDK. */
if(!io_dpdk_init()) {
Expand Down
1 change: 1 addition & 0 deletions code/bngblaster/src/bbl.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "bbl_tcp.h"
#include "bbl_http_client.h"
#include "bbl_http_server.h"
#include "bbl_fragment.h"

#include "io/io.h"
#include "bgp/bgp.h"
Expand Down
2 changes: 1 addition & 1 deletion code/bngblaster/src/bbl_access.c
Original file line number Diff line number Diff line change
Expand Up @@ -631,11 +631,11 @@ bbl_access_rx_ipv4(bbl_access_interface_s *interface,
bbl_udp_s *udp;

if(ipv4->offset & ~IPV4_DF) {
/* Reassembling of fragmented IPv4 packets is currently not supported. */
session->stats.accounting_packets_rx++;
session->stats.accounting_bytes_rx += eth->length;
session->stats.ipv4_fragmented_rx++;
interface->stats.ipv4_fragmented_rx++;
bbl_fragment_rx(interface, NULL, eth, ipv4);
return;
}

Expand Down
5 changes: 5 additions & 0 deletions code/bngblaster/src/bbl_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -3499,6 +3499,7 @@ json_parse_config(json_t *root)
"stream-rate-calculation",
"stream-delay-calculation",
"stream-burst-ms",
"reassemble-fragments",
"multicast-autostart",
"udp-checksum"
};
Expand Down Expand Up @@ -3531,6 +3532,10 @@ json_parse_config(json_t *root)
if(value) {
g_ctx->config.stream_burst_ms = json_number_value(value) * MSEC;
}
JSON_OBJ_GET_BOOL(section, value, "traffic", "reassemble-fragments");
if(value) {
g_ctx->config.traffic_reassemble_fragments = json_boolean_value(value);
}
JSON_OBJ_GET_BOOL(section, value, "traffic", "multicast-autostart");
if(value) {
g_ctx->config.multicast_traffic_autostart = json_boolean_value(value);
Expand Down
4 changes: 4 additions & 0 deletions code/bngblaster/src/bbl_ctx.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ typedef struct bbl_ctx_
struct timer_ *keyboard_timer;

struct timer_ *tcp_timer;
struct timer_ *fragmentation_timer;

struct timespec timestamp_start;
struct timespec timestamp_stop;
Expand Down Expand Up @@ -93,6 +94,8 @@ typedef struct bbl_ctx_
ldp_instance_s *ldp_instances;
ldp_raw_update_s *ldp_raw_updates;

bbl_fragment_s *ipv4_fragments;

/* Scratchpad memory */
uint8_t *sp;

Expand Down Expand Up @@ -328,6 +331,7 @@ typedef struct bbl_ctx_
/* Global Traffic */
bool traffic_autostart;
bool traffic_stop_verified;
bool traffic_reassemble_fragments;

/* Stream Traffic */
bool stream_autostart;
Expand Down
1 change: 1 addition & 0 deletions code/bngblaster/src/bbl_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,5 +244,6 @@ typedef struct bbl_http_client_ bbl_http_client_s;
typedef struct bbl_http_server_config_ bbl_http_server_config_s;
typedef struct bbl_http_server_ bbl_http_server_s;
typedef struct bbl_http_server_connection_ bbl_http_server_connection_s;
typedef struct bbl_fragment_ bbl_fragment_s;

#endif
188 changes: 188 additions & 0 deletions code/bngblaster/src/bbl_fragment.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
* BNG Blaster (BBL) - IP Fragmentation
*
* Christian Giese, October 2024
*
* Copyright (C) 2020-2024, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "bbl.h"

static void
bbl_fragment_free(bbl_fragment_s *ipv4_fragment)
{
if(ipv4_fragment->next) {
ipv4_fragment->next->prev = ipv4_fragment->prev;
}
if(ipv4_fragment->prev) {
ipv4_fragment->prev->next = ipv4_fragment->next;
} else {
g_ctx->ipv4_fragments = ipv4_fragment->next;
}
free(ipv4_fragment);
}

/**
* bbl_fragment_rx
*
* This function stores incoming IPv4 fragments in fragmentation buffers,
* which are organized as a doubly-linked list. A cleanup job periodically
* removes outdated buffers. Once all fragments of a single packet have been received,
* the packet is reassembled and processed.
*
* Currently, this function supports BBL stream traffic only!
*
* @param access_interface pointer to access interface on which packet was received
* @param network_interface pointer to network interface on which packet was received
* @param eth pointer to ethernet header structure of received packet
* @param ipv4 pointer to IPv4 header structure of received packet
*/
void
bbl_fragment_rx(bbl_access_interface_s *access_interface,
bbl_network_interface_s *network_interface,
bbl_ethernet_header_s *eth, bbl_ipv4_s *ipv4)
{
if(!g_ctx->config.traffic_reassemble_fragments) return;

bbl_fragment_s *fragment = g_ctx->ipv4_fragments;
bbl_stream_s *stream;

uint8_t *bbl_start;
bbl_bbl_s bbl;

uint16_t offset;

while(fragment) {
if(fragment->id == ipv4->id &&
fragment->src == ipv4->src &&
fragment->dst == ipv4->dst) {
break;
}
fragment = fragment->next;
}
if(!fragment) {
fragment = calloc(1, sizeof(bbl_fragment_s));
fragment->id = ipv4->id;
fragment->src = ipv4->src;
fragment->dst = ipv4->dst;
if(g_ctx->ipv4_fragments) {
g_ctx->ipv4_fragments->prev = fragment;
fragment->next = g_ctx->ipv4_fragments;
}
g_ctx->ipv4_fragments = fragment;
}

offset = (ipv4->offset & 0x1FFF) * 8;
if(offset+ipv4->payload_len > sizeof(fragment->buf)) {
LOG(INFO, "IPv4 fragmented packet to big (%u)\n", (offset+ipv4->payload_len));
bbl_fragment_free(fragment);
return;
}

if(eth->length > fragment->max_length) {
fragment->max_length = eth->length;
}
if(offset > fragment->max_offset) {
fragment->max_offset = offset;
}

fragment->fragments++;
fragment->timestamp = eth->timestamp.tv_sec;

memcpy(fragment->buf+offset, ipv4->payload, ipv4->payload_len);
fragment->recived += ipv4->payload_len;

if(!(ipv4->offset & IPV4_MF)) {
/* Last fragment recieved. */
fragment->expected = offset + ipv4->payload_len;
}
if(fragment->recived == fragment->expected) {
/* All fragments received. */
if(packet_is_bbl(fragment->buf, fragment->recived)) {
/* Currently, we support only the reassembly of BBL stream packets. */
bbl_start = fragment->buf + (fragment->recived-BBL_HEADER_LEN);
bbl.type = *(bbl_start+8);
bbl.sub_type = *(bbl_start+9);
bbl.direction = *(bbl_start+10);
bbl.tos = *(bbl_start+11);
bbl.session_id = *(uint32_t*)(bbl_start+12);
if(bbl.type == BBL_TYPE_UNICAST) {
bbl.ifindex = *(uint32_t*)(bbl_start+16);
bbl.outer_vlan_id = *(uint16_t*)(bbl_start+20);
bbl.inner_vlan_id = *(uint16_t*)(bbl_start+22);
bbl.mc_source = 0;
bbl.mc_source = 0;
} else {
bbl.mc_source = *(uint32_t*)(bbl_start+16);
bbl.mc_source = *(uint32_t*)(bbl_start+20);
bbl.ifindex = 0;
bbl.outer_vlan_id = 0;
bbl.inner_vlan_id = 0;
}
bbl.flow_id = *(uint64_t*)(bbl_start+24);
bbl.flow_seq = *(uint64_t*)(bbl_start+32);
bbl.timestamp.tv_sec = *(uint32_t*)(bbl_start+40);
bbl.timestamp.tv_nsec = *(uint32_t*)(bbl_start+44);

eth->bbl = &bbl;
eth->length = fragment->max_length;

if(access_interface) {
stream = bbl_stream_rx(eth, NULL);
if(stream) {
if(stream->rx_access_interface == NULL) {
stream->rx_access_interface = access_interface;
}
}
} else if (network_interface) {
stream = bbl_stream_rx(eth, network_interface->mac);
if(stream) {
if(stream->rx_network_interface != network_interface) {
if(stream->rx_network_interface) {
/* RX interface has changed! */
stream->rx_interface_changes++;
stream->rx_interface_changed_epoch = eth->timestamp.tv_sec;
}
stream->rx_network_interface = network_interface;
}
}
}
if(stream) {
if(fragment->fragments > stream->rx_fragments) {
stream->rx_fragments = fragment->fragments;
}
if(fragment->max_offset > stream->rx_fragment_offset) {
stream->rx_fragment_offset = fragment->max_offset;
}
}
}
bbl_fragment_free(fragment);
}
}

void
bbl_fragment_cleanup_job(timer_s *timer)
{
bbl_fragment_s *fragment = g_ctx->ipv4_fragments;
bbl_fragment_s *next = fragment;

/* Delete all fragments older than 10 seconds. */
uint32_t timestamp = timer->timestamp->tv_sec - 10;
while(next) {
fragment = next;
next = fragment->next;
if(fragment->timestamp < timestamp) {
bbl_fragment_free(fragment);
}
}
}

void
bbl_fragment_init()
{
if(!g_ctx->config.traffic_reassemble_fragments) return;

timer_add_periodic(&g_ctx->timer_root, &g_ctx->fragmentation_timer,
"FRAGMENT", 3, 0, NULL,
&bbl_fragment_cleanup_job);
}
39 changes: 39 additions & 0 deletions code/bngblaster/src/bbl_fragment.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* BNG Blaster (BBL) - IP Fragmentation
*
* Christian Giese, October 2024
*
* Copyright (C) 2020-2024, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/

#ifndef __BBL_FRAGMENT_H__
#define __BBL_FRAGMENT_H__

typedef struct bbl_fragment_ {
uint32_t timestamp;
uint32_t src;
uint32_t dst;
uint16_t id;
uint16_t fragments; /* Number of fragments */
uint16_t max_offset; /* Max offset value */
uint16_t max_length; /* Max length (L2) */
uint16_t recived;
uint16_t expected;

struct bbl_fragment_ *prev;
struct bbl_fragment_ *next;

uint8_t buf[4050];

} bbl_fragment_s;

void
bbl_fragment_rx(bbl_access_interface_s *access_interface,
bbl_network_interface_s *network_interface,
bbl_ethernet_header_s *eth, bbl_ipv4_s *ipv4);

void
bbl_fragment_init();

#endif
3 changes: 3 additions & 0 deletions code/bngblaster/src/bbl_network.c
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,9 @@ bbl_network_rx_handler(bbl_network_interface_s *interface,
} else if(ipv4->protocol == PROTOCOL_IPV4_OSPF && interface->ospfv2_interface) {
ospf_handler_rx_ipv4(interface, eth, ipv4);
return;
} else if(ipv4->offset & ~IPV4_DF) {
interface->stats.ipv4_fragmented_rx++;
bbl_fragment_rx(NULL, interface, eth, ipv4);
}
break;
case ETH_TYPE_IPV6:
Expand Down
20 changes: 15 additions & 5 deletions code/bngblaster/src/bbl_stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -1084,11 +1084,12 @@ bbl_stream_rx_stats(bbl_stream_s *stream, uint64_t packets, uint64_t bytes, uint
if(packets == 0) return;
if(stream->rx_access_interface) {
access_interface = stream->rx_access_interface;
access_interface->stats.packets_rx += packets;
access_interface->stats.bytes_rx += bytes;
access_interface->stats.stream_rx += packets;
access_interface->stats.stream_loss += loss;
session = stream->session;
if(!stream->rx_fragments) {
access_interface->stats.packets_rx += packets;
access_interface->stats.bytes_rx += bytes;
}
if(session) {
session->stats.packets_rx += packets;
session->stats.bytes_rx += bytes;
Expand All @@ -1115,10 +1116,12 @@ bbl_stream_rx_stats(bbl_stream_s *stream, uint64_t packets, uint64_t bytes, uint
}
} else if(stream->rx_network_interface) {
network_interface = stream->rx_network_interface;
network_interface->stats.packets_rx += packets;
network_interface->stats.bytes_rx += bytes;
network_interface->stats.stream_rx += packets;
network_interface->stats.stream_loss += loss;
if(!stream->rx_fragments) {
network_interface->stats.packets_rx += packets;
network_interface->stats.bytes_rx += bytes;
}
if(session) {
if(session->l2tp_session) {
network_interface->stats.l2tp_data_rx += packets;
Expand Down Expand Up @@ -2173,6 +2176,9 @@ bbl_stream_reset(bbl_stream_s *stream)
stream->rx_min_delay_us = 0;
stream->rx_max_delay_us = 0;
stream->rx_len = 0;
stream->rx_fragments = 0;
stream->rx_fragment_offset = 0;
stream->rx_ttl = 0;
stream->rx_priority = 0;
stream->rx_outer_vlan_pbit = 0;
stream->rx_inner_vlan_pbit = 0;
Expand Down Expand Up @@ -2596,6 +2602,10 @@ bbl_stream_json(bbl_stream_s *stream, bool debug)
json_object_set_new(root, "rx-interface-changes", json_integer(stream->rx_interface_changes));
json_object_set_new(root, "rx-interface-changed-epoch", json_integer(stream->rx_interface_changed_epoch));
}
if(stream->rx_fragments) {
json_object_set_new(root, "rx-fragments", json_integer(stream->rx_fragments));
json_object_set_new(root, "rx-fragment-offset", json_integer(stream->rx_fragment_offset));
}
if(stream->config->rx_mpls1) {
json_object_set_new(root, "rx-mpls1-expected", json_integer(stream->config->rx_mpls1_label));
}
Expand Down
3 changes: 3 additions & 0 deletions code/bngblaster/src/bbl_stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ typedef struct bbl_stream_
__time_t rx_interface_changed_epoch;
uint8_t rx_interface_changes;

uint8_t rx_fragments;
uint16_t rx_fragment_offset; /* Max fragmentation offset received */

uint8_t rx_ttl; /* IPv4 or IPv6 TTL */
uint8_t rx_priority; /* IPv4 TOS or IPv6 TC */
uint8_t rx_outer_vlan_pbit;
Expand Down
1 change: 1 addition & 0 deletions code/bngblaster/src/ospf/ospf.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ ospf_handler_rx_ipv4(bbl_network_interface_s *interface,
/* The following code provides very simplified support for
* reassembling of fragmented IPv4 OSPFv2 packets.
* This code supports only in order fragments from single source. */
interface->stats.ipv4_fragmented_rx++;
LOG(OSPF, "OSPFv2 RX PDU fragment on interface %s\n", interface->name);
if(ipv4->id == ospf_interface->frag_id) {
if(ospf_interface->frag_off == 0 ||
Expand Down
Loading

0 comments on commit 33a21f8

Please sign in to comment.