diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 19fab514..0c7c3c80 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -6,4 +6,5 @@ add_subdirectory(bngblaster) add_subdirectory(lspgen) install(PROGRAMS bngblaster-cli DESTINATION sbin) -install(PROGRAMS bgpupdate DESTINATION bin) \ No newline at end of file +install(PROGRAMS bgpupdate DESTINATION bin) +install(PROGRAMS ldpupdate DESTINATION bin) \ No newline at end of file diff --git a/code/README.md b/code/README.md index 240d9b39..ca21e73e 100644 --- a/code/README.md +++ b/code/README.md @@ -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. diff --git a/code/bngblaster/CMakeLists.txt b/code/bngblaster/CMakeLists.txt index c4834c34..c8ecab55 100644 --- a/code/bngblaster/CMakeLists.txt +++ b/code/bngblaster/CMakeLists.txt @@ -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 diff --git a/code/bngblaster/src/bbl.c b/code/bngblaster/src/bbl.c index c5bd3870..bd0ecc78 100644 --- a/code/bngblaster/src/bbl.c +++ b/code/bngblaster/src/bbl.c @@ -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"); diff --git a/code/bngblaster/src/bbl.h b/code/bngblaster/src/bbl.h index 082b7160..4d32822a 100644 --- a/code/bngblaster/src/bbl.h +++ b/code/bngblaster/src/bbl.h @@ -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; diff --git a/code/bngblaster/src/bbl_config.c b/code/bngblaster/src/bbl_config.c index 86775843..cb63e57c 100644 --- a/code/bngblaster/src/bbl_config.c +++ b/code/bngblaster/src/bbl_config.c @@ -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 * diff --git a/code/bngblaster/src/bbl_config.h b/code/bngblaster/src/bbl_config.h index 1e1d1f09..b54e88e0 100644 --- a/code/bngblaster/src/bbl_config.h +++ b/code/bngblaster/src/bbl_config.h @@ -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; diff --git a/code/bngblaster/src/bbl_ctrl.c b/code/bngblaster/src/bbl_ctrl.c index 378346fe..52f71115 100644 --- a/code/bngblaster/src/bbl_ctrl.c +++ b/code/bngblaster/src/bbl_ctrl.c @@ -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}, diff --git a/code/bngblaster/src/bbl_ctx.h b/code/bngblaster/src/bbl_ctx.h index 0facf2e5..f84e8a1e 100644 --- a/code/bngblaster/src/bbl_ctx.h +++ b/code/bngblaster/src/bbl_ctx.h @@ -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; diff --git a/code/bngblaster/src/bbl_def.h b/code/bngblaster/src/bbl_def.h index e86f129d..efef9abf 100644 --- a/code/bngblaster/src/bbl_def.h +++ b/code/bngblaster/src/bbl_def.h @@ -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, diff --git a/code/bngblaster/src/bbl_network.c b/code/bngblaster/src/bbl_network.c index 4becdbd8..c2737ce1 100644 --- a/code/bngblaster/src/bbl_network.c +++ b/code/bngblaster/src/bbl_network.c @@ -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; -} \ No newline at end of file +} diff --git a/code/bngblaster/src/bbl_network.h b/code/bngblaster/src/bbl_network.h index 3b69503c..422ccb97 100644 --- a/code/bngblaster/src/bbl_network.h +++ b/code/bngblaster/src/bbl_network.h @@ -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; diff --git a/code/bngblaster/src/bbl_protocols.c b/code/bngblaster/src/bbl_protocols.c index e56369d6..273c9e1a 100644 --- a/code/bngblaster/src/bbl_protocols.c +++ b/code/bngblaster/src/bbl_protocols.c @@ -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)); diff --git a/code/bngblaster/src/bbl_protocols.h b/code/bngblaster/src/bbl_protocols.h index 0aaff8b2..86a50283 100644 --- a/code/bngblaster/src/bbl_protocols.h +++ b/code/bngblaster/src/bbl_protocols.h @@ -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 \ No newline at end of file +#endif diff --git a/code/bngblaster/src/bbl_session.c b/code/bngblaster/src/bbl_session.c index 2452ef7b..f872f851 100644 --- a/code/bngblaster/src/bbl_session.c +++ b/code/bngblaster/src/bbl_session.c @@ -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; } diff --git a/code/bngblaster/src/bbl_stream.c b/code/bngblaster/src/bbl_stream.c index d3e6b392..d8ea262d 100644 --- a/code/bngblaster/src/bbl_stream.c +++ b/code/bngblaster/src/bbl_stream.c @@ -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); diff --git a/code/bngblaster/src/bbl_stream.h b/code/bngblaster/src/bbl_stream.h index a4dc758d..bd3a93c3 100644 --- a/code/bngblaster/src/bbl_stream.h +++ b/code/bngblaster/src/bbl_stream.h @@ -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; diff --git a/code/bngblaster/src/bbl_tcp.c b/code/bngblaster/src/bbl_tcp.c index a2e156c1..496ce5d3 100644 --- a/code/bngblaster/src/bbl_tcp.c +++ b/code/bngblaster/src/bbl_tcp.c @@ -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; diff --git a/code/bngblaster/src/bbl_tcp.h b/code/bngblaster/src/bbl_tcp.h index ab80cb7d..0a3b3f3f 100644 --- a/code/bngblaster/src/bbl_tcp.h +++ b/code/bngblaster/src/bbl_tcp.h @@ -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); diff --git a/code/bngblaster/src/bbl_tx.c b/code/bngblaster/src/bbl_tx.c index b8e0bcf0..016d8cd2 100644 --- a/code/bngblaster/src/bbl_tx.c +++ b/code/bngblaster/src/bbl_tx.c @@ -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; } diff --git a/code/bngblaster/src/bgp/bgp_ctrl.c b/code/bngblaster/src/bgp/bgp_ctrl.c index 98d304e1..30a2c722 100644 --- a/code/bngblaster/src/bgp/bgp_ctrl.c +++ b/code/bngblaster/src/bgp/bgp_ctrl.c @@ -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, diff --git a/code/bngblaster/src/bgp/bgp_def.h b/code/bngblaster/src/bgp/bgp_def.h index 7ea4418f..ab905c2c 100644 --- a/code/bngblaster/src/bgp/bgp_def.h +++ b/code/bngblaster/src/bgp/bgp_def.h @@ -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 { diff --git a/code/bngblaster/src/bgp/bgp_message.c b/code/bngblaster/src/bgp/bgp_message.c index f9e7e620..4904f197 100644 --- a/code/bngblaster/src/bgp/bgp_message.c +++ b/code/bngblaster/src/bgp/bgp_message.c @@ -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 */ diff --git a/code/bngblaster/src/bgp/bgp_receive.c b/code/bngblaster/src/bgp/bgp_receive.c index dd1d9187..8bf21667 100644 --- a/code/bngblaster/src/bgp/bgp_receive.c +++ b/code/bngblaster/src/bgp/bgp_receive.c @@ -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); } } \ No newline at end of file diff --git a/code/bngblaster/src/bgp/bgp_session.c b/code/bngblaster/src/bgp/bgp_session.c index a203357f..9bfb87de 100644 --- a/code/bngblaster/src/bgp/bgp_session.c +++ b/code/bngblaster/src/bgp/bgp_session.c @@ -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); diff --git a/code/bngblaster/src/io/io_packet_mmap.c b/code/bngblaster/src/io/io_packet_mmap.c index dcc53946..587f38d5 100644 --- a/code/bngblaster/src/io/io_packet_mmap.c +++ b/code/bngblaster/src/io/io_packet_mmap.c @@ -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; diff --git a/code/bngblaster/src/io/io_thread.c b/code/bngblaster/src/io/io_thread.c index 423e9d5e..fcc8426e 100644 --- a/code/bngblaster/src/io/io_thread.c +++ b/code/bngblaster/src/io/io_thread.c @@ -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; } -} \ No newline at end of file +} diff --git a/code/bngblaster/src/isis/isis.c b/code/bngblaster/src/isis/isis.c index ce907306..d40ccab1 100644 --- a/code/bngblaster/src/isis/isis.c +++ b/code/bngblaster/src/isis/isis.c @@ -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: diff --git a/code/bngblaster/src/isis/isis_adjacency.c b/code/bngblaster/src/isis/isis_adjacency.c index 31582c66..aa965415 100644 --- a/code/bngblaster/src/isis/isis_adjacency.c +++ b/code/bngblaster/src/isis/isis_adjacency.c @@ -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--; } \ No newline at end of file diff --git a/code/bngblaster/src/isis/isis_csnp.c b/code/bngblaster/src/isis/isis_csnp.c index 26b59ea4..185b1199 100644 --- a/code/bngblaster/src/isis/isis_csnp.c +++ b/code/bngblaster/src/isis/isis_csnp.c @@ -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); diff --git a/code/bngblaster/src/isis/isis_ctrl.c b/code/bngblaster/src/isis/isis_ctrl.c index 3305850e..d32d4737 100644 --- a/code/bngblaster/src/isis/isis_ctrl.c +++ b/code/bngblaster/src/isis/isis_ctrl.c @@ -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, diff --git a/code/bngblaster/src/isis/isis_def.h b/code/bngblaster/src/isis/isis_def.h index 3c3d3593..698beb7d 100644 --- a/code/bngblaster/src/isis/isis_def.h +++ b/code/bngblaster/src/isis/isis_def.h @@ -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; diff --git a/code/bngblaster/src/isis/isis_lsp.c b/code/bngblaster/src/isis/isis_lsp.c index aee22e9c..79230e88 100644 --- a/code/bngblaster/src/isis/isis_lsp.c +++ b/code/bngblaster/src/isis/isis_lsp.c @@ -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; } diff --git a/code/bngblaster/src/isis/isis_p2p_hello.c b/code/bngblaster/src/isis/isis_p2p_hello.c index ab059f0f..cc0024ae 100644 --- a/code/bngblaster/src/isis/isis_p2p_hello.c +++ b/code/bngblaster/src/isis/isis_p2p_hello.c @@ -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; iisis_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) { diff --git a/code/bngblaster/src/isis/isis_psnp.c b/code/bngblaster/src/isis/isis_psnp.c index 11fa2f5a..3bfd02ab 100644 --- a/code/bngblaster/src/isis/isis_psnp.c +++ b/code/bngblaster/src/isis/isis_psnp.c @@ -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; } diff --git a/code/bngblaster/src/isis/isis_utils.c b/code/bngblaster/src/isis/isis_utils.c index b410be3a..5c17fac5 100644 --- a/code/bngblaster/src/isis/isis_utils.c +++ b/code/bngblaster/src/isis/isis_utils.c @@ -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"; diff --git a/code/bngblaster/src/isis/isis_utils.h b/code/bngblaster/src/isis/isis_utils.h index 703ba443..697c7a24 100644 --- a/code/bngblaster/src/isis/isis_utils.h +++ b/code/bngblaster/src/isis/isis_utils.h @@ -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); diff --git a/code/bngblaster/src/ldp/ldp.c b/code/bngblaster/src/ldp/ldp.c new file mode 100644 index 00000000..56c642ba --- /dev/null +++ b/code/bngblaster/src/ldp/ldp.c @@ -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; + } +} \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp.h b/code/bngblaster/src/ldp/ldp.h new file mode 100644 index 00000000..d52e390f --- /dev/null +++ b/code/bngblaster/src/ldp/ldp.h @@ -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 \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_ctrl.c b/code/bngblaster/src/ldp/ldp_ctrl.c new file mode 100644 index 00000000..6c3e32d3 --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_ctrl.c @@ -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"); + } +} \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_ctrl.h b/code/bngblaster/src/ldp/ldp_ctrl.h new file mode 100644 index 00000000..6bf0bc04 --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_ctrl.h @@ -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 \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_db.c b/code/bngblaster/src/ldp/ldp_db.c new file mode 100644 index 00000000..8890d319 --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_db.c @@ -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; +} \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_db.h b/code/bngblaster/src/ldp/ldp_db.h new file mode 100644 index 00000000..31d84647 --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_db.h @@ -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 \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_def.h b/code/bngblaster/src/ldp/ldp_def.h new file mode 100644 index 00000000..af2e3622 --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_def.h @@ -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 \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_hello.c b/code/bngblaster/src/ldp/ldp_hello.c new file mode 100644 index 00000000..efac5d6f --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_hello.c @@ -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); +} \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_hello.h b/code/bngblaster/src/ldp/ldp_hello.h new file mode 100644 index 00000000..a9eae30a --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_hello.h @@ -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 \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_interface.c b/code/bngblaster/src/ldp/ldp_interface.c new file mode 100644 index 00000000..fe0247dd --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_interface.c @@ -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; +} \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_interface.h b/code/bngblaster/src/ldp/ldp_interface.h new file mode 100644 index 00000000..8711bf4c --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_interface.h @@ -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 \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_message.c b/code/bngblaster/src/ldp/ldp_message.c new file mode 100644 index 00000000..5b3bcf10 --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_message.c @@ -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); +} \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_message.h b/code/bngblaster/src/ldp/ldp_message.h new file mode 100644 index 00000000..d5b3424f --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_message.h @@ -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 \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_raw_update.c b/code/bngblaster/src/ldp/ldp_raw_update.c new file mode 100644 index 00000000..d8d8a020 --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_raw_update.c @@ -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; + } +} \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_raw_update.h b/code/bngblaster/src/ldp/ldp_raw_update.h new file mode 100644 index 00000000..91883951 --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_raw_update.h @@ -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 \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_receive.c b/code/bngblaster/src/ldp/ldp_receive.c new file mode 100644 index 00000000..f1d573b1 --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_receive.c @@ -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); + } +} \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_receive.h b/code/bngblaster/src/ldp/ldp_receive.h new file mode 100644 index 00000000..0055e156 --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_receive.h @@ -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 \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_session.c b/code/bngblaster/src/ldp/ldp_session.c new file mode 100644 index 00000000..a3a5e367 --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_session.c @@ -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); +} \ No newline at end of file diff --git a/code/bngblaster/src/ldp/ldp_session.h b/code/bngblaster/src/ldp/ldp_session.h new file mode 100644 index 00000000..e97232bf --- /dev/null +++ b/code/bngblaster/src/ldp/ldp_session.h @@ -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 \ No newline at end of file diff --git a/code/common/src/common.h b/code/common/src/common.h index 6595bdc8..1eb804b2 100644 --- a/code/common/src/common.h +++ b/code/common/src/common.h @@ -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 */ diff --git a/code/common/src/logging.h b/code/common/src/logging.h index 4cfc588a..41e5838a 100644 --- a/code/common/src/logging.h +++ b/code/common/src/logging.h @@ -36,6 +36,7 @@ enum { L2TP, DHCP, ISIS, + LDP, BGP, TCP, LSDB, diff --git a/code/common/src/utils.h b/code/common/src/utils.h index 926cfa2e..3ae13ccf 100644 --- a/code/common/src/utils.h +++ b/code/common/src/utils.h @@ -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); diff --git a/code/ldpupdate b/code/ldpupdate new file mode 100644 index 00000000..9150bc94 --- /dev/null +++ b/code/ldpupdate @@ -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() \ No newline at end of file diff --git a/code/lspgen/src/lspgen.c b/code/lspgen/src/lspgen.c index 3555e3b7..cf3ef37a 100644 --- a/code/lspgen/src/lspgen.c +++ b/code/lspgen/src/lspgen.c @@ -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; } diff --git a/code/lspgen/src/lspgen_lsdb.h b/code/lspgen/src/lspgen_lsdb.h index 5e7f4d18..e42ff6b8 100644 --- a/code/lspgen/src/lspgen_lsdb.h +++ b/code/lspgen/src/lspgen_lsdb.h @@ -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; diff --git a/code/lspgen/src/lspgen_packet.c b/code/lspgen/src/lspgen_packet.c index a06574b7..c0ef8db8 100644 --- a/code/lspgen/src/lspgen_packet.c +++ b/code/lspgen/src/lspgen_packet.c @@ -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); diff --git a/docsrc/sources/api/index.rst b/docsrc/sources/api/index.rst index 66798ba5..cc0bbb7a 100644 --- a/docsrc/sources/api/index.rst +++ b/docsrc/sources/api/index.rst @@ -189,6 +189,10 @@ BGP --- .. include:: bgp.rst +LDP +--- +.. include:: ldp.rst + CFM --- .. include:: cfm.rst diff --git a/docsrc/sources/api/ldp.rst b/docsrc/sources/api/ldp.rst new file mode 100644 index 00000000..9ddef527 --- /dev/null +++ b/docsrc/sources/api/ldp.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` diff --git a/docsrc/sources/conf.py b/docsrc/sources/conf.py index a5e88739..33d74a20 100644 --- a/docsrc/sources/conf.py +++ b/docsrc/sources/conf.py @@ -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 diff --git a/docsrc/sources/configuration/bgp.rst b/docsrc/sources/configuration/bgp.rst index 584080c8..16aa5fdf 100644 --- a/docsrc/sources/configuration/bgp.rst +++ b/docsrc/sources/configuration/bgp.rst @@ -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 diff --git a/docsrc/sources/configuration/index.rst b/docsrc/sources/configuration/index.rst index ec6412a5..a691ae9d 100644 --- a/docsrc/sources/configuration/index.rst +++ b/docsrc/sources/configuration/index.rst @@ -114,4 +114,8 @@ ISIS External Connections BGP --- -.. include:: bgp.rst \ No newline at end of file +.. include:: bgp.rst + +LDP +--- +.. include:: ldp.rst \ No newline at end of file diff --git a/docsrc/sources/configuration/isis.rst b/docsrc/sources/configuration/isis.rst index 85341a4b..adde659a 100644 --- a/docsrc/sources/configuration/isis.rst +++ b/docsrc/sources/configuration/isis.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 diff --git a/docsrc/sources/configuration/ldp.rst b/docsrc/sources/configuration/ldp.rst new file mode 100644 index 00000000..ed1ec783 --- /dev/null +++ b/docsrc/sources/configuration/ldp.rst @@ -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 + - \ No newline at end of file diff --git a/docsrc/sources/configuration/sessions.rst b/docsrc/sources/configuration/sessions.rst index fb5397a6..feb8f97f 100644 --- a/docsrc/sources/configuration/sessions.rst +++ b/docsrc/sources/configuration/sessions.rst @@ -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 diff --git a/docsrc/sources/configuration/streams.rst b/docsrc/sources/configuration/streams.rst index 0986e88e..04e0fbdc 100644 --- a/docsrc/sources/configuration/streams.rst +++ b/docsrc/sources/configuration/streams.rst @@ -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. diff --git a/docsrc/sources/index.rst b/docsrc/sources/index.rst index bcf0bfed..e0c3ccd1 100644 --- a/docsrc/sources/index.rst +++ b/docsrc/sources/index.rst @@ -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) * ... diff --git a/docsrc/sources/install.rst b/docsrc/sources/install.rst index ceb7e29d..e64e44a6 100644 --- a/docsrc/sources/install.rst +++ b/docsrc/sources/install.rst @@ -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 diff --git a/docsrc/sources/quickstart.rst b/docsrc/sources/quickstart.rst index 160f7bba..d0cbc358 100644 --- a/docsrc/sources/quickstart.rst +++ b/docsrc/sources/quickstart.rst @@ -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 ` 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 + diff --git a/docsrc/sources/routing/bgp.rst b/docsrc/sources/routing/bgp.rst index 513c5ac0..34ef4245 100644 --- a/docsrc/sources/routing/bgp.rst +++ b/docsrc/sources/routing/bgp.rst @@ -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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + + diff --git a/docsrc/sources/routing/index.rst b/docsrc/sources/routing/index.rst index bfc7e839..6583eed0 100644 --- a/docsrc/sources/routing/index.rst +++ b/docsrc/sources/routing/index.rst @@ -9,5 +9,6 @@ router testing. :maxdepth: 1 isis.rst - bgp.rst mpls.rst + bgp.rst + ldp.rst diff --git a/docsrc/sources/routing/ldp.rst b/docsrc/sources/routing/ldp.rst new file mode 100644 index 00000000..e4711636 --- /dev/null +++ b/docsrc/sources/routing/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 ` 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. \ No newline at end of file