From 09818f358087d9977678cb2440000dc7d5faacec Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Wed, 17 Feb 2021 22:06:32 +0100 Subject: [PATCH 01/27] #3 L2TPv2 LNS Support (WIP) --- src/bbl.c | 6 +- src/bbl.h | 23 +- src/bbl_config.c | 78 +++++- src/bbl_l2tp.c | 590 ++++++++++++++++++++++++++++++++++++++++ src/bbl_l2tp.h | 213 +++++++++++++++ src/bbl_l2tp_avp.c | 649 ++++++++++++++++++++++++++++++++++++++++++++ src/bbl_l2tp_avp.h | 85 ++++++ src/bbl_logging.c | 1 + src/bbl_logging.h | 1 + src/bbl_protocols.c | 259 +++++++++++++++++- src/bbl_protocols.h | 70 ++++- src/bbl_rx.c | 21 +- src/bbl_timer.c | 23 +- src/bbl_tx.c | 65 ++++- 14 files changed, 2055 insertions(+), 29 deletions(-) create mode 100644 src/bbl_l2tp.c create mode 100644 src/bbl_l2tp.h create mode 100644 src/bbl_l2tp_avp.c create mode 100644 src/bbl_l2tp_avp.h diff --git a/src/bbl.c b/src/bbl.c index 1dc3cbf6..39eea2d8 100644 --- a/src/bbl.c +++ b/src/bbl.c @@ -447,7 +447,7 @@ bbl_add_interface (bbl_ctx_s *ctx, char *interface_name, int slots) * List for sessions who want to transmit. */ CIRCLEQ_INIT(&interface->session_tx_qhead); - + CIRCLEQ_INIT(&interface->l2tp_tx_qhead); return interface; } @@ -633,6 +633,10 @@ bbl_add_ctx (void) bbl_session_hash, BBL_SESSION_HASHTABLE_SIZE); + ctx->l2tp_session_dict = hashtable2_dict_new((dict_compare_func)bbl_compare_session, + bbl_session_hash, + BBL_SESSION_HASHTABLE_SIZE); + return ctx; } diff --git a/src/bbl.h b/src/bbl.h index 91043a72..64b17184 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -43,6 +43,8 @@ #include "bbl_utils.h" #include "bbl_rx.h" #include "bbl_tx.h" +#include "bbl_l2tp.h" +#include "bbl_l2tp_avp.h" #define WRITE_BUF_LEN 1514 #define SCRATCHPAD_LEN 1514 @@ -83,6 +85,7 @@ #define BBL_IF_SEND_ARP_REPLY 0x00000002 #define BBL_IF_SEND_ICMPV6_NS 0x00000004 #define BBL_IF_SEND_ICMPV6_NA 0x00000008 +#define BBL_IF_SEND_SEC_ARP_REPLY 0x00000010 #define DUID_LEN 10 @@ -92,6 +95,9 @@ #define BBL_AVG_SAMPLES 5 #define DATA_TRAFFIC_MAX_LEN 1500 +#define UNUSED(x) (void)x + + typedef struct bbl_rate_ { uint32_t diff_value[BBL_AVG_SAMPLES]; @@ -99,7 +105,6 @@ typedef struct bbl_rate_ uint64_t last_value; uint64_t avg; uint64_t avg_max; - } bbl_rate_s; typedef enum { @@ -131,6 +136,13 @@ typedef struct bbl_igmp_group_ struct timespec last_mc_rx_time; } bbl_igmp_group_s; +typedef struct bbl_secondary_ip_ +{ + uint32_t ip; + bool arp_reply; + void *next; +} bbl_secondary_ip_s; + typedef struct bbl_interface_ { CIRCLEQ_ENTRY(bbl_interface_) interface_qnode; @@ -157,6 +169,7 @@ typedef struct bbl_interface_ uint32_t send_requests; bool arp_resolved; + uint32_t arp_reply_ip; uint32_t ip; uint32_t gateway; uint8_t mac[ETH_ADDR_LEN]; @@ -256,6 +269,7 @@ typedef struct bbl_interface_ struct timespec tx_timestamp; /* user space timestamps */ struct timespec rx_timestamp; /* user space timestamps */ CIRCLEQ_HEAD(bbl_interface__, bbl_session_ ) session_tx_qhead; /* list of sessions that want to transmit */ + CIRCLEQ_HEAD(bbl_interface___, bbl_l2tp_queue_ ) l2tp_tx_qhead; /* list of messages that want to transmit */ } bbl_interface_s; typedef struct bbl_access_config_ @@ -340,6 +354,7 @@ typedef struct bbl_ctx_ CIRCLEQ_HEAD(bbl_ctx__, bbl_interface_ ) interface_qhead; /* list of interfaces */ dict *session_dict; /* hashtable for sessions */ + dict *l2tp_session_dict; /* hashtable for L2TP sessions */ uint64_t flow_id; @@ -402,6 +417,8 @@ typedef struct bbl_ctx_ ipv6_prefix network_gateway6; uint16_t network_vlan; + bbl_secondary_ip_s *secondary_ip_addresses; + /* Access Interfaces */ bbl_access_config_s *access_config; @@ -496,6 +513,9 @@ typedef struct bbl_ctx_ uint16_t session_traffic_ipv4_pps; uint16_t session_traffic_ipv6_pps; uint16_t session_traffic_ipv6pd_pps; + + /* L2TP Server Config (LNS) */ + bbl_l2tp_server_t *l2tp_server; } config; } bbl_ctx_s; @@ -539,7 +559,6 @@ typedef struct session_key_ { uint16_t inner_vlan_id; } __attribute__ ((__packed__)) session_key_t; - #define BBL_SESSION_HASHTABLE_SIZE 32771 /* is a prime number */ /* diff --git a/src/bbl_config.c b/src/bbl_config.c index c6a03e16..1978f0f7 100644 --- a/src/bbl_config.c +++ b/src/bbl_config.c @@ -223,8 +223,9 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { const char *s; uint32_t ipv4; int i, size; - bbl_access_config_s *access_config = NULL; + bbl_l2tp_server_t *l2tp_server = NULL; + bbl_secondary_ip_s *secondary_ip; if(json_typeof(root) != JSON_OBJECT) { fprintf(stderr, "JSON config error: Configuration root element must object\n"); @@ -546,6 +547,7 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { } } + /* Interface Configuration */ section = json_object_get(root, "interfaces"); if (json_is_object(section)) { @@ -578,7 +580,7 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { fprintf(stderr, "JSON config error: Invalid value for network->gateway\n"); return false; } - ctx->config.network_gateway= ipv4; + ctx->config.network_gateway = ipv4; } if (json_unpack(sub, "{s:s}", "address-ipv6", &s) == 0) { if(!inet_pton(AF_INET6, s, &ctx->config.network_ip6.address)) { @@ -632,6 +634,78 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { fprintf(stderr, "JSON config error: Missing interfaces section\n"); return false; } + + /* L2TP Server Configuration (LNS) */ + section = json_object_get(root, "l2tp-server"); + if (json_is_array(section)) { + if(!ctx->config.network_gateway || !ctx->config.network_ip) { + fprintf(stderr, "JSON config error: Failed to add L2TP server because of missing or incomplete network interface config\n"); + return false; + } + size = json_array_size(section); + for (i = 0; i < size; i++) { + sub = json_array_get(section, i); + if(!l2tp_server) { + ctx->config.l2tp_server = malloc(sizeof(bbl_l2tp_server_t)); + l2tp_server = ctx->config.l2tp_server; + } else { + l2tp_server->next = malloc(sizeof(bbl_l2tp_server_t)); + l2tp_server = l2tp_server->next; + } + memset(l2tp_server, 0x0, sizeof(bbl_l2tp_server_t)); + if (json_unpack(sub, "{s:s}", "name", &s) == 0) { + l2tp_server->host_name = strdup(s); + } else { + fprintf(stderr, "JSON config error: Missing value for l2tp-server->name\n"); + return false; + } + if (json_unpack(sub, "{s:s}", "secret", &s) == 0) { + l2tp_server->secret = strdup(s); + } + if (json_unpack(sub, "{s:s}", "address", &s) == 0) { + if(!inet_pton(AF_INET, s, &ipv4)) { + fprintf(stderr, "JSON config error: Invalid value for l2tp-server->address\n"); + return false; + } + l2tp_server->ip = ipv4; + CIRCLEQ_INIT(&l2tp_server->tunnel_qhead); + + if(ipv4 != ctx->config.network_ip) { + /* Add secondary IP address to be served by ARP */ + secondary_ip = ctx->config.secondary_ip_addresses; + if(secondary_ip) { + while(secondary_ip) { + if(secondary_ip->ip == ipv4) { + /* Address is already known ... */ + break; + } + if(secondary_ip->next) { + /* Check next address ... */ + secondary_ip = secondary_ip->next; + } else { + /* Append secondary address ... */ + secondary_ip->next = malloc(sizeof(bbl_secondary_ip_s)); + memset(secondary_ip->next, 0x0, sizeof(bbl_secondary_ip_s)); + secondary_ip = secondary_ip->next; + secondary_ip->ip = ipv4; + break; + } + } + } else { + /* Add first secondary address */ + ctx->config.secondary_ip_addresses = malloc(sizeof(bbl_secondary_ip_s)); + memset(ctx->config.secondary_ip_addresses, 0x0, sizeof(bbl_secondary_ip_s)); + ctx->config.secondary_ip_addresses->ip = ipv4; + } + } + } else { + fprintf(stderr, "JSON config error: Missing value for l2tp-server->address\n"); + } + } + } else if (json_is_object(sub)) { + fprintf(stderr, "JSON config error: List expected in L2TP server configuration but dictionary found\n"); + } + return true; } diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c new file mode 100644 index 00000000..7e51ffb3 --- /dev/null +++ b/src/bbl_l2tp.c @@ -0,0 +1,590 @@ +/* + * BNG Blaster (BBL) - L2TPv2 Functions + * + * Christian Giese, February 2022 + * + * Copyright (C) 2020-2021, RtBrick, Inc. + */ + +#include "bbl.h" +#include "bbl_logging.h" +#include +#include + +const char* +l2tp_message_string(l2tp_message_type type) +{ + switch(type) { + case L2TP_MESSAGE_DATA: return "DATA"; + case L2TP_MESSAGE_SCCRQ: return "SCCRQ"; + case L2TP_MESSAGE_SCCRP: return "SCCRP"; + case L2TP_MESSAGE_SCCCN: return "SCCCN"; + case L2TP_MESSAGE_STOPCCN: return "StopCCN"; + case L2TP_MESSAGE_HELLO: return "HELLO"; + case L2TP_MESSAGE_OCRQ: return "OCRQ"; + case L2TP_MESSAGE_OCRP: return "OCRP"; + case L2TP_MESSAGE_OCCN: return "OCCN"; + case L2TP_MESSAGE_ICRQ: return "ICRQ"; + case L2TP_MESSAGE_ICRP: return "ICRP"; + case L2TP_MESSAGE_ICCN: return "ICCN"; + case L2TP_MESSAGE_CDN: return "CDN"; + case L2TP_MESSAGE_WEN: return "WEN"; + case L2TP_MESSAGE_CSUN: return "CSUN"; + case L2TP_MESSAGE_CSURQ: return "CSURQ"; + case L2TP_MESSAGE_ZLB: return "ZLB"; + default: return "UNKNOWN"; + } +} + +/** + * bbl_l2tp_session_delete + * + * This function will free all dynamic memory for the given + * l2tp session instance. + * + * @param l2tp_session Pointer to L2TP session object to be deleted. + */ +void +bbl_l2tp_session_delete(bbl_l2tp_session_t *l2tp_session) { + if(l2tp_session) { + /* Remove session from tunnel object */ + if(CIRCLEQ_NEXT(l2tp_session, session_qnode) != NULL) { + CIRCLEQ_REMOVE(&l2tp_session->tunnel->session_qhead, l2tp_session, session_qnode); + } + /* Free tunnel memory */ + if(l2tp_session->proxy_auth_name) free(l2tp_session->proxy_auth_name); + if(l2tp_session->proxy_auth_challenge) free(l2tp_session->proxy_auth_challenge); + if(l2tp_session->proxy_auth_response) free(l2tp_session->proxy_auth_response); + if(l2tp_session->peer_called_number) free(l2tp_session->peer_called_number); + if(l2tp_session->peer_calling_number) free(l2tp_session->peer_calling_number); + if(l2tp_session->peer_sub_address) free(l2tp_session->peer_sub_address); + free(l2tp_session); + } +} + +/** + * bbl_l2tp_tunnel_delete + * + * This function will free all dynamic memory for the given + * l2tp tunnel instance including corresponding send queues. + * + * @param l2tp_tunnel Pointer to L2TP tunnel object to be deleted. + */ +void +bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_t *l2tp_tunnel) { + bbl_l2tp_queue_t *q = NULL; + bbl_interface_s *interface = l2tp_tunnel->server->interface; + + if(l2tp_tunnel) { + /* Delete all remaining sessions */ + while (!CIRCLEQ_EMPTY(&l2tp_tunnel->session_qhead)) { + bbl_l2tp_session_delete(CIRCLEQ_FIRST(&l2tp_tunnel->session_qhead)); + } + /* Remove tunnel from server object */ + if(CIRCLEQ_NEXT(l2tp_tunnel, tunnel_qnode) != NULL) { + CIRCLEQ_REMOVE(&l2tp_tunnel->server->tunnel_qhead, l2tp_tunnel, tunnel_qnode); + } + /* Cleanup send queues */ + while (!CIRCLEQ_EMPTY(&l2tp_tunnel->txq_qhead)) { + q = CIRCLEQ_FIRST(&l2tp_tunnel->txq_qhead); + if(CIRCLEQ_NEXT(q, tx_qnode) != NULL) { + CIRCLEQ_REMOVE(&interface->l2tp_tx_qhead, q, tx_qnode); + } + free(q->packet); + free(q); + } + + if(l2tp_tunnel->zlb_qnode) { + free(l2tp_tunnel->zlb_qnode->packet); + free(l2tp_tunnel->zlb_qnode); + } + /* Free tunnel memory */ + if(l2tp_tunnel->challenge) free(l2tp_tunnel->challenge); + if(l2tp_tunnel->peer_challenge) free(l2tp_tunnel->peer_challenge); + if(l2tp_tunnel->challenge_response) free(l2tp_tunnel->challenge_response); + if(l2tp_tunnel->peer_challenge_response) free(l2tp_tunnel->peer_challenge_response); + if(l2tp_tunnel->peer_name) free(l2tp_tunnel->peer_name); + if(l2tp_tunnel->peer_vendor) free(l2tp_tunnel->peer_vendor); + free(l2tp_tunnel); + } +} + +/** + * bbl_l2tp_tunnel_tx_job + * + * This function ... + */ +void +bbl_l2tp_tunnel_tx_job (timer_s *timer) { + bbl_l2tp_tunnel_t *l2tp_tunnel = timer->data; + bbl_interface_s *interface = l2tp_tunnel->server->interface; + bbl_l2tp_queue_t *q = NULL; + bbl_l2tp_queue_t *q_del = NULL; + + struct timespec timestamp; + struct timespec time_diff; + double time_diff_ms; + + uint16_t max_ns = l2tp_tunnel->peer_nr + l2tp_tunnel->cwnd; + + l2tp_tunnel->timer_tx_active = false; + if(!CIRCLEQ_EMPTY(&interface->l2tp_tx_qhead)) { + return; + } + + clock_gettime(CLOCK_REALTIME, ×tamp); + + q = CIRCLEQ_FIRST(&l2tp_tunnel->txq_qhead); + while (q != (const void *)(&l2tp_tunnel->txq_qhead)) { + if (L2TP_SEQ_LT(q->ns, l2tp_tunnel->peer_nr)) { + /* Delete acknowledged messages from queue. */ + q_del = q; + q = CIRCLEQ_NEXT(q, txq_qnode); + CIRCLEQ_REMOVE(&l2tp_tunnel->txq_qhead, q_del, txq_qnode); + free(q_del->packet); + free(q_del); + } + if (L2TP_SEQ_LT(q->ns, max_ns)) { + if(q->last_tx_time.tv_sec) { + timespec_sub(&time_diff, ×tamp, &q->last_tx_time); + time_diff_ms = round(time_diff.tv_nsec / 1.0e6) * (time_diff.tv_sec * 1000); + if(time_diff_ms < 1000) { + continue; + } + } + CIRCLEQ_INSERT_TAIL(&interface->l2tp_tx_qhead, q, tx_qnode); + l2tp_tunnel->zlb = false; + q->last_tx_time.tv_sec = timestamp.tv_sec; + q->last_tx_time.tv_nsec = timestamp.tv_nsec; + /* Update Nr. ... */ + *(uint16_t*)(q->packet + q->nr_offset) = htobe16(l2tp_tunnel->nr); + q->retries++; + } else { + break; + } + } + if(l2tp_tunnel->zlb) { + CIRCLEQ_INSERT_TAIL(&interface->l2tp_tx_qhead, l2tp_tunnel->zlb_qnode, tx_qnode); + *(uint16_t*)(l2tp_tunnel->zlb_qnode->packet + l2tp_tunnel->zlb_qnode->ns_offset) = htobe16(l2tp_tunnel->ns); + *(uint16_t*)(l2tp_tunnel->zlb_qnode->packet + l2tp_tunnel->zlb_qnode->nr_offset) = htobe16(l2tp_tunnel->nr); + } +} + +/** + * bbl_l2tp_tunnel_control_job + * + * This function ... + */ +void +bbl_l2tp_tunnel_control_job (timer_s *timer) { + bbl_l2tp_tunnel_t *l2tp_tunnel = timer->data; + bbl_interface_s *interface = l2tp_tunnel->server->interface; + bbl_ctx_s *ctx = interface->ctx; + + if(!l2tp_tunnel->timer_tx_active) { + timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, 10 * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); + l2tp_tunnel->timer_tx_active = true; + } +} + +/** + * bbl_l2tp_send + * + * This function ... + * + * @param l2tp_tunnel Mandatory pointer to L2TP tunnel object. + * @param l2tp_session Optional pointer to L2TP session object. + * This parameter is only required of L2TP session packets. + * @param l2tp_type L2TP message type (SCCRP, ICRP, ...) + */ +static void +bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, l2tp_message_type l2tp_type) { + + bbl_interface_s *interface = l2tp_tunnel->server->interface; + bbl_ctx_s *ctx = interface->ctx; + + bbl_l2tp_queue_t *q = calloc(1, sizeof(bbl_l2tp_queue_t)); + + bbl_ethernet_header_t eth = {0}; + bbl_ipv4_t ipv4 = {0}; + bbl_udp_t udp = {0}; + bbl_l2tp_t l2tp = {0}; + + uint8_t sp[L2TP_MAX_AVP_SIZE]; /* scratchpad memory to craft the AVP attributes */ + uint16_t sp_len; + uint len; + + eth.dst = interface->gateway_mac; + eth.src = interface->mac; + eth.vlan_outer = interface->ctx->config.network_vlan; + eth.type = ETH_TYPE_IPV4; + eth.next = &ipv4; + ipv4.dst = l2tp_tunnel->peer_ip; + ipv4.src = l2tp_tunnel->server->ip; + ipv4.ttl = 64; + ipv4.protocol = PROTOCOL_IPV4_UDP; + ipv4.next = &udp; + udp.src = L2TP_UDP_PORT; + udp.dst = L2TP_UDP_PORT; + udp.protocol = UDP_PROTOCOL_L2TP; + udp.next = &l2tp; + l2tp.type = l2tp_type; + l2tp.ns = l2tp_tunnel->ns++; + + /* The Nr. will be set on the fly while sending + * using the latest received Ns. from peer. + * Therfore we need to remember offset to Nr. */ + l2tp.nr = 0; + if(eth.vlan_outer) { + q->ns_offset = 54; + q->nr_offset = 56; + + } else { + q->ns_offset = 50; + q->nr_offset = 52; + } + q->ns = l2tp.ns; + q->tunnel = l2tp_tunnel; + + bbl_l2tp_avp_encode_attributes(l2tp_tunnel, l2tp_session, l2tp_type, sp, &sp_len); + q->packet = malloc(L2TP_MAX_PACKET_SIZE); + if(encode_ethernet(q->packet, &len, ð) == PROTOCOL_SUCCESS) { + q->packet_len = len; + if(l2tp_type == L2TP_MESSAGE_ZLB) { + if(l2tp_tunnel->zlb_qnode) { + free(q->packet); + free(q); + } else { + l2tp_tunnel->zlb_qnode = q; + } + } else { + CIRCLEQ_INSERT_TAIL(&l2tp_tunnel->txq_qhead, q, txq_qnode); + if(!l2tp_tunnel->timer_tx_active) { + timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, 10 * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); + l2tp_tunnel->timer_tx_active = true; + } + } + } else { + /* Encode error.... */ + LOG(ERROR, "L2TP Encode Error\n"); + free(q->packet); + free(q); + } +} + +static void +bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface) { + MD5_CTX md5_ctx; + + bbl_ctx_s *ctx = interface->ctx; + bbl_ipv4_t *ipv4 = (bbl_ipv4_t*)eth->next; + + bbl_l2tp_server_t *l2tp_server = ctx->config.l2tp_server; + bbl_l2tp_tunnel_t *l2tp_tunnel; + bbl_l2tp_tunnel_t *l2tp_tunnel2; + bbl_l2tp_session_t *l2tp_session; + + l2tp_key_t key; + dict_insert_result result; + void **search; + + uint8_t l2tp_type; + + while(l2tp_server) { + if(l2tp_server->ip == ipv4->dst) { + LOG(L2TP, "L2TP Info (%s) SCCRQ received from %s\n", + l2tp_server->host_name, format_ipv4_address(&ipv4->src)); + + l2tp_tunnel = calloc(1, sizeof(bbl_l2tp_tunnel_t)); + l2tp_tunnel->peer_ip = ipv4->src; + l2tp_tunnel->server = l2tp_server; + l2tp_tunnel->state = BBL_L2TP_TUNNEL_WAIT_CTR_CONN; + if(!bbl_l2tp_avp_decode_tunnel(l2tp, l2tp_tunnel)) { + return bbl_l2tp_tunnel_delete(l2tp_tunnel); + } + if(!l2tp_tunnel->peer_tunnel_id || + !l2tp_tunnel->peer_name) { + LOG(L2TP, "L2TP Error (%s) Invalid SCCRQ received from %s\n", + l2tp_server->host_name, format_ipv4_address(&ipv4->src)); + return bbl_l2tp_tunnel_delete(l2tp_tunnel); + } + + /* Check for SCCRQ retry ... */ + CIRCLEQ_FOREACH(l2tp_tunnel2, &l2tp_server->tunnel_qhead, tunnel_qnode) { + if(l2tp_tunnel2->peer_ip == l2tp_tunnel->peer_ip && + l2tp_tunnel2->peer_tunnel_id == l2tp_tunnel->peer_tunnel_id) { + /* Seems to be an SCCRQ retry ... */ + return bbl_l2tp_tunnel_delete(l2tp_tunnel); + } + } + + /* Assign tunnel id ... */ + while(true) { + key.ip = l2tp_server->ip; + key.tunnel_id = l2tp_server->next_tunnel_id++; + key.session_id = 0; + search = dict_search(ctx->l2tp_session_dict, &key); + if(search) { + /* Used, try next ... */ + key.tunnel_id = l2tp_server->next_tunnel_id++; + } else { + break; + } + } + l2tp_tunnel->tunnel_id = key.tunnel_id; + + /* Add dummy tunnel session, this session is only used + * to search for tunnel using the same dictionary. */ + l2tp_session = calloc(1, sizeof(bbl_l2tp_session_t)); + l2tp_session->state = BBL_L2TP_SESSION_MAX; + l2tp_session->tunnel = l2tp_tunnel; + l2tp_session->key.ip = key.ip; + l2tp_session->key.tunnel_id = key.tunnel_id; + result = dict_insert(ctx->l2tp_session_dict, &key); + if (!result.inserted) { + /* TODO: Here we need to handle this properly but actually + * this should not happen. */ + free(l2tp_session); + return; + } + *result.datum_ptr = l2tp_session; + CIRCLEQ_INIT(&l2tp_tunnel->txq_qhead); + CIRCLEQ_INIT(&l2tp_tunnel->session_qhead); + CIRCLEQ_INSERT_TAIL(&l2tp_tunnel->session_qhead, l2tp_session, session_qnode); + + /* L2TP Challenge/Response */ + if(l2tp_server->secret) { + l2tp_tunnel->challenge = malloc(L2TP_MD5_DIGEST_LEN); + l2tp_tunnel->challenge_len = L2TP_MD5_DIGEST_LEN; + RAND_bytes(l2tp_tunnel->challenge, l2tp_tunnel->challenge_len); + if(l2tp_tunnel->peer_challenge_len) { + l2tp_tunnel->challenge_response = malloc(L2TP_MD5_DIGEST_LEN); + l2tp_tunnel->challenge_response_len = L2TP_MD5_DIGEST_LEN; + l2tp_type = L2TP_MESSAGE_SCCRP; + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, &l2tp_type, 1); + MD5_Update(&md5_ctx, (unsigned char *) l2tp_server->secret, strlen(l2tp_server->secret)); + MD5_Update(&md5_ctx, l2tp_tunnel->peer_challenge, l2tp_tunnel->peer_challenge_len); + MD5_Final(l2tp_tunnel->challenge_response, &md5_ctx); + } else { + /* We are not able to setup a session if no challenge + * is received but there is a secret configured! */ + l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; + } + } else { + if(l2tp_tunnel->peer_challenge_len) { + /* We are not able to setup a session if challenge + * is received but not secret configured! */ + l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; + } + } + /* Add tunnel to server */ + CIRCLEQ_INSERT_TAIL(&l2tp_server->tunnel_qhead, l2tp_tunnel, tunnel_qnode); + /* Start control job */ + timer_add_periodic(&ctx->timer_root, &l2tp_tunnel->timer_ctrl, "L2TP Control", 1, 0, l2tp_tunnel, bbl_l2tp_tunnel_control_job); + + /* Send response */ + if(l2tp_tunnel->state == BBL_L2TP_TUNNEL_SEND_STOPCCN) { + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); + } else { + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_SCCRP); + } + return; + } + l2tp_server = l2tp_server->next; + } +} + +static void +bbl_l2tp_scccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_tunnel_t *l2tp_tunnel) { + bbl_ctx_s *ctx = interface->ctx; + + UNUSED(ctx); + UNUSED(eth); + UNUSED(l2tp); + + if(l2tp_tunnel->state == BBL_L2TP_TUNNEL_WAIT_CTR_CONN) { + l2tp_tunnel->state = BBL_L2TP_TUNNEL_ESTABLISHED; + LOG(L2TP, "L2TP Info (%s) Tunnel from %s (%s) estbalished\n", + l2tp_tunnel->server->host_name, l2tp_tunnel->peer_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); + } +} + +static void +bbl_l2tp_stopccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_tunnel_t *l2tp_tunnel) { + bbl_ctx_s *ctx = interface->ctx; + + UNUSED(ctx); + UNUSED(eth); + UNUSED(l2tp); + + l2tp_tunnel->state = BBL_L2TP_TUNNEL_TERMINATED; +} + +static void +bbl_l2tp_icrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_tunnel_t *l2tp_tunnel) { + bbl_ctx_s *ctx = interface->ctx; + + l2tp_key_t key; + dict_insert_result result; + void **search; + + bbl_l2tp_session_t *l2tp_session = calloc(1, sizeof(bbl_l2tp_session_t)); + + UNUSED(eth); + UNUSED(l2tp); + + l2tp_session->tunnel = l2tp_tunnel; + l2tp_tunnel->state = BBL_L2TP_SESSION_WAIT_CONN; + + if(!bbl_l2tp_avp_decode_session(l2tp, l2tp_tunnel, l2tp_session)) { + return bbl_l2tp_session_delete(l2tp_session); + } + + /* Assign session id ... */ + while(true) { + key.ip = l2tp_tunnel->server->ip; + key.tunnel_id = l2tp_tunnel->tunnel_id; + key.session_id = l2tp_tunnel->next_session_id++; + search = dict_search(ctx->l2tp_session_dict, &key); + if(search) { + /* Used, try next ... */ + key.session_id = l2tp_tunnel->next_session_id++; + } else { + break; + } + } + l2tp_session->key.ip = key.ip; + l2tp_session->key.tunnel_id = key.tunnel_id; + l2tp_session->key.session_id = key.session_id; + + result = dict_insert(ctx->l2tp_session_dict, &key); + if (!result.inserted) { + /* TODO: Here we need to handle this properly but actually + * this should not happen. */ + free(l2tp_session); + return; + } + *result.datum_ptr = l2tp_session; + CIRCLEQ_INSERT_TAIL(&l2tp_tunnel->session_qhead, l2tp_session, session_qnode); + + bbl_l2tp_send(l2tp_tunnel, l2tp_session, L2TP_MESSAGE_ICRP); +} + +static void +bbl_l2tp_iccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_session_t *l2tp_session) { + bbl_ctx_s *ctx = interface->ctx; + + UNUSED(ctx); + UNUSED(eth); + UNUSED(l2tp); + + if(l2tp_session->state == BBL_L2TP_SESSION_WAIT_CONN) { + l2tp_session->state = BBL_L2TP_SESSION_ESTABLISHED; + } +} + +static void +bbl_l2tp_cdn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_session_t *l2tp_session) { + bbl_ctx_s *ctx = interface->ctx; + + UNUSED(ctx); + UNUSED(eth); + UNUSED(l2tp); + + l2tp_session->state = BBL_L2TP_SESSION_TERMINATED; + bbl_l2tp_session_delete(l2tp_session); +} + +static void +bbl_l2tp_data_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_session_t *l2tp_session) { + bbl_ctx_s *ctx = interface->ctx; + + UNUSED(ctx); + UNUSED(eth); + UNUSED(l2tp); + UNUSED(l2tp_session); + +} + +/** + * bbl_l2tp_handler_rx + * + * This function ... + * + * @param eth ... + * @param l2tp ... + * @param interface ... + */ +void +bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface) { + bbl_ctx_s *ctx = interface->ctx; + bbl_ipv4_t *ipv4 = (bbl_ipv4_t*)eth->next; + bbl_l2tp_session_t *l2tp_session; + bbl_l2tp_tunnel_t *l2tp_tunnel; + + l2tp_key_t key; + void **search; + + if(l2tp->type == L2TP_MESSAGE_SCCRQ) { + return bbl_l2tp_sccrq_rx(eth, l2tp, interface); + } + + key.ip = ipv4->src; + key.tunnel_id = l2tp->tunnel_id; + key.session_id = l2tp->session_id; + + search = dict_search(interface->ctx->l2tp_session_dict, &key); + if(search) { + l2tp_session = *search; + l2tp_tunnel = l2tp_session->tunnel; + + if(l2tp->type == L2TP_MESSAGE_DATA) { + l2tp_tunnel->stats.data_rx++; + return bbl_l2tp_data_rx(eth, l2tp, interface, l2tp_session); + } + if (L2TP_SEQ_GT(l2tp->nr, l2tp_tunnel->peer_nr)) { + l2tp_tunnel->peer_nr = l2tp->nr; + } + if (l2tp_tunnel->nr == l2tp->ns) { + /* In-Order packet received */ + l2tp_tunnel->peer_ns = l2tp->ns; + l2tp_tunnel->nr = (l2tp->ns + 1); + l2tp_tunnel->zlb = true; + l2tp_tunnel->stats.control_rx++; + if(!l2tp_tunnel->timer_tx_active) { + timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, 10 * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); + l2tp_tunnel->timer_tx_active = true; + } + if(l2tp_tunnel->state != BBL_L2TP_TUNNEL_TERMINATED) { + switch (l2tp->type) { + case L2TP_MESSAGE_SCCCN: + return bbl_l2tp_scccn_rx(eth, l2tp, interface, l2tp_tunnel); + case L2TP_MESSAGE_STOPCCN: + return bbl_l2tp_stopccn_rx(eth, l2tp, interface, l2tp_tunnel); + case L2TP_MESSAGE_ICRQ: + return bbl_l2tp_icrq_rx(eth, l2tp, interface, l2tp_tunnel); + case L2TP_MESSAGE_ICCN: + return bbl_l2tp_iccn_rx(eth, l2tp, interface, l2tp_session); + case L2TP_MESSAGE_CDN: + return bbl_l2tp_cdn_rx(eth, l2tp, interface, l2tp_session); + default: + break; + } + } + } else { + if (L2TP_SEQ_LT(l2tp->ns, l2tp_tunnel->nr)) { + /* Duplicate packet received */ + l2tp_tunnel->zlb = true; + l2tp_tunnel->stats.control_rx_dup++; + if(!l2tp_tunnel->timer_tx_active) { + timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, 10 * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); + l2tp_tunnel->timer_tx_active = true; + } + } else { + /* Out-of-Order packet received */ + l2tp_tunnel->stats.control_rx_ooo++; + } + } + } +} \ No newline at end of file diff --git a/src/bbl_l2tp.h b/src/bbl_l2tp.h new file mode 100644 index 00000000..c7fdab31 --- /dev/null +++ b/src/bbl_l2tp.h @@ -0,0 +1,213 @@ +/* + * BNG Blaster (BBL) - L2TPv2 Functions (RFC2661) + * + * Christian Giese, February 2022 + * + * Copyright (C) 2020-2021, RtBrick, Inc. + */ + +#ifndef __BBL_L2TP_H__ +#define __BBL_L2TP_H__ + +#define L2TP_MD5_DIGEST_LEN 16 +#define L2TP_MAX_PACKET_SIZE 1500 +#define L2TP_MAX_AVP_SIZE 1024 + +#define L2TP_SEQ_LT(_a, _b)\ + (((_a) < (_b) && (_b) - (_a) < 32768) || ((_a) > (_b) && (_a) - (_b) > 32768)) + +#define L2TP_SEQ_GT(_a, _b)\ + (((_a) > (_b) && (_a) - (_b) < 32768) || ((_a) < (_b) && (_b) - (_a) > 32768)) + +typedef struct bbl_interface_ bbl_interface_s; + +/* L2TP Tunnel State */ +typedef enum { + BBL_L2TP_TUNNEL_IDLE = 0, + BBL_L2TP_TUNNEL_WAIT_CTR_CONN = 1, + BBL_L2TP_TUNNEL_ESTABLISHED = 2, + BBL_L2TP_TUNNEL_SEND_STOPCCN = 3, + BBL_L2TP_TUNNEL_TERMINATED = 4, + BBL_L2TP_TUNNEL_MAX +} __attribute__ ((__packed__)) l2tp_tunnel_state_t; + +/* L2TP Session State */ +typedef enum { + BBL_L2TP_SESSION_IDLE = 0, + BBL_L2TP_SESSION_WAIT_CONN = 1, + BBL_L2TP_SESSION_ESTABLISHED = 2, + BBL_L2TP_SESSION_TERMINATED = 3, + BBL_L2TP_SESSION_MAX +} __attribute__ ((__packed__)) l2tp_session_state_t; + +/* L2TP Server Configuration (LNS) */ +typedef struct bbl_l2tp_server_ +{ + struct bbl_interface_ *interface; + l2tp_tunnel_state_t state; + + uint32_t ip; + uint32_t framing; + uint32_t bearer; + uint16_t idle_timeout; + uint16_t hello_interval; + uint16_t session_limit; + uint16_t receive_window; + uint16_t max_retry; + uint16_t next_tunnel_id; + + void *next; /* pointer to next L2TP server element */ + + /* The following members must be freed + * if server is destroyed! */ + + char *secret; + char *host_name; + char *vendor; + + /* List of L2TP tunnel instances + * for the corresponding server. */ + CIRCLEQ_HEAD(bbl_l2tp_server__, bbl_l2tp_tunnel_) tunnel_qhead; +} bbl_l2tp_server_t; + +/* L2TP Session Key */ +typedef struct l2tp_key_ { + uint32_t ip; + uint16_t tunnel_id; + uint16_t session_id; +} __attribute__ ((__packed__)) l2tp_key_t; + +/* L2TP TX Queue Entry */ +typedef struct bbl_l2tp_queue_ +{ + uint16_t ns; + uint8_t ns_offset; + uint8_t nr_offset; + uint8_t retries; + uint8_t *packet; + uint16_t packet_len; + struct timespec last_tx_time; + struct bbl_l2tp_tunnel_ *tunnel; + CIRCLEQ_ENTRY(bbl_l2tp_queue_) txq_qnode; /* TX queue */ + CIRCLEQ_ENTRY(bbl_l2tp_queue_) tx_qnode; /* TX request */ +} bbl_l2tp_queue_t; + +/* L2TP Tunnel Instance */ +typedef struct bbl_l2tp_tunnel_ +{ + CIRCLEQ_ENTRY(bbl_l2tp_tunnel_) tunnel_qnode; + + bbl_l2tp_server_t *server; + l2tp_tunnel_state_t state; + + uint16_t tunnel_id; + uint16_t peer_tunnel_id; + uint16_t next_session_id; + + uint16_t ns; + uint16_t nr; + uint16_t peer_ns; + uint16_t peer_nr; + + uint16_t peer_receive_window; + uint16_t peer_firmware; + uint32_t peer_ip; + uint32_t peer_framing; + uint32_t peer_bearer; + uint32_t peer_tie_breaker; + + bool timer_tx_active; + struct timer_ *timer_tx; + struct timer_ *timer_ctrl; + + + uint16_t retry; + uint16_t cwnd; + uint16_t ssthresh; + uint16_t cwcount; + uint32_t send_timestamp; + + bool zlb; + bbl_l2tp_queue_t *zlb_qnode; + + struct { + uint32_t control_rx; + uint32_t control_rx_dup; + uint32_t control_rx_ooo; + uint32_t control_tx; + uint32_t control_retry; + uint64_t data_rx; + uint64_t data_tx; + } stats; + + uint16_t challenge_len; + uint16_t peer_challenge_len; + uint16_t challenge_response_len; + uint16_t peer_challenge_response_len; + + /* The following members must be freed + * if tunnel is destroyed! */ + + uint8_t *challenge; + uint8_t *peer_challenge; + uint8_t *challenge_response; + uint8_t *peer_challenge_response; + + char *peer_name; + char *peer_vendor; + + CIRCLEQ_HEAD(bbl_l2tp_tunnel__, bbl_l2tp_queue_) txq_qhead; + CIRCLEQ_HEAD(bbl_l2tp_tunnel___, bbl_l2tp_session_) session_qhead; +} bbl_l2tp_tunnel_t; + +/* L2TP Session Instance */ +typedef struct bbl_l2tp_session_ +{ + CIRCLEQ_ENTRY(bbl_l2tp_session_) session_qnode; + + bbl_l2tp_tunnel_t *tunnel; + l2tp_session_state_t state; + + struct { + uint32_t ip; + uint16_t tunnel_id; + uint16_t session_id; + } key; + + uint16_t peer_session_id; + + bool data_sequencing; + + uint32_t peer_tx_bps; + uint32_t peer_rx_bps; + uint32_t peer_framing; + uint32_t peer_bearer; + uint32_t peer_physical_channel_id; + uint32_t peer_private_group_id; + uint32_t peer_call_serial_number; + + uint16_t proxy_auth_type; + uint16_t proxy_auth_id; + uint16_t proxy_auth_name_len; + uint16_t proxy_auth_challenge_len; + uint16_t proxy_auth_response_len; + + /* The following members must be freed + * if session is destroyed! */ + + uint8_t *proxy_auth_name; + uint8_t *proxy_auth_challenge; + uint8_t *proxy_auth_response; + + char *peer_called_number; + char *peer_calling_number; + char *peer_sub_address; + +} bbl_l2tp_session_t; + +const char* +l2tp_message_string(l2tp_message_type type); + +void bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface); + +#endif \ No newline at end of file diff --git a/src/bbl_l2tp_avp.c b/src/bbl_l2tp_avp.c new file mode 100644 index 00000000..1460cf6d --- /dev/null +++ b/src/bbl_l2tp_avp.c @@ -0,0 +1,649 @@ +/* + * BNG Blaster (BBL) - L2TPv2 AVP Functions + * + * Christian Giese, February 2022 + * + * Copyright (C) 2020-2021, RtBrick, Inc. + */ + +#include "bbl.h" +#include "bbl_logging.h" +#include +#include + +/* bbl_l2tp_avp_decode + * 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |M|H| rsvd | Length | Vendor ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Attribute Type | Attribute Value... + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * [until Length is reached]... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ +static bool +bbl_l2tp_avp_decode(uint8_t **_buf, uint16_t *_len, bbl_l2tp_avp_t *avp) { + uint8_t *buf = *_buf; + uint16_t len = *_len; + + if(len < L2TP_AVP_HDR_LEN) { + return false; + } + avp->len = be16toh(*(uint16_t*)buf); + avp->m = !!(avp->len & L2TP_AVP_M_BIT_MASK); + avp->h = !!(avp->len & L2TP_AVP_H_BIT_MASK); + avp->len &= L2TP_AVP_LEN_MASK; + if(avp->len < L2TP_AVP_HDR_LEN || avp->len > len) { + return false; + } + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + avp->len -= L2TP_AVP_HDR_LEN; /* store value len instead of not AVP len */ + avp->vendor = be16toh(*(uint16_t*)buf); + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + avp->type = be16toh(*(uint16_t*)buf); + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + if(avp->len) { + avp->value = buf; + BUMP_BUFFER(buf, len, avp->len); + } else { + avp->value = NULL; + } + *_buf = buf; + *_len = len; + return true; +} + +/* bbl_l2tp_avp_encode */ +static void +bbl_l2tp_avp_encode(uint8_t *buf, uint16_t *len, bbl_l2tp_avp_t *avp) { + uint16_t avp_len_field; + + /* TODO: Currently we do not support to hide (H) AVP values! */ + + avp_len_field = avp->len + L2TP_AVP_HDR_LEN; + if(avp->m) avp_len_field |= L2TP_AVP_M_BIT_MASK; + *(uint16_t*)buf = htobe16(len); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + *(uint16_t*)buf = htobe16(avp->vendor); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + *(uint16_t*)buf = htobe16(avp->type); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + switch (avp->value_type) { + case L2TP_AVP_VALUE_UINT64: + *(uint64_t*)buf = htobe64(*(uint64_t*)avp->value); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint64_t)); + break; + case L2TP_AVP_VALUE_UINT32: + *(uint32_t*)buf = htobe32(*(uint32_t*)avp->value); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint32_t)); + break; + case L2TP_AVP_VALUE_UINT16: + *(uint16_t*)buf = htobe16(*(uint16_t*)avp->value); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + break; + default: + memcpy(buf, avp->value, avp->len); + BUMP_WRITE_BUFFER(buf, len, avp->len); + break; + } +} + +/* bbl_l2tp_avp_unhide */ +static bool +bbl_l2tp_avp_unhide(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_avp_t *avp, uint8_t + *random_vector, uint16_t random_vector_len) { + + MD5_CTX ctx; + + uint8_t digest[L2TP_MD5_DIGEST_LEN]; + static uint8_t sp[L2TP_AVP_MAX_LEN]; + + uint8_t *cursor; + uint8_t *value = avp->value; + uint16_t type = avp->type; + uint16_t len = 0; + uint8_t idx = 0; + + char *secret; + uint16_t secret_len = 0; + + if(!(random_vector && l2tp_tunnel->server->secret)) { + return false; + } + + secret = l2tp_tunnel->server->secret; + secret_len = strlen(secret); + + MD5_Init(&ctx); + MD5_Update(&ctx, &type, L2TP_AVP_TYPE_LEN); + MD5_Update(&ctx, secret, secret_len); + MD5_Update(&ctx, random_vector, random_vector_len); + MD5_Final(digest, &ctx); + + len = (digest[idx++] ^ *value) << 8; + value++; + len = digest[idx++] ^ *value; + value++; + + if (len + 2 > avp->len) { + return false; + } + + avp->len = len; + avp->value = sp; + cursor = avp->value; + + while(len) { + *cursor = digest[idx++] ^ *value; + len--; value++; cursor++; + + if((idx >= L2TP_MD5_DIGEST_LEN) && len) { + idx = 0; + MD5_Init(&ctx); + MD5_Update(&ctx, secret, secret_len); + MD5_Update(&ctx, (value-L2TP_MD5_DIGEST_LEN), L2TP_MD5_DIGEST_LEN); + MD5_Final(digest, &ctx); + } + } + *cursor = 0; // set finale zero + return true; +} + +bool +bbl_l2tp_avp_decode_session(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session) { + + uint8_t *buf = l2tp->payload; + uint16_t len = l2tp->payload_len; + bbl_l2tp_avp_t avp = {0}; + + uint8_t *random_vector = NULL; + uint16_t random_vector_len = 0; + + while(len) { + if(bbl_l2tp_avp_decode(&buf, &len, &avp)) { + if(avp.h) { + if(!bbl_l2tp_avp_unhide(l2tp_tunnel, &avp, random_vector, random_vector_len)) { + LOG(L2TP, "L2TP (%s) Failed to decrypt hidden AVP %u in %s from %s\n", + l2tp_tunnel->server->host_name, avp.type, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + } + if(avp.vendor == 0) { + switch(avp.type) { + case L2TP_AVP_ASSIGNED_SESSION_ID: + if(avp.len != 2) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP assigned session id AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_session->peer_session_id = be16toh(*(uint16_t*)avp.value); + break; + case L2TP_AVP_CALL_SERIAL_NUMBER: + if(avp.len != 4) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP call serial number AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_session->peer_call_serial_number = be32toh(*(uint32_t*)avp.value); + break; + case L2TP_AVP_FRAMING_TYPE: + if(avp.len != 4) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP framing type AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_session->peer_framing = be32toh(*(uint32_t*)avp.value); + break; + case L2TP_AVP_BEARER_TYPE: + if(avp.len != 4) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP bearer type AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_session->peer_bearer = be32toh(*(uint32_t*)avp.value); + break; + case L2TP_AVP_CALLED_NUMBER: + if(!l2tp_session->peer_called_number && avp.len) { + l2tp_session->peer_called_number = malloc(avp.len + 1); + memcpy(l2tp_session->peer_called_number, avp.value, avp.len); + *(l2tp_session->peer_called_number + avp.len) = '\0'; + } + break; + case L2TP_AVP_CALLING_NUMBER: + if(!l2tp_session->peer_calling_number && avp.len) { + l2tp_session->peer_calling_number = malloc(avp.len + 1); + memcpy(l2tp_session->peer_calling_number, avp.value, avp.len); + *(l2tp_session->peer_calling_number + avp.len) = '\0'; + } + break; + case L2TP_AVP_SUB_ADDRESS: + if(!l2tp_session->peer_sub_address && avp.len) { + l2tp_session->peer_sub_address = malloc(avp.len + 1); + memcpy(l2tp_session->peer_sub_address, avp.value, avp.len); + *(l2tp_session->peer_sub_address + avp.len) = '\0'; + } + break; + case L2TP_AVP_TX_CONNECT_SPEED: + if(avp.len != 4) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP tx connect speed AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_session->peer_tx_bps = be32toh(*(uint32_t*)avp.value); + break; + case L2TP_AVP_RX_CONNECT_SPEED: + if(avp.len != 4) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP rx connect speed AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_session->peer_rx_bps = be32toh(*(uint32_t*)avp.value); + break; + case L2TP_AVP_PHYSICAL_CHANNEL_ID: + if(avp.len != 4) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP physical channel AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_session->peer_physical_channel_id = be32toh(*(uint32_t*)avp.value); + break; + case L2TP_AVP_PRIVATE_GROUP_ID: + if(avp.len != 4) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP private group id AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_session->peer_private_group_id = be32toh(*(uint32_t*)avp.value); + break; + case L2TP_AVP_SEQUENCING_REQUIRED: + l2tp_session->data_sequencing = true; + break; + case L2TP_AVP_INITIAL_RECEIVED_CONFREQ: + case L2TP_AVP_LAST_SENT_CONFREQ: + case L2TP_AVP_LAST_RECEIVED_CONFREQ: + /* Currently not needed! */ + break; + case L2TP_AVP_PROXY_AUTHEN_TYPE: + if(avp.len != 2) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP proxy auth type AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_session->proxy_auth_type = be16toh(*(uint16_t*)avp.value); + break; + case L2TP_AVP_PROXY_AUTHEN_NAME: + if(!l2tp_session->proxy_auth_name && avp.len) { + l2tp_session->proxy_auth_name = malloc(avp.len + 1); + memcpy(l2tp_session->proxy_auth_name, avp.value, avp.len); + *(l2tp_session->proxy_auth_name + avp.len) = '\0'; + l2tp_session->proxy_auth_name_len = avp.len; + } + break; + case L2TP_AVP_PROXY_AUTHEN_CHALLENGE: + if(!l2tp_session->proxy_auth_challenge && avp.len) { + l2tp_session->proxy_auth_challenge = malloc(avp.len); + memcpy(l2tp_session->proxy_auth_challenge, avp.value, avp.len); + l2tp_session->proxy_auth_challenge_len = avp.len; + } + break; + case L2TP_AVP_PROXY_AUTHEN_ID: + if(avp.len != 2) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP proxy auth id AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_session->proxy_auth_id = be16toh(*(uint16_t*)avp.value); + break; + case L2TP_AVP_PROXY_AUTHEN_RESPONSE: + if(!l2tp_session->proxy_auth_response && avp.len) { + l2tp_session->proxy_auth_response = malloc(avp.len + 1); + memcpy(l2tp_session->proxy_auth_response, avp.value, avp.len); + *(l2tp_session->proxy_auth_response + avp.len) = '\0'; + l2tp_session->proxy_auth_response_len = avp.len; + } + break; + default: + if(avp.m) { + LOG(L2TP, "L2TP Error (%s) Mandatory standard AVP with unknown type %u in %s from %s\n", + l2tp_tunnel->server->host_name, avp.type, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } else { + LOG(L2TP, "L2TP Warning (%s) Optional standard AVP with unknown type %u in %s from %s\n", + l2tp_tunnel->server->host_name, avp.type, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + } + break; + } + } else { + if(avp.m) { + LOG(L2TP, "L2TP (%s) Mandatory AVP with unknown vendor %u in %s from %s\n", + l2tp_tunnel->server->host_name, avp.vendor, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + } + } else { + LOG(L2TP, "L2TP (%s) Failed to decdoe session attributes in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + } + return true; +} + +bool +bbl_l2tp_avp_decode_tunnel(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel) { + + uint8_t *buf = l2tp->payload; + uint16_t len = l2tp->payload_len; + bbl_l2tp_avp_t avp = {0}; + + uint8_t *random_vector = NULL; + uint16_t random_vector_len = 0; + + while(len) { + if(bbl_l2tp_avp_decode(&buf, &len, &avp)) { + if(avp.h) { + if(!bbl_l2tp_avp_unhide(l2tp_tunnel, &avp, random_vector, random_vector_len)) { + LOG(L2TP, "L2TP (%s) Failed to decrypt hidden AVP %u in %s from %s\n", + l2tp_tunnel->server->host_name, avp.type, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + } + if(avp.vendor == 0) { + switch(avp.type) { + case L2TP_AVP_PROTOCOL_VERSION: + if(avp.len != 2 || be16toh(*(uint16_t*)avp.value) != 256) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP protocol version AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + break; + case L2TP_AVP_FRAMING_CAPABILITIES: + if(avp.len != 4) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP framing capabilities AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_tunnel->peer_framing = be32toh(*(uint32_t*)avp.value); + break; + case L2TP_AVP_BEARER_CAPABILITIES: + if(avp.len != 4) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP bearer capabilities AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_tunnel->peer_bearer = be32toh(*(uint32_t*)avp.value); + break; + case L2TP_AVP_FIRMWARE_REVISION: + if(avp.len != 2) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP firmware revision AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_tunnel->peer_firmware = be16toh(*(uint16_t*)avp.value); + break; + case L2TP_AVP_HOST_NAME: + if(!l2tp_tunnel->peer_name && avp.len) { + l2tp_tunnel->peer_name = malloc(avp.len + 1); + memcpy(l2tp_tunnel->peer_name, avp.value, avp.len); + *(l2tp_tunnel->peer_name + avp.len) = '\0'; + } + break; + case L2TP_AVP_VENDOR_NAME: + if(!l2tp_tunnel->peer_vendor && avp.len) { + l2tp_tunnel->peer_vendor = malloc(avp.len + 1); + memcpy(l2tp_tunnel->peer_vendor, avp.value, avp.len); + *(l2tp_tunnel->peer_vendor + avp.len) = '\0'; + } + break; + case L2TP_AVP_ASSIGNED_TUNNEL_ID: + if(avp.len != 2) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP assigned tunnel id AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_tunnel->peer_tunnel_id = be16toh(*(uint16_t*)avp.value); + break; + case L2TP_AVP_RECEIVE_WINDOW_SIZE: + if(avp.len != 2) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP receive window size AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + l2tp_tunnel->peer_receive_window = be16toh(*(uint16_t*)avp.value); + break; + case L2TP_AVP_CHALLENGE: + if(!l2tp_tunnel->peer_challenge && avp.len) { + l2tp_tunnel->peer_challenge = malloc(avp.len); + memcpy(l2tp_tunnel->peer_challenge, avp.value, avp.len); + } + break; + case L2TP_AVP_CHALLENGE_RESPONSE: + if(avp.len != L2TP_MD5_DIGEST_LEN) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP challenge response AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + if(!l2tp_tunnel->peer_challenge_response) { + l2tp_tunnel->peer_challenge_response = malloc(avp.len); + memcpy(l2tp_tunnel->peer_challenge_response, avp.value, avp.len); + } + break; + default: + if(avp.m) { + LOG(L2TP, "L2TP Error (%s) Mandatory standard AVP with unknown type %u in %s from %s\n", + l2tp_tunnel->server->host_name, avp.type, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } else { + LOG(L2TP, "L2TP Warning (%s) Optional standard AVP with unknown type %u in %s from %s\n", + l2tp_tunnel->server->host_name, avp.type, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + } + break; + } + } else { + if(avp.m) { + LOG(L2TP, "L2TP (%s) Mandatory AVP with unknown vendor %u received from %s\n", + l2tp_tunnel->server->host_name, avp.vendor, format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + } + } else { + LOG(L2TP, "L2TP (%s) Failed to decdoe tunnel attributes from %s\n", + l2tp_tunnel->server->host_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + } + return true; +} + + +void +bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, + l2tp_message_type l2tp_type, uint8_t *buf, uint16_t *len) { + + bbl_l2tp_avp_t avp; + + uint16_t v16; + uint32_t v32; + + if(l2tp_type == L2TP_MESSAGE_ZLB) { + return; + } + + /* Protocol Version */ + v16 = l2tp_type; + avp.m = true; + if(l2tp_type == L2TP_MESSAGE_CSURQ) { + avp.m = false; + } + avp.type = L2TP_AVP_MESSAGE_TYPE; + avp.len = 2; + avp.value_type = L2TP_AVP_VALUE_UINT16; + avp.value = (void*)&v16; + bbl_l2tp_avp_encode(buf, len, &avp); + + switch (l2tp_type) { + case L2TP_MESSAGE_SCCRP: + /* Protocol Version */ + v16 = 256; + avp.m = true; + avp.type = L2TP_AVP_PROTOCOL_VERSION; + avp.len = 2; + avp.value_type = L2TP_AVP_VALUE_UINT16; + avp.value = (void*)&v16; + bbl_l2tp_avp_encode(buf, len, &avp); + /* Framing Capabilities */ + v32 = 3; /* A + S */ + avp.m = true; + avp.type = L2TP_AVP_FRAMING_CAPABILITIES; + avp.len = 4; + avp.value_type = L2TP_AVP_VALUE_UINT32; + avp.value = (void*)&v32; + /* Bearer Capabilities */ + bbl_l2tp_avp_encode(buf, len, &avp); + v32 = 1; /* D */ + avp.m = true; + avp.type = L2TP_AVP_BEARER_CAPABILITIES; + avp.len = 4; + avp.value_type = L2TP_AVP_VALUE_UINT32; + avp.value = (void*)&v32; + bbl_l2tp_avp_encode(buf, len, &avp); + /* Firmware Revision */ + v16 = 1; + avp.m = false; + avp.type = L2TP_AVP_BEARER_CAPABILITIES; + avp.len = 2; + avp.value_type = L2TP_AVP_VALUE_UINT16; + avp.value = (void*)&v16; + bbl_l2tp_avp_encode(buf, len, &avp); + /* Host Name */ + avp.m = true; + avp.type = L2TP_AVP_HOST_NAME; + avp.len = strlen(l2tp_tunnel->server->host_name); + avp.value_type = L2TP_AVP_VALUE_BYTES; + avp.value = (void*)(l2tp_tunnel->server->host_name); + bbl_l2tp_avp_encode(buf, len, &avp); + /* Vendor Name */ + avp.m = false; + avp.type = L2TP_AVP_VENDOR_NAME; + avp.len = strlen(l2tp_tunnel->server->vendor); + avp.value_type = L2TP_AVP_VALUE_BYTES; + avp.value = (void*)(l2tp_tunnel->server->vendor); + bbl_l2tp_avp_encode(buf, len, &avp); + /* Assigned Tunnel ID */ + avp.m = true; + avp.type = L2TP_AVP_ASSIGNED_TUNNEL_ID; + avp.len = 2; + avp.value_type = L2TP_AVP_VALUE_UINT16; + avp.value = (void*)(&l2tp_tunnel->tunnel_id); + bbl_l2tp_avp_encode(buf, len, &avp); + /* Receive Window Size */ + avp.m = true; + avp.type = L2TP_AVP_RECEIVE_WINDOW_SIZE; + avp.len = 2; + avp.value_type = L2TP_AVP_VALUE_UINT16; + avp.value = (void*)(&l2tp_tunnel->server->receive_window); + bbl_l2tp_avp_encode(buf, len, &avp); + /* Challenge */ + if(l2tp_tunnel->challenge_len) { + avp.m = false; + avp.type = L2TP_AVP_CHALLENGE; + avp.len = l2tp_tunnel->challenge_len; + avp.value_type = L2TP_AVP_VALUE_BYTES; + avp.value = l2tp_tunnel->challenge; + bbl_l2tp_avp_encode(buf, len, &avp); + } + /* Challenge Response */ + if(l2tp_tunnel->challenge_response_len) { + avp.m = false; + avp.type = L2TP_AVP_CHALLENGE_RESPONSE; + avp.len = l2tp_tunnel->challenge_response_len; + avp.value_type = L2TP_AVP_VALUE_BYTES; + avp.value = l2tp_tunnel->challenge_response; + bbl_l2tp_avp_encode(buf, len, &avp); + } + break; + case L2TP_MESSAGE_STOPCCN: + /* Assigned Tunnel ID */ + avp.m = true; + avp.type = L2TP_AVP_ASSIGNED_TUNNEL_ID; + avp.len = 2; + avp.value_type = L2TP_AVP_VALUE_UINT16; + avp.value = (void*)(&l2tp_tunnel->tunnel_id); + bbl_l2tp_avp_encode(buf, len, &avp); + break; + case L2TP_MESSAGE_ICRP: + /* Assigned Session ID */ + if(l2tp_session) { + avp.m = true; + avp.type = L2TP_AVP_ASSIGNED_SESSION_ID; + avp.len = 2; + avp.value_type = L2TP_AVP_VALUE_UINT16; + avp.value = (void*)(&l2tp_session->key.session_id); + bbl_l2tp_avp_encode(buf, len, &avp); + } + break; + case L2TP_MESSAGE_CDN: + /* Assigned Session ID */ + if(l2tp_session) { + avp.m = true; + avp.type = L2TP_AVP_ASSIGNED_SESSION_ID; + avp.len = 2; + avp.value_type = L2TP_AVP_VALUE_UINT16; + avp.value = (void*)(&l2tp_session->key.session_id); + bbl_l2tp_avp_encode(buf, len, &avp); + } + break; + default: + break; + } +} \ No newline at end of file diff --git a/src/bbl_l2tp_avp.h b/src/bbl_l2tp_avp.h new file mode 100644 index 00000000..bb6805bd --- /dev/null +++ b/src/bbl_l2tp_avp.h @@ -0,0 +1,85 @@ +/* + * BNG Blaster (BBL) - L2TPv2 Functions (RFC2661) + * + * Christian Giese, February 2022 + * + * Copyright (C) 2020-2021, RtBrick, Inc. + */ + +#ifndef __BBL_L2TP_AVP_H__ +#define __BBL_L2TP_AVP_H__ + +typedef enum l2tp_avp_type_ { + L2TP_AVP_MESSAGE_TYPE = 0, + L2TP_AVP_RESULT_CODE = 1, + L2TP_AVP_PROTOCOL_VERSION = 2, + L2TP_AVP_FRAMING_CAPABILITIES = 3, + L2TP_AVP_BEARER_CAPABILITIES = 4, + L2TP_AVP_TIE_BREAKER = 5, + L2TP_AVP_FIRMWARE_REVISION = 6, + L2TP_AVP_HOST_NAME = 7, + L2TP_AVP_VENDOR_NAME = 8, + L2TP_AVP_ASSIGNED_TUNNEL_ID = 9, + L2TP_AVP_RECEIVE_WINDOW_SIZE = 10, + L2TP_AVP_CHALLENGE = 11, + L2TP_AVP_CHALLENGE_RESPONSE = 13, + L2TP_AVP_ASSIGNED_SESSION_ID = 14, + L2TP_AVP_CALL_SERIAL_NUMBER = 15, + L2TP_AVP_MINIMUM_BPS = 16, + L2TP_AVP_MAXIMUM_BPS = 17, + L2TP_AVP_BEARER_TYPE = 18, + L2TP_AVP_FRAMING_TYPE = 19, + L2TP_AVP_CALLED_NUMBER = 21, + L2TP_AVP_CALLING_NUMBER = 22, + L2TP_AVP_SUB_ADDRESS = 23, + L2TP_AVP_TX_CONNECT_SPEED = 24, + L2TP_AVP_PHYSICAL_CHANNEL_ID = 25, + L2TP_AVP_INITIAL_RECEIVED_CONFREQ = 26, + L2TP_AVP_LAST_SENT_CONFREQ = 27, + L2TP_AVP_LAST_RECEIVED_CONFREQ = 28, + L2TP_AVP_PROXY_AUTHEN_TYPE = 29, + L2TP_AVP_PROXY_AUTHEN_NAME = 30, + L2TP_AVP_PROXY_AUTHEN_CHALLENGE = 31, + L2TP_AVP_PROXY_AUTHEN_ID = 32, + L2TP_AVP_PROXY_AUTHEN_RESPONSE = 33, + L2TP_AVP_CALL_ERRORS = 34, + L2TP_AVP_ACCM = 35, + L2TP_AVP_RANDOM_VECTOR = 36, + L2TP_AVP_PRIVATE_GROUP_ID = 37, + L2TP_AVP_RX_CONNECT_SPEED = 38, + L2TP_AVP_SEQUENCING_REQUIRED = 39, + L2TP_AVP_PPP_DISCONNECT_CODE = 46, + L2TP_AVP_CONNECT_SPEED_UPDATE = 97, + L2TP_AVP_CONNECT_SPEED_UPDATE_ENABLE = 98, + L2TP_AVP_MAX +} l2tp_avp_type; + +typedef enum l2tp_avp_value_type_ { + L2TP_AVP_VALUE_BYTES = 0, + L2TP_AVP_VALUE_UINT16, + L2TP_AVP_VALUE_UINT32, + L2TP_AVP_VALUE_UINT64 +} l2tp_avp_value_type; + +typedef struct bbl_l2tp_avp_ +{ + bool m; + bool h; + uint16_t len; + uint16_t vendor; + uint16_t type; + uint8_t *value; + uint8_t value_type; +} bbl_l2tp_avp_t; + +bool +bbl_l2tp_avp_decode_session(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session); + +bool +bbl_l2tp_avp_decode_tunnel(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel); + +void +bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, + l2tp_message_type l2tp_type, uint8_t *buf, uint16_t *len); + +#endif \ No newline at end of file diff --git a/src/bbl_logging.c b/src/bbl_logging.c index 989ceef2..2e7533bd 100644 --- a/src/bbl_logging.c +++ b/src/bbl_logging.c @@ -27,6 +27,7 @@ struct keyval_ log_names[] = { { TIMER_DETAIL, "timer-detail" }, { IP, "ip" }, { LOSS, "loss" }, + { L2TP, "l2tp" }, { 0, NULL} }; diff --git a/src/bbl_logging.h b/src/bbl_logging.h index a2f13d0f..75447d44 100644 --- a/src/bbl_logging.h +++ b/src/bbl_logging.h @@ -32,6 +32,7 @@ enum { PCAP, IP, LOSS, + L2TP, LOG_ID_MAX }; diff --git a/src/bbl_protocols.c b/src/bbl_protocols.c index 2982e558..69247702 100644 --- a/src/bbl_protocols.c +++ b/src/bbl_protocols.c @@ -7,6 +7,10 @@ */ #include "bbl_protocols.h" + +protocol_error_t decode_l2tp(uint8_t *buf, uint len, uint8_t *sp, uint sp_len, bbl_l2tp_t **_l2tp); +protocol_error_t encode_l2tp(uint8_t *buf, uint *len, bbl_l2tp_t *l2tp); + uint16_t checksum(uint16_t *buf, uint16_t len) { uint32_t sum = 0; @@ -220,7 +224,6 @@ encode_bbl(uint8_t *buf, uint *len, return PROTOCOL_SUCCESS; } - /* * encode_icmp */ @@ -245,6 +248,9 @@ encode_udp(uint8_t *buf, uint *len, case UDP_PROTOCOL_BBL: result = encode_bbl(buf, len, (bbl_bbl_t*)udp->next); break; + case UDP_PROTOCOL_L2TP: + result = encode_l2tp(buf, len, (bbl_l2tp_t*)udp->next); + break; default: result = PROTOCOL_SUCCESS; break; @@ -801,6 +807,109 @@ encode_ppp_lcp(uint8_t *buf, uint *len, return PROTOCOL_SUCCESS; } +protocol_error_t +encode_l2tp(uint8_t *buf, uint *len, bbl_l2tp_t *l2tp) { + + protocol_error_t result; + uint16_t *l2tp_len_field = NULL; + uint16_t l2tp_len = *len; + + /* Set flags */ + if(l2tp->type) { + /* L2TP control packet */ + *buf |= L2TP_HDR_CTRL_BIT_MASK; + *buf |= L2TP_HDR_LEN_BIT_MASK; + *buf |= L2TP_HDR_SEQ_BIT_MASK; + l2tp->with_length = true; + l2tp->with_sequence = true; + } else { + if(l2tp->with_length) *buf |= L2TP_HDR_LEN_BIT_MASK; + } + if(l2tp->with_offset) *buf |= L2TP_HDR_OFFSET_BIT_MASK; + if(l2tp->with_priority) *buf |= L2TP_HDR_PRIORITY_BIT_MASK; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + *buf = 2; /* Set L2TP version */ + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + if(l2tp->with_length) { + /* Remember L2TP length field position */ + l2tp_len_field = (uint16_t*)buf; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + } + *(uint16_t*)buf = htobe16(l2tp->tunnel_id); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + *(uint16_t*)buf = htobe16(l2tp->session_id); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + if(l2tp->with_sequence) { + *(uint16_t*)buf = htobe16(l2tp->ns); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + *(uint16_t*)buf = htobe16(l2tp->nr); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + } + if(l2tp->with_offset) { + *(uint16_t*)buf = htobe16(l2tp->offset); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + if(l2tp->offset) { + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + } + } + if(l2tp->type) { + /* L2TP control packet */ + *buf = 0x80; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + *buf = 0x08; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + *(uint32_t*)buf = 0; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint32_t)); + *(uint16_t*)buf = htobe16(l2tp->type); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + if(l2tp->payload_len) { + memcpy(buf, l2tp->payload, l2tp->payload_len); + BUMP_WRITE_BUFFER(buf, len, l2tp->payload_len); + } + result = PROTOCOL_SUCCESS; + } else { + /* L2TP data packet */ + *buf = 0xff; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + *buf = 0x03; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + *(uint16_t*)buf = htobe16(l2tp->protocol); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + /* Add protocol */ + switch(l2tp->protocol) { + case PROTOCOL_LCP: + result = encode_ppp_lcp(buf, len, (bbl_lcp_t*)l2tp->next); + break; + case PROTOCOL_IPCP: + result = encode_ppp_ipcp(buf, len, (bbl_ipcp_t*)l2tp->next); + break; + case PROTOCOL_IP6CP: + result = encode_ppp_ip6cp(buf, len, (bbl_ip6cp_t*)l2tp->next); + break; + case PROTOCOL_PAP: + result = encode_ppp_pap(buf, len, (bbl_pap_t*)l2tp->next); + break; + case PROTOCOL_CHAP: + result = encode_ppp_chap(buf, len, (bbl_chap_t*)l2tp->next); + break; + case PROTOCOL_IPV4: + result = encode_ipv4(buf, len, (bbl_ipv4_t*)l2tp->next); + break; + case PROTOCOL_IPV6: + result = encode_ipv6(buf, len, (bbl_ipv6_t*)l2tp->next); + break; + default: + result = UNKNOWN_PROTOCOL; + break; + } + } + if(l2tp->with_length && l2tp_len_field) { + l2tp_len = *len - l2tp_len; + *l2tp_len_field = htobe16(l2tp_len); + } + return result; +} + /* * encode_pppoe_discovery * @@ -1337,6 +1446,10 @@ decode_udp(uint8_t *buf, uint len, udp->protocol = UDP_PROTOCOL_BBL; ret_val = decode_bbl(buf, len, sp, sp_len, (bbl_bbl_t**)&udp->next); break; + case L2TP_UDP_PORT: + udp->protocol = UDP_PROTOCOL_L2TP; + ret_val = decode_l2tp(buf, len, sp, sp_len, (bbl_l2tp_t**)&udp->next); + break; default: udp->next = NULL; break; @@ -1810,6 +1923,150 @@ decode_ppp_lcp(uint8_t *buf, uint len, return PROTOCOL_SUCCESS; } +protocol_error_t +decode_l2tp(uint8_t *buf, uint len, + uint8_t *sp, uint sp_len, + bbl_l2tp_t **_l2tp) { + + protocol_error_t ret_val = UNKNOWN_PROTOCOL; + bbl_l2tp_t *l2tp; + + uint16_t l2tp_len = 0; + + if(len < 8 || sp_len < sizeof(bbl_l2tp_t)) { + return DECODE_ERROR; + } + + /* Init L2TP header */ + l2tp = (bbl_l2tp_t*)sp; BUMP_BUFFER(sp, sp_len, sizeof(bbl_l2tp_t)); + memset(l2tp, 0x0, sizeof(bbl_l2tp_t)); + + /* 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 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |T|L|x|x|S|x|O|P|x|x|x|x| Ver | Length (opt) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Tunnel ID | Session ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ns (opt) | Nr (opt) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Offset Size (opt) | Offset pad... (opt) + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ + + if(*buf & L2TP_HDR_CTRL_BIT_MASK) { + l2tp->type = L2TP_MESSAGE_ZLB; + } + + l2tp->with_length = *buf & L2TP_HDR_LEN_BIT_MASK; + l2tp->with_sequence = *buf & L2TP_HDR_SEQ_BIT_MASK; + l2tp->with_offset = *buf & L2TP_HDR_OFFSET_BIT_MASK; + l2tp->with_priority = *buf & L2TP_HDR_PRIORITY_BIT_MASK; + + BUMP_BUFFER(buf, len, sizeof(uint8_t)); + + if((*buf & L2TP_HDR_VERSION_MASK) != 2) { /* Check L2TP version */ + return DECODE_ERROR; + } + BUMP_BUFFER(buf, len, sizeof(uint8_t)); + + /* L2TP length */ + if(l2tp->with_length) { + l2tp->length = be16toh(*(uint16_t*)buf); + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + l2tp_len = l2tp->length - 4; + if(l2tp_len > len) { + return DECODE_ERROR; + } + len = l2tp_len; + } else if(l2tp->type) { + /* Length is mandatory for control packets */ + return DECODE_ERROR; + } + l2tp->tunnel_id = be16toh(*(uint16_t*)buf); + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + l2tp->session_id = be16toh(*(uint16_t*)buf); + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + + if(l2tp->with_sequence) { + if(len < 4) return DECODE_ERROR; + l2tp->ns = be16toh(*(uint16_t*)buf); + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + l2tp->nr = be16toh(*(uint16_t*)buf); + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + } else if(l2tp->type) { + /* Sequence is mandatory for control packets */ + return DECODE_ERROR; + } + + if(l2tp->with_offset) { + if(len < 2) return DECODE_ERROR; + l2tp->offset = be16toh(*(uint16_t*)buf); + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + if(l2tp->offset) { + if(len < l2tp->offset) return DECODE_ERROR; + /* Actually never seen a BNG sending offset + * different than zero... */ + BUMP_BUFFER(buf, len, l2tp->offset); + } + } + + if(l2tp->type) { + /* L2TP control packet */ + if(len) { + if(len < 8) return DECODE_ERROR; + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + if(*(uint32_t*)buf != 0) return DECODE_ERROR; + BUMP_BUFFER(buf, len, sizeof(uint32_t)); + l2tp->type = be16toh(*(uint16_t*)buf); + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + if(len) { + l2tp->payload = buf; + l2tp->payload_len = len; + } + } + ret_val = PROTOCOL_SUCCESS; + } else { + /* L2TP data packet */ + if(len < 4) return DECODE_ERROR; + + l2tp->payload = buf; + l2tp->payload_len = len; + + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + l2tp->protocol = be16toh(*(uint16_t*)buf); + BUMP_BUFFER(buf, len, sizeof(uint16_t)); + + /* Decode protocol */ + switch(l2tp->protocol) { + case PROTOCOL_IPV4: + ret_val = decode_ipv4(buf, len, sp, sp_len, (bbl_ipv4_t**)&l2tp->next); + break; + case PROTOCOL_IPV6: + ret_val = decode_ipv6(buf, len, sp, sp_len, (bbl_ipv6_t**)&l2tp->next); + break; + case PROTOCOL_LCP: + ret_val = decode_ppp_lcp(buf, len, sp, sp_len, (bbl_lcp_t**)&l2tp->next); + break; + case PROTOCOL_IPCP: + ret_val = decode_ppp_ipcp(buf, len, sp, sp_len, (bbl_ipcp_t**)&l2tp->next); + break; + case PROTOCOL_IP6CP: + ret_val = decode_ppp_ip6cp(buf, len, sp, sp_len, (bbl_ip6cp_t**)&l2tp->next); + break; + case PROTOCOL_PAP: + ret_val = decode_ppp_pap(buf, len, sp, sp_len, (bbl_pap_t**)&l2tp->next); + break; + case PROTOCOL_CHAP: + ret_val = decode_ppp_chap(buf, len, sp, sp_len, (bbl_chap_t**)&l2tp->next); + break; + default: + break; + } + } + *_l2tp = l2tp; + return ret_val; +} + /* * decode_pppoe_discovery * diff --git a/src/bbl_protocols.h b/src/bbl_protocols.h index 7cd850bb..0408d66b 100644 --- a/src/bbl_protocols.h +++ b/src/bbl_protocols.h @@ -133,6 +133,7 @@ #define UDP_PROTOCOL_DHCPV6 1 #define UDP_PROTOCOL_BBL 2 +#define UDP_PROTOCOL_L2TP 3 #define IPV6_NEXT_HEADER_UDP 17 #define IPV6_NEXT_HEADER_ICMPV6 58 @@ -153,6 +154,28 @@ #define DHCPV6_UDP_CLIENT 546 #define DHCPV6_UDP_SERVER 547 +#define L2TP_UDP_PORT 1701 +#define L2TP_HDR_VERSION_MASK 0x0f +#define L2TP_HDR_CTRL_BIT_MASK 0x80 +#define L2TP_HDR_LEN_BIT_MASK 0x40 +#define L2TP_HDR_SEQ_BIT_MASK 0x08 +#define L2TP_HDR_OFFSET_BIT_MASK 0x02 +#define L2TP_HDR_PRIORITY_BIT_MASK 0x01 +#define L2TP_HDR_LEN_MIN_WITH_LEN 8 +#define L2TP_AVP_M_BIT_SHIFT 15 +#define L2TP_AVP_H_BIT_SHIFT 14 +#define L2TP_AVP_LEN_MASK 0x03FF +#define L2TP_AVP_HDR_LEN 6 +#define L2TP_AVP_M_BIT_MASK 0x8000 +#define L2TP_AVP_H_BIT_MASK 0x4000 +#define L2TP_AVP_TYPE_LEN 2 +#define L2TP_AVP_TYPE_LEN 2 +#define L2TP_AVP_HIDDEN_FIXED_LEN 2 +#define L2TP_AVP_MAX_LEN 1024 + +#define L2TP_NH_TYPE_VALUE 18 + + #define MAX_VLANS 3 #define BUMP_BUFFER(_buf, _len, _size) \ @@ -212,6 +235,27 @@ typedef enum dhcpv6_message_type_ { DHCPV6_MESSAGE_MAX, } dhcpv6_message_type; +typedef enum l2tp_message_type_ { + L2TP_MESSAGE_DATA = 0, + L2TP_MESSAGE_SCCRQ = 1, + L2TP_MESSAGE_SCCRP = 2, + L2TP_MESSAGE_SCCCN = 3, + L2TP_MESSAGE_STOPCCN = 4, + L2TP_MESSAGE_HELLO = 6, + L2TP_MESSAGE_OCRQ = 7, + L2TP_MESSAGE_OCRP = 8, + L2TP_MESSAGE_OCCN = 9, + L2TP_MESSAGE_ICRQ = 10, + L2TP_MESSAGE_ICRP = 11, + L2TP_MESSAGE_ICCN = 12, + L2TP_MESSAGE_CDN = 14, + L2TP_MESSAGE_WEN = 15, + L2TP_MESSAGE_CSUN = 28, + L2TP_MESSAGE_CSURQ = 29, + L2TP_MESSAGE_ZLB = 32767, + L2TP_MESSAGE_MAX, +} l2tp_message_type; + typedef enum dhcpv6_option_code_ { DHCPV6_OPTION_CLIENTID = 1, DHCPV6_OPTION_SERVERID = 2, @@ -399,7 +443,7 @@ typedef struct bbl_ppp_chap_ { } bbl_chap_t; /* - * PPP IPv4 Structure + * IPv4 Structure */ typedef struct bbl_ipv4_ { uint32_t src; @@ -414,7 +458,7 @@ typedef struct bbl_ipv4_ { } bbl_ipv4_t; /* - * PPP IPv6 Structure + * IPv6 Structure */ typedef struct bbl_ipv6_ { uint8_t *src; @@ -428,7 +472,7 @@ typedef struct bbl_ipv6_ { } bbl_ipv6_t; /* - * PPP UDP Structure + * UDP Structure */ typedef struct bbl_udp_ { uint16_t src; @@ -440,7 +484,7 @@ typedef struct bbl_udp_ { } bbl_udp_t; /* - * PPP IGMP Structure + * IGMP Structure */ typedef struct bbl_igmp_group_record_ { uint8_t type; @@ -499,6 +543,24 @@ typedef struct bbl_dhcpv6_ { uint8_t ia_pd_option_len; } bbl_dhcpv6_t; +typedef struct bbl_l2tp_ { + bool with_length; // L Bit + bool with_sequence; // S Bit + bool with_offset; // O Bit + bool with_priority; // P Bit + uint16_t type; + uint16_t length; + uint16_t tunnel_id; + uint16_t session_id; + uint16_t ns; + uint16_t nr; + uint16_t offset; + uint16_t protocol; + void *next; // next header + void *payload; // l2tp payload + uint16_t payload_len; // l2tp payload length +} bbl_l2tp_t; + typedef struct bbl_bbl_ { uint8_t type; uint8_t sub_type; diff --git a/src/bbl_rx.c b/src/bbl_rx.c index d5c57b8f..67f3c07d 100644 --- a/src/bbl_rx.c +++ b/src/bbl_rx.c @@ -1731,6 +1731,8 @@ bbl_rx_handler_access(bbl_ethernet_header_t *eth, bbl_interface_s *interface) { void bbl_rx_network_arp(bbl_ethernet_header_t *eth, bbl_interface_s *interface) { + bbl_secondary_ip_s *secondary_ip; + bbl_arp_t *arp = (bbl_arp_t*)eth->next; if(arp->sender_ip == interface->gateway) { interface->arp_resolved = true; @@ -1738,7 +1740,20 @@ bbl_rx_network_arp(bbl_ethernet_header_t *eth, bbl_interface_s *interface) { memcpy(interface->gateway_mac, arp->sender, ETH_ADDR_LEN); } if(arp->code == ARP_REQUEST) { - interface->send_requests |= BBL_IF_SEND_ARP_REPLY; + if(arp->target_ip == interface->ip) { + interface->arp_reply_ip = interface->ip; + interface->send_requests |= BBL_IF_SEND_ARP_REPLY; + } else { + secondary_ip = interface->ctx->config.secondary_ip_addresses; + while(secondary_ip) { + if(arp->target_ip == secondary_ip->ip) { + secondary_ip->arp_reply = true; + interface->send_requests |= BBL_IF_SEND_SEC_ARP_REPLY; + return; + } + secondary_ip = secondary_ip->next; + } + } } } } @@ -1765,6 +1780,8 @@ bbl_rx_network_icmpv6(bbl_ethernet_header_t *eth, bbl_interface_s *interface) { } } + + void bbl_rx_handler_network(bbl_ethernet_header_t *eth, bbl_interface_s *interface) { @@ -1794,6 +1811,8 @@ bbl_rx_handler_network(bbl_ethernet_header_t *eth, bbl_interface_s *interface) { udp = (bbl_udp_t*)ipv4->next; if(udp->protocol == UDP_PROTOCOL_BBL) { bbl = (bbl_bbl_t*)udp->next; + } else if(udp->protocol == UDP_PROTOCOL_L2TP) { + return bbl_l2tp_handler_rx(eth, (bbl_l2tp_t*)udp->next, interface); } } break; diff --git a/src/bbl_timer.c b/src/bbl_timer.c index 4f42307d..cbad3312 100644 --- a/src/bbl_timer.c +++ b/src/bbl_timer.c @@ -365,17 +365,16 @@ timer_add (timer_root_s *root, */ if (timer) { timer_requeue(timer, sec, nsec); - - /* - * Update data and cb if there was a change. - * Do the reformatting of name only during a change due to snprintf() being expensive. - */ - if (timer->data != data || timer->cb != cb) { - snprintf(timer->name, sizeof(timer->name), "%s", name); - timer->data = data; - timer->cb = cb; - } - return; + /* + * Update data and cb if there was a change. + * Do the reformatting of name only during a change due to snprintf() being expensive. + */ + if (timer->data != data || timer->cb != cb) { + snprintf(timer->name, sizeof(timer->name), "%s", name); + timer->data = data; + timer->cb = cb; + } + return; } if (CIRCLEQ_EMPTY(&root->timer_gc_qhead)) { @@ -392,7 +391,7 @@ timer_add (timer_root_s *root, timer = CIRCLEQ_FIRST(&root->timer_gc_qhead); CIRCLEQ_REMOVE(&root->timer_gc_qhead, timer, timer_qnode); root->gc--; - memset(timer, 0, sizeof(timer_s)); + memset(timer, 0, sizeof(timer_s)); } if (!timer) { diff --git a/src/bbl_tx.c b/src/bbl_tx.c index 41308d78..3baa7510 100644 --- a/src/bbl_tx.c +++ b/src/bbl_tx.c @@ -1263,6 +1263,8 @@ bbl_encode_interface_packet (bbl_interface_s *interface, u_char *frame_ptr) uint len = 0; uint8_t *buf = frame_ptr + TPACKET2_HDRLEN - sizeof(struct sockaddr_ll); + bbl_secondary_ip_s *secondary_ip; + eth.src = interface->mac; eth.vlan_outer = interface->ctx->config.network_vlan; if(interface->send_requests & BBL_IF_SEND_ARP_REQUEST) { @@ -1284,12 +1286,34 @@ bbl_encode_interface_packet (bbl_interface_s *interface, u_char *frame_ptr) eth.dst = interface->gateway_mac; eth.type = ETH_TYPE_ARP; eth.next = &arp; - arp.code = ARP_REQUEST; + arp.code = ARP_REPLY; arp.sender = interface->mac; - arp.sender_ip = interface->ip; + arp.sender_ip = interface->arp_reply_ip; arp.target = interface->gateway_mac; arp.target_ip = interface->gateway; result = encode_ethernet(buf, &len, ð); + } else if(interface->send_requests & BBL_IF_SEND_SEC_ARP_REPLY) { + secondary_ip = interface->ctx->config.secondary_ip_addresses; + while(secondary_ip) { + if(secondary_ip->arp_reply) { + secondary_ip->arp_reply = false; + eth.dst = interface->gateway_mac; + eth.type = ETH_TYPE_ARP; + eth.next = &arp; + arp.code = ARP_REPLY; + arp.sender = interface->mac; + arp.sender_ip = secondary_ip->ip; + arp.target = interface->gateway_mac; + arp.target_ip = interface->gateway; + result = encode_ethernet(buf, &len, ð); + break; + } + secondary_ip = secondary_ip->next; + } + if(!secondary_ip) { + /* Stop if we reach end of secondary IP address list */ + interface->send_requests &= ~BBL_IF_SEND_SEC_ARP_REPLY; + } } else if(interface->send_requests & BBL_IF_SEND_ICMPV6_NS) { interface->send_requests &= ~BBL_IF_SEND_ICMPV6_NS; if(*(uint32_t*)interface->gateway_mac == 0) { @@ -1361,6 +1385,7 @@ bbl_tx_job (timer_s *timer) bbl_ctx_s *ctx; bbl_interface_s *interface; bbl_session_s *session; + bbl_l2tp_queue_t *q; struct tpacket2_hdr* tphdr; u_char *frame_ptr; struct pollfd fds[1] = {0}; @@ -1400,7 +1425,7 @@ bbl_tx_job (timer_s *timer) /* Check if this slot available for writing. */ if (tphdr->tp_status != TP_STATUS_AVAILABLE) { interface->stats.no_tx_buffer++; - break; + goto Send; } /* Encode the packet straight into the mmapped send buffer. */ if(bbl_encode_interface_packet(interface, frame_ptr)){ @@ -1424,7 +1449,7 @@ bbl_tx_job (timer_s *timer) /* Check if this slot available for writing. */ if (tphdr->tp_status != TP_STATUS_AVAILABLE) { interface->stats.no_tx_buffer++; - break; + goto Send; } /* Encode the packet straight into the mmapped send buffer. */ encode_success = false; @@ -1473,6 +1498,34 @@ bbl_tx_job (timer_s *timer) /* Network Interface Only! */ if(!interface->access) { + /* Send L2TP Packets */ + while (!CIRCLEQ_EMPTY(&interface->l2tp_tx_qhead)) { + /* Check if this slot available for writing. */ + if (tphdr->tp_status != TP_STATUS_AVAILABLE) { + interface->stats.no_tx_buffer++; + goto Send; + } + /* Pop element from queue */ + q = CIRCLEQ_FIRST(&interface->l2tp_tx_qhead); + CIRCLEQ_REMOVE(&interface->l2tp_tx_qhead, q, tx_qnode); + CIRCLEQ_NEXT(q, tx_qnode) = NULL; + CIRCLEQ_PREV(q, tx_qnode) = NULL; + /* Copy packet from queue to ring buffer */ + frame_ptr = interface->ring_tx + (interface->cursor_tx * interface->req_tx.tp_frame_size); + tphdr = (struct tpacket2_hdr *)frame_ptr; + memcpy(frame_ptr + TPACKET2_HDRLEN - sizeof(struct sockaddr_ll) , q->packet, q->packet_len); + tphdr->tp_len = interface->mc_packet_len; + tphdr->tp_status = TP_STATUS_SEND_REQUEST; + interface->stats.packets_tx++; + interface->cursor_tx = (interface->cursor_tx + 1) % interface->req_tx.tp_frame_nr; + /* Captrue packet */ + if (ctx->pcap.write_buf) { + pcapng_push_packet_header(ctx, &interface->tx_timestamp, + frame_ptr + TPACKET2_HDRLEN - sizeof(struct sockaddr_ll), + tphdr->tp_len, interface->pcap_index, PCAPNG_EPB_FLAGS_OUTBOUND); + } + } + /* Generate Multicast Traffic */ g = ctx->config.igmp_group_count; if(ctx->config.send_multicast_traffic && ctx->multicast_traffic && g) { @@ -1483,8 +1536,7 @@ bbl_tx_job (timer_s *timer) /* Check if this slot available for writing. */ if (tphdr->tp_status != TP_STATUS_AVAILABLE) { interface->stats.no_tx_buffer++; - interface->mc_packet_seq--; - break; + goto Send; } if(bbl_encode_multicast_packet(interface, i, frame_ptr)) { @@ -1502,6 +1554,7 @@ bbl_tx_job (timer_s *timer) } } +Send: pcapng_fflush(ctx); /* Notify kernel. */ From b932786a6f0bd3fa564c6861a780ff8a5bdd9e0a Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Thu, 18 Feb 2021 18:53:44 +0100 Subject: [PATCH 02/27] #3 L2TPv2 LNS Support (WIP) - 2 --- src/bbl.c | 15 ++- src/bbl.h | 8 ++ src/bbl_config.c | 12 ++ src/bbl_l2tp.c | 304 +++++++++++++++++++++++++++++++++++++------- src/bbl_l2tp.h | 53 ++++---- src/bbl_l2tp_avp.c | 55 ++++---- src/bbl_protocols.c | 10 +- src/bbl_tx.c | 11 +- 8 files changed, 360 insertions(+), 108 deletions(-) diff --git a/src/bbl.c b/src/bbl.c index 39eea2d8..3795b830 100644 --- a/src/bbl.c +++ b/src/bbl.c @@ -949,13 +949,24 @@ bbl_ctrl_job (timer_s *timer) if(ctx->sessions) { if(ctx->sessions_terminated >= ctx->sessions) { - CIRCLEQ_INIT(&ctx->timer_root.timer_bucket_qhead); + /* Now also close all L2TP tunnels ... */ + if(bbl_l2tp_tunnel_count(ctx) == 0) { + /* Stop event loop to close application! */ + CIRCLEQ_INIT(&ctx->timer_root.timer_bucket_qhead); + } else { + bbl_l2tp_stop_all_tunnel(ctx); + } return; } } else { /* Network interface only... */ if(g_teardown) { - CIRCLEQ_INIT(&ctx->timer_root.timer_bucket_qhead); + if(bbl_l2tp_tunnel_count(ctx) == 0) { + /* Stop event loop to close application! */ + CIRCLEQ_INIT(&ctx->timer_root.timer_bucket_qhead); + } else { + bbl_l2tp_stop_all_tunnel(ctx); + } return; } return; diff --git a/src/bbl.h b/src/bbl.h index 64b17184..154d5ec4 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -260,6 +260,14 @@ typedef struct bbl_interface_ uint64_t session_ipv4_wrong_session; uint64_t session_ipv6_wrong_session; uint64_t session_ipv6pd_wrong_session; + + uint32_t l2tp_control_rx; + uint32_t l2tp_control_rx_dup; + uint32_t l2tp_control_rx_ooo; + uint32_t l2tp_control_tx; + uint32_t l2tp_control_retry; + uint64_t l2tp_data_rx; + uint64_t l2tp_data_tx; } stats; struct timer_ *tx_job; diff --git a/src/bbl_config.c b/src/bbl_config.c index 1978f0f7..74f2faad 100644 --- a/src/bbl_config.c +++ b/src/bbl_config.c @@ -701,6 +701,18 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { } else { fprintf(stderr, "JSON config error: Missing value for l2tp-server->address\n"); } + value = json_object_get(sub, "receive-window-size"); + if (json_is_number(value)) { + l2tp_server->receive_window = json_number_value(value); + } else { + l2tp_server->receive_window = 4; + } + value = json_object_get(sub, "max-retry"); + if (json_is_number(value)) { + l2tp_server->max_retry = json_number_value(value); + } else { + l2tp_server->max_retry = 30; + } } } else if (json_is_object(sub)) { fprintf(stderr, "JSON config error: List expected in L2TP server configuration but dictionary found\n"); diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 7e51ffb3..375997dd 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -11,6 +11,9 @@ #include #include +static void +bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, l2tp_message_type l2tp_type); + const char* l2tp_message_string(l2tp_message_type type) { @@ -46,11 +49,17 @@ l2tp_message_string(l2tp_message_type type) */ void bbl_l2tp_session_delete(bbl_l2tp_session_t *l2tp_session) { + bbl_ctx_s *ctx; + if(l2tp_session) { + ctx = l2tp_session->tunnel->interface->ctx; /* Remove session from tunnel object */ if(CIRCLEQ_NEXT(l2tp_session, session_qnode) != NULL) { CIRCLEQ_REMOVE(&l2tp_session->tunnel->session_qhead, l2tp_session, session_qnode); + CIRCLEQ_NEXT(l2tp_session, session_qnode) = NULL; } + /* Remove session from dict */ + dict_remove(ctx->l2tp_session_dict, &l2tp_session->key); /* Free tunnel memory */ if(l2tp_session->proxy_auth_name) free(l2tp_session->proxy_auth_name); if(l2tp_session->proxy_auth_challenge) free(l2tp_session->proxy_auth_challenge); @@ -73,9 +82,17 @@ bbl_l2tp_session_delete(bbl_l2tp_session_t *l2tp_session) { void bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_t *l2tp_tunnel) { bbl_l2tp_queue_t *q = NULL; - bbl_interface_s *interface = l2tp_tunnel->server->interface; + bbl_interface_s *interface = l2tp_tunnel->interface; if(l2tp_tunnel) { + LOG(L2TP, "L2TP DEBUG (%s) Tunnel (%u) from %s (%s) deleted\n", + l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, + l2tp_tunnel->peer_name, + format_ipv4_address(&l2tp_tunnel->peer_ip)); + + /* Delete timer */ + timer_del(l2tp_tunnel->timer_tx); + timer_del(l2tp_tunnel->timer_ctrl); /* Delete all remaining sessions */ while (!CIRCLEQ_EMPTY(&l2tp_tunnel->session_qhead)) { bbl_l2tp_session_delete(CIRCLEQ_FIRST(&l2tp_tunnel->session_qhead)); @@ -83,17 +100,20 @@ bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_t *l2tp_tunnel) { /* Remove tunnel from server object */ if(CIRCLEQ_NEXT(l2tp_tunnel, tunnel_qnode) != NULL) { CIRCLEQ_REMOVE(&l2tp_tunnel->server->tunnel_qhead, l2tp_tunnel, tunnel_qnode); + CIRCLEQ_NEXT(l2tp_tunnel, tunnel_qnode) = NULL; } /* Cleanup send queues */ while (!CIRCLEQ_EMPTY(&l2tp_tunnel->txq_qhead)) { q = CIRCLEQ_FIRST(&l2tp_tunnel->txq_qhead); + CIRCLEQ_REMOVE(&l2tp_tunnel->txq_qhead, q, txq_qnode); + CIRCLEQ_NEXT(q, txq_qnode) = NULL; if(CIRCLEQ_NEXT(q, tx_qnode) != NULL) { CIRCLEQ_REMOVE(&interface->l2tp_tx_qhead, q, tx_qnode); + CIRCLEQ_NEXT(q, tx_qnode) = NULL; } free(q->packet); free(q); } - if(l2tp_tunnel->zlb_qnode) { free(l2tp_tunnel->zlb_qnode->packet); free(l2tp_tunnel->zlb_qnode); @@ -117,7 +137,7 @@ bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_t *l2tp_tunnel) { void bbl_l2tp_tunnel_tx_job (timer_s *timer) { bbl_l2tp_tunnel_t *l2tp_tunnel = timer->data; - bbl_interface_s *interface = l2tp_tunnel->server->interface; + bbl_interface_s *interface = l2tp_tunnel->interface; bbl_l2tp_queue_t *q = NULL; bbl_l2tp_queue_t *q_del = NULL; @@ -132,6 +152,13 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { return; } + if(l2tp_tunnel->state == BBL_L2TP_TUNNEL_SEND_STOPCCN) { + if(!CIRCLEQ_EMPTY(&l2tp_tunnel->txq_qhead)) { + l2tp_tunnel->state = BBL_L2TP_TUNNEL_TERMINATED; + l2tp_tunnel->state_seconds = 0; + } + } + clock_gettime(CLOCK_REALTIME, ×tamp); q = CIRCLEQ_FIRST(&l2tp_tunnel->txq_qhead); @@ -143,12 +170,14 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { CIRCLEQ_REMOVE(&l2tp_tunnel->txq_qhead, q_del, txq_qnode); free(q_del->packet); free(q_del); + continue; } if (L2TP_SEQ_LT(q->ns, max_ns)) { if(q->last_tx_time.tv_sec) { timespec_sub(&time_diff, ×tamp, &q->last_tx_time); time_diff_ms = round(time_diff.tv_nsec / 1.0e6) * (time_diff.tv_sec * 1000); if(time_diff_ms < 1000) { + q = CIRCLEQ_NEXT(q, txq_qnode); continue; } } @@ -158,12 +187,27 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { q->last_tx_time.tv_nsec = timestamp.tv_nsec; /* Update Nr. ... */ *(uint16_t*)(q->packet + q->nr_offset) = htobe16(l2tp_tunnel->nr); + if(q->retries) { + l2tp_tunnel->stats.control_retry++; + interface->stats.l2tp_control_retry++; + if(q->retries > l2tp_tunnel->server->max_retry) { + l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; + l2tp_tunnel->state_seconds = 0; + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); + } + } q->retries++; + q = CIRCLEQ_NEXT(q, txq_qnode); } else { break; } } if(l2tp_tunnel->zlb) { + l2tp_tunnel->zlb = false; + /* Update Ns. ... */ + *(uint16_t*)(q->packet + q->nr_offset) = htobe16(l2tp_tunnel->ns); + /* Update Nr. ... */ + *(uint16_t*)(q->packet + q->nr_offset) = htobe16(l2tp_tunnel->nr); CIRCLEQ_INSERT_TAIL(&interface->l2tp_tx_qhead, l2tp_tunnel->zlb_qnode, tx_qnode); *(uint16_t*)(l2tp_tunnel->zlb_qnode->packet + l2tp_tunnel->zlb_qnode->ns_offset) = htobe16(l2tp_tunnel->ns); *(uint16_t*)(l2tp_tunnel->zlb_qnode->packet + l2tp_tunnel->zlb_qnode->nr_offset) = htobe16(l2tp_tunnel->nr); @@ -178,15 +222,44 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { void bbl_l2tp_tunnel_control_job (timer_s *timer) { bbl_l2tp_tunnel_t *l2tp_tunnel = timer->data; - bbl_interface_s *interface = l2tp_tunnel->server->interface; + bbl_interface_s *interface = l2tp_tunnel->interface; bbl_ctx_s *ctx = interface->ctx; - + l2tp_tunnel->state_seconds++; + switch(l2tp_tunnel->state) { + case BBL_L2TP_TUNNEL_WAIT_CTR_CONN: + if(l2tp_tunnel->state_seconds > 30) { + l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; + l2tp_tunnel->state_seconds = 0; + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); + } + break; + case BBL_L2TP_TUNNEL_ESTABLISHED: + if(l2tp_tunnel->server->hello_interval) { + if(l2tp_tunnel->state_seconds % l2tp_tunnel->server->hello_interval == 0) { + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_HELLO); + } + } + break; + case BBL_L2TP_TUNNEL_SEND_STOPCCN: + case BBL_L2TP_TUNNEL_RCVD_STOPCCN: + if(l2tp_tunnel->state_seconds > 5) { + l2tp_tunnel->state = BBL_L2TP_TUNNEL_TERMINATED; + l2tp_tunnel->state_seconds = 0; + } + break; + case BBL_L2TP_TUNNEL_TERMINATED: + timer->periodic = false; + return bbl_l2tp_tunnel_delete(l2tp_tunnel); + default: + break; + } if(!l2tp_tunnel->timer_tx_active) { timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, 10 * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); l2tp_tunnel->timer_tx_active = true; } } + /** * bbl_l2tp_send * @@ -200,7 +273,7 @@ bbl_l2tp_tunnel_control_job (timer_s *timer) { static void bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, l2tp_message_type l2tp_type) { - bbl_interface_s *interface = l2tp_tunnel->server->interface; + bbl_interface_s *interface = l2tp_tunnel->interface; bbl_ctx_s *ctx = interface->ctx; bbl_l2tp_queue_t *q = calloc(1, sizeof(bbl_l2tp_queue_t)); @@ -211,8 +284,8 @@ bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, bbl_l2tp_t l2tp = {0}; uint8_t sp[L2TP_MAX_AVP_SIZE]; /* scratchpad memory to craft the AVP attributes */ - uint16_t sp_len; - uint len; + uint16_t sp_len = 0; + uint len = 0; eth.dst = interface->gateway_mac; eth.src = interface->mac; @@ -229,8 +302,10 @@ bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, udp.protocol = UDP_PROTOCOL_L2TP; udp.next = &l2tp; l2tp.type = l2tp_type; - l2tp.ns = l2tp_tunnel->ns++; - + l2tp.tunnel_id = l2tp_tunnel->peer_tunnel_id; + if(l2tp_session) { + l2tp.session_id = l2tp_session->peer_session_id; + } /* The Nr. will be set on the fly while sending * using the latest received Ns. from peer. * Therfore we need to remember offset to Nr. */ @@ -243,10 +318,19 @@ bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, q->ns_offset = 50; q->nr_offset = 52; } + if(l2tp_type != L2TP_MESSAGE_ZLB) { + if(l2tp_tunnel->initial_packet_send) { + l2tp_tunnel->ns++; + } else{ + l2tp_tunnel->initial_packet_send = true; + } + l2tp.ns = l2tp_tunnel->ns; + bbl_l2tp_avp_encode_attributes(l2tp_tunnel, l2tp_session, l2tp_type, sp, &sp_len); + l2tp.payload = sp; + l2tp.payload_len = sp_len; + } q->ns = l2tp.ns; q->tunnel = l2tp_tunnel; - - bbl_l2tp_avp_encode_attributes(l2tp_tunnel, l2tp_session, l2tp_type, sp, &sp_len); q->packet = malloc(L2TP_MAX_PACKET_SIZE); if(encode_ethernet(q->packet, &len, ð) == PROTOCOL_SUCCESS) { q->packet_len = len; @@ -266,7 +350,7 @@ bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, } } else { /* Encode error.... */ - LOG(ERROR, "L2TP Encode Error\n"); + LOG(ERROR, "L2TP Encode Error!\n"); free(q->packet); free(q); } @@ -284,28 +368,40 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s bbl_l2tp_tunnel_t *l2tp_tunnel2; bbl_l2tp_session_t *l2tp_session; - l2tp_key_t key; - dict_insert_result result; - void **search; + l2tp_key_t key = {0}; + dict_insert_result result = {0}; + void **search = NULL; uint8_t l2tp_type; while(l2tp_server) { if(l2tp_server->ip == ipv4->dst) { - LOG(L2TP, "L2TP Info (%s) SCCRQ received from %s\n", - l2tp_server->host_name, format_ipv4_address(&ipv4->src)); - + LOG(DEBUG, "L2TP Debug (%s) SCCRQ received from %s\n", + l2tp_server->host_name, + format_ipv4_address(&ipv4->src)); + /* Init tunnel ... */ l2tp_tunnel = calloc(1, sizeof(bbl_l2tp_tunnel_t)); - l2tp_tunnel->peer_ip = ipv4->src; + CIRCLEQ_INIT(&l2tp_tunnel->txq_qhead); + CIRCLEQ_INIT(&l2tp_tunnel->session_qhead); + l2tp_tunnel->interface = interface; l2tp_tunnel->server = l2tp_server; + l2tp_tunnel->peer_receive_window = 4; + l2tp_tunnel->ssthresh = 4; + l2tp_tunnel->cwnd = 1; + l2tp_tunnel->peer_tunnel_id = 1; + l2tp_tunnel->peer_ip = ipv4->src; + l2tp_tunnel->peer_ns = l2tp->ns; + l2tp_tunnel->nr = (l2tp->ns + 1); l2tp_tunnel->state = BBL_L2TP_TUNNEL_WAIT_CTR_CONN; + /* Decode received attributes and store in tunnel */ if(!bbl_l2tp_avp_decode_tunnel(l2tp, l2tp_tunnel)) { return bbl_l2tp_tunnel_delete(l2tp_tunnel); } if(!l2tp_tunnel->peer_tunnel_id || !l2tp_tunnel->peer_name) { - LOG(L2TP, "L2TP Error (%s) Invalid SCCRQ received from %s\n", - l2tp_server->host_name, format_ipv4_address(&ipv4->src)); + LOG(ERROR, "L2TP Error (%s) Invalid SCCRQ received from %s\n", + l2tp_server->host_name, + format_ipv4_address(&ipv4->src)); return bbl_l2tp_tunnel_delete(l2tp_tunnel); } @@ -323,6 +419,7 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s key.ip = l2tp_server->ip; key.tunnel_id = l2tp_server->next_tunnel_id++; key.session_id = 0; + if(key.tunnel_id == 0) continue; /* skip tunnel 0 */ search = dict_search(ctx->l2tp_session_dict, &key); if(search) { /* Used, try next ... */ @@ -332,6 +429,8 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s } } l2tp_tunnel->tunnel_id = key.tunnel_id; + l2tp_tunnel->stats.control_rx++; + interface->stats.l2tp_control_rx++; /* Add dummy tunnel session, this session is only used * to search for tunnel using the same dictionary. */ @@ -340,16 +439,14 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s l2tp_session->tunnel = l2tp_tunnel; l2tp_session->key.ip = key.ip; l2tp_session->key.tunnel_id = key.tunnel_id; - result = dict_insert(ctx->l2tp_session_dict, &key); + result = dict_insert(ctx->l2tp_session_dict, &l2tp_session->key); if (!result.inserted) { - /* TODO: Here we need to handle this properly but actually - * this should not happen. */ + LOG(ERROR, "L2TP Error (%s) Failed to add tunnel session\n", + l2tp_tunnel->server->host_name); free(l2tp_session); - return; + return bbl_l2tp_tunnel_delete(l2tp_tunnel); } *result.datum_ptr = l2tp_session; - CIRCLEQ_INIT(&l2tp_tunnel->txq_qhead); - CIRCLEQ_INIT(&l2tp_tunnel->session_qhead); CIRCLEQ_INSERT_TAIL(&l2tp_tunnel->session_qhead, l2tp_session, session_qnode); /* L2TP Challenge/Response */ @@ -369,20 +466,28 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s } else { /* We are not able to setup a session if no challenge * is received but there is a secret configured! */ + LOG(ERROR, "L2TP Error (%s) Missing challenge in SCCRQ from %s\n", + l2tp_tunnel->server->host_name, + format_ipv4_address(&l2tp_tunnel->peer_ip)); l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; } } else { if(l2tp_tunnel->peer_challenge_len) { /* We are not able to setup a session if challenge * is received but not secret configured! */ + LOG(ERROR, "L2TP Error (%s) No secret found but challenge received in SCCRQ from %s\n", + l2tp_tunnel->server->host_name, + format_ipv4_address(&l2tp_tunnel->peer_ip)); l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; } } /* Add tunnel to server */ CIRCLEQ_INSERT_TAIL(&l2tp_server->tunnel_qhead, l2tp_tunnel, tunnel_qnode); - /* Start control job */ + /* Start control job + * WARNING: Do not change the interval! */ timer_add_periodic(&ctx->timer_root, &l2tp_tunnel->timer_ctrl, "L2TP Control", 1, 0, l2tp_tunnel, bbl_l2tp_tunnel_control_job); - + /* Prepare ZLB */ + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_ZLB); /* Send response */ if(l2tp_tunnel->state == BBL_L2TP_TUNNEL_SEND_STOPCCN) { bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); @@ -398,15 +503,54 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s static void bbl_l2tp_scccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_tunnel_t *l2tp_tunnel) { bbl_ctx_s *ctx = interface->ctx; - + + uint8_t digest[L2TP_MD5_DIGEST_LEN]; + MD5_CTX md5_ctx; + uint8_t l2tp_type = L2TP_MESSAGE_SCCCN; + UNUSED(ctx); UNUSED(eth); - UNUSED(l2tp); if(l2tp_tunnel->state == BBL_L2TP_TUNNEL_WAIT_CTR_CONN) { + if(!bbl_l2tp_avp_decode_tunnel(l2tp, l2tp_tunnel)) { + LOG(ERROR, "L2TP Error (%s) Invalid SCCCN received from %s\n", + l2tp_tunnel->server->host_name, + format_ipv4_address(&l2tp_tunnel->peer_ip)); + l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; + l2tp_tunnel->state_seconds = 0; + return bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); + } + /* Check challenge response ... */ + if(l2tp_tunnel->server->secret) { + if(l2tp_tunnel->peer_challenge_response_len) { + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, &l2tp_type, 1); + MD5_Update(&md5_ctx, (unsigned char *) l2tp_tunnel->server->secret, strlen(l2tp_tunnel->server->secret)); + MD5_Update(&md5_ctx, l2tp_tunnel->challenge, l2tp_tunnel->challenge_len); + MD5_Final(digest, &md5_ctx); + if (memcmp(digest, l2tp_tunnel->peer_challenge_response, L2TP_MD5_DIGEST_LEN) != 0) { + LOG(ERROR, "L2TP Error (%s) Wrong challenge response in SCCCN from %s\n", + l2tp_tunnel->server->host_name, + format_ipv4_address(&l2tp_tunnel->peer_ip)); + l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; + l2tp_tunnel->state_seconds = 0; + return bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); + } + } else { + LOG(ERROR, "L2TP Error (%s) Missing challenge response in SCCCN from %s\n", + l2tp_tunnel->server->host_name, + format_ipv4_address(&l2tp_tunnel->peer_ip)); + l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; + l2tp_tunnel->state_seconds = 0; + return bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); + } + } l2tp_tunnel->state = BBL_L2TP_TUNNEL_ESTABLISHED; - LOG(L2TP, "L2TP Info (%s) Tunnel from %s (%s) estbalished\n", - l2tp_tunnel->server->host_name, l2tp_tunnel->peer_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); + l2tp_tunnel->state_seconds = 0; + LOG(L2TP, "L2TP Info (%s) Tunnel (%u) from %s (%s) estbalished\n", + l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, + l2tp_tunnel->peer_name, + format_ipv4_address(&l2tp_tunnel->peer_ip)); } } @@ -418,7 +562,8 @@ bbl_l2tp_stopccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ UNUSED(eth); UNUSED(l2tp); - l2tp_tunnel->state = BBL_L2TP_TUNNEL_TERMINATED; + l2tp_tunnel->state = BBL_L2TP_TUNNEL_RCVD_STOPCCN; + l2tp_tunnel->state_seconds = 0; } static void @@ -429,13 +574,15 @@ bbl_l2tp_icrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * dict_insert_result result; void **search; - bbl_l2tp_session_t *l2tp_session = calloc(1, sizeof(bbl_l2tp_session_t)); - UNUSED(eth); - UNUSED(l2tp); + if(l2tp_tunnel->state != BBL_L2TP_TUNNEL_ESTABLISHED) { + return; + } + + bbl_l2tp_session_t *l2tp_session = calloc(1, sizeof(bbl_l2tp_session_t)); l2tp_session->tunnel = l2tp_tunnel; - l2tp_tunnel->state = BBL_L2TP_SESSION_WAIT_CONN; + l2tp_session->state = BBL_L2TP_SESSION_WAIT_CONN; if(!bbl_l2tp_avp_decode_session(l2tp, l2tp_tunnel, l2tp_session)) { return bbl_l2tp_session_delete(l2tp_session); @@ -474,25 +621,44 @@ bbl_l2tp_icrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * static void bbl_l2tp_iccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_session_t *l2tp_session) { bbl_ctx_s *ctx = interface->ctx; + bbl_l2tp_tunnel_t *l2tp_tunnel = l2tp_session->tunnel; UNUSED(ctx); UNUSED(eth); UNUSED(l2tp); + if(!bbl_l2tp_avp_decode_session(l2tp, l2tp_tunnel, l2tp_session)) { + bbl_l2tp_send(l2tp_tunnel, l2tp_session, L2TP_MESSAGE_CDN); + return bbl_l2tp_session_delete(l2tp_session); + } if(l2tp_session->state == BBL_L2TP_SESSION_WAIT_CONN) { l2tp_session->state = BBL_L2TP_SESSION_ESTABLISHED; + LOG(L2TP, "L2TP Info (%s) Tunnel (%u) from %s (%s) session (%u) estbalished\n", + l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, + l2tp_tunnel->peer_name, + format_ipv4_address(&l2tp_tunnel->peer_ip), + l2tp_session->key.session_id); } } static void bbl_l2tp_cdn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_session_t *l2tp_session) { bbl_ctx_s *ctx = interface->ctx; + bbl_l2tp_tunnel_t *l2tp_tunnel = l2tp_session->tunnel; UNUSED(ctx); UNUSED(eth); UNUSED(l2tp); + bbl_l2tp_avp_decode_session(l2tp, l2tp_tunnel, l2tp_session); + l2tp_session->state = BBL_L2TP_SESSION_TERMINATED; + LOG(L2TP, "L2TP Info (%s) Tunnel (%u) from %s (%s) session (%u) terminated\n", + l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, + l2tp_tunnel->peer_name, + format_ipv4_address(&l2tp_tunnel->peer_ip), + l2tp_session->key.session_id); + bbl_l2tp_session_delete(l2tp_session); } @@ -523,24 +689,27 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ bbl_l2tp_session_t *l2tp_session; bbl_l2tp_tunnel_t *l2tp_tunnel; - l2tp_key_t key; - void **search; + l2tp_key_t key = {0}; + void **search = NULL; if(l2tp->type == L2TP_MESSAGE_SCCRQ) { return bbl_l2tp_sccrq_rx(eth, l2tp, interface); } - key.ip = ipv4->src; + LOG(DEBUG, "L2TP Debug %s received from %s\n", l2tp_message_string(l2tp->type), format_ipv4_address(&ipv4->src)); + + key.ip = ipv4->dst; key.tunnel_id = l2tp->tunnel_id; key.session_id = l2tp->session_id; - search = dict_search(interface->ctx->l2tp_session_dict, &key); + search = dict_search(ctx->l2tp_session_dict, &key); if(search) { l2tp_session = *search; l2tp_tunnel = l2tp_session->tunnel; if(l2tp->type == L2TP_MESSAGE_DATA) { l2tp_tunnel->stats.data_rx++; + interface->stats.l2tp_data_rx++; return bbl_l2tp_data_rx(eth, l2tp, interface, l2tp_session); } if (L2TP_SEQ_GT(l2tp->nr, l2tp_tunnel->peer_nr)) { @@ -548,10 +717,19 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ } if (l2tp_tunnel->nr == l2tp->ns) { /* In-Order packet received */ + LOG(DEBUG, "L2TP Debug (%s) %s received from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&ipv4->src)); + + /* Update tunnel */ l2tp_tunnel->peer_ns = l2tp->ns; l2tp_tunnel->nr = (l2tp->ns + 1); - l2tp_tunnel->zlb = true; + if(l2tp->type != L2TP_MESSAGE_ZLB) { + l2tp_tunnel->zlb = true; + } l2tp_tunnel->stats.control_rx++; + interface->stats.l2tp_control_rx++; if(!l2tp_tunnel->timer_tx_active) { timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, 10 * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); l2tp_tunnel->timer_tx_active = true; @@ -575,16 +753,52 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ } else { if (L2TP_SEQ_LT(l2tp->ns, l2tp_tunnel->nr)) { /* Duplicate packet received */ + LOG(DEBUG, "L2TP Debug duplicate received\n"); l2tp_tunnel->zlb = true; l2tp_tunnel->stats.control_rx_dup++; + interface->stats.l2tp_control_rx_dup++; if(!l2tp_tunnel->timer_tx_active) { timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, 10 * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); l2tp_tunnel->timer_tx_active = true; } } else { /* Out-of-Order packet received */ + LOG(DEBUG, "L2TP Debug out of order received\n"); l2tp_tunnel->stats.control_rx_ooo++; + interface->stats.l2tp_control_rx_ooo++; + } + } + } +} + +void +bbl_l2tp_stop_all_tunnel(bbl_ctx_s *ctx) { + bbl_l2tp_server_t *l2tp_server = ctx->config.l2tp_server; + bbl_l2tp_tunnel_t *l2tp_tunnel; + while(l2tp_server) { + CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_server->tunnel_qhead, tunnel_qnode) { + if(l2tp_tunnel->state < BBL_L2TP_TUNNEL_SEND_STOPCCN) { + l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; + l2tp_tunnel->state_seconds = 0; + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } } + l2tp_server = l2tp_server->next; } -} \ No newline at end of file +} + +uint16_t +bbl_l2tp_tunnel_count(bbl_ctx_s *ctx) { + bbl_l2tp_server_t *l2tp_server = ctx->config.l2tp_server; + bbl_l2tp_tunnel_t *l2tp_tunnel; + uint16_t active = 0; + + while(l2tp_server) { + CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_server->tunnel_qhead, tunnel_qnode) { + active++; + } + l2tp_server = l2tp_server->next; + } + return active; +} + diff --git a/src/bbl_l2tp.h b/src/bbl_l2tp.h index c7fdab31..9b3da83c 100644 --- a/src/bbl_l2tp.h +++ b/src/bbl_l2tp.h @@ -20,14 +20,16 @@ (((_a) > (_b) && (_a) - (_b) < 32768) || ((_a) < (_b) && (_b) - (_a) > 32768)) typedef struct bbl_interface_ bbl_interface_s; +typedef struct bbl_ctx_ bbl_ctx_s; /* L2TP Tunnel State */ typedef enum { - BBL_L2TP_TUNNEL_IDLE = 0, - BBL_L2TP_TUNNEL_WAIT_CTR_CONN = 1, - BBL_L2TP_TUNNEL_ESTABLISHED = 2, - BBL_L2TP_TUNNEL_SEND_STOPCCN = 3, - BBL_L2TP_TUNNEL_TERMINATED = 4, + BBL_L2TP_TUNNEL_IDLE = 0, + BBL_L2TP_TUNNEL_WAIT_CTR_CONN = 1, + BBL_L2TP_TUNNEL_ESTABLISHED = 2, + BBL_L2TP_TUNNEL_SEND_STOPCCN = 3, + BBL_L2TP_TUNNEL_RCVD_STOPCCN = 4, + BBL_L2TP_TUNNEL_TERMINATED = 5, BBL_L2TP_TUNNEL_MAX } __attribute__ ((__packed__)) l2tp_tunnel_state_t; @@ -43,27 +45,20 @@ typedef enum { /* L2TP Server Configuration (LNS) */ typedef struct bbl_l2tp_server_ { - struct bbl_interface_ *interface; - l2tp_tunnel_state_t state; - + /* Filled by configuration ...*/ uint32_t ip; - uint32_t framing; - uint32_t bearer; uint16_t idle_timeout; uint16_t hello_interval; uint16_t session_limit; uint16_t receive_window; uint16_t max_retry; - uint16_t next_tunnel_id; - - void *next; /* pointer to next L2TP server element */ - - /* The following members must be freed - * if server is destroyed! */ - char *secret; char *host_name; - char *vendor; + + /* Pointer to next L2TP server + * configuration (simple list). */ + uint16_t next_tunnel_id; + void *next; /* List of L2TP tunnel instances * for the corresponding server. */ @@ -97,13 +92,24 @@ typedef struct bbl_l2tp_tunnel_ { CIRCLEQ_ENTRY(bbl_l2tp_tunnel_) tunnel_qnode; - bbl_l2tp_server_t *server; - l2tp_tunnel_state_t state; + /* Pointer to corresponding network interface */ + struct bbl_interface_ *interface; + + /* Pointer to L2TP server configuration */ + bbl_l2tp_server_t *server; + /* L2TP tunnel state */ + l2tp_tunnel_state_t state; + uint32_t state_seconds; + uint16_t tunnel_id; uint16_t peer_tunnel_id; uint16_t next_session_id; + CIRCLEQ_HEAD(bbl_l2tp_tunnel___, bbl_l2tp_session_) session_qhead; + + bool initial_packet_send; + uint16_t ns; uint16_t nr; uint16_t peer_ns; @@ -120,7 +126,6 @@ typedef struct bbl_l2tp_tunnel_ struct timer_ *timer_tx; struct timer_ *timer_ctrl; - uint16_t retry; uint16_t cwnd; uint16_t ssthresh; @@ -129,6 +134,7 @@ typedef struct bbl_l2tp_tunnel_ bool zlb; bbl_l2tp_queue_t *zlb_qnode; + CIRCLEQ_HEAD(bbl_l2tp_tunnel__, bbl_l2tp_queue_) txq_qhead; struct { uint32_t control_rx; @@ -156,13 +162,12 @@ typedef struct bbl_l2tp_tunnel_ char *peer_name; char *peer_vendor; - CIRCLEQ_HEAD(bbl_l2tp_tunnel__, bbl_l2tp_queue_) txq_qhead; - CIRCLEQ_HEAD(bbl_l2tp_tunnel___, bbl_l2tp_session_) session_qhead; } bbl_l2tp_tunnel_t; /* L2TP Session Instance */ typedef struct bbl_l2tp_session_ { + CIRCLEQ_ENTRY(bbl_l2tp_session_) session_qnode; bbl_l2tp_tunnel_t *tunnel; @@ -209,5 +214,7 @@ const char* l2tp_message_string(l2tp_message_type type); void bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface); +void bbl_l2tp_stop_all_tunnel(bbl_ctx_s *ctx); +uint16_t bbl_l2tp_tunnel_count(bbl_ctx_s *ctx); #endif \ No newline at end of file diff --git a/src/bbl_l2tp_avp.c b/src/bbl_l2tp_avp.c index 1460cf6d..3b2f669a 100644 --- a/src/bbl_l2tp_avp.c +++ b/src/bbl_l2tp_avp.c @@ -55,14 +55,15 @@ bbl_l2tp_avp_decode(uint8_t **_buf, uint16_t *_len, bbl_l2tp_avp_t *avp) { /* bbl_l2tp_avp_encode */ static void -bbl_l2tp_avp_encode(uint8_t *buf, uint16_t *len, bbl_l2tp_avp_t *avp) { +bbl_l2tp_avp_encode(uint8_t **_buf, uint16_t *len, bbl_l2tp_avp_t *avp) { uint16_t avp_len_field; + uint8_t *buf = *_buf; /* TODO: Currently we do not support to hide (H) AVP values! */ avp_len_field = avp->len + L2TP_AVP_HDR_LEN; if(avp->m) avp_len_field |= L2TP_AVP_M_BIT_MASK; - *(uint16_t*)buf = htobe16(len); + *(uint16_t*)buf = htobe16(avp_len_field); BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); *(uint16_t*)buf = htobe16(avp->vendor); BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); @@ -86,6 +87,7 @@ bbl_l2tp_avp_encode(uint8_t *buf, uint16_t *len, bbl_l2tp_avp_t *avp) { BUMP_WRITE_BUFFER(buf, len, avp->len); break; } + *_buf = buf; } /* bbl_l2tp_avp_unhide */ @@ -454,10 +456,12 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel) { return false; } l2tp_tunnel->peer_receive_window = be16toh(*(uint16_t*)avp.value); + l2tp_tunnel->ssthresh = l2tp_tunnel->peer_receive_window; break; case L2TP_AVP_CHALLENGE: if(!l2tp_tunnel->peer_challenge && avp.len) { l2tp_tunnel->peer_challenge = malloc(avp.len); + l2tp_tunnel->peer_challenge_len = avp.len; memcpy(l2tp_tunnel->peer_challenge, avp.value, avp.len); } break; @@ -471,6 +475,7 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel) { } if(!l2tp_tunnel->peer_challenge_response) { l2tp_tunnel->peer_challenge_response = malloc(avp.len); + l2tp_tunnel->peer_challenge_response_len = avp.len; memcpy(l2tp_tunnel->peer_challenge_response, avp.value, avp.len); } break; @@ -510,7 +515,7 @@ void bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, l2tp_message_type l2tp_type, uint8_t *buf, uint16_t *len) { - bbl_l2tp_avp_t avp; + bbl_l2tp_avp_t avp = {0}; uint16_t v16; uint32_t v32; @@ -529,7 +534,7 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_ avp.len = 2; avp.value_type = L2TP_AVP_VALUE_UINT16; avp.value = (void*)&v16; - bbl_l2tp_avp_encode(buf, len, &avp); + bbl_l2tp_avp_encode(&buf, len, &avp); switch (l2tp_type) { case L2TP_MESSAGE_SCCRP: @@ -540,7 +545,7 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_ avp.len = 2; avp.value_type = L2TP_AVP_VALUE_UINT16; avp.value = (void*)&v16; - bbl_l2tp_avp_encode(buf, len, &avp); + bbl_l2tp_avp_encode(&buf, len, &avp); /* Framing Capabilities */ v32 = 3; /* A + S */ avp.m = true; @@ -549,67 +554,71 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_ avp.value_type = L2TP_AVP_VALUE_UINT32; avp.value = (void*)&v32; /* Bearer Capabilities */ - bbl_l2tp_avp_encode(buf, len, &avp); + bbl_l2tp_avp_encode(&buf, len, &avp); v32 = 1; /* D */ avp.m = true; avp.type = L2TP_AVP_BEARER_CAPABILITIES; avp.len = 4; avp.value_type = L2TP_AVP_VALUE_UINT32; avp.value = (void*)&v32; - bbl_l2tp_avp_encode(buf, len, &avp); + bbl_l2tp_avp_encode(&buf, len, &avp); /* Firmware Revision */ v16 = 1; avp.m = false; - avp.type = L2TP_AVP_BEARER_CAPABILITIES; + avp.type = L2TP_AVP_FIRMWARE_REVISION; avp.len = 2; avp.value_type = L2TP_AVP_VALUE_UINT16; avp.value = (void*)&v16; - bbl_l2tp_avp_encode(buf, len, &avp); + bbl_l2tp_avp_encode(&buf, len, &avp); /* Host Name */ avp.m = true; avp.type = L2TP_AVP_HOST_NAME; avp.len = strlen(l2tp_tunnel->server->host_name); avp.value_type = L2TP_AVP_VALUE_BYTES; avp.value = (void*)(l2tp_tunnel->server->host_name); - bbl_l2tp_avp_encode(buf, len, &avp); + bbl_l2tp_avp_encode(&buf, len, &avp); /* Vendor Name */ avp.m = false; avp.type = L2TP_AVP_VENDOR_NAME; - avp.len = strlen(l2tp_tunnel->server->vendor); + avp.len = sizeof("bngblaster") - 1; avp.value_type = L2TP_AVP_VALUE_BYTES; - avp.value = (void*)(l2tp_tunnel->server->vendor); - bbl_l2tp_avp_encode(buf, len, &avp); + avp.value = (void*)"bngblaster"; + bbl_l2tp_avp_encode(&buf, len, &avp); /* Assigned Tunnel ID */ avp.m = true; avp.type = L2TP_AVP_ASSIGNED_TUNNEL_ID; avp.len = 2; avp.value_type = L2TP_AVP_VALUE_UINT16; avp.value = (void*)(&l2tp_tunnel->tunnel_id); - bbl_l2tp_avp_encode(buf, len, &avp); + bbl_l2tp_avp_encode(&buf, len, &avp); /* Receive Window Size */ + v16 = 4; + if(l2tp_tunnel->server->receive_window) { + v16 = l2tp_tunnel->server->receive_window; + } avp.m = true; avp.type = L2TP_AVP_RECEIVE_WINDOW_SIZE; avp.len = 2; avp.value_type = L2TP_AVP_VALUE_UINT16; - avp.value = (void*)(&l2tp_tunnel->server->receive_window); - bbl_l2tp_avp_encode(buf, len, &avp); + avp.value = (void*)&v16; + bbl_l2tp_avp_encode(&buf, len, &avp); /* Challenge */ if(l2tp_tunnel->challenge_len) { - avp.m = false; + avp.m = true; avp.type = L2TP_AVP_CHALLENGE; avp.len = l2tp_tunnel->challenge_len; avp.value_type = L2TP_AVP_VALUE_BYTES; avp.value = l2tp_tunnel->challenge; - bbl_l2tp_avp_encode(buf, len, &avp); + bbl_l2tp_avp_encode(&buf, len, &avp); } /* Challenge Response */ if(l2tp_tunnel->challenge_response_len) { - avp.m = false; + avp.m = true; avp.type = L2TP_AVP_CHALLENGE_RESPONSE; avp.len = l2tp_tunnel->challenge_response_len; avp.value_type = L2TP_AVP_VALUE_BYTES; avp.value = l2tp_tunnel->challenge_response; - bbl_l2tp_avp_encode(buf, len, &avp); + bbl_l2tp_avp_encode(&buf, len, &avp); } break; case L2TP_MESSAGE_STOPCCN: @@ -619,7 +628,7 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_ avp.len = 2; avp.value_type = L2TP_AVP_VALUE_UINT16; avp.value = (void*)(&l2tp_tunnel->tunnel_id); - bbl_l2tp_avp_encode(buf, len, &avp); + bbl_l2tp_avp_encode(&buf, len, &avp); break; case L2TP_MESSAGE_ICRP: /* Assigned Session ID */ @@ -629,7 +638,7 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_ avp.len = 2; avp.value_type = L2TP_AVP_VALUE_UINT16; avp.value = (void*)(&l2tp_session->key.session_id); - bbl_l2tp_avp_encode(buf, len, &avp); + bbl_l2tp_avp_encode(&buf, len, &avp); } break; case L2TP_MESSAGE_CDN: @@ -640,7 +649,7 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_ avp.len = 2; avp.value_type = L2TP_AVP_VALUE_UINT16; avp.value = (void*)(&l2tp_session->key.session_id); - bbl_l2tp_avp_encode(buf, len, &avp); + bbl_l2tp_avp_encode(&buf, len, &avp); } break; default: diff --git a/src/bbl_protocols.c b/src/bbl_protocols.c index 69247702..38b73152 100644 --- a/src/bbl_protocols.c +++ b/src/bbl_protocols.c @@ -854,15 +854,7 @@ encode_l2tp(uint8_t *buf, uint *len, bbl_l2tp_t *l2tp) { } if(l2tp->type) { /* L2TP control packet */ - *buf = 0x80; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - *buf = 0x08; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - *(uint32_t*)buf = 0; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint32_t)); - *(uint16_t*)buf = htobe16(l2tp->type); - BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); - if(l2tp->payload_len) { + if(l2tp->payload && l2tp->payload_len) { memcpy(buf, l2tp->payload, l2tp->payload_len); BUMP_WRITE_BUFFER(buf, len, l2tp->payload_len); } diff --git a/src/bbl_tx.c b/src/bbl_tx.c index 3baa7510..781c1bd4 100644 --- a/src/bbl_tx.c +++ b/src/bbl_tx.c @@ -1214,7 +1214,6 @@ bbl_encode_network_packet (bbl_interface_s *interface, bbl_session_s *session, u session->write_buf = frame_ptr + TPACKET2_HDRLEN - sizeof(struct sockaddr_ll); session->write_idx = 0; - if (session->network_send_requests & BBL_SEND_SESSION_IPV4) { result = bbl_encode_packet_network_session_ipv4(interface, session); session->network_send_requests &= ~BBL_SEND_SESSION_IPV4; @@ -1500,21 +1499,22 @@ bbl_tx_job (timer_s *timer) if(!interface->access) { /* Send L2TP Packets */ while (!CIRCLEQ_EMPTY(&interface->l2tp_tx_qhead)) { + frame_ptr = interface->ring_tx + (interface->cursor_tx * interface->req_tx.tp_frame_size); + tphdr = (struct tpacket2_hdr *)frame_ptr; /* Check if this slot available for writing. */ if (tphdr->tp_status != TP_STATUS_AVAILABLE) { interface->stats.no_tx_buffer++; goto Send; } + /* Pop element from queue */ q = CIRCLEQ_FIRST(&interface->l2tp_tx_qhead); CIRCLEQ_REMOVE(&interface->l2tp_tx_qhead, q, tx_qnode); CIRCLEQ_NEXT(q, tx_qnode) = NULL; CIRCLEQ_PREV(q, tx_qnode) = NULL; /* Copy packet from queue to ring buffer */ - frame_ptr = interface->ring_tx + (interface->cursor_tx * interface->req_tx.tp_frame_size); - tphdr = (struct tpacket2_hdr *)frame_ptr; - memcpy(frame_ptr + TPACKET2_HDRLEN - sizeof(struct sockaddr_ll) , q->packet, q->packet_len); - tphdr->tp_len = interface->mc_packet_len; + memcpy((frame_ptr + TPACKET2_HDRLEN - sizeof(struct sockaddr_ll)), q->packet, q->packet_len); + tphdr->tp_len = q->packet_len; tphdr->tp_status = TP_STATUS_SEND_REQUEST; interface->stats.packets_tx++; interface->cursor_tx = (interface->cursor_tx + 1) % interface->req_tx.tp_frame_nr; @@ -1525,7 +1525,6 @@ bbl_tx_job (timer_s *timer) tphdr->tp_len, interface->pcap_index, PCAPNG_EPB_FLAGS_OUTBOUND); } } - /* Generate Multicast Traffic */ g = ctx->config.igmp_group_count; if(ctx->config.send_multicast_traffic && ctx->multicast_traffic && g) { From 66cc9b2a58a1eb36ff7d621d34ca2a767899ec75 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Thu, 18 Feb 2021 23:32:52 +0100 Subject: [PATCH 03/27] #3 L2TPv2 LNS Support (WIP) - 3 --- src/bbl.c | 20 +++- src/bbl.h | 1 + src/bbl_l2tp.c | 238 +++++++++++++++++++++++++++++++++++--------- src/bbl_l2tp.h | 27 +++-- src/bbl_l2tp_avp.c | 6 ++ src/bbl_protocols.c | 2 +- src/bbl_tx.c | 4 + 7 files changed, 241 insertions(+), 57 deletions(-) diff --git a/src/bbl.c b/src/bbl.c index 3795b830..a2eb48bb 100644 --- a/src/bbl.c +++ b/src/bbl.c @@ -587,6 +587,14 @@ bbl_compare_session (void *key1, void *key2) return (a > b) - (a < b); } +int +bbl_compare_l2tp_session (void *key1, void *key2) +{ + const uint32_t a = *(const uint32_t*)key1; + const uint32_t b = *(const uint32_t*)key2; + return (a > b) - (a < b); +} + uint bbl_session_hash (const void* k) { @@ -599,6 +607,14 @@ bbl_session_hash (const void* k) return hash; } +uint +bbl_l2tp_session_hash (const void* k) +{ + uint hash = 2166136261U; + hash ^= *(uint32_t *)k; + return hash; +} + /* * Allocate a context which is our top-level data structure. */ @@ -633,8 +649,8 @@ bbl_add_ctx (void) bbl_session_hash, BBL_SESSION_HASHTABLE_SIZE); - ctx->l2tp_session_dict = hashtable2_dict_new((dict_compare_func)bbl_compare_session, - bbl_session_hash, + ctx->l2tp_session_dict = hashtable2_dict_new((dict_compare_func)bbl_compare_l2tp_session, + bbl_l2tp_session_hash, BBL_SESSION_HASHTABLE_SIZE); return ctx; diff --git a/src/bbl.h b/src/bbl.h index 154d5ec4..8df4415c 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -363,6 +363,7 @@ typedef struct bbl_ctx_ dict *session_dict; /* hashtable for sessions */ dict *l2tp_session_dict; /* hashtable for L2TP sessions */ + uint16_t next_tunnel_id; uint64_t flow_id; diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 375997dd..6a1d9378 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -153,7 +153,7 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { } if(l2tp_tunnel->state == BBL_L2TP_TUNNEL_SEND_STOPCCN) { - if(!CIRCLEQ_EMPTY(&l2tp_tunnel->txq_qhead)) { + if(CIRCLEQ_EMPTY(&l2tp_tunnel->txq_qhead)) { l2tp_tunnel->state = BBL_L2TP_TUNNEL_TERMINATED; l2tp_tunnel->state_seconds = 0; } @@ -182,6 +182,8 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { } } CIRCLEQ_INSERT_TAIL(&interface->l2tp_tx_qhead, q, tx_qnode); + l2tp_tunnel->stats.control_tx++; + interface->stats.l2tp_control_tx++; l2tp_tunnel->zlb = false; q->last_tx_time.tv_sec = timestamp.tv_sec; q->last_tx_time.tv_nsec = timestamp.tv_nsec; @@ -209,6 +211,8 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { /* Update Nr. ... */ *(uint16_t*)(q->packet + q->nr_offset) = htobe16(l2tp_tunnel->nr); CIRCLEQ_INSERT_TAIL(&interface->l2tp_tx_qhead, l2tp_tunnel->zlb_qnode, tx_qnode); + l2tp_tunnel->stats.control_tx++; + interface->stats.l2tp_control_tx++; *(uint16_t*)(l2tp_tunnel->zlb_qnode->packet + l2tp_tunnel->zlb_qnode->ns_offset) = htobe16(l2tp_tunnel->ns); *(uint16_t*)(l2tp_tunnel->zlb_qnode->packet + l2tp_tunnel->zlb_qnode->nr_offset) = htobe16(l2tp_tunnel->nr); } @@ -259,7 +263,6 @@ bbl_l2tp_tunnel_control_job (timer_s *timer) { } } - /** * bbl_l2tp_send * @@ -289,7 +292,7 @@ bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, eth.dst = interface->gateway_mac; eth.src = interface->mac; - eth.vlan_outer = interface->ctx->config.network_vlan; + eth.vlan_outer = ctx->config.network_vlan; eth.type = ETH_TYPE_IPV4; eth.next = &ipv4; ipv4.dst = l2tp_tunnel->peer_ip; @@ -356,6 +359,61 @@ bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, } } +/** + * bbl_l2tp_send_data + * + * This function ... + * + * @param l2tp_tunnel Mandatory pointer to L2TP tunnel object. + * @param l2tp_session Mandatory pointer to L2TP session object. + * @param protocol ... + * @param next ... + */ +static void +bbl_l2tp_send_data(bbl_l2tp_session_t *l2tp_session, uint16_t protocol, void *next) { + + bbl_l2tp_tunnel_t *l2tp_tunnel = l2tp_session->tunnel; + bbl_interface_s *interface = l2tp_tunnel->interface; + bbl_l2tp_queue_t *q = calloc(1, sizeof(bbl_l2tp_queue_t)); + bbl_ethernet_header_t eth = {0}; + bbl_ipv4_t ipv4 = {0}; + bbl_udp_t udp = {0}; + bbl_l2tp_t l2tp = {0}; + uint len = 0; + eth.dst = interface->gateway_mac; + eth.src = interface->mac; + eth.vlan_outer = interface->ctx->config.network_vlan; + eth.type = ETH_TYPE_IPV4; + eth.next = &ipv4; + ipv4.dst = l2tp_tunnel->peer_ip; + ipv4.src = l2tp_tunnel->server->ip; + ipv4.ttl = 64; + ipv4.protocol = PROTOCOL_IPV4_UDP; + ipv4.next = &udp; + udp.src = L2TP_UDP_PORT; + udp.dst = L2TP_UDP_PORT; + udp.protocol = UDP_PROTOCOL_L2TP; + udp.next = &l2tp; + l2tp.type = L2TP_MESSAGE_DATA; + l2tp.tunnel_id = l2tp_tunnel->peer_tunnel_id; + l2tp.session_id = l2tp_session->peer_session_id; + l2tp.protocol = protocol; + l2tp.next = next; + q->data = true; + q->packet = malloc(L2TP_MAX_PACKET_SIZE); + if(encode_ethernet(q->packet, &len, ð) == PROTOCOL_SUCCESS) { + q->packet_len = len; + CIRCLEQ_INSERT_TAIL(&interface->l2tp_tx_qhead, q, tx_qnode); + l2tp_tunnel->stats.data_tx++; + interface->stats.l2tp_data_tx++; + } else { + /* Encode error.... */ + LOG(ERROR, "L2TP Data Encode Error!\n"); + free(q->packet); + free(q); + } +} + static void bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface) { MD5_CTX md5_ctx; @@ -368,8 +426,7 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s bbl_l2tp_tunnel_t *l2tp_tunnel2; bbl_l2tp_session_t *l2tp_session; - l2tp_key_t key = {0}; - dict_insert_result result = {0}; + dict_insert_result result; void **search = NULL; uint8_t l2tp_type; @@ -388,11 +445,12 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s l2tp_tunnel->peer_receive_window = 4; l2tp_tunnel->ssthresh = 4; l2tp_tunnel->cwnd = 1; - l2tp_tunnel->peer_tunnel_id = 1; l2tp_tunnel->peer_ip = ipv4->src; l2tp_tunnel->peer_ns = l2tp->ns; l2tp_tunnel->nr = (l2tp->ns + 1); l2tp_tunnel->state = BBL_L2TP_TUNNEL_WAIT_CTR_CONN; + l2tp_tunnel->stats.control_rx++; + interface->stats.l2tp_control_rx++; /* Decode received attributes and store in tunnel */ if(!bbl_l2tp_avp_decode_tunnel(l2tp, l2tp_tunnel)) { return bbl_l2tp_tunnel_delete(l2tp_tunnel); @@ -414,31 +472,26 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s } } + /* Add dummy tunnel session, this session is only used + * to search for tunnel using the same dictionary. */ + l2tp_session = calloc(1, sizeof(bbl_l2tp_session_t)); + l2tp_session->state = BBL_L2TP_SESSION_MAX; + l2tp_session->tunnel = l2tp_tunnel; + l2tp_session->key.session_id = 0; + /* Assign tunnel id ... */ while(true) { - key.ip = l2tp_server->ip; - key.tunnel_id = l2tp_server->next_tunnel_id++; - key.session_id = 0; - if(key.tunnel_id == 0) continue; /* skip tunnel 0 */ - search = dict_search(ctx->l2tp_session_dict, &key); + l2tp_session->key.tunnel_id = ctx->next_tunnel_id++; + if(l2tp_session->key.tunnel_id == 0) continue; /* skip tunnel 0 */ + search = dict_search(ctx->l2tp_session_dict, &l2tp_session->key); if(search) { /* Used, try next ... */ - key.tunnel_id = l2tp_server->next_tunnel_id++; + continue; } else { break; } } - l2tp_tunnel->tunnel_id = key.tunnel_id; - l2tp_tunnel->stats.control_rx++; - interface->stats.l2tp_control_rx++; - - /* Add dummy tunnel session, this session is only used - * to search for tunnel using the same dictionary. */ - l2tp_session = calloc(1, sizeof(bbl_l2tp_session_t)); - l2tp_session->state = BBL_L2TP_SESSION_MAX; - l2tp_session->tunnel = l2tp_tunnel; - l2tp_session->key.ip = key.ip; - l2tp_session->key.tunnel_id = key.tunnel_id; + l2tp_tunnel->tunnel_id = l2tp_session->key.tunnel_id; result = dict_insert(ctx->l2tp_session_dict, &l2tp_session->key); if (!result.inserted) { LOG(ERROR, "L2TP Error (%s) Failed to add tunnel session\n", @@ -448,7 +501,6 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s } *result.datum_ptr = l2tp_session; CIRCLEQ_INSERT_TAIL(&l2tp_tunnel->session_qhead, l2tp_session, session_qnode); - /* L2TP Challenge/Response */ if(l2tp_server->secret) { l2tp_tunnel->challenge = malloc(L2TP_MD5_DIGEST_LEN); @@ -570,7 +622,6 @@ static void bbl_l2tp_icrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_tunnel_t *l2tp_tunnel) { bbl_ctx_s *ctx = interface->ctx; - l2tp_key_t key; dict_insert_result result; void **search; @@ -588,33 +639,29 @@ bbl_l2tp_icrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * return bbl_l2tp_session_delete(l2tp_session); } + l2tp_session->key.tunnel_id = l2tp_tunnel->tunnel_id; + /* Assign session id ... */ while(true) { - key.ip = l2tp_tunnel->server->ip; - key.tunnel_id = l2tp_tunnel->tunnel_id; - key.session_id = l2tp_tunnel->next_session_id++; - search = dict_search(ctx->l2tp_session_dict, &key); + l2tp_session->key.session_id = l2tp_tunnel->next_session_id++; + if(l2tp_session->key.session_id == 0) continue; /* skip tunnel 0 */ + search = dict_search(ctx->l2tp_session_dict, &l2tp_session->key); if(search) { /* Used, try next ... */ - key.session_id = l2tp_tunnel->next_session_id++; + continue; } else { break; } } - l2tp_session->key.ip = key.ip; - l2tp_session->key.tunnel_id = key.tunnel_id; - l2tp_session->key.session_id = key.session_id; - - result = dict_insert(ctx->l2tp_session_dict, &key); + result = dict_insert(ctx->l2tp_session_dict, &l2tp_session->key); if (!result.inserted) { - /* TODO: Here we need to handle this properly but actually - * this should not happen. */ + LOG(ERROR, "L2TP Error (%s) Failed to add session\n", + l2tp_tunnel->server->host_name); free(l2tp_session); - return; + return ; } *result.datum_ptr = l2tp_session; CIRCLEQ_INSERT_TAIL(&l2tp_tunnel->session_qhead, l2tp_session, session_qnode); - bbl_l2tp_send(l2tp_tunnel, l2tp_session, L2TP_MESSAGE_ICRP); } @@ -666,10 +713,109 @@ static void bbl_l2tp_data_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_session_t *l2tp_session) { bbl_ctx_s *ctx = interface->ctx; + bbl_lcp_t *lcp_rx; + bbl_pap_t *pap_rx; + bbl_pap_t pap_tx; + bbl_ipcp_t *ipcp_rx; + bbl_ipcp_t ipcp_tx; + bbl_ip6cp_t *ip6cp_rx; + bbl_ip6cp_t ip6cp_tx; + UNUSED(ctx); UNUSED(eth); - UNUSED(l2tp); - UNUSED(l2tp_session); + + if(l2tp_session->state != BBL_L2TP_SESSION_ESTABLISHED) { + return; + } + + switch (l2tp->protocol) { + case PROTOCOL_LCP: + lcp_rx = (bbl_lcp_t*)l2tp->next; + if(lcp_rx->code == PPP_CODE_TERM_REQUEST) { + bbl_l2tp_send(l2tp_session->tunnel, l2tp_session, L2TP_MESSAGE_CDN); + return bbl_l2tp_session_delete(l2tp_session); + } + if(lcp_rx->code == PPP_CODE_ECHO_REQUEST) { + lcp_rx->code = PPP_CODE_ECHO_REPLY; + bbl_l2tp_send_data(l2tp_session, PROTOCOL_LCP, lcp_rx); + } + break; + case PROTOCOL_PAP: + memset(&pap_tx, 0x0, sizeof(bbl_pap_t)); + pap_rx = (bbl_pap_t*)l2tp->next; + pap_tx.code = PAP_CODE_ACK; + pap_tx.identifier = pap_rx->identifier; + bbl_l2tp_send_data(l2tp_session, PROTOCOL_PAP, &pap_tx); + break; + case PROTOCOL_IPCP: + ipcp_rx = (bbl_ipcp_t*)l2tp->next; + memset(&ipcp_tx, 0x0, sizeof(bbl_ipcp_t)); + if(ipcp_rx->code == PPP_CODE_CONF_REQUEST) { + ipcp_rx->options = NULL; + ipcp_rx->options_len = 0; + if(ipcp_rx->address == L2TP_IPCP_IP_REMOTE) { + ipcp_rx->code = PPP_CODE_CONF_ACK; + if(l2tp_session->ipcp_state == BBL_PPP_LOCAL_ACK) { + l2tp_session->ipcp_state = BBL_PPP_OPENED; + } else { + l2tp_session->ipcp_state = BBL_PPP_PEER_ACK; + ipcp_tx.code = PPP_CODE_CONF_REQUEST; + ipcp_tx.identifier = 1; + ipcp_tx.address = L2TP_IPCP_IP_LOCAL; + ipcp_tx.option_address = true; + bbl_l2tp_send_data(l2tp_session, PROTOCOL_IPCP, &ipcp_tx); + } + } else { + ipcp_rx->code = PPP_CODE_CONF_NAK; + ipcp_rx->address = L2TP_IPCP_IP_REMOTE; + ipcp_rx->option_address = true; + ipcp_rx->option_dns1 = false; + ipcp_rx->option_dns2 = false; + } + bbl_l2tp_send_data(l2tp_session, PROTOCOL_IPCP, ipcp_rx); + } else if (ipcp_rx->code == PPP_CODE_CONF_ACK) { + if(l2tp_session->ipcp_state == BBL_PPP_PEER_ACK) { + l2tp_session->ipcp_state = BBL_PPP_OPENED; + } else { + l2tp_session->ipcp_state = BBL_PPP_LOCAL_ACK; + ipcp_tx.code = PPP_CODE_CONF_REQUEST; + ipcp_tx.identifier = 1; + ipcp_tx.address = L2TP_IPCP_IP_LOCAL; + ipcp_tx.option_address = true; + bbl_l2tp_send_data(l2tp_session, PROTOCOL_IPCP, &ipcp_tx); + } + } + break; + case PROTOCOL_IP6CP: + ip6cp_rx = (bbl_ip6cp_t*)l2tp->next; + memset(&ip6cp_tx, 0x0, sizeof(bbl_ip6cp_t)); + if(ip6cp_rx->code == PPP_CODE_CONF_REQUEST) { + ip6cp_rx->code = PPP_CODE_CONF_ACK; + if(l2tp_session->ip6cp_state == BBL_PPP_LOCAL_ACK) { + l2tp_session->ip6cp_state = BBL_PPP_OPENED; + } else { + l2tp_session->ip6cp_state = BBL_PPP_PEER_ACK; + ip6cp_tx.code = PPP_CODE_CONF_REQUEST; + ip6cp_tx.identifier = 1; + ip6cp_tx.ipv6_identifier = 1; + bbl_l2tp_send_data(l2tp_session, PROTOCOL_IPCP, &ip6cp_tx); + } + bbl_l2tp_send_data(l2tp_session, PROTOCOL_IPCP, ip6cp_rx); + } else if (ip6cp_rx->code == PPP_CODE_CONF_ACK) { + if(l2tp_session->ip6cp_state == BBL_PPP_PEER_ACK) { + l2tp_session->ip6cp_state = BBL_PPP_OPENED; + } else { + l2tp_session->ip6cp_state = BBL_PPP_LOCAL_ACK; + ip6cp_tx.code = PPP_CODE_CONF_REQUEST; + ip6cp_tx.identifier = 1; + ip6cp_tx.ipv6_identifier = 1; + bbl_l2tp_send_data(l2tp_session, PROTOCOL_IPCP, &ip6cp_tx); + } + } + break; + default: + break; + } } @@ -696,12 +842,8 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ return bbl_l2tp_sccrq_rx(eth, l2tp, interface); } - LOG(DEBUG, "L2TP Debug %s received from %s\n", l2tp_message_string(l2tp->type), format_ipv4_address(&ipv4->src)); - - key.ip = ipv4->dst; key.tunnel_id = l2tp->tunnel_id; key.session_id = l2tp->session_id; - search = dict_search(ctx->l2tp_session_dict, &key); if(search) { l2tp_session = *search; @@ -728,6 +870,9 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ if(l2tp->type != L2TP_MESSAGE_ZLB) { l2tp_tunnel->zlb = true; } + if(l2tp_tunnel->cwnd < l2tp_tunnel->peer_receive_window) { + l2tp_tunnel->cwnd++; + } l2tp_tunnel->stats.control_rx++; interface->stats.l2tp_control_rx++; if(!l2tp_tunnel->timer_tx_active) { @@ -753,7 +898,6 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ } else { if (L2TP_SEQ_LT(l2tp->ns, l2tp_tunnel->nr)) { /* Duplicate packet received */ - LOG(DEBUG, "L2TP Debug duplicate received\n"); l2tp_tunnel->zlb = true; l2tp_tunnel->stats.control_rx_dup++; interface->stats.l2tp_control_rx_dup++; @@ -763,7 +907,6 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ } } else { /* Out-of-Order packet received */ - LOG(DEBUG, "L2TP Debug out of order received\n"); l2tp_tunnel->stats.control_rx_ooo++; interface->stats.l2tp_control_rx_ooo++; } @@ -799,6 +942,7 @@ bbl_l2tp_tunnel_count(bbl_ctx_s *ctx) { } l2tp_server = l2tp_server->next; } + LOG(DEBUG, "TUNNEL %u\n", active); return active; } diff --git a/src/bbl_l2tp.h b/src/bbl_l2tp.h index 9b3da83c..463f8417 100644 --- a/src/bbl_l2tp.h +++ b/src/bbl_l2tp.h @@ -13,6 +13,10 @@ #define L2TP_MAX_PACKET_SIZE 1500 #define L2TP_MAX_AVP_SIZE 1024 +#define L2TP_IPCP_IP_LOCAL 168495882 +#define L2TP_IPCP_IP_REMOTE 168430090 + + #define L2TP_SEQ_LT(_a, _b)\ (((_a) < (_b) && (_b) - (_a) < 32768) || ((_a) > (_b) && (_a) - (_b) > 32768)) @@ -57,7 +61,6 @@ typedef struct bbl_l2tp_server_ /* Pointer to next L2TP server * configuration (simple list). */ - uint16_t next_tunnel_id; void *next; /* List of L2TP tunnel instances @@ -67,14 +70,14 @@ typedef struct bbl_l2tp_server_ /* L2TP Session Key */ typedef struct l2tp_key_ { - uint32_t ip; uint16_t tunnel_id; uint16_t session_id; } __attribute__ ((__packed__)) l2tp_key_t; -/* L2TP TX Queue Entry */ +/* L2TP Control TX Queue Entry */ typedef struct bbl_l2tp_queue_ { + bool data; /* l2tp data packets */ uint16_t ns; uint8_t ns_offset; uint8_t nr_offset; @@ -87,11 +90,22 @@ typedef struct bbl_l2tp_queue_ CIRCLEQ_ENTRY(bbl_l2tp_queue_) tx_qnode; /* TX request */ } bbl_l2tp_queue_t; +/* L2TP Data TX Queue Entry */ +typedef struct bbl_l2tp_data_queue_ +{ + uint8_t *packet; + uint16_t packet_len; + CIRCLEQ_ENTRY(bbl_l2tp_data_queue_) tx_qnode; /* TX request */ +} bbl_l2tp_data_queue_t; + /* L2TP Tunnel Instance */ typedef struct bbl_l2tp_tunnel_ { CIRCLEQ_ENTRY(bbl_l2tp_tunnel_) tunnel_qnode; + CIRCLEQ_HEAD(bbl_l2tp_tunnel__, bbl_l2tp_session_) session_qhead; + CIRCLEQ_HEAD(bbl_l2tp_tunnel___, bbl_l2tp_queue_) txq_qhead; + /* Pointer to corresponding network interface */ struct bbl_interface_ *interface; @@ -106,8 +120,6 @@ typedef struct bbl_l2tp_tunnel_ uint16_t peer_tunnel_id; uint16_t next_session_id; - CIRCLEQ_HEAD(bbl_l2tp_tunnel___, bbl_l2tp_session_) session_qhead; - bool initial_packet_send; uint16_t ns; @@ -134,7 +146,6 @@ typedef struct bbl_l2tp_tunnel_ bool zlb; bbl_l2tp_queue_t *zlb_qnode; - CIRCLEQ_HEAD(bbl_l2tp_tunnel__, bbl_l2tp_queue_) txq_qhead; struct { uint32_t control_rx; @@ -174,7 +185,6 @@ typedef struct bbl_l2tp_session_ l2tp_session_state_t state; struct { - uint32_t ip; uint16_t tunnel_id; uint16_t session_id; } key; @@ -197,6 +207,9 @@ typedef struct bbl_l2tp_session_ uint16_t proxy_auth_challenge_len; uint16_t proxy_auth_response_len; + uint8_t ipcp_state; + uint8_t ip6cp_state; + /* The following members must be freed * if session is destroyed! */ diff --git a/src/bbl_l2tp_avp.c b/src/bbl_l2tp_avp.c index 3b2f669a..624e634a 100644 --- a/src/bbl_l2tp_avp.c +++ b/src/bbl_l2tp_avp.c @@ -174,6 +174,9 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel, bb } if(avp.vendor == 0) { switch(avp.type) { + case L2TP_AVP_RESULT_CODE: + /* Currently not needed! */ + break; case L2TP_AVP_ASSIGNED_SESSION_ID: if(avp.len != 2) { LOG(L2TP, "L2TP Error (%s) Invalid L2TP assigned session id AVP in %s from %s\n", @@ -384,6 +387,9 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel) { } if(avp.vendor == 0) { switch(avp.type) { + case L2TP_AVP_RESULT_CODE: + /* Currently not needed! */ + break; case L2TP_AVP_PROTOCOL_VERSION: if(avp.len != 2 || be16toh(*(uint16_t*)avp.value) != 256) { LOG(L2TP, "L2TP Error (%s) Invalid L2TP protocol version AVP in %s from %s\n", diff --git a/src/bbl_protocols.c b/src/bbl_protocols.c index 38b73152..10b4bf1b 100644 --- a/src/bbl_protocols.c +++ b/src/bbl_protocols.c @@ -813,7 +813,7 @@ encode_l2tp(uint8_t *buf, uint *len, bbl_l2tp_t *l2tp) { protocol_error_t result; uint16_t *l2tp_len_field = NULL; uint16_t l2tp_len = *len; - + *(uint64_t*)buf = 0; /* Set flags */ if(l2tp->type) { /* L2TP control packet */ diff --git a/src/bbl_tx.c b/src/bbl_tx.c index 781c1bd4..69b04bb9 100644 --- a/src/bbl_tx.c +++ b/src/bbl_tx.c @@ -1524,6 +1524,10 @@ bbl_tx_job (timer_s *timer) frame_ptr + TPACKET2_HDRLEN - sizeof(struct sockaddr_ll), tphdr->tp_len, interface->pcap_index, PCAPNG_EPB_FLAGS_OUTBOUND); } + if(q->data) { + free(q->packet); + free(q); + } } /* Generate Multicast Traffic */ g = ctx->config.igmp_group_count; From fbb3726be244d3ad68fedd3ad0be4839cda2067f Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Fri, 19 Feb 2021 10:59:12 +0100 Subject: [PATCH 04/27] #3 L2TPv2 LNS Support (WIP) - 4 --- src/bbl.c | 6 +- src/bbl.h | 9 +++ src/bbl_interactive.c | 18 +++++ src/bbl_l2tp.c | 161 ++++++++++++++++++++++++++++-------------- src/bbl_l2tp.h | 6 +- src/bbl_stats.c | 38 +++++++++- 6 files changed, 178 insertions(+), 60 deletions(-) diff --git a/src/bbl.c b/src/bbl.c index a2eb48bb..24f00057 100644 --- a/src/bbl.c +++ b/src/bbl.c @@ -965,8 +965,8 @@ bbl_ctrl_job (timer_s *timer) if(ctx->sessions) { if(ctx->sessions_terminated >= ctx->sessions) { - /* Now also close all L2TP tunnels ... */ - if(bbl_l2tp_tunnel_count(ctx) == 0) { + /* Now close all L2TP tunnels ... */ + if(ctx->l2tp_tunnels == 0) { /* Stop event loop to close application! */ CIRCLEQ_INIT(&ctx->timer_root.timer_bucket_qhead); } else { @@ -977,7 +977,7 @@ bbl_ctrl_job (timer_s *timer) } else { /* Network interface only... */ if(g_teardown) { - if(bbl_l2tp_tunnel_count(ctx) == 0) { + if(ctx->l2tp_tunnels == 0) { /* Stop event loop to close application! */ CIRCLEQ_INIT(&ctx->timer_root.timer_bucket_qhead); } else { diff --git a/src/bbl.h b/src/bbl.h index 8df4415c..3f432bb3 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -268,6 +268,8 @@ typedef struct bbl_interface_ uint32_t l2tp_control_retry; uint64_t l2tp_data_rx; uint64_t l2tp_data_tx; + bbl_rate_s rate_l2tp_data_rx; + bbl_rate_s rate_l2tp_data_tx; } stats; struct timer_ *tx_job; @@ -357,6 +359,13 @@ typedef struct bbl_ctx_ uint32_t dhcpv6_established; uint32_t dhcpv6_established_max; + uint32_t l2tp_sessions; + uint32_t l2tp_sessions_max; + uint32_t l2tp_tunnels; + uint32_t l2tp_tunnels_max; + uint32_t l2tp_tunnels_established; + uint32_t l2tp_tunnels_established_max; + CIRCLEQ_HEAD(bbl_ctx_idle_, bbl_session_ ) sessions_idle_qhead; CIRCLEQ_HEAD(bbl_ctx_teardown_, bbl_session_ ) sessions_teardown_qhead; CIRCLEQ_HEAD(bbl_ctx__, bbl_interface_ ) interface_qhead; /* list of interfaces */ diff --git a/src/bbl_interactive.c b/src/bbl_interactive.c index 03f61f88..1ba5051e 100644 --- a/src/bbl_interactive.c +++ b/src/bbl_interactive.c @@ -178,6 +178,24 @@ bbl_stats_job (timer_s *timer) } if (ctx->op.network_if) { + if(ctx->config.l2tp_server) { + wprintw(stats_win, "\nL2TP LNS Statistics\n"); + wprintw(stats_win, " Tunnels %10lu\n", ctx->l2tp_tunnels); + wprintw(stats_win, " Established %10lu\n", ctx->l2tp_tunnels_established); + wprintw(stats_win, " Sessions %10lu\n", ctx->l2tp_sessions); + wprintw(stats_win, " Packets:\n"); + wprintw(stats_win, " Tx Control %10lu Retries: %lu\n", + ctx->op.network_if->stats.l2tp_control_tx, ctx->op.network_if->stats.l2tp_control_retry); + wprintw(stats_win, " Rx Control %10lu Duplicate: %lu Out-of-Order: %lu\n", + ctx->op.network_if->stats.l2tp_control_rx, + ctx->op.network_if->stats.l2tp_control_rx_dup, + ctx->op.network_if->stats.l2tp_control_rx_ooo); + wprintw(stats_win, " Tx Data %10lu (%7lu PPS)\n", + ctx->op.network_if->stats.l2tp_data_tx, ctx->op.network_if->stats.rate_l2tp_data_tx.avg); + wprintw(stats_win, " Rx Data %10lu (%7lu PPS)\n", + ctx->op.network_if->stats.l2tp_data_rx, ctx->op.network_if->stats.rate_l2tp_data_tx.avg); + } + if(access_if && ctx->stats.session_traffic_flows) { wprintw(stats_win, "\nSession Traffic\n"); wprintw(stats_win, " Flows %10lu\n", ctx->stats.session_traffic_flows); diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 6a1d9378..156ae33e 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -39,6 +39,32 @@ l2tp_message_string(l2tp_message_type type) } } +const char* +l2tp_tunnel_state_string(l2tp_tunnel_state_t state) +{ + switch(state) { + case BBL_L2TP_TUNNEL_IDLE: return "Idle"; + case BBL_L2TP_TUNNEL_WAIT_CTR_CONN: return "Wait-Control-Connection"; + case BBL_L2TP_TUNNEL_ESTABLISHED: return "Established"; + case BBL_L2TP_TUNNEL_SEND_STOPCCN: return "Send-StopCCN"; + case BBL_L2TP_TUNNEL_RCVD_STOPCCN: return "Received-StopCCN"; + case BBL_L2TP_TUNNEL_TERMINATED: return "Terminated"; + default: return "UNKNOWN"; + } +} + +const char* +l2tp_session_state_string(l2tp_session_state_t state) +{ + switch(state) { + case BBL_L2TP_SESSION_IDLE: return "Idle"; + case BBL_L2TP_SESSION_WAIT_CONN: return "Wait-Control-Connection"; + case BBL_L2TP_SESSION_ESTABLISHED: return "Established"; + case BBL_L2TP_SESSION_TERMINATED: return "Terminated"; + default: return "UNKNOWN"; + } +} + /** * bbl_l2tp_session_delete * @@ -50,9 +76,19 @@ l2tp_message_string(l2tp_message_type type) void bbl_l2tp_session_delete(bbl_l2tp_session_t *l2tp_session) { bbl_ctx_s *ctx; - + bbl_l2tp_tunnel_t *l2tp_tunnel; + if(l2tp_session) { - ctx = l2tp_session->tunnel->interface->ctx; + l2tp_tunnel = l2tp_session->tunnel; + ctx = l2tp_tunnel->interface->ctx; + + if(l2tp_session->key.session_id) { + /* Here we skip the session with ID zero which is the tunnel session. */ + LOG(DEBUG, "L2TP Debug (%s) Tunnel %u Session %u deleted\n", + l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, l2tp_session->key.session_id); + + if(ctx->l2tp_sessions) ctx->l2tp_sessions--; + } /* Remove session from tunnel object */ if(CIRCLEQ_NEXT(l2tp_session, session_qnode) != NULL) { CIRCLEQ_REMOVE(&l2tp_session->tunnel->session_qhead, l2tp_session, session_qnode); @@ -81,14 +117,17 @@ bbl_l2tp_session_delete(bbl_l2tp_session_t *l2tp_session) { */ void bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_t *l2tp_tunnel) { - bbl_l2tp_queue_t *q = NULL; - bbl_interface_s *interface = l2tp_tunnel->interface; + bbl_l2tp_queue_t *q; + bbl_interface_s *interface; + bbl_ctx_s *ctx; if(l2tp_tunnel) { - LOG(L2TP, "L2TP DEBUG (%s) Tunnel (%u) from %s (%s) deleted\n", - l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, - l2tp_tunnel->peer_name, - format_ipv4_address(&l2tp_tunnel->peer_ip)); + LOG(DEBUG, "L2TP Debug (%s) Tunnel %u deleted\n", + l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id); + + interface = l2tp_tunnel->interface; + ctx = interface->ctx; + if(ctx->l2tp_tunnels) ctx->l2tp_tunnels--; /* Delete timer */ timer_del(l2tp_tunnel->timer_tx); @@ -129,6 +168,45 @@ bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_t *l2tp_tunnel) { } } +/** + * bbl_l2tp_tunnel_update_state + * + * @param l2tp_tunnel Mandatory pointer to L2TP tunnel object. + * @param state New L2TP tunnel state. + */ +void +bbl_l2tp_tunnel_update_state(bbl_l2tp_tunnel_t *l2tp_tunnel, l2tp_tunnel_state_t state) { + bbl_ctx_s *ctx; + if(l2tp_tunnel->state != state) { + /* State has changed */ + ctx = l2tp_tunnel->interface->ctx; + + LOG(DEBUG, "L2TP Debug (%s) Tunnel %u state changed from %s to %s\n", + l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, + l2tp_tunnel_state_string(l2tp_tunnel->state), + l2tp_tunnel_state_string(state)); + + if(state == BBL_L2TP_TUNNEL_ESTABLISHED) { + /* New state established */ + ctx->l2tp_tunnels_established++; + if(ctx->l2tp_tunnels_established > ctx->l2tp_tunnels_established_max) { + ctx->l2tp_tunnels_established_max = ctx->l2tp_tunnels_established; + } + LOG(L2TP, "L2TP Info (%s) Tunnel %u with %s (%s) estbalished\n", + l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, + l2tp_tunnel->peer_name, + format_ipv4_address(&l2tp_tunnel->peer_ip)); + } else if (l2tp_tunnel->state == BBL_L2TP_TUNNEL_ESTABLISHED) { + if(ctx->l2tp_tunnels_established) { + ctx->l2tp_tunnels_established--; + } + } + /* Set new state and reset state seconds */ + l2tp_tunnel->state = state; + l2tp_tunnel->state_seconds = 0; + } +} + /** * bbl_l2tp_tunnel_tx_job * @@ -154,8 +232,7 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { if(l2tp_tunnel->state == BBL_L2TP_TUNNEL_SEND_STOPCCN) { if(CIRCLEQ_EMPTY(&l2tp_tunnel->txq_qhead)) { - l2tp_tunnel->state = BBL_L2TP_TUNNEL_TERMINATED; - l2tp_tunnel->state_seconds = 0; + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_TERMINATED); } } @@ -193,8 +270,7 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { l2tp_tunnel->stats.control_retry++; interface->stats.l2tp_control_retry++; if(q->retries > l2tp_tunnel->server->max_retry) { - l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; - l2tp_tunnel->state_seconds = 0; + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } } @@ -232,8 +308,7 @@ bbl_l2tp_tunnel_control_job (timer_s *timer) { switch(l2tp_tunnel->state) { case BBL_L2TP_TUNNEL_WAIT_CTR_CONN: if(l2tp_tunnel->state_seconds > 30) { - l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; - l2tp_tunnel->state_seconds = 0; + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } break; @@ -247,8 +322,7 @@ bbl_l2tp_tunnel_control_job (timer_s *timer) { case BBL_L2TP_TUNNEL_SEND_STOPCCN: case BBL_L2TP_TUNNEL_RCVD_STOPCCN: if(l2tp_tunnel->state_seconds > 5) { - l2tp_tunnel->state = BBL_L2TP_TUNNEL_TERMINATED; - l2tp_tunnel->state_seconds = 0; + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_TERMINATED); } break; case BBL_L2TP_TUNNEL_TERMINATED: @@ -438,6 +512,8 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s format_ipv4_address(&ipv4->src)); /* Init tunnel ... */ l2tp_tunnel = calloc(1, sizeof(bbl_l2tp_tunnel_t)); + ctx->l2tp_tunnels++; + if(ctx->l2tp_tunnels > ctx->l2tp_tunnels_max) ctx->l2tp_tunnels_max = ctx->l2tp_tunnels; CIRCLEQ_INIT(&l2tp_tunnel->txq_qhead); CIRCLEQ_INIT(&l2tp_tunnel->session_qhead); l2tp_tunnel->interface = interface; @@ -568,8 +644,7 @@ bbl_l2tp_scccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s LOG(ERROR, "L2TP Error (%s) Invalid SCCCN received from %s\n", l2tp_tunnel->server->host_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); - l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; - l2tp_tunnel->state_seconds = 0; + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); return bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } /* Check challenge response ... */ @@ -584,38 +659,29 @@ bbl_l2tp_scccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s LOG(ERROR, "L2TP Error (%s) Wrong challenge response in SCCCN from %s\n", l2tp_tunnel->server->host_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); - l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; - l2tp_tunnel->state_seconds = 0; + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); return bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } } else { LOG(ERROR, "L2TP Error (%s) Missing challenge response in SCCCN from %s\n", l2tp_tunnel->server->host_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); - l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; - l2tp_tunnel->state_seconds = 0; + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); return bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } } - l2tp_tunnel->state = BBL_L2TP_TUNNEL_ESTABLISHED; - l2tp_tunnel->state_seconds = 0; - LOG(L2TP, "L2TP Info (%s) Tunnel (%u) from %s (%s) estbalished\n", - l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, - l2tp_tunnel->peer_name, - format_ipv4_address(&l2tp_tunnel->peer_ip)); + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_ESTABLISHED); } } static void bbl_l2tp_stopccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_tunnel_t *l2tp_tunnel) { - bbl_ctx_s *ctx = interface->ctx; - UNUSED(ctx); UNUSED(eth); UNUSED(l2tp); + UNUSED(interface); - l2tp_tunnel->state = BBL_L2TP_TUNNEL_RCVD_STOPCCN; - l2tp_tunnel->state_seconds = 0; + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_RCVD_STOPCCN); } static void @@ -632,6 +698,8 @@ bbl_l2tp_icrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * } bbl_l2tp_session_t *l2tp_session = calloc(1, sizeof(bbl_l2tp_session_t)); + ctx->l2tp_sessions++; + if(ctx->l2tp_sessions > ctx->l2tp_sessions_max) ctx->l2tp_sessions_max = ctx->l2tp_sessions; l2tp_session->tunnel = l2tp_tunnel; l2tp_session->state = BBL_L2TP_SESSION_WAIT_CONN; @@ -914,6 +982,13 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ } } +/** + * bbl_l2tp_stop_all_tunnel + * + * This function greacefully teardowns all L2TP tunnels. + * + * @param ctx ... + */ void bbl_l2tp_stop_all_tunnel(bbl_ctx_s *ctx) { bbl_l2tp_server_t *l2tp_server = ctx->config.l2tp_server; @@ -921,28 +996,10 @@ bbl_l2tp_stop_all_tunnel(bbl_ctx_s *ctx) { while(l2tp_server) { CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_server->tunnel_qhead, tunnel_qnode) { if(l2tp_tunnel->state < BBL_L2TP_TUNNEL_SEND_STOPCCN) { - l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; - l2tp_tunnel->state_seconds = 0; + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } } l2tp_server = l2tp_server->next; } -} - -uint16_t -bbl_l2tp_tunnel_count(bbl_ctx_s *ctx) { - bbl_l2tp_server_t *l2tp_server = ctx->config.l2tp_server; - bbl_l2tp_tunnel_t *l2tp_tunnel; - uint16_t active = 0; - - while(l2tp_server) { - CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_server->tunnel_qhead, tunnel_qnode) { - active++; - } - l2tp_server = l2tp_server->next; - } - LOG(DEBUG, "TUNNEL %u\n", active); - return active; -} - +} \ No newline at end of file diff --git a/src/bbl_l2tp.h b/src/bbl_l2tp.h index 463f8417..94a51806 100644 --- a/src/bbl_l2tp.h +++ b/src/bbl_l2tp.h @@ -223,11 +223,11 @@ typedef struct bbl_l2tp_session_ } bbl_l2tp_session_t; -const char* -l2tp_message_string(l2tp_message_type type); +const char* l2tp_message_string(l2tp_message_type type); +const char* l2tp_tunnel_state_string(l2tp_tunnel_state_t state); +const char* l2tp_session_state_string(l2tp_session_state_t state); void bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface); void bbl_l2tp_stop_all_tunnel(bbl_ctx_s *ctx); -uint16_t bbl_l2tp_tunnel_count(bbl_ctx_s *ctx); #endif \ No newline at end of file diff --git a/src/bbl_stats.c b/src/bbl_stats.c index 6c3ddbd3..8c097480 100644 --- a/src/bbl_stats.c +++ b/src/bbl_stats.c @@ -165,6 +165,21 @@ bbl_stats_stdout (bbl_ctx_s *ctx, bbl_stats_t * stats) { printf("Flapped: %u\n", ctx->sessions_flapped); if(ctx->op.network_if) { + if(ctx->config.l2tp_server) { + printf("\nL2TP LNS Statistics:\n"); + printf(" Tunnels: %10u\n", ctx->l2tp_tunnels_max); + printf(" Established: %10u\n", ctx->l2tp_tunnels_established_max); + printf(" Sessions: %10u\n", ctx->l2tp_sessions_max); + printf(" Packets:\n"); + printf(" TX Control: %10u packets (%u retries)\n", + ctx->op.network_if->stats.l2tp_control_tx, ctx->op.network_if->stats.l2tp_control_retry); + printf(" RX Control: %10u packets (%u duplicate %u out-of-order)\n", + ctx->op.network_if->stats.l2tp_control_rx, + ctx->op.network_if->stats.l2tp_control_rx_dup, + ctx->op.network_if->stats.l2tp_control_rx_ooo); + printf(" TX Data: %10lu packets\n", ctx->op.network_if->stats.l2tp_data_tx); + printf(" RX Data: %10lu packets\n", ctx->op.network_if->stats.l2tp_data_rx); + } printf("\nNetwork Interface ( %s ):\n", ctx->op.network_if->name); printf(" TX: %10lu packets\n", ctx->op.network_if->stats.packets_tx); printf(" RX: %10lu packets\n", ctx->op.network_if->stats.packets_rx); @@ -296,6 +311,7 @@ bbl_stats_json (bbl_ctx_s *ctx, bbl_stats_t * stats) { json_t *jobj_array = NULL; json_t *jobj_access_if = NULL; json_t *jobj_network_if = NULL; + json_t *jobj_l2tp = NULL; json_t *jobj_straffic = NULL; json_t *jobj_multicast = NULL; json_t *jobj_protocols = NULL; @@ -319,6 +335,20 @@ bbl_stats_json (bbl_ctx_s *ctx, bbl_stats_t * stats) { jobj_array = json_array(); if (ctx->op.network_if) { + if(ctx->config.l2tp_server) { + jobj_l2tp = json_object(); + json_object_set(jobj_l2tp, "tunnels", json_integer(ctx->l2tp_tunnels_max)); + json_object_set(jobj_l2tp, "tunnels-established", json_integer(ctx->l2tp_tunnels_established_max)); + json_object_set(jobj_l2tp, "sessions", json_integer(ctx->l2tp_sessions_max)); + json_object_set(jobj_l2tp, "tx-control-packets", json_integer(ctx->op.network_if->stats.l2tp_control_tx)); + json_object_set(jobj_l2tp, "tx-control-packets-retry", json_integer(ctx->op.network_if->stats.l2tp_control_retry)); + json_object_set(jobj_l2tp, "rx-control-packets", json_integer(ctx->op.network_if->stats.l2tp_control_rx)); + json_object_set(jobj_l2tp, "rx-control-packets-duplicate", json_integer(ctx->op.network_if->stats.l2tp_control_rx_dup)); + json_object_set(jobj_l2tp, "rx-control-packets-out-of-order", json_integer(ctx->op.network_if->stats.l2tp_control_rx_ooo)); + json_object_set(jobj_l2tp, "tx-data-packets", json_integer(ctx->op.network_if->stats.l2tp_data_tx)); + json_object_set(jobj_l2tp, "rx-data-packets", json_integer(ctx->op.network_if->stats.l2tp_data_rx)); + json_object_set(jobj, "l2tp", jobj_l2tp); + } jobj_network_if = json_object(); json_object_set(jobj_network_if, "name", json_string(ctx->op.network_if->name)); json_object_set(jobj_network_if, "tx-packets", json_integer(ctx->op.network_if->stats.packets_tx)); @@ -506,12 +536,16 @@ bbl_compute_interface_rate_job (timer_s *timer) bbl_compute_avg_rate(&interface->stats.rate_packets_tx, interface->stats.packets_tx); bbl_compute_avg_rate(&interface->stats.rate_packets_rx, interface->stats.packets_rx); - bbl_compute_avg_rate(&interface->stats.rate_mc_tx, interface->stats.mc_tx); - bbl_compute_avg_rate(&interface->stats.rate_mc_rx, interface->stats.mc_rx); bbl_compute_avg_rate(&interface->stats.rate_session_ipv4_tx, interface->stats.session_ipv4_tx); bbl_compute_avg_rate(&interface->stats.rate_session_ipv4_rx, interface->stats.session_ipv4_rx); bbl_compute_avg_rate(&interface->stats.rate_session_ipv6_tx, interface->stats.session_ipv6_tx); bbl_compute_avg_rate(&interface->stats.rate_session_ipv6_rx, interface->stats.session_ipv6_rx); bbl_compute_avg_rate(&interface->stats.rate_session_ipv6pd_tx, interface->stats.session_ipv6pd_tx); bbl_compute_avg_rate(&interface->stats.rate_session_ipv6pd_rx, interface->stats.session_ipv6pd_rx); + bbl_compute_avg_rate(&interface->stats.rate_mc_rx, interface->stats.mc_rx); + if(!interface->access) { + bbl_compute_avg_rate(&interface->stats.rate_mc_tx, interface->stats.mc_tx); + bbl_compute_avg_rate(&interface->stats.rate_l2tp_data_rx, interface->stats.l2tp_data_rx); + bbl_compute_avg_rate(&interface->stats.rate_l2tp_data_tx, interface->stats.l2tp_data_tx); + } } From 67a40494ac68374ada6df0a2becb1447bca48ea4 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Fri, 19 Feb 2021 13:40:40 +0100 Subject: [PATCH 05/27] #3 L2TPv2 LNS Support (WIP) - 5 --- src/bbl.h | 5 +- src/bbl_config.c | 14 ++++++ src/bbl_l2tp.c | 125 ++++++++++++++++++++++++++++++++++++++--------- src/bbl_l2tp.h | 15 ++++-- 4 files changed, 131 insertions(+), 28 deletions(-) diff --git a/src/bbl.h b/src/bbl.h index 3f432bb3..96b422a7 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -262,8 +262,9 @@ typedef struct bbl_interface_ uint64_t session_ipv6pd_wrong_session; uint32_t l2tp_control_rx; - uint32_t l2tp_control_rx_dup; - uint32_t l2tp_control_rx_ooo; + uint32_t l2tp_control_rx_dup; /* duplicate */ + uint32_t l2tp_control_rx_ooo; /* out of order */ + uint32_t l2tp_control_rx_nf; /* session not found */ uint32_t l2tp_control_tx; uint32_t l2tp_control_retry; uint64_t l2tp_data_rx; diff --git a/src/bbl_config.c b/src/bbl_config.c index 74f2faad..ebade12f 100644 --- a/src/bbl_config.c +++ b/src/bbl_config.c @@ -713,6 +713,20 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { } else { l2tp_server->max_retry = 30; } + if (json_unpack(sub, "{s:s}", "congestion-mode", &s) == 0) { + if (strcmp(s, "default") == 0) { + l2tp_server->congestion_mode = BBL_L2TP_CONGESTION_DEFAULT; + } else if (strcmp(s, "slow") == 0) { + l2tp_server->congestion_mode = BBL_L2TP_CONGESTION_SLOW; + } else if (strcmp(s, "aggressive") == 0) { + l2tp_server->congestion_mode = BBL_L2TP_CONGESTION_AGGRESSIVE; + } else { + fprintf(stderr, "Config error: Invalid value for l2tp-server->congestion-mode\n"); + return false; + } + } else { + l2tp_server->congestion_mode = BBL_L2TP_CONGESTION_DEFAULT; + } } } else if (json_is_object(sub)) { fprintf(stderr, "JSON config error: List expected in L2TP server configuration but dictionary found\n"); diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 156ae33e..755cf845 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -253,7 +253,7 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { if(q->last_tx_time.tv_sec) { timespec_sub(&time_diff, ×tamp, &q->last_tx_time); time_diff_ms = round(time_diff.tv_nsec / 1.0e6) * (time_diff.tv_sec * 1000); - if(time_diff_ms < 1000) { + if(time_diff_ms < (q->retries * 1000)) { q = CIRCLEQ_NEXT(q, txq_qnode); continue; } @@ -273,6 +273,14 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } + /* When congestion occurs (indicated by the triggering of a + * retransmission) one half of the congestion window (CWND) + * is saved in SSTHRESH, and CWND is set to one. The sender + * then reenters the slow start phase. */ + l2tp_tunnel->ssthresh = l2tp_tunnel->cwnd/2; + if(!l2tp_tunnel->ssthresh) l2tp_tunnel->ssthresh = 1; + l2tp_tunnel->cwnd = 1; + l2tp_tunnel->cwcount = 0; } q->retries++; q = CIRCLEQ_NEXT(q, txq_qnode); @@ -332,7 +340,7 @@ bbl_l2tp_tunnel_control_job (timer_s *timer) { break; } if(!l2tp_tunnel->timer_tx_active) { - timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, 10 * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); + timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, L2TP_TX_WAIT_MS * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); l2tp_tunnel->timer_tx_active = true; } } @@ -396,12 +404,7 @@ bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, q->nr_offset = 52; } if(l2tp_type != L2TP_MESSAGE_ZLB) { - if(l2tp_tunnel->initial_packet_send) { - l2tp_tunnel->ns++; - } else{ - l2tp_tunnel->initial_packet_send = true; - } - l2tp.ns = l2tp_tunnel->ns; + l2tp.ns = l2tp_tunnel->ns++; bbl_l2tp_avp_encode_attributes(l2tp_tunnel, l2tp_session, l2tp_type, sp, &sp_len); l2tp.payload = sp; l2tp.payload_len = sp_len; @@ -421,7 +424,7 @@ bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, } else { CIRCLEQ_INSERT_TAIL(&l2tp_tunnel->txq_qhead, q, txq_qnode); if(!l2tp_tunnel->timer_tx_active) { - timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, 10 * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); + timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, L2TP_TX_WAIT_MS * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); l2tp_tunnel->timer_tx_active = true; } } @@ -742,6 +745,10 @@ bbl_l2tp_iccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * UNUSED(eth); UNUSED(l2tp); + if(!l2tp_session) { + return; + } + if(!bbl_l2tp_avp_decode_session(l2tp, l2tp_tunnel, l2tp_session)) { bbl_l2tp_send(l2tp_tunnel, l2tp_session, L2TP_MESSAGE_CDN); return bbl_l2tp_session_delete(l2tp_session); @@ -765,6 +772,10 @@ bbl_l2tp_cdn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *i UNUSED(eth); UNUSED(l2tp); + if(!l2tp_session) { + return; + } + bbl_l2tp_avp_decode_session(l2tp, l2tp_tunnel, l2tp_session); l2tp_session->state = BBL_L2TP_SESSION_TERMINATED; @@ -913,15 +924,26 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ key.tunnel_id = l2tp->tunnel_id; key.session_id = l2tp->session_id; search = dict_search(ctx->l2tp_session_dict, &key); + if(!search && l2tp->type && key.session_id != 0) { + /* Try with session zero (tunnel session) in case + * the corresponding session was already deleted. + * This is required for reliable delivery of control + * messages. */ + key.session_id = 0; + search = dict_search(ctx->l2tp_session_dict, &key); + } if(search) { l2tp_session = *search; l2tp_tunnel = l2tp_session->tunnel; - if(l2tp->type == L2TP_MESSAGE_DATA) { + /* L2TP Data Packet */ l2tp_tunnel->stats.data_rx++; interface->stats.l2tp_data_rx++; return bbl_l2tp_data_rx(eth, l2tp, interface, l2tp_session); } + /* L2TP Control Packet */ + l2tp_tunnel->stats.control_rx++; + interface->stats.l2tp_control_rx++; if (L2TP_SEQ_GT(l2tp->nr, l2tp_tunnel->peer_nr)) { l2tp_tunnel->peer_nr = l2tp->nr; } @@ -931,22 +953,58 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ l2tp_tunnel->server->host_name, l2tp_message_string(l2tp->type), format_ipv4_address(&ipv4->src)); - /* Update tunnel */ l2tp_tunnel->peer_ns = l2tp->ns; - l2tp_tunnel->nr = (l2tp->ns + 1); if(l2tp->type != L2TP_MESSAGE_ZLB) { + l2tp_tunnel->nr = (l2tp->ns + 1); l2tp_tunnel->zlb = true; + /* Start tx timer */ + if(!l2tp_tunnel->timer_tx_active) { + timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, L2TP_TX_WAIT_MS * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); + l2tp_tunnel->timer_tx_active = true; + } } - if(l2tp_tunnel->cwnd < l2tp_tunnel->peer_receive_window) { - l2tp_tunnel->cwnd++; - } - l2tp_tunnel->stats.control_rx++; - interface->stats.l2tp_control_rx++; - if(!l2tp_tunnel->timer_tx_active) { - timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, 10 * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); - l2tp_tunnel->timer_tx_active = true; + /* Reliable Delivery of Control Messages */ + switch (l2tp_tunnel->server->congestion_mode) { + case BBL_L2TP_CONGESTION_AGGRESSIVE: + l2tp_tunnel->cwnd = l2tp_tunnel->peer_receive_window; + break; + case BBL_L2TP_CONGESTION_SLOW: + l2tp_tunnel->cwnd = 1; + break; + default: + /* Adjust tunnel congestion window as defined + * in RFC 2661 Appendix A */ + if(l2tp_tunnel->cwnd < l2tp_tunnel->peer_receive_window) { + if(l2tp_tunnel->cwnd < l2tp_tunnel->ssthresh) { + /* Slow Start Phase + * + * The congestion window (CWND) increases by 1 + * for every new ACK received resulting in an + * exponential increase. + */ + l2tp_tunnel->cwcount = 0; + l2tp_tunnel->cwnd++; + } else { + /* Congestion Avoidance Phase + * + * The congestion window (CWND) increases by 1/CWND + * for every new ACK received. resulting in an + * linear increase. The variable cwcount is used + * track when to increment the congestion window. + */ + l2tp_tunnel->cwcount++; + if(l2tp_tunnel->cwcount >= l2tp_tunnel->cwnd) { + l2tp_tunnel->cwcount = 0; + l2tp_tunnel->cwnd++; + } + } + } else { + l2tp_tunnel->ssthresh = l2tp_tunnel->peer_receive_window; + } + break; } + /* Handle received packet */ if(l2tp_tunnel->state != BBL_L2TP_TUNNEL_TERMINATED) { switch (l2tp->type) { case L2TP_MESSAGE_SCCCN: @@ -956,9 +1014,15 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ case L2TP_MESSAGE_ICRQ: return bbl_l2tp_icrq_rx(eth, l2tp, interface, l2tp_tunnel); case L2TP_MESSAGE_ICCN: - return bbl_l2tp_iccn_rx(eth, l2tp, interface, l2tp_session); + if(l2tp_session->key.session_id) { + return bbl_l2tp_iccn_rx(eth, l2tp, interface, l2tp_session); + } + break; case L2TP_MESSAGE_CDN: - return bbl_l2tp_cdn_rx(eth, l2tp, interface, l2tp_session); + if(l2tp_session->key.session_id) { + return bbl_l2tp_cdn_rx(eth, l2tp, interface, l2tp_session); + } + break; default: break; } @@ -966,19 +1030,34 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ } else { if (L2TP_SEQ_LT(l2tp->ns, l2tp_tunnel->nr)) { /* Duplicate packet received */ + LOG(DEBUG, "L2TP Debug (%s) Duplicate %s received with Ns. %u (expected %u) from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + l2tp->ns, l2tp_tunnel->nr, + format_ipv4_address(&ipv4->src)); + l2tp_tunnel->zlb = true; l2tp_tunnel->stats.control_rx_dup++; interface->stats.l2tp_control_rx_dup++; if(!l2tp_tunnel->timer_tx_active) { - timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, 10 * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); + timer_add(&ctx->timer_root, &l2tp_tunnel->timer_tx, "L2TP TX", 0, L2TP_TX_WAIT_MS * MSEC, l2tp_tunnel, bbl_l2tp_tunnel_tx_job); l2tp_tunnel->timer_tx_active = true; } } else { /* Out-of-Order packet received */ + LOG(DEBUG, "L2TP Debug (%s) Out-of-Order %s received with Ns. %u (expected %u) from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + l2tp->ns, l2tp_tunnel->nr, + format_ipv4_address(&ipv4->src)); + l2tp_tunnel->stats.control_rx_ooo++; interface->stats.l2tp_control_rx_ooo++; } } + } else { + /* Corresponding tunnel or session not found */ + interface->stats.l2tp_control_rx_nf++; } } diff --git a/src/bbl_l2tp.h b/src/bbl_l2tp.h index 94a51806..7a3b4f33 100644 --- a/src/bbl_l2tp.h +++ b/src/bbl_l2tp.h @@ -15,7 +15,7 @@ #define L2TP_IPCP_IP_LOCAL 168495882 #define L2TP_IPCP_IP_REMOTE 168430090 - +#define L2TP_TX_WAIT_MS 10 #define L2TP_SEQ_LT(_a, _b)\ (((_a) < (_b) && (_b) - (_a) < 32768) || ((_a) > (_b) && (_a) - (_b) > 32768)) @@ -46,6 +46,14 @@ typedef enum { BBL_L2TP_SESSION_MAX } __attribute__ ((__packed__)) l2tp_session_state_t; +typedef enum { + BBL_L2TP_CONGESTION_DEFAULT = 0, + BBL_L2TP_CONGESTION_SLOW = 1, + BBL_L2TP_CONGESTION_AGGRESSIVE = 2, + BBL_L2TP_CONGESTION_MAX +} l2tp_congestion_mode_t; + + /* L2TP Server Configuration (LNS) */ typedef struct bbl_l2tp_server_ { @@ -56,6 +64,9 @@ typedef struct bbl_l2tp_server_ uint16_t session_limit; uint16_t receive_window; uint16_t max_retry; + + l2tp_congestion_mode_t congestion_mode; + char *secret; char *host_name; @@ -120,8 +131,6 @@ typedef struct bbl_l2tp_tunnel_ uint16_t peer_tunnel_id; uint16_t next_session_id; - bool initial_packet_send; - uint16_t ns; uint16_t nr; uint16_t peer_ns; From b0c3d17442b29f4860ce29426bd0f9d7b9c02a6f Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Fri, 19 Feb 2021 17:10:16 +0100 Subject: [PATCH 06/27] #3 L2TPv2 LNS Support (WIP) - 6 L2TP TX queue memory enhancements. --- src/bbl_l2tp.c | 10 +--------- src/bbl_l2tp.h | 2 +- src/bbl_tx.c | 1 - 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 755cf845..5fac6234 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -131,7 +131,7 @@ bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_t *l2tp_tunnel) { /* Delete timer */ timer_del(l2tp_tunnel->timer_tx); - timer_del(l2tp_tunnel->timer_ctrl); + /* Delete all remaining sessions */ while (!CIRCLEQ_EMPTY(&l2tp_tunnel->session_qhead)) { bbl_l2tp_session_delete(CIRCLEQ_FIRST(&l2tp_tunnel->session_qhead)); @@ -150,11 +150,9 @@ bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_t *l2tp_tunnel) { CIRCLEQ_REMOVE(&interface->l2tp_tx_qhead, q, tx_qnode); CIRCLEQ_NEXT(q, tx_qnode) = NULL; } - free(q->packet); free(q); } if(l2tp_tunnel->zlb_qnode) { - free(l2tp_tunnel->zlb_qnode->packet); free(l2tp_tunnel->zlb_qnode); } /* Free tunnel memory */ @@ -245,7 +243,6 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { q_del = q; q = CIRCLEQ_NEXT(q, txq_qnode); CIRCLEQ_REMOVE(&l2tp_tunnel->txq_qhead, q_del, txq_qnode); - free(q_del->packet); free(q_del); continue; } @@ -411,12 +408,10 @@ bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, } q->ns = l2tp.ns; q->tunnel = l2tp_tunnel; - q->packet = malloc(L2TP_MAX_PACKET_SIZE); if(encode_ethernet(q->packet, &len, ð) == PROTOCOL_SUCCESS) { q->packet_len = len; if(l2tp_type == L2TP_MESSAGE_ZLB) { if(l2tp_tunnel->zlb_qnode) { - free(q->packet); free(q); } else { l2tp_tunnel->zlb_qnode = q; @@ -431,7 +426,6 @@ bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, } else { /* Encode error.... */ LOG(ERROR, "L2TP Encode Error!\n"); - free(q->packet); free(q); } } @@ -477,7 +471,6 @@ bbl_l2tp_send_data(bbl_l2tp_session_t *l2tp_session, uint16_t protocol, void *ne l2tp.protocol = protocol; l2tp.next = next; q->data = true; - q->packet = malloc(L2TP_MAX_PACKET_SIZE); if(encode_ethernet(q->packet, &len, ð) == PROTOCOL_SUCCESS) { q->packet_len = len; CIRCLEQ_INSERT_TAIL(&interface->l2tp_tx_qhead, q, tx_qnode); @@ -486,7 +479,6 @@ bbl_l2tp_send_data(bbl_l2tp_session_t *l2tp_session, uint16_t protocol, void *ne } else { /* Encode error.... */ LOG(ERROR, "L2TP Data Encode Error!\n"); - free(q->packet); free(q); } } diff --git a/src/bbl_l2tp.h b/src/bbl_l2tp.h index 7a3b4f33..b6b769e9 100644 --- a/src/bbl_l2tp.h +++ b/src/bbl_l2tp.h @@ -93,7 +93,7 @@ typedef struct bbl_l2tp_queue_ uint8_t ns_offset; uint8_t nr_offset; uint8_t retries; - uint8_t *packet; + uint8_t packet[L2TP_MAX_PACKET_SIZE]; uint16_t packet_len; struct timespec last_tx_time; struct bbl_l2tp_tunnel_ *tunnel; diff --git a/src/bbl_tx.c b/src/bbl_tx.c index 69b04bb9..08c89259 100644 --- a/src/bbl_tx.c +++ b/src/bbl_tx.c @@ -1525,7 +1525,6 @@ bbl_tx_job (timer_s *timer) tphdr->tp_len, interface->pcap_index, PCAPNG_EPB_FLAGS_OUTBOUND); } if(q->data) { - free(q->packet); free(q); } } From 9c68f836244fbd785d21382b0ccf973c836b1c1c Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Fri, 19 Feb 2021 17:45:18 +0100 Subject: [PATCH 07/27] #3 L2TPv2 LNS Support (WIP) - 7 Fix L2TP teardown... --- src/bbl_l2tp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 5fac6234..77661118 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -324,12 +324,16 @@ bbl_l2tp_tunnel_control_job (timer_s *timer) { } } break; - case BBL_L2TP_TUNNEL_SEND_STOPCCN: case BBL_L2TP_TUNNEL_RCVD_STOPCCN: if(l2tp_tunnel->state_seconds > 5) { bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_TERMINATED); } break; + case BBL_L2TP_TUNNEL_SEND_STOPCCN: + if(l2tp_tunnel->state_seconds > 30) { + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_TERMINATED); + } + break; case BBL_L2TP_TUNNEL_TERMINATED: timer->periodic = false; return bbl_l2tp_tunnel_delete(l2tp_tunnel); From 3331ea610ec5561c2fae5e48772fef987861ae89 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Fri, 19 Feb 2021 19:03:50 +0100 Subject: [PATCH 08/27] #3 L2TPv2 LNS Support (WIP) - 8 Fix tunnel TX job --- src/bbl_l2tp.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 77661118..3234ac9d 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -122,9 +122,10 @@ bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_t *l2tp_tunnel) { bbl_ctx_s *ctx; if(l2tp_tunnel) { - LOG(DEBUG, "L2TP Debug (%s) Tunnel %u deleted\n", - l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id); - + if(l2tp_tunnel->tunnel_id) { + LOG(DEBUG, "L2TP Debug (%s) Tunnel %u deleted\n", + l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id); + } interface = l2tp_tunnel->interface; ctx = interface->ctx; if(ctx->l2tp_tunnels) ctx->l2tp_tunnels--; @@ -224,10 +225,6 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { uint16_t max_ns = l2tp_tunnel->peer_nr + l2tp_tunnel->cwnd; l2tp_tunnel->timer_tx_active = false; - if(!CIRCLEQ_EMPTY(&interface->l2tp_tx_qhead)) { - return; - } - if(l2tp_tunnel->state == BBL_L2TP_TUNNEL_SEND_STOPCCN) { if(CIRCLEQ_EMPTY(&l2tp_tunnel->txq_qhead)) { bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_TERMINATED); @@ -243,6 +240,9 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { q_del = q; q = CIRCLEQ_NEXT(q, txq_qnode); CIRCLEQ_REMOVE(&l2tp_tunnel->txq_qhead, q_del, txq_qnode); + if(CIRCLEQ_NEXT(q_del, tx_qnode)) { + CIRCLEQ_REMOVE(&interface->l2tp_tx_qhead, q_del, tx_qnode); + } free(q_del); continue; } @@ -512,7 +512,6 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s /* Init tunnel ... */ l2tp_tunnel = calloc(1, sizeof(bbl_l2tp_tunnel_t)); ctx->l2tp_tunnels++; - if(ctx->l2tp_tunnels > ctx->l2tp_tunnels_max) ctx->l2tp_tunnels_max = ctx->l2tp_tunnels; CIRCLEQ_INIT(&l2tp_tunnel->txq_qhead); CIRCLEQ_INIT(&l2tp_tunnel->session_qhead); l2tp_tunnel->interface = interface; @@ -546,7 +545,6 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s return bbl_l2tp_tunnel_delete(l2tp_tunnel); } } - /* Add dummy tunnel session, this session is only used * to search for tunnel using the same dictionary. */ l2tp_session = calloc(1, sizeof(bbl_l2tp_session_t)); @@ -576,6 +574,7 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s } *result.datum_ptr = l2tp_session; CIRCLEQ_INSERT_TAIL(&l2tp_tunnel->session_qhead, l2tp_session, session_qnode); + if(ctx->l2tp_tunnels > ctx->l2tp_tunnels_max) ctx->l2tp_tunnels_max = ctx->l2tp_tunnels; /* L2TP Challenge/Response */ if(l2tp_server->secret) { l2tp_tunnel->challenge = malloc(L2TP_MD5_DIGEST_LEN); @@ -698,7 +697,6 @@ bbl_l2tp_icrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * bbl_l2tp_session_t *l2tp_session = calloc(1, sizeof(bbl_l2tp_session_t)); ctx->l2tp_sessions++; - if(ctx->l2tp_sessions > ctx->l2tp_sessions_max) ctx->l2tp_sessions_max = ctx->l2tp_sessions; l2tp_session->tunnel = l2tp_tunnel; l2tp_session->state = BBL_L2TP_SESSION_WAIT_CONN; @@ -729,6 +727,7 @@ bbl_l2tp_icrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * } *result.datum_ptr = l2tp_session; CIRCLEQ_INSERT_TAIL(&l2tp_tunnel->session_qhead, l2tp_session, session_qnode); + if(ctx->l2tp_sessions > ctx->l2tp_sessions_max) ctx->l2tp_sessions_max = ctx->l2tp_sessions; bbl_l2tp_send(l2tp_tunnel, l2tp_session, L2TP_MESSAGE_ICRP); } From 8cad4ae001278b1a4bee6cbcb0410181e7d25bf7 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Fri, 19 Feb 2021 21:23:54 +0100 Subject: [PATCH 09/27] #3 L2TPv2 LNS Support (WIP) - 9 Fix PPP IP6CP (LNS) --- src/bbl_l2tp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 3234ac9d..75b1ff1a 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -872,9 +872,9 @@ bbl_l2tp_data_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * ip6cp_tx.code = PPP_CODE_CONF_REQUEST; ip6cp_tx.identifier = 1; ip6cp_tx.ipv6_identifier = 1; - bbl_l2tp_send_data(l2tp_session, PROTOCOL_IPCP, &ip6cp_tx); + bbl_l2tp_send_data(l2tp_session, PROTOCOL_IP6CP, &ip6cp_tx); } - bbl_l2tp_send_data(l2tp_session, PROTOCOL_IPCP, ip6cp_rx); + bbl_l2tp_send_data(l2tp_session, PROTOCOL_IP6CP, ip6cp_rx); } else if (ip6cp_rx->code == PPP_CODE_CONF_ACK) { if(l2tp_session->ip6cp_state == BBL_PPP_PEER_ACK) { l2tp_session->ip6cp_state = BBL_PPP_OPENED; @@ -883,7 +883,7 @@ bbl_l2tp_data_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * ip6cp_tx.code = PPP_CODE_CONF_REQUEST; ip6cp_tx.identifier = 1; ip6cp_tx.ipv6_identifier = 1; - bbl_l2tp_send_data(l2tp_session, PROTOCOL_IPCP, &ip6cp_tx); + bbl_l2tp_send_data(l2tp_session, PROTOCOL_IP6CP, &ip6cp_tx); } } break; From b6618b7616a9150cb39650fc8aa37097b7416120 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Mon, 22 Feb 2021 21:47:44 +0100 Subject: [PATCH 10/27] #3 L2TPv2 LNS Support (WIP) - 10 + add chap support + add PAP/CHAP reply message + recognice L2TP using reply message + ... --- src/bbl.h | 3 ++ src/bbl_l2tp.c | 13 +++++ src/bbl_l2tp.h | 2 + src/bbl_protocols.c | 112 ++++++++++++++++++++++++++++++++++---------- src/bbl_protocols.h | 4 ++ src/bbl_rx.c | 76 ++++++++++-------------------- src/bbl_rx.h | 3 -- src/bbl_tx.c | 42 +++++++++++++++++ 8 files changed, 176 insertions(+), 79 deletions(-) diff --git a/src/bbl.h b/src/bbl.h index 96b422a7..5d770098 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -629,6 +629,9 @@ typedef struct bbl_session_ bbl_access_type_t access_type; uint16_t access_third_vlan; + /* Set to true if session is tunnelled via L2TP. */ + bool l2tp; + /* Authentication */ char username[USERNAME_LEN]; char password[PASSWORD_LEN]; diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 75b1ff1a..08b7243d 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -790,6 +790,8 @@ bbl_l2tp_data_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * bbl_lcp_t *lcp_rx; bbl_pap_t *pap_rx; bbl_pap_t pap_tx; + bbl_chap_t *chap_rx; + bbl_chap_t chap_tx; bbl_ipcp_t *ipcp_rx; bbl_ipcp_t ipcp_tx; bbl_ip6cp_t *ip6cp_rx; @@ -819,8 +821,19 @@ bbl_l2tp_data_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * pap_rx = (bbl_pap_t*)l2tp->next; pap_tx.code = PAP_CODE_ACK; pap_tx.identifier = pap_rx->identifier; + pap_tx.reply_message = (void*)L2TP_REPLY_MESSAGE; + pap_tx.reply_message_len = sizeof(L2TP_REPLY_MESSAGE) - 1; bbl_l2tp_send_data(l2tp_session, PROTOCOL_PAP, &pap_tx); break; + case PROTOCOL_CHAP: + memset(&chap_tx, 0x0, sizeof(bbl_chap_t)); + chap_rx = (bbl_chap_t*)l2tp->next; + chap_tx.code = CHAP_CODE_SUCCESS; + chap_tx.identifier = chap_rx->identifier; + chap_tx.reply_message = (void*)L2TP_REPLY_MESSAGE; + chap_tx.reply_message_len = sizeof(L2TP_REPLY_MESSAGE) - 1; + bbl_l2tp_send_data(l2tp_session, PROTOCOL_CHAP, &chap_tx); + break; case PROTOCOL_IPCP: ipcp_rx = (bbl_ipcp_t*)l2tp->next; memset(&ipcp_tx, 0x0, sizeof(bbl_ipcp_t)); diff --git a/src/bbl_l2tp.h b/src/bbl_l2tp.h index b6b769e9..537c5d0d 100644 --- a/src/bbl_l2tp.h +++ b/src/bbl_l2tp.h @@ -17,6 +17,8 @@ #define L2TP_IPCP_IP_REMOTE 168430090 #define L2TP_TX_WAIT_MS 10 +#define L2TP_REPLY_MESSAGE "BNG Blaster L2TP LNS" + #define L2TP_SEQ_LT(_a, _b)\ (((_a) < (_b) && (_b) - (_a) < 32768) || ((_a) > (_b) && (_a) - (_b) > 32768)) diff --git a/src/bbl_protocols.c b/src/bbl_protocols.c index 10b4bf1b..68a16e99 100644 --- a/src/bbl_protocols.c +++ b/src/bbl_protocols.c @@ -601,42 +601,67 @@ protocol_error_t encode_ppp_pap(uint8_t *buf, uint *len, bbl_pap_t *pap) { + uint16_t *pap_len_field = NULL; + uint16_t pap_len = *len; + *buf = pap->code; BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); *buf = pap->identifier; BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - *buf = 6 + pap->username_len + pap->password_len; + pap_len_field = (uint16_t*)buf; BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); - *buf = pap->username_len; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - memcpy(buf, pap->username, pap->username_len); - BUMP_WRITE_BUFFER(buf, len, pap->username_len); - *buf = pap->password_len; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - memcpy(buf, pap->password, pap->password_len); - BUMP_WRITE_BUFFER(buf, len, pap->password_len); + if(pap->username) { + *buf = pap->username_len; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + memcpy(buf, pap->username, pap->username_len); + BUMP_WRITE_BUFFER(buf, len, pap->username_len); + } + if(pap->password) { + *buf = pap->password_len; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + memcpy(buf, pap->password, pap->password_len); + BUMP_WRITE_BUFFER(buf, len, pap->password_len); + } + if(pap->reply_message) { + *buf = pap->reply_message_len; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + memcpy(buf, pap->reply_message, pap->reply_message_len); + BUMP_WRITE_BUFFER(buf, len, pap->reply_message_len); + } + pap_len = *len - pap_len; + *pap_len_field = htobe16(pap_len); return PROTOCOL_SUCCESS; } protocol_error_t encode_ppp_chap(uint8_t *buf, uint *len, bbl_chap_t *chap) { - uint16_t chap_len; - chap_len = 5 + chap->challenge_len + chap->name_len; + uint16_t *chap_len_field = NULL; + uint16_t chap_len = *len; *buf = chap->code; BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); *buf = chap->identifier; BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - *(uint16_t*)buf = htobe16(chap_len); + chap_len_field = (uint16_t*)buf; BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); - *buf = chap->challenge_len; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - memcpy(buf, chap->challenge, chap->challenge_len); - BUMP_WRITE_BUFFER(buf, len, chap->challenge_len); - memcpy(buf, chap->name, chap->name_len); - BUMP_WRITE_BUFFER(buf, len, chap->name_len); + if(chap->challenge) { + *buf = chap->challenge_len; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + memcpy(buf, chap->challenge, chap->challenge_len); + BUMP_WRITE_BUFFER(buf, len, chap->challenge_len); + } + if(chap->name) { + memcpy(buf, chap->name, chap->name_len); + BUMP_WRITE_BUFFER(buf, len, chap->name_len); + } + if(chap->reply_message) { + memcpy(buf, chap->reply_message, chap->reply_message_len); + BUMP_WRITE_BUFFER(buf, len, chap->reply_message_len); + } + chap_len = *len - chap_len; + *chap_len_field = htobe16(chap_len); return PROTOCOL_SUCCESS; } @@ -1598,6 +1623,7 @@ decode_ppp_pap(uint8_t *buf, uint len, bbl_pap_t **ppp_pap) { bbl_pap_t *pap; + uint16_t pap_len; if(len < 4 || sp_len < sizeof(bbl_pap_t)) { return DECODE_ERROR; @@ -1608,7 +1634,33 @@ decode_ppp_pap(uint8_t *buf, uint len, memset(pap, 0x0, sizeof(bbl_pap_t)); pap->code = *buf; - + pap->identifier = *(buf+1); + pap_len = be16toh(*(uint16_t*)(buf+2)); + if(pap->code == PAP_CODE_REQUEST) { + if(pap_len < 6 || pap_len > len) { + return DECODE_ERROR; + } + BUMP_BUFFER(buf, len, sizeof(uint32_t)); + pap->username_len = *buf; + BUMP_BUFFER(buf, len, sizeof(uint8_t)); + pap->username = (char*)buf; + BUMP_BUFFER(buf, len, pap->username_len); + pap->password_len = *buf; + BUMP_BUFFER(buf, len, sizeof(uint8_t)); + pap->password = (char*)buf; + BUMP_BUFFER(buf, len, pap->password_len); + } else { + if(pap_len < 5 || pap_len > len) { + return DECODE_ERROR; + } + BUMP_BUFFER(buf, len, sizeof(uint32_t)); + pap->reply_message_len = *buf; + BUMP_BUFFER(buf, len, sizeof(uint8_t)); + if(pap->reply_message_len) { + pap->reply_message = (char*)buf; + BUMP_BUFFER(buf, len, pap->reply_message_len); + } + } *ppp_pap = pap; return PROTOCOL_SUCCESS; } @@ -1632,14 +1684,12 @@ decode_ppp_chap(uint8_t *buf, uint len, chap->code = *buf; chap->identifier = *(buf+1); chap_len = be16toh(*(uint16_t*)(buf+2)); - - if(chap_len < 4 || chap_len > len) { - return DECODE_ERROR; - - } - BUMP_BUFFER(buf, len, sizeof(uint32_t)); - if(chap->code == CHAP_CODE_CHALLENGE) { + if(chap_len < 5 || chap_len > len) { + return DECODE_ERROR; + } + BUMP_BUFFER(buf, len, sizeof(uint32_t)); + chap->challenge_len = *buf; if(chap->challenge_len > len) { return DECODE_ERROR; @@ -1647,6 +1697,16 @@ decode_ppp_chap(uint8_t *buf, uint len, BUMP_BUFFER(buf, len, sizeof(uint8_t)); chap->challenge = buf; BUMP_BUFFER(buf, len, chap->challenge_len); + } else { + if(chap_len < 4 || chap_len > len) { + return DECODE_ERROR; + } + BUMP_BUFFER(buf, len, sizeof(uint32_t)); + chap->reply_message_len = chap_len - 4; + if(chap->reply_message_len) { + chap->reply_message = (char*)buf; + BUMP_BUFFER(buf, len, chap->reply_message_len); + } } *ppp_chap = chap; return PROTOCOL_SUCCESS; diff --git a/src/bbl_protocols.h b/src/bbl_protocols.h index 0408d66b..9cdddf8f 100644 --- a/src/bbl_protocols.h +++ b/src/bbl_protocols.h @@ -428,6 +428,8 @@ typedef struct bbl_ppp_pap_ { uint8_t username_len; char *password; uint8_t password_len; + char *reply_message; + uint8_t reply_message_len; } bbl_pap_t; /* @@ -440,6 +442,8 @@ typedef struct bbl_ppp_chap_ { uint8_t name_len; uint8_t *challenge; uint8_t challenge_len; + char *reply_message; + uint8_t reply_message_len; } bbl_chap_t; /* diff --git a/src/bbl_rx.c b/src/bbl_rx.c index 67f3c07d..a5e58ca3 100644 --- a/src/bbl_rx.c +++ b/src/bbl_rx.c @@ -564,48 +564,6 @@ bbl_igmp_initial_join(timer_s *timer) } } -void -bbl_igmp_timeout(timer_s *timer) -{ - bbl_session_s *session = timer->data; - bbl_igmp_group_s *group = NULL; - int i; - bool send = false; - - if(session->access_type == ACCESS_TYPE_PPPOE) { - if(session->session_state != BBL_ESTABLISHED || - session->ipcp_state != BBL_PPP_OPENED) { - return; - } - } - - for(i=0; i < IGMP_MAX_GROUPS; i++) { - group = &session->igmp_groups[i]; - if(group->state == IGMP_GROUP_JOINING) { - if(group->robustness_count) { - session->send_requests |= BBL_SEND_IGMP; - group->send = true; - send = true; - } else { - group->state = IGMP_GROUP_ACTIVE; - } - } else if(group->state == IGMP_GROUP_LEAVING) { - if(group->robustness_count) { - session->send_requests |= BBL_SEND_IGMP; - group->send = true; - send = true; - } else { - group->state = IGMP_GROUP_IDLE; - } - } - } - if(send) { - session->send_requests |= BBL_SEND_IGMP; - bbl_session_tx_qnode_insert(session); - } - return; -} - void bbl_rx_dhcpv6(bbl_ipv6_t *ipv6, bbl_interface_s *interface, bbl_session_s *session) { @@ -628,7 +586,8 @@ bbl_rx_dhcpv6(bbl_ipv6_t *ipv6, bbl_interface_s *interface, bbl_session_s *sessi LOG(IP, "IPv6 (Q-in-Q %u:%u) DHCPv6 PD prefix %s/%d\n", session->key.outer_vlan_id, session->key.inner_vlan_id, format_ipv6_address(&session->delegated_ipv6_prefix.address), session->delegated_ipv6_prefix.len); - if(ctx->config.session_traffic_ipv6pd_pps && ctx->op.network_if && ctx->op.network_if->ip6.len) { + if(session->l2tp == false && ctx->config.session_traffic_ipv6pd_pps && + ctx->op.network_if && ctx->op.network_if->ip6.len) { /* Start IPv6 PD Session Traffic */ if(bbl_add_session_packets_ipv6(ctx, session, true)) { if(ctx->config.session_traffic_ipv6pd_pps > 1) { @@ -780,7 +739,8 @@ bbl_rx_icmpv6(bbl_ipv6_t *ipv6, bbl_interface_s *interface, bbl_session_s *sessi LOG(IP, "IPv6 (Q-in-Q %u:%u) ICMPv6 RA prefix %s/%d\n", session->key.outer_vlan_id, session->key.inner_vlan_id, format_ipv6_address(&session->ipv6_prefix.address), session->ipv6_prefix.len); - if(ctx->config.session_traffic_ipv6_pps && ctx->op.network_if && ctx->op.network_if->ip6.len) { + if(session->l2tp == false && ctx->config.session_traffic_ipv6_pps && + ctx->op.network_if && ctx->op.network_if->ip6.len) { /* Start IPv6 Session Traffic */ if(bbl_add_session_packets_ipv6(ctx, session, false)) { if(ctx->config.session_traffic_ipv6_pps > 1) { @@ -849,11 +809,13 @@ bbl_rx_igmp(bbl_ipv4_t *ipv4, bbl_session_s *session) { int i; bool send = false; - //LOG(IGMP, "IGMPv%d (Q-in-Q %u:%u) type %s received\n", - // igmp->version, - // session->key.outer_vlan_id, - // session->key.inner_vlan_id, - // val2key(igmp_msg_names, igmp->type)); +#if 0 + LOG(IGMP, "IGMPv%d (Q-in-Q %u:%u) type %s received\n", + igmp->version, + session->key.outer_vlan_id, + session->key.inner_vlan_id, + val2key(igmp_msg_names, igmp->type)); +#endif if(igmp->type == IGMP_TYPE_QUERY) { @@ -1036,6 +998,13 @@ bbl_rx_pap(bbl_ethernet_header_t *eth, bbl_interface_s *interface, bbl_session_s if(session->session_state == BBL_PPP_AUTH) { switch(pap->code) { case PAP_CODE_ACK: + if(pap->reply_message_len) { + if(strncmp(pap->reply_message, L2TP_REPLY_MESSAGE, pap->reply_message_len) == 0) { + session->l2tp = true; + LOG(L2TP, "L2TP (Q-in-Q %u:%u) Session with BNG Blaster LNS\n", + session->key.outer_vlan_id, session->key.inner_vlan_id); + } + } bbl_session_update_state(ctx, session, BBL_PPP_NETWORK); if(ctx->config.ipcp_enable) { session->ipcp_state = BBL_PPP_INIT; @@ -1093,6 +1062,13 @@ bbl_rx_chap(bbl_ethernet_header_t *eth, bbl_interface_s *interface, bbl_session_ } break; case CHAP_CODE_SUCCESS: + if(chap->reply_message_len) { + if(strncmp(chap->reply_message, L2TP_REPLY_MESSAGE, chap->reply_message_len) == 0) { + session->l2tp = true; + LOG(L2TP, "L2TP (Q-in-Q %u:%u) Session with BNG Blaster LNS\n", + session->key.outer_vlan_id, session->key.inner_vlan_id); + } + } bbl_session_update_state(ctx, session, BBL_PPP_NETWORK); if(ctx->config.ipcp_enable) { session->ipcp_state = BBL_PPP_INIT; @@ -1169,7 +1145,7 @@ bbl_rx_established(bbl_ethernet_header_t *eth, bbl_interface_s *interface, bbl_s /* Start Session Timer */ timer_add(&ctx->timer_root, &session->timer_session, "Session", ctx->config.pppoe_session_time, 0, session, bbl_session_timeout); } - if(ctx->config.session_traffic_ipv4_pps && session->ip_address && + if(session->l2tp == false && ctx->config.session_traffic_ipv4_pps && session->ip_address && ctx->op.network_if && ctx->op.network_if->ip) { /* Start IPv4 Session Traffic */ if(bbl_add_session_packets_ipv4(ctx, session)) { diff --git a/src/bbl_rx.h b/src/bbl_rx.h index ae160599..93f41573 100644 --- a/src/bbl_rx.h +++ b/src/bbl_rx.h @@ -10,9 +10,6 @@ #ifndef __BBL_RX_H__ #define __BBL_RX_H__ -void -bbl_igmp_timeout(timer_s *timer); - void bbl_rx_job (timer_s *timer); diff --git a/src/bbl_tx.c b/src/bbl_tx.c index 08c89259..b3318615 100644 --- a/src/bbl_tx.c +++ b/src/bbl_tx.c @@ -175,6 +175,48 @@ bbl_encode_packet_network_session_ipv6pd (bbl_interface_s *interface, bbl_sessio return PROTOCOL_SUCCESS; } +void +bbl_igmp_timeout(timer_s *timer) +{ + bbl_session_s *session = timer->data; + bbl_igmp_group_s *group = NULL; + int i; + bool send = false; + + if(session->access_type == ACCESS_TYPE_PPPOE) { + if(session->session_state != BBL_ESTABLISHED || + session->ipcp_state != BBL_PPP_OPENED) { + return; + } + } + + for(i=0; i < IGMP_MAX_GROUPS; i++) { + group = &session->igmp_groups[i]; + if(group->state == IGMP_GROUP_JOINING) { + if(group->robustness_count) { + session->send_requests |= BBL_SEND_IGMP; + group->send = true; + send = true; + } else { + group->state = IGMP_GROUP_ACTIVE; + } + } else if(group->state == IGMP_GROUP_LEAVING) { + if(group->robustness_count) { + session->send_requests |= BBL_SEND_IGMP; + group->send = true; + send = true; + } else { + group->state = IGMP_GROUP_IDLE; + } + } + } + if(send) { + session->send_requests |= BBL_SEND_IGMP; + bbl_session_tx_qnode_insert(session); + } + return; +} + protocol_error_t bbl_encode_packet_igmp (bbl_session_s *session) { From 9283cd0fe7a2b9d63d049f6f54fd39c74011e838 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Tue, 23 Feb 2021 10:14:07 +0100 Subject: [PATCH 11/27] #3 L2TPv2 LNS Support (WIP) - 11 Add L2TP session traffic. --- src/bbl_l2tp.c | 20 ++++++++++++++++++++ src/bbl_rx.c | 12 +++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 08b7243d..420ad230 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -796,6 +796,11 @@ bbl_l2tp_data_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * bbl_ipcp_t ipcp_tx; bbl_ip6cp_t *ip6cp_rx; bbl_ip6cp_t ip6cp_tx; + bbl_ipv4_t *ipv4; + bbl_udp_t *udp; + bbl_bbl_t *bbl; + + uint32_t tmp; UNUSED(ctx); UNUSED(eth); @@ -900,6 +905,21 @@ bbl_l2tp_data_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * } } break; + case PROTOCOL_IPV4: + ipv4 = (bbl_ipv4_t*)l2tp->next; + if(ipv4->protocol == PROTOCOL_IPV4_UDP) { + udp = (bbl_udp_t*)ipv4->next; + if(udp->protocol == UDP_PROTOCOL_BBL) { + /* Send BNG Blaster session traffic back by swapping + * IP address and set direction to downstream. */ + bbl = (bbl_bbl_t*)udp->next; + tmp = ipv4->dst; + ipv4->dst = ipv4->src; + ipv4->src = tmp; + bbl->direction = BBL_DIRECTION_DOWN; + bbl_l2tp_send_data(l2tp_session, PROTOCOL_IPV4, ipv4); + } + } default: break; } diff --git a/src/bbl_rx.c b/src/bbl_rx.c index a5e58ca3..d5660d20 100644 --- a/src/bbl_rx.c +++ b/src/bbl_rx.c @@ -83,6 +83,10 @@ bbl_add_session_packets_ipv4 (bbl_ctx_s *ctx, bbl_session_s *session) } session->access_ipv4_tx_packet_len = len; + if(session->l2tp) { + return true; + } + /* Prepare Network to Access (Session) Packet */ len = 0; if(!session->network_ipv4_tx_packet_template) { @@ -262,9 +266,11 @@ bbl_session_traffic_ipv4(timer_s *timer) } if(session->session_traffic) { session->send_requests |= BBL_SEND_SESSION_IPV4; - session->network_send_requests |= BBL_SEND_SESSION_IPV4; bbl_session_tx_qnode_insert(session); - bbl_session_network_tx_qnode_insert(session); + if(session->l2tp == false) { + session->network_send_requests |= BBL_SEND_SESSION_IPV4; + bbl_session_network_tx_qnode_insert(session); + } } } @@ -1145,7 +1151,7 @@ bbl_rx_established(bbl_ethernet_header_t *eth, bbl_interface_s *interface, bbl_s /* Start Session Timer */ timer_add(&ctx->timer_root, &session->timer_session, "Session", ctx->config.pppoe_session_time, 0, session, bbl_session_timeout); } - if(session->l2tp == false && ctx->config.session_traffic_ipv4_pps && session->ip_address && + if(ctx->config.session_traffic_ipv4_pps && session->ip_address && ctx->op.network_if && ctx->op.network_if->ip) { /* Start IPv4 Session Traffic */ if(bbl_add_session_packets_ipv4(ctx, session)) { From 37c93467cb675407dad42dfd528216411a3daacb Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Tue, 23 Feb 2021 10:28:03 +0100 Subject: [PATCH 12/27] #3 L2TPv2 LNS Support --- src/bbl_l2tp.c | 38 +++++++++++++++++--------------------- src/bbl_l2tp.h | 2 +- src/bbl_l2tp_avp.c | 2 +- src/bbl_l2tp_avp.h | 2 +- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 420ad230..1f4d2a2f 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -1,7 +1,7 @@ /* * BNG Blaster (BBL) - L2TPv2 Functions * - * Christian Giese, February 2022 + * Christian Giese, February 2021 * * Copyright (C) 2020-2021, RtBrick, Inc. */ @@ -71,7 +71,7 @@ l2tp_session_state_string(l2tp_session_state_t state) * This function will free all dynamic memory for the given * l2tp session instance. * - * @param l2tp_session Pointer to L2TP session object to be deleted. + * @param l2tp_session L2TP session structure to be deleted. */ void bbl_l2tp_session_delete(bbl_l2tp_session_t *l2tp_session) { @@ -113,7 +113,7 @@ bbl_l2tp_session_delete(bbl_l2tp_session_t *l2tp_session) { * This function will free all dynamic memory for the given * l2tp tunnel instance including corresponding send queues. * - * @param l2tp_tunnel Pointer to L2TP tunnel object to be deleted. + * @param l2tp_tunnel L2TP tunnel structure to be deleted. */ void bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_t *l2tp_tunnel) { @@ -170,7 +170,7 @@ bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_t *l2tp_tunnel) { /** * bbl_l2tp_tunnel_update_state * - * @param l2tp_tunnel Mandatory pointer to L2TP tunnel object. + * @param l2tp_tunnel L2TP tunnel structure. * @param state New L2TP tunnel state. */ void @@ -208,8 +208,6 @@ bbl_l2tp_tunnel_update_state(bbl_l2tp_tunnel_t *l2tp_tunnel, l2tp_tunnel_state_t /** * bbl_l2tp_tunnel_tx_job - * - * This function ... */ void bbl_l2tp_tunnel_tx_job (timer_s *timer) { @@ -349,12 +347,12 @@ bbl_l2tp_tunnel_control_job (timer_s *timer) { /** * bbl_l2tp_send * - * This function ... + * This function send control packets for a given L2TP tunnel. * - * @param l2tp_tunnel Mandatory pointer to L2TP tunnel object. - * @param l2tp_session Optional pointer to L2TP session object. + * @param l2tp_tunnel L2TP tunnel structure. + * @param l2tp_session Optional L2TP session structure. * This parameter is only required of L2TP session packets. - * @param l2tp_type L2TP message type (SCCRP, ICRP, ...) + * @param l2tp_type L2TP message type (SCCRP, ICRP, ...). */ static void bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, l2tp_message_type l2tp_type) { @@ -437,12 +435,12 @@ bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, /** * bbl_l2tp_send_data * - * This function ... + * This function send data packets for a given L2TP session. * - * @param l2tp_tunnel Mandatory pointer to L2TP tunnel object. - * @param l2tp_session Mandatory pointer to L2TP session object. - * @param protocol ... - * @param next ... + * @param l2tp_tunnel L2TP tunnel structure. + * @param l2tp_session L2TP session structure. + * @param protocol Payload type (IPCP, IPv4, ...). + * @param next Payload structure. */ static void bbl_l2tp_send_data(bbl_l2tp_session_t *l2tp_session, uint16_t protocol, void *next) { @@ -929,11 +927,11 @@ bbl_l2tp_data_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * /** * bbl_l2tp_handler_rx * - * This function ... + * This function handles all received L2TPv2 traffic. * - * @param eth ... - * @param l2tp ... - * @param interface ... + * @param eth Received ethernet packet. + * @param l2tp L2TP header of received ethernet packet. + * @param interface Receiving interface. */ void bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface) { @@ -1093,8 +1091,6 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ * bbl_l2tp_stop_all_tunnel * * This function greacefully teardowns all L2TP tunnels. - * - * @param ctx ... */ void bbl_l2tp_stop_all_tunnel(bbl_ctx_s *ctx) { diff --git a/src/bbl_l2tp.h b/src/bbl_l2tp.h index 537c5d0d..48c717a7 100644 --- a/src/bbl_l2tp.h +++ b/src/bbl_l2tp.h @@ -1,7 +1,7 @@ /* * BNG Blaster (BBL) - L2TPv2 Functions (RFC2661) * - * Christian Giese, February 2022 + * Christian Giese, February 2021 * * Copyright (C) 2020-2021, RtBrick, Inc. */ diff --git a/src/bbl_l2tp_avp.c b/src/bbl_l2tp_avp.c index 624e634a..aa13121f 100644 --- a/src/bbl_l2tp_avp.c +++ b/src/bbl_l2tp_avp.c @@ -1,7 +1,7 @@ /* * BNG Blaster (BBL) - L2TPv2 AVP Functions * - * Christian Giese, February 2022 + * Christian Giese, February 2021 * * Copyright (C) 2020-2021, RtBrick, Inc. */ diff --git a/src/bbl_l2tp_avp.h b/src/bbl_l2tp_avp.h index bb6805bd..f3b10282 100644 --- a/src/bbl_l2tp_avp.h +++ b/src/bbl_l2tp_avp.h @@ -1,7 +1,7 @@ /* * BNG Blaster (BBL) - L2TPv2 Functions (RFC2661) * - * Christian Giese, February 2022 + * Christian Giese, February 2021 * * Copyright (C) 2020-2021, RtBrick, Inc. */ From 5dd3e4c19a3cd9b7aed65f142c7a0cc015e6c09a Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Tue, 23 Feb 2021 12:47:32 +0100 Subject: [PATCH 13/27] #10 LI Flow Support --- src/bbl.c | 4 ++ src/bbl.h | 6 +++ src/bbl_ctrl.c | 41 +++++++++++++++++++++ src/bbl_interactive.c | 6 +++ src/bbl_li.c | 85 +++++++++++++++++++++++++++++++++++++++++++ src/bbl_li.h | 32 ++++++++++++++++ src/bbl_protocols.c | 35 +++++++++++++++++- src/bbl_protocols.h | 14 +++++++ src/bbl_rx.c | 2 + src/bbl_stats.c | 15 ++++++++ 10 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 src/bbl_li.c create mode 100644 src/bbl_li.h diff --git a/src/bbl.c b/src/bbl.c index 24f00057..02ecef66 100644 --- a/src/bbl.c +++ b/src/bbl.c @@ -653,6 +653,10 @@ bbl_add_ctx (void) bbl_l2tp_session_hash, BBL_SESSION_HASHTABLE_SIZE); + ctx->li_flow_dict = hashtable2_dict_new((dict_compare_func)bbl_compare_l2tp_session, + bbl_l2tp_session_hash, + BBL_SESSION_HASHTABLE_SIZE); + return ctx; } diff --git a/src/bbl.h b/src/bbl.h index 5d770098..10396501 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -45,6 +45,7 @@ #include "bbl_tx.h" #include "bbl_l2tp.h" #include "bbl_l2tp_avp.h" +#include "bbl_li.h" #define WRITE_BUF_LEN 1514 #define SCRATCHPAD_LEN 1514 @@ -271,6 +272,9 @@ typedef struct bbl_interface_ uint64_t l2tp_data_tx; bbl_rate_s rate_l2tp_data_rx; bbl_rate_s rate_l2tp_data_tx; + + uint64_t li_rx; + bbl_rate_s rate_li_rx; } stats; struct timer_ *tx_job; @@ -373,6 +377,8 @@ typedef struct bbl_ctx_ dict *session_dict; /* hashtable for sessions */ dict *l2tp_session_dict; /* hashtable for L2TP sessions */ + dict *li_flow_dict; /* hashtable for LI flows */ + uint16_t next_tunnel_id; uint64_t flow_id; diff --git a/src/bbl_ctrl.c b/src/bbl_ctrl.c index dc2f9b61..16d8e617 100644 --- a/src/bbl_ctrl.c +++ b/src/bbl_ctrl.c @@ -650,6 +650,46 @@ bbl_ctrl_session_ip6cp_close(int fd, bbl_ctx_s *ctx, session_key_t *key, json_t* return bbl_ctrl_session_ncp_open_close(fd, ctx, key, false, false); } +ssize_t +bbl_ctrl_li_flows(int fd, bbl_ctx_s *ctx, session_key_t *key __attribute__((unused)), json_t* arguments __attribute__((unused))) { + ssize_t result = 0; + json_t *root, *flows, *flow; + bbl_li_flow_t *li_flow; + struct dict_itor *itor; + + flows = json_array(); + itor = dict_itor_new(ctx->li_flow_dict); + dict_itor_first(itor); + for (; dict_itor_valid(itor); dict_itor_next(itor)) { + li_flow = (bbl_li_flow_t*)*dict_itor_datum(itor); + if(li_flow) { + flow = json_pack("{si si si si si si si si si si}", + "direction", li_flow->direction, + "packet-type", li_flow->packet_type, + "sub-packet-type", li_flow->sub_packet_type, + "liid", li_flow->liid, + "bytes-rx", li_flow->bytes_rx, + "packets-rx", li_flow->packets_rx, + "packets-rx-ipv4", li_flow->packets_rx_ipv4, + "packets-rx-ipv4-tcp", li_flow->packets_rx_ipv4_tcp, + "packets-rx-ipv4-udp", li_flow->packets_rx_ipv4_udp, + "packets-rx-ipv4-host-internal", li_flow->packets_rx_ipv4_internal); + json_array_append(flows, flow); + } + } + root = json_pack("{ss si so}", + "status", "ok", + "code", 200, + "li-flows", flows); + if(root) { + result = json_dumpfd(root, fd, 0); + json_decref(root); + } else { + bbl_ctrl_status(fd, "error", 500, "internal error"); + json_decref(flows); + } + return result; +} struct action { char *name; callback_function *fn; @@ -673,6 +713,7 @@ struct action actions[] = { {"igmp-join", bbl_ctrl_igmp_join}, {"igmp-leave", bbl_ctrl_igmp_leave}, {"igmp-info", bbl_ctrl_igmp_info}, + {"li-flows", bbl_ctrl_li_flows}, {NULL, NULL}, }; diff --git a/src/bbl_interactive.c b/src/bbl_interactive.c index 1ba5051e..f6c47491 100644 --- a/src/bbl_interactive.c +++ b/src/bbl_interactive.c @@ -178,6 +178,12 @@ bbl_stats_job (timer_s *timer) } if (ctx->op.network_if) { + if(dict_count(ctx->li_flow_dict)) { + wprintw(stats_win, "\nLI Statistics\n"); + wprintw(stats_win, " Flows %10lu\n", dict_count(ctx->li_flow_dict)); + wprintw(stats_win, " Rx Packets %10lu (%7lu PPS)\n", + ctx->op.network_if->stats.li_rx, ctx->op.network_if->stats.rate_li_rx.avg); + } if(ctx->config.l2tp_server) { wprintw(stats_win, "\nL2TP LNS Statistics\n"); wprintw(stats_win, " Tunnels %10lu\n", ctx->l2tp_tunnels); diff --git a/src/bbl_li.c b/src/bbl_li.c new file mode 100644 index 00000000..e7ecf7cb --- /dev/null +++ b/src/bbl_li.c @@ -0,0 +1,85 @@ +/* + * BNG Blaster (BBL) - LI Functions + * + * Christian Giese, February 2021 + * + * Copyright (C) 2020-2021, RtBrick, Inc. + */ + +#include "bbl.h" +#include "bbl_logging.h" + +/** + * bbl_l2tp_handler_rx + * + * This function handles all received L2TPv2 traffic. + * + * @param eth Received ethernet packet. + * @param l2tp L2TP header of received ethernet packet. + * @param interface Receiving interface. + */ +void +bbl_qmx_li_handler_rx(bbl_ethernet_header_t *eth, bbl_qmx_li_t *qmx_li, bbl_interface_s *interface) { + bbl_ctx_s *ctx = interface->ctx; + bbl_ipv4_t *ipv4 = (bbl_ipv4_t*)eth->next; + bbl_li_flow_t *li_flow; + + bbl_ethernet_header_t *inner_eth; + bbl_pppoe_session_t *inner_pppoe; + bbl_ipv4_t *inner_ipv4 = NULL; + + dict_insert_result result; + void **search = NULL; + + UNUSED(eth); + + search = dict_search(ctx->li_flow_dict, &qmx_li->header); + if(search) { + li_flow = *search; + } else { + /* New flow ... */ + li_flow = calloc(1, sizeof(bbl_li_flow_t)); + li_flow->direction = qmx_li->direction; + li_flow->packet_type = qmx_li->packet_type; + li_flow->sub_packet_type = qmx_li->sub_packet_type; + li_flow->liid = qmx_li->liid; + result = dict_insert(ctx->li_flow_dict, &qmx_li->header); + if (!result.inserted) { + free(li_flow); + return; + } + *result.datum_ptr = li_flow; + } + + interface->stats.li_rx++; + li_flow->packets_rx++; + li_flow->bytes_rx += qmx_li->payload_len; + + inner_eth = (bbl_ethernet_header_t*)qmx_li->next; + if(inner_eth->type == ETH_TYPE_PPPOE_SESSION) { + inner_pppoe = (bbl_pppoe_session_t*)inner_eth->next; + if(inner_pppoe->protocol == PROTOCOL_IPV4) { + inner_ipv4 = (bbl_ipv4_t*)inner_pppoe->next; + + } + } else if(inner_eth->type == ETH_TYPE_IPV4) { + inner_ipv4 = (bbl_ipv4_t*)eth->next; + } + + if(inner_ipv4) { + li_flow->packets_rx_ipv4++; + switch(ipv4->protocol) { + case PROTOCOL_IPV4_TCP: + li_flow->packets_rx_ipv4_tcp++; + break; + case PROTOCOL_IPV4_UDP: + li_flow->packets_rx_ipv4_udp++; + break; + case PROTOCOL_IPV4_INTERNAL: + li_flow->packets_rx_ipv4_internal++; + break; + default: + break; + } + } +} diff --git a/src/bbl_li.h b/src/bbl_li.h new file mode 100644 index 00000000..e7de936e --- /dev/null +++ b/src/bbl_li.h @@ -0,0 +1,32 @@ +/* + * BNG Blaster (BBL) - LI Functions + * + * Christian Giese, February 2021 + * + * Copyright (C) 2020-2021, RtBrick, Inc. + */ + +#ifndef __BBL_LI_H__ +#define __BBL_LI_H__ + +typedef struct bbl_interface_ bbl_interface_s; + +typedef struct bbl_li_flow_ +{ + uint8_t direction; + uint8_t packet_type; + uint8_t sub_packet_type; + uint32_t liid; + + uint64_t packets_rx; + uint64_t bytes_rx; + uint64_t packets_rx_ipv4; + uint64_t packets_rx_ipv4_tcp; + uint64_t packets_rx_ipv4_udp; + uint64_t packets_rx_ipv4_internal; +} bbl_li_flow_t; + + +void bbl_qmx_li_handler_rx(bbl_ethernet_header_t *eth, bbl_qmx_li_t *qmx_li, bbl_interface_s *interface); + +#endif \ No newline at end of file diff --git a/src/bbl_protocols.c b/src/bbl_protocols.c index 68a16e99..835cb65e 100644 --- a/src/bbl_protocols.c +++ b/src/bbl_protocols.c @@ -1422,6 +1422,30 @@ decode_bbl(uint8_t *buf, uint len, return PROTOCOL_SUCCESS; } +protocol_error_t +decode_qmx_li(uint8_t *buf, uint len, + uint8_t *sp, uint sp_len, + bbl_qmx_li_t **_qmx_li) { + + bbl_qmx_li_t *qmx_li; + + if(len < 4 || sp_len < sizeof(bbl_qmx_li_t)) { + return DECODE_ERROR; + } + /* Init QMX LI header */ + qmx_li = (bbl_qmx_li_t*)sp; BUMP_BUFFER(sp, sp_len, sizeof(bbl_qmx_li_t)); + qmx_li->header = *(uint32_t*)buf; + qmx_li->direction = (*buf >> 5) & 0x7; + qmx_li->packet_type = (*buf >> 1) & 0xf; + qmx_li->sub_packet_type =(*(uint16_t*)buf >> 6) & 0x0f; + qmx_li->liid =*(uint32_t*)buf & 0x3FFFFF; + BUMP_BUFFER(buf, len, sizeof(uint32_t)); + qmx_li->payload = buf; + qmx_li->payload_len = len; + *_qmx_li = qmx_li; + return decode_ethernet(buf, len, sp, sp_len, (bbl_ethernet_header_t**)&qmx_li->next); +} + protocol_error_t decode_udp(uint8_t *buf, uint len, uint8_t *sp, uint sp_len, @@ -1467,8 +1491,17 @@ decode_udp(uint8_t *buf, uint len, udp->protocol = UDP_PROTOCOL_L2TP; ret_val = decode_l2tp(buf, len, sp, sp_len, (bbl_l2tp_t**)&udp->next); break; + case QMX_LI_UDP_PORT: + udp->protocol = UDP_PROTOCOL_QMX_LI; + ret_val = decode_qmx_li(buf, len, sp, sp_len, (bbl_qmx_li_t**)&udp->next); + break; default: - udp->next = NULL; + 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_t**)&udp->next); + } else { + udp->next = NULL; + } break; } diff --git a/src/bbl_protocols.h b/src/bbl_protocols.h index 9cdddf8f..686048bd 100644 --- a/src/bbl_protocols.h +++ b/src/bbl_protocols.h @@ -65,6 +65,7 @@ #define PROTOCOL_IPV4_IGMP 0x02 #define PROTOCOL_IPV4_TCP 0x06 #define PROTOCOL_IPV4_UDP 0x11 +#define PROTOCOL_IPV4_INTERNAL 0x3D #define ICMP_TYPE_ECHO_REPLY 0x00 #define ICMP_TYPE_ECHO_REQUEST 0x08 @@ -134,6 +135,7 @@ #define UDP_PROTOCOL_DHCPV6 1 #define UDP_PROTOCOL_BBL 2 #define UDP_PROTOCOL_L2TP 3 +#define UDP_PROTOCOL_QMX_LI 4 #define IPV6_NEXT_HEADER_UDP 17 #define IPV6_NEXT_HEADER_ICMPV6 58 @@ -175,6 +177,7 @@ #define L2TP_NH_TYPE_VALUE 18 +#define QMX_LI_UDP_PORT 49152 #define MAX_VLANS 3 @@ -580,6 +583,17 @@ typedef struct bbl_bbl_ { uint64_t timestamp; } bbl_bbl_t; +typedef struct bbl_qmx_li_ { + uint32_t header; + uint8_t direction; + uint8_t packet_type; + uint8_t sub_packet_type; + uint32_t liid; + void *next; // next header + void *payload; // LI payload + uint16_t payload_len; // LI payload length +} bbl_qmx_li_t; + /* * decode_ethernet */ diff --git a/src/bbl_rx.c b/src/bbl_rx.c index d5660d20..fe915b88 100644 --- a/src/bbl_rx.c +++ b/src/bbl_rx.c @@ -1793,6 +1793,8 @@ bbl_rx_handler_network(bbl_ethernet_header_t *eth, bbl_interface_s *interface) { udp = (bbl_udp_t*)ipv4->next; if(udp->protocol == UDP_PROTOCOL_BBL) { bbl = (bbl_bbl_t*)udp->next; + } else if(udp->protocol == UDP_PROTOCOL_QMX_LI) { + return bbl_qmx_li_handler_rx(eth, (bbl_qmx_li_t*)udp->next, interface); } else if(udp->protocol == UDP_PROTOCOL_L2TP) { return bbl_l2tp_handler_rx(eth, (bbl_l2tp_t*)udp->next, interface); } diff --git a/src/bbl_stats.c b/src/bbl_stats.c index 8c097480..c87e4315 100644 --- a/src/bbl_stats.c +++ b/src/bbl_stats.c @@ -165,6 +165,11 @@ bbl_stats_stdout (bbl_ctx_s *ctx, bbl_stats_t * stats) { printf("Flapped: %u\n", ctx->sessions_flapped); if(ctx->op.network_if) { + if(dict_count(ctx->li_flow_dict)) { + printf("\nLI Statistics:\n"); + printf(" Flows: %10lu\n", dict_count(ctx->li_flow_dict)); + printf(" RX Packets: %10lu\n", ctx->op.network_if->stats.li_rx); + } if(ctx->config.l2tp_server) { printf("\nL2TP LNS Statistics:\n"); printf(" Tunnels: %10u\n", ctx->l2tp_tunnels_max); @@ -312,6 +317,7 @@ bbl_stats_json (bbl_ctx_s *ctx, bbl_stats_t * stats) { json_t *jobj_access_if = NULL; json_t *jobj_network_if = NULL; json_t *jobj_l2tp = NULL; + json_t *jobj_li = NULL; json_t *jobj_straffic = NULL; json_t *jobj_multicast = NULL; json_t *jobj_protocols = NULL; @@ -335,6 +341,12 @@ bbl_stats_json (bbl_ctx_s *ctx, bbl_stats_t * stats) { jobj_array = json_array(); if (ctx->op.network_if) { + if(dict_count(ctx->li_flow_dict)) { + jobj_li = json_object(); + json_object_set(jobj_li, "flows", json_integer(dict_count(ctx->li_flow_dict))); + json_object_set(jobj_li, "rx-packets", json_integer(ctx->op.network_if->stats.li_rx)); + json_object_set(jobj, "li-statistics", jobj_li); + } if(ctx->config.l2tp_server) { jobj_l2tp = json_object(); json_object_set(jobj_l2tp, "tunnels", json_integer(ctx->l2tp_tunnels_max)); @@ -505,6 +517,8 @@ bbl_compute_avg_rate (bbl_rate_s *rate, uint64_t current_value) uint idx, div; uint64_t sum; + if(current_value == 0) return; + rate->diff_value[rate->cursor] = current_value - rate->last_value; sum = 0; @@ -547,5 +561,6 @@ bbl_compute_interface_rate_job (timer_s *timer) bbl_compute_avg_rate(&interface->stats.rate_mc_tx, interface->stats.mc_tx); bbl_compute_avg_rate(&interface->stats.rate_l2tp_data_rx, interface->stats.l2tp_data_rx); bbl_compute_avg_rate(&interface->stats.rate_l2tp_data_tx, interface->stats.l2tp_data_tx); + bbl_compute_avg_rate(&interface->stats.rate_li_rx, interface->stats.li_rx); } } From 091f7db80a389a314bc24f31794a1c6aa2ca28c1 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Tue, 23 Feb 2021 13:20:23 +0100 Subject: [PATCH 14/27] #10 LI Flow Support Fixes --- src/bbl_ctrl.c | 8 ++++---- src/bbl_li.c | 32 ++++++++++++++++++++++++++++++++ src/bbl_li.h | 3 +++ src/bbl_protocols.c | 4 ++-- 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/bbl_ctrl.c b/src/bbl_ctrl.c index 16d8e617..6b2dbe35 100644 --- a/src/bbl_ctrl.c +++ b/src/bbl_ctrl.c @@ -663,10 +663,10 @@ bbl_ctrl_li_flows(int fd, bbl_ctx_s *ctx, session_key_t *key __attribute__((unus for (; dict_itor_valid(itor); dict_itor_next(itor)) { li_flow = (bbl_li_flow_t*)*dict_itor_datum(itor); if(li_flow) { - flow = json_pack("{si si si si si si si si si si}", - "direction", li_flow->direction, - "packet-type", li_flow->packet_type, - "sub-packet-type", li_flow->sub_packet_type, + flow = json_pack("{ss ss ss si si si si si si si}", + "direction", bbl_li_direction_string(li_flow->direction), + "packet-type", bbl_li_packet_type_string(li_flow->packet_type), + "sub-packet-type", bbl_li_sub_packet_type_string(li_flow->sub_packet_type), "liid", li_flow->liid, "bytes-rx", li_flow->bytes_rx, "packets-rx", li_flow->packets_rx, diff --git a/src/bbl_li.c b/src/bbl_li.c index e7ecf7cb..5b9be3d2 100644 --- a/src/bbl_li.c +++ b/src/bbl_li.c @@ -9,6 +9,38 @@ #include "bbl.h" #include "bbl_logging.h" +const char* +bbl_li_direction_string(uint8_t direction) +{ + switch(direction) { + case 2: return "downstream"; + case 3: return "upstream"; + default: return "invlid"; + } +} + +const char* +bbl_li_packet_type_string(uint8_t packet_type) +{ + switch(packet_type) { + case 5: return "ipv4"; + case 6: return "ipv6"; + case 7: return "ethernet"; + default: return "unkown"; + } +} + +const char* +bbl_li_sub_packet_type_string(uint8_t sub_packet_type) +{ + switch(sub_packet_type) { + case 1: return "single-tagged"; + case 2: return "double-tagged"; + case 3: return "untagged"; + default: return "unkown"; + } +} + /** * bbl_l2tp_handler_rx * diff --git a/src/bbl_li.h b/src/bbl_li.h index e7de936e..64227458 100644 --- a/src/bbl_li.h +++ b/src/bbl_li.h @@ -26,6 +26,9 @@ typedef struct bbl_li_flow_ uint64_t packets_rx_ipv4_internal; } bbl_li_flow_t; +const char* bbl_li_direction_string(uint8_t direction); +const char* bbl_li_packet_type_string(uint8_t packet_type); +const char* bbl_li_sub_packet_type_string(uint8_t sub_packet_type); void bbl_qmx_li_handler_rx(bbl_ethernet_header_t *eth, bbl_qmx_li_t *qmx_li, bbl_interface_s *interface); diff --git a/src/bbl_protocols.c b/src/bbl_protocols.c index 835cb65e..af3796f7 100644 --- a/src/bbl_protocols.c +++ b/src/bbl_protocols.c @@ -1435,10 +1435,10 @@ decode_qmx_li(uint8_t *buf, uint len, /* Init QMX LI header */ qmx_li = (bbl_qmx_li_t*)sp; BUMP_BUFFER(sp, sp_len, sizeof(bbl_qmx_li_t)); qmx_li->header = *(uint32_t*)buf; + qmx_li->liid = *(uint32_t*)buf & 0x003fffff; qmx_li->direction = (*buf >> 5) & 0x7; qmx_li->packet_type = (*buf >> 1) & 0xf; - qmx_li->sub_packet_type =(*(uint16_t*)buf >> 6) & 0x0f; - qmx_li->liid =*(uint32_t*)buf & 0x3FFFFF; + qmx_li->sub_packet_type =(*(uint16_t*)buf >> 6) & 0x07; BUMP_BUFFER(buf, len, sizeof(uint32_t)); qmx_li->payload = buf; qmx_li->payload_len = len; From cdcdc57f089d78f0c5fd92ce7c2b59038e1c7c37 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Tue, 23 Feb 2021 13:46:57 +0100 Subject: [PATCH 15/27] #10 LI Flow Support Fixes --- src/bbl_ctrl.c | 6 +++++- src/bbl_li.c | 10 +++++++--- src/bbl_li.h | 5 +++++ src/bbl_protocols.c | 4 ++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/bbl_ctrl.c b/src/bbl_ctrl.c index 6b2dbe35..f1372427 100644 --- a/src/bbl_ctrl.c +++ b/src/bbl_ctrl.c @@ -663,7 +663,11 @@ bbl_ctrl_li_flows(int fd, bbl_ctx_s *ctx, session_key_t *key __attribute__((unus for (; dict_itor_valid(itor); dict_itor_next(itor)) { li_flow = (bbl_li_flow_t*)*dict_itor_datum(itor); if(li_flow) { - flow = json_pack("{ss ss ss si si si si si si si}", + flow = json_pack("{ss si ss si ss ss ss si si si si si si si}", + "source-address", format_ipv4_address(&li_flow->src_ipv4), + "source-port", li_flow->src_port, + "destination-address", format_ipv4_address(&li_flow->dst_ipv4), + "destination-port", li_flow->dst_port, "direction", bbl_li_direction_string(li_flow->direction), "packet-type", bbl_li_packet_type_string(li_flow->packet_type), "sub-packet-type", bbl_li_sub_packet_type_string(li_flow->sub_packet_type), diff --git a/src/bbl_li.c b/src/bbl_li.c index 5b9be3d2..c883d69f 100644 --- a/src/bbl_li.c +++ b/src/bbl_li.c @@ -54,11 +54,11 @@ void bbl_qmx_li_handler_rx(bbl_ethernet_header_t *eth, bbl_qmx_li_t *qmx_li, bbl_interface_s *interface) { bbl_ctx_s *ctx = interface->ctx; bbl_ipv4_t *ipv4 = (bbl_ipv4_t*)eth->next; - bbl_li_flow_t *li_flow; - + bbl_udp_t *udp = (bbl_udp_t*)ipv4->next; bbl_ethernet_header_t *inner_eth; bbl_pppoe_session_t *inner_pppoe; bbl_ipv4_t *inner_ipv4 = NULL; + bbl_li_flow_t *li_flow; dict_insert_result result; void **search = NULL; @@ -71,6 +71,10 @@ bbl_qmx_li_handler_rx(bbl_ethernet_header_t *eth, bbl_qmx_li_t *qmx_li, bbl_inte } else { /* New flow ... */ li_flow = calloc(1, sizeof(bbl_li_flow_t)); + li_flow->src_ipv4 = ipv4->src; + li_flow->dst_ipv4 = ipv4->dst; + li_flow->src_port = udp->src; + li_flow->dst_port = udp->dst; li_flow->direction = qmx_li->direction; li_flow->packet_type = qmx_li->packet_type; li_flow->sub_packet_type = qmx_li->sub_packet_type; @@ -100,7 +104,7 @@ bbl_qmx_li_handler_rx(bbl_ethernet_header_t *eth, bbl_qmx_li_t *qmx_li, bbl_inte if(inner_ipv4) { li_flow->packets_rx_ipv4++; - switch(ipv4->protocol) { + switch(inner_ipv4->protocol) { case PROTOCOL_IPV4_TCP: li_flow->packets_rx_ipv4_tcp++; break; diff --git a/src/bbl_li.h b/src/bbl_li.h index 64227458..e03b6527 100644 --- a/src/bbl_li.h +++ b/src/bbl_li.h @@ -13,6 +13,11 @@ typedef struct bbl_interface_ bbl_interface_s; typedef struct bbl_li_flow_ { + uint32_t src_ipv4; + uint32_t dst_ipv4; + uint32_t src_port; + uint32_t dst_port; + uint8_t direction; uint8_t packet_type; uint8_t sub_packet_type; diff --git a/src/bbl_protocols.c b/src/bbl_protocols.c index af3796f7..ad00e4eb 100644 --- a/src/bbl_protocols.c +++ b/src/bbl_protocols.c @@ -1435,10 +1435,10 @@ decode_qmx_li(uint8_t *buf, uint len, /* Init QMX LI header */ qmx_li = (bbl_qmx_li_t*)sp; BUMP_BUFFER(sp, sp_len, sizeof(bbl_qmx_li_t)); qmx_li->header = *(uint32_t*)buf; - qmx_li->liid = *(uint32_t*)buf & 0x003fffff; + qmx_li->liid = be32toh(*(uint32_t*)buf) & 0x003fffff; qmx_li->direction = (*buf >> 5) & 0x7; qmx_li->packet_type = (*buf >> 1) & 0xf; - qmx_li->sub_packet_type =(*(uint16_t*)buf >> 6) & 0x07; + qmx_li->sub_packet_type =(be16toh(*(uint16_t*)buf) >> 6) & 0x07; BUMP_BUFFER(buf, len, sizeof(uint32_t)); qmx_li->payload = buf; qmx_li->payload_len = len; From 5f63682e9c1b830f4a42688899a4b1f0e0cacc03 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Tue, 23 Feb 2021 14:39:58 +0100 Subject: [PATCH 16/27] docu update --- docs/ctrl.md | 1 + docs/index.md | 2 ++ docs/l2tp.md | 2 ++ docs/li.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 docs/l2tp.md create mode 100644 docs/li.md diff --git a/docs/ctrl.md b/docs/ctrl.md index 4070999e..783df89b 100644 --- a/docs/ctrl.md +++ b/docs/ctrl.md @@ -114,6 +114,7 @@ Attribute | Description `session-traffic-disabled` | Disable session traffic for all sessions `multicast-traffic-start` | Start sending multicast traffic from network interface `multicast-traffic-stop` | Stop sending multicast traffic from network interface +`li-flows` | List all LI flows with detailed statistics ### Session Commands diff --git a/docs/index.md b/docs/index.md index a68498b4..cc23c5fd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,6 +7,8 @@ - [Traffic](traffic) - [Control Socket](ctrl) - [Multicast](multicast) +- [L2TPv2](li) +- [Legal Interception](li) A short introduction can be found on [YouTube](https://youtu.be/EHJ70p0_Sw0 "BNG Blaster"). diff --git a/docs/l2tp.md b/docs/l2tp.md new file mode 100644 index 00000000..2bd125bb --- /dev/null +++ b/docs/l2tp.md @@ -0,0 +1,2 @@ +# L2TPv2 + diff --git a/docs/li.md b/docs/li.md new file mode 100644 index 00000000..0789ad8d --- /dev/null +++ b/docs/li.md @@ -0,0 +1,77 @@ +# Legal Interception (LI) + +The BNG Blaster can be used to emulate a mediation device providing detailed statistics +about the received flows. Today only the BCM QMX LI header format is supported but further +headers can be easily integrated. + +*BCM QMX LI Header Format* +``` + 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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| D | PT | SPT | LIID | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +``` + +The functionality is automatically enabled on the network interface +and works combined with sessions in one instance or as standalone +mediation device as shown in the following example. + +```json +{ + "interfaces": { + "tx-interval": 10, + "rx-interval": 1, + "network": { + "interface": "eth2", + "address": "100.0.0.10", + "gateway": "100.0.0.2" + } + } +} + +The received flows can be queried using the control socket. + +`$ sudo ./cli.py run.sock li-flows` +```json +{ + "status": "ok", + "code": 200, + "li-flows": [ + { + "source-address": "1.1.1.1", + "source-port": 49152, + "destination-address": "1.1.1.2", + "destination-port": 49152, + "direction": "downstream", + "packet-type": "ethernet", + "sub-packet-type": "double-tagged", + "liid": 4194301, + "bytes-rx": 94, + "packets-rx": 1, + "packets-rx-ipv4": 0, + "packets-rx-ipv4-tcp": 0, + "packets-rx-ipv4-udp": 0, + "packets-rx-ipv4-host-internal": 0 + }, + { + "source-address": "1.1.1.1", + "source-port": 49152, + "destination-address": "1.1.1.2", + "destination-port": 49152, + "direction": "upstream", + "packet-type": "ethernet", + "sub-packet-type": "double-tagged", + "liid": 4194301, + "bytes-rx": 160720, + "packets-rx": 820, + "packets-rx-ipv4": 820, + "packets-rx-ipv4-tcp": 0, + "packets-rx-ipv4-udp": 0, + "packets-rx-ipv4-host-internal": 820 + } + ] +} +``` + +The `packets-rx-ipv4-host-internal` refers to the IPv4 protocol number 61 (any host internal protocol) +which is used by some network testers as default type for traffic streams. \ No newline at end of file From db71bf29f808f8452b4a9d53781c5da63eeefc42 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Tue, 23 Feb 2021 19:47:21 +0100 Subject: [PATCH 17/27] #3 L2TPv2 LNS Support RFC5515 and CTRL commands... + add ctrl command l2tp-tunnels + add ctrl command l2tp-sessions + add ctrl command l2tp-csurq + add l2tp csun support + add l2tp csurq support + add support for ARI/ACI AVP + ... --- src/bbl_ctrl.c | 207 ++++++++++++++++++++++++++++++++++++++++++++- src/bbl_l2tp.c | 15 +++- src/bbl_l2tp.h | 11 ++- src/bbl_l2tp_avp.c | 132 +++++++++++++++++++++++++++-- src/bbl_l2tp_avp.h | 3 + 5 files changed, 354 insertions(+), 14 deletions(-) diff --git a/src/bbl_ctrl.c b/src/bbl_ctrl.c index f1372427..8dfdf590 100644 --- a/src/bbl_ctrl.c +++ b/src/bbl_ctrl.c @@ -63,6 +63,16 @@ ppp_state_string(uint32_t state) { } } +static char * +string_or_na(char *string) { + if(string) { + return string; + } else { + return "N/A"; + } +} + + ssize_t bbl_ctrl_status(int fd, const char *status, uint32_t code, const char *message) { ssize_t result = 0; @@ -343,7 +353,7 @@ bbl_ctrl_igmp_info(int fd, bbl_ctx_s *ctx, session_key_t *key, json_t* arguments result = json_dumpfd(root, fd, 0); json_decref(root); } else { - bbl_ctrl_status(fd, "error", 500, "internal error"); + result = bbl_ctrl_status(fd, "error", 500, "internal error"); json_decref(groups); } return result; @@ -458,7 +468,7 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, session_key_t *key, json_t* argume result = json_dumpfd(root, fd, 0); json_decref(root); } else { - bbl_ctrl_status(fd, "error", 500, "internal error"); + result = bbl_ctrl_status(fd, "error", 500, "internal error"); json_decref(session_traffic); } return result; @@ -494,7 +504,7 @@ bbl_ctrl_interfaces(int fd, bbl_ctx_s *ctx, session_key_t *key __attribute__((un result = json_dumpfd(root, fd, 0); json_decref(root); } else { - bbl_ctrl_status(fd, "error", 500, "internal error"); + result = bbl_ctrl_status(fd, "error", 500, "internal error"); json_decref(interfaces); } return result; @@ -689,11 +699,197 @@ bbl_ctrl_li_flows(int fd, bbl_ctx_s *ctx, session_key_t *key __attribute__((unus result = json_dumpfd(root, fd, 0); json_decref(root); } else { - bbl_ctrl_status(fd, "error", 500, "internal error"); + result = bbl_ctrl_status(fd, "error", 500, "internal error"); json_decref(flows); } return result; } + +ssize_t +bbl_ctrl_l2tp_tunnels(int fd, bbl_ctx_s *ctx, session_key_t *key __attribute__((unused)), json_t* arguments __attribute__((unused))) { + ssize_t result = 0; + json_t *root, *tunnels, *tunnel; + + bbl_l2tp_server_t *l2tp_server = ctx->config.l2tp_server; + bbl_l2tp_tunnel_t *l2tp_tunnel; + + tunnels = json_array(); + + while(l2tp_server) { + CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_server->tunnel_qhead, tunnel_qnode) { + + tunnel = json_pack("{ss ss ss si si ss ss ss ss si si si si si si si}", + "state", l2tp_tunnel_state_string(l2tp_tunnel->state), + "server-name", l2tp_server->host_name, + "server-address", format_ipv4_address(&l2tp_server->ip), + "tunnel-id", l2tp_tunnel->tunnel_id, + "peer-tunnel-id", l2tp_tunnel->peer_tunnel_id, + "peer-name", string_or_na(l2tp_tunnel->peer_name), + "peer-address", format_ipv4_address(&l2tp_tunnel->peer_ip), + "peer-vendor", string_or_na(l2tp_tunnel->peer_vendor), + "secret", string_or_na(l2tp_server->secret), + "control-packets-rx", l2tp_tunnel->stats.control_rx, + "control-packets-rx-dup", l2tp_tunnel->stats.control_rx_dup, + "control-packets-rx-out-of-order", l2tp_tunnel->stats.control_rx_ooo, + "control-packets-tx", l2tp_tunnel->stats.control_tx, + "control-packets-tx-retry", l2tp_tunnel->stats.control_retry, + "control-data-rx", l2tp_tunnel->stats.data_rx, + "control-data-tx", l2tp_tunnel->stats.data_tx); + json_array_append(tunnels, tunnel); + } + l2tp_server = l2tp_server->next; + } + + root = json_pack("{ss si so}", + "status", "ok", + "code", 200, + "l2tp-tunnels", tunnels); + if(root) { + result = json_dumpfd(root, fd, 0); + json_decref(root); + } else { + result = bbl_ctrl_status(fd, "error", 500, "internal error"); + json_decref(tunnels); + } + return result; +} + +json_t * +l2tp_session_json(bbl_l2tp_session_t *l2tp_session) { + return json_pack("{ss si si si si ss ss ss ss si si ss ss}", + "state", l2tp_session_state_string(l2tp_session->state), + "tunnel-id", l2tp_session->key.tunnel_id, + "session-id", l2tp_session->key.session_id, + "peer-tunnel-id", l2tp_session->tunnel->peer_tunnel_id, + "peer-session-id", l2tp_session->peer_session_id, + "peer-proxy-auth-name", string_or_na(l2tp_session->proxy_auth_name), + "peer-called-number", string_or_na(l2tp_session->peer_called_number), + "peer-calling-number", string_or_na(l2tp_session->peer_calling_number), + "peer-sub-address", string_or_na(l2tp_session->peer_sub_address), + "peer-tx-bps", l2tp_session->peer_tx_bps, + "peer-rx-bps", l2tp_session->peer_rx_bps, + "peer-ari", string_or_na(l2tp_session->peer_ari), + "peer-aci", string_or_na(l2tp_session->peer_aci)); +} + +ssize_t +bbl_ctrl_l2tp_sessions(int fd, bbl_ctx_s *ctx, session_key_t *key __attribute__((unused)), json_t* arguments) { + ssize_t result = 0; + json_t *root, *sessions; + + bbl_l2tp_server_t *l2tp_server = ctx->config.l2tp_server; + bbl_l2tp_tunnel_t *l2tp_tunnel; + bbl_l2tp_session_t *l2tp_session; + l2tp_key_t l2tp_key = {0}; + void **search = NULL; + + int tunnel_id = 0; + int session_id = 0; + + json_unpack(arguments, "{s:i}", "tunnel-id", &tunnel_id); + json_unpack(arguments, "{s:i}", "session-id", &session_id); + + sessions = json_array(); + + if(tunnel_id && session_id) { + l2tp_key.tunnel_id = tunnel_id; + l2tp_key.session_id = session_id; + search = dict_search(ctx->l2tp_session_dict, &l2tp_key); + if(search) { + l2tp_session = *search; + json_array_append(sessions, l2tp_session_json(l2tp_session)); + } else { + result = bbl_ctrl_status(fd, "warning", 404, "session not found"); + json_decref(sessions); + return result; + } + } else if (tunnel_id) { + l2tp_key.tunnel_id = tunnel_id; + search = dict_search(ctx->l2tp_session_dict, &l2tp_key); + if(search) { + l2tp_session = *search; + l2tp_tunnel = l2tp_session->tunnel; + CIRCLEQ_FOREACH(l2tp_session, &l2tp_tunnel->session_qhead, session_qnode) { + if(!l2tp_session->key.session_id) continue; /* skip tunnel session */ + json_array_append(sessions, l2tp_session_json(l2tp_session)); + } + } else { + result = bbl_ctrl_status(fd, "warning", 404, "tunnel not found"); + json_decref(sessions); + return result; + } + } else { + while(l2tp_server) { + CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_server->tunnel_qhead, tunnel_qnode) { + CIRCLEQ_FOREACH(l2tp_session, &l2tp_tunnel->session_qhead, session_qnode) { + if(!l2tp_session->key.session_id) continue; /* skip tunnel session */ + json_array_append(sessions, l2tp_session_json(l2tp_session)); + } + } + l2tp_server = l2tp_server->next; + } + } + root = json_pack("{ss si so}", + "status", "ok", + "code", 200, + "l2tp-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; +} + +ssize_t +bbl_ctrl_l2tp_csurq(int fd, bbl_ctx_s *ctx, session_key_t *key __attribute__((unused)), json_t* arguments) { + json_t *sessions, *number; + + bbl_l2tp_tunnel_t *l2tp_tunnel; + bbl_l2tp_session_t *l2tp_session; + l2tp_key_t l2tp_key = {0}; + void **search = NULL; + + uint16_t session_id = 0; + int tunnel_id = 0; + int size, i; + + /* Unpack further arguments */ + if (json_unpack(arguments, "{s:i}", "tunnel-id", &tunnel_id) != 0) { + return bbl_ctrl_status(fd, "error", 400, "missing tunnel-id"); + } + l2tp_key.tunnel_id = tunnel_id; + search = dict_search(ctx->l2tp_session_dict, &l2tp_key); + if(search) { + l2tp_session = *search; + l2tp_tunnel = l2tp_session->tunnel; + if(l2tp_tunnel->state != BBL_L2TP_TUNNEL_ESTABLISHED) { + return bbl_ctrl_status(fd, "warning", 404, "tunnel not found"); + } + sessions = json_object_get(arguments, "sessions"); + if (json_is_array(sessions)) { + size = json_array_size(sessions); + l2tp_tunnel->csurq_requests_len = size; + l2tp_tunnel->csurq_requests = malloc(size * sizeof(uint16_t)); + for (i = 0; i < size; i++) { + number = json_array_get(sessions, i); + if(json_is_number(number)) { + session_id = json_number_value(number); + l2tp_tunnel->csurq_requests[i] = session_id; + } + } + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_CSURQ); + return bbl_ctrl_status(fd, "ok", 200, NULL); + } else { + return bbl_ctrl_status(fd, "error", 400, "invalid request"); + } + } else { + return bbl_ctrl_status(fd, "warning", 404, "tunnel not found"); + } +} + struct action { char *name; callback_function *fn; @@ -718,6 +914,9 @@ struct action actions[] = { {"igmp-leave", bbl_ctrl_igmp_leave}, {"igmp-info", bbl_ctrl_igmp_info}, {"li-flows", bbl_ctrl_li_flows}, + {"l2tp-tunnels", bbl_ctrl_l2tp_tunnels}, + {"l2tp-sessions", bbl_ctrl_l2tp_sessions}, + {"l2tp-csurq", bbl_ctrl_l2tp_csurq}, {NULL, NULL}, }; diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 1f4d2a2f..e73810f8 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -11,7 +11,7 @@ #include #include -static void +void bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, l2tp_message_type l2tp_type); const char* @@ -354,7 +354,7 @@ bbl_l2tp_tunnel_control_job (timer_s *timer) { * This parameter is only required of L2TP session packets. * @param l2tp_type L2TP message type (SCCRP, ICRP, ...). */ -static void +void bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, l2tp_message_type l2tp_type) { bbl_interface_s *interface = l2tp_tunnel->interface; @@ -680,6 +680,15 @@ bbl_l2tp_stopccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_RCVD_STOPCCN); } +static void +bbl_l2tp_csun_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_tunnel_t *l2tp_tunnel) { + + UNUSED(eth); + UNUSED(interface); + + bbl_l2tp_avp_decode_csun(l2tp, l2tp_tunnel); +} + static void bbl_l2tp_icrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface, bbl_l2tp_tunnel_t *l2tp_tunnel) { bbl_ctx_s *ctx = interface->ctx; @@ -1044,6 +1053,8 @@ bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_ return bbl_l2tp_iccn_rx(eth, l2tp, interface, l2tp_session); } break; + case L2TP_MESSAGE_CSUN: + return bbl_l2tp_csun_rx(eth, l2tp, interface, l2tp_tunnel); case L2TP_MESSAGE_CDN: if(l2tp_session->key.session_id) { return bbl_l2tp_cdn_rx(eth, l2tp, interface, l2tp_session); diff --git a/src/bbl_l2tp.h b/src/bbl_l2tp.h index 48c717a7..c301e544 100644 --- a/src/bbl_l2tp.h +++ b/src/bbl_l2tp.h @@ -125,6 +125,10 @@ typedef struct bbl_l2tp_tunnel_ /* Pointer to L2TP server configuration */ bbl_l2tp_server_t *server; + /* RFC5515 CSURQ */ + uint16_t *csurq_requests; + uint16_t csurq_requests_len; + /* L2TP tunnel state */ l2tp_tunnel_state_t state; uint32_t state_seconds; @@ -203,6 +207,7 @@ typedef struct bbl_l2tp_session_ uint16_t peer_session_id; bool data_sequencing; + bool connect_speed_update_enabled; uint32_t peer_tx_bps; uint32_t peer_rx_bps; @@ -224,20 +229,22 @@ typedef struct bbl_l2tp_session_ /* The following members must be freed * if session is destroyed! */ - uint8_t *proxy_auth_name; + char *proxy_auth_name; uint8_t *proxy_auth_challenge; uint8_t *proxy_auth_response; char *peer_called_number; char *peer_calling_number; char *peer_sub_address; - + char *peer_ari; + char *peer_aci; } bbl_l2tp_session_t; const char* l2tp_message_string(l2tp_message_type type); const char* l2tp_tunnel_state_string(l2tp_tunnel_state_t state); const char* l2tp_session_state_string(l2tp_session_state_t state); +void bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, l2tp_message_type l2tp_type); void bbl_l2tp_handler_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s *interface); void bbl_l2tp_stop_all_tunnel(bbl_ctx_s *ctx); diff --git a/src/bbl_l2tp_avp.c b/src/bbl_l2tp_avp.c index aa13121f..e19facc3 100644 --- a/src/bbl_l2tp_avp.c +++ b/src/bbl_l2tp_avp.c @@ -329,18 +329,48 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel, bb l2tp_session->proxy_auth_response_len = avp.len; } break; + case L2TP_AVP_CONNECT_SPEED_UPDATE_ENABLE: + /* See RFC5515 */ + l2tp_session->connect_speed_update_enabled = true; + break; default: if(avp.m) { LOG(L2TP, "L2TP Error (%s) Mandatory standard AVP with unknown type %u in %s from %s\n", - l2tp_tunnel->server->host_name, avp.type, - l2tp_message_string(l2tp->type), - format_ipv4_address(&l2tp_tunnel->peer_ip)); + l2tp_tunnel->server->host_name, avp.type, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; } else { LOG(L2TP, "L2TP Warning (%s) Optional standard AVP with unknown type %u in %s from %s\n", - l2tp_tunnel->server->host_name, avp.type, - l2tp_message_string(l2tp->type), - format_ipv4_address(&l2tp_tunnel->peer_ip)); + l2tp_tunnel->server->host_name, avp.type, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + } + break; + } + } else if(avp.vendor == BROADBAND_FORUM_VENDOR_ID) { + switch(avp.type) { + case ACCESS_LINE_ACI: + if(!l2tp_session->peer_aci && avp.len) { + l2tp_session->peer_aci = malloc(avp.len + 1); + memcpy(l2tp_session->peer_aci, avp.value, avp.len); + *(l2tp_session->peer_aci + avp.len) = '\0'; + } + break; + case ACCESS_LINE_ARI: + if(!l2tp_session->peer_ari && avp.len) { + l2tp_session->peer_ari = malloc(avp.len + 1); + memcpy(l2tp_session->peer_ari, avp.value, avp.len); + *(l2tp_session->peer_ari + avp.len) = '\0'; + } + break; + default: + if(avp.m) { + LOG(L2TP, "L2TP Error (%s) Mandatory Broadband Forum AVP with unknown type %u in %s from %s\n", + l2tp_tunnel->server->host_name, avp.type, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; } break; } @@ -516,6 +546,77 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel) { return true; } +bool +bbl_l2tp_avp_decode_csun(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel) { + + bbl_ctx_s *ctx = l2tp_tunnel->interface->ctx; + + uint8_t *buf = l2tp->payload; + uint16_t len = l2tp->payload_len; + bbl_l2tp_avp_t avp = {0}; + + bbl_l2tp_session_t *l2tp_session; + l2tp_key_t key = {0}; + void **search = NULL; + + key.tunnel_id = l2tp_tunnel->tunnel_id; + + while(len) { + if(bbl_l2tp_avp_decode(&buf, &len, &avp)) { + if(avp.h) { + return false; + } + if(avp.vendor == 0) { + switch(avp.type) { + case L2TP_AVP_CONNECT_SPEED_UPDATE: + if(avp.len != 12) { + LOG(L2TP, "L2TP Error (%s) Invalid L2TP connect speed update AVP in %s from %s\n", + l2tp_tunnel->server->host_name, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + key.session_id = be16toh(*(uint16_t*)(avp.value+2)); + search = dict_search(ctx->l2tp_session_dict, &key); + if(search) { + l2tp_session = *search; + if(l2tp_session->connect_speed_update_enabled && l2tp_session->state == BBL_L2TP_SESSION_ESTABLISHED) { + l2tp_session->peer_tx_bps = be32toh(*(uint32_t*)(avp.value+4)); + l2tp_session->peer_rx_bps = be32toh(*(uint32_t*)(avp.value+8)); + } + } + break; + default: + if(avp.m) { + LOG(L2TP, "L2TP Error (%s) Mandatory standard AVP with unknown type %u in %s from %s\n", + l2tp_tunnel->server->host_name, avp.type, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } else { + LOG(L2TP, "L2TP Warning (%s) Optional standard AVP with unknown type %u in %s from %s\n", + l2tp_tunnel->server->host_name, avp.type, + l2tp_message_string(l2tp->type), + format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + break; + } + } else { + if(avp.m) { + LOG(L2TP, "L2TP (%s) Mandatory AVP with unknown vendor %u received from %s\n", + l2tp_tunnel->server->host_name, avp.vendor, format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + } + } else { + LOG(L2TP, "L2TP (%s) Failed to decdoe tunnel attributes from %s\n", + l2tp_tunnel->server->host_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); + return false; + } + } + return true; +} void bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, @@ -525,6 +626,9 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_ uint16_t v16; uint32_t v32; + uint8_t update[12] = {0}; + + int i; if(l2tp_type == L2TP_MESSAGE_ZLB) { return; @@ -658,6 +762,22 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_ bbl_l2tp_avp_encode(&buf, len, &avp); } break; + case L2TP_MESSAGE_CSURQ: + if(l2tp_tunnel->csurq_requests_len) { + for(i = 0; i < l2tp_tunnel->csurq_requests_len; i++) { + /* Connect Speed Update */ + avp.m = false; + avp.type = L2TP_AVP_CONNECT_SPEED_UPDATE; + avp.len = 12; + avp.value_type = L2TP_AVP_VALUE_BYTES; + *(uint16_t*)(update +2) = htobe16(l2tp_tunnel->csurq_requests[i]); + avp.value = update; + bbl_l2tp_avp_encode(&buf, len, &avp); + } + l2tp_tunnel->csurq_requests_len = 0; + free(l2tp_tunnel->csurq_requests); + } + break; default: break; } diff --git a/src/bbl_l2tp_avp.h b/src/bbl_l2tp_avp.h index f3b10282..5903341e 100644 --- a/src/bbl_l2tp_avp.h +++ b/src/bbl_l2tp_avp.h @@ -78,6 +78,9 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel, bb bool bbl_l2tp_avp_decode_tunnel(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel); +bool +bbl_l2tp_avp_decode_csun(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel); + void bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, l2tp_message_type l2tp_type, uint8_t *buf, uint16_t *len); From 40d23ec4cd13cfb6aebb121282b0c4ceac69c2f3 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Tue, 23 Feb 2021 22:04:48 +0100 Subject: [PATCH 18/27] #3 L2TPv2 LNS Support + update docs + add L2TP data traffic config options + ... --- cli.py | 10 +- docs/config.md | 52 +++++++ docs/ctrl.md | 31 ++++ docs/l2tp.md | 381 +++++++++++++++++++++++++++++++++++++++++++++++ src/bbl_config.c | 12 ++ src/bbl_l2tp.c | 9 +- src/bbl_l2tp.h | 4 + 7 files changed, 495 insertions(+), 4 deletions(-) diff --git a/cli.py b/cli.py index 501d71ce..496d872c 100755 --- a/cli.py +++ b/cli.py @@ -3,6 +3,7 @@ import socket import os import json +import ast def usage(): print(""" @@ -14,6 +15,8 @@ def usage(): {c} run.sock session-info outer-vlan 1 inner-vlan 1 {c} run.sock igmp-join outer-vlan 1 inner-vlan 1 group 239.0.0.1 source1 1.1.1.1 source2 2.2.2.2 source3 3.3.3.3 {c} run.sock igmp-info outer-vlan 1 inner-vlan 1 + {c} run.sock l2tp-csurq tunnel-id 1 sessions [1,2] + """.format(c=sys.argv[0])) sys.exit(1) @@ -33,11 +36,12 @@ def main(): if(len(sys.argv)) > 4: request["arguments"] = {} for i in range(3, len(sys.argv), 2): + arg = sys.argv[i+1] try: - request["arguments"][sys.argv[i]] = int(sys.argv[i+1]) + request["arguments"][sys.argv[i]] = int(arg) except: - request["arguments"][sys.argv[i]] = sys.argv[i+1] - + request["arguments"][sys.argv[i]] = ast.literal_eval(arg) + #print(json.dumps(request).encode('utf-8')) if os.path.exists(socket_path): client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: diff --git a/docs/config.md b/docs/config.md index 2b0f572c..ee719a06 100644 --- a/docs/config.md +++ b/docs/config.md @@ -442,3 +442,55 @@ Attribute | Description | Default `ipv4-pps` | Generate bidirectional IPv4 traffic between network interface and all session framed IPv4 addresses | 0 (disabled) `ipv6-pps` | Generate bidirectional IPv6 traffic between network interface and all session framed IPv6 addresses | 0 (disabled) `ipv6pd-pps` | Generate bidirectional Ipv6 traffic between network interface and all session delegated IPv6 addresses | 0 (disabled) + +## l2TP Server + +This section describes all attributes of the `l2tp-server` (LNS) hierarchy. + +The BNG Blaster supports multiple L2TPv2 servers (LNS) over the network interface +as shown in the example below. + +```json +{ + "interfaces": { + "network": { + "interface": "eth2", + "address": "10.0.0.1", + "gateway": "10.0.0.2" + } + }, + "l2tp-server": [ + { + "name": "LNS1", + "address": "10.0.0.10", + "secret": "test1", + }, + { + "name": "LNS2", + "address": "10.0.0.11", + "secret": "test2", + }, + ] +} +``` + +There is actually no hard limit in the amount of L2TP servers. + +Attribute | Description | Default +--------- | ----------- | ------- +`name` | Mandatory L2TP LNS server hostname (AVP 7) | +`address` | Mandatory L2TP server address | +`secret` | Tunnel secret | +`receive-window-size` | Control messages receive window size | 4 +`max-retry` | Control messages max retry | 30 +`congestion-mode` | Control messages congestion mode | default +`data-control-priority` | Set the priority bit in the L2TP header for all non-IP data packets (LCP, IPCP, ...) | false +`data-length` | Set length bit for all data packets | false +`data-offset` | Set offset bit with offset zero for all data packets | false + +The BNG Blaster supports different congestion modes for the +reliable delivery of control messages. The `default` mode +is described in RFC2661 appendix A (Control Channel Slow Start and +Congestion Avoidance). The mode `slow` uses a fixed control window +size of 1 where `aggressive` sticks to max permitted based on peer +received window size. \ No newline at end of file diff --git a/docs/ctrl.md b/docs/ctrl.md index 783df89b..9a105d5b 100644 --- a/docs/ctrl.md +++ b/docs/ctrl.md @@ -148,3 +148,34 @@ Attribute | Description | Mandatory Arguments | Optional Arguments `igmp-join` | Join group | `group` | `source1`, `source2`, `source3` `igmp-leave` | Leave group | `group` | `igmp-info` | IGMP information | | + +### L2TP Commands + +Attribute | Description | Mandatory Arguments | Optional Arguments +--------- | ----------- | ------------------- | ------------------ +`l2tp-tunnels` | L2TP tunnel information | | +`l2tp-sessions` | L2TP session information | | `tunnel-id`, `session-id` +`l2tp-csurq`| Send L2TP CSURQ | `tunnel-id` | `sessions` + +The L2TP CSURQ command expects the local tunnel-id and a list of remote +session-id for which a connect speed update is requested. + +`$ cat command.json | jq .` +```json +{ + "command": "l2tp-csurq", + "arguments": { + "tunnel-id": 1, + "sessions": [ + 1, + 2, + 3, + 4 + ] + } +} +``` + +This command can be executed as shown below using the CLI tool. + +`$ sudo ./cli.py run.sock l2tp-csurq tunnel-id 1 sessions [1,2,3,4]` \ No newline at end of file diff --git a/docs/l2tp.md b/docs/l2tp.md index 2bd125bb..9195adaf 100644 --- a/docs/l2tp.md +++ b/docs/l2tp.md @@ -1,2 +1,383 @@ # L2TPv2 +The BNG Blaster is able to emulate L2TPv2 (RFC2661) LNS servers to +be able to test the L2TPv2 LAC functionality of the BNG device under +test. + +## Configuration + +Following an example with 30 L2TP LNS servers. + +```json +{ + "interfaces": { + "network": { + "interface": "eth2", + "address": "10.0.0.1", + "gateway": "10.0.0.2", + "address-ipv6": "fc66:1337:7331:8::10", + "gateway-ipv6": "fc66:1337:7331:8::1" + }, + "access": [ + { + "interface": "eth1", + "outer-vlan-min": 1, + "outer-vlan-max": 4000, + "inner-vlan-min": 7, + "inner-vlan-max": 7, + "authentication-protocol": "PAP" + }, + { + "interface": "eth1", + "outer-vlan-min": 1, + "outer-vlan-max": 4000, + "inner-vlan-min": 8, + "inner-vlan-max": 8, + "authentication-protocol": "CHAP" + } + ] + }, + "pppoe": { + "reconnect": true, + "discovery-timeout": 3, + "discovery-retry": 10 + }, + "ppp": { + "mru": 1492, + "authentication": { + "username": "blaster@l2tp.de", + "password": "test", + "timeout": 1, + "retry": 60 + }, + "lcp": { + "conf-request-timeout": 5, + "conf-request-retry": 30, + "keepalive-interval": 30, + "keepalive-retry": 3 + }, + "ipcp": { + "enable": true + }, + "ip6cp": { + "enable": true + } + }, + "access-line": { + "agent-remote-id": "DEU.RTBRICK.{session}", + "agent-circuit-id": "0.0.0.0/0.0.0.0 eth 0:{session}", + "rate-up": 1024, + "rate-down": 16384 + }, + "l2tp-server": [ + { + "name": "LNS1", + "address": "10.0.0.11", + "secret": "test1", + "receive-window-size": 8 + }, + { + "name": "LNS2", + "address": "10.0.0.12", + "secret": "test2", + "receive-window-size": 8 + }, + { + "name": "LNS3", + "address": "10.0.0.13", + "secret": "test3", + "receive-window-size": 8 + }, + { + "name": "LNS4", + "address": "10.0.0.14", + "secret": "test4", + "receive-window-size": 8 + }, + { + "name": "LNS5", + "address": "10.0.0.15", + "secret": "test5", + "receive-window-size": 8 + }, + { + "name": "LNS6", + "address": "10.0.0.16", + "secret": "test6", + "receive-window-size": 8 + }, + { + "name": "LNS7", + "address": "10.0.0.17", + "secret": "test7", + "receive-window-size": 8 + }, + { + "name": "LNS8", + "address": "10.0.0.18", + "secret": "test8", + "receive-window-size": 8 + }, + { + "name": "LNS9", + "address": "10.0.0.19", + "secret": "test9", + "receive-window-size": 8 + }, + { + "name": "LNS10", + "address": "10.0.0.20", + "secret": "test10", + "receive-window-size": 8 + }, + { + "name": "LNS11", + "address": "10.0.0.21", + "secret": "test11", + "receive-window-size": 8 + }, + { + "name": "LNS12", + "address": "10.0.0.22", + "secret": "test12", + "receive-window-size": 8 + }, + { + "name": "LNS13", + "address": "10.0.0.23", + "secret": "test13", + "receive-window-size": 8 + }, + { + "name": "LNS14", + "address": "10.0.0.24", + "secret": "test14", + "receive-window-size": 8 + }, + { + "name": "LNS15", + "address": "10.0.0.25", + "secret": "test15", + "receive-window-size": 8 + }, + { + "name": "LNS16", + "address": "10.0.0.26", + "secret": "test16", + "receive-window-size": 8 + }, + { + "name": "LNS17", + "address": "10.0.0.27", + "secret": "test17", + "receive-window-size": 8 + }, + { + "name": "LNS18", + "address": "10.0.0.28", + "secret": "test18", + "receive-window-size": 8 + }, + { + "name": "LNS19", + "address": "10.0.0.29", + "secret": "test19", + "receive-window-size": 8 + }, + { + "name": "LNS20", + "address": "10.0.0.30", + "secret": "test20", + "receive-window-size": 8 + }, + { + "name": "LNS21", + "address": "10.0.0.31", + "secret": "test21", + "receive-window-size": 8 + }, + { + "name": "LNS22", + "address": "10.0.0.32", + "secret": "test22", + "receive-window-size": 8 + }, + { + "name": "LNS23", + "address": "10.0.0.33", + "secret": "test23", + "receive-window-size": 8 + }, + { + "name": "LNS24", + "address": "10.0.0.34", + "secret": "test24", + "receive-window-size": 8 + }, + { + "name": "LNS25", + "address": "10.0.0.35", + "secret": "test25", + "receive-window-size": 8 + }, + { + "name": "LNS26", + "address": "10.0.0.36", + "secret": "test26", + "receive-window-size": 8 + }, + { + "name": "LNS27", + "address": "10.0.0.37", + "secret": "test27", + "receive-window-size": 8 + }, + { + "name": "LNS28", + "address": "10.0.0.38", + "secret": "test28", + "receive-window-size": 8 + }, + { + "name": "LNS29", + "address": "10.0.0.39", + "secret": "test29", + "receive-window-size": 8 + }, + { + "name": "LNS30", + "address": "10.0.0.40", + "secret": "test30", + "receive-window-size": 8 + } + ], + "session-traffic": { + "autostart": true, + "ipv4-pps": 1 + } +} +``` + +## Receive Tunnel Information + +`$ sudo ./cli.py run.sock l2tp-tunnels` +```json +{ + "status": "ok", + "code": 200, + "l2tp-tunnels": [ + { + "state": "Established", + "server-name": "LNS1", + "server-address": "10.0.0.11", + "tunnel-id": 1, + "peer-tunnel-id": 50011, + "peer-name": "BNG", + "peer-address": "10.0.0.2", + "peer-vendor": "RtBrick, Inc.", + "secret": "test1", + "control-packets-rx": 102, + "control-packets-rx-dup": 0, + "control-packets-rx-out-of-order": 0, + "control-packets-tx": 102, + "control-packets-tx-retry": 0, + "control-data-rx": 1406, + "control-data-tx": 206 + } + ] +} +``` + +## Receive Session Information + +The `l2tp-sessions` command returns all L2TP sessions. + +`$ sudo ./cli.py run.sock l2tp-sessions` +```json +{ + "status": "ok", + "code": 200, + "l2tp-sessions": [ + { + "state": "Established", + "tunnel-id": 1, + "session-id": 1, + "peer-tunnel-id": 50011, + "peer-session-id": 32867, + "peer-proxy-auth-name": "blaster@l2tp.de", + "peer-called-number": "N/A", + "peer-calling-number": "N/A", + "peer-sub-address": "N/A", + "peer-tx-bps": 48000, + "peer-rx-bps": 1000, + "peer-ari": "DEU.RTBRICK.1", + "peer-aci": "0.0.0.0/0.0.0.0 eth 0:1" + } + ] +} +``` + +This output can be also filtered to return only sessions +of a given tunnel. + +`sudo ./cli.py run.sock l2tp-sessions tunnel-id 1` + +It is also possible to display a single session. + +`$ sudo ./cli.py run.sock l2tp-sessions tunnel-id 1 session-id 1` + +## RFC5515 + +The Agent-Circuit-Id and Agent-Remote-Id AVP defined in RFC5515 +is supported and stored for each session if received. Received +CSUN messages are processed correctly and via control socket it +is possible to send also CSURQ requests to the LAC. + +## Variable Data Header + +The L2TP protocol allows different data header options resulting in +variable header lengths. The most common options can be tested with just +four servers as shown in the example below. + +```json +{ + "l2tp-server": [ + { + "name": "LNS1", + "address": "10.0.0.11", + "secret": "test1", + "receive-window-size": 8, + "congestion-mode": "default", + "data-control-priority": true + }, + { + "name": "LNS2", + "address": "10.0.0.12", + "secret": "test2", + "receive-window-size": 8, + "congestion-mode": "default", + "data-control-priority": true, + "data-length": true + }, + { + "name": "LNS3", + "address": "10.0.0.11", + "secret": "test3", + "receive-window-size": 8, + "congestion-mode": "default", + "data-control-priority": true, + "data-offset": true + }, + { + "name": "LNS4", + "address": "10.0.0.12", + "secret": "test4", + "receive-window-size": 8, + "congestion-mode": "default", + "data-control-priority": true, + "data-length": true, + "data-offset": true + } + ] +} +``` \ No newline at end of file diff --git a/src/bbl_config.c b/src/bbl_config.c index ebade12f..b94cab31 100644 --- a/src/bbl_config.c +++ b/src/bbl_config.c @@ -727,6 +727,18 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { } else { l2tp_server->congestion_mode = BBL_L2TP_CONGESTION_DEFAULT; } + value = json_object_get(sub, "data-control-priority"); + if (json_is_boolean(value)) { + l2tp_server->data_control_priority = json_boolean_value(value); + } + value = json_object_get(sub, "data-length"); + if (json_is_boolean(value)) { + l2tp_server->data_lenght = json_boolean_value(value); + } + value = json_object_get(sub, "data-offset"); + if (json_is_boolean(value)) { + l2tp_server->data_offset = json_boolean_value(value); + } } } else if (json_is_object(sub)) { fprintf(stderr, "JSON config error: List expected in L2TP server configuration but dictionary found\n"); diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index e73810f8..c7a8b901 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -446,6 +446,7 @@ static void bbl_l2tp_send_data(bbl_l2tp_session_t *l2tp_session, uint16_t protocol, void *next) { bbl_l2tp_tunnel_t *l2tp_tunnel = l2tp_session->tunnel; + bbl_l2tp_server_t *l2tp_server = l2tp_tunnel->server; bbl_interface_s *interface = l2tp_tunnel->interface; bbl_l2tp_queue_t *q = calloc(1, sizeof(bbl_l2tp_queue_t)); bbl_ethernet_header_t eth = {0}; @@ -471,6 +472,13 @@ bbl_l2tp_send_data(bbl_l2tp_session_t *l2tp_session, uint16_t protocol, void *ne l2tp.tunnel_id = l2tp_tunnel->peer_tunnel_id; l2tp.session_id = l2tp_session->peer_session_id; l2tp.protocol = protocol; + l2tp.with_length = l2tp_server->data_lenght; + l2tp.with_offset = l2tp_server->data_offset; + if(l2tp_server->data_control_priority) { + if(protocol != PROTOCOL_IPV4 && protocol != PROTOCOL_IPV6) { + l2tp.with_priority = true; + } + } l2tp.next = next; q->data = true; if(encode_ethernet(q->packet, &len, ð) == PROTOCOL_SUCCESS) { @@ -479,7 +487,6 @@ bbl_l2tp_send_data(bbl_l2tp_session_t *l2tp_session, uint16_t protocol, void *ne l2tp_tunnel->stats.data_tx++; interface->stats.l2tp_data_tx++; } else { - /* Encode error.... */ LOG(ERROR, "L2TP Data Encode Error!\n"); free(q); } diff --git a/src/bbl_l2tp.h b/src/bbl_l2tp.h index c301e544..b44ea654 100644 --- a/src/bbl_l2tp.h +++ b/src/bbl_l2tp.h @@ -67,6 +67,10 @@ typedef struct bbl_l2tp_server_ uint16_t receive_window; uint16_t max_retry; + bool data_control_priority; + bool data_lenght; + bool data_offset; + l2tp_congestion_mode_t congestion_mode; char *secret; From d7f80e1b04a0174c8aaf7c9c7c10cc883ecf4e36 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Wed, 24 Feb 2021 17:02:23 +0100 Subject: [PATCH 19/27] #3 L2TPv2 LNS Support + Add result codes to CDN and STOPCCN + Fix attribute hiding + ... --- src/bbl_ctrl.c | 6 +++--- src/bbl_l2tp.c | 25 ++++++++++++++++++++++ src/bbl_l2tp.h | 8 +++++++ src/bbl_l2tp_avp.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 3 deletions(-) diff --git a/src/bbl_ctrl.c b/src/bbl_ctrl.c index 8dfdf590..ee040430 100644 --- a/src/bbl_ctrl.c +++ b/src/bbl_ctrl.c @@ -692,9 +692,9 @@ bbl_ctrl_li_flows(int fd, bbl_ctx_s *ctx, session_key_t *key __attribute__((unus } } root = json_pack("{ss si so}", - "status", "ok", - "code", 200, - "li-flows", flows); + "status", "ok", + "code", 200, + "li-flows", flows); if(root) { result = json_dumpfd(root, fd, 0); json_decref(root); diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index c7a8b901..4e13ff7d 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -265,6 +265,9 @@ bbl_l2tp_tunnel_tx_job (timer_s *timer) { l2tp_tunnel->stats.control_retry++; interface->stats.l2tp_control_retry++; if(q->retries > l2tp_tunnel->server->max_retry) { + l2tp_tunnel->result_code = 2; + l2tp_tunnel->error_code = 6; + l2tp_tunnel->error_message = "max retry"; bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } @@ -311,6 +314,9 @@ bbl_l2tp_tunnel_control_job (timer_s *timer) { switch(l2tp_tunnel->state) { case BBL_L2TP_TUNNEL_WAIT_CTR_CONN: if(l2tp_tunnel->state_seconds > 30) { + l2tp_tunnel->result_code = 2; + l2tp_tunnel->error_code = 6; + l2tp_tunnel->error_message = "timeout"; bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } @@ -600,6 +606,9 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s LOG(ERROR, "L2TP Error (%s) Missing challenge in SCCRQ from %s\n", l2tp_tunnel->server->host_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); + l2tp_tunnel->result_code = 2; + l2tp_tunnel->error_code = 6; + l2tp_tunnel->error_message = "missing challenge"; l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; } } else { @@ -609,6 +618,9 @@ bbl_l2tp_sccrq_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s LOG(ERROR, "L2TP Error (%s) No secret found but challenge received in SCCRQ from %s\n", l2tp_tunnel->server->host_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); + l2tp_tunnel->result_code = 2; + l2tp_tunnel->error_code = 6; + l2tp_tunnel->error_message = "no challenge expected"; l2tp_tunnel->state = BBL_L2TP_TUNNEL_SEND_STOPCCN; } } @@ -648,6 +660,9 @@ bbl_l2tp_scccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s l2tp_tunnel->server->host_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); + l2tp_tunnel->result_code = 2; + l2tp_tunnel->error_code = 6; + l2tp_tunnel->error_message = "decode error"; return bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } /* Check challenge response ... */ @@ -663,6 +678,9 @@ bbl_l2tp_scccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s l2tp_tunnel->server->host_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); + l2tp_tunnel->result_code = 2; + l2tp_tunnel->error_code = 6; + l2tp_tunnel->error_message = "challenge authentication failed"; return bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } } else { @@ -670,6 +688,9 @@ bbl_l2tp_scccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s l2tp_tunnel->server->host_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); + l2tp_tunnel->result_code = 2; + l2tp_tunnel->error_code = 6; + l2tp_tunnel->error_message = "missing challenge response"; return bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } } @@ -759,6 +780,9 @@ bbl_l2tp_iccn_rx(bbl_ethernet_header_t *eth, bbl_l2tp_t *l2tp, bbl_interface_s * } if(!bbl_l2tp_avp_decode_session(l2tp, l2tp_tunnel, l2tp_session)) { + l2tp_session->result_code = 2; + l2tp_session->error_code = 6; + l2tp_session->error_message = "decode error"; bbl_l2tp_send(l2tp_tunnel, l2tp_session, L2TP_MESSAGE_CDN); return bbl_l2tp_session_delete(l2tp_session); } @@ -1118,6 +1142,7 @@ bbl_l2tp_stop_all_tunnel(bbl_ctx_s *ctx) { CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_server->tunnel_qhead, tunnel_qnode) { if(l2tp_tunnel->state < BBL_L2TP_TUNNEL_SEND_STOPCCN) { bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); + l2tp_tunnel->result_code = 6; bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); } } diff --git a/src/bbl_l2tp.h b/src/bbl_l2tp.h index b44ea654..5a300bbf 100644 --- a/src/bbl_l2tp.h +++ b/src/bbl_l2tp.h @@ -163,6 +163,10 @@ typedef struct bbl_l2tp_tunnel_ uint16_t cwcount; uint32_t send_timestamp; + uint16_t result_code; + uint16_t error_code; + char* error_message; + bool zlb; bbl_l2tp_queue_t *zlb_qnode; @@ -230,6 +234,10 @@ typedef struct bbl_l2tp_session_ uint8_t ipcp_state; uint8_t ip6cp_state; + uint16_t result_code; + uint16_t error_code; + char* error_message; + /* The following members must be freed * if session is destroyed! */ diff --git a/src/bbl_l2tp_avp.c b/src/bbl_l2tp_avp.c index e19facc3..e03fed79 100644 --- a/src/bbl_l2tp_avp.c +++ b/src/bbl_l2tp_avp.c @@ -90,6 +90,41 @@ bbl_l2tp_avp_encode(uint8_t **_buf, uint16_t *len, bbl_l2tp_avp_t *avp) { *_buf = buf; } +static void +bbl_l2tp_avp_encode_result_code(uint8_t **_buf, uint16_t *len, + uint16_t result_code, uint16_t error_code, + char *error_message) { + uint8_t *avp_len_field; + uint16_t avp_len = L2TP_AVP_HDR_LEN; + uint8_t *buf = *_buf; + + avp_len_field = buf; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + *(uint16_t*)buf = 0; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + *(uint16_t*)buf = htobe16(L2TP_AVP_RESULT_CODE); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + + *(uint16_t*)buf = htobe16(result_code); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + avp_len += 2; + + if(error_code) { + *(uint16_t*)buf = htobe16(error_code); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + avp_len += 2; + if(error_message) { + memcpy(buf, error_message, strlen(error_message)); + BUMP_WRITE_BUFFER(buf, len, strlen(error_message)); + avp_len += strlen(error_message); + } + } + + avp_len |= L2TP_AVP_M_BIT_MASK; + *(uint16_t*)avp_len_field = htobe16(avp_len); + *_buf = buf; +} + /* bbl_l2tp_avp_unhide */ static bool bbl_l2tp_avp_unhide(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_avp_t *avp, uint8_t @@ -329,6 +364,10 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel, bb l2tp_session->proxy_auth_response_len = avp.len; } break; + case L2TP_AVP_RANDOM_VECTOR: + random_vector = avp.value; + random_vector_len = avp.len; + break; case L2TP_AVP_CONNECT_SPEED_UPDATE_ENABLE: /* See RFC5515 */ l2tp_session->connect_speed_update_enabled = true; @@ -515,6 +554,10 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_t *l2tp, bbl_l2tp_tunnel_t *l2tp_tunnel) { memcpy(l2tp_tunnel->peer_challenge_response, avp.value, avp.len); } break; + case L2TP_AVP_RANDOM_VECTOR: + random_vector = avp.value; + random_vector_len = avp.len; + break; default: if(avp.m) { LOG(L2TP, "L2TP Error (%s) Mandatory standard AVP with unknown type %u in %s from %s\n", @@ -739,6 +782,11 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_ avp.value_type = L2TP_AVP_VALUE_UINT16; avp.value = (void*)(&l2tp_tunnel->tunnel_id); bbl_l2tp_avp_encode(&buf, len, &avp); + /* Result Code */ + bbl_l2tp_avp_encode_result_code(&buf, len, + l2tp_tunnel->result_code, + l2tp_tunnel->error_code, + l2tp_tunnel->error_message); break; case L2TP_MESSAGE_ICRP: /* Assigned Session ID */ @@ -760,6 +808,11 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_ avp.value_type = L2TP_AVP_VALUE_UINT16; avp.value = (void*)(&l2tp_session->key.session_id); bbl_l2tp_avp_encode(&buf, len, &avp); + /* Result Code */ + bbl_l2tp_avp_encode_result_code(&buf, len, + l2tp_session->result_code, + l2tp_session->error_code, + l2tp_session->error_message); } break; case L2TP_MESSAGE_CSURQ: From 0c4ad43c690c319487a977fef51b3b3ca1004bed Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Wed, 24 Feb 2021 23:36:41 +0100 Subject: [PATCH 20/27] PPPoE enhancements ... + add PPPoE service name support #13 + add PPPoE host uniq support + use dynamic memory for username, password, ari and aci + --- docs/config.md | 2 + docs/intro.md | 2 +- src/bbl.c | 57 +++++++++++++------ src/bbl.h | 39 +++++++------ src/bbl_config.c | 40 +++++++------ src/bbl_logging.c | 3 +- src/bbl_logging.h | 3 +- src/bbl_protocols.c | 133 +++++++++++++++++++++++++++----------------- src/bbl_protocols.h | 6 ++ src/bbl_rx.c | 68 +++++++++++++++++++--- src/bbl_tx.c | 22 ++++++-- 11 files changed, 254 insertions(+), 121 deletions(-) diff --git a/docs/config.md b/docs/config.md index ee719a06..a8ea0b11 100644 --- a/docs/config.md +++ b/docs/config.md @@ -318,6 +318,8 @@ Attribute | Description | Default `reconnect` | Automatically reconnect sessions if terminated | false `discovery-timeout` | PPPoE discovery (PADI and PADR) timeout in seconds | 5 `discovery-retry` | PPPoE discovery (PADI and PADR) max retry | 10 +`service-name` | PPPoE discovery service name | +`host-uniq` | PPPoE discovery host-uniq | false ## PPP diff --git a/docs/intro.md b/docs/intro.md index 4897a06a..942bf1a2 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -36,7 +36,7 @@ Usage: bngblaster [OPTIONS] -h --help -C --config - -l --logging error|igmp|io|lcp|ncp|normal|pcap|timer|timer-detail|ip + -l --logging error|igmp|io|pppoe|normal|pcap|timer|timer-detail|ip -L --log-file -u --username -p --password diff --git a/src/bbl.c b/src/bbl.c index 02ecef66..895e4aaa 100644 --- a/src/bbl.c +++ b/src/bbl.c @@ -151,7 +151,18 @@ bbl_session_update_state(bbl_ctx_s *ctx, bbl_session_s *session, session_state_t CIRCLEQ_INSERT_TAIL(&ctx->sessions_idle_qhead, session, session_idle_qnode); memset(&session->server_mac, 0xff, ETH_ADDR_LEN); // init with broadcast MAC session->pppoe_session_id = 0; + if(session->pppoe_ac_cookie) { + free(session->pppoe_ac_cookie); + session->pppoe_ac_cookie = NULL; + } session->pppoe_ac_cookie_len = 0; + if(!session->interface->ctx->config.pppoe_service_name) { + if(session->pppoe_service_name) { + free(session->pppoe_service_name); + session->pppoe_service_name = NULL; + } + session->pppoe_service_name_len = 0; + } session->ip_address = 0; session->peer_ip_address = 0; session->dns1 = 0; @@ -711,10 +722,10 @@ bbl_add_session (bbl_ctx_s *ctx, bbl_interface_s *interface, bbl_session_s *sess memcpy(session->client_mac, session_template->client_mac, ETH_ADDR_LEN); session->mru = session_template->mru; session->magic_number = session_template->magic_number; - snprintf(session->username, USERNAME_LEN, "%s", session_template->username); - snprintf(session->password, PASSWORD_LEN, "%s", session_template->password); - snprintf(session->agent_circuit_id, ACI_LEN, "%s", session_template->agent_circuit_id); - snprintf(session->agent_remote_id, ARI_LEN, "%s", session_template->agent_remote_id); + session->username = session_template->username; + session->password = session_template->password; + session->agent_circuit_id = session_template->agent_circuit_id; + session->agent_remote_id = session_template->agent_remote_id; session->rate_up = session_template->rate_up; session->rate_down = session_template->rate_down; session->duid[1] = 3; @@ -725,7 +736,13 @@ bbl_add_session (bbl_ctx_s *ctx, bbl_interface_s *interface, bbl_session_s *sess session->igmp_robustness = 2; /* init robustness with 2 */ session->zapping_group_max = be32toh(ctx->config.igmp_group) + ((ctx->config.igmp_group_count - 1) * be32toh(ctx->config.igmp_group_iter)); session->session_traffic = access_config->session_traffic_autostart; - if(session->access_type == ACCESS_TYPE_IPOE) { + if(session->access_type == ACCESS_TYPE_PPPOE) { + if(ctx->config.pppoe_service_name) { + session->pppoe_service_name = (uint8_t*)ctx->config.pppoe_service_name; + session->pppoe_service_name_len = strlen(ctx->config.pppoe_service_name); + } + session->pppoe_host_uniq = session_template->pppoe_host_uniq; + } else if(session->access_type == ACCESS_TYPE_IPOE) { if(access_config->static_ip && access_config->static_gateway) { session->ip_address = access_config->static_ip; session->peer_ip_address = access_config->static_gateway; @@ -830,30 +847,38 @@ bbl_init_sessions (bbl_ctx_s *ctx) session_template.client_mac[3] = i>>16; session_template.client_mac[4] = i>>8; session_template.client_mac[5] = i; - session_template.magic_number = i; + session_template.magic_number = htobe32(i); + if(ctx->config.pppoe_host_uniq) { + session_template.pppoe_host_uniq = htobe64(i); + } /* Populate session identifiaction attributes */ snprintf(snum1, 6, "%d", i); snprintf(snum2, 6, "%d", access_config->sessions); + /* Update username */ s = replace_substring(access_config->username, "{session-global}", snum1); - snprintf(session_template.username, USERNAME_LEN, "%s", s); + session_template.username = s; s = replace_substring(session_template.username, "{session}", snum2); - snprintf(session_template.username, USERNAME_LEN, "%s", s); + session_template.username = strdup(s); + /* Update password */ s = replace_substring(access_config->password, "{session-global}", snum1); - snprintf(session_template.password, PASSWORD_LEN, "%s", s); + session_template.password = s; s = replace_substring(session_template.password, "{session}", snum2); - snprintf(session_template.password, PASSWORD_LEN, "%s", s); + session_template.password = strdup(s); + /* Update ACI */ s = replace_substring(access_config->agent_circuit_id, "{session-global}", snum1); - snprintf(session_template.agent_circuit_id, ACI_LEN, "%s", s); + session_template.agent_circuit_id = s; s = replace_substring(session_template.agent_circuit_id, "{session}", snum2); - snprintf(session_template.agent_circuit_id, ACI_LEN, "%s", s); + session_template.agent_circuit_id = strdup(s); + /* Update ARI */ s = replace_substring(access_config->agent_remote_id, "{session-global}", snum1); - snprintf(session_template.agent_remote_id, ARI_LEN, "%s", s); + session_template.agent_remote_id = s; s = replace_substring(session_template.agent_remote_id, "{session}", snum2); - snprintf(session_template.agent_remote_id, ARI_LEN, "%s", s); + session_template.agent_remote_id = strdup(s); + /* Update rates ... */ session_template.rate_up = access_config->rate_up; session_template.rate_down = access_config->rate_down; @@ -1200,8 +1225,8 @@ main (int argc, char *argv[]) fprintf(stderr, "Error: Failed to load configuration file %s\n", config_file); exit(1); } - if(username) snprintf(ctx->config.username, USERNAME_LEN, "%s", username); - if(password) snprintf(ctx->config.password, PASSWORD_LEN, "%s", password); + if(username) ctx->config.username = username; + if(password) ctx->config.password = password; if(sessions) ctx->config.sessions = atoi(sessions); if(igmp_group) { inet_pton(AF_INET, igmp_group, &ipv4); diff --git a/src/bbl.h b/src/bbl.h index 10396501..5f1a5683 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -49,12 +49,6 @@ #define WRITE_BUF_LEN 1514 #define SCRATCHPAD_LEN 1514 -#define PPPOE_AC_COOKIE_LEN 32 - -#define USERNAME_LEN 251 -#define PASSWORD_LEN 65 -#define ARI_LEN 65 -#define ACI_LEN 65 #define CHALLENGE_LEN 16 /* Access Interface */ @@ -312,13 +306,13 @@ typedef struct bbl_access_config_ uint32_t static_gateway_iter; /* Authentication */ - char username[USERNAME_LEN]; - char password[PASSWORD_LEN]; + char *username; + char *password; uint16_t authentication_protocol; /* Access Line */ - char agent_remote_id[ARI_LEN]; - char agent_circuit_id[ACI_LEN]; + char *agent_remote_id; + char *agent_circuit_id; uint32_t rate_up; uint32_t rate_down; @@ -461,12 +455,12 @@ typedef struct bbl_ctx_ uint32_t static_gateway_iter; /* Authentication */ - char username[USERNAME_LEN]; - char password[PASSWORD_LEN]; + char *username; + char *password; /* Access Line */ - char agent_remote_id[ARI_LEN]; - char agent_circuit_id[ACI_LEN]; + char *agent_remote_id; + char *agent_circuit_id; uint32_t rate_up; uint32_t rate_down; @@ -474,7 +468,9 @@ typedef struct bbl_ctx_ uint32_t pppoe_session_time; uint16_t pppoe_discovery_timeout; uint16_t pppoe_discovery_retry; - bool pppoe_reconnect; + char *pppoe_service_name; + bool pppoe_reconnect; + bool pppoe_host_uniq; /* PPP */ uint16_t ppp_mru; @@ -639,15 +635,15 @@ typedef struct bbl_session_ bool l2tp; /* Authentication */ - char username[USERNAME_LEN]; - char password[PASSWORD_LEN]; + char *username; + char *password; uint8_t chap_identifier; uint8_t chap_response[CHALLENGE_LEN]; /* Access Line */ - char agent_circuit_id[ACI_LEN]; - char agent_remote_id[ARI_LEN]; + char *agent_circuit_id; + char *agent_remote_id; uint32_t rate_up; uint32_t rate_down; @@ -657,8 +653,11 @@ typedef struct bbl_session_ /* PPPoE */ uint16_t pppoe_session_id; - uint8_t pppoe_ac_cookie[PPPOE_AC_COOKIE_LEN]; + uint8_t *pppoe_ac_cookie; uint16_t pppoe_ac_cookie_len; + uint8_t *pppoe_service_name; + uint16_t pppoe_service_name_len; + uint64_t pppoe_host_uniq; /* LCP */ ppp_state_t lcp_state; diff --git a/src/bbl_config.c b/src/bbl_config.c index b94cab31..d4758200 100644 --- a/src/bbl_config.c +++ b/src/bbl_config.c @@ -102,15 +102,15 @@ json_parse_access_interface (bbl_ctx_s *ctx, json_t *access_interface, bbl_acces /* Optionally overload some settings per range */ if (json_unpack(access_interface, "{s:s}", "username", &s) == 0) { - snprintf(access_config->username, USERNAME_LEN, "%s", s); + access_config->username = strdup(s); } else { - snprintf(access_config->username, USERNAME_LEN, "%s", ctx->config.username); + access_config->username = strdup(ctx->config.username); } if (json_unpack(access_interface, "{s:s}", "password", &s) == 0) { - snprintf(access_config->password, PASSWORD_LEN, "%s", s); + access_config->password = strdup(s); } else { - snprintf(access_config->password, PASSWORD_LEN, "%s", ctx->config.password); + access_config->password = strdup(ctx->config.password); } if (json_unpack(access_interface, "{s:s}", "authentication-protocol", &s) == 0) { @@ -128,15 +128,15 @@ json_parse_access_interface (bbl_ctx_s *ctx, json_t *access_interface, bbl_acces /* Access Line */ if (json_unpack(access_interface, "{s:s}", "agent-circuit-id", &s) == 0) { - snprintf(access_config->agent_circuit_id, ACI_LEN, "%s", s); + access_config->agent_circuit_id = strdup(s); } else { - snprintf(access_config->agent_circuit_id, ACI_LEN, "%s", ctx->config.agent_circuit_id); + access_config->agent_circuit_id = strdup(ctx->config.agent_circuit_id); } if (json_unpack(access_interface, "{s:s}", "agent-remote-id", &s) == 0) { - snprintf(access_config->agent_remote_id, ARI_LEN, "%s", s); + access_config->agent_remote_id = strdup(s); } else { - snprintf(access_config->agent_remote_id, ARI_LEN, "%s", ctx->config.agent_remote_id); + access_config->agent_remote_id = strdup(ctx->config.agent_remote_id); } value = json_object_get(access_interface, "rate-up"); @@ -297,7 +297,6 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { ctx->config.sessions_stop_rate = json_number_value(value); } /* ... Deprecated */ - value = json_object_get(section, "session-time"); if (json_is_number(value)) { ctx->config.pppoe_session_time = json_number_value(value); @@ -314,6 +313,13 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { if (json_is_number(value)) { ctx->config.pppoe_discovery_retry = json_number_value(value); } + if (json_unpack(section, "{s:s}", "service-name", &s) == 0) { + ctx->config.pppoe_service_name = strdup(s); + } + value = json_object_get(section, "host-uniq"); + if (json_is_boolean(value)) { + ctx->config.pppoe_host_uniq = json_boolean_value(value); + } } /* PPP Configuration */ @@ -326,10 +332,10 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { sub = json_object_get(section, "authentication"); if (json_is_object(sub)) { if (json_unpack(sub, "{s:s}", "username", &s) == 0) { - snprintf(ctx->config.username, USERNAME_LEN, "%s", s); + ctx->config.username = strdup(s); } if (json_unpack(sub, "{s:s}", "password", &s) == 0) { - snprintf(ctx->config.password, PASSWORD_LEN, "%s", s); + ctx->config.password = strdup(s); } value = json_object_get(sub, "timeout"); if (json_is_number(value)) { @@ -511,10 +517,10 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { section = json_object_get(root, "access-line"); if (json_is_object(section)) { if (json_unpack(section, "{s:s}", "agent-circuit-id", &s) == 0) { - snprintf(ctx->config.agent_circuit_id, ACI_LEN, "%s", s ); + ctx->config.agent_circuit_id = strdup(s); } if (json_unpack(section, "{s:s}", "agent-remote-id", &s) == 0) { - snprintf(ctx->config.agent_remote_id, ARI_LEN, "%s", s ); + ctx->config.agent_remote_id = strdup(s); } value = json_object_get(section, "rate-up"); if (json_is_number(value)) { @@ -777,10 +783,10 @@ bbl_config_load_json (char *filename, bbl_ctx_s *ctx) { */ void bbl_config_init_defaults (bbl_ctx_s *ctx) { - snprintf(ctx->config.username, USERNAME_LEN, "%s", g_default_user); - snprintf(ctx->config.password, PASSWORD_LEN, "%s", g_default_pass); - snprintf(ctx->config.agent_remote_id, ARI_LEN, "%s", g_default_ari); - snprintf(ctx->config.agent_circuit_id, ACI_LEN, "%s", g_default_aci); + ctx->config.username = (char *)g_default_user; + ctx->config.password = (char *)g_default_pass; + ctx->config.agent_remote_id = (char *)g_default_ari; + ctx->config.agent_circuit_id = (char *)g_default_aci; ctx->config.tx_interval = 5; ctx->config.rx_interval = 5; ctx->config.qdisc_bypass = true; diff --git a/src/bbl_logging.c b/src/bbl_logging.c index 2e7533bd..480cc59b 100644 --- a/src/bbl_logging.c +++ b/src/bbl_logging.c @@ -19,8 +19,7 @@ struct keyval_ log_names[] = { { ERROR, "error" }, { IGMP, "igmp" }, { IO, "io" }, - { LCP, "lcp" }, - { NCP, "ncp" }, + { PPPOE, "pppoe" }, { NORMAL, "normal" }, { PCAP, "pcap" }, { TIMER, "timer" }, diff --git a/src/bbl_logging.h b/src/bbl_logging.h index 75447d44..bcf85423 100644 --- a/src/bbl_logging.h +++ b/src/bbl_logging.h @@ -25,8 +25,7 @@ enum { TIMER, TIMER_DETAIL, IO, - LCP, - NCP, + PPPOE, NORMAL, ERROR, PCAP, diff --git a/src/bbl_protocols.c b/src/bbl_protocols.c index ad00e4eb..2c72cc28 100644 --- a/src/bbl_protocols.c +++ b/src/bbl_protocols.c @@ -958,66 +958,89 @@ encode_pppoe_discovery(uint8_t *buf, uint *len, pppoe_len_field = (uint16_t*)buf; BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); - if(pppoe->ac_cookie) { - *(uint16_t*)buf = htobe16(PPPOE_TAG_AC_COOKIE); + if(pppoe->code != PPPOE_PADT) { + *(uint16_t*)buf = htobe16(PPPOE_TAG_SERVICE_NAME); BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); - *(uint16_t*)buf = htobe16(pppoe->ac_cookie_len); + *(uint16_t*)buf = htobe16(pppoe->service_name_len); BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); pppoe_len += 4; - memcpy(buf, pppoe->ac_cookie, pppoe->ac_cookie_len); - BUMP_WRITE_BUFFER(buf, len, pppoe->ac_cookie_len); - pppoe_len += pppoe->ac_cookie_len; - } - if(pppoe->access_line) { - *(uint16_t*)buf = htobe16(PPPOE_TAG_VENDOR); - BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); - vendor_len_field = (uint16_t*)buf; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); - *(uint32_t*)buf = htobe32(BROADBAND_FORUM_VENDOR_ID); - BUMP_WRITE_BUFFER(buf, len, sizeof(uint32_t)); - vendor_len = 4; - if(pppoe->access_line->aci) { - *buf = ACCESS_LINE_ACI; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - str_len = strnlen(pppoe->access_line->aci, 128); - *buf = str_len; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - memcpy(buf, pppoe->access_line->aci, str_len); - BUMP_WRITE_BUFFER(buf, len, str_len); - vendor_len += 2 + str_len; + /* When the tag length is zero this tag is used to indicate that + * any service is acceptable. */ + if(pppoe->service_name_len) { + memcpy(buf, pppoe->service_name, pppoe->service_name_len); + BUMP_WRITE_BUFFER(buf, len, pppoe->service_name_len); + pppoe_len += pppoe->service_name_len; } - if(pppoe->access_line->ari) { - *buf = ACCESS_LINE_ARI; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - str_len = strnlen(pppoe->access_line->ari, 128); - *buf = str_len; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - memcpy(buf, pppoe->access_line->ari, str_len); - BUMP_WRITE_BUFFER(buf, len, str_len); - vendor_len += 2 + str_len; + if(pppoe->host_uniq_len) { + *(uint16_t*)buf = htobe16(PPPOE_TAG_HOST_UNIQ); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + *(uint16_t*)buf = htobe16(pppoe->host_uniq_len); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + pppoe_len += 4; + memcpy(buf, pppoe->host_uniq, pppoe->host_uniq_len); + BUMP_WRITE_BUFFER(buf, len, pppoe->host_uniq_len); + pppoe_len += pppoe->host_uniq_len; } - if(pppoe->access_line->up) { - *buf = ACCESS_LINE_ACT_UP; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - *buf = 4; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - *(uint32_t*)buf = htobe32(pppoe->access_line->up); - BUMP_WRITE_BUFFER(buf, len, sizeof(uint32_t)); - vendor_len += 6; + if(pppoe->ac_cookie) { + *(uint16_t*)buf = htobe16(PPPOE_TAG_AC_COOKIE); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + *(uint16_t*)buf = htobe16(pppoe->ac_cookie_len); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + pppoe_len += 4; + memcpy(buf, pppoe->ac_cookie, pppoe->ac_cookie_len); + BUMP_WRITE_BUFFER(buf, len, pppoe->ac_cookie_len); + pppoe_len += pppoe->ac_cookie_len; } - if(pppoe->access_line->down) { - *buf = ACCESS_LINE_ACT_DOWN; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - *buf = 4; - BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); - *(uint32_t*)buf = htobe32(pppoe->access_line->down); + if(pppoe->access_line) { + *(uint16_t*)buf = htobe16(PPPOE_TAG_VENDOR); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + vendor_len_field = (uint16_t*)buf; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint16_t)); + *(uint32_t*)buf = htobe32(BROADBAND_FORUM_VENDOR_ID); BUMP_WRITE_BUFFER(buf, len, sizeof(uint32_t)); - vendor_len += 6; + vendor_len = 4; + if(pppoe->access_line->aci) { + *buf = ACCESS_LINE_ACI; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + str_len = strnlen(pppoe->access_line->aci, 128); + *buf = str_len; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + memcpy(buf, pppoe->access_line->aci, str_len); + BUMP_WRITE_BUFFER(buf, len, str_len); + vendor_len += 2 + str_len; + } + if(pppoe->access_line->ari) { + *buf = ACCESS_LINE_ARI; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + str_len = strnlen(pppoe->access_line->ari, 128); + *buf = str_len; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + memcpy(buf, pppoe->access_line->ari, str_len); + BUMP_WRITE_BUFFER(buf, len, str_len); + vendor_len += 2 + str_len; + } + if(pppoe->access_line->up) { + *buf = ACCESS_LINE_ACT_UP; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + *buf = 4; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + *(uint32_t*)buf = htobe32(pppoe->access_line->up); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint32_t)); + vendor_len += 6; + } + if(pppoe->access_line->down) { + *buf = ACCESS_LINE_ACT_DOWN; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + *buf = 4; + BUMP_WRITE_BUFFER(buf, len, sizeof(uint8_t)); + *(uint32_t*)buf = htobe32(pppoe->access_line->down); + BUMP_WRITE_BUFFER(buf, len, sizeof(uint32_t)); + vendor_len += 6; + } + *vendor_len_field = htobe16(vendor_len); + pppoe_len += 4 + vendor_len; } - *vendor_len_field = htobe16(vendor_len); - pppoe_len += 4 + vendor_len; } - *pppoe_len_field = htobe16(pppoe_len); return PROTOCOL_SUCCESS; } @@ -2206,6 +2229,14 @@ decode_pppoe_discovery(uint8_t *buf, uint len, return DECODE_ERROR; } switch (pppoe_tag_type) { + case PPPOE_TAG_SERVICE_NAME: + pppoe->service_name = buf; + pppoe->service_name_len = pppoe_tag_len; + break; + case PPPOE_TAG_HOST_UNIQ: + pppoe->host_uniq = buf; + pppoe->host_uniq_len = pppoe_tag_len; + break; case PPPOE_TAG_AC_COOKIE: pppoe->ac_cookie = buf; pppoe->ac_cookie_len = pppoe_tag_len; diff --git a/src/bbl_protocols.h b/src/bbl_protocols.h index 686048bd..968dfab8 100644 --- a/src/bbl_protocols.h +++ b/src/bbl_protocols.h @@ -45,6 +45,8 @@ #define IPV6_ADDR_LEN 16 #define IPV6_IDENTIFER_LEN 8 +#define PPPOE_TAG_SERVICE_NAME 0x0101 +#define PPPOE_TAG_HOST_UNIQ 0x0103 #define PPPOE_TAG_AC_COOKIE 0x0104 #define PPPOE_TAG_VENDOR 0x0105 @@ -352,8 +354,12 @@ typedef struct bbl_ethernet_header_ { typedef struct bbl_pppoe_discovery_ { uint8_t code; uint16_t session_id; + uint8_t *service_name; + uint16_t service_name_len; uint8_t *ac_cookie; uint16_t ac_cookie_len; + uint8_t *host_uniq; + uint16_t host_uniq_len; access_line_t *access_line; } bbl_pppoe_discovery_t; diff --git a/src/bbl_rx.c b/src/bbl_rx.c index fe915b88..57b989d6 100644 --- a/src/bbl_rx.c +++ b/src/bbl_rx.c @@ -344,7 +344,7 @@ bbl_lcp_echo(timer_s *timer) interface->stats.lcp_echo_timeout++; } if(session->lcp_retries > ctx->config.lcp_keepalive_retry) { - LOG(LCP, "LCP ECHO TIMEOUT (Q-in-Q %u:%u)\n", + LOG(PPPOE, "LCP ECHO TIMEOUT (Q-in-Q %u:%u)\n", session->key.outer_vlan_id, session->key.inner_vlan_id); /* Force terminate session after timeout. */ session->send_requests = BBL_SEND_DISCOVERY; @@ -1606,11 +1606,43 @@ bbl_rx_discovery(bbl_ethernet_header_t *eth, bbl_interface_s *interface, bbl_ses case PPPOE_PADO: interface->stats.pado_rx++; if(session->session_state == BBL_PPPOE_INIT) { + /* Store server MAC address */ memcpy(session->server_mac, eth->src, ETH_ADDR_LEN); - if(pppoed->ac_cookie_len && pppoed->ac_cookie_len <= PPPOE_AC_COOKIE_LEN) { + if(pppoed->ac_cookie_len) { + /* Store AC cookie */ + if(session->pppoe_ac_cookie) free(session->pppoe_ac_cookie); + session->pppoe_ac_cookie = malloc(pppoed->ac_cookie_len); session->pppoe_ac_cookie_len = pppoed->ac_cookie_len; memcpy(session->pppoe_ac_cookie, pppoed->ac_cookie, pppoed->ac_cookie_len); } + if(pppoed->service_name_len) { + if(session->pppoe_service_name_len) { + /* Compare service name */ + if(pppoed->service_name_len != session->pppoe_service_name_len || + memcmp(pppoed->service_name, session->pppoe_service_name, session->pppoe_service_name_len) != 0) { + LOG(PPPOE, "PPPoE Error (Q-in-Q %u:%u) Wrong service name in PADO\n", + session->key.outer_vlan_id, session->key.inner_vlan_id); + return; + } + } else { + /* Store service name */ + session->pppoe_service_name = malloc(pppoed->service_name_len); + session->pppoe_service_name_len = pppoed->service_name_len; + memcpy(session->pppoe_service_name, pppoed->service_name, pppoed->service_name_len); + } + } else { + LOG(PPPOE, "PPPoE Error (Q-in-Q %u:%u) Missing service name in PADO\n", + session->key.outer_vlan_id, session->key.inner_vlan_id); + return; + } + if(session->pppoe_host_uniq) { + if(pppoed->host_uniq_len != sizeof(uint64_t) || + *(uint64_t*)pppoed->host_uniq != session->pppoe_host_uniq) { + LOG(PPPOE, "PPPoE Error (Q-in-Q %u:%u) Wrong host-uniq in PADO\n", + session->key.outer_vlan_id, session->key.inner_vlan_id); + return; + } + } bbl_session_update_state(ctx, session, BBL_PPPOE_REQUEST); session->send_requests = BBL_SEND_DISCOVERY; bbl_session_tx_qnode_insert(session); @@ -1619,12 +1651,32 @@ bbl_rx_discovery(bbl_ethernet_header_t *eth, bbl_interface_s *interface, bbl_ses case PPPOE_PADS: interface->stats.pads_rx++; if(session->session_state == BBL_PPPOE_REQUEST) { - session->pppoe_session_id = pppoed->session_id; - bbl_session_update_state(ctx, session, BBL_PPP_LINK); - session->send_requests = BBL_SEND_LCP_REQUEST; - session->lcp_request_code = PPP_CODE_CONF_REQUEST; - session->lcp_state = BBL_PPP_INIT; - bbl_session_tx_qnode_insert(session); + if(pppoed->session_id) { + if(session->pppoe_host_uniq) { + if(pppoed->host_uniq_len != sizeof(uint64_t) || + *(uint64_t*)pppoed->host_uniq != session->pppoe_host_uniq) { + LOG(PPPOE, "PPPoE Error (Q-in-Q %u:%u) Wrong host-uniq in PADS\n", + session->key.outer_vlan_id, session->key.inner_vlan_id); + return; + } + } + if(pppoed->service_name_len != session->pppoe_service_name_len || + memcmp(pppoed->service_name, session->pppoe_service_name, session->pppoe_service_name_len) != 0) { + LOG(PPPOE, "PPPoE Error (Q-in-Q %u:%u) Wrong service name in PADS\n", + session->key.outer_vlan_id, session->key.inner_vlan_id); + return; + } + session->pppoe_session_id = pppoed->session_id; + bbl_session_update_state(ctx, session, BBL_PPP_LINK); + session->send_requests = BBL_SEND_LCP_REQUEST; + session->lcp_request_code = PPP_CODE_CONF_REQUEST; + session->lcp_state = BBL_PPP_INIT; + bbl_session_tx_qnode_insert(session); + } else { + LOG(PPPOE, "PPPoE Error (Q-in-Q %u:%u) Invalid PADS\n", + session->key.outer_vlan_id, session->key.inner_vlan_id); + return; + } } break; case PPPOE_PADT: diff --git a/src/bbl_tx.c b/src/bbl_tx.c index b3318615..ef8bdef4 100644 --- a/src/bbl_tx.c +++ b/src/bbl_tx.c @@ -678,7 +678,7 @@ bbl_ip6cp_timeout (timer_s *timer) } if(session->ip6cp_retries > ctx->config.ip6cp_conf_request_retry) { session->ip6cp_state = BBL_PPP_CLOSED; - LOG(NCP, "IP6CP TIMEOUT (Q-in-Q %u:%u)\n", + LOG(PPPOE, "IP6CP TIMEOUT (Q-in-Q %u:%u)\n", session->key.outer_vlan_id, session->key.inner_vlan_id); if(session->ipcp_state == BBL_PPP_CLOSED && session->ip6cp_state == BBL_PPP_CLOSED) { bbl_session_clear(ctx, session); @@ -773,7 +773,7 @@ bbl_ipcp_timeout (timer_s *timer) } if(session->ipcp_retries > ctx->config.ipcp_conf_request_retry) { session->ipcp_state = BBL_PPP_CLOSED; - LOG(NCP, "IPCP TIMEOUT (Q-in-Q %u:%u)\n", + LOG(PPPOE, "IPCP TIMEOUT (Q-in-Q %u:%u)\n", session->key.outer_vlan_id, session->key.inner_vlan_id); if(session->ipcp_state == BBL_PPP_CLOSED && session->ip6cp_state == BBL_PPP_CLOSED) { bbl_session_clear(ctx, session); @@ -1007,7 +1007,14 @@ bbl_encode_padi (bbl_session_s *session) eth.type = ETH_TYPE_PPPOE_DISCOVERY; eth.next = &pppoe; pppoe.code = PPPOE_PADI; - + if(session->pppoe_service_name) { + pppoe.service_name = (uint8_t*)session->pppoe_service_name; + pppoe.service_name_len = session->pppoe_service_name_len; + } + if(session->pppoe_host_uniq) { + pppoe.host_uniq = (uint8_t*)&session->pppoe_host_uniq; + pppoe.host_uniq_len = sizeof(uint64_t); + } if(strlen(session->agent_circuit_id) || strlen(session->agent_remote_id)) { access_line.aci = session->agent_circuit_id; access_line.ari = session->agent_remote_id; @@ -1035,7 +1042,14 @@ bbl_encode_padr (bbl_session_s *session) pppoe.code = PPPOE_PADR; pppoe.ac_cookie = session->pppoe_ac_cookie; pppoe.ac_cookie_len = session->pppoe_ac_cookie_len; - + if(session->pppoe_service_name) { + pppoe.service_name = (uint8_t*)session->pppoe_service_name; + pppoe.service_name_len = session->pppoe_service_name_len; + } + if(session->pppoe_host_uniq) { + pppoe.host_uniq = (uint8_t*)&session->pppoe_host_uniq; + pppoe.host_uniq_len = sizeof(uint64_t); + } if(strlen(session->agent_circuit_id) || strlen(session->agent_remote_id)) { access_line.aci = session->agent_circuit_id; access_line.ari = session->agent_remote_id; From 8a48165601eedfba37fd5f105d2084268e811d6e Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Wed, 24 Feb 2021 23:37:04 +0100 Subject: [PATCH 21/27] Display IPv4 DNS servers with session-info cmd --- src/bbl_ctrl.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/bbl_ctrl.c b/src/bbl_ctrl.c index ee040430..07bd15bb 100644 --- a/src/bbl_ctrl.c +++ b/src/bbl_ctrl.c @@ -390,6 +390,8 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, session_key_t *key, json_t* argume void **search; const char *ipv4 = NULL; + const char *dns1 = NULL; + const char *dns2 = NULL; const char *ipv6 = NULL; const char *ipv6pd = NULL; const char *type = NULL; @@ -404,6 +406,12 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, session_key_t *key, json_t* argume if(session->ip_address) { ipv4 = format_ipv4_address(&session->ip_address); } + if(session->dns1) { + dns1 = format_ipv4_address(&session->dns1); + } + if(session->dns2) { + dns2 = format_ipv4_address(&session->dns2); + } if(session->ipv6_prefix.len) { ipv6 = format_ipv6_prefix(&session->ipv6_prefix); } @@ -448,7 +456,7 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, session_key_t *key, json_t* argume "network-rx-session-packets-ipv6pd", session->stats.network_ipv6pd_rx, "network-rx-session-packets-ipv6pd-loss", session->stats.network_ipv6pd_loss); } - root = json_pack("{ss si s{ss ss* ss ss ss ss* ss* ss* ss* ss* ss* so*}}", + root = json_pack("{ss si s{ss ss* ss ss ss ss* ss* ss* ss* ss* ss* ss* ss* so*}}", "status", "ok", "code", 200, "session-information", @@ -460,7 +468,9 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, session_key_t *key, json_t* argume "lcp-state", lcp, "ipcp-state", ipcp, "ip6cp-state", ip6cp, - "ipv4-address",ipv4, + "ipv4-address", ipv4, + "ipv4-dns1", dns1, + "ipv4-dns2", dns2, "ipv6-prefix", ipv6, "ipv6-delegated-prefix", ipv6pd, "session-traffic", session_traffic); From 320e5baed0ed834b26a9348dc569d58fff44d48e Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Thu, 25 Feb 2021 21:21:36 +0100 Subject: [PATCH 22/27] Fix crash if control command is executed for Blaster intances without network interfaces. --- src/bbl_ctrl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bbl_ctrl.c b/src/bbl_ctrl.c index 07bd15bb..018a3fc2 100644 --- a/src/bbl_ctrl.c +++ b/src/bbl_ctrl.c @@ -990,7 +990,9 @@ bbl_ctrl_socket_job (timer_s *timer) { } } else { /* Use first interface as default. */ - key.ifindex = ctx->op.access_if[0]->addr.sll_ifindex; + if(ctx->op.access_if[0]) { + key.ifindex = ctx->op.access_if[0]->addr.sll_ifindex; + } } value = json_object_get(arguments, "outer-vlan"); if (value) { From 010fc2bffa362c676b1fc4b130355b0876f20b4f Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Fri, 26 Feb 2021 14:02:06 +0100 Subject: [PATCH 23/27] AFL Fuzzing Tests Add AFL fuzzing tests for protocol decode functions. --- fuzzing/CMakeLists.txt | 12 ++++ fuzzing/README.md | 34 +++++++++++ fuzzing/protocols_decode.c | 69 ++++++++++++++++++++++ fuzzing/protocols_decode_in/icmpv6_ra.raw | Bin 0 -> 86 bytes fuzzing/protocols_decode_in/l2tp.raw | Bin 0 -> 174 bytes fuzzing/protocols_decode_in/pap.raw | Bin 0 -> 57 bytes 6 files changed, 115 insertions(+) create mode 100644 fuzzing/CMakeLists.txt create mode 100644 fuzzing/README.md create mode 100644 fuzzing/protocols_decode.c create mode 100644 fuzzing/protocols_decode_in/icmpv6_ra.raw create mode 100644 fuzzing/protocols_decode_in/l2tp.raw create mode 100644 fuzzing/protocols_decode_in/pap.raw diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt new file mode 100644 index 00000000..1d81560f --- /dev/null +++ b/fuzzing/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required (VERSION 3.0) +project(bngbalster-fuzzing C) + +set(CMAKE_C_COMPILER "afl-gcc") +set(PROPERTIES + POSITION_INDEPENDENT_CODE OFF) + +include_directories("../src") + +add_executable(fuzz-protocols-decode protocols_decode.c ../src/bbl_protocols.c) +target_compile_options(fuzz-protocols-decode PRIVATE -Wall -Wextra -pedantic) + diff --git a/fuzzing/README.md b/fuzzing/README.md new file mode 100644 index 00000000..09e4fe13 --- /dev/null +++ b/fuzzing/README.md @@ -0,0 +1,34 @@ +# AFL Fuzzing + +## Install AFL + +``` +sudo apt install afl +``` + +## Build Fuzzing Tests + +``` +# cd fuzzing +export AFL_USE_ASAN=1 +cmake . +make clean all; +``` + +## Run Tests + +### Protocols Decode + +``` +# cd fuzzing +afl-fuzz -m none -i protocols_decode_in -o protocols_decode_out ./fuzz-protocols-decode @@ +``` + +## RAM Disks and Saving Your SSD From AFL Fuzzing + +... +mkdir /tmp/afl-ramdisk && chmod 777 /tmp/afl-ramdisk +sudo mount -t tmpfs -o size=512M tmpfs /tmp/afl-ramdisk +cp -R bngblaster /tmp/afl-ramdisk/ +cd /tmp/afl-ramdisk/bngblaster +... \ No newline at end of file diff --git a/fuzzing/protocols_decode.c b/fuzzing/protocols_decode.c new file mode 100644 index 00000000..ec03f01c --- /dev/null +++ b/fuzzing/protocols_decode.c @@ -0,0 +1,69 @@ +/* + * BNG Blaster Protocols Decode Fuzzing + * + * Author(s): Christian Giese + * + * Copyright (C) 2016 - 2020, RtBrick, Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCRATCHPAD_LEN 1514 +#define BUFFER_SIZE 64000 + +static uint8_t scratchpad[SCRATCHPAD_LEN]; +static uint8_t buffer[BUFFER_SIZE]; + +int main(int argc, char *argv[]) { + + bbl_ethernet_header_t *eth; + protocol_error_t decode_result; + uint8_t *sp = scratchpad; + uint8_t *buf = buffer; + uint8_t *input = NULL; + uint16_t len; + int fd; + + assert(argc == 2); + fd = open(argv[1], O_RDONLY); + assert(fd >= 0); + + /* find file size */ + len = lseek(fd, 0, SEEK_END); + assert(len); + + /* jump back to beginning */ + lseek(fd, 0, SEEK_SET); + input = mmap( + NULL, /* pointer */ + len, /* length */ + PROT_READ, /* read only */ + MAP_PRIVATE, /* flags */ + fd, /* file descriptor */ + 0); /* offset */ + + if(len <= BUFFER_SIZE) { + /* copy input to buffer */ + buf += BUFFER_SIZE - len; + memcpy(buf, input, len); + /* call test function */ + decode_result = decode_ethernet(buf, len, sp, SCRATCHPAD_LEN, ð); + if(decode_result == PROTOCOL_SUCCESS) { + printf("OK\n"); + } else { + printf("FAILED\n"); + } + } + return 0; +} diff --git a/fuzzing/protocols_decode_in/icmpv6_ra.raw b/fuzzing/protocols_decode_in/icmpv6_ra.raw new file mode 100644 index 0000000000000000000000000000000000000000..39356a5c1e8d9f9045d628ccc9043b4750323865 GIT binary patch literal 86 zcmXpuW?*1o+>teX=?v4hy9q!JgM!une+?i81H-PY>Hq%$W&Sfk_)wCujUjG^0|SE` POq3C70xKg(g!K*p?{^eO literal 0 HcmV?d00001 diff --git a/fuzzing/protocols_decode_in/l2tp.raw b/fuzzing/protocols_decode_in/l2tp.raw new file mode 100644 index 0000000000000000000000000000000000000000..496b1df874bd8461432cd754a6b46fe79a749fe2 GIT binary patch literal 174 zcmdmyHGSy<(;Yx^hG~a8%Ol1At_%!w7=Ylv;0&(=j4aIY3=B+cOWBq(bTBZSU}9(h z$$>xv2bf|6(@cyE4PrpPiWB?ac_m3#nN+HrKifoap4-3$6lDhC22LQ!?&RkV*1*}= z2Bx_f6c~ho5*$G#PDPo?**XfIdC7VQB!EKP1{tQhht1|h9iLjg=+}F<><%jc{7@^j literal 0 HcmV?d00001 diff --git a/fuzzing/protocols_decode_in/pap.raw b/fuzzing/protocols_decode_in/pap.raw new file mode 100644 index 0000000000000000000000000000000000000000..4138de89a0b8ea250d4ab6d459e20b5b56799473 GIT binary patch literal 57 zcmdmyHGSy}Muvc}ZPF7P85kOXn7t!KkbyygLH2+$BO`;fU} Date: Fri, 26 Feb 2021 14:04:55 +0100 Subject: [PATCH 24/27] Fix Bugs found with AFL --- src/bbl.c | 2 +- src/bbl.h | 5 +- src/bbl_l2tp.c | 4 +- src/bbl_protocols.c | 158 ++++++++++++++++++++++++++------------------ src/bbl_protocols.h | 8 +-- src/bbl_rx.c | 4 +- src/bbl_tx.c | 2 +- 7 files changed, 105 insertions(+), 78 deletions(-) diff --git a/src/bbl.c b/src/bbl.c index 895e4aaa..1dd94dd7 100644 --- a/src/bbl.c +++ b/src/bbl.c @@ -208,7 +208,7 @@ bbl_add_multicast_packets (bbl_ctx_s *ctx, bbl_interface_s *interface) uint32_t source; int i; - uint len = 0; + uint16_t len = 0; if(ctx->config.send_multicast_traffic && ctx->config.igmp_group_count) { interface->mc_packets = malloc(ctx->config.igmp_group_count * 1500); diff --git a/src/bbl.h b/src/bbl.h index 5f1a5683..c3f556e2 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -607,8 +607,8 @@ typedef struct bbl_session_ struct bbl_interface_ *interface; /* where this session is attached to */ struct bbl_access_config_ *access_config; - u_char *write_buf; /* pointer to the slot in the tx_ring */ - uint write_idx; + uint8_t *write_buf; /* pointer to the slot in the tx_ring */ + uint16_t write_idx; /* Session timer */ struct timer_ *timer_arp; @@ -850,6 +850,7 @@ void bbl_session_network_tx_qnode_insert(struct bbl_session_ *session); void bbl_session_network_tx_qnode_remove(struct bbl_session_ *session); void bbl_session_update_state(bbl_ctx_s *ctx, bbl_session_s *session, session_state_t state); void bbl_session_clear(bbl_ctx_s *ctx, bbl_session_s *session); +bbl_ctx_s * bbl_add_ctx (void); WINDOW *log_win; WINDOW *stats_win; diff --git a/src/bbl_l2tp.c b/src/bbl_l2tp.c index 4e13ff7d..6fb2edc9 100644 --- a/src/bbl_l2tp.c +++ b/src/bbl_l2tp.c @@ -375,7 +375,7 @@ bbl_l2tp_send(bbl_l2tp_tunnel_t *l2tp_tunnel, bbl_l2tp_session_t *l2tp_session, uint8_t sp[L2TP_MAX_AVP_SIZE]; /* scratchpad memory to craft the AVP attributes */ uint16_t sp_len = 0; - uint len = 0; + uint16_t len = 0; eth.dst = interface->gateway_mac; eth.src = interface->mac; @@ -459,7 +459,7 @@ bbl_l2tp_send_data(bbl_l2tp_session_t *l2tp_session, uint16_t protocol, void *ne bbl_ipv4_t ipv4 = {0}; bbl_udp_t udp = {0}; bbl_l2tp_t l2tp = {0}; - uint len = 0; + uint16_t len = 0; eth.dst = interface->gateway_mac; eth.src = interface->mac; eth.vlan_outer = interface->ctx->config.network_vlan; diff --git a/src/bbl_protocols.c b/src/bbl_protocols.c index 2c72cc28..cd300a94 100644 --- a/src/bbl_protocols.c +++ b/src/bbl_protocols.c @@ -8,8 +8,8 @@ #include "bbl_protocols.h" -protocol_error_t decode_l2tp(uint8_t *buf, uint len, uint8_t *sp, uint sp_len, bbl_l2tp_t **_l2tp); -protocol_error_t encode_l2tp(uint8_t *buf, uint *len, bbl_l2tp_t *l2tp); +protocol_error_t decode_l2tp(uint8_t *buf, uint16_t len, uint8_t *sp, uint16_t sp_len, bbl_l2tp_t **_l2tp); +protocol_error_t encode_l2tp(uint8_t *buf, uint16_t *len, bbl_l2tp_t *l2tp); uint16_t checksum(uint16_t *buf, uint16_t len) { @@ -116,7 +116,7 @@ bbl_ipv6_icmpv6_checksum(ipv6addr_t src, ipv6addr_t dst, uint8_t *buf, uint16_t * encode_dhcpv6 */ protocol_error_t -encode_dhcpv6(uint8_t *buf, uint *len, +encode_dhcpv6(uint8_t *buf, uint16_t *len, bbl_dhcpv6_t *dhcpv6) { *(uint32_t*)buf = dhcpv6->transaction_id; @@ -188,7 +188,7 @@ encode_dhcpv6(uint8_t *buf, uint *len, * encode_icmp */ protocol_error_t -encode_bbl(uint8_t *buf, uint *len, +encode_bbl(uint8_t *buf, uint16_t *len, bbl_bbl_t *bbl) { *(uint64_t*)buf = BBL_MAGIC_NUMBER; @@ -228,7 +228,7 @@ encode_bbl(uint8_t *buf, uint *len, * encode_icmp */ protocol_error_t -encode_udp(uint8_t *buf, uint *len, +encode_udp(uint8_t *buf, uint16_t *len, bbl_udp_t *udp) { protocol_error_t result; @@ -259,7 +259,7 @@ encode_udp(uint8_t *buf, uint *len, } protocol_error_t -encode_icmpv6(uint8_t *buf, uint *len, +encode_icmpv6(uint8_t *buf, uint16_t *len, bbl_icmpv6_t *icmp) { @@ -323,7 +323,7 @@ encode_icmpv6(uint8_t *buf, uint *len, } protocol_error_t -encode_arp(uint8_t *buf, uint *len, +encode_arp(uint8_t *buf, uint16_t *len, bbl_arp_t *arp) { *(uint16_t*)buf = 0x0100; @@ -359,7 +359,7 @@ encode_arp(uint8_t *buf, uint *len, * encode_icmp */ protocol_error_t -encode_icmp(uint8_t *buf, uint *len, +encode_icmp(uint8_t *buf, uint16_t *len, bbl_icmp_t *icmp) { uint8_t *start = buf; @@ -389,7 +389,7 @@ encode_icmp(uint8_t *buf, uint *len, * encode_igmp */ protocol_error_t -encode_igmp(uint8_t *buf, uint *len, +encode_igmp(uint8_t *buf, uint16_t *len, bbl_igmp_t *igmp) { uint8_t *start = buf; @@ -459,7 +459,7 @@ encode_igmp(uint8_t *buf, uint *len, } protocol_error_t -encode_ipv6(uint8_t *buf, uint *len, +encode_ipv6(uint8_t *buf, uint16_t *len, bbl_ipv6_t *ipv6) { protocol_error_t result; @@ -516,8 +516,8 @@ encode_ipv6(uint8_t *buf, uint *len, } protocol_error_t -encode_ipv4(uint8_t *buf, uint *len, - bbl_ipv4_t *ipv4) { +encode_ipv4(uint8_t *buf, uint16_t *len, + bbl_ipv4_t *ipv4) { protocol_error_t result; @@ -598,7 +598,7 @@ encode_ipv4(uint8_t *buf, uint *len, } protocol_error_t -encode_ppp_pap(uint8_t *buf, uint *len, +encode_ppp_pap(uint8_t *buf, uint16_t *len, bbl_pap_t *pap) { uint16_t *pap_len_field = NULL; @@ -634,7 +634,7 @@ encode_ppp_pap(uint8_t *buf, uint *len, } protocol_error_t -encode_ppp_chap(uint8_t *buf, uint *len, +encode_ppp_chap(uint8_t *buf, uint16_t *len, bbl_chap_t *chap) { uint16_t *chap_len_field = NULL; @@ -666,7 +666,7 @@ encode_ppp_chap(uint8_t *buf, uint *len, } protocol_error_t -encode_ppp_ip6cp(uint8_t *buf, uint *len, +encode_ppp_ip6cp(uint8_t *buf, uint16_t *len, bbl_ip6cp_t *ip6cp) { uint16_t *ip6cp_len_field; @@ -703,7 +703,7 @@ encode_ppp_ip6cp(uint8_t *buf, uint *len, protocol_error_t -encode_ppp_ipcp(uint8_t *buf, uint *len, +encode_ppp_ipcp(uint8_t *buf, uint16_t *len, bbl_ipcp_t *ipcp) { uint16_t *ipcp_len_field; @@ -770,7 +770,7 @@ encode_ppp_ipcp(uint8_t *buf, uint *len, * +-+-+-+-+ */ protocol_error_t -encode_ppp_lcp(uint8_t *buf, uint *len, +encode_ppp_lcp(uint8_t *buf, uint16_t *len, bbl_lcp_t *lcp) { uint16_t *lcp_len_field; @@ -833,7 +833,7 @@ encode_ppp_lcp(uint8_t *buf, uint *len, } protocol_error_t -encode_l2tp(uint8_t *buf, uint *len, bbl_l2tp_t *l2tp) { +encode_l2tp(uint8_t *buf, uint16_t *len, bbl_l2tp_t *l2tp) { protocol_error_t result; uint16_t *l2tp_len_field = NULL; @@ -938,7 +938,7 @@ encode_l2tp(uint8_t *buf, uint *len, bbl_l2tp_t *l2tp) { * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ protocol_error_t -encode_pppoe_discovery(uint8_t *buf, uint *len, +encode_pppoe_discovery(uint8_t *buf, uint16_t *len, bbl_pppoe_discovery_t *pppoe) { uint16_t *pppoe_len_field; @@ -1046,7 +1046,7 @@ encode_pppoe_discovery(uint8_t *buf, uint *len, } protocol_error_t -encode_pppoe_session(uint8_t *buf, uint *len, +encode_pppoe_session(uint8_t *buf, uint16_t *len, bbl_pppoe_session_t *pppoe) { protocol_error_t result; @@ -1104,7 +1104,7 @@ encode_pppoe_session(uint8_t *buf, uint *len, } protocol_error_t -encode_ethernet(uint8_t *buf, uint *len, +encode_ethernet(uint8_t *buf, uint16_t *len, bbl_ethernet_header_t *eth) { if(eth->dst) { @@ -1167,8 +1167,8 @@ encode_ethernet(uint8_t *buf, uint *len, * decode_icmp */ protocol_error_t -decode_icmp(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_icmp(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_icmp_t **_icmp) { bbl_icmp_t *icmp; @@ -1199,8 +1199,8 @@ decode_icmp(uint8_t *buf, uint len, * decode_icmpv6 */ protocol_error_t -decode_icmpv6(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_icmpv6(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_icmpv6_t **_icmpv6) { bbl_icmpv6_t *icmpv6; @@ -1230,6 +1230,9 @@ decode_icmpv6(uint8_t *buf, uint len, switch(icmpv6->type) { case IPV6_ICMPV6_ROUTER_ADVERTISEMENT: + if(len < 12) { + return DECODE_ERROR; + } BUMP_BUFFER(buf, len, sizeof(uint8_t)); // hop limit flags = *buf; if(flags & ICMPV6_FLAGS_OTHER_CONFIG) icmpv6->other = true; @@ -1240,6 +1243,9 @@ decode_icmpv6(uint8_t *buf, uint len, option_len = *buf; option_len = option_len * 8; BUMP_BUFFER(buf, len, sizeof(uint8_t)); + if(option_len < 2 || len < option_len-2) { + return DECODE_ERROR; + } if(option == ICMPV6_OPTION_PREFIX) { icmpv6->prefix.len = *buf; memcpy(&icmpv6->prefix.address, buf+14, IPV6_ADDR_LEN); @@ -1249,6 +1255,9 @@ decode_icmpv6(uint8_t *buf, uint len, break; case IPV6_ICMPV6_NEIGHBOR_SOLICITATION: 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); break; @@ -1270,8 +1279,8 @@ decode_icmpv6(uint8_t *buf, uint len, * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ protocol_error_t -decode_igmp(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_igmp(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_igmp_t **_igmp) { bbl_igmp_t *igmp; @@ -1282,7 +1291,7 @@ decode_igmp(uint8_t *buf, uint len, return DECODE_ERROR; } - /* Init IPv4 header */ + /* Init IGMP header */ igmp = (bbl_igmp_t*)sp; BUMP_BUFFER(sp, sp_len, sizeof(bbl_igmp_t)); memset(igmp, 0x0, sizeof(bbl_igmp_t)); @@ -1342,8 +1351,8 @@ decode_igmp(uint8_t *buf, uint len, } protocol_error_t -decode_dhcpv6(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_dhcpv6(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_dhcpv6_t **_dhcpv6) { protocol_error_t ret_val = PROTOCOL_SUCCESS; @@ -1396,8 +1405,8 @@ decode_dhcpv6(uint8_t *buf, uint len, } protocol_error_t -decode_bbl(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_bbl(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_bbl_t **_bbl) { bbl_bbl_t *bbl; @@ -1446,8 +1455,8 @@ decode_bbl(uint8_t *buf, uint len, } protocol_error_t -decode_qmx_li(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_qmx_li(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_qmx_li_t **_qmx_li) { bbl_qmx_li_t *qmx_li; @@ -1470,8 +1479,8 @@ decode_qmx_li(uint8_t *buf, uint len, } protocol_error_t -decode_udp(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_udp(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_udp_t **_udp) { protocol_error_t ret_val = PROTOCOL_SUCCESS; @@ -1533,8 +1542,8 @@ decode_udp(uint8_t *buf, uint len, } protocol_error_t -decode_ipv6(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_ipv6(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_ipv6_t **_ipv6) { protocol_error_t ret_val = PROTOCOL_SUCCESS; @@ -1561,6 +1570,11 @@ decode_ipv6(uint8_t *buf, uint len, ipv6->dst = buf; BUMP_BUFFER(buf, len, IPV6_ADDR_LEN); + if(ipv6->payload_len > len) { + return DECODE_ERROR; + } + len = ipv6->payload_len; + /* Decode protocol */ switch(ipv6->protocol) { case IPV6_NEXT_HEADER_ICMPV6: @@ -1599,8 +1613,8 @@ decode_ipv6(uint8_t *buf, uint len, * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ protocol_error_t -decode_ipv4(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_ipv4(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_ipv4_t **_ipv4) { protocol_error_t ret_val = PROTOCOL_SUCCESS; @@ -1635,7 +1649,8 @@ decode_ipv4(uint8_t *buf, uint len, ipv4->tos = header->ip_tos; ipv4_total_len = be16toh(header->ip_len); - if(ipv4_header_len > ipv4_total_len) { + if(ipv4_header_len > ipv4_total_len || + ipv4_total_len > len) { return DECODE_ERROR; } @@ -1674,8 +1689,8 @@ decode_ipv4(uint8_t *buf, uint len, } protocol_error_t -decode_ppp_pap(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_ppp_pap(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_pap_t **ppp_pap) { bbl_pap_t *pap; @@ -1699,10 +1714,16 @@ decode_ppp_pap(uint8_t *buf, uint len, BUMP_BUFFER(buf, len, sizeof(uint32_t)); pap->username_len = *buf; BUMP_BUFFER(buf, len, sizeof(uint8_t)); + if(pap->username_len >= len) { + return DECODE_ERROR; + } pap->username = (char*)buf; BUMP_BUFFER(buf, len, pap->username_len); pap->password_len = *buf; BUMP_BUFFER(buf, len, sizeof(uint8_t)); + if(pap->password_len > len) { + return DECODE_ERROR; + } pap->password = (char*)buf; BUMP_BUFFER(buf, len, pap->password_len); } else { @@ -1713,6 +1734,9 @@ decode_ppp_pap(uint8_t *buf, uint len, pap->reply_message_len = *buf; BUMP_BUFFER(buf, len, sizeof(uint8_t)); if(pap->reply_message_len) { + if(pap->reply_message_len > len) { + return DECODE_ERROR; + } pap->reply_message = (char*)buf; BUMP_BUFFER(buf, len, pap->reply_message_len); } @@ -1722,8 +1746,8 @@ decode_ppp_pap(uint8_t *buf, uint len, } protocol_error_t -decode_ppp_chap(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_ppp_chap(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_chap_t **ppp_chap) { bbl_chap_t *chap; @@ -1745,12 +1769,11 @@ decode_ppp_chap(uint8_t *buf, uint len, return DECODE_ERROR; } BUMP_BUFFER(buf, len, sizeof(uint32_t)); - chap->challenge_len = *buf; + BUMP_BUFFER(buf, len, sizeof(uint8_t)); if(chap->challenge_len > len) { return DECODE_ERROR; } - BUMP_BUFFER(buf, len, sizeof(uint8_t)); chap->challenge = buf; BUMP_BUFFER(buf, len, chap->challenge_len); } else { @@ -1760,6 +1783,9 @@ decode_ppp_chap(uint8_t *buf, uint len, BUMP_BUFFER(buf, len, sizeof(uint32_t)); chap->reply_message_len = chap_len - 4; if(chap->reply_message_len) { + if(chap->reply_message_len > len) { + return DECODE_ERROR; + } chap->reply_message = (char*)buf; BUMP_BUFFER(buf, len, chap->reply_message_len); } @@ -1769,8 +1795,8 @@ decode_ppp_chap(uint8_t *buf, uint len, } protocol_error_t -decode_ppp_ip6cp(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_ppp_ip6cp(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_ip6cp_t **ppp_ip6cp) { bbl_ip6cp_t *ip6cp; @@ -1798,7 +1824,7 @@ decode_ppp_ip6cp(uint8_t *buf, uint len, } ip6cp_len -= 4; - if(ip6cp_len < len) { + if(ip6cp_len > len) { return DECODE_ERROR; } @@ -1847,8 +1873,8 @@ decode_ppp_ip6cp(uint8_t *buf, uint len, } protocol_error_t -decode_ppp_ipcp(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_ppp_ipcp(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_ipcp_t **ppp_ipcp) { bbl_ipcp_t *ipcp; @@ -1876,7 +1902,7 @@ decode_ppp_ipcp(uint8_t *buf, uint len, } ipcp_len -= 4; - if(ipcp_len < len) { + if(ipcp_len > len) { return DECODE_ERROR; } @@ -1942,8 +1968,8 @@ decode_ppp_ipcp(uint8_t *buf, uint len, * +-+-+-+-+ */ protocol_error_t -decode_ppp_lcp(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_ppp_lcp(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_lcp_t **ppp_lcp) { bbl_lcp_t *lcp; @@ -1971,7 +1997,7 @@ decode_ppp_lcp(uint8_t *buf, uint len, } lcp_len -= 4; - if(lcp_len < len) { + if(lcp_len > len) { return DECODE_ERROR; } @@ -2032,8 +2058,8 @@ decode_ppp_lcp(uint8_t *buf, uint len, } protocol_error_t -decode_l2tp(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_l2tp(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_l2tp_t **_l2tp) { protocol_error_t ret_val = UNKNOWN_PROTOCOL; @@ -2186,8 +2212,8 @@ decode_l2tp(uint8_t *buf, uint len, * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ protocol_error_t -decode_pppoe_discovery(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_pppoe_discovery(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_pppoe_discovery_t **pppoe_discovery) { bbl_pppoe_discovery_t *pppoe; uint16_t pppoe_len = 0; @@ -2252,8 +2278,8 @@ decode_pppoe_discovery(uint8_t *buf, uint len, } protocol_error_t -decode_pppoe_session(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_pppoe_session(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_pppoe_session_t **pppoe_session) { protocol_error_t ret_val = UNKNOWN_PROTOCOL; @@ -2318,8 +2344,8 @@ decode_pppoe_session(uint8_t *buf, uint len, } protocol_error_t -decode_arp(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_arp(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_arp_t **_arp) { bbl_arp_t *arp; @@ -2349,8 +2375,8 @@ decode_arp(uint8_t *buf, uint len, } protocol_error_t -decode_ethernet(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_ethernet(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_ethernet_header_t **ethernet) { bbl_ethernet_header_t *eth; diff --git a/src/bbl_protocols.h b/src/bbl_protocols.h index 968dfab8..923aa84b 100644 --- a/src/bbl_protocols.h +++ b/src/bbl_protocols.h @@ -189,7 +189,7 @@ #define BUMP_WRITE_BUFFER(_buf, _len, _size) \ (_buf) += _size; \ - *(uint*)(_len) += _size; + *(uint16_t*)(_len) += _size; typedef uint8_t ipv6addr_t[IPV6_ADDR_LEN]; @@ -604,15 +604,15 @@ typedef struct bbl_qmx_li_ { * decode_ethernet */ protocol_error_t -decode_ethernet(uint8_t *buf, uint len, - uint8_t *sp, uint sp_len, +decode_ethernet(uint8_t *buf, uint16_t len, + uint8_t *sp, uint16_t sp_len, bbl_ethernet_header_t **ethernet); /* * encode_ethernet */ protocol_error_t -encode_ethernet(uint8_t *buf, uint *len, +encode_ethernet(uint8_t *buf, uint16_t *len, bbl_ethernet_header_t *eth); #endif diff --git a/src/bbl_rx.c b/src/bbl_rx.c index 57b989d6..44f57249 100644 --- a/src/bbl_rx.c +++ b/src/bbl_rx.c @@ -30,7 +30,7 @@ bbl_add_session_packets_ipv4 (bbl_ctx_s *ctx, bbl_session_s *session) bbl_udp_t udp = {0}; bbl_bbl_t bbl = {0}; uint8_t *buf; - uint len = 0; + uint16_t len = 0; /* Init BBL Session Key */ bbl.type = BBL_TYPE_UNICAST_SESSION; @@ -127,7 +127,7 @@ bbl_add_session_packets_ipv6 (bbl_ctx_s *ctx, bbl_session_s *session, bool ipv6_ bbl_udp_t udp = {0}; bbl_bbl_t bbl = {0}; uint8_t *buf; - uint len = 0; + uint16_t len = 0; /* Init BBL Session Key */ bbl.type = BBL_TYPE_UNICAST_SESSION; diff --git a/src/bbl_tx.c b/src/bbl_tx.c index ef8bdef4..600b1eb2 100644 --- a/src/bbl_tx.c +++ b/src/bbl_tx.c @@ -1315,7 +1315,7 @@ bbl_encode_interface_packet (bbl_interface_s *interface, u_char *frame_ptr) bbl_arp_t arp = {0}; bbl_ipv6_t ipv6 = {0}; bbl_icmpv6_t icmpv6 = {0}; - uint len = 0; + uint16_t len = 0; uint8_t *buf = frame_ptr + TPACKET2_HDRLEN - sizeof(struct sockaddr_ll); bbl_secondary_ip_s *secondary_ip; From 000d6ce07a5dc69ea8635021a950fa84102cdd2e Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Fri, 26 Feb 2021 15:05:28 +0100 Subject: [PATCH 25/27] docs update --- docs/README.md | 4 +++- fuzzing/README.md | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/README.md b/docs/README.md index aa4a1ecc..291d04b9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,4 +6,6 @@ - [Reports](reports.md) - [Traffic](traffic.md) - [Control Socket](ctrl.md) -- [Multicast](multicast.md) \ No newline at end of file +- [Multicast](multicast.md) +- [L2TPv2](l2tp.md) +- [Legal Interception](li.md) \ No newline at end of file diff --git a/fuzzing/README.md b/fuzzing/README.md index 09e4fe13..dcf64a58 100644 --- a/fuzzing/README.md +++ b/fuzzing/README.md @@ -26,9 +26,9 @@ afl-fuzz -m none -i protocols_decode_in -o protocols_decode_out ./fuzz-protocols ## RAM Disks and Saving Your SSD From AFL Fuzzing -... +``` mkdir /tmp/afl-ramdisk && chmod 777 /tmp/afl-ramdisk sudo mount -t tmpfs -o size=512M tmpfs /tmp/afl-ramdisk cp -R bngblaster /tmp/afl-ramdisk/ cd /tmp/afl-ramdisk/bngblaster -... \ No newline at end of file +``` From c54b38b6408937dd24deb4b092dcd5e81439dc58 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Fri, 26 Feb 2021 17:53:27 +0100 Subject: [PATCH 26/27] Add IPv6 DNS Servers Store and display IPv6 DNS servers learned via RA and DHCPv6. --- src/bbl.c | 4 ++++ src/bbl.h | 8 ++++++-- src/bbl_ctrl.c | 22 +++++++++++++++++++++- src/bbl_protocols.c | 26 ++++++++++++++++++++++++++ src/bbl_protocols.h | 5 +++++ src/bbl_rx.c | 12 ++++++++++++ 6 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/bbl.c b/src/bbl.c index 1dd94dd7..0bad645a 100644 --- a/src/bbl.c +++ b/src/bbl.c @@ -170,10 +170,14 @@ bbl_session_update_state(bbl_ctx_s *ctx, bbl_session_s *session, session_state_t session->ipv6_prefix.len = 0; session->delegated_ipv6_prefix.len = 0; session->icmpv6_ra_received = false; + memset(session->ipv6_dns1, 0x0, IPV6_ADDR_LEN); + memset(session->ipv6_dns2, 0x0, IPV6_ADDR_LEN); session->dhcpv6_requested = false; session->dhcpv6_received = false; session->dhcpv6_type = DHCPV6_MESSAGE_SOLICIT; session->dhcpv6_ia_pd_option_len = 0; + memset(session->dhcpv6_dns1, 0x0, IPV6_ADDR_LEN); + memset(session->dhcpv6_dns2, 0x0, IPV6_ADDR_LEN); session->zapping_joined_group = NULL; session->zapping_leaved_group = NULL; session->zapping_count = 0; diff --git a/src/bbl.h b/src/bbl.h index c3f556e2..c90c85ce 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -710,18 +710,22 @@ typedef struct bbl_session_ ipv6addr_t link_local_ipv6_address; ipv6_prefix ipv6_prefix; ipv6addr_t ipv6_address; + ipv6addr_t ipv6_dns1; /* DNS learned via RA */ + ipv6addr_t ipv6_dns2; /* DNS learned via RA */ + + /* DHCPv6 */ ipv6_prefix delegated_ipv6_prefix; ipv6addr_t delegated_ipv6_address; uint8_t duid[DUID_LEN]; uint8_t server_duid[DHCPV6_BUFFER]; uint8_t server_duid_len; - - /* DHCPv6 */ bool dhcpv6_requested; bool dhcpv6_received; uint8_t dhcpv6_type; uint8_t dhcpv6_ia_pd_option[DHCPV6_BUFFER]; uint8_t dhcpv6_ia_pd_option_len; + ipv6addr_t dhcpv6_dns1; + ipv6addr_t dhcpv6_dns2; /* IGMP */ bool igmp_autostart; diff --git a/src/bbl_ctrl.c b/src/bbl_ctrl.c index 018a3fc2..d0739000 100644 --- a/src/bbl_ctrl.c +++ b/src/bbl_ctrl.c @@ -394,6 +394,10 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, session_key_t *key, json_t* argume const char *dns2 = NULL; const char *ipv6 = NULL; const char *ipv6pd = NULL; + const char *ipv6_dns1 = NULL; + const char *ipv6_dns2 = NULL; + const char *dhcpv6_dns1 = NULL; + const char *dhcpv6_dns2 = NULL; const char *type = NULL; const char *username = NULL; const char *lcp = NULL; @@ -418,6 +422,18 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, session_key_t *key, json_t* argume if(session->delegated_ipv6_prefix.len) { ipv6pd = format_ipv6_prefix(&session->delegated_ipv6_prefix); } + if(*(uint64_t*)session->ipv6_dns1) { + ipv6_dns1 = format_ipv6_address(&session->ipv6_dns1); + } + if(*(uint64_t*)session->ipv6_dns2) { + ipv6_dns2 = format_ipv6_address(&session->ipv6_dns2); + } + if(*(uint64_t*)session->dhcpv6_dns1) { + dhcpv6_dns1 = format_ipv6_address(&session->dhcpv6_dns1); + } + if(*(uint64_t*)session->dhcpv6_dns2) { + dhcpv6_dns2 = format_ipv6_address(&session->dhcpv6_dns2); + } if(session->access_type == ACCESS_TYPE_PPPOE) { type = "pppoe"; @@ -456,7 +472,7 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, session_key_t *key, json_t* argume "network-rx-session-packets-ipv6pd", session->stats.network_ipv6pd_rx, "network-rx-session-packets-ipv6pd-loss", session->stats.network_ipv6pd_loss); } - root = json_pack("{ss si s{ss ss* ss ss ss ss* ss* ss* ss* ss* ss* ss* ss* so*}}", + root = json_pack("{ss si s{ss ss* ss ss ss ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* so*}}", "status", "ok", "code", 200, "session-information", @@ -473,6 +489,10 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, session_key_t *key, json_t* argume "ipv4-dns2", dns2, "ipv6-prefix", ipv6, "ipv6-delegated-prefix", ipv6pd, + "ipv6-dns1", ipv6_dns1, + "ipv6-dns2", ipv6_dns2, + "dhcpv6-dns1", dhcpv6_dns1, + "dhcpv6-dns2", dhcpv6_dns2, "session-traffic", session_traffic); if(root) { result = json_dumpfd(root, fd, 0); diff --git a/src/bbl_protocols.c b/src/bbl_protocols.c index cd300a94..0b530772 100644 --- a/src/bbl_protocols.c +++ b/src/bbl_protocols.c @@ -1247,8 +1247,18 @@ decode_icmpv6(uint8_t *buf, uint16_t len, return DECODE_ERROR; } if(option == ICMPV6_OPTION_PREFIX) { + if(option_len < 32) { + return DECODE_ERROR; + } icmpv6->prefix.len = *buf; memcpy(&icmpv6->prefix.address, buf+14, IPV6_ADDR_LEN); + } else if(option == ICMPV6_OPTION_DNS) { + if(option_len >= 24) { + icmpv6->dns1 = (ipv6addr_t*)(buf+6); + if(option_len >= 40) { + icmpv6->dns2 = (ipv6addr_t*)(buf+22); + } + } } BUMP_BUFFER(buf, len, option_len-2); } @@ -1379,11 +1389,17 @@ decode_dhcpv6(uint8_t *buf, uint16_t len, BUMP_BUFFER(buf, len, sizeof(uint16_t)); option_len = be16toh(*(uint16_t*)buf); BUMP_BUFFER(buf, len, sizeof(uint16_t)); + if(option_len > len) { + return DECODE_ERROR; + } switch(option) { case DHCPV6_OPTION_RAPID_COMMIT: dhcpv6->rapid = true; break; case DHCPV6_OPTION_IA_PD: + if(option_len < 41) { + return DECODE_ERROR; + } dhcpv6->ia_pd_option = buf; dhcpv6->ia_pd_option_len = option_len; option = be16toh(*(uint16_t*)(buf+12)); @@ -1392,9 +1408,19 @@ decode_dhcpv6(uint8_t *buf, uint16_t len, } break; case DHCPV6_OPTION_SERVERID: + if(option_len < 2) { + return DECODE_ERROR; + } dhcpv6->server_duid = buf; dhcpv6->server_duid_len = option_len; break; + case DHCPV6_OPTION_DNS_SERVERS: + if(option_len >= 16) { + dhcpv6->dns1 = (ipv6addr_t*)(buf); + if(option_len >= 32) { + dhcpv6->dns2 = (ipv6addr_t*)(buf+16); + } + } default: break; } diff --git a/src/bbl_protocols.h b/src/bbl_protocols.h index 923aa84b..d9bd9a00 100644 --- a/src/bbl_protocols.h +++ b/src/bbl_protocols.h @@ -144,6 +144,7 @@ #define ICMPV6_FLAGS_OTHER_CONFIG 0x40 #define ICMPV6_OPTION_PREFIX 3 +#define ICMPV6_OPTION_DNS 25 #define DHCPV6_TRANS_ID_LEN 3 #define DHCPV6_TYPE_MASK 0x00ffffff @@ -539,6 +540,8 @@ typedef struct bbl_icmpv6_ { uint8_t *mac; uint8_t *data; uint16_t data_len; + ipv6addr_t *dns1; + ipv6addr_t *dns2; } bbl_icmpv6_t; typedef struct bbl_dhcpv6_ { @@ -554,6 +557,8 @@ typedef struct bbl_dhcpv6_ { ipv6_prefix *delegated_prefix; uint8_t *ia_pd_option; uint8_t ia_pd_option_len; + ipv6addr_t *dns1; + ipv6addr_t *dns2; } bbl_dhcpv6_t; typedef struct bbl_l2tp_ { diff --git a/src/bbl_rx.c b/src/bbl_rx.c index 44f57249..77992a29 100644 --- a/src/bbl_rx.c +++ b/src/bbl_rx.c @@ -592,6 +592,12 @@ bbl_rx_dhcpv6(bbl_ipv6_t *ipv6, bbl_interface_s *interface, bbl_session_s *sessi LOG(IP, "IPv6 (Q-in-Q %u:%u) DHCPv6 PD prefix %s/%d\n", session->key.outer_vlan_id, session->key.inner_vlan_id, format_ipv6_address(&session->delegated_ipv6_prefix.address), session->delegated_ipv6_prefix.len); + if(dhcpv6->dns1) { + memcpy(&session->dhcpv6_dns1, dhcpv6->dns1, IPV6_ADDR_LEN); + if(dhcpv6->dns2) { + memcpy(&session->dhcpv6_dns2, dhcpv6->dns2, IPV6_ADDR_LEN); + } + } if(session->l2tp == false && ctx->config.session_traffic_ipv6pd_pps && ctx->op.network_if && ctx->op.network_if->ip6.len) { /* Start IPv6 PD Session Traffic */ @@ -745,6 +751,12 @@ bbl_rx_icmpv6(bbl_ipv6_t *ipv6, bbl_interface_s *interface, bbl_session_s *sessi LOG(IP, "IPv6 (Q-in-Q %u:%u) ICMPv6 RA prefix %s/%d\n", session->key.outer_vlan_id, session->key.inner_vlan_id, format_ipv6_address(&session->ipv6_prefix.address), session->ipv6_prefix.len); + if(icmpv6->dns1) { + memcpy(&session->ipv6_dns1, icmpv6->dns1, IPV6_ADDR_LEN); + if(icmpv6->dns2) { + memcpy(&session->ipv6_dns2, icmpv6->dns2, IPV6_ADDR_LEN); + } + } if(session->l2tp == false && ctx->config.session_traffic_ipv6_pps && ctx->op.network_if && ctx->op.network_if->ip6.len) { /* Start IPv6 Session Traffic */ From f18783a0ea304d4adbf5c333668c6ac9e6a375c1 Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Fri, 26 Feb 2021 18:00:22 +0100 Subject: [PATCH 27/27] Do not start IGMP for L2TP sessions --- src/bbl_rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bbl_rx.c b/src/bbl_rx.c index 77992a29..5c67c93c 100644 --- a/src/bbl_rx.c +++ b/src/bbl_rx.c @@ -1155,7 +1155,7 @@ bbl_rx_established(bbl_ethernet_header_t *eth, bbl_interface_s *interface, bbl_s /* Start LCP echo request / keep alive */ timer_add_periodic(&ctx->timer_root, &session->timer_lcp_echo, "LCP ECHO", ctx->config.lcp_keepalive_interval, 0, session, bbl_lcp_echo); } - if(ctx->config.igmp_group && ctx->config.igmp_autostart && ctx->config.igmp_start_delay) { + if(session->l2tp == false && ctx->config.igmp_group && ctx->config.igmp_autostart && ctx->config.igmp_start_delay) { /* Start IGMP */ timer_add(&ctx->timer_root, &session->timer_igmp, "IGMP", ctx->config.igmp_start_delay, 0, session, bbl_igmp_initial_join); }