mirror of
https://github.com/rtbrick/bngblaster.git
synced 2024-05-06 15:54:57 +00:00
+2
-1
@@ -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)
|
||||
@@ -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,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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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, ð);
|
||||
} 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, ð);
|
||||
} else {
|
||||
interface->send_requests = 0;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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, ð);
|
||||
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;
|
||||
|
||||
@@ -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, ð);
|
||||
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, ð);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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--;
|
||||
}
|
||||
@@ -147,8 +147,9 @@ isis_csnp_job(timer_s *timer)
|
||||
isis.pdu_len = pdu.pdu_len;
|
||||
if(bbl_txq_to_buffer(adjacency->interface->txq, ð) == 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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -129,8 +129,9 @@ isis_psnp_job (timer_s *timer)
|
||||
isis.pdu_len = pdu.pdu_len;
|
||||
if(bbl_txq_to_buffer(adjacency->interface->txq, ð) == 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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
|
||||
@@ -36,6 +36,7 @@ enum {
|
||||
L2TP,
|
||||
DHCP,
|
||||
ISIS,
|
||||
LDP,
|
||||
BGP,
|
||||
TCP,
|
||||
LSDB,
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -189,6 +189,10 @@ BGP
|
||||
---
|
||||
.. include:: bgp.rst
|
||||
|
||||
LDP
|
||||
---
|
||||
.. include:: ldp.rst
|
||||
|
||||
CFM
|
||||
---
|
||||
.. include:: cfm.rst
|
||||
|
||||
@@ -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`
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -114,4 +114,8 @@ ISIS External Connections
|
||||
|
||||
BGP
|
||||
---
|
||||
.. include:: bgp.rst
|
||||
.. include:: bgp.rst
|
||||
|
||||
LDP
|
||||
---
|
||||
.. include:: ldp.rst
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
* ...
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
+ +
|
||||
|
||||
@@ -9,5 +9,6 @@ router testing.
|
||||
:maxdepth: 1
|
||||
|
||||
isis.rst
|
||||
bgp.rst
|
||||
mpls.rst
|
||||
bgp.rst
|
||||
ldp.rst
|
||||
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user