Merge pull request #141 from rtbrick/dev

Dev
This commit is contained in:
Hannes Gredler
2023-01-13 17:00:59 +01:00
committed by GitHub
78 changed files with 4363 additions and 384 deletions
+2 -1
View File
@@ -6,4 +6,5 @@ add_subdirectory(bngblaster)
add_subdirectory(lspgen)
install(PROGRAMS bngblaster-cli DESTINATION sbin)
install(PROGRAMS bgpupdate DESTINATION bin)
install(PROGRAMS bgpupdate DESTINATION bin)
install(PROGRAMS ldpupdate DESTINATION bin)
+5
View File
@@ -33,3 +33,8 @@ control socket JSON RPC API.
Simple python script to generate BGP RAW update
streams for use with the BNG Blaster.
### ldpupdate
Simple python script to generate LDP RAW update
streams for use with the BNG Blaster.
+1 -1
View File
@@ -1,4 +1,4 @@
FILE(GLOB BBL_SOURCES src/*.c src/io/*.c src/isis/*.c src/bgp/*.c)
FILE(GLOB BBL_SOURCES src/*.c src/io/*.c src/isis/*.c src/ldp/*.c src/bgp/*.c)
list(SORT BBL_SOURCES)
# Deterministic randomness for symbol name creation
+12 -5
View File
@@ -23,16 +23,15 @@
bbl_ctx_s *g_ctx = NULL;
/* Global Variables */
bool g_interactive = false; /* interactive mode using ncurses */
uint8_t g_log_buf_cur = 0;
char *g_log_buf = NULL;
bool g_interactive = false; /* interactive mode using ncurses */
bool g_init_phase = true;
bool g_traffic = true;
bool g_banner = true;
bool g_monkey = true;
uint8_t g_log_buf_cur = 0;
char *g_log_buf = NULL;
extern char *g_log_file;
volatile bool g_teardown = false;
volatile bool g_teardown_request = false;
@@ -152,6 +151,7 @@ struct keyval_ log_names[] = {
{ L2TP, "l2tp" },
{ DHCP, "dhcp" },
{ ISIS, "isis" },
{ LDP, "ldp" },
{ BGP, "bgp" },
{ TCP, "tcp" },
{ LAG, "lag" },
@@ -293,6 +293,7 @@ bbl_ctrl_job(timer_s *timer)
}
/* Teardown routing protocols. */
isis_teardown();
ldp_teardown();
bgp_teardown();
g_teardown_request = false;
} else {
@@ -549,6 +550,12 @@ main(int argc, char *argv[])
goto CLEANUP;
}
/* Init LDP instances. */
if(!ldp_init()) {
fprintf(stderr, "Error: Failed to init LDP\n");
goto CLEANUP;
}
/* Init interfaces. */
if(!bbl_interface_init()) {
fprintf(stderr, "Error: Failed to init interfaces\n");
+2
View File
@@ -27,6 +27,7 @@
#include "io/io_def.h"
#include "bgp/bgp_def.h"
#include "isis/isis_def.h"
#include "ldp/ldp_def.h"
#include "bbl_ctrl.h"
#include "bbl_stats.h"
@@ -49,6 +50,7 @@
#include "io/io.h"
#include "bgp/bgp.h"
#include "isis/isis.h"
#include "ldp/ldp.h"
extern bbl_ctx_s *g_ctx;
extern WINDOW *log_win;
+206 -82
View File
@@ -635,6 +635,13 @@ json_parse_network_interface(json_t *network_interface, bbl_network_config_s *ne
network_config->isis_l2_metric = 10;
}
}
/* LDP interface configuration */
value = json_object_get(network_interface, "ldp-instance-id");
if(json_is_number(value)) {
network_config->ldp_instance_id = json_number_value(value);
}
return true;
}
@@ -1068,11 +1075,11 @@ json_parse_bgp_config(json_t *bgp, bgp_config_s *bgp_config)
bgp_config->peer_as = bgp_config->local_as;
}
value = json_object_get(bgp, "holdtime");
value = json_object_get(bgp, "hold-time");
if(value) {
bgp_config->holdtime = json_number_value(value);
bgp_config->hold_time = json_number_value(value);
} else {
bgp_config->holdtime = BGP_DEFAULT_HOLDTIME;
bgp_config->hold_time = BGP_DEFAULT_HOLD_TIME;
}
bgp_config->id = htobe32(0x01020304);
@@ -1191,11 +1198,11 @@ json_parse_isis_config(json_t *isis, isis_config_s *isis_config)
isis_config->hello_padding = json_boolean_value(value);
}
value = json_object_get(isis, "holding-time");
value = json_object_get(isis, "hold-time");
if(json_is_number(value)) {
isis_config->holding_time = json_number_value(value);
isis_config->hold_time = json_number_value(value);
} else {
isis_config->holding_time = ISIS_DEFAULT_HOLDING_TIME;
isis_config->hold_time = ISIS_DEFAULT_HOLD_TIME;
}
value = json_object_get(isis, "lsp-lifetime");
@@ -1358,6 +1365,76 @@ json_parse_isis_config(json_t *isis, isis_config_s *isis_config)
return true;
}
static bool
json_parse_ldp_config(json_t *ldp, ldp_config_s *ldp_config)
{
json_t *value = NULL;
const char *s = NULL;
g_ctx->tcp = true;
value = json_object_get(ldp, "instance-id");
if(value) {
ldp_config->id = json_number_value(value);
} else {
fprintf(stderr, "JSON config error: Missing value for ldp->instance-id\n");
return false;
}
value = json_object_get(ldp, "keepalive-time");
if(json_is_number(value)) {
ldp_config->keepalive_time = json_number_value(value);
} else {
ldp_config->keepalive_time = LDP_DEFAULT_KEEPALIVE_TIME;
}
value = json_object_get(ldp, "hold-time");
if(json_is_number(value)) {
ldp_config->hold_time = json_number_value(value);
} else {
ldp_config->hold_time = LDP_DEFAULT_HOLD_TIME;
}
value = json_object_get(ldp, "teardown-time");
if(json_is_number(value)) {
ldp_config->teardown_time = json_number_value(value);
} else {
ldp_config->teardown_time = LDP_DEFAULT_TEARDOWN_TIME;
}
if(json_unpack(ldp, "{s:s}", "hostname", &s) == 0) {
ldp_config->hostname = strdup(s);
} else {
ldp_config->hostname = g_default_hostname;
}
if(json_unpack(ldp, "{s:s}", "lsr-id", &s) == 0) {
ldp_config->lsr_id_str = strdup(s);
} else {
ldp_config->lsr_id_str = g_default_router_id;
}
if(!inet_pton(AF_INET, ldp_config->lsr_id_str, &ldp_config->lsr_id)) {
fprintf(stderr, "JSON config error: Invalid value for ldp->lsr-id\n");
return false;
}
if(json_unpack(ldp, "{s:s}", "ipv4-transport-address", &s) == 0) {
if(!inet_pton(AF_INET, s, &ldp_config->ipv4_transport_address)) {
fprintf(stderr, "JSON config error: Invalid value for ldp->ipv4-transport-address\n");
return false;
}
} else {
ldp_config->ipv4_transport_address = ldp_config->lsr_id;
}
if(json_unpack(ldp, "{s:s}", "raw-update-file", &s) == 0) {
ldp_config->raw_update_file = strdup(s);
if(!ldp_raw_update_load(ldp_config->raw_update_file, true)) {
return false;
}
}
return true;
}
static bool
json_parse_stream(json_t *stream, bbl_stream_config_s *stream_config)
{
@@ -1500,6 +1577,13 @@ json_parse_stream(json_t *stream, bbl_stream_config_s *stream_config)
stream_config->start_delay = json_number_value(value);
}
if(json_unpack(stream, "{s:s}", "ldp-ipv4-lookup-address", &s) == 0) {
if(!inet_pton(AF_INET, s, &stream_config->ipv4_ldp_lookup_address)) {
fprintf(stderr, "JSON config error: Invalid value for stream->ldp-ipv4-lookup-address\n");
return false;
}
}
if(json_unpack(stream, "{s:s}", "access-ipv4-source-address", &s) == 0) {
if(!inet_pton(AF_INET, s, &stream_config->ipv4_access_src_address)) {
fprintf(stderr, "JSON config error: Invalid value for stream->access-ipv4-source-address\n");
@@ -1557,32 +1641,33 @@ json_parse_stream(json_t *stream, bbl_stream_config_s *stream_config)
if(value) {
stream_config->tx_mpls1 = true;
stream_config->tx_mpls1_label = json_number_value(value);
value = json_object_get(stream, "tx-label1-exp");
if(value) {
stream_config->tx_mpls1_exp = json_number_value(value);
}
value = json_object_get(stream, "tx-label1-ttl");
if(value) {
stream_config->tx_mpls1_ttl = json_number_value(value);
} else {
stream_config->tx_mpls1_ttl = 255;
}
}
value = json_object_get(stream, "tx-label1-exp");
if(value) {
stream_config->tx_mpls1_exp = json_number_value(value);
}
value = json_object_get(stream, "tx-label1-ttl");
if(value) {
stream_config->tx_mpls1_ttl = json_number_value(value);
} else {
stream_config->tx_mpls1_ttl = 255;
}
value = json_object_get(stream, "tx-label2");
if(value) {
stream_config->tx_mpls2 = true;
stream_config->tx_mpls2_label = json_number_value(value);
value = json_object_get(stream, "tx-label2-exp");
if(value) {
stream_config->tx_mpls2_exp = json_number_value(value);
}
value = json_object_get(stream, "tx-label2-ttl");
if(value) {
stream_config->tx_mpls2_ttl = json_number_value(value);
} else {
stream_config->tx_mpls2_ttl = 255;
}
}
value = json_object_get(stream, "tx-label2-exp");
if(value) {
stream_config->tx_mpls2_exp = json_number_value(value);
}
value = json_object_get(stream, "tx-label2-ttl");
if(value) {
stream_config->tx_mpls2_ttl = json_number_value(value);
} else {
stream_config->tx_mpls2_ttl = 255;
}
value = json_object_get(stream, "rx-label1");
if(value) {
stream_config->rx_mpls1 = true;
@@ -1621,6 +1706,46 @@ json_parse_stream(json_t *stream, bbl_stream_config_s *stream_config)
return true;
}
static bool
json_parse_config_streams(json_t *root)
{
json_t *section = NULL;
int i, size;
bbl_stream_config_s *stream_config = g_ctx->config.stream_config;
if(json_typeof(root) != JSON_OBJECT) {
fprintf(stderr, "JSON config error: Configuration root element must object\n");
return false;
}
section = json_object_get(root, "streams");
if(json_is_array(section)) {
/* Get tail end of stream-config list. */
if(stream_config) {
while(stream_config->next) {
stream_config = stream_config->next;
}
}
/* Config is provided as array (multiple streams) */
size = json_array_size(section);
for(i = 0; i < size; i++) {
if(!stream_config) {
g_ctx->config.stream_config = calloc(1, sizeof(bbl_stream_config_s));
stream_config = g_ctx->config.stream_config;
} else {
stream_config->next = calloc(1, sizeof(bbl_stream_config_s));
stream_config = stream_config->next;
}
if(!json_parse_stream(json_array_get(section, i), stream_config)) {
return false;
}
}
}
return true;
}
static bool
json_parse_config(json_t *root)
{
@@ -1631,7 +1756,6 @@ json_parse_config(json_t *root)
double number;
bbl_access_line_profile_s *access_line_profile = NULL;
bbl_stream_config_s *stream_config = NULL;
bbl_l2tp_server_s *l2tp_server = NULL;
bbl_lag_config_s *lag_config = NULL;
@@ -1642,6 +1766,7 @@ json_parse_config(json_t *root)
bgp_config_s *bgp_config = NULL;
isis_config_s *isis_config = NULL;
ldp_config_s *ldp_config = NULL;
if(json_typeof(root) != JSON_OBJECT) {
fprintf(stderr, "JSON config error: Configuration root element must object\n");
@@ -1679,6 +1804,10 @@ json_parse_config(json_t *root)
if(json_is_boolean(value)) {
g_ctx->config.sessions_autostart = json_boolean_value(value);
}
value = json_object_get(section, "reconnect");
if(json_is_boolean(value)) {
g_ctx->config.sessions_reconnect = json_boolean_value(value);
}
value = json_object_get(section, "monkey-autostart");
if(json_is_boolean(value)) {
g_ctx->config.monkey_autostart = json_boolean_value(value);
@@ -1740,6 +1869,8 @@ json_parse_config(json_t *root)
value = json_object_get(section, "reconnect");
if(json_is_boolean(value)) {
g_ctx->config.pppoe_reconnect = json_boolean_value(value);
} else {
g_ctx->config.pppoe_reconnect = g_ctx->config.sessions_reconnect;
}
value = json_object_get(section, "discovery-timeout");
if(json_is_number(value)) {
@@ -2167,7 +2298,7 @@ json_parse_config(json_t *root)
/* BGP Configuration */
sub = json_object_get(root, "bgp");
if(json_is_array(sub)) {
/* Config is provided as array (multiple bgp sessions) */
/* Config is provided as array (multiple BGP sessions) */
size = json_array_size(sub);
for(i = 0; i < size; i++) {
if(!bgp_config) {
@@ -2182,7 +2313,7 @@ json_parse_config(json_t *root)
}
}
} else if(json_is_object(sub)) {
/* Config is provided as object (single bgp session) */
/* Config is provided as object (single BGP session) */
bgp_config = calloc(1, sizeof(bgp_config_s));
if(!g_ctx->config.bgp_config) {
g_ctx->config.bgp_config = bgp_config;
@@ -2209,7 +2340,7 @@ json_parse_config(json_t *root)
/* IS-IS Configuration */
sub = json_object_get(root, "isis");
if(json_is_array(sub)) {
/* Config is provided as array (multiple isis instances) */
/* Config is provided as array (multiple IS-IS instances) */
size = json_array_size(sub);
for(i = 0; i < size; i++) {
if(!isis_config) {
@@ -2224,7 +2355,7 @@ json_parse_config(json_t *root)
}
}
} else if(json_is_object(sub)) {
/* Config is provided as object (single isis instance) */
/* Config is provided as object (single IS-IS instance) */
isis_config = calloc(1, sizeof(isis_config_s));
if(!g_ctx->config.isis_config) {
g_ctx->config.isis_config = isis_config;
@@ -2234,6 +2365,48 @@ json_parse_config(json_t *root)
}
}
/* LDP Configuration */
sub = json_object_get(root, "ldp");
if(json_is_array(sub)) {
/* Config is provided as array (multiple LDP instances) */
size = json_array_size(sub);
for(i = 0; i < size; i++) {
if(!ldp_config) {
g_ctx->config.ldp_config = calloc(1, sizeof(ldp_config_s));
ldp_config = g_ctx->config.ldp_config;
} else {
ldp_config->next = calloc(1, sizeof(ldp_config_s));
ldp_config = ldp_config->next;
}
if(!json_parse_ldp_config(json_array_get(sub, i), ldp_config)) {
return false;
}
}
} else if(json_is_object(sub)) {
/* Config is provided as object (single LDP instance) */
ldp_config = calloc(1, sizeof(ldp_config_s));
if(!g_ctx->config.ldp_config) {
g_ctx->config.ldp_config = ldp_config;
}
if(!json_parse_ldp_config(sub, ldp_config)) {
return false;
}
}
/* Pre-Load LDP RAW update files */
sub = json_object_get(root, "ldp-raw-update-files");
if(json_is_array(sub)) {
size = json_array_size(sub);
for(i = 0; i < size; i++) {
s = json_string_value(json_array_get(sub, i));
if(s) {
if(!ldp_raw_update_load(s, true)) {
return false;
}
}
}
}
/* Interface Configuration */
section = json_object_get(root, "interfaces");
if(json_is_object(section)) {
@@ -2562,24 +2735,9 @@ json_parse_config(json_t *root)
}
/* Traffic Streams Configuration */
section = json_object_get(root, "streams");
if(json_is_array(section)) {
/* Config is provided as array (multiple streams) */
size = json_array_size(section);
for(i = 0; i < size; i++) {
if(!stream_config) {
g_ctx->config.stream_config = calloc(1, sizeof(bbl_stream_config_s));
stream_config = g_ctx->config.stream_config;
} else {
stream_config->next = calloc(1, sizeof(bbl_stream_config_s));
stream_config = stream_config->next;
}
if(!json_parse_stream(json_array_get(section, i), stream_config)) {
return false;
}
}
if(!json_parse_config_streams(root)) {
return false;
}
return true;
}
@@ -2610,40 +2768,6 @@ bbl_config_load_json(const char *filename)
return result;
}
static bool
json_parse_config_streams(json_t *root)
{
json_t *section = NULL;
int i, size;
bbl_stream_config_s *stream_config = g_ctx->config.stream_config;
if(json_typeof(root) != JSON_OBJECT) {
fprintf(stderr, "JSON config error: Configuration root element must object\n");
return false;
}
section = json_object_get(root, "streams");
if(json_is_array(section)) {
/* Config is provided as array (multiple streams) */
size = json_array_size(section);
for(i = 0; i < size; i++) {
if(!stream_config) {
g_ctx->config.stream_config = calloc(1, sizeof(bbl_stream_config_s));
stream_config = g_ctx->config.stream_config;
} else {
stream_config->next = calloc(1, sizeof(bbl_stream_config_s));
stream_config = stream_config->next;
}
if(!json_parse_stream(json_array_get(section, i), stream_config)) {
return false;
}
}
}
return true;
}
/**
* bbl_config_streams_load_json
*
+2
View File
@@ -106,6 +106,8 @@ typedef struct bbl_network_config_
uint32_t isis_l1_metric;
uint32_t isis_l2_metric;
uint16_t ldp_instance_id;
void *next; /* pointer to next network config element */
bbl_network_interface_s *network_interface;
} bbl_network_config_s;
+7
View File
@@ -156,6 +156,13 @@ struct action actions[] = {
{"bgp-teardown", bgp_ctrl_teardown, true},
{"bgp-raw-update-list", bgp_ctrl_raw_update_list, true},
{"bgp-raw-update", bgp_ctrl_raw_update, false},
{"ldp-adjacencies", ldp_ctrl_adjacencies, true},
{"ldp-sessions", ldp_ctrl_sessions, true},
{"ldp-database", ldb_ctrl_database, true},
{"ldp-disconnect", ldp_ctrl_disconnect, false},
{"ldp-teardown", ldp_ctrl_teardown, true},
{"ldp-raw-update-list", ldp_ctrl_raw_update_list, true},
{"ldp-raw-update", ldp_ctrl_raw_update, false},
{"monkey-start", bbl_session_ctrl_monkey_start, false},
{"monkey-stop", bbl_session_ctrl_monkey_stop, false},
{"lag-info", bbl_lag_ctrl_info, true},
+6
View File
@@ -95,6 +95,8 @@ typedef struct bbl_ctx_
bgp_session_s *bgp_sessions;
bgp_raw_update_s *bgp_raw_updates;
isis_instance_s *isis_instances;
ldp_instance_s *ldp_instances;
ldp_raw_update_s *ldp_raw_updates;
/* Scratchpad memory */
uint8_t *sp;
@@ -191,12 +193,16 @@ typedef struct bbl_ctx_
/* ISIS Instances */
isis_config_s *isis_config;
/* LDP Instances */
ldp_config_s *ldp_config;
/* Global Session Settings */
uint32_t sessions;
uint32_t sessions_max_outstanding;
uint16_t sessions_start_rate;
uint16_t sessions_stop_rate;
uint16_t sessions_start_delay;
bool sessions_reconnect;
bool sessions_autostart;
bool monkey_autostart;
bool iterate_outer_vlan;
+7 -1
View File
@@ -90,6 +90,7 @@
#define BBL_IF_SEND_ARP_REQUEST 0x00000001
#define BBL_IF_SEND_ICMPV6_NS 0x00000002
#define BBL_IF_SEND_ISIS_P2P_HELLO 0x00000004
#define BBL_IF_SEND_LDP_HELLO 0x00000008
#define DUID_LEN 10
@@ -106,6 +107,12 @@
#define ACTIVATE_ENDPOINT(_endpoint) \
if(_endpoint != ENDPOINT_DISABLED) _endpoint = ENDPOINT_ACTIVE
typedef enum {
IANA_AFI_RESERVED = 0,
IANA_AFI_IPV4 = 1,
IANA_AFI_IPV6 = 2,
} __attribute__ ((__packed__)) iana_afi_t;
typedef enum {
ACCESS_TYPE_PPPOE = 0,
ACCESS_TYPE_IPOE
@@ -129,7 +136,6 @@ typedef enum {
LAG_MEMBER_INTERFACE,
} __attribute__ ((__packed__)) interface_type_t;
typedef enum {
IGMP_GROUP_IDLE = 0,
IGMP_GROUP_LEAVING,
+61 -18
View File
@@ -7,6 +7,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "bbl.h"
#include "bbl_protocols.h"
#include "bbl_session.h"
#include "bbl_stream.h"
@@ -41,7 +42,8 @@ bbl_network_interfaces_add()
bbl_network_interface_s *network_interface;
bbl_interface_s *interface;
isis_instance_s *isis;
bool isis_result;
ldp_instance_s *ldp;
bool result;
char ifname[SUB_STR_LEN];
@@ -141,12 +143,12 @@ bbl_network_interfaces_add()
/* Init routing protocols */
if(network_config->isis_instance_id) {
isis_result = false;
result = false;
isis = g_ctx->isis_instances;
while (isis) {
while(isis) {
if(isis->config->id == network_config->isis_instance_id) {
isis_result = isis_adjacency_init(network_interface, network_config, isis);
if(!isis_result) {
result = isis_adjacency_init(network_interface, network_config, isis);
if(!result) {
LOG(ERROR, "Failed to enable IS-IS for network interface %s (adjacency init failed)\n", ifname);
return false;
}
@@ -154,11 +156,30 @@ bbl_network_interfaces_add()
}
isis = isis->next;
}
if(!isis_result) {
if(!result) {
LOG(ERROR, "Failed to enable IS-IS for network interface %s (instance not found)\n", ifname);
return false;
}
}
if(network_config->ldp_instance_id) {
result = false;
ldp = g_ctx->ldp_instances;
while(ldp) {
if(ldp->config->id == network_config->ldp_instance_id) {
result = ldp_interface_init(network_interface, network_config, ldp);
if(!result) {
LOG(ERROR, "Failed to enable LDP for network interface %s (adjacency init failed)\n", ifname);
return false;
}
break;
}
ldp = ldp->next;
}
if(!result) {
LOG(ERROR, "Failed to enable LDP for network interface %s (instance not found)\n", ifname);
return false;
}
}
/* TX list init */
CIRCLEQ_INIT(&network_interface->l2tp_tx_qhead);
@@ -309,6 +330,7 @@ bbl_network_rx_arp(bbl_network_interface_s *interface, bbl_ethernet_header_s *et
static void
bbl_network_rx_icmpv6(bbl_network_interface_s *interface,
bbl_ethernet_header_s *eth) {
uint8_t *gw_mac;
bbl_ipv6_s *ipv6;
bbl_icmpv6_s *icmpv6;
bbl_secondary_ip6_s *secondary_ip6;
@@ -316,13 +338,20 @@ bbl_network_rx_icmpv6(bbl_network_interface_s *interface,
ipv6 = (bbl_ipv6_s*)eth->next;
icmpv6 = (bbl_icmpv6_s*)ipv6->next;
if(memcmp(ipv6->src, interface->gateway6, IPV6_ADDR_LEN) == 0) {
interface->icmpv6_nd_resolved = true;
if(*(uint32_t*)interface->gateway_mac == 0) {
memcpy(interface->gateway_mac, eth->src, ETH_ADDR_LEN);
if(icmpv6->type == IPV6_ICMPV6_NEIGHBOR_ADVERTISEMENT) {
if(memcmp(icmpv6->prefix.address, interface->gateway6, IPV6_ADDR_LEN) == 0) {
interface->icmpv6_nd_resolved = true;
if(*(uint32_t*)interface->gateway_mac == 0) {
if(icmpv6->dst_mac == NULL) {
gw_mac = eth->src;
} else {
gw_mac = icmpv6->dst_mac;
}
memcpy(interface->gateway_mac, gw_mac, ETH_ADDR_LEN);
}
}
}
if(icmpv6->type == IPV6_ICMPV6_NEIGHBOR_SOLICITATION) {
} else if(icmpv6->type == IPV6_ICMPV6_NEIGHBOR_SOLICITATION) {
if(memcmp(icmpv6->prefix.address, interface->ip6.address, IPV6_ADDR_LEN) == 0) {
bbl_network_icmpv6_na(interface, eth, ipv6, icmpv6);
} else if(memcmp(icmpv6->prefix.address, interface->ip6_ll, IPV6_ADDR_LEN) == 0) {
@@ -377,25 +406,39 @@ bbl_network_rx_handler(bbl_network_interface_s *interface,
bbl_network_rx_arp(interface, eth);
return;
case ETH_TYPE_IPV4:
if(memcmp(interface->mac, eth->dst, ETH_ADDR_LEN) != 0) {
/* Drop wrong MAC */
return;
}
ipv4 = (bbl_ipv4_s*)eth->next;
if(ipv4->protocol == PROTOCOL_IPV4_UDP) {
udp = (bbl_udp_s*)ipv4->next;
/* LDP hello is send to all routers multicast address and therefore
* processed before check on local MAC address. */
if(udp->protocol == UDP_PROTOCOL_LDP) {
ldp_hello_rx(interface, eth, ipv4, (bbl_ldp_hello_s*)udp->next);
return;
}
if(memcmp(interface->mac, eth->dst, ETH_ADDR_LEN) != 0) {
/* Drop wrong MAC */
return;
}
if(udp->protocol == UDP_PROTOCOL_QMX_LI) {
bbl_qmx_li_handler_rx(interface, eth, (bbl_qmx_li_s*)udp->next);
return;
} else if(udp->protocol == UDP_PROTOCOL_L2TP) {
bbl_l2tp_handler_rx(interface, eth, (bbl_l2tp_s*)udp->next);
return;
}
}
} else if(ipv4->protocol == PROTOCOL_IPV4_ICMP) {
if(memcmp(interface->mac, eth->dst, ETH_ADDR_LEN) != 0) {
/* Drop wrong MAC */
return;
}
interface->stats.icmp_rx++;
bbl_network_rx_icmp(interface, eth, ipv4);
return;
} else if(ipv4->protocol == PROTOCOL_IPV4_TCP) {
if(memcmp(interface->mac, eth->dst, ETH_ADDR_LEN) != 0) {
/* Drop wrong MAC */
return;
}
interface->stats.tcp_rx++;
bbl_tcp_ipv4_rx(interface, eth, ipv4);
return;
@@ -487,4 +530,4 @@ bbl_network_ctrl_interfaces(int fd, uint32_t session_id __attribute__((unused)),
json_decref(interfaces);
}
return result;
}
}
+5
View File
@@ -56,6 +56,7 @@ typedef struct bbl_network_interface_
isis_adjacency_p2p_s *isis_adjacency_p2p;
isis_adjacency_s *isis_adjacency[ISIS_LEVELS];
ldp_adjacency_s *ldp_adjacency;
struct {
uint64_t packets_tx;
@@ -108,6 +109,10 @@ typedef struct bbl_network_interface_
uint32_t isis_tx;
uint32_t isis_rx_error;
uint32_t ldp_udp_rx;
uint32_t ldp_udp_tx;
uint32_t ldp_udp_rx_error;
/* Rate Stats */
bbl_rate_s rate_packets_tx;
+213 -22
View File
@@ -10,6 +10,7 @@
#include "bbl_protocols.h"
#include "bbl_access_line.h"
#include "isis/isis_def.h"
#include "ldp/ldp_def.h"
static protocol_error_t decode_l2tp(uint8_t *buf, uint16_t len, uint8_t *sp, uint16_t sp_len, bbl_ethernet_header_s *eth, bbl_l2tp_s **_l2tp);
static protocol_error_t encode_l2tp(uint8_t *buf, uint16_t *len, bbl_l2tp_s *l2tp);
@@ -551,6 +552,55 @@ encode_dhcp(uint8_t *buf, uint16_t *len,
return PROTOCOL_SUCCESS;
}
/*
* encode_ldp_hello
*/
static protocol_error_t
encode_ldp_hello(uint8_t *buf, uint16_t *len, bbl_ldp_hello_s *ldp)
{
/* PDU version and length */
*(uint16_t*)buf = htobe16(1);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
*(uint16_t*)buf = htobe16(30);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
/* LDP identifier (LSR ID + label space) */
*(uint32_t*)buf = ldp->lsr_id;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint32_t));
*(uint16_t*)buf = htobe16(ldp->label_space_id);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
/* LDP message type and length */
*(uint16_t*)buf = htobe16(LDP_MESSAGE_TYPE_HELLO);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
*(uint16_t*)buf = htobe16(20);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
/* LDP message ID */
*(uint32_t*)buf = htobe32(ldp->msg_id);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint32_t));
/* Common hello parameters TLV */
*(uint16_t*)buf = htobe16(LDP_TLV_TYPE_COMMON_HELLO_PARAMETERS);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
*(uint16_t*)buf = htobe16(4);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
*(uint16_t*)buf = htobe16(ldp->hold_time);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
*(uint16_t*)buf = 0;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
/* IPv4 transport address TLV */
*(uint16_t*)buf = htobe16(LDP_TLV_TYPE_IPV4_TRANSPORT_ADDRESS);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
*(uint16_t*)buf = htobe16(4);
BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t));
*(uint32_t*)buf = ldp->ipv4_transport_address;
BUMP_WRITE_BUFFER(buf, len, sizeof(uint32_t));
return PROTOCOL_SUCCESS;
}
/*
* encode_bbl
*/
@@ -628,6 +678,9 @@ encode_udp(uint8_t *buf, uint16_t *len,
case UDP_PROTOCOL_DHCP:
result = encode_dhcp(buf, len, (bbl_dhcp_s*)udp->next);
break;
case UDP_PROTOCOL_LDP:
result = encode_ldp_hello(buf, len, (bbl_ldp_hello_s*)udp->next);
break;
default:
result = PROTOCOL_SUCCESS;
break;
@@ -2175,7 +2228,7 @@ decode_icmpv6(uint8_t *buf, uint16_t len,
BUMP_BUFFER(buf, len, sizeof(uint8_t)); /* hop limit */
icmpv6->flags = *buf;
BUMP_BUFFER(buf, len, 11);
while(len > 2) {
while(len >= 8) {
option = *buf;
BUMP_BUFFER(buf, len, sizeof(uint8_t));
option_len = *buf;
@@ -2202,12 +2255,37 @@ decode_icmpv6(uint8_t *buf, uint16_t len,
}
break;
case IPV6_ICMPV6_NEIGHBOR_SOLICITATION:
if(len < 20) {
return DECODE_ERROR;
}
BUMP_BUFFER(buf, len, sizeof(uint32_t)); /* flags / reserved */
memcpy(&icmpv6->prefix.address, buf, IPV6_ADDR_LEN);
break;
case IPV6_ICMPV6_NEIGHBOR_ADVERTISEMENT:
if(len < 20) {
return DECODE_ERROR;
}
BUMP_BUFFER(buf, len, sizeof(uint32_t)); /* flags / reserved */
memcpy(&icmpv6->prefix.address, buf, IPV6_ADDR_LEN);
BUMP_BUFFER(buf, len, IPV6_ADDR_LEN);
while(len >= 8) {
option = *buf;
BUMP_BUFFER(buf, len, sizeof(uint8_t));
option_len = (*buf) * 8;
BUMP_BUFFER(buf, len, sizeof(uint8_t));
if(option_len < 2 || len < option_len - 2) {
return DECODE_ERROR;
}
if(option == ICMPV6_OPTION_DEST_LINK_LAYER) {
if(option_len != 8) {
// Maleformed ICMPv6 packet
return DECODE_ERROR;
}
icmpv6->dst_mac = buf;
break;
}
BUMP_BUFFER(buf, len, option_len-2);
}
break;
default:
break;
@@ -2721,6 +2799,106 @@ decode_qmx_li(uint8_t *buf, uint16_t len,
return decode_ethernet(buf, len, sp, sp_len, (bbl_ethernet_header_s**)&qmx_li->next);
}
/*
* decode_ldp_hello
*/
static protocol_error_t
decode_ldp_hello(uint8_t *buf, uint16_t len,
uint8_t *sp, uint16_t sp_len,
bbl_ldp_hello_s **_ldp)
{
bbl_ldp_hello_s *ldp;
uint16_t pdu_version;
uint16_t pdu_len;
uint16_t msg_type;
uint16_t msg_len;
uint16_t tlv_type;
uint16_t tlv_len;
if(len < 10 || sp_len < sizeof(bbl_ldp_hello_s)) {
return DECODE_ERROR;
}
/* Init LDP structure */
ldp = (bbl_ldp_hello_s*)sp; BUMP_BUFFER(sp, sp_len, sizeof(bbl_ldp_hello_s));
memset(ldp, 0x0, sizeof(bbl_ldp_hello_s));
/* PDU version and length */
pdu_version = be16toh(*(uint16_t*)buf) & 0x7FFF;
BUMP_BUFFER(buf, len, sizeof(uint16_t));
pdu_len = be16toh(*(uint16_t*)buf);
BUMP_BUFFER(buf, len, sizeof(uint16_t));
if(pdu_version != 1) {
return UNKNOWN_PROTOCOL;
}
/* The PDU length is defined as two octet integer specifying
* the total length of the PDU in octets, excluding the version
* and PDU length fields. */
if(pdu_len > len) {
return UNKNOWN_PROTOCOL;
}
len = pdu_len;
/* LDP identifier (LSR ID + label space) */
ldp->lsr_id = *(uint32_t*)buf;
BUMP_BUFFER(buf, len, sizeof(uint32_t));
ldp->label_space_id = be16toh(*(uint16_t*)buf);
BUMP_BUFFER(buf, len, sizeof(uint16_t));
/* LDP message type and length */
if(len < 4) {
return DECODE_ERROR;
}
msg_type = be16toh(*(uint16_t*)buf);
BUMP_BUFFER(buf, len, sizeof(uint16_t));
msg_len = be16toh(*(uint16_t*)buf);
BUMP_BUFFER(buf, len, sizeof(uint16_t));
if(msg_len > len || msg_len < 4 ||
msg_type != LDP_MESSAGE_TYPE_HELLO) {
return DECODE_ERROR;
}
len = msg_len;
/* LDP message ID */
ldp->msg_id = be32toh(*(uint32_t*)buf);
BUMP_BUFFER(buf, len, sizeof(uint32_t));
/* LDP TLV's */
while(len >= LDP_TLV_LEN_MIN) {
tlv_type = be16toh(*(uint16_t*)buf);
BUMP_BUFFER(buf, len, sizeof(uint16_t));
tlv_len = be16toh(*(uint16_t*)buf);
BUMP_BUFFER(buf, len, sizeof(uint16_t));
if(tlv_len > len) {
return DECODE_ERROR;
}
switch(tlv_type) {
case LDP_TLV_TYPE_COMMON_HELLO_PARAMETERS:
if(tlv_len != 4) {
return DECODE_ERROR;
}
ldp->hold_time = be16toh(*(uint16_t*)buf);
break;
case LDP_TLV_TYPE_IPV4_TRANSPORT_ADDRESS:
if(tlv_len != 4) {
return DECODE_ERROR;
}
ldp->ipv4_transport_address = *(uint32_t*)buf;
break;
default:
break;
}
BUMP_BUFFER(buf, len, tlv_len);
}
*_ldp = ldp;
return PROTOCOL_SUCCESS;
}
/*
* decode_udp
*/
@@ -2730,7 +2908,7 @@ decode_udp(uint8_t *buf, uint16_t len,
bbl_ethernet_header_s *eth,
bbl_udp_s **_udp)
{
protocol_error_t ret_val = PROTOCOL_SUCCESS;
protocol_error_t ret_val = UNKNOWN_PROTOCOL;
bbl_udp_s *udp;
@@ -2766,8 +2944,10 @@ decode_udp(uint8_t *buf, uint16_t len,
eth->bbl = udp->next;
break;
case L2TP_UDP_PORT:
udp->protocol = UDP_PROTOCOL_L2TP;
ret_val = decode_l2tp(buf, len, sp, sp_len, eth, (bbl_l2tp_s**)&udp->next);
if(udp->src == L2TP_UDP_PORT) {
udp->protocol = UDP_PROTOCOL_L2TP;
ret_val = decode_l2tp(buf, len, sp, sp_len, eth, (bbl_l2tp_s**)&udp->next);
}
break;
case DHCP_UDP_CLIENT:
case DHCP_UDP_SERVER:
@@ -2778,23 +2958,34 @@ decode_udp(uint8_t *buf, uint16_t len,
udp->protocol = UDP_PROTOCOL_QMX_LI;
ret_val = decode_qmx_li(buf, len, sp, sp_len, (bbl_qmx_li_s**)&udp->next);
break;
default:
if(udp->src == QMX_LI_UDP_PORT) {
udp->protocol = UDP_PROTOCOL_QMX_LI;
ret_val = decode_qmx_li(buf, len, sp, sp_len, (bbl_qmx_li_s**)&udp->next);
} else {
/* Try if payload could be decoded as BBL!
* This fails fast if the 64 bit magic number
* is not found on the expected position. */
if(decode_bbl(buf, len, sp, sp_len, (bbl_bbl_s**)&udp->next) == PROTOCOL_SUCCESS) {
udp->protocol = UDP_PROTOCOL_BBL;
eth->bbl = udp->next;
} else {
udp->protocol = 0;
udp->next = NULL;
}
case LDP_PORT:
if(udp->src == LDP_PORT) {
udp->protocol = UDP_PROTOCOL_LDP;
ret_val = decode_ldp_hello(buf, len, sp, sp_len, (bbl_ldp_hello_s**)&udp->next);
}
break;
default:
break;
}
if(ret_val == UNKNOWN_PROTOCOL) {
if(udp->src == QMX_LI_UDP_PORT) {
udp->protocol = UDP_PROTOCOL_QMX_LI;
ret_val = decode_qmx_li(buf, len, sp, sp_len, (bbl_qmx_li_s**)&udp->next);
} else {
/* Try if payload could be decoded as BBL!
* This fails fast if the 64 bit magic number
* is not found on the expected position. */
ret_val = decode_bbl(buf, len, sp, sp_len, (bbl_bbl_s**)&udp->next);
if(ret_val == PROTOCOL_SUCCESS) {
udp->protocol = UDP_PROTOCOL_BBL;
eth->bbl = udp->next;
} else {
ret_val = PROTOCOL_SUCCESS;
udp->protocol = 0;
udp->next = NULL;
}
}
}
*_udp = udp;
@@ -3932,7 +4123,7 @@ decode_ethernet(uint8_t *buf, uint16_t len,
}
eth->vlan_outer_priority = *buf >> 5;
eth->vlan_outer = be16toh(*(uint16_t*)buf);
eth->vlan_outer &= ETH_VLAN_ID_MAX;
eth->vlan_outer &= BBL_ETH_VLAN_ID_MAX;
BUMP_BUFFER(buf, len, sizeof(uint16_t));
eth->type = *(uint16_t*)buf;
@@ -3943,7 +4134,7 @@ decode_ethernet(uint8_t *buf, uint16_t len,
}
eth->vlan_inner_priority = *buf >> 5;
eth->vlan_inner = be16toh(*(uint16_t*)buf);
eth->vlan_inner &= ETH_VLAN_ID_MAX;
eth->vlan_inner &= BBL_ETH_VLAN_ID_MAX;
BUMP_BUFFER(buf, len, sizeof(uint16_t));
eth->type = *(uint16_t*)buf;
BUMP_BUFFER(buf, len, sizeof(uint16_t));
@@ -3952,7 +4143,7 @@ decode_ethernet(uint8_t *buf, uint16_t len,
return DECODE_ERROR;
}
eth->vlan_three = be16toh(*(uint16_t*)buf);
eth->vlan_three &= ETH_VLAN_ID_MAX;
eth->vlan_three &= BBL_ETH_VLAN_ID_MAX;
BUMP_BUFFER(buf, len, sizeof(uint16_t));
eth->type = *(uint16_t*)buf;
BUMP_BUFFER(buf, len, sizeof(uint16_t));
+17 -4
View File
@@ -87,8 +87,8 @@
#define NB_ETH_TYPE_MPLS 0x8847
#endif
#define ETH_VLAN_ID_MAX 4095
#define ETH_VLAN_PBIT_MAX 7
#define BBL_ETH_VLAN_ID_MAX 4095
#define BBL_ETH_VLAN_PBIT_MAX 7
#define ETH_IEEE_802_3_MAX_LEN 1500
@@ -202,6 +202,7 @@
#define UDP_PROTOCOL_L2TP 3
#define UDP_PROTOCOL_QMX_LI 4
#define UDP_PROTOCOL_DHCP 5
#define UDP_PROTOCOL_LDP 6
#define IPV6_NEXT_HEADER_TCP 6
#define IPV6_NEXT_HEADER_UDP 17
@@ -212,6 +213,7 @@
#define ICMPV6_FLAGS_MANAGED 0x80
#define ICMPV6_FLAGS_OTHER_CONFIG 0x40
#define ICMPV6_OPTION_DEST_LINK_LAYER 2
#define ICMPV6_OPTION_PREFIX 3
#define ICMPV6_OPTION_DNS 25
@@ -284,6 +286,8 @@ static const ipv6addr_t ipv6_solicited_node_multicast = {0xFF, 0x02, 0x00, 0x00,
/* MAC Addresses */
static const uint8_t broadcast_mac[ETH_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
static const uint8_t slow_mac[ETH_ADDR_LEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02};
static const uint8_t all_hosts_mac[ETH_ADDR_LEN] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x01};
static const uint8_t all_routers_mac[ETH_ADDR_LEN] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x02};
typedef enum protocol_error_ {
PROTOCOL_SUCCESS = 0,
@@ -529,12 +533,20 @@ typedef struct bbl_mpls_ {
/*
* ISIS PDU
*/
typedef struct bbl_isis_s {
typedef struct bbl_isis_ {
uint8_t type;
uint8_t *pdu;
uint16_t pdu_len;
} bbl_isis_s;
typedef struct bbl_ldp_hello_ {
uint32_t lsr_id;
uint16_t label_space_id;
uint32_t msg_id;
uint32_t ipv4_transport_address;
uint16_t hold_time;
} bbl_ldp_hello_s;
typedef struct bbl_bbl_ {
uint16_t padding;
uint8_t type;
@@ -799,6 +811,7 @@ typedef struct bbl_icmpv6_ {
uint16_t data_len;
ipv6addr_t *dns1;
ipv6addr_t *dns2;
uint8_t *dst_mac;
} bbl_icmpv6_s;
typedef struct bbl_dhcpv6_ {
@@ -953,4 +966,4 @@ protocol_error_t
encode_ethernet(uint8_t *buf, uint16_t *len,
bbl_ethernet_header_s *eth);
#endif
#endif
+60 -16
View File
@@ -10,6 +10,7 @@
#include "bbl_session.h"
#include "bbl_stream.h"
#include "bbl_stats.h"
#include "bbl_dhcp.h"
#include "bbl_dhcpv6.h"
extern volatile bool g_teardown;
@@ -200,6 +201,50 @@ bbl_session_monkey_pppoe(bbl_session_s *session) {
}
}
static void
bbl_session_monkey_ipoe(bbl_session_s *session) {
switch(rand() % 256) {
case 10:
/* Clear session. */
bbl_session_clear(session);
break;
case 20:
/* Release DHCP */
if(session->dhcp_state != BBL_DHCP_RELEASE) {
session->dhcp_state = BBL_DHCP_RELEASE;
session->dhcp_xid = rand();
session->dhcp_request_timestamp.tv_sec = 0;
session->dhcp_request_timestamp.tv_nsec = 0;
session->dhcp_retry = 0;
session->send_requests |= BBL_SEND_DHCP_REQUEST;
bbl_session_tx_qnode_insert(session);
}
break;
case 30:
/* Release DHCPv6 */
if(session->dhcpv6_state != BBL_DHCP_RELEASE) {
session->dhcpv6_state = BBL_DHCP_RELEASE;
session->dhcpv6_xid = rand() & 0xffffff;
session->dhcpv6_request_timestamp.tv_sec = 0;
session->dhcpv6_request_timestamp.tv_nsec = 0;
session->dhcpv6_retry = 0;
session->send_requests |= BBL_SEND_DHCPV6_REQUEST;
bbl_session_tx_qnode_insert(session);
}
break;
case 40:
/* Restart DHCPv4 */
bbl_dhcp_restart(session);
break;
case 50:
/* Restart DHCPv6 */
bbl_dhcpv6_restart(session);
break;
default:
break;
}
}
void
bbl_session_monkey_job(timer_s *timer) {
bbl_session_s *session = timer->data;
@@ -218,6 +263,8 @@ bbl_session_monkey_job(timer_s *timer) {
if(session->access_type == ACCESS_TYPE_PPPOE) {
bbl_session_monkey_pppoe(session);
} else if(session->access_type == ACCESS_TYPE_IPOE) {
bbl_session_monkey_ipoe(session);
}
}
@@ -322,6 +369,7 @@ bbl_session_reset(bbl_session_s *session) {
session->dns2 = 0;
session->ipv6_prefix.len = 0;
session->delegated_ipv6_prefix.len = 0;
session->arp_resolved = false;
session->icmpv6_ra_received = false;
if(session->dhcpv6_state > BBL_DHCP_DISABLED) {
session->dhcpv6_state = BBL_DHCP_INIT;
@@ -503,24 +551,20 @@ bbl_session_update_state(bbl_session_s *session, session_state_t state)
if(g_teardown) {
g_ctx->sessions_terminated++;
} else {
if(session->access_type == ACCESS_TYPE_PPPOE) {
if(g_ctx->config.pppoe_reconnect) {
/* Increment flap counter */
session->stats.flapped++;
g_ctx->sessions_flapped++;
if(session->reconnect_delay) {
timer_add(&g_ctx->timer_root, &session->timer_reconnect, "RECONNECT",
session->reconnect_delay, 0, session, &bbl_session_reconnect_job);
} else {
state = BBL_IDLE;
bbl_session_reset(session);
CIRCLEQ_INSERT_TAIL(&g_ctx->sessions_idle_qhead, session, session_idle_qnode);
}
if((session->access_type == ACCESS_TYPE_PPPOE && g_ctx->config.pppoe_reconnect) ||
(session->access_type == ACCESS_TYPE_IPOE && g_ctx->config.sessions_reconnect)) {
/* Increment flap counter */
session->stats.flapped++;
g_ctx->sessions_flapped++;
if(session->reconnect_delay) {
timer_add(&g_ctx->timer_root, &session->timer_reconnect, "RECONNECT",
session->reconnect_delay, 0, session, &bbl_session_reconnect_job);
} else {
g_ctx->sessions_terminated++;
state = BBL_IDLE;
bbl_session_reset(session);
CIRCLEQ_INSERT_TAIL(&g_ctx->sessions_idle_qhead, session, session_idle_qnode);
}
} else {
/* IPoE */
g_ctx->sessions_terminated++;
}
}
@@ -1384,7 +1428,7 @@ bbl_session_ctrl_terminate(int fd, uint32_t session_id, json_t *arguments)
/* Terminate single matching session ... */
session = bbl_session_get(session_id);
if(session) {
json_unpack(arguments, "{s:i}", "reconnect-delay", &session->reconnect_delay);
json_unpack(arguments, "{s:i}", "reconnect-delay", &reconnect_delay);
if(reconnect_delay > 0) {
session->reconnect_delay = reconnect_delay;
}
+42 -3
View File
@@ -580,9 +580,13 @@ bbl_stream_build_network_packet(bbl_stream_s *stream)
eth.vlan_inner = 0;
/* Add MPLS labels */
if(config->tx_mpls1) {
if(config->tx_mpls1 || stream->ldp_entry) {
eth.mpls = &mpls1;
mpls1.label = config->tx_mpls1_label;
if(stream->ldp_entry) {
mpls1.label = stream->ldp_entry->label;
} else {
mpls1.label = config->tx_mpls1_label;
}
mpls1.exp = config->tx_mpls1_exp;
mpls1.ttl = config->tx_mpls1_ttl;
if(config->tx_mpls2) {
@@ -1143,6 +1147,32 @@ bbl_stream_final()
dict_itor_free(itor);
}
static bool
bbl_stream_ldp_lookup(bbl_stream_s *stream)
{
if(!stream->ldp_entry) {
if(stream->config->ipv4_ldp_lookup_address) {
stream->ldp_entry = ldb_db_lookup_ipv4(
stream->network_interface->ldp_adjacency->instance,
stream->config->ipv4_ldp_lookup_address);
}
}
if(!(stream->ldp_entry && stream->ldp_entry->active)) {
return false;
}
if(stream->ldp_entry->version != stream->ldp_entry_version) {
stream->ldp_entry_version = stream->ldp_entry->version;
/* Free packet if LDP entry has changed. */
if(stream->tx_buf) {
free(stream->tx_buf);
stream->tx_buf = NULL;
stream->tx_len = 0;
}
}
return true;
}
static bool
bbl_stream_can_send(bbl_stream_s *stream)
{
@@ -1155,10 +1185,13 @@ bbl_stream_can_send(bbl_stream_s *stream)
stream->stop) {
return false;
}
if(stream->ldp_lookup) {
return bbl_stream_ldp_lookup(stream);
}
return true;
}
/* Free packet if not ready to send */
/* Free packet if not ready to send. */
if(stream->tx_buf) {
free(stream->tx_buf);
stream->tx_buf = NULL;
@@ -1594,6 +1627,9 @@ bbl_stream_session_add(bbl_stream_config_s *config, bbl_session_s *session)
if(network_interface) {
stream->network_interface = network_interface;
stream->tx_interface = network_interface->interface;
if(config->ipv4_ldp_lookup_address && network_interface->ldp_adjacency) {
stream->ldp_lookup = true;
}
bbl_stream_add(stream);
if(stream->session_traffic) {
g_ctx->stats.session_traffic_flows++;
@@ -1732,6 +1768,9 @@ bbl_stream_init() {
stream->network_interface = network_interface;
stream->tx_interface = network_interface->interface;
stream->tx_interval = tx_interval;
if(config->ipv4_ldp_lookup_address && network_interface->ldp_adjacency) {
stream->ldp_lookup = true;
}
result = dict_insert(g_ctx->stream_flow_dict, &stream->flow_id);
if(!result.inserted) {
LOG(ERROR, "Failed to insert RAW stream %s\n", config->name);
+4
View File
@@ -30,6 +30,7 @@ typedef struct bbl_stream_config_
uint8_t priority; /* IPv4 TOS or IPv6 TC */
uint8_t vlan_priority;
uint32_t ipv4_ldp_lookup_address;
uint32_t ipv4_access_src_address; /* overwrite default IPv4 access address */
ipv6addr_t ipv6_access_src_address; /* overwrite default IPv6 access address */
uint32_t ipv4_network_address; /* overwrite default IPv4 network address */
@@ -105,6 +106,9 @@ typedef struct bbl_stream_
bool stop;
bool reset;
bool lag;
bool ldp_lookup;
uint32_t ldp_entry_version;
ldp_db_entry_s *ldp_entry;
bool send_window_active;
uint64_t send_window_start_packets;
+171 -39
View File
@@ -12,7 +12,8 @@
#endif
const char *
tcp_err_string(err_t err) {
tcp_err_string(err_t err)
{
switch(err) {
case ERR_OK: return "ok";
case ERR_MEM: return "out of memory error";
@@ -43,14 +44,17 @@ tcp_err_string(err_t err) {
* @param tcpc TCP context
*/
void
bbl_tcp_close(bbl_tcp_ctx_s *tcpc) {
bbl_tcp_close(bbl_tcp_ctx_s *tcpc)
{
if(tcpc) {
if(tcpc->pcb) {
tcp_arg(tcpc->pcb, NULL);
tcp_sent(tcpc->pcb, NULL);
tcp_recv(tcpc->pcb, NULL);
tcp_err(tcpc->pcb, NULL);
tcp_poll(tcpc->pcb, NULL, 0);
if(!tcpc->listen) {
tcp_arg(tcpc->pcb, NULL);
tcp_sent(tcpc->pcb, NULL);
tcp_recv(tcpc->pcb, NULL);
tcp_err(tcpc->pcb, NULL);
tcp_poll(tcpc->pcb, NULL, 0);
}
tcp_close(tcpc->pcb);
}
tcpc->state = BBL_TCP_STATE_CLOSED;
@@ -75,35 +79,36 @@ bbl_tcp_ctx_free(bbl_tcp_ctx_s *tcpc) {
}
static bbl_tcp_ctx_s *
bbl_tcp_ctx_new(bbl_network_interface_s *interface) {
bbl_tcp_ctx_s *tcp;
bbl_tcp_ctx_new(bbl_network_interface_s *interface)
{
bbl_tcp_ctx_s *tcpc;
/* Init TCP context */
tcp = calloc(1, sizeof(bbl_tcp_ctx_s));
if(!tcp) {
tcpc = calloc(1, sizeof(bbl_tcp_ctx_s));
if(!tcpc) {
return NULL;
}
tcp->interface = interface;
tcpc->interface = interface;
/* Init TCP PCB */
tcp->pcb = tcp_new();
if(!tcp->pcb) {
free(tcp);
tcpc->pcb = tcp_new();
if(!tcpc->pcb) {
free(tcpc);
return NULL;
}
/* Bind local network interface */
tcp_bind_netif(tcp->pcb, &interface->netif);
tcp_bind_netif(tcpc->pcb, &interface->netif);
/* Add BBL TCP context as argument */
tcp_arg(tcp->pcb, tcp);
tcp_arg(tcpc->pcb, tcpc);
return tcp;
return tcpc;
}
err_t
bbl_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len) {
bbl_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
bbl_tcp_ctx_s *tcpc = arg;
uint16_t tx = tcp_sndbuf(tpcb);
err_t result = ERR_OK;
@@ -138,7 +143,8 @@ bbl_tcp_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len) {
}
err_t
bbl_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
bbl_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
bbl_tcp_ctx_s *tcpc = arg;
struct pbuf *_p;
@@ -172,7 +178,8 @@ bbl_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
* ERR_RST: the connection was reset by the remote host
*/
void
bbl_tcp_error_cb(void *arg, err_t err) {
bbl_tcp_error_cb(void *arg, err_t err)
{
bbl_tcp_ctx_s *tcpc = arg;
tcpc->state = BBL_TCP_STATE_CLOSED;
tcpc->pcb = NULL;
@@ -207,7 +214,8 @@ bbl_tcp_error_cb(void *arg, err_t err) {
* callback function!
*/
err_t
bbl_tcp_poll_cb(void *arg, struct tcp_pcb *tpcb) {
bbl_tcp_poll_cb(void *arg, struct tcp_pcb *tpcb)
{
bbl_tcp_ctx_s *tcpc = arg;
if(tcpc->poll_cb) {
return (tcpc->poll_cb)(tcpc->arg, tpcb);
@@ -216,7 +224,8 @@ bbl_tcp_poll_cb(void *arg, struct tcp_pcb *tpcb) {
}
err_t
bbl_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err) {
bbl_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{
bbl_tcp_ctx_s *tcpc = arg;
UNUSED(err); /* TODO!!! */
@@ -250,6 +259,123 @@ bbl_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err) {
return ERR_OK;
}
err_t
bbl_tcp_ipv4_listen_accepted(void *arg, struct tcp_pcb *tpcb, err_t err)
{
bbl_tcp_ctx_s *listen = arg;
bbl_tcp_ctx_s *tcpc;
UNUSED(err); /* TODO!!! */
tcpc = calloc(1, sizeof(bbl_tcp_ctx_s));
if(!tcpc) {
return ERR_MEM;
}
tcpc->interface = listen->interface;
tcpc->af = listen->af;
tcpc->local_port = listen->local_port;
tcpc->remote_port = tpcb->remote_port;
tcpc->accepted_cb = listen->accepted_cb;
tcpc->connected_cb = listen->connected_cb;
tcpc->idle_cb = listen->idle_cb;
tcpc->receive_cb = listen->receive_cb;
tcpc->error_cb = listen->error_cb;
tcpc->poll_cb = listen->poll_cb;
tcpc->poll_interval = listen->poll_interval;
tcpc->arg = listen->arg;
tcpc->pcb = tpcb;
tcp_arg(tpcb, tcpc);
/* Add send/receive callback functions. */
tcp_sent(tpcb, bbl_tcp_sent_cb);
tcp_recv(tpcb, bbl_tcp_recv_cb);
if(tcpc->poll_cb && tcpc->poll_interval) {
tcp_poll(tpcb, bbl_tcp_poll_cb, tcpc->poll_interval);
}
tcp_err(tpcb, bbl_tcp_error_cb);
if(tcpc->af == AF_INET) {
tcpc->local_ipv4 = tpcb->local_ip.u_addr.ip4.addr;
tcpc->remote_ipv4 = tpcb->remote_ip.u_addr.ip4.addr;
LOG(TCP, "TCP (%s %s:%u - %s:%u) session accepted\n",
tcpc->interface->name,
format_ipv4_address(&tcpc->local_ipv4), tcpc->local_port,
format_ipv4_address(&tcpc->remote_ipv4), tcpc->remote_port);
} else {
memcpy(tcpc->local_ipv6, tpcb->local_ip.u_addr.ip6.addr, IPV6_ADDR_LEN);
memcpy(tcpc->remote_ipv6, tpcb->remote_ip.u_addr.ip6.addr, IPV6_ADDR_LEN);
LOG(TCP, "TCP (%s %s:%u - %s:%u) session accepted\n",
tcpc->interface->name,
format_ipv6_address(&tcpc->local_ipv6), tcpc->local_port,
format_ipv6_address(&tcpc->remote_ipv6), tcpc->remote_port);
}
/* Call application TCP accepted callback function. */
if(listen->accepted_cb) {
if((listen->accepted_cb)(tcpc, listen->arg) != ERR_OK) {
free(tcpc);
tcp_abort(tpcb);
return ERR_ABRT;
};
}
tcpc->state = BBL_TCP_STATE_IDLE;
/* Call application TCP connected callback function. */
if(tcpc->connected_cb) {
(tcpc->connected_cb)(tcpc->arg);
}
bbl_tcp_sent_cb(tcpc, tpcb, 0);
return ERR_OK;
}
/**
* bbl_tcp_ipv4_listen
*
* @param interface interface
* @param address local address
* @param port local port
* @return TCP context
*/
bbl_tcp_ctx_s *
bbl_tcp_ipv4_listen(bbl_network_interface_s *interface, ipv4addr_t *address, uint16_t port)
{
bbl_tcp_ctx_s *tcpc;
if(!g_ctx->tcp) {
/* TCP not enabled! */
return NULL;
}
tcpc = bbl_tcp_ctx_new(interface);
if(!tcpc) {
return NULL;
}
/* Bind local IP address and port */
if(tcp_bind(tcpc->pcb, (const ip_addr_t*)address, port) != ERR_OK) {
bbl_tcp_ctx_free(tcpc);
}
tcpc->pcb = tcp_listen(tcpc->pcb);
tcp_accept(tcpc->pcb, bbl_tcp_ipv4_listen_accepted);
tcpc->listen = true;
tcpc->af = AF_INET;
tcpc->local_port = port;
tcpc->local_ipv4 = *address;
tcpc->pcb->local_ip.type = IPADDR_TYPE_V4;
tcpc->pcb->remote_ip.type = IPADDR_TYPE_V4;
tcpc->state = BBL_TCP_STATE_LISTEN;
LOG(TCP, "TCP (%s %s:%u) listen\n",
interface->name,
format_ipv4_address(&tcpc->local_ipv4), tcpc->local_port);
return tcpc;
}
/**
* bbl_tcp_ipv4_connect
*
@@ -260,8 +386,8 @@ bbl_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err) {
* @return TCP context
*/
bbl_tcp_ctx_s *
bbl_tcp_ipv4_connect(bbl_network_interface_s *interface, ipv4addr_t *src, ipv4addr_t *dst, uint16_t port) {
bbl_tcp_ipv4_connect(bbl_network_interface_s *interface, ipv4addr_t *src, ipv4addr_t *dst, uint16_t port)
{
bbl_tcp_ctx_s *tcpc;
if(!g_ctx->tcp) {
@@ -349,8 +475,8 @@ bbl_tcp_ipv4_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth,
* @return TCP context
*/
bbl_tcp_ctx_s *
bbl_tcp_ipv6_connect(bbl_network_interface_s *interface, ipv6addr_t *src, ipv6addr_t *dst, uint16_t port) {
bbl_tcp_ipv6_connect(bbl_network_interface_s *interface, ipv6addr_t *src, ipv6addr_t *dst, uint16_t port)
{
if(!g_ctx->tcp) {
/* TCP not enabled! */
return NULL;
@@ -371,7 +497,8 @@ bbl_tcp_ipv6_connect(bbl_network_interface_s *interface, ipv6addr_t *src, ipv6ad
* @param interface receiving interface
*/
void
bbl_tcp_ipv6_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth, bbl_ipv6_s *ipv6) {
bbl_tcp_ipv6_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth, bbl_ipv6_s *ipv6)
{
struct pbuf *pbuf;
UNUSED(eth);
@@ -401,8 +528,8 @@ bbl_tcp_ipv6_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth,
* @return true if successful
*/
bool
bbl_tcp_send(bbl_tcp_ctx_s *tcpc, uint8_t *buf, uint32_t len) {
bbl_tcp_send(bbl_tcp_ctx_s *tcpc, uint8_t *buf, uint32_t len)
{
if(tcpc->state == BBL_TCP_STATE_SENDING) {
return false;
}
@@ -421,7 +548,8 @@ bbl_tcp_send(bbl_tcp_ctx_s *tcpc, uint8_t *buf, uint32_t len) {
* Function of type netif_output_fn
*/
err_t
bbl_tcp_netif_output_ipv4(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr) {
bbl_tcp_netif_output_ipv4(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr)
{
bbl_network_interface_s *interface = netif->state;
bbl_ethernet_header_s eth = {0};
UNUSED(ipaddr);
@@ -443,9 +571,9 @@ bbl_tcp_netif_output_ipv4(struct netif *netif, struct pbuf *p, const ip4_addr_t
* Function of type netif_output_ip6_fn
*/
err_t
bbl_tcp_netif_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr) {
bbl_tcp_ctx_s *tcpc = netif->state;
bbl_network_interface_s *interface = tcpc->interface;
bbl_tcp_netif_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
{
bbl_network_interface_s *interface = netif->state;
bbl_ethernet_header_s eth = {0};
UNUSED(ipaddr);
@@ -463,7 +591,8 @@ bbl_tcp_netif_output_ipv6(struct netif *netif, struct pbuf *p, const ip6_addr_t
}
err_t
bbl_tcp_netif_init(struct netif *netif) {
bbl_tcp_netif_init(struct netif *netif)
{
netif->output = bbl_tcp_netif_output_ipv4;
netif->output_ip6 = bbl_tcp_netif_output_ipv6;
netif_set_up(netif);
@@ -480,7 +609,8 @@ bbl_tcp_netif_init(struct netif *netif) {
* @return return true if successfully
*/
bool
bbl_tcp_network_interface_init(bbl_network_interface_s *interface, bbl_network_config_s *config) {
bbl_tcp_network_interface_init(bbl_network_interface_s *interface, bbl_network_config_s *config)
{
if(!g_ctx->tcp) {
/* TCP not enabled! */
return true;
@@ -495,7 +625,8 @@ bbl_tcp_network_interface_init(bbl_network_interface_s *interface, bbl_network_c
}
void
bbl_tcp_simer(timer_s *timer) {
bbl_tcp_simer(timer_s *timer)
{
UNUSED(timer);
sys_check_timeouts();
}
@@ -506,7 +637,8 @@ bbl_tcp_simer(timer_s *timer) {
* Init TCP (LwIP) and start global TCP timer job.
*/
void
bbl_tcp_init() {
bbl_tcp_init()
{
if(!g_ctx->tcp) {
/* TCP not enabled! */
return;
+7
View File
@@ -19,11 +19,13 @@
typedef enum bbl_tcp_state_ {
BBL_TCP_STATE_CLOSED,
BBL_TCP_STATE_LISTEN,
BBL_TCP_STATE_CONNECTING,
BBL_TCP_STATE_IDLE,
BBL_TCP_STATE_SENDING,
} bbl_tcp_state_t;
typedef err_t (*bbl_tcp_accepted_fn)(bbl_tcp_ctx_s *tcpc, void *arg);
typedef void (*bbl_tcp_callback_fn)(void *arg);
typedef void (*bbl_tcp_receive_fn)(void *arg, uint8_t *buf, uint16_t len);
typedef void (*bbl_tcp_error_fn)(void *arg, err_t err);
@@ -33,6 +35,7 @@ typedef struct bbl_tcp_ctx_
{
bbl_network_interface_s *interface;
bool listen;
uint8_t af; /* AF_INET or AF_INET6 */
uint16_t local_port;
@@ -45,6 +48,7 @@ typedef struct bbl_tcp_ctx_
struct tcp_pcb *pcb;
bbl_tcp_accepted_fn accepted_cb; /* accepted callback (listen) */
bbl_tcp_callback_fn connected_cb; /* application connected callback */
bbl_tcp_callback_fn idle_cb; /* application idle callback */
@@ -82,6 +86,9 @@ bbl_tcp_close(bbl_tcp_ctx_s *tcpc);
void
bbl_tcp_ctx_free(bbl_tcp_ctx_s *tcpc);
bbl_tcp_ctx_s *
bbl_tcp_ipv4_listen(bbl_network_interface_s *interface, ipv4addr_t *address, uint16_t port);
bbl_tcp_ctx_s *
bbl_tcp_ipv4_connect(bbl_network_interface_s *interface, ipv4addr_t *src, ipv4addr_t *dst, uint16_t port);
+3
View File
@@ -1421,6 +1421,9 @@ bbl_tx_encode_network_packet(bbl_network_interface_s *interface, uint8_t *buf, u
} else if(interface->send_requests & BBL_IF_SEND_ISIS_P2P_HELLO) {
interface->send_requests &= ~BBL_IF_SEND_ISIS_P2P_HELLO;
result = isis_p2p_hello_encode(interface, buf, len, &eth);
} else if(interface->send_requests & BBL_IF_SEND_LDP_HELLO) {
interface->send_requests &= ~BBL_IF_SEND_LDP_HELLO;
result = ldp_hello_encode(interface, buf, len, &eth);
} else {
interface->send_requests = 0;
}
+2 -2
View File
@@ -60,11 +60,11 @@ bgp_ctrl_session_json(bgp_session_s *session)
"local-address", format_ipv4_address(&session->ipv4_local_address),
"local-id", format_ipv4_address(&session->config->id),
"local-as", session->config->local_as,
"local-holdtime", session->config->holdtime,
"local-hold-time", session->config->hold_time,
"peer-address", format_ipv4_address(&session->ipv4_peer_address),
"peer-id", format_ipv4_address(&session->peer.id),
"peer-as", session->peer.as,
"peer-holdtime", session->peer.holdtime,
"peer-hold-time", session->peer.hold_time,
"state", bgp_session_state_string(session->state),
"raw-update-state", raw_update_state(session),
"raw-update-file", raw_update_file,
+3 -6
View File
@@ -16,7 +16,7 @@
#define BGP_MAX_MESSAGE_SIZE 4096U
#define BGP_BUF_SIZE 256*1024
#define BGP_DEFAULT_AS 65000
#define BGP_DEFAULT_HOLDTIME 90
#define BGP_DEFAULT_HOLD_TIME 90
#define BGP_DEFAULT_TEARDOWN_TIME 5
#define BGP_MSG_OPEN 1
@@ -58,14 +58,13 @@ typedef struct bgp_config_ {
uint32_t id;
uint32_t local_as;
uint32_t peer_as;
uint16_t holdtime;
uint16_t hold_time;
uint16_t teardown_time;
bool reconnect;
bool start_traffic;
char *network_interface;
char *mrt_file;
char *raw_update_file;
/* Pointer to next instance */
@@ -84,8 +83,6 @@ typedef struct bgp_session_ {
bbl_tcp_ctx_s *tcpc;
struct timer_ *connect_timer;
struct timer_ *send_open_timer;
struct timer_ *open_sent_timer;
struct timer_ *keepalive_timer;
struct timer_ *hold_timer;
struct timer_ *close_timer;
@@ -101,7 +98,7 @@ typedef struct bgp_session_ {
struct {
uint32_t as;
uint32_t id;
uint16_t holdtime;
uint16_t hold_time;
} peer;
struct {
+1 -1
View File
@@ -69,7 +69,7 @@ bgp_push_open_message(bgp_session_s *session)
} else {
push_be_uint(buffer, 2, session->config->local_as);
}
push_be_uint(buffer, 2, session->config->holdtime); /* holdtime */
push_be_uint(buffer, 2, session->config->hold_time); /* hold-time */
push_data(buffer, (uint8_t*)&session->config->id, 4); /* BGP ID */
/* Optional parameters */
+22 -18
View File
@@ -84,35 +84,33 @@ bgp_decode_error(bgp_session_s *session)
bgp_session_close(session);
}
static void
static bool
bgp_open(bgp_session_s *session, uint8_t *start, uint16_t length)
{
if(length < 28) {
bgp_decode_error(session);
return;
return false;
}
session->peer.as = read_be_uint(start+20, 2);
session->peer.holdtime = read_be_uint(start+22, 2);
session->peer.hold_time = read_be_uint(start+22, 2);
session->peer.id = read_be_uint(start+24, 4);
LOG(BGP, "BGP (%s %s - %s) open message received with peer AS: %u, holdtime: %us\n",
LOG(BGP, "BGP (%s %s - %s) open message received with peer AS: %u, hold-time: %us\n",
session->interface->name,
format_ipv4_address(&session->ipv4_local_address),
format_ipv4_address(&session->ipv4_peer_address),
session->peer.as, session->peer.holdtime);
session->peer.as, session->peer.hold_time);
bgp_session_state_change(session, BGP_OPENCONFIRM);
return;
return true;
}
static void
static bool
bgp_notification(bgp_session_s *session, uint8_t *start, uint16_t length)
{
uint8_t error_code, error_subcode;
if(length < 21) {
bgp_decode_error(session);
return;
return false;
}
error_code = *(start+19);
@@ -160,7 +158,7 @@ bgp_notification(bgp_session_s *session, uint8_t *start, uint16_t length)
}
session->error_code = 0;
bgp_session_close(session);
return;
return true;
}
/*
@@ -185,7 +183,7 @@ bgp_rebase_read_buffer(bgp_session_s *session)
}
static void
bpg_read(bgp_session_s *session)
bgp_read(bgp_session_s *session)
{
uint32_t size;
uint16_t length;
@@ -206,7 +204,7 @@ bpg_read(bgp_session_s *session)
if(length < BGP_MIN_MESSAGE_SIZE ||
length > BGP_MAX_MESSAGE_SIZE) {
bgp_decode_error(session);
break;
return;
}
/* Full message on the wire to consume? */
@@ -223,12 +221,18 @@ bpg_read(bgp_session_s *session)
format_ipv4_address(&session->ipv4_peer_address),
keyval_get_key(bgp_msg_names, type));
switch (type) {
switch(type) {
case BGP_MSG_OPEN:
bgp_open(session, start, length);
if(!bgp_open(session, start, length)) {
bgp_decode_error(session);
return;
}
break;
case BGP_MSG_NOTIFICATION:
bgp_notification(session, start, length);
if(!bgp_notification(session, start, length)) {
bgp_decode_error(session);
return;
}
return;
case BGP_MSG_KEEPALIVE:
session->stats.keepalive_rx++;
@@ -244,7 +248,7 @@ bpg_read(bgp_session_s *session)
}
/* Reset hold timer */
bgp_restart_hold_timer(session, session->config->holdtime);
bgp_restart_hold_timer(session, session->config->hold_time);
/* Progress pointer to next BGP message */
buffer->start_idx += length;
@@ -274,6 +278,6 @@ bgp_receive_cb(void *arg, uint8_t *buf, uint16_t len)
memcpy(buffer->data+buffer->idx, buf, len);
buffer->idx+=len;
} else {
bpg_read(session);
bgp_read(session);
}
}
+5 -7
View File
@@ -176,10 +176,10 @@ bgp_session_state_established(bgp_session_s *session)
clock_gettime(CLOCK_MONOTONIC, &session->established_timestamp);
/* Start BGP keepalive */
if(session->peer.holdtime < session->config->holdtime) {
keepalive_interval = session->peer.holdtime/2U;
if(session->peer.hold_time < session->config->hold_time) {
keepalive_interval = session->peer.hold_time/2U;
} else {
keepalive_interval = session->config->holdtime/2U;
keepalive_interval = session->config->hold_time/2U;
}
if(!keepalive_interval) {
keepalive_interval = 1;
@@ -216,7 +216,7 @@ bgp_session_state_change(bgp_session_s *session, bgp_state_t new_state)
session->state = new_state;
switch (new_state) {
switch(new_state) {
case BGP_OPENSENT:
bgp_session_state_opensent(session);
break;
@@ -320,7 +320,7 @@ bgp_session_connect(bgp_session_s *session, time_t delay)
session->peer.as = 0;
session->peer.id = 0;
session->peer.holdtime = 0;
session->peer.hold_time = 0;
session->stats.message_rx = 0;
session->stats.message_tx = 0;
@@ -376,8 +376,6 @@ bgp_session_close(bgp_session_s *session)
/* Stop all timers */
timer_del(session->connect_timer);
timer_del(session->send_open_timer);
timer_del(session->open_sent_timer);
timer_del(session->keepalive_timer);
timer_del(session->hold_timer);
timer_del(session->update_timer);
+1 -1
View File
@@ -72,7 +72,7 @@ io_packet_mmap_rx_job(timer_s *timer)
io->stats.bytes += io->buf_len;
decode_result = decode_ethernet(io->buf, io->buf_len, g_ctx->sp, SCRATCHPAD_LEN, &eth);
if(decode_result == PROTOCOL_SUCCESS) {
vlan = tphdr->tp_vlan_tci & ETH_VLAN_ID_MAX;
vlan = tphdr->tp_vlan_tci & BBL_ETH_VLAN_ID_MAX;
if(vlan && eth->vlan_outer != vlan) {
/* The outer VLAN is stripped from header */
eth->vlan_inner = eth->vlan_outer;
+3 -3
View File
@@ -66,7 +66,7 @@ io_thread_rx_handler(io_thread_s *thread, io_handle_s *io)
/** Process */
decode_result = decode_ethernet(io->buf, io->buf_len, thread->sp, SCRATCHPAD_LEN, &eth);
if(decode_result == PROTOCOL_SUCCESS) {
vlan = io->vlan_tci & ETH_VLAN_ID_MAX;
vlan = io->vlan_tci & BBL_ETH_VLAN_ID_MAX;
if(eth->vlan_outer != vlan) {
/* The outer VLAN is stripped from header */
eth->vlan_inner = eth->vlan_outer;
@@ -114,7 +114,7 @@ io_thread_main_rx_job(timer_s *timer)
while((slot = bbl_txq_read_slot(thread->txq))) {
decode_result = decode_ethernet(slot->packet, slot->packet_len, g_ctx->sp, SCRATCHPAD_LEN, &eth);
if(decode_result == PROTOCOL_SUCCESS) {
vlan = slot->vlan_tci & ETH_VLAN_ID_MAX;
vlan = slot->vlan_tci & BBL_ETH_VLAN_ID_MAX;
if(vlan && eth->vlan_outer != vlan) {
/* Restore outer VLAN */
eth->vlan_inner = eth->vlan_outer;
@@ -337,4 +337,4 @@ io_thread_stop_all()
pthread_join(thread->thread, NULL);
thread = thread->next;
}
}
}
+3 -3
View File
@@ -111,20 +111,20 @@ isis_handler_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth)
result = isis_pdu_load(&pdu, isis->pdu, isis->pdu_len);
if(result != PROTOCOL_SUCCESS) {
LOG(ISIS, "ISIS RX %s PDU decode error on interface %s\n",
isis_pdu_sype_string(pdu.pdu_type), interface->name);
isis_pdu_type_string(pdu.pdu_type), interface->name);
interface->stats.isis_rx_error++;
return;
}
if(!isis_pdu_validate_checksum(&pdu)) {
LOG(ISIS, "ISIS RX %s PDU checksum error on interface %s\n",
isis_pdu_sype_string(pdu.pdu_type), interface->name);
isis_pdu_type_string(pdu.pdu_type), interface->name);
interface->stats.isis_rx_error++;
return;
}
LOG(DEBUG, "ISIS RX %s on interface %s\n",
isis_pdu_sype_string(pdu.pdu_type), interface->name);
isis_pdu_type_string(pdu.pdu_type), interface->name);
switch (pdu.pdu_type) {
case ISIS_PDU_P2P_HELLO:
+1 -1
View File
@@ -140,7 +140,7 @@ isis_adjacency_down(isis_adjacency_s *adjacency)
timer_del(adjacency->timer_retry);
timer_del(adjacency->timer_csnp);
timer_del(adjacency->timer_csnp_next);
timer_del(adjacency->timer_holding);
timer_del(adjacency->timer_hold);
if(g_ctx->routing_sessions) g_ctx->routing_sessions--;
}
+2 -1
View File
@@ -147,8 +147,9 @@ isis_csnp_job(timer_s *timer)
isis.pdu_len = pdu.pdu_len;
if(bbl_txq_to_buffer(adjacency->interface->txq, &eth) == BBL_TXQ_OK) {
LOG(DEBUG, "ISIS TX %s on interface %s\n",
isis_pdu_sype_string(isis.type), adjacency->interface->name);
isis_pdu_type_string(isis.type), adjacency->interface->name);
adjacency->stats.csnp_tx++;
adjacency->interface->stats.isis_tx++;
/* Clear PSNP tree after CSNP was send */
hb_tree_clear(adjacency->psnp_tree, isis_psnp_free);
timer_del(adjacency->timer_psnp_next);
+2 -2
View File
@@ -21,7 +21,7 @@ isis_ctrl_adjacency(isis_adjacency_s *adjacency)
peer = json_pack("{ss si}",
"system-id", isis_system_id_to_str(adjacency->peer->system_id),
"holding-timer", adjacency->peer->holding_time);
"hold-timer", adjacency->peer->hold_time);
root = json_pack("{ss ss ss si ss so}",
"interface", adjacency->interface->name,
@@ -50,7 +50,7 @@ isis_ctrl_adjacency_p2p(isis_adjacency_p2p_s *adjacency)
peer = json_pack("{ss si}",
"system-id", isis_system_id_to_str(adjacency->peer->system_id),
"holding-timer", adjacency->peer->holding_time);
"hold-timer", adjacency->peer->hold_time);
root = json_pack("{ss ss, ss si ss so}",
"interface", adjacency->interface->name,
+6 -6
View File
@@ -62,7 +62,7 @@
#define ISIS_DEFAULT_HELLO_INTERVAL 10
#define ISIS_DEFAULT_CSNP_INTERVAL 30
#define ISIS_DEFAULT_HOLDING_TIME 30
#define ISIS_DEFAULT_HOLD_TIME 30
#define ISIS_DEFAULT_LSP_LIFETIME 65535
#define ISIS_DEFAULT_LSP_RETRY_IVL 5
#define ISIS_DEFAULT_LSP_REFRESH_IVL 300
@@ -130,7 +130,7 @@ typedef enum isis_pdu_type_ {
/* IS-IS TLV Codepoints
* https://www.iana.org/assignments/isis-tlv-codepoints/isis-tlv-codepoints.xhtml
*/
typedef enum isis_tlv_sype_ {
typedef enum isis_tlv_type_ {
ISIS_TLV_AREA_ADDRESSES = 1,
ISIS_TLV_PADDING = 8,
ISIS_TLV_LSP_ENTRIES = 9,
@@ -145,7 +145,7 @@ typedef enum isis_tlv_sype_ {
ISIS_TLV_IPV6_REACHABILITY = 236,
ISIS_TLV_P2P_ADJACENCY_STATE = 240,
ISIS_TLV_ROUTER_CAPABILITY = 242
} isis_tlv_sype;
} isis_tlv_type;
/* STRUCTURES ... */
@@ -203,7 +203,7 @@ typedef struct isis_config_ {
uint16_t lsp_refresh_interval;
uint16_t lsp_lifetime;
uint16_t hello_interval;
uint16_t holding_time;
uint16_t hold_time;
uint16_t teardown_time;
const char *hostname;
@@ -238,7 +238,7 @@ typedef struct isis_config_ {
typedef struct isis_peer_ {
uint8_t level;
uint8_t system_id[ISIS_SYSTEM_ID_LEN];
uint16_t holding_time;
uint16_t hold_time;
char *hostname;
} isis_peer_s;
@@ -260,7 +260,7 @@ typedef struct isis_adjacency_ {
struct timer_ *timer_csnp;
struct timer_ *timer_csnp_next;
struct timer_ *timer_psnp_next;
struct timer_ *timer_holding;
struct timer_ *timer_hold;
bool timer_psnp_started;
+1
View File
@@ -291,6 +291,7 @@ isis_lsp_sx_job(timer_s *timer)
entry->tx_timestamp.tv_sec = now.tv_sec;
entry->tx_timestamp.tv_nsec = now.tv_nsec;
adjacency->stats.lsp_tx++;
adjacency->interface->stats.isis_tx++;
if(window) window--;
if(window == 0) break;
}
+12 -11
View File
@@ -16,7 +16,7 @@ isis_p2p_hello_timeout(timer_s *timer)
}
void
isis_p2p_holding_timeout(timer_s *timer)
isis_p2p_hold_timeout(timer_s *timer)
{
isis_adjacency_s *adjacency = timer->data;
@@ -24,7 +24,7 @@ isis_p2p_holding_timeout(timer_s *timer)
return;
}
LOG(ISIS, "ISIS %s holding timeout to %s on interface %s\n",
LOG(ISIS, "ISIS %s hold timeout to %s on interface %s\n",
isis_level_string(adjacency->level),
isis_system_id_to_str(adjacency->peer->system_id),
adjacency->interface->name);
@@ -36,16 +36,16 @@ isis_p2p_holding_timeout(timer_s *timer)
}
static void
isis_p2p_restart_holding_timers(bbl_network_interface_s *interface)
isis_p2p_restart_hold_timers(bbl_network_interface_s *interface)
{
isis_adjacency_s *adjacency;
for(int i=0; i<ISIS_LEVELS; i++) {
adjacency = interface->isis_adjacency[i];
if(adjacency) {
timer_add(&g_ctx->timer_root, &adjacency->timer_holding,
"ISIS holding", adjacency->peer->holding_time, 0, adjacency,
&isis_p2p_holding_timeout);
timer_add(&g_ctx->timer_root, &adjacency->timer_hold,
"ISIS Hold", adjacency->peer->hold_time, 0, adjacency,
&isis_p2p_hold_timeout);
}
}
}
@@ -77,7 +77,7 @@ isis_p2p_hello_encode(bbl_network_interface_s *interface,
/* Start next timer ... */
timer_add(&g_ctx->timer_root, &interface->timer_isis_hello,
"ISIS hello", config->hello_interval, 0, interface,
"ISIS Hello", config->hello_interval, 0, interface,
&isis_p2p_hello_timeout);
if(config->level1_auth && config->level1_key) {
@@ -93,7 +93,7 @@ isis_p2p_hello_encode(bbl_network_interface_s *interface,
/* PDU header */
isis_pdu_add_u8(&pdu, adjacency->level);
isis_pdu_add_bytes(&pdu, config->system_id, ISIS_SYSTEM_ID_LEN);
isis_pdu_add_u16(&pdu, config->holding_time);
isis_pdu_add_u16(&pdu, config->hold_time);
isis_pdu_add_u16(&pdu, 0);
isis_pdu_add_u8(&pdu, 0x1);
/* TLV section */
@@ -118,8 +118,9 @@ isis_p2p_hello_encode(bbl_network_interface_s *interface,
result = encode_ethernet(buf, len, eth);
if(result == PROTOCOL_SUCCESS) {
LOG(DEBUG, "ISIS TX %s on interface %s\n",
isis_pdu_sype_string(isis.type), interface->name);
isis_pdu_type_string(isis.type), interface->name);
adjacency->stats.hello_tx++;
adjacency->interface->stats.isis_tx++;
}
return result;
}
@@ -170,9 +171,9 @@ isis_p2p_hello_handler_rx(bbl_network_interface_s *interface, isis_pdu_s *pdu)
peer = adjacency->peer;
peer->level = *PDU_OFFSET(pdu, ISIS_OFFSET_P2P_HELLO_LEVEL) & 0x03;
memcpy(peer->system_id, PDU_OFFSET(pdu, ISIS_OFFSET_P2P_HELLO_SYSTEM_ID), ISIS_SYSTEM_ID_LEN);
peer->holding_time = be16toh(*(uint16_t*)PDU_OFFSET(pdu, ISIS_OFFSET_P2P_HELLO_HOLD_TIME));
peer->hold_time = be16toh(*(uint16_t*)PDU_OFFSET(pdu, ISIS_OFFSET_P2P_HELLO_HOLD_TIME));
isis_p2p_restart_holding_timers(interface);
isis_p2p_restart_hold_timers(interface);
tlv = isis_pdu_first_tlv(pdu);
while(tlv) {
+2 -1
View File
@@ -129,8 +129,9 @@ isis_psnp_job (timer_s *timer)
isis.pdu_len = pdu.pdu_len;
if(bbl_txq_to_buffer(adjacency->interface->txq, &eth) == BBL_TXQ_OK) {
LOG(DEBUG, "ISIS TX %s on interface %s\n",
isis_pdu_sype_string(isis.type), adjacency->interface->name);
isis_pdu_type_string(isis.type), adjacency->interface->name);
adjacency->stats.psnp_tx++;
adjacency->interface->stats.isis_tx++;
}
return;
}
+1 -1
View File
@@ -52,7 +52,7 @@ isis_adjacency_state_string(uint8_t state)
}
const char *
isis_pdu_sype_string(uint8_t type)
isis_pdu_type_string(uint8_t type)
{
switch(type) {
case ISIS_PDU_L1_HELLO: return "L1-Hello";
+1 -1
View File
@@ -22,7 +22,7 @@ const char *
isis_adjacency_state_string(uint8_t state);
const char *
isis_pdu_sype_string(uint8_t type);
isis_pdu_type_string(uint8_t type);
bool
isis_str_to_area(const char *str, isis_area_s *area);
+95
View File
@@ -0,0 +1,95 @@
/*
* BNG Blaster (BBL) - LDP Functions
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ldp.h"
/**
* ldp_id_to_str
*
* @param lsr_id LDP LSR ID
* @param label_space_id LDP label space
* @return LDP identifier string
*/
char *
ldp_id_to_str(uint32_t lsr_id, uint16_t label_space_id)
{
static char buffer[4][LDP_IDENTIFIER_STR_LEN];
static int idx = 0;
char *ret;
ret = buffer[idx];
idx = (idx+1) & 3;
snprintf(ret, LDP_IDENTIFIER_STR_LEN, "%s:%u",
format_ipv4_address(&lsr_id),
label_space_id);
return ret;
}
/**
* ldp_init
*
* This function inits all LDP sessions.
*/
bool
ldp_init()
{
ldp_config_s *config = g_ctx->config.ldp_config;
ldp_instance_s *instance = NULL;
while(config) {
LOG(LDP, "Init LDP instance %u\n", config->id);
if(instance) {
instance->next = calloc(1, sizeof(ldp_instance_s));
instance = instance->next;
} else {
instance = calloc(1, sizeof(ldp_instance_s));
g_ctx->ldp_instances = instance;
}
instance->config = config;
ldb_db_init(instance);
config = config->next;
}
return true;
}
void
ldp_teardown_job(timer_s *timer) {
ldp_instance_s *instance = timer->data;
UNUSED(instance);
}
/**
* ldp_teardown
*
* This function stops all LDP sessions.
*/
void
ldp_teardown()
{
ldp_instance_s *instance = g_ctx->ldp_instances;
ldp_session_s *session;
while(instance) {
if(!instance->teardown) {
LOG(LDP, "Teardown LDP instance %u\n", instance->config->id);
instance->teardown = true;
session = instance->sessions;
while(session) {
session->teardown = true;
ldp_session_close(session);
session = session->next;
}
timer_add(&g_ctx->timer_root, &instance->teardown_timer,
"LDP TEARDOWN", instance->config->teardown_time, 0, instance,
&ldp_teardown_job);
}
instance = instance->next;
}
}
+32
View File
@@ -0,0 +1,32 @@
/*
* BNG Blaster (BBL) - LDP Main
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __BBL_LDP_H__
#define __BBL_LDP_H__
#include "../bbl.h"
#include "ldp_def.h"
#include "ldp_hello.h"
#include "ldp_interface.h"
#include "ldp_session.h"
#include "ldp_message.h"
#include "ldp_db.h"
#include "ldp_receive.h"
#include "ldp_raw_update.h"
#include "ldp_ctrl.h"
char *
ldp_id_to_str(uint32_t lsr_id, uint16_t label_space_id);
bool
ldp_init();
void
ldp_teardown();
#endif
+509
View File
@@ -0,0 +1,509 @@
/*
* BNG Blaster (BBL) - LDP CTRL (Control Commands)
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ldp.h"
#include "../bbl_ctrl.h"
static const char *
raw_update_state(ldp_session_s *session)
{
if(session->raw_update) {
if(session->update_start_timestamp.tv_sec) {
if(session->raw_update_sending) {
return "sending";
}
if(session->update_stop_timestamp.tv_sec) {
return "done";
}
} else {
return "wait";
}
}
return NULL;
}
int
ldp_ctrl_adjacencies(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments)
{
int result = 0;
json_t *root, *adjacencies, *adjacency;
ldp_instance_s *ldp_instance = g_ctx->ldp_instances;
ldp_adjacency_s *ldp_adjacency;
int ldp_instance_id = 0;
/* Unpack further arguments */
json_unpack(arguments, "{s:i}", "ldp-instance-id", &ldp_instance_id);
adjacencies = json_array();
while(ldp_instance) {
if(ldp_instance_id > 0 && ldp_instance_id != ldp_instance->config->id) {
ldp_instance = ldp_instance->next;
continue;
}
ldp_adjacency = ldp_instance->adjacencies;
while(ldp_adjacency) {
adjacency = json_pack("{si ss ss si si si si}",
"ldp-instance-id", ldp_adjacency->instance->config->id,
"interface", ldp_adjacency->interface->name,
"state", ldp_adjacency->state == LDP_ADJACENCY_STATE_UP ? "up" : "down",
"state-transitions", ldp_adjacency->state_transitions,
"rx-discovery", ldp_adjacency->interface->stats.ldp_udp_rx,
"rx-discovery-error", ldp_adjacency->interface->stats.ldp_udp_rx_error,
"tx-discovery", ldp_adjacency->interface->stats.ldp_udp_tx);
if(adjacency) {
json_array_append(adjacencies, adjacency);
}
ldp_adjacency = ldp_adjacency->next;
}
ldp_instance = ldp_instance->next;
}
root = json_pack("{ss si so}",
"status", "ok",
"code", 200,
"ldp-adjacencies", adjacencies);
if(root) {
result = json_dumpfd(root, fd, 0);
json_decref(root);
} else {
result = bbl_ctrl_status(fd, "error", 500, "internal error");
json_decref(adjacencies);
}
return result;
}
static json_t *
ldp_ctrl_session_json(ldp_session_s *session)
{
json_t *root = NULL;
json_t *stats = NULL;
const char *raw_update_file = NULL;
if(!session) {
return NULL;
}
if(session->raw_update) {
raw_update_file = session->raw_update->file;
}
stats = json_pack("{si si si si si si}",
"pdu-rx", session->stats.pdu_rx,
"pdu-tx", session->stats.pdu_tx,
"messages-rx", session->stats.message_rx,
"messages-tx", session->stats.message_tx,
"keepalive-rx", session->stats.keepalive_rx,
"keepalive-tx", session->stats.keepalive_tx);
if(!stats) {
return NULL;
}
root = json_pack("{si ss ss ss ss ss ss si ss* ss* so*}",
"ldp-instance-id", session->instance->config->id,
"interface", session->interface->name,
"local-address", format_ipv4_address(&session->local.ipv4_address),
"local-identifier", ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
"peer-address", format_ipv4_address(&session->peer.ipv4_address),
"peer-identifier", ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id),
"state", ldp_session_state_string(session->state),
"state-transitions", session->state_transitions,
"raw-update-state", raw_update_state(session),
"raw-update-file", raw_update_file,
"stats", stats);
if(!root) {
if(stats) json_decref(stats);
}
return root;
}
int
ldp_ctrl_sessions(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments)
{
int result = 0;
json_t *root, *sessions, *session;
ldp_instance_s *ldp_instance = g_ctx->ldp_instances;
ldp_session_s *ldp_session;
const char *s;
uint32_t ipv4_local_address = 0;
uint32_t ipv4_peer_address = 0;
int ldp_instance_id = 0;
/* Unpack further arguments */
json_unpack(arguments, "{s:i}", "ldp-instance-id", &ldp_instance_id);
if(json_unpack(arguments, "{s:s}", "local-ipv4-address", &s) == 0) {
if(!inet_pton(AF_INET, s, &ipv4_local_address)) {
return bbl_ctrl_status(fd, "error", 400, "invalid local-ipv4-address");
}
}
if(json_unpack(arguments, "{s:s}", "peer-ipv4-address", &s) == 0) {
if(!inet_pton(AF_INET, s, &ipv4_peer_address)) {
return bbl_ctrl_status(fd, "error", 400, "invalid peer-ipv4-address");
}
}
sessions = json_array();
while(ldp_instance) {
if(ldp_instance_id > 0 && ldp_instance_id != ldp_instance->config->id) {
ldp_instance = ldp_instance->next;
continue;
}
ldp_session = ldp_instance->sessions;
while(ldp_session) {
if(ipv4_local_address && ldp_session->local.ipv4_address != ipv4_local_address) {
ldp_session = ldp_session->next;
continue;
}
if(ipv4_peer_address && ldp_session->peer.ipv4_address != ipv4_peer_address) {
ldp_session = ldp_session->next;
continue;
}
session = ldp_ctrl_session_json(ldp_session);
if(session) {
json_array_append(sessions, session);
}
ldp_session = ldp_session->next;
}
ldp_instance = ldp_instance->next;
}
root = json_pack("{ss si so}",
"status", "ok",
"code", 200,
"ldp-sessions", sessions);
if(root) {
result = json_dumpfd(root, fd, 0);
json_decref(root);
} else {
result = bbl_ctrl_status(fd, "error", 500, "internal error");
json_decref(sessions);
}
return result;
}
int
ldp_ctrl_teardown(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments __attribute__((unused)))
{
ldp_teardown();
return bbl_ctrl_status(fd, "ok", 200, NULL);
}
int
ldp_ctrl_raw_update(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments)
{
int result = 0;
json_t *root;
ldp_instance_s *ldp_instance = g_ctx->ldp_instances;
ldp_session_s *ldp_session;
ldp_raw_update_s *raw_update;
const char *s;
const char *file_path;
uint16_t started = 0;
uint16_t skipped = 0;
uint16_t filtered = 0;
uint32_t ipv4_local_address = 0;
uint32_t ipv4_peer_address = 0;
int ldp_instance_id = 0;
/* Unpack further arguments */
if(json_unpack(arguments, "{s:s}", "file", &file_path) != 0) {
return bbl_ctrl_status(fd, "error", 400, "missing argument file");
}
json_unpack(arguments, "{s:i}", "ldp-instance-id", &ldp_instance_id);
if(json_unpack(arguments, "{s:s}", "local-ipv4-address", &s) == 0) {
if(!inet_pton(AF_INET, s, &ipv4_local_address)) {
return bbl_ctrl_status(fd, "error", 400, "invalid local-ipv4-address");
}
}
if(json_unpack(arguments, "{s:s}", "peer-ipv4-address", &s) == 0) {
if(!inet_pton(AF_INET, s, &ipv4_peer_address)) {
return bbl_ctrl_status(fd, "error", 400, "invalid peer-ipv4-address");
}
}
/* Load file. */
raw_update = ldp_raw_update_load(file_path, true);
if(!raw_update) {
return bbl_ctrl_status(fd, "error", 400, "failed to load file");
}
while(ldp_instance) {
ldp_session = ldp_instance->sessions;
while(ldp_session) {
if(ldp_instance_id > 0 && ldp_instance_id != ldp_instance->config->id) {
ldp_session = ldp_session->next;
filtered++;
continue;
}
if(ipv4_local_address && ldp_session->local.ipv4_address != ipv4_local_address) {
ldp_session = ldp_session->next;
filtered++;
continue;
}
if(ipv4_peer_address && ldp_session->peer.ipv4_address != ipv4_peer_address) {
ldp_session = ldp_session->next;
filtered++;
continue;
}
if(ldp_session->raw_update_sending) {
ldp_session = ldp_session->next;
skipped++;
continue;
}
ldp_session->raw_update = raw_update;
timer_add(&g_ctx->timer_root, &ldp_session->update_timer,
"LDP UPDATE", 0, 0, ldp_session,
&ldp_session_update_job);
started++;
ldp_session = ldp_session->next;
}
ldp_instance = ldp_instance->next;
}
root = json_pack("{ss si s{si si si}}",
"status", "ok",
"code", 200,
"ldp-raw-update",
"started", started,
"skipped", skipped,
"filtered", filtered);
if(root) {
result = json_dumpfd(root, fd, 0);
json_decref(root);
} else {
result = bbl_ctrl_status(fd, "error", 500, "internal error");
}
return result;
}
int
ldp_ctrl_raw_update_list(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments __attribute__((unused)))
{
int result = 0;
ldp_raw_update_s *raw_update = g_ctx->ldp_raw_updates;
json_t *root, *updates, *update;
updates = json_array();
while(raw_update){
update = json_pack("{ss* si si si}",
"file", raw_update->file,
"len", raw_update->len,
"pdu", raw_update->pdu,
"messages", raw_update->messages);
if(update) {
json_array_append(updates, update);
}
raw_update = raw_update->next;
}
root = json_pack("{ss si so}",
"status", "ok",
"code", 200,
"ldp-raw-update-list", updates);
if(root) {
result = json_dumpfd(root, fd, 0);
json_decref(root);
} else {
result = bbl_ctrl_status(fd, "error", 500, "internal error");
json_decref(updates);
}
return result;
}
int
ldp_ctrl_disconnect(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments)
{
int result = 0;
json_t *root;
ldp_instance_s *ldp_instance = g_ctx->ldp_instances;
ldp_session_s *ldp_session;
const char *s;
uint16_t disconnected = 0;
uint16_t skipped = 0;
uint16_t filtered = 0;
uint32_t ipv4_local_address = 0;
uint32_t ipv4_peer_address = 0;
int ldp_instance_id = 0;
/* Unpack further arguments */
json_unpack(arguments, "{s:i}", "ldp-instance-id", &ldp_instance_id);
if(json_unpack(arguments, "{s:s}", "local-ipv4-address", &s) == 0) {
if(!inet_pton(AF_INET, s, &ipv4_local_address)) {
return bbl_ctrl_status(fd, "error", 400, "invalid local-ipv4-address");
}
}
if(json_unpack(arguments, "{s:s}", "peer-ipv4-address", &s) == 0) {
if(!inet_pton(AF_INET, s, &ipv4_peer_address)) {
return bbl_ctrl_status(fd, "error", 400, "invalid peer-ipv4-address");
}
}
while(ldp_instance) {
ldp_session = ldp_instance->sessions;
while(ldp_session) {
if(ldp_instance_id > 0 && ldp_instance_id != ldp_instance->config->id) {
ldp_session = ldp_session->next;
filtered++;
continue;
}
if(ipv4_local_address && ldp_session->local.ipv4_address != ipv4_local_address) {
ldp_session = ldp_session->next;
filtered++;
continue;
}
if(ipv4_peer_address && ldp_session->peer.ipv4_address != ipv4_peer_address) {
ldp_session = ldp_session->next;
filtered++;
continue;
}
if(ldp_session->state == LDP_CLOSED || ldp_session->state == LDP_CLOSING) {
ldp_session = ldp_session->next;
skipped++;
continue;
}
if(!ldp_session->error_code) {
ldp_session->error_code = LDP_STATUS_SHUTDOWN|LDP_STATUS_FATAL_ERROR;
}
ldp_session_close(ldp_session);
disconnected++;
ldp_session = ldp_session->next;
}
ldp_instance = ldp_instance->next;
}
root = json_pack("{ss si s{si si si}}",
"status", "ok",
"code", 200,
"ldp-disconnect",
"disconnected", disconnected,
"skipped", skipped,
"filtered", filtered);
if(root) {
result = json_dumpfd(root, fd, 0);
json_decref(root);
} else {
result = bbl_ctrl_status(fd, "error", 500, "internal error");
}
return result;
}
static json_t *
ldb_ctrl_database_entries(ldp_instance_s *instance)
{
json_t *json_database, *json_entry;
ldp_db_entry_s *entry;
hb_tree *db;
hb_itor *itor;
bool next;
json_database = json_array();
/* Add IPv4 prefixes. */
db = instance->db.ipv4;
itor = hb_itor_new(db);
next = hb_itor_first(itor);
while(next) {
entry = *hb_itor_datum(itor);
json_entry = json_pack("{ss ss* si ss*}",
"afi", "ipv4",
"prefix", format_ipv4_prefix(&entry->prefix.ipv4),
"label", entry->label,
"source-identifier", ldp_id_to_str(entry->source->peer.lsr_id, entry->source->peer.label_space_id));
if(json_entry) {
json_array_append(json_database, json_entry);
}
next = hb_itor_next(itor);
}
hb_itor_free(itor);
/* Add IPv6 prefixes. */
db = instance->db.ipv6;
itor = hb_itor_new(db);
next = hb_itor_first(itor);
while(next) {
entry = *hb_itor_datum(itor);
json_entry = json_pack("{ss ss* si ss*}",
"afi", "ipv6",
"prefix", format_ipv6_prefix(&entry->prefix.ipv6),
"label", entry->label,
"source-identifier", ldp_id_to_str(entry->source->peer.lsr_id, entry->source->peer.label_space_id));
if(json_entry) {
json_array_append(json_database, json_entry);
}
next = hb_itor_next(itor);
}
hb_itor_free(itor);
return json_database;
}
int
ldb_ctrl_database(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments)
{
int result = 0;
json_t *root = NULL;
json_t *database = NULL;
ldp_instance_s *instance = NULL;
int instance_id = 0;
/* Unpack further arguments */
if(json_unpack(arguments, "{s:i}", "ldp-instance-id", &instance_id) != 0) {
return bbl_ctrl_status(fd, "error", 400, "LDP instance missing");
}
/* Search for matching instance */
instance = g_ctx->ldp_instances;
while(instance) {
if(instance->config->id == instance_id) {
break;
}
instance = instance->next;
}
if(!instance) {
return bbl_ctrl_status(fd, "error", 400, "LDP instance not found");
}
database = ldb_ctrl_database_entries(instance);
if(database) {
root = json_pack("{ss si so}",
"status", "ok",
"code", 200,
"ldp-database", database);
if(root) {
result = json_dumpfd(root, fd, 0);
json_decref(root);
} else {
result = bbl_ctrl_status(fd, "error", 500, "internal error");
json_decref(database);
}
return result;
} else {
return bbl_ctrl_status(fd, "error", 500, "internal error");
}
}
+33
View File
@@ -0,0 +1,33 @@
/*
* BNG Blaster (BBL) - LDP CTRL (Control Commands)
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __BBL_LDP_CTRL_H__
#define __BBL_LDP_CTRL_H__
int
ldp_ctrl_adjacencies(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments);
int
ldp_ctrl_sessions(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments __attribute__((unused)));
int
ldp_ctrl_teardown(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments __attribute__((unused)));
int
ldp_ctrl_raw_update(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments);
int
ldp_ctrl_raw_update_list(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments __attribute__((unused)));
int
ldp_ctrl_disconnect(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments);
int
ldb_ctrl_database(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments);
#endif
+80
View File
@@ -0,0 +1,80 @@
/*
* BNG Blaster (BBL) - LDP Database
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ldp.h"
int
ldb_db_ipv4_compare(void *id1, void *id2)
{
const uint32_t a = *(const uint32_t*)id1;
const uint32_t b = *(const uint32_t*)id2;
return (a > b) - (a < b);
}
int
ldb_db_ipv6_compare(void *id1, void *id2)
{
const uint64_t a = *(const uint64_t*)id1;
const uint64_t b = *(const uint64_t*)id2;
return (a > b) - (a < b);
}
bool
ldb_db_init(ldp_instance_s *instance)
{
instance->db.ipv4 = hb_tree_new((dict_compare_func)ldb_db_ipv4_compare);
instance->db.ipv6 = hb_tree_new((dict_compare_func)ldb_db_ipv6_compare);
return true;
}
bool
ldb_db_add_ipv4(ldp_session_s *session, ipv4_prefix *prefix, uint32_t label)
{
void **search = NULL;
ldp_instance_s *instance = session->instance;
ldp_db_entry_s *entry;
dict_insert_result result;
search = hb_tree_search(instance->db.ipv4, &prefix->address);
if(search) {
entry = *search;
entry->version++;
} else {
entry = calloc(1, sizeof(ldp_db_entry_s));
entry->afi = IANA_AFI_IPV4;
entry->prefix.ipv4.address = prefix->address;
result = hb_tree_insert(instance->db.ipv4, &entry->prefix.ipv4.address);
if(result.inserted) {
*result.datum_ptr = entry;
} else {
LOG(ERROR, "LDP (%s - %s) failed to add IPv4 entry to database\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id));
return false;
}
}
entry->active = true;
entry->prefix.ipv4.len = prefix->len;
entry->label = label;
entry->source = session;
return true;
}
ldp_db_entry_s *
ldb_db_lookup_ipv4(ldp_instance_s *instance, uint32_t address)
{
void **search = NULL;
ldp_db_entry_s *entry;
search = hb_tree_search(instance->db.ipv4, &address);
if(search) {
entry = *search;
return entry;
}
return NULL;
}
+21
View File
@@ -0,0 +1,21 @@
/*
* BNG Blaster (BBL) - LDP Database
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __BBL_LDP_DB_H__
#define __BBL_LDP_DB_H__
bool
ldb_db_init(ldp_instance_s *instance);
bool
ldb_db_add_ipv4(ldp_session_s *session, ipv4_prefix *prefix, uint32_t label);
ldp_db_entry_s *
ldb_db_lookup_ipv4(ldp_instance_s *instance, uint32_t address);
#endif
+273
View File
@@ -0,0 +1,273 @@
/*
* BNG Blaster (BBL) - LDP Definitions
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __BBL_LDP_DEF_H__
#define __BBL_LDP_DEF_H__
/* DEFINITIONS ... */
#define LDP_PORT 646
#define LDP_IDENTIFIER_LEN 6U
#define LDP_IDENTIFIER_STR_LEN sizeof("255.255.255.255:6500")
#define LDP_MAX_PDU_LEN_INIT 4096U
#define LDP_MIN_PDU_LEN 10
#define LDP_MIN_MSG_LEN 8
#define LDP_MIN_TLV_LEN 4
#define LDP_BUF_SIZE 256*1024
#define LDP_MESSAGE_TYPE_NOTIFICATION 0x0001
#define LDP_MESSAGE_TYPE_HELLO 0x0100
#define LDP_MESSAGE_TYPE_INITIALIZATION 0x0200
#define LDP_MESSAGE_TYPE_KEEPALIVE 0x0201
#define LDP_MESSAGE_TYPE_ADDRESS 0x0300
#define LDP_MESSAGE_TYPE_ADDRESS_WITHDRAW 0x0301
#define LDP_MESSAGE_TYPE_LABEL_MAPPING 0x0400
#define LDP_MESSAGE_TYPE_LABEL_REQUEST 0x0401
#define LDP_MESSAGE_TYPE_LABEL_WITHDRAW 0x0402
#define LDP_MESSAGE_TYPE_LABEL_RELEASE 0x0403
#define LDP_MESSAGE_TYPE_ABORT_REQUEST 0x0404
#define LDP_TLV_TYPE_FEC 0x0100
#define LDP_TLV_TYPE_ADDRESS_LIST 0x0101
#define LDP_TLV_TYPE_HOP_COUNT 0x0103
#define LDP_TLV_TYPE_PATH_VECTOR 0x0104
#define LDP_TLV_TYPE_GENERIC_LABEL 0x0200
#define LDP_TLV_TYPE_STATUS 0x0300
#define LDP_TLV_TYPE_EXTENDED_STATUS 0x0301
#define LDP_TLV_TYPE_RETURNED_PDU 0x0302
#define LDP_TLV_TYPE_RETURNED_MESSAGE 0x0303
#define LDP_TLV_TYPE_COMMON_HELLO_PARAMETERS 0x0400
#define LDP_TLV_TYPE_IPV4_TRANSPORT_ADDRESS 0x0401
#define LDP_TLV_TYPE_CONFIG_SEQ_NUMBER 0x0402
#define LDP_TLV_TYPE_IPV6_TRANSPORT_ADDRESS 0x0403
#define LDP_TLV_TYPE_COMMON_SESSION_PARAMETERS 0x0500
#define LDP_TLV_TYPE_LABEL_REQUEST_ID 0x0600
#define LDP_TLV_LEN_MIN 4
#define LDP_FEC_LEN_MIN 4
#define LDP_FEC_ELEMENT_TYPE_PREFIX 2
#define LDP_STATUS_LEN_MIN 10
#define LDP_STATUS_SUCCESS 0x00000000
#define LDP_STATUS_BAD_IDENTIFIER 0x00000001
#define LDP_STATUS_BAD_VERSION 0x00000002
#define LDP_STATUS_BAD_PDU_LEN 0x00000003
#define LDP_STATUS_UNKNOWN_MSG_TYPE 0x00000004
#define LDP_STATUS_BAD_MSG_LEN 0x00000005
#define LDP_STATUS_UNKNOWN_TLV_TYPE 0x00000006
#define LDP_STATUS_BAD_TLV_LEN 0x00000007
#define LDP_STATUS_BAD_TLV_VALUE 0x00000008
#define LDP_STATUS_HOLD_TIMER_EXPIRED 0x00000009
#define LDP_STATUS_KEEPALIVE_TIMER_EXPIRED 0x00000014
#define LDP_STATUS_SHUTDOWN 0x0000000A
#define LDP_STATUS_INTERNAL_ERROR 0x00000019
#define LDP_STATUS_FATAL_ERROR 0x80000000
#define LDP_STATUS_FORWARD 0x40000000
#define LDP_DEFAULT_KEEPALIVE_TIME 15
#define LDP_DEFAULT_HOLD_TIME 15
#define LDP_DEFAULT_TEARDOWN_TIME 5
typedef enum ldp_state_ {
LDP_CLOSED,
LDP_IDLE,
LDP_LISTEN,
LDP_CONNECT,
LDP_INITIALIZED,
LDP_OPENREC,
LDP_OPENSENT,
LDP_OPERATIONAL,
LDP_CLOSING,
LDP_ERROR
} ldp_state_t;
typedef enum ldp_event_ {
LDP_EVENT_START,
LDP_EVENT_RX_INITIALIZED,
LDP_EVENT_RX_KEEPALIVE,
} ldp_event_t;
typedef enum ldp_adjacency_state_ {
LDP_ADJACENCY_STATE_DOWN = 0,
LDP_ADJACENCY_STATE_UP = 1
} ldp_adjacency_state;
typedef struct ldp_instance_ ldp_instance_s;
typedef struct ldp_session_ ldp_session_s;
typedef struct ldp_adjacency_ ldp_adjacency_s;
/*
* LDP database entry
*/
typedef struct ldp_db_entry_ {
iana_afi_t afi;
bool active;
union {
ipv4_prefix ipv4;
ipv6_prefix ipv6;
} prefix;
uint32_t label;
uint32_t version;
ldp_session_s *source;
} ldp_db_entry_s;
/*
* LDP RAW Update File
*/
typedef struct ldp_raw_update_ {
const char *file;
uint8_t *buf;
uint32_t len;
uint32_t pdu; /* PDU counter */
uint32_t messages; /* Message counter*/
/* Pointer to next instance */
struct ldp_raw_update_ *next;
} ldp_raw_update_s;
/*
* LDP Instance Configuration.
*/
typedef struct ldp_config_ {
uint16_t id; /* LDP instance identifier */
uint32_t lsr_id;
uint32_t ipv4_transport_address;
const char *lsr_id_str;
const char *hostname;
uint16_t keepalive_time;
uint16_t hold_time;
uint16_t teardown_time;
char *raw_update_file;
/* Pointer to next instance. */
struct ldp_config_ *next;
} ldp_config_s;
/*
* LDP Session.
*/
typedef struct ldp_session_ {
ldp_instance_s *instance;
bbl_network_interface_s *interface;
bbl_tcp_ctx_s *tcpc;
bbl_tcp_ctx_s *listen_tcpc;
struct timer_ *connect_timer;
struct timer_ *keepalive_timer;
struct timer_ *keepalive_timeout_timer;
struct timer_ *close_timer;
struct timer_ *update_timer;
struct timer_ *teardown_timer;
io_buffer_t read_buf;
io_buffer_t write_buf;
uint32_t pdu_start_idx;
uint32_t msg_start_idx;
uint32_t tlv_start_idx;
uint32_t message_id;
uint32_t error_code;
bool decode_error;
bool active;
ldp_state_t state;
uint32_t state_transitions;
uint16_t max_pdu_len;
uint16_t keepalive_time;
struct {
uint32_t ipv4_address;
uint32_t lsr_id;
uint16_t label_space_id;
uint16_t keepalive_time;
uint16_t max_pdu_len;
} local;
struct {
uint32_t ipv4_address;
uint32_t lsr_id;
uint16_t label_space_id;
uint16_t keepalive_time;
uint16_t max_pdu_len;
} peer;
struct {
uint32_t pdu_rx;
uint32_t pdu_tx;
uint32_t message_rx;
uint32_t message_tx;
uint32_t keepalive_rx;
uint32_t keepalive_tx;
} stats;
ldp_raw_update_s *raw_update_start;
ldp_raw_update_s *raw_update;
bool raw_update_sending;
struct timespec operational_timestamp;
struct timespec update_start_timestamp;
struct timespec update_stop_timestamp;
bool teardown;
/* Pointer to next peer of
* corresponding instance. */
struct ldp_session_ *next;
} ldp_session_s;
/*
* LDP Adjacency.
*/
typedef struct ldp_adjacency_ {
bbl_network_interface_s *interface;
ldp_instance_s *instance;
struct timer_ *hello_timer;
struct timer_ *hold_timer;
uint16_t hold_time;
ldp_adjacency_state state;
uint32_t state_transitions;
/* Pointer to next adjacency of
* corresponding instance with. */
struct ldp_adjacency_ *next;
} ldp_adjacency_s;
/*
* LDP Instance
*/
typedef struct ldp_instance_ {
ldp_config_s *config;
bool teardown;
struct timer_ *teardown_timer;
ldp_adjacency_s *adjacencies;
ldp_session_s *sessions;
struct {
hb_tree *ipv4;
hb_tree *ipv6;
} db; /* Label database. */
/* Pointer to next instance. */
struct ldp_instance_ *next;
} ldp_instance_s;
#endif
+162
View File
@@ -0,0 +1,162 @@
/*
* BNG Blaster (BBL) - LDP Hello
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ldp.h"
/**
* ldp_hello_encode
*
* @param interface send interface
* @param buf send buffer
* @param len send buffer length
* @param eth send ethernet parent structure
* @return PROTOCOL_SUCCESS on success
*/
protocol_error_t
ldp_hello_encode(bbl_network_interface_s *interface,
uint8_t *buf, uint16_t *len,
bbl_ethernet_header_s *eth)
{
protocol_error_t result;
bbl_ipv4_s ipv4 = {0};
bbl_udp_s udp = {0};
bbl_ldp_hello_s ldp = {0};
ldp_adjacency_s *adjacency = interface->ldp_adjacency;
ldp_instance_s *instance = adjacency->instance;
ldp_config_s *config = instance->config;
/* Build packet ... */
eth->type = ETH_TYPE_IPV4;
eth->next = &ipv4;
eth->dst = (uint8_t*)all_routers_mac;
ipv4.dst = IPV4_MC_ALL_ROUTERS;
ipv4.src = interface->ip.address;
ipv4.ttl = 1;
ipv4.protocol = PROTOCOL_IPV4_UDP;
ipv4.next = &udp;
udp.src = LDP_PORT;
udp.dst = LDP_PORT;
udp.protocol = UDP_PROTOCOL_LDP;
udp.next = &ldp;
ldp.lsr_id = config->lsr_id;
ldp.hold_time = config->hold_time;
ldp.ipv4_transport_address = config->ipv4_transport_address;
result = encode_ethernet(buf, len, eth);
if(result == PROTOCOL_SUCCESS) {
LOG(DEBUG, "LDP TX hello on interface %s\n", interface->name);
adjacency->interface->stats.ldp_udp_tx++;
}
return result;
}
void
ldp_hello_job(timer_s *timer)
{
ldp_adjacency_s *adjacency = timer->data;
bbl_network_interface_s *interface = adjacency->interface;
interface->send_requests |= BBL_IF_SEND_LDP_HELLO;
}
/**
* ldp_hello_start
*
* @param config LDP configuration
* @param adjacency LDP adjacency
*/
void
ldp_hello_start(ldp_adjacency_s *adjacency)
{
time_t hello_interval = adjacency->hold_time/3U;
if(!hello_interval) {
hello_interval = 1;
}
timer_add_periodic(&g_ctx->timer_root, &adjacency->hello_timer,
"LDP Hello", hello_interval, 0, adjacency,
&ldp_hello_job);
}
void
ldp_hello_hold_timeout_job(timer_s *timer)
{
ldp_adjacency_s *adjacency = timer->data;
if(adjacency->state == LDP_ADJACENCY_STATE_DOWN) {
return;
}
adjacency->state_transitions++;
adjacency->state = LDP_ADJACENCY_STATE_DOWN;
LOG(LDP, "LDP hold timeout on interface %s\n", adjacency->interface->name);
}
static void
ldp_hello_restart_hold_timeout(ldp_adjacency_s *adjacency)
{
if(adjacency->state == LDP_ADJACENCY_STATE_DOWN) {
adjacency->state_transitions++;
adjacency->state = LDP_ADJACENCY_STATE_UP;
LOG(LDP, "LDP adjacency on interface %s\n", adjacency->interface->name);
}
timer_add(&g_ctx->timer_root, &adjacency->hold_timer,
"LDP HOLD TIMEOUT", adjacency->hold_time, 0, adjacency, &ldp_hello_hold_timeout_job);
}
/**
* ldp_hello_rx
*
* This function handles all received LDP hello packets.
*
* @param interface receiving interface
* @param eth received ethernet header
* @param ipv4 received ipv4 header
* @param ldp LDP header of received packet
*/
void
ldp_hello_rx(bbl_network_interface_s *interface,
bbl_ethernet_header_s *eth,
bbl_ipv4_s *ipv4,
bbl_ldp_hello_s *ldp)
{
ldp_adjacency_s *adjacency = interface->ldp_adjacency;
ldp_instance_s *instance;
ldp_session_s *session;
UNUSED(eth);
interface->stats.ldp_udp_rx++;
if(!adjacency) {
return;
}
LOG(DEBUG, "LDP RX hello on interface %s\n", interface->name);
instance = adjacency->instance;
session = instance->sessions;
if(ldp->hold_time > 0 && ldp->hold_time < adjacency->hold_time) {
adjacency->hold_time = ldp->hold_time;
ldp_hello_start(adjacency);
}
ldp_hello_restart_hold_timeout(adjacency);
while(session) {
if(session->peer.lsr_id == ldp->lsr_id &&
session->peer.label_space_id == ldp->label_space_id) {
if(session->state == LDP_CLOSED) {
break;
} else {
return;
}
}
session = session->next;
}
/* Init LDP session. */
ldp_session_init(session, adjacency, ipv4, ldp);
}
+26
View File
@@ -0,0 +1,26 @@
/*
* BNG Blaster (BBL) - LDP Hello
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __BBL_LDP_HELLO_H__
#define __BBL_LDP_HELLO_H__
protocol_error_t
ldp_hello_encode(bbl_network_interface_s *interface,
uint8_t *buf, uint16_t *len,
bbl_ethernet_header_s *eth);
void
ldp_hello_start(ldp_adjacency_s *adjacency);
void
ldp_hello_rx(bbl_network_interface_s *interface,
bbl_ethernet_header_s *eth,
bbl_ipv4_s *ipv4,
bbl_ldp_hello_s *ldp);
#endif
+41
View File
@@ -0,0 +1,41 @@
/*
* BNG Blaster (BBL) - LDP Interface
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ldp.h"
/**
* ldp_interface_init
*
* This function inits the LDP interface.
*
* @param interface network interface
* @param config network interface configuration
* @param instance LDP instance
*/
bool
ldp_interface_init(bbl_network_interface_s *interface,
bbl_network_config_s *interface_config,
ldp_instance_s *instance)
{
ldp_config_s *config = instance->config;
ldp_adjacency_s *adjacency;
LOG(LDP, "Add network interface %s to LDP instance %u\n",
interface->name, interface_config->ldp_instance_id);
adjacency = calloc(1, sizeof(ldp_adjacency_s));
adjacency->next = instance->adjacencies;
instance->adjacencies = adjacency;
adjacency->instance = instance;
adjacency->interface = interface;
adjacency->hold_time = config->hold_time;
interface->ldp_adjacency = adjacency;
ldp_hello_start(adjacency);
return true;
}
+17
View File
@@ -0,0 +1,17 @@
/*
* BNG Blaster (BBL) - LDP Interface
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __BBL_LDP_INTERFACE_H__
#define __BBL_LDP_INTERFACE_H__
bool
ldp_interface_init(bbl_network_interface_s *interface,
bbl_network_config_s *interface_config,
ldp_instance_s *instance);
#endif
+222
View File
@@ -0,0 +1,222 @@
/*
* BNG Blaster (BBL) - LDP Protocol Messages
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ldp.h"
void
ldp_pdu_close(ldp_session_s *session)
{
io_buffer_t *buffer = &session->write_buf;
uint16_t len;
if(session->pdu_start_idx) {
/* update PDU length */
len = buffer->idx - session->pdu_start_idx;
write_be_uint(buffer->data+session->pdu_start_idx-2, 2, len);
session->pdu_start_idx = 0;
}
session->stats.pdu_tx++;
}
bool
ldp_pdu_init(ldp_session_s *session)
{
io_buffer_t *buffer = &session->write_buf;
if(session->pdu_start_idx) {
ldp_pdu_close(session);
}
if(IO_BUF_REMAINING(buffer) < LDP_MIN_PDU_LEN) {
return false;
}
push_be_uint(buffer, 2, 1); /* PDU Version */
push_be_uint(buffer, 2, 0); /* PDU Length */
session->pdu_start_idx = buffer->idx;
push_data(buffer, (uint8_t*)&session->local.lsr_id, 4);
push_be_uint(buffer, 2, session->local.label_space_id);
return true;
}
static void
ldp_msg_close(ldp_session_s *session)
{
io_buffer_t *buffer = &session->write_buf;
uint16_t len;
if(session->msg_start_idx) {
/* update message length */
len = buffer->idx - session->msg_start_idx;
write_be_uint(buffer->data+session->msg_start_idx-2, 2, len);
session->msg_start_idx = 0;
}
session->stats.message_tx++;
}
static bool
ldp_msg_init(ldp_session_s *session, uint16_t type)
{
io_buffer_t *buffer = &session->write_buf;
if(session->msg_start_idx) {
ldp_msg_close(session);
}
if(IO_BUF_REMAINING(buffer) < LDP_MIN_MSG_LEN) {
return false;
}
push_be_uint(buffer, 2, type); /* Type */
push_be_uint(buffer, 2, 0); /* Length */
session->msg_start_idx = buffer->idx;
push_be_uint(buffer, 4, session->message_id++);
return true;
}
static void
ldp_tlv_close(ldp_session_s *session)
{
io_buffer_t *buffer = &session->write_buf;
uint16_t len;
if(session->tlv_start_idx) {
/* update length */
len = buffer->idx - session->tlv_start_idx;
write_be_uint(buffer->data+session->tlv_start_idx-2, 2, len);
session->tlv_start_idx = 0;
}
}
static bool
ldp_tlv_init(ldp_session_s *session, uint16_t type)
{
io_buffer_t *buffer = &session->write_buf;
if(session->tlv_start_idx) {
ldp_tlv_close(session);
}
if(IO_BUF_REMAINING(buffer) < LDP_MIN_TLV_LEN) {
return false;
}
push_be_uint(buffer, 2, type); /* Type */
push_be_uint(buffer, 2, 0); /* Length */
session->tlv_start_idx = buffer->idx;
return true;
}
void
ldp_push_init_message(ldp_session_s *session)
{
io_buffer_t *buffer = &session->write_buf;
ldp_msg_init(session, LDP_MESSAGE_TYPE_INITIALIZATION);
ldp_tlv_init(session, LDP_TLV_TYPE_COMMON_SESSION_PARAMETERS);
push_be_uint(buffer, 2, 1);
push_be_uint(buffer, 2, session->local.keepalive_time);
push_be_uint(buffer, 2, 0);
push_be_uint(buffer, 2, LDP_MAX_PDU_LEN_INIT);
push_data(buffer, (uint8_t*)&session->peer.lsr_id, 4);
push_be_uint(buffer, 2, session->peer.label_space_id);
ldp_tlv_close(session);
ldp_msg_close(session);
}
void
ldp_push_keepalive_message(ldp_session_s *session)
{
ldp_msg_init(session, LDP_MESSAGE_TYPE_KEEPALIVE);
ldp_msg_close(session);
session->stats.keepalive_tx++;
}
void
ldp_push_notification_message(ldp_session_s *session)
{
io_buffer_t *buffer = &session->write_buf;
ldp_msg_init(session, LDP_MESSAGE_TYPE_NOTIFICATION);
ldp_tlv_init(session, LDP_TLV_TYPE_STATUS);
push_be_uint(buffer, 4, session->error_code);
push_be_uint(buffer, 4, 0);
push_be_uint(buffer, 2, 0);
ldp_tlv_close(session);
ldp_msg_close(session);
}
void
ldp_push_label_mapping_message(ldp_session_s *session, ipv4_prefix *prefix, uint32_t label)
{
io_buffer_t *buffer = &session->write_buf;
uint8_t prefix_bytes = BITS_TO_BYTES(prefix->len);
ldp_msg_init(session, LDP_MESSAGE_TYPE_LABEL_MAPPING);
ldp_tlv_init(session, LDP_TLV_TYPE_FEC);
push_be_uint(buffer, 1, 2); /* Prefix FEC */
push_be_uint(buffer, 2, 1); /* IPv4 */
push_be_uint(buffer, 1, prefix->len); /* IPv4 */
push_data(buffer, (uint8_t*)&prefix->address, prefix_bytes);
ldp_tlv_close(session);
ldp_tlv_init(session, LDP_TLV_TYPE_GENERIC_LABEL);
push_be_uint(buffer, 4, label);
ldp_tlv_close(session);
ldp_msg_close(session);
}
void
ldp_push_self_message(ldp_session_s *session)
{
io_buffer_t *buffer = &session->write_buf;
ldp_adjacency_s *adjacency = session->instance->adjacencies;
uint32_t lsr_id = session->instance->config->lsr_id;
uint32_t local_ipv4 = session->local.ipv4_address;
uint32_t ipv4;
uint8_t prefix_len;
uint8_t prefix_bytes;
ldp_msg_init(session, LDP_MESSAGE_TYPE_ADDRESS);
ldp_tlv_init(session, LDP_TLV_TYPE_ADDRESS_LIST);
push_be_uint(buffer, 2, 1); /* IPv4 */
push_data(buffer, (uint8_t*)&lsr_id, 4);
if(lsr_id != local_ipv4) {
push_data(buffer, (uint8_t*)&local_ipv4, 4);
}
while(adjacency) {
ipv4 = adjacency->interface->ip.address;
if(ipv4 && ipv4 != lsr_id && ipv4 != local_ipv4) {
push_data(buffer, (uint8_t*)&adjacency->interface->ip.address, 4);
}
adjacency = adjacency->next;
}
ldp_tlv_close(session);
ldp_msg_close(session);
ldp_msg_init(session, LDP_MESSAGE_TYPE_LABEL_MAPPING);
ldp_tlv_init(session, LDP_TLV_TYPE_FEC);
push_be_uint(buffer, 1, LDP_FEC_ELEMENT_TYPE_PREFIX);
push_be_uint(buffer, 2, IANA_AFI_IPV4);
push_be_uint(buffer, 1, 32);
push_data(buffer, (uint8_t*)&lsr_id, 4);
adjacency = session->instance->adjacencies;
while(adjacency) {
ipv4 = adjacency->interface->ip.address;
prefix_len = adjacency->interface->ip.len;
prefix_bytes = BITS_TO_BYTES(prefix_len);
push_be_uint(buffer, 1, LDP_FEC_ELEMENT_TYPE_PREFIX);
push_be_uint(buffer, 2, IANA_AFI_IPV4);
push_be_uint(buffer, 1, prefix_len);
push_data(buffer, (uint8_t*)&adjacency->interface->ip.address, prefix_bytes);
adjacency = adjacency->next;
}
ldp_tlv_close(session);
ldp_tlv_init(session, LDP_TLV_TYPE_GENERIC_LABEL);
push_be_uint(buffer, 4, 3); /* Implicit NULL Label */
ldp_tlv_close(session);
ldp_msg_close(session);
}
+33
View File
@@ -0,0 +1,33 @@
/*
* BNG Blaster (BBL) - LDP Protocol Messages
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __BBL_LDP_MESSAGE_H__
#define __BBL_LDP_MESSAGE_H__
void
ldp_pdu_close(ldp_session_s *session);
bool
ldp_pdu_init(ldp_session_s *session);
void
ldp_push_init_message(ldp_session_s *session);
void
ldp_push_keepalive_message(ldp_session_s *session);
void
ldp_push_notification_message(ldp_session_s *session);
void
ldp_push_label_mapping_message(ldp_session_s *session, ipv4_prefix *prefix, uint32_t label);
void
ldp_push_self_message(ldp_session_s *session);
#endif
+123
View File
@@ -0,0 +1,123 @@
/*
* BNG Blaster (BBL) - LDP RAW Update Functions
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ldp.h"
static ldp_raw_update_s *
ldp_raw_update_load_file(const char *file, bool decode_file)
{
ldp_raw_update_s *raw_update = NULL;
long fsize;
double fsize_kb;
uint8_t *buf = NULL;
uint32_t len = 0;
uint16_t pdu_length;
uint16_t msg_len;
/* Open file */
FILE *f = fopen(file, "rb");
if(f == NULL) {
LOG(ERROR, "Failed to open LDP RAW update file %s\n", file);
return NULL;
}
/* Get file size */
fseek(f, 0, SEEK_END);
fsize = ftell(f);
fsize_kb = fsize/1024.0;
fseek(f, 0, SEEK_SET);
/* Load file into memory */
raw_update = calloc(1, sizeof(ldp_raw_update_s));
raw_update->file = strdup(file);
raw_update->buf = malloc(fsize);
raw_update->len = fsize;
if(fread(raw_update->buf, fsize, 1, f) != 1) {
fclose(f);
LOG(ERROR, "Failed to read LDP RAW update file %s\n", file);
goto ERROR;
}
fclose(f);
if(decode_file) {
/* Decode update stream */
buf = raw_update->buf;
len = raw_update->len;
while(len) {
if(len < LDP_MIN_PDU_LEN) {
goto DECODE_ERROR;
}
raw_update->pdu++;
BUMP_BUFFER(buf, len, sizeof(uint16_t));
pdu_length = be16toh(*(uint16_t*)buf);
BUMP_BUFFER(buf, len, sizeof(uint16_t));
if(pdu_length > len) {
goto DECODE_ERROR;
}
BUMP_BUFFER(buf, len, LDP_IDENTIFIER_LEN);
while(pdu_length > LDP_MIN_MSG_LEN) {
BUMP_BUFFER(buf, len, sizeof(uint16_t));
msg_len = be16toh(*(uint16_t*)buf);
BUMP_BUFFER(buf, len, sizeof(uint16_t));
pdu_length -= 4;
if(msg_len > pdu_length) {
goto DECODE_ERROR;
}
BUMP_BUFFER(buf, len, msg_len);
pdu_length -= msg_len;
raw_update->messages++;
}
}
}
LOG(INFO, "Loaded LDP RAW update file %s (%.2f KB, %u pdu, %u messages)\n",
file, fsize_kb, raw_update->pdu, raw_update->messages);
return raw_update;
DECODE_ERROR:
LOG(ERROR, "Failed to decode LDP RAW update file %s\n", file);
ERROR:
if(raw_update) {
if(raw_update->buf) {
free(raw_update->buf);
}
free(raw_update);
}
return NULL;
}
/**
* ldp_raw_update_load
*
* @param file update file
* @param decode_file decode/parse file content if true
* @return LDP RAW update structure
*/
ldp_raw_update_s *
ldp_raw_update_load(const char *file, bool decode_file)
{
ldp_raw_update_s *raw_update = g_ctx->ldp_raw_updates;
/* Check if file is already loaded */
while(raw_update){
if(strcmp(file, raw_update->file) == 0) {
return raw_update;
}
raw_update = raw_update->next;
}
raw_update = ldp_raw_update_load_file(file, decode_file);
if(raw_update) {
raw_update->next = g_ctx->ldp_raw_updates;
g_ctx->ldp_raw_updates = raw_update;
return raw_update;
} else {
return NULL;
}
}
+15
View File
@@ -0,0 +1,15 @@
/*
* BNG Blaster (BBL) - LDP RAW Update Functions
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __BBL_LDP_RAW_UPDATE_H__
#define __BBL_LDP_RAW_UPDATE_H__
ldp_raw_update_s *
ldp_raw_update_load(const char *file, bool decode_file);
#endif
+371
View File
@@ -0,0 +1,371 @@
/*
* BNG Blaster (BBL) - LDP Receive Functions
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ldp.h"
struct keyval_ ldp_msg_names[] = {
{ LDP_MESSAGE_TYPE_NOTIFICATION, "notification" },
{ LDP_MESSAGE_TYPE_HELLO, "hello" },
{ LDP_MESSAGE_TYPE_INITIALIZATION, "initialization" },
{ LDP_MESSAGE_TYPE_KEEPALIVE, "keepalive" },
{ LDP_MESSAGE_TYPE_ADDRESS, "address" },
{ LDP_MESSAGE_TYPE_ADDRESS_WITHDRAW, "address-withdraw" },
{ LDP_MESSAGE_TYPE_LABEL_MAPPING, "label-mapping" },
{ LDP_MESSAGE_TYPE_LABEL_REQUEST, "label-request" },
{ LDP_MESSAGE_TYPE_LABEL_WITHDRAW, "label-withdraw" },
{ LDP_MESSAGE_TYPE_LABEL_RELEASE, "label-release" },
{ LDP_MESSAGE_TYPE_ABORT_REQUEST, "abort-request" },
{ 0, NULL}
};
struct keyval_ ldp_status_names[] = {
{ LDP_STATUS_SUCCESS, "success" },
{ LDP_STATUS_BAD_IDENTIFIER, "bad LDP identifier" },
{ LDP_STATUS_BAD_VERSION, "bad protocol version" },
{ LDP_STATUS_BAD_PDU_LEN, "bad PDU length" },
{ LDP_STATUS_UNKNOWN_MSG_TYPE, "unknown message type" },
{ LDP_STATUS_BAD_MSG_LEN, "bad message length" },
{ LDP_STATUS_UNKNOWN_TLV_TYPE, "unknown TLV type" },
{ LDP_STATUS_BAD_TLV_LEN, "bad TLV length" },
{ LDP_STATUS_BAD_TLV_VALUE, "bad TLV value" },
{ LDP_STATUS_HOLD_TIMER_EXPIRED, "hold timer expired" },
{ LDP_STATUS_KEEPALIVE_TIMER_EXPIRED, "keepalive timer expired" },
{ LDP_STATUS_SHUTDOWN, "shutdown" },
{ LDP_STATUS_INTERNAL_ERROR, "internal error" },
{ 0, NULL}
};
static void
ldp_decode_error(ldp_session_s *session)
{
LOG(LDP, "LDP (%s - %s) invalid PDU received\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id));
session->decode_error = true;
if(!session->error_code) {
session->error_code = LDP_STATUS_INTERNAL_ERROR|LDP_STATUS_FATAL_ERROR;
}
ldp_session_close(session);
}
static bool
ldp_notification(ldp_session_s *session, uint8_t *start, uint16_t length)
{
uint8_t *tlv_start = start;
uint16_t tlv_type;
uint16_t tlv_length;
uint32_t status_code = UINT32_MAX;
bool e_bit = false;
bool f_bit = false;
/* Read all TLV's. */
while(length >= LDP_TLV_LEN_MIN) {
tlv_type = read_be_uint(tlv_start, 2) & 0x3FFF;
tlv_length = read_be_uint(tlv_start+2, 2);
if(tlv_length+LDP_TLV_LEN_MIN > length) {
return false;
}
switch(tlv_type) {
case LDP_TLV_TYPE_STATUS:
if(tlv_length < LDP_STATUS_LEN_MIN) {
return false;
}
status_code = read_be_uint(tlv_start+LDP_TLV_LEN_MIN, 4);
e_bit = status_code & LDP_STATUS_FATAL_ERROR;
f_bit = status_code & LDP_STATUS_FORWARD;
status_code &= 0x3FFFFFFF;
break;
default:
break;
}
length -= (tlv_length+LDP_TLV_LEN_MIN);
tlv_start += (tlv_length+LDP_TLV_LEN_MIN);
}
LOG(LDP, "LDP (%s - %s) received %s notification with status code %u (%s)%s\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id),
(e_bit ? "fatal error" : "advisory"),
status_code, keyval_get_key(ldp_status_names, status_code),
(f_bit ? " wit forwarding bit set" : ""));
return true;
}
static bool
ldp_label_mapping(ldp_session_s *session, uint8_t *start, uint16_t length)
{
uint8_t *tlv_start = start;
uint16_t tlv_type;
uint16_t tlv_length;
uint8_t prefix_length;
uint8_t prefix_bytes;
uint8_t *fec_element;
uint16_t fec_length;
uint16_t fec_afi;
uint32_t label;
ipv4_prefix ipv4prefix;
/* Read all TLV's. */
while(length >= LDP_TLV_LEN_MIN) {
tlv_type = read_be_uint(tlv_start, 2) & 0x3FFF;
tlv_length = read_be_uint(tlv_start+2, 2);
if(tlv_length+LDP_TLV_LEN_MIN > length) {
return false;
}
switch(tlv_type) {
case LDP_TLV_TYPE_FEC:
if(tlv_length < LDP_FEC_LEN_MIN) {
return false;
}
fec_element = tlv_start+LDP_TLV_LEN_MIN;
fec_length = tlv_length;
if(*fec_element != LDP_FEC_ELEMENT_TYPE_PREFIX) {
return false;
}
break;
case LDP_TLV_TYPE_GENERIC_LABEL:
if(tlv_length < sizeof(label)) {
return false;
}
label = read_be_uint(tlv_start+LDP_TLV_LEN_MIN, sizeof(label));
break;
default:
break;
}
length -= (tlv_length+LDP_TLV_LEN_MIN);
tlv_start += (tlv_length+LDP_TLV_LEN_MIN);
}
/* Read all FEC elements. */
while(fec_length >= LDP_FEC_LEN_MIN) {
fec_afi = read_be_uint(fec_element+1, 2);
prefix_length = *(fec_element+3);
prefix_bytes = BITS_TO_BYTES(prefix_length);
if(prefix_bytes+LDP_FEC_LEN_MIN > fec_length) {
return false;
}
if(fec_afi == IANA_AFI_IPV4) {
ipv4prefix.len = prefix_length;
ipv4prefix.address = 0;
memcpy((uint8_t*)&ipv4prefix.address, fec_element+LDP_FEC_LEN_MIN, prefix_bytes);
LOG(DEBUG, "LDP (%s - %s) add %s via label %u\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id),
format_ipv4_prefix(&ipv4prefix), label);
ldb_db_add_ipv4(session, &ipv4prefix, label);
}
fec_length -= (prefix_bytes+LDP_FEC_LEN_MIN);
fec_element += (prefix_bytes+LDP_FEC_LEN_MIN);
}
return true;
}
static bool
ldp_initialization(ldp_session_s *session, uint8_t *start, uint16_t length)
{
uint8_t *tlv_start = start;
uint16_t tlv_type;
uint16_t tlv_length;
while(length > LDP_TLV_LEN_MIN) {
tlv_type = read_be_uint(tlv_start, 2) & 0x3FFF;
tlv_length = read_be_uint(tlv_start+2, 2);
if(tlv_length+LDP_TLV_LEN_MIN > length) {
return false;
}
switch(tlv_type) {
case LDP_TLV_TYPE_COMMON_SESSION_PARAMETERS:
if(tlv_length < 14) {
return false;
}
session->peer.keepalive_time = read_be_uint(tlv_start+6, 2);
session->peer.max_pdu_len = read_be_uint(tlv_start+10, 2);
break;
default:
break;
}
length -= (tlv_length+LDP_TLV_LEN_MIN);
tlv_start += (tlv_length+LDP_TLV_LEN_MIN);
}
ldp_session_fsm(session, LDP_EVENT_RX_INITIALIZED);
return true;
}
/*
* When there is only little data left and
* the buffer start is close to buffer end,
* then 'rebase' the buffer by copying
* the tail data to the buffer head.
*/
static void
ldp_rebase_read_buffer(io_buffer_t *buffer)
{
uint32_t size;
size = buffer->idx - buffer->start_idx;
if(size) {
/* Copy what is left to the buffer start. */
memcpy(buffer->data, buffer->data+buffer->start_idx, size);
}
buffer->start_idx = 0;
buffer->idx = size;
}
static void
ldp_read(ldp_session_s *session)
{
uint32_t size;
uint16_t length;
io_buffer_t *buffer = &session->read_buf;
uint32_t lsr_id;
uint16_t label_space_id;
uint8_t *pdu_start;
uint16_t pdu_version;
uint16_t pdu_length;
uint8_t *msg_start;
uint16_t msg_type;
uint16_t msg_length;
uint32_t msg_id;
while(!session->decode_error) {
pdu_start = buffer->data+buffer->start_idx;
size = buffer->idx - buffer->start_idx;
/* Minimum PDU size */
if(size < LDP_MIN_PDU_LEN) {
break;
}
pdu_version = read_be_uint(pdu_start, 2);
pdu_length = read_be_uint(pdu_start+2, 2);
/* The PDU length is defined as two octet integer specifying
* the total length of the PDU in octets, excluding the version
* and PDU length fields. */
if(pdu_version != 1 ||
pdu_length < LDP_IDENTIFIER_LEN ||
pdu_length > session->max_pdu_len) {
ldp_decode_error(session);
return;
}
/* Full message on the wire to consume? */
length = pdu_length+4;
if(length > size) {
break;
}
session->stats.pdu_rx++;
/* Read LDP Identifier. */
lsr_id = *(uint32_t*)(pdu_start+4);
label_space_id = read_be_uint(pdu_start+8, 2);
if(lsr_id != session->peer.lsr_id ||
label_space_id != session->peer.label_space_id) {
ldp_decode_error(session);
return;
}
pdu_length -= LDP_IDENTIFIER_LEN;
msg_start = pdu_start+LDP_MIN_PDU_LEN;
while(pdu_length >= LDP_MIN_MSG_LEN) {
session->stats.message_rx++;
msg_type = read_be_uint(msg_start, 2);
msg_length = read_be_uint(msg_start+2, 2);
msg_id = read_be_uint(msg_start+4, 4);
if(msg_length+4 > pdu_length) {
ldp_decode_error(session);
return;
}
LOG(DEBUG, "LDP (%s - %s) read %s message (%u)\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id),
keyval_get_key(ldp_msg_names, msg_type), msg_id);
ldp_session_restart_keepalive_timeout(session);
switch(msg_type) {
case LDP_MESSAGE_TYPE_NOTIFICATION:
if(!ldp_notification(session, msg_start+8, msg_length-4)) {
ldp_decode_error(session);
return;
}
break;
case LDP_MESSAGE_TYPE_INITIALIZATION:
if(!ldp_initialization(session, msg_start+8, msg_length-4)) {
ldp_decode_error(session);
return;
}
break;
case LDP_MESSAGE_TYPE_KEEPALIVE:
session->stats.keepalive_rx++;
ldp_session_fsm(session, LDP_EVENT_RX_KEEPALIVE);
break;
case LDP_MESSAGE_TYPE_LABEL_MAPPING:
if(!ldp_label_mapping(session, msg_start+8, msg_length-4)) {
ldp_decode_error(session);
return;
}
break;
case LDP_MESSAGE_TYPE_ADDRESS:
case LDP_MESSAGE_TYPE_ADDRESS_WITHDRAW:
case LDP_MESSAGE_TYPE_LABEL_REQUEST:
case LDP_MESSAGE_TYPE_LABEL_WITHDRAW:
case LDP_MESSAGE_TYPE_LABEL_RELEASE:
case LDP_MESSAGE_TYPE_ABORT_REQUEST:
break;
default:
break;
}
pdu_length -= (msg_length+4);
msg_start += (msg_length+4);
}
/* Progress pointer to next LDP PDU. */
buffer->start_idx += length;
}
ldp_rebase_read_buffer(buffer);
}
void
ldp_receive_cb(void *arg, uint8_t *buf, uint16_t len)
{
ldp_session_s *session = (ldp_session_s*)arg;
io_buffer_t *buffer = &session->read_buf;
if(buf) {
if(buffer->idx+len > buffer->size) {
LOG(ERROR, "LDP (%s - %s) receive error (read buffer exhausted)\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id));
if(!session->error_code) {
session->error_code = LDP_STATUS_INTERNAL_ERROR|LDP_STATUS_FATAL_ERROR;
}
ldp_session_close(session);
return;
}
memcpy(buffer->data+buffer->idx, buf, len);
buffer->idx+=len;
} else {
ldp_read(session);
}
}
+15
View File
@@ -0,0 +1,15 @@
/*
* BNG Blaster (BBL) - LDP Receive Functions
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __BBL_LDP_RECEIVE_H__
#define __BBL_LDP_RECEIVE_H__
void
ldp_receive_cb(void *arg, uint8_t *buf, uint16_t len);
#endif
+561
View File
@@ -0,0 +1,561 @@
/*
* BNG Blaster (BBL) - LDP Session
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "ldp.h"
extern bool g_init_phase;
extern volatile bool g_teardown;
const char *
ldp_session_state_string(ldp_state_t state)
{
switch(state) {
case LDP_CLOSED: return "closed";
case LDP_IDLE: return "idle";
case LDP_LISTEN: return "listen";
case LDP_CONNECT: return "connect";
case LDP_INITIALIZED: return "initialized";
case LDP_OPENREC: return "open-received";
case LDP_OPENSENT: return "open-sent";
case LDP_OPERATIONAL: return "operational";
case LDP_CLOSING: return "closing";
case LDP_ERROR: return "error";
default: return "unknown";
}
}
static void
ldp_session_state_change(ldp_session_s *session, ldp_state_t new_state)
{
if(session->state != new_state) {
if(session->state == LDP_OPERATIONAL || new_state == LDP_OPERATIONAL) {
session->state_transitions++;
}
LOG(LDP, "LDP (%s - %s) state changed from %s -> %s\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id),
ldp_session_state_string(session->state),
ldp_session_state_string(new_state));
session->state = new_state;
}
}
/**
* ldp_session_reset_read_buffer
*
* @param session LDP session
*/
void
ldp_session_reset_read_buffer(ldp_session_s *session)
{
session->read_buf.idx = 0;
session->read_buf.start_idx = 0;
}
/**
* ldp_session_reset_write_buffer
*
* @param session LDP session
*/
void
ldp_session_reset_write_buffer(ldp_session_s *session)
{
if(session->tcpc && session->tcpc->state == BBL_TCP_STATE_SENDING) {
return;
}
session->write_buf.idx = 0;
session->write_buf.start_idx = 0;
}
/**
* ldp_session_send
*
* @param session LDP session
*/
static bool
ldp_session_send(ldp_session_s *session)
{
bbl_tcp_ctx_s *tcpc = session->tcpc;
if(tcpc && tcpc->state == BBL_TCP_STATE_SENDING &&
tcpc->tx.buf == session->write_buf.data &&
tcpc->tx.len < session->write_buf.idx) {
tcpc->tx.len = session->write_buf.idx;
return true;
}
return bbl_tcp_send(session->tcpc, session->write_buf.data, session->write_buf.idx);
}
void
ldp_raw_update_stop_cb(void *arg)
{
ldp_session_s *session = (ldp_session_s*)arg;
struct timespec time_diff;
session->tcpc->idle_cb = NULL;
clock_gettime(CLOCK_MONOTONIC, &session->update_stop_timestamp);
timespec_sub(&time_diff,
&session->update_stop_timestamp,
&session->update_start_timestamp);
session->raw_update_sending = false;
session->stats.pdu_tx += session->raw_update->pdu;
session->stats.message_tx += session->raw_update->messages;
LOG(LDP, "LDP (%s - %s) raw update stop after %lds\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id),
time_diff.tv_sec);
}
void
ldp_session_update_job(timer_s *timer)
{
ldp_session_s *session = timer->data;
if(session->state == LDP_OPERATIONAL) {
if(session->raw_update && !session->raw_update_sending) {
if(bbl_tcp_send(session->tcpc, session->raw_update->buf, session->raw_update->len)) {
session->raw_update_sending = true;
LOG(LDP, "LDP (%s - %s) raw update start\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id));
clock_gettime(CLOCK_MONOTONIC, &session->update_start_timestamp);
session->tcpc->idle_cb = ldp_raw_update_stop_cb;
} else {
goto RETRY;
}
}
}
timer->periodic = false;
return;
RETRY:
/* Try again ... */
timer_add_periodic(&g_ctx->timer_root, &session->connect_timer,
"LDP UPDATE", 1, 0, session,
&ldp_session_update_job);
}
void
ldp_session_keepalive_job(timer_s *timer)
{
ldp_session_s *session = timer->data;
if(session->state == LDP_OPERATIONAL) {
if(session->tcpc && session->tcpc->state == BBL_TCP_STATE_IDLE) {
ldp_session_reset_write_buffer(session);
ldp_pdu_init(session);
ldp_push_keepalive_message(session);
ldp_pdu_close(session);
ldp_session_send(session);
}
}
}
void
ldp_session_keepalive_timeout_job(timer_s *timer)
{
ldp_session_s *session = timer->data;
LOG(LDP, "LDP (%s - %s) keepalive timer expired\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id));
if(!session->error_code) {
session->error_code = LDP_STATUS_KEEPALIVE_TIMER_EXPIRED|LDP_STATUS_FATAL_ERROR;
}
ldp_session_close(session);
}
void
ldp_session_restart_keepalive_timeout(ldp_session_s *session)
{
timer_add(&g_ctx->timer_root, &session->keepalive_timeout_timer,
"LDP TIMEOUT", session->keepalive_time, 0, session, &ldp_session_keepalive_timeout_job);
}
static void
ldp_session_operational(ldp_session_s *session)
{
time_t keepalive_interval;
ldp_session_state_change(session, LDP_OPERATIONAL);
clock_gettime(CLOCK_MONOTONIC, &session->operational_timestamp);
/* Select max PDU length. */
if(session->peer.max_pdu_len > 255 &&
session->peer.max_pdu_len < session->local.max_pdu_len) {
session->max_pdu_len = session->peer.max_pdu_len;
} else {
session->max_pdu_len = session->local.max_pdu_len;
}
/* Start LDP keepalive. */
if(session->peer.keepalive_time > 0 &&
session->peer.keepalive_time < session->local.keepalive_time) {
session->keepalive_time = session->peer.keepalive_time;
} else {
session->keepalive_time = session->local.keepalive_time;
}
keepalive_interval = session->keepalive_time/3U;
if(!keepalive_interval) {
keepalive_interval = 1;
}
timer_add_periodic(&g_ctx->timer_root, &session->keepalive_timer,
"LDP KEEPALIVE", keepalive_interval, 0, session,
&ldp_session_keepalive_job);
/* Start LDP updates. */
timer_add(&g_ctx->timer_root, &session->update_timer,
"LDP UPDATE", 0, 0, session,
&ldp_session_update_job);
}
void
ldp_session_fsm(ldp_session_s *session, ldp_event_t event)
{
switch(event) {
case LDP_EVENT_START:
ldp_session_state_change(session, LDP_INITIALIZED);
if(session->active) {
ldp_session_reset_write_buffer(session);
ldp_pdu_init(session);
ldp_push_init_message(session);
ldp_pdu_close(session);
ldp_session_send(session);
ldp_session_state_change(session, LDP_OPENSENT);
}
break;
case LDP_EVENT_RX_INITIALIZED:
if(session->state == LDP_INITIALIZED) {
ldp_session_reset_write_buffer(session);
ldp_pdu_init(session);
ldp_push_init_message(session);
ldp_push_keepalive_message(session);
ldp_pdu_close(session);
ldp_session_send(session);
ldp_session_state_change(session, LDP_OPENREC);
} else if(session->state == LDP_OPENSENT) {
ldp_session_reset_write_buffer(session);
ldp_pdu_init(session);
ldp_push_keepalive_message(session);
ldp_pdu_close(session);
ldp_session_send(session);
ldp_session_state_change(session, LDP_OPENREC);
} else {
if(!session->error_code) {
session->error_code = LDP_STATUS_INTERNAL_ERROR|LDP_STATUS_FATAL_ERROR;
}
ldp_session_close(session);
}
break;
case LDP_EVENT_RX_KEEPALIVE:
if(session->state == LDP_OPENREC) {
ldp_session_operational(session);
ldp_session_reset_write_buffer(session);
ldp_pdu_init(session);
ldp_push_self_message(session);
ldp_pdu_close(session);
ldp_session_send(session);
}
break;
default:
break;
}
}
err_t
ldp_accepted_cb(bbl_tcp_ctx_s *tcpc, void *arg)
{
ldp_session_s *session = (ldp_session_s*)arg;
if(session->state == LDP_LISTEN) {
session->tcpc = tcpc;
} else {
return ERR_ABRT;
}
return ERR_OK;
}
void
ldp_connected_cb(void *arg)
{
ldp_session_s *session = (ldp_session_s*)arg;
g_ctx->routing_sessions++;
ldp_session_fsm(session, LDP_EVENT_START);
}
void
ldp_error_cb(void *arg, err_t err) {
ldp_session_s *session = (ldp_session_s*)arg;
LOG(LDP, "LDP (%s - %s) TCP error %d (%s)\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id),
err, tcp_err_string(err));
ldp_session_state_change(session, LDP_ERROR);
ldp_session_close(session);
}
void
ldp_session_connect_job(timer_s *timer)
{
ldp_session_s *session = timer->data;
time_t timeout = 5;
if(g_init_phase) {
/* Wait for all network interfaces to be resolved */
timeout = 1;
} else if(session->state == LDP_IDLE) {
/* Connect TCP session */
session->tcpc = bbl_tcp_ipv4_connect(
session->interface,
&session->local.ipv4_address,
&session->peer.ipv4_address,
LDP_PORT);
if(session->tcpc) {
session->tcpc->arg = session;
session->tcpc->connected_cb = ldp_connected_cb;
session->tcpc->receive_cb = ldp_receive_cb;
session->tcpc->error_cb = ldp_error_cb;
ldp_session_state_change(session, LDP_CONNECT);
/* Close session if not established within 60 seconds */
timeout = 60;
} else {
LOG(LDP, "LDP (%s - %s) TCP connect failed\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id));
}
} else if(session->state == LDP_OPERATIONAL) {
timer->periodic = false;
return;
} else {
LOG(LDP, "LDP (%s - %s) connect timeout\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id));
ldp_session_close(session);
timer->periodic = false;
return;
}
timer_add_periodic(&g_ctx->timer_root, &session->connect_timer,
"LDP CONNECT", timeout, 0, session,
&ldp_session_connect_job);
}
static void
ldp_session_listen(ldp_session_s *session)
{
session->listen_tcpc = bbl_tcp_ipv4_listen(
session->interface,
&session->local.ipv4_address,
LDP_PORT);
if(session->listen_tcpc) {
session->listen_tcpc->arg = session;
session->listen_tcpc->accepted_cb = ldp_accepted_cb;
session->listen_tcpc->connected_cb = ldp_connected_cb;
session->listen_tcpc->receive_cb = ldp_receive_cb;
session->listen_tcpc->error_cb = ldp_error_cb;
ldp_session_state_change(session, LDP_LISTEN);
timer_add_periodic(&g_ctx->timer_root, &session->connect_timer,
"LDP CONNECT", 60, 0, session,
&ldp_session_connect_job);
} else {
LOG(LDP, "LDP (%s - %s) TCP listen failed\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id));
ldp_session_close(session);
}
}
/**
* ldp_session_connect
*
* @param session BGP session
* @param delay delay
*/
void
ldp_session_connect(ldp_session_s *session, time_t delay)
{
if(!session->teardown && session->state == LDP_CLOSED) {
bbl_tcp_ctx_free(session->tcpc);
bbl_tcp_ctx_free(session->listen_tcpc);
session->tcpc = NULL;
session->listen_tcpc = NULL;
ldp_session_reset_read_buffer(session);
ldp_session_reset_write_buffer(session);
session->pdu_start_idx = 0;
session->msg_start_idx = 0;
session->tlv_start_idx = 0;
session->message_id = 0;
session->error_code = 0;
session->decode_error = false;
session->max_pdu_len = LDP_MAX_PDU_LEN_INIT;
session->keepalive_time = session->local.keepalive_time;
session->stats.pdu_rx = 0;
session->stats.pdu_tx = 0;
session->stats.message_rx = 0;
session->stats.message_tx = 0;
session->stats.keepalive_rx = 0;
session->stats.keepalive_tx = 0;
session->raw_update = session->raw_update_start;
session->raw_update_sending = false;
session->operational_timestamp.tv_sec = 0;
session->operational_timestamp.tv_nsec = 0;
session->update_start_timestamp.tv_sec = 0;
session->update_start_timestamp.tv_nsec = 0;
session->update_stop_timestamp.tv_sec = 0;
session->update_stop_timestamp.tv_nsec = 0;
if(session->active) {
ldp_session_state_change(session, LDP_IDLE);
timer_add(&g_ctx->timer_root, &session->connect_timer,
"LDP CONNECT", delay, 0, session,
&ldp_session_connect_job);
} else {
ldp_session_listen(session);
}
}
}
/**
* ldp_session_init
*
* @param session LDP session (optional)
* @param adjacency LDP adjacency
* @param ipv4 received IPv4 header
* @param ldp received LDP hello PDU
*/
void
ldp_session_init(ldp_session_s *session, ldp_adjacency_s *adjacency,
bbl_ipv4_s *ipv4, bbl_ldp_hello_s *ldp)
{
ldp_instance_s *instance = adjacency->instance;
ldp_config_s *config = instance->config;
if(!session) {
session = calloc(1, sizeof(ldp_session_s));
session->instance = instance;
session->local.ipv4_address = config->ipv4_transport_address;
session->local.lsr_id = config->lsr_id;
session->local.label_space_id = 0;
session->local.keepalive_time = config->keepalive_time;
session->local.max_pdu_len = LDP_MAX_PDU_LEN_INIT;
if(config->raw_update_file) {
session->raw_update_start = ldp_raw_update_load(config->raw_update_file, true);
}
session->next = instance->sessions;
instance->sessions = session;
}
session->interface = adjacency->interface;
session->max_pdu_len = session->local.max_pdu_len;
session->keepalive_time = session->local.keepalive_time;
if(ldp->ipv4_transport_address) {
session->peer.ipv4_address = ldp->ipv4_transport_address;
} else {
session->peer.ipv4_address = ipv4->src;
}
session->peer.lsr_id = ldp->lsr_id;
session->peer.label_space_id = ldp->label_space_id;
session->peer.keepalive_time = 0;
session->peer.max_pdu_len = 0;
/* Init read/write buffer */
session->read_buf.data = malloc(LDP_BUF_SIZE);
session->read_buf.size = LDP_BUF_SIZE;
session->write_buf.data = malloc(LDP_BUF_SIZE);
session->write_buf.size = LDP_BUF_SIZE;
if(be32toh(session->local.ipv4_address) > be32toh(session->peer.ipv4_address)) {
session->active = true;
} else {
session->active = false;
}
ldp_session_connect(session, 0);
}
void
ldp_session_close_job(timer_s *timer)
{
ldp_session_s *session = timer->data;
if(g_ctx->routing_sessions) g_ctx->routing_sessions--;
/* Close TCP session */
if(session->listen_tcpc) {
bbl_tcp_ctx_free(session->listen_tcpc);
session->listen_tcpc = NULL;
}
if(session->state > LDP_IDLE) {
bbl_tcp_ctx_free(session->tcpc);
session->tcpc = NULL;
}
ldp_session_state_change(session, LDP_CLOSED);
if(!session->teardown) {
ldp_session_connect(session, 5);
}
}
/**
* ldp_session_close
*
* @param session LDP session
*/
void
ldp_session_close(ldp_session_s *session)
{
time_t delay = 0;
LOG(LDP, "LDP (%s - %s) close session\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id));
/* Stop all timers */
timer_del(session->connect_timer);
timer_del(session->keepalive_timer);
timer_del(session->keepalive_timeout_timer);
timer_del(session->update_timer);
if(!session->error_code) {
session->error_code = LDP_STATUS_SHUTDOWN|LDP_STATUS_FATAL_ERROR;
}
if(session->state > LDP_CONNECT && session->state < LDP_CLOSING) {
LOG(LDP, "LDP (%s - %s) send notification message\n",
ldp_id_to_str(session->local.lsr_id, session->local.label_space_id),
ldp_id_to_str(session->peer.lsr_id, session->peer.label_space_id));
ldp_session_reset_write_buffer(session);
ldp_pdu_init(session);
ldp_push_notification_message(session);
ldp_pdu_close(session);
ldp_session_send(session);
session->stats.pdu_tx++;
session->stats.message_tx++;
ldp_session_state_change(session, LDP_CLOSING);
delay = 3;
}
timer_add(&g_ctx->timer_root, &session->close_timer,
"LDP CLOSE", delay, 0, session,
&ldp_session_close_job);
}
+34
View File
@@ -0,0 +1,34 @@
/*
* BNG Blaster (BBL) - LDP Session
*
* Christian Giese, November 2022
*
* Copyright (C) 2020-2022, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __BBL_LDP_SESSION_H__
#define __BBL_LDP_SESSION_H__
const char *
ldp_session_state_string(ldp_state_t state);
void
ldp_session_update_job(timer_s *timer);
void
ldp_session_restart_keepalive_timeout(ldp_session_s *session);
void
ldp_session_fsm(ldp_session_s *session, ldp_event_t event);
void
ldp_session_connect(ldp_session_s *session, time_t delay);
void
ldp_session_init(ldp_session_s *session, ldp_adjacency_s *adjacency,
bbl_ipv4_s *ipv4, bbl_ldp_hello_s *ldp);
void
ldp_session_close(ldp_session_s *session);
#endif
+3
View File
@@ -53,6 +53,9 @@
#define POWEROF2(x) ((((x)-1) & (x)) == 0) /* true if x is a power of 2 */
#define BITS_TO_BYTES(_len) ((_len+7) >> 3)
#define IO_BUF_REMAINING(_buf) (_buf->size - _buf->idx)
/* Key-Value structure. */
typedef struct keyval_ {
uint32_t val; /* value */
+1
View File
@@ -36,6 +36,7 @@ enum {
L2TP,
DHCP,
ISIS,
LDP,
BGP,
TCP,
LSDB,
+1 -1
View File
@@ -23,7 +23,7 @@ bool push_le_uint(struct io_buffer_ *buffer, uint32_t length, uint64_t value);
const char *val2key(struct keyval_ *keyval, uint32_t val);
uint32_t key2val(struct keyval_ *ptr, const char *key);
const char *keyval_get_key (struct keyval_ *keyval, uint32_t val);
const char *keyval_get_key(struct keyval_ *keyval, uint32_t val);
char *format_mac_address(uint8_t *mac);
char *format_ipv4_address(uint32_t *addr4);
+213
View File
@@ -0,0 +1,213 @@
#!/usr/bin/env python3
"""
LDP RAW Update Generator
Christian Giese, December 2022
Copyright (C) 2020-2022, RtBrick, Inc.
SPDX-License-Identifier: BSD-3-Clause
"""
import argparse
import ipaddress
import logging
import struct
import sys
try:
from scapy.all import *
log_runtime.setLevel(logging.ERROR)
from scapy.contrib.ldp import *
log_runtime.setLevel(logging.INFO)
except:
print("Failed to load scapy!")
exit(1)
# ==============================================================
# PATCHES
# ==============================================================
# Monkey patch scapy LDP FecTLVField
def _monkey_i2m(self, pkt, x):
if not x:
return b""
if isinstance(x, bytes):
return x
s = b"\x01\x00"
tmp_len = 0
fec = b""
for o in x:
fec += b"\x02\x00\x01"
# mask length
fec += struct.pack("!B", o[1])
# Prefix
l = o[1]+7>>3
p = inet_aton(o[0])
fec += p[:l]
tmp_len += 4+l
s += struct.pack("!H", tmp_len)
s += fec
return s
scapy.contrib.ldp.FecTLVField.i2m = _monkey_i2m
# ==============================================================
# DEFINITIONS
# ==============================================================
DESCRIPTION = """
The LDP RAW update generator is a simple
tool to generate LDP RAW update streams
for use with the BNG Blaster.
"""
LOG_LEVELS = {
'warning': logging.WARNING,
'info': logging.INFO,
'debug': logging.DEBUG
}
MPLS_LABEL_MIN = 1
MPLS_LABEL_MAX = 1048575
# ==============================================================
# FUNCTIONS
# ==============================================================
def init_logging(log_level: int) -> logging.Logger:
"""Init logging."""
level = LOG_LEVELS[log_level]
log = logging.getLogger()
log.setLevel(level)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(level)
formatter = logging.Formatter('[%(asctime)s][%(levelname)-7s] %(message)s')
formatter.datefmt = '%Y-%m-%d %H:%M:%S'
handler.setFormatter(formatter)
log.addHandler(handler)
return log
def label_type(label: int) -> int:
"""Argument parser type for MPLS labels."""
label = int(label)
if label < MPLS_LABEL_MIN or label > MPLS_LABEL_MAX:
raise argparse.ArgumentTypeError("MPLS label out of range %s - %s" % (MPLS_LABEL_MIN, MPLS_LABEL_MAX))
return label
# ==============================================================
# MAIN
# ==============================================================
def main():
# parse arguments
parser = argparse.ArgumentParser(description=DESCRIPTION)
parser.add_argument('-l', '--lsr-id', metavar='ADDRESS', type=ipaddress.ip_address, required=True, help='LSR identifier')
parser.add_argument('-i', '--message-id-base', metavar='N', type=int, default=1000, help='message identifier base')
parser.add_argument('-w', '--withdraw', action="store_true", help='withdraw')
parser.add_argument('-a', '--address-base', metavar='ADDRESS', type=ipaddress.ip_address, help='address message base')
parser.add_argument('-A', '--address-num', metavar='N', type=int, default=0, help='address message count')
parser.add_argument('-p', '--prefix-base', metavar='PREFIX', type=ipaddress.ip_network, help='label mapping base prefix')
parser.add_argument('-P', '--prefix-num', metavar='N', type=int, default=0, help='label mapping prefix count')
parser.add_argument('-m', '--label-base', metavar='LABEL', type=label_type, default=10000, help='label base')
parser.add_argument('-M', '--label-num', metavar='N', type=int, default=1, help='label count')
parser.add_argument('-f', '--file', type=str, default="out.ldp", help='output file')
parser.add_argument('--append', action="store_true", help="append to file if exist")
parser.add_argument('--pcap', metavar='FILE', type=str, help="write LDP updates to PCAP file")
parser.add_argument('--log-level', type=str, default='info', choices=LOG_LEVELS.keys(), help='logging Level')
args = parser.parse_args()
# init logging
log = init_logging(args.log_level)
# Here we will store packets for optional PCAP output
pcap_packets = []
def pcap(message):
if args.pcap:
pcap_packets.append(Ether()/IP()/TCP(sport=len(pcap_packets)+10000, dport=646, seq=1, flags='PA')/message)
addresses = []
if args.address_base and args.address_num > 0:
log.info("init %s addresses" % (args.address_num))
address = args.address_base
for _ in range(args.address_num):
addresses.append(address)
address += 1
prefixes = []
if args.prefix_base and args.prefix_num > 0:
if args.prefix_base.version != 4:
log.error("prefix must be IPv4")
exit(1)
log.info("init %s labeled IPv4 prefixes" % (args.prefix_num))
prefix = args.prefix_base
label_index = 0
label = args.label_base
for _ in range(args.prefix_num):
log.debug("add prefix %s via label %s" % (prefix, label))
prefixes.append((prefix, label))
label_index += 1
if label_index < args.label_num:
label = args.label_base + label_index
if label > MPLS_LABEL_MAX:
label_index = 0
label = args.label_base
else:
label_index = 0
label = args.label_base
prefix = ipaddress.ip_network("%s/%s" % (prefix.broadcast_address+1, prefix.prefixlen))
if len(addresses) + len(prefixes) == 0:
exit(0)
if args.append:
log.info("open file %s (append)" % args.file)
file_flags = "ab"
else:
log.info("open file %s (replace)" % args.file)
file_flags = "wb"
with open(args.file, file_flags) as f:
mid = args.message_id_base
for address in addresses:
if args.withdraw:
pdu = LDP(id=args.lsr_id)/LDPAddressWM(id=mid, address=[str(address)])
else:
pdu = LDP(id=args.lsr_id)/LDPAddress(id=mid, address=[str(address)])
mid+=1
pdu_bin = bytearray(pdu.build())
pcap(pdu)
f.write(pdu_bin)
for prefix, label in prefixes:
if args.withdraw:
pdu = LDP(id=args.lsr_id)/LDPLabelWM(id=mid, fec=[(str(prefix.network_address),int(prefix.prefixlen))],label=label)
else:
pdu = LDP(id=args.lsr_id)/LDPLabelMM(id=mid, fec=[(str(prefix.network_address),int(prefix.prefixlen))],label=label)
mid+=1
pdu_bin = bytearray(pdu.build())
pcap(pdu)
f.write(pdu_bin)
if args.pcap:
log.info("create PCAP file %s" % args.pcap)
try:
wrpcap(args.pcap, pcap_packets)
except Exception as e:
log.error("failed to create PCAP file")
log.debug(e)
log.info("finished")
if __name__ == "__main__":
main()
+84 -73
View File
@@ -54,6 +54,7 @@ static struct option long_options[] = {
{"lsp-lifetime", required_argument, NULL, 'M'},
{"no-ipv4", no_argument, NULL, 'z'},
{"no-ipv6", no_argument, NULL, 'Z'},
{"no-sr", no_argument, NULL, 'y'},
{"external-count", required_argument, NULL, 'e'},
{"graphviz-file", required_argument, NULL, 'g'},
{"help", no_argument, NULL, 'h'},
@@ -369,7 +370,7 @@ lspgen_gen_attr(struct lsdb_ctx_ *ctx)
lsdb_add_node_attr(node, &attr_template);
}
/* ipv4 loopback address */
/* IPv4 loopback address */
lsdb_reset_attr_template(&attr_template);
addr = lspgen_load_addr((uint8_t*)&ctx->ipv4_node_prefix.address, sizeof(ipv4addr_t));
addr += node->node_index;
@@ -378,18 +379,20 @@ lspgen_gen_attr(struct lsdb_ctx_ *ctx)
attr_template.key.attr_type = ISIS_TLV_IPV4_ADDR;
lsdb_add_node_attr(node, &attr_template);
/* ipv4 loopback prefix */
/* IPv4 loopback prefix */
lsdb_reset_attr_template(&attr_template);
lspgen_store_addr(addr, (uint8_t*)&attr_template.key.prefix.ipv4_prefix.address, sizeof(ipv4addr_t));
attr_template.key.prefix.ipv4_prefix.len = ctx->ipv4_node_prefix.len;
attr_template.key.prefix.sid = node->node_index;
attr_template.key.prefix.adv_sid = true;
if (!ctx->no_sr) {
attr_template.key.prefix.sid = node->node_index;
attr_template.key.prefix.adv_sid = true;
}
attr_template.key.prefix.node_flag = true;
attr_template.key.ordinal = 1;
attr_template.key.attr_type = ISIS_TLV_EXTD_IPV4_REACH;
lsdb_add_node_attr(node, &attr_template);
/* ipv6 loopback address */
/* IPv6 loopback address */
lsdb_reset_attr_template(&attr_template);
addr = lspgen_load_addr(ctx->ipv6_node_prefix.address, IPV6_ADDR_LEN);
addr += node->node_index;
@@ -398,12 +401,14 @@ lspgen_gen_attr(struct lsdb_ctx_ *ctx)
attr_template.key.attr_type = ISIS_TLV_IPV6_ADDR;
lsdb_add_node_attr(node, &attr_template);
/* ipv6 loopback prefix */
/* IPv6 loopback prefix */
lsdb_reset_attr_template(&attr_template);
lspgen_store_addr(addr, attr_template.key.prefix.ipv6_prefix.address, IPV6_ADDR_LEN);
attr_template.key.prefix.ipv6_prefix.len = ctx->ipv6_node_prefix.len;
attr_template.key.prefix.sid = ctx->srgb_range/2 + node->node_index;
attr_template.key.prefix.adv_sid = true;
if (!ctx->no_sr) {
attr_template.key.prefix.sid = ctx->srgb_range/2 + node->node_index;
attr_template.key.prefix.adv_sid = true;
}
attr_template.key.prefix.node_flag = true;
attr_template.key.ordinal = 1;
attr_template.key.attr_type = ISIS_TLV_EXTD_IPV6_REACH;
@@ -431,26 +436,28 @@ lspgen_gen_attr(struct lsdb_ctx_ *ctx)
ext_addr6 += ext_incr6;
}
/* SR capability */
lsdb_reset_attr_template(&attr_template);
addr = lspgen_load_addr((uint8_t*)&ctx->ipv4_node_prefix.address, sizeof(ipv4addr_t));
addr += node->node_index;
lspgen_store_addr(addr, attr_template.key.cap.router_id, sizeof(ipv4addr_t));
attr_template.key.cap.srgb_base = ctx->srgb_base;
attr_template.key.cap.srgb_range = ctx->srgb_range;
if (!ctx->no_ipv4) {
attr_template.key.cap.mpls_ipv4_flag = true; /* mpls ipv4 */
if (!ctx->no_sr) {
/* SR capability */
lsdb_reset_attr_template(&attr_template);
addr = lspgen_load_addr((uint8_t*)&ctx->ipv4_node_prefix.address, sizeof(ipv4addr_t));
addr += node->node_index;
lspgen_store_addr(addr, attr_template.key.cap.router_id, sizeof(ipv4addr_t));
attr_template.key.cap.srgb_base = ctx->srgb_base;
attr_template.key.cap.srgb_range = ctx->srgb_range;
if (!ctx->no_ipv4) {
attr_template.key.cap.mpls_ipv4_flag = true; /* mpls ipv4 */
}
if (!ctx->no_ipv6) {
attr_template.key.cap.mpls_ipv6_flag = true; /* mpls ipv6 */
}
attr_template.key.attr_type = ISIS_TLV_CAP;
attr_template.key.ordinal = 1;
lsdb_add_node_attr(node, &attr_template);
}
if (!ctx->no_ipv6) {
attr_template.key.cap.mpls_ipv6_flag = true; /* mpls ipv6 */
}
attr_template.key.attr_type = ISIS_TLV_CAP;
attr_template.key.ordinal = 1;
lsdb_add_node_attr(node, &attr_template);
/*
* Walk all of our neighbors.
*/
* Walk all of our neighbors.
*/
CIRCLEQ_FOREACH(link, &node->link_qhead, link_qnode) {
/* Generate an IS reach for each link */
@@ -460,7 +467,7 @@ lspgen_gen_attr(struct lsdb_ctx_ *ctx)
attr_template.key.link.metric = link->link_metric;
lsdb_add_node_attr(node, &attr_template);
/* Generate an ipv4 prefix for each link */
/* Generate an IPv4 prefix for each link */
lsdb_reset_attr_template(&attr_template);
addr = lspgen_load_addr((uint8_t*)&ctx->ipv4_link_prefix.address, sizeof(ipv4addr_t));
inc = lspgen_get_prefix_inc(AF_INET, ctx->ipv4_link_prefix.len);
@@ -471,7 +478,7 @@ lspgen_gen_attr(struct lsdb_ctx_ *ctx)
attr_template.key.attr_type = ISIS_TLV_EXTD_IPV4_REACH;
lsdb_add_node_attr(node, &attr_template);
/* Generate an ipv6 prefix for each link */
/* Generate an IPv6 prefix for each link */
lsdb_reset_attr_template(&attr_template);
addr = lspgen_load_addr(ctx->ipv6_link_prefix.address, IPV6_ADDR_LEN);
inc = lspgen_get_prefix_inc(AF_INET6, ctx->ipv6_link_prefix.len);
@@ -549,7 +556,7 @@ lspgen_compute_end_prefix4(struct ipv4_prefix_ *start_prefix, unsigned int num_p
end_prefix = *start_prefix;
if (!num_prefixes) {
return &end_prefix;
return &end_prefix;
}
prefix_inc = lspgen_get_prefix_inc(AF_INET, start_prefix->len);
@@ -569,7 +576,7 @@ lspgen_compute_end_prefix6(struct ipv6_prefix_ *start_prefix, unsigned int num_p
end_prefix = *start_prefix;
if (!num_prefixes) {
return &end_prefix;
return &end_prefix;
}
prefix_inc = lspgen_get_prefix_inc(AF_INET6, start_prefix->len);
@@ -605,36 +612,36 @@ lspgen_log_ctx(struct lsdb_ctx_ *ctx)
ctx->authentication_key, val2key(isis_auth_names, ctx->authentication_type));
}
if (!ctx->no_ipv4) {
end_prefix4 = lspgen_compute_end_prefix4(&ctx->ipv4_node_prefix, ctx->num_nodes);
LOG(NORMAL, " IPv4 Node Base Prefix %s, End Prefix %s, %u prefixes\n",
format_ipv4_prefix(&ctx->ipv4_node_prefix),
format_ipv4_prefix(end_prefix4), ctx->num_nodes);
end_prefix4 = lspgen_compute_end_prefix4(&ctx->ipv4_node_prefix, ctx->num_nodes);
LOG(NORMAL, " IPv4 Node Base Prefix %s, End Prefix %s, %u prefixes\n",
format_ipv4_prefix(&ctx->ipv4_node_prefix),
format_ipv4_prefix(end_prefix4), ctx->num_nodes);
end_prefix4 = lspgen_compute_end_prefix4(&ctx->ipv4_link_prefix, ctx->num_nodes*2);
LOG(NORMAL, " IPv4 Link Base Prefix %s, End Prefix %s, %u prefixes\n",
format_ipv4_prefix(&ctx->ipv4_link_prefix),
format_ipv4_prefix(end_prefix4), ctx->num_nodes*2);
end_prefix4 = lspgen_compute_end_prefix4(&ctx->ipv4_link_prefix, ctx->num_nodes*2);
LOG(NORMAL, " IPv4 Link Base Prefix %s, End Prefix %s, %u prefixes\n",
format_ipv4_prefix(&ctx->ipv4_link_prefix),
format_ipv4_prefix(end_prefix4), ctx->num_nodes*2);
end_prefix4 = lspgen_compute_end_prefix4(&ctx->ipv4_ext_prefix, ctx->num_ext);
LOG(NORMAL, " IPv4 External Base Prefix %s, End Prefix %s, %u prefixes\n",
format_ipv4_prefix(&ctx->ipv4_ext_prefix),
format_ipv4_prefix(end_prefix4), ctx->num_ext);
end_prefix4 = lspgen_compute_end_prefix4(&ctx->ipv4_ext_prefix, ctx->num_ext);
LOG(NORMAL, " IPv4 External Base Prefix %s, End Prefix %s, %u prefixes\n",
format_ipv4_prefix(&ctx->ipv4_ext_prefix),
format_ipv4_prefix(end_prefix4), ctx->num_ext);
}
if (!ctx->no_ipv6) {
end_prefix6 = lspgen_compute_end_prefix6(&ctx->ipv6_node_prefix, ctx->num_nodes);
LOG(NORMAL, " IPv6 Node Base Prefix %s, End Prefix %s, %u prefixes\n",
format_ipv6_prefix(&ctx->ipv6_node_prefix),
format_ipv6_prefix(end_prefix6), ctx->num_nodes);
end_prefix6 = lspgen_compute_end_prefix6(&ctx->ipv6_node_prefix, ctx->num_nodes);
LOG(NORMAL, " IPv6 Node Base Prefix %s, End Prefix %s, %u prefixes\n",
format_ipv6_prefix(&ctx->ipv6_node_prefix),
format_ipv6_prefix(end_prefix6), ctx->num_nodes);
end_prefix6 = lspgen_compute_end_prefix6(&ctx->ipv6_link_prefix, ctx->num_nodes*2);
LOG(NORMAL, " IPv6 Link Base Prefix %s, End Prefix %s, %u prefixes\n",
format_ipv6_prefix(&ctx->ipv6_link_prefix),
format_ipv6_prefix(end_prefix6), ctx->num_nodes);
end_prefix6 = lspgen_compute_end_prefix6(&ctx->ipv6_link_prefix, ctx->num_nodes*2);
LOG(NORMAL, " IPv6 Link Base Prefix %s, End Prefix %s, %u prefixes\n",
format_ipv6_prefix(&ctx->ipv6_link_prefix),
format_ipv6_prefix(end_prefix6), ctx->num_nodes);
end_prefix6 = lspgen_compute_end_prefix6(&ctx->ipv6_ext_prefix, ctx->num_ext);
LOG(NORMAL, " IPv6 External Base Prefix %s, End Prefix %s, %u prefixes\n",
format_ipv6_prefix(&ctx->ipv6_ext_prefix),
format_ipv6_prefix(end_prefix6), ctx->num_ext);
end_prefix6 = lspgen_compute_end_prefix6(&ctx->ipv6_ext_prefix, ctx->num_ext);
LOG(NORMAL, " IPv6 External Base Prefix %s, End Prefix %s, %u prefixes\n",
format_ipv6_prefix(&ctx->ipv6_ext_prefix),
format_ipv6_prefix(end_prefix6), ctx->num_ext);
}
LOG(NORMAL, " SRGB base %u, range %u\n", ctx->srgb_base, ctx->srgb_range);
}
@@ -648,16 +655,16 @@ lspgen_compute_srgb_range (struct lsdb_ctx_ *ctx)
unsigned int range;
if (ctx->no_ipv4 && ctx->no_ipv6) {
ctx->srgb_range = 0;
return;
ctx->srgb_range = 0;
return;
}
range = ctx->num_nodes * 2;
if (ctx->no_ipv4) {
range = ctx->num_nodes;
range = ctx->num_nodes;
}
if (ctx->no_ipv6) {
range = ctx->num_nodes;
range = ctx->num_nodes;
}
ctx->srgb_range = range;
@@ -756,7 +763,7 @@ main(int argc, char *argv[])
* Parse options.
*/
idx = 0;
while ((opt = getopt_long(argc, argv, "vha:c:C:e:f:g:l:L:m:M:n:K:N:p:q:Qr:s:S:t:T:V:w:x:X:zZ",
while ((opt = getopt_long(argc, argv, "vha:c:C:e:f:g:l:L:m:M:n:K:N:p:q:Qr:s:S:t:T:V:w:x:X:yzZ",
long_options, &idx)) != -1) {
switch (opt) {
case 'v':
@@ -790,6 +797,10 @@ main(int argc, char *argv[])
LOG(ERROR, "Set lsp-lifetime to min %us\n", ctx->lsp_lifetime);
}
break;
case 'y':
/* no-sr */
ctx->no_sr = true;
break;
case 'z':
/* no-ipv4 */
ctx->no_ipv4 = true;
@@ -812,7 +823,7 @@ main(int argc, char *argv[])
ctx->num_nodes = 5;
LOG(ERROR, "Set node count to minimal %u\n", ctx->num_nodes);
}
lspgen_compute_srgb_range(ctx);
lspgen_compute_srgb_range(ctx);
break;
case 'C':
/* connector */
@@ -923,7 +934,7 @@ main(int argc, char *argv[])
}
/*
* Bump the seqeunce numbers if there is a cache file.
* Bump the sequence numbers if there is a cache file.
*/
lspgen_read_seq_cache(ctx);
@@ -986,16 +997,16 @@ main(int argc, char *argv[])
ctx->ctrl_io_buf.size = CTRL_SOCKET_BUFSIZE;
ctx->ctrl_io_buf.data = malloc(ctx->ctrl_io_buf.size);
if (!ctx->ctrl_io_buf.data) {
goto flush_and_close;
goto EXIT;
}
timer_add_periodic(&ctx->timer_root, &ctx->ctrl_socket_connect_timer,
"connect", 1, 0, ctx, &lspgen_ctrl_connect_cb);
/*
* Wakup at least once a second doing nothing,
* such that we do not sleep while user tries to quit the timer loop.
*/
/*
* Wakeup at least once a second doing nothing,
* such that we do not sleep while user tries to quit the timer loop.
*/
timer_add_periodic(&ctx->timer_root, &ctx->ctrl_socket_wakeup_timer,
"wakeup", 1, 0, ctx, &lspgen_ctrl_wakeup_cb);
@@ -1005,14 +1016,14 @@ main(int argc, char *argv[])
*/
signal(SIGPIPE, SIG_IGN);
/*
* Keep running until Ctrl-C is hit.
*/
signal(SIGINT, lspgen_sig_handler);
/*
* Keep running until Ctrl-C is hit.
*/
signal(SIGINT, lspgen_sig_handler);
while (loop_running) {
timer_walk(&ctx->timer_root);
}
while (loop_running) {
timer_walk(&ctx->timer_root);
}
}
/*
@@ -1023,7 +1034,7 @@ main(int argc, char *argv[])
/*
* Flush and close all we have.
*/
flush_and_close:
EXIT:
lsdb_delete_ctx(ctx);
return 0;
}
+1
View File
@@ -72,6 +72,7 @@ typedef struct lsdb_ctx_
struct lsdb_node_id_ connector[3];
uint32_t num_connector;
uint32_t num_ext; /* number of external prefixes */
bool no_sr;
bool no_ipv4;
bool no_ipv6;
uint16_t lsp_lifetime;
+32 -33
View File
@@ -346,16 +346,16 @@ lspgen_serialize_attr(lsdb_attr_t *attr, io_buffer_t *buf)
break;
case ISIS_TLV_IS_REACH:
push_be_uint(buf, 1, 0); /* virtual */
metric = attr->key.link.metric;
if (metric > 63) {
metric = 63; /* limit metric to 6 bits */
}
metric = attr->key.link.metric;
if (metric > 63) {
metric = 63; /* limit metric to 6 bits */
}
push_be_uint(buf, 1, metric); /* default metric */
push_be_uint(buf, 1, 0x80); /* delay metric */
push_be_uint(buf, 1, 0x80); /* expense metric */
push_be_uint(buf, 1, 0x80); /* error metric */
push_data(buf, attr->key.link.remote_node_id, 7);
break;
break;
case ISIS_TLV_EXTD_IS_REACH:
push_data(buf, attr->key.link.remote_node_id, 7);
push_be_uint(buf, 3, attr->key.link.metric); /* Metric */
@@ -373,11 +373,11 @@ lspgen_serialize_attr(lsdb_attr_t *attr, io_buffer_t *buf)
break;
case ISIS_TLV_INT_IPV4_REACH: /* fall through */
case ISIS_TLV_EXT_IPV4_REACH:
metric = attr->key.prefix.metric;
if (metric > 63) {
metric = 63; /* limit metric to 6 bits */
}
flags = metric;
metric = attr->key.prefix.metric;
if (metric > 63) {
metric = 63; /* limit metric to 6 bits */
}
flags = metric;
if (attr->key.prefix.updown_flag) {
flags |= 0x80;
}
@@ -386,11 +386,11 @@ lspgen_serialize_attr(lsdb_attr_t *attr, io_buffer_t *buf)
push_be_uint(buf, 1, 0x80); /* expense metric */
push_be_uint(buf, 1, 0x80); /* error metric */
push_data(buf, (uint8_t*)&attr->key.prefix.ipv4_prefix.address, 4);
mask = 0xffffffff;
/* convert prefix length to mask */
mask &= ~((1 << ((32 - attr->key.prefix.ipv4_prefix.len)))-1);
push_be_uint(buf, 4, mask);
break;
mask = 0xffffffff;
/* convert prefix length to mask */
mask &= ~((1 << ((32 - attr->key.prefix.ipv4_prefix.len)))-1);
push_be_uint(buf, 4, mask);
break;
case ISIS_TLV_EXTD_IPV4_REACH:
push_be_uint(buf, 4, attr->key.prefix.metric); /* Metric */
flags = attr->key.prefix.ipv4_prefix.len & 0x3f;
@@ -398,12 +398,11 @@ lspgen_serialize_attr(lsdb_attr_t *attr, io_buffer_t *buf)
flags |= 0x80;
}
if (subtlv_present) {
flags |= 0x40; /* subTLVs present */
flags |= 0x40; /* Sub-TLVs present */
}
push_be_uint(buf, 1, flags); /* Flags */
attr_len = (attr->key.prefix.ipv4_prefix.len+7)/8;
push_data(buf, (uint8_t*)&attr->key.prefix.ipv4_prefix.address, attr_len);
/* Generate subTLVs */
if (subtlv_present) {
lspgen_serialize_prefix_subtlv(attr, buf, true);
@@ -549,7 +548,7 @@ lspgen_add_packet(lsdb_ctx_t *ctx, lsdb_node_t *node, uint32_t id)
*/
CIRCLEQ_INSERT_TAIL(&ctx->packet_change_qhead, packet, packet_change_qnode);
packet->on_change_list = true;
ctx->ctrl_stats.packets_queued++;
ctx->ctrl_stats.packets_queued++;
/*
* Parent
@@ -589,10 +588,10 @@ lspgen_refresh_cb (timer_s *timer)
*/
if (ctx->ctrl_socket_path) {
if (!ctx->ctrl_socket_connect_timer) {
timer_add_periodic(&ctx->timer_root, &ctx->ctrl_socket_connect_timer,
"connect", 1, 0, ctx, &lspgen_ctrl_connect_cb);
}
if (!ctx->ctrl_socket_connect_timer) {
timer_add_periodic(&ctx->timer_root, &ctx->ctrl_socket_connect_timer,
"connect", 1, 0, ctx, &lspgen_ctrl_connect_cb);
}
}
}
@@ -607,7 +606,7 @@ lspgen_refresh_interval (lsdb_ctx_t *ctx)
refresh = ctx->lsp_lifetime - 300;
if (refresh < 60) {
refresh = 60;
refresh = 60;
}
return refresh;
@@ -667,8 +666,8 @@ lspgen_gen_packet_node(lsdb_node_t *node)
* Start refresh timer.
*/
if (ctx->ctrl_socket_path) {
timer_add_periodic(&ctx->timer_root, &node->refresh_timer, "refresh",
lspgen_refresh_interval(ctx), 0, node, &lspgen_refresh_cb);
timer_add_periodic(&ctx->timer_root, &node->refresh_timer, "refresh",
lspgen_refresh_interval(ctx), 0, node, &lspgen_refresh_cb);
}
do {
@@ -712,7 +711,7 @@ lspgen_gen_packet_node(lsdb_node_t *node)
*/
if ((last_attr != attr->key.attr_type) ||
(lspgen_calculate_tlv_space(&packet->buf, tlv_start_idx) < attr->size) ||
attr->key.start_tlv) {
attr->key.start_tlv) {
tlv_start_idx = packet->buf.idx;
push_be_uint(&packet->buf, 1, attr->key.attr_type); /* Type */
push_be_uint(&packet->buf, 1, 0); /* Length */
@@ -758,14 +757,14 @@ lspgen_gen_packet(lsdb_ctx_t *ctx)
do {
node = *dict_itor_datum(itor);
/*
* Init per-packet tree for this node.
*/
if (!node->packet_dict) {
node->packet_dict = hb_dict_new((dict_compare_func)lsdb_compare_packet);
}
/*
* Init per-packet tree for this node.
*/
if (!node->packet_dict) {
node->packet_dict = hb_dict_new((dict_compare_func)lsdb_compare_packet);
}
/*
/*
* Generate the link-state packets for this node.
*/
lspgen_gen_packet_node(node);
+4
View File
@@ -189,6 +189,10 @@ BGP
---
.. include:: bgp.rst
LDP
---
.. include:: ldp.rst
CFM
---
.. include:: cfm.rst
+35
View File
@@ -0,0 +1,35 @@
.. list-table::
:header-rows: 1
* - Attribute
- Description
- Mandatory Arguments
- Optional Arguments
* - `ldp-adjacencies`
- Display all LDP adjacencies
-
- `ldp-instance-id`
* - `ldp-sessions`
- Display all matching LDP sessions
-
- `ldp-instance-id`, `local-ipv4-address`, `peer-ipv4-address`
* - `ldp-database`
- Display LDP database
- `ldp-instance-id`
-
* - `ldp-disconnect`
- Disconnect all matching LDP sessions
-
- `ldp-instance-id`, `local-ipv4-address`, `peer-ipv4-address`
* - `ldp-teardown`
- Teardown LDP
-
-
* - `ldp-raw-update-list`
- List all loaded LDP RAW update files
-
-
* - `ldp-raw-update`
- Update all matching LDP session
- `file`
- `ldp-instance-id`, `local-ipv4-address`, `peer-ipv4-address`
+1 -1
View File
@@ -6,7 +6,7 @@ project = 'BNG Blaster'
copyright = '2020-2022, RtBrick, Inc.'
author = 'Christian Giese'
release = '0.8'
version = '0.8.1'
version = '0.8.X'
# -- General configuration
+2 -2
View File
@@ -25,8 +25,8 @@
* - `peer-as`
- BGP peer AS
- local AS
* - `holdtime`
- BGP holdtime in seconds
* - `hold-time`
- BGP hold-time in seconds
- 90
* - `id`
- BGP identifier
+5 -1
View File
@@ -114,4 +114,8 @@ ISIS External Connections
BGP
---
.. include:: bgp.rst
.. include:: bgp.rst
LDP
---
.. include:: ldp.rst
+2 -2
View File
@@ -43,8 +43,8 @@
* - `hello-padding`
- ISIS hello padding
- false
* - `holding-time`
- ISIS holding time in seconds
* - `hold-time`
- ISIS hold time in seconds
- 30
* - `lsp-lifetime`
- ISIS LSP lifetime in seconds
+36
View File
@@ -0,0 +1,36 @@
.. code-block:: json
{ "ldp": {} }
.. list-table::
:widths: 25 50 25
:header-rows: 1
* - Attribute
- Description
- Default
* - `instance-id`
- LDP instance identifier
-
* - `keepalive-time`
- LDP keepalive time in seconds
- 15
* - `hold-time`
- LDP hold time in seconds
- 30
* - `hostname`
- LDP hostname
- bngblaster
* - `lsr-id`
- LDP LSR identifier
- 10.10.10.10
* - `teardown-time`
- LDP teardown time in seconds
- 5
* - `ipv4-transport-address`
- LDP transport address
- `lsr-id`
* - `raw-update-file`
- LDP RAW update file
-
@@ -28,6 +28,9 @@
* - `start-delay`
- Wait N seconds after all interfaces are resolved before starting sessions
- 0
* - `reconnect`
- Automatically reconnect sessions (PPPoE and IPoE) if terminated
- false
* - `autostart`
- Start sessions automatically
- true
+5 -2
View File
@@ -95,10 +95,10 @@
- MPLS send (TX) label (inner label)
-
* - `tx-label2-exp`
- EXP bits of the first label (inner label)
- EXP bits of the second label (inner label)
- 0
* - `tx-label2-ttl`
- TTL of the first label (inner label)
- TTL of the second label (inner label)
- 255
* - `rx-label1`
- Expected receive MPLS label (outer label)
@@ -106,6 +106,9 @@
* - `rx-label2`
- Expected receive MPLS label (inner label)
-
* - `ldp-ipv4-lookup-address`
- Dynamically resolve outer label
-
For L2TP downstream traffic, the IPv4 TOS is applied to the outer IPv4
and inner IPv4 header.
+4 -3
View File
@@ -2,7 +2,7 @@ BNG Blaster
===========
The **BNG Blaster** is an open-source network tester
for access and routing protocols. It can emulate massive
for access and routing protocols. It can emulate a huge amount of
PPPoE and IPoE (DHCP) subscribers including IPTV, and L2TP (LNS).
There are various routing protocols supported like ISIS and BGP.
So you can use it for end-to-end BNG and non-BNG router testing.
@@ -41,10 +41,11 @@ RtBrick and many more.
.. tab:: Routing Protocols
* Emulate ISIS topologies with thousands of nodes
* Support for ISIS Segment Routing
* Setup thousands of BGP sessions with millions of prefixes
* Verify MPLS labels for millions of flows
* Emulate ISIS topologies with thousands of nodes
* Support for ISIS Segment Routing
* Support for LDP and traffic streams with dynamically resolved labels
* Support all routing protocols with link aggregation (LAG)
* ...
+10 -3
View File
@@ -152,7 +152,14 @@ The following steps are required to build the BNG Blaster with experimental
Tested with DPDK version 21.11.2 (LTS) and Ubuntu 22.04 (LTS)!
Download and Install DPDK:
It is recommended to install the DPDK development package if possible:
.. code-block:: none
sudo apt install dpdk libdpdk-dev
This package does not support all NIC types (e.g. Mellanox, ...),
which requires downloading and installing DPDK manually:
https://doc.dpdk.org/guides/linux_gsg/build_dpdk.html
.. code-block:: none
@@ -205,8 +212,8 @@ Running BNG Blaster
-------------------
The BNG Blaster needs permission to send raw packets and change network interface
settings. The easiest way to run the BNG Blaster is either as the root user or with
sudo:
settings. The easiest way to run the BNG Blaster is either as the root user or
with `sudo`:
.. code-block:: none
+68 -3
View File
@@ -519,7 +519,7 @@ Finally, start the BNG Blaster in another terminal window.
Apr 08 14:53:52.904389 All network interfaces resolved
Apr 08 14:53:53.904448 BGP (veth1.2 192.168.92.2 - 192.168.92.1) state changed from idle -> connect
Apr 08 14:53:53.905659 BGP (veth1.2 192.168.92.2 - 192.168.92.1) state changed from connect -> opensent
Apr 08 14:53:53.907888 BGP (veth1.2 192.168.92.2 - 192.168.92.1) open message received with peer AS: 65001, holdtime: 90s
Apr 08 14:53:53.907888 BGP (veth1.2 192.168.92.2 - 192.168.92.1) open message received with peer AS: 65001, hold-time: 90s
Apr 08 14:53:53.907903 BGP (veth1.2 192.168.92.2 - 192.168.92.1) state changed from opensent -> openconfirm
Apr 08 14:53:53.907917 BGP (veth1.2 192.168.92.2 - 192.168.92.1) state changed from openconfirm -> established
Apr 08 14:53:54.907989 BGP (veth1.2 192.168.92.2 - 192.168.92.1) raw update start
@@ -544,11 +544,11 @@ from where you started the BNG Blaster and enter the following command.
"local-address": "192.168.92.2",
"local-id": "1.2.3.4",
"local-as": 65001,
"local-holdtime": 90,
"local-hold-time": 90,
"peer-address": "192.168.92.1",
"peer-id": "1.92.168.192",
"peer-as": 65001,
"peer-holdtime": 90,
"peer-hold-time": 90,
"state": "established",
"raw-update-state": "done",
"raw-update-file": "out.bgp",
@@ -646,3 +646,68 @@ Finally, you can withdraw them again.
bgpupdate -a 65001 -n 192.168.92.2 -p 22.0.0.0/28 -P 100000 -f withdraw.bgp --withdraw
sudo bngblaster-cli run.sock bgp-raw-update file withdraw.bgp
LDP
---
In the following example, we create two connected :ref:`LDP <ldp>` instances.
**ldp.json:**
.. code-block:: json
{
"interfaces": {
"capture-include-streams": true,
"network": [
{
"interface": "veth1.1",
"address": "10.0.0.1/24",
"gateway": "10.0.0.2",
"ldp-instance-id": 1
},
{
"interface": "veth1.2",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"ldp-instance-id": 2
}
]
},
"ldp": [
{
"instance-id": 1,
"lsr-id": "10.2.3.1",
"raw-update-file": "out.ldp"
},
{
"instance-id": 2,
"lsr-id": "10.2.3.2"
}
],
"streams": [
{
"name": "S1",
"type": "ipv4",
"direction": "downstream",
"priority": 128,
"network-interface": "veth1.2",
"destination-ipv4-address": "100.0.0.1",
"ldp-ipv4-lookup-address": "13.37.0.1",
"pps": 1
}
]
}
Use the included tool ``ldpupdate`` to generate an LDP update file
with 10 labeled IPv4 prefixes.
.. code-block:: none
ldpupdate -l 10.2.3.1 -p 13.37.0.0/32 -P 10 -M 10000
Now you can start the BNG Blaster with this configuration.
.. code-block:: none
sudo bngblaster -C ldp.json -l ldp -S run.sock -P ldp.pcap
+2 -2
View File
@@ -67,8 +67,8 @@ but not limited to update messages.
.. code-block:: none
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
+2 -1
View File
@@ -9,5 +9,6 @@ router testing.
:maxdepth: 1
isis.rst
bgp.rst
mpls.rst
bgp.rst
ldp.rst
+285
View File
@@ -0,0 +1,285 @@
.. _ldp:
LDP
---
Label Distribution Protocol (LDP) is a protocol in which routers capable
of Multiprotocol Label Switching (MPLS) exchange label mapping information.
Two routers with an established session are called LDP peers and the exchange
of information is bi-directional. LDP is used to build and maintain LSP databases
that are used to forward traffic through MPLS networks.
LDP discovery runs on UDP port 646 and the session is built on TCP port 646. During
the discovery phase hello packets are sent on UDP port 646 to the 'all routers on this subnet'
group multicast address (224.0.0.2).
LDP is defined by the IETF (RFC 5036).
Configuration
~~~~~~~~~~~~~
Following an example LDP configuration with one instance
attached to a network interface function.
.. code-block:: json
{
"interfaces": {
"network": [
{
"interface": "eth1",
"address": "10.0.1.2/24",
"gateway": "10.0.1.1",
"ldp-instance-id": 1,
}
]
},
"ldp": [
{
"instance-id": 1,
"lsr-id": "10.10.10.11"
}
]
}
.. include:: ../configuration/ldp.rst
Limitations
~~~~~~~~~~~
The following LDP functionalities are currently
not supported:
+ Targeted LDP
+ LDP TCP authentication
+ LDP sessions between IPv6 addresses
+ Learn IPv6 label mappings
+ Multiple links between LDP instance and DUT (ECMP)
LDP Adjacencies
~~~~~~~~~~~~~~~~
When the BNG Blaster receives an LDP discovery hello message,
an LDP adjacency is set up between the two peers.
``$ sudo bngblaster-cli run.sock ldp-adjacencies``
.. code-block:: json
{
"status": "ok",
"code": 200,
"ldp-adjacencies": [
{
"ldp-instance-id": 1,
"interface": "eth0",
"state": "up"
}
]
}
LDP Sessions
~~~~~~~~~~~~
LDP peers exchange messages over a TCP session which is initiated by
the peer with the larger transport IP address (active peer).
The LDP transport IP address can be explicitly configured for the
LDP instance using the option `ipv4-transport-address`. The `lsr-id`
is used as a transport IP address if not explicitly configured.
.. note::
It is currently not supported to setup multiple links between
a single LDP instance and the device under test (ECMP).
``$ sudo bngblaster-cli run.sock ldp-sessions``
.. code-block:: json
{
"status": "ok",
"code": 200,
"ldp-sessions": [
{
"ldp-instance-id": 1,
"interface": "eth0",
"local-address": "10.2.3.1",
"local-identifier": "10.2.3.1:0",
"peer-address": "10.2.3.2",
"peer-identifier": "10.2.3.2:0",
"state": "operational",
"raw-update-state": "done",
"raw-update-file": "out.ldp",
"stats": {
"pdu-rx": 23,
"pdu-tx": 32,
"messages-rx": 24,
"messages-tx": 34,
"keepalive-rx": 21,
"keepalive-tx": 21
}
}
]
}
LDP Traffic Streams
~~~~~~~~~~~~~~~~~~~
Traffic streams send from network interface functions (downstream)
can be configured to dynamically resolve the outer MPLS
label using the learned label mappings.
The traffic stream configuration option `ldp-ipv4-lookup-address`
specifies the lookup IPv4 address. This means that traffic
will not start until this address is found in the corresponding
label database of the sending network interface function.
.. code-block:: json
{
"streams": [
{
"name": "S1",
"type": "ipv4",
"direction": "downstream",
"priority": 128,
"network-interface": "eth1",
"destination-ipv4-address": "10.0.0.1",
"ldp-ipv4-lookup-address": "13.37.0.1",
"pps": 1
}
]
}
``$ sudo bngblaster-cli run.sock ldp-database instance 1``
.. code-block:: json
{
"status": "ok",
"code": 200,
"ldp-database": [
{
"direction": "ipv4",
"prefix": "10.0.0.0/24",
"label": 3,
"source-identifier": "10.2.3.1:0"
},
{
"direction": "ipv4",
"prefix": "13.37.0.0/32",
"label": 10000,
"source-identifier": "10.2.3.1:0"
},
{
"direction": "ipv4",
"prefix": "13.37.0.1/32",
"label": 10001,
"source-identifier": "10.2.3.1:0"
}
]
}
The `ldp-ipv4-lookup-address` must exactly match the prefix address
as shown in the LDP database.
.. note::
There is currently no longest prefix match supported,
meaning that the actual prefix length is ignored!
RAW Update Files
~~~~~~~~~~~~~~~~
The BNG Blaster can inject LDP PDU from a pre-compiled
RAW update file into the defined sessions. A RAW update file is not
more than a pre-compiled binary stream of LDP PDU.
.. code-block:: none
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version | PDU Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| LDP Identifier |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
. LDP Messages
.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Version | PDU Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| LDP Identifier |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
. LDP Messages
.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Those files can be created using the included LDP RAW update generator
script ``ldpupdate`` or manually using libraries like scapy.
The configured ``raw-update-file`` under the LDP instance is loaded
during BNG Blaster startup phase and send it as soon as the session is
established.
The ``ldp-raw-update`` :ref:`command <api>` allows to send further updates during
the session lifetime.
``$ sudo bngblaster-cli run.sock ldp-raw-update file update1.ldp``
This allows loading label mappings after the LDP session has
started and manually trigger a series of changes using incremental
updates files.
All LDP RAW update files are loaded once and can then be used for
multiple sessions. Meaning if two or more sessions reference the
same file identified by file name, this file is loaded once into
memory and used by multiple sessions.
LDP RAW Update Generator
~~~~~~~~~~~~~~~~~~~~~~~~
The LDP RAW update generator is a simple tool to generate LDP RAW update
streams for use with the BNG Blaster.
.. code-block:: none
$ ldpupdate --help
usage: ldpupdate [-h] -l ADDRESS [-i N] -p PREFIX [-P N] [-m LABEL] [-M N]
[-f FILE] [--append] [--pcap FILE]
[--log-level {warning,info,debug}]
The LDP RAW update generator is a simple tool to generate LDP RAW update
streams for use with the BNG Blaster.
optional arguments:
-h, --help show this help message and exit
-l ADDRESS, --lsr-id ADDRESS
LSR identifier
-i N, --message-id-base N
message identifier base
-w, --withdraw withdraw
-a ADDRESS, --address-base ADDRESS
address message base
-A N, --address-num N
address message count
-p PREFIX, --prefix-base PREFIX
label mapping base prefix
-P N, --prefix-num N label mapping prefix count
-m LABEL, --label-base LABEL
label base
-M N, --label-num N label count
-f FILE, --file FILE output file
--append append to file if exist
--pcap FILE write LDP updates to PCAP file
--log-level {warning,info,debug}
logging Level
The python LDP RAW update generator is a python script that uses
scapy to build LDP PDU. Therefore this tool can be easily
modified, extend or used as a blueprint for your own tools to generate
valid LDP update streams.