diff --git a/code/bngblaster/src/bbl.c b/code/bngblaster/src/bbl.c index d1212c12..c516e7bc 100644 --- a/code/bngblaster/src/bbl.c +++ b/code/bngblaster/src/bbl.c @@ -18,6 +18,7 @@ #include "bbl_stream.h" #include "bbl_dhcp.h" #include "bbl_dhcpv6.h" +#include "bbl_l2tp.h" static unsigned int ctrl_job_period_ns = MSEC100; @@ -239,6 +240,7 @@ bbl_ctrl_job(timer_s *timer) bbl_session_s *session; bbl_interface_s *interface; bbl_network_interface_s *network_interface; + bbl_l2tp_tunnel_s *l2tp_tunnel; uint32_t i; @@ -270,6 +272,7 @@ bbl_ctrl_job(timer_s *timer) g_init_phase = false; LOG_NOARG(INFO, "All network interfaces resolved\n"); clock_gettime(CLOCK_MONOTONIC, &g_ctx->timestamp_resolved); + } if(g_teardown) { @@ -379,6 +382,14 @@ bbl_ctrl_job(timer_s *timer) } } break; + case ACCESS_TYPE_PPPOL2TP: + /* PPP over L2TP (LAC) */ + session->session_state = BBL_L2TP_WAIT; + l2tp_tunnel = bbl_l2tp_client_session_get_tunnel(session); + if(l2tp_tunnel) { + bbl_l2tp_client_session_connect(l2tp_tunnel, session); + } + break; } bbl_session_tx_qnode_insert(session); /* Remove from idle queue */ diff --git a/code/bngblaster/src/bbl_access.c b/code/bngblaster/src/bbl_access.c index 2826ccca..daa9ead0 100644 --- a/code/bngblaster/src/bbl_access.c +++ b/code/bngblaster/src/bbl_access.c @@ -64,6 +64,7 @@ bbl_access_interfaces_add() access_interface->name = access_config->interface; access_interface->interface = interface; access_interface->ifindex = interface->ifindex; + access_interface->access_type = access_config->access_type; /* Init TXQ */ access_interface->txq = calloc(1, sizeof(bbl_txq_s)); @@ -546,7 +547,8 @@ bbl_access_rx_icmpv6(bbl_access_interface_s *interface, memcpy(&session->ipv6_prefix, &icmpv6->prefix, sizeof(ipv6_prefix)); *(uint64_t*)&session->ipv6_address[0] = *(uint64_t*)session->ipv6_prefix.address; *(uint64_t*)&session->ipv6_address[8] = session->ip6cp_ipv6_identifier; - if(session->access_type == ACCESS_TYPE_PPPOE) { + if(session->access_type == ACCESS_TYPE_PPPOE || + session->access_type == ACCESS_TYPE_PPPOL2TP) { ACTIVATE_ENDPOINT(session->endpoint.ipv6); } session->version++; @@ -580,7 +582,7 @@ bbl_access_rx_icmpv6(bbl_access_interface_s *interface, } } } - } else if(icmpv6->type == IPV6_ICMPV6_NEIGHBOR_SOLICITATION) { + } else if(eth && icmpv6->type == IPV6_ICMPV6_NEIGHBOR_SOLICITATION) { if(memcmp(icmpv6->prefix.address, session->ipv6_address, IPV6_ADDR_LEN) == 0) { if(bbl_access_icmpv6_na(session, eth, ipv6, icmpv6) == BBL_TXQ_OK) { return true; @@ -590,19 +592,19 @@ bbl_access_rx_icmpv6(bbl_access_interface_s *interface, return true; } } - } else if(icmpv6->type == IPV6_ICMPV6_ECHO_REQUEST) { + } else if(eth && icmpv6->type == IPV6_ICMPV6_ECHO_REQUEST) { if(bbl_access_icmpv6_echo_reply(session, eth, ipv6, icmpv6) == BBL_TXQ_OK) { return true; } } else if(icmpv6->type == IPV6_ICMPV6_NEIGHBOR_ADVERTISEMENT) { - if(ipv6_addr_not_zero(&session->icmpv6_ns_request) && + if(ipv6_addr_not_zero(&session->icmpv6_ns_request) && memcmp(icmpv6->prefix.address, session->icmpv6_ns_request, IPV6_ADDR_LEN) == 0) { memset(session->icmpv6_ns_request, 0x0, IPV6_ADDR_LEN); if(memcmp(icmpv6->prefix.address, session->access_config->static_gateway6, IPV6_ADDR_LEN) == 0) { if(!session->icmpv6_ra_received) { session->icmpv6_ra_received = true; } - if(!session->arp_resolved) { + if(eth && !session->arp_resolved) { memcpy(session->server_mac, eth->src, ETH_ADDR_LEN); } bbl_access_rx_established_ipoe(interface, session, eth); @@ -616,6 +618,11 @@ static bool bbl_access_rx_icmp(bbl_session_s *session, bbl_ethernet_header_s *eth, bbl_ipv4_s *ipv4) { bbl_icmp_s *icmp = (bbl_icmp_s*)ipv4->next; + + if(!eth) { + return false; + } + if(session->ip_address && session->ip_address == ipv4->dst) { if(icmp->type == ICMP_TYPE_ECHO_REQUEST) { /* Send ICMP reply... */ @@ -636,11 +643,16 @@ bbl_access_rx_ipv4_mc(bbl_access_interface_s *interface, bbl_session_s *session, bbl_ethernet_header_s *eth, bbl_ipv4_s *ipv4) { - bbl_bbl_s *bbl = eth->bbl; + bbl_bbl_s *bbl; bbl_igmp_group_s *group = NULL; uint64_t loss; int i; + if(!eth) { + return; + } + bbl = eth->bbl; + for(i=0; i < IGMP_MAX_GROUPS; i++) { group = &session->igmp_groups[i]; if(ipv4->dst == group->group) { @@ -690,7 +702,7 @@ bbl_access_rx_ipv4(bbl_access_interface_s *interface, if(ipv4->offset & ~IPV4_DF) { session->stats.accounting_packets_rx++; - session->stats.accounting_bytes_rx += eth->length; + session->stats.accounting_bytes_rx += eth ? eth->length : ipv4->len; session->stats.ipv4_fragmented_rx++; interface->stats.ipv4_fragmented_rx++; bbl_fragment_rx(interface, NULL, eth, ipv4); @@ -717,14 +729,14 @@ bbl_access_rx_ipv4(bbl_access_interface_s *interface, } break; case PROTOCOL_IPV4_TCP: - bbl_tcp_ipv4_rx_session(session, eth, ipv4); + bbl_tcp_ipv4_rx_session(session, ipv4); break; default: break; } session->stats.accounting_packets_rx++; - session->stats.accounting_bytes_rx += eth->length; + session->stats.accounting_bytes_rx += eth ? eth->length : ipv4->len; /* All IPv4 multicast addresses start with 1110 */ if((ipv4->dst & htobe32(0xf0000000)) == htobe32(0xe0000000)) { @@ -758,13 +770,13 @@ bbl_access_rx_ipv6(bbl_access_interface_s *interface, bbl_access_rx_udp_ipv6(interface, session, eth, ipv6); return; case IPV6_NEXT_HEADER_TCP: - bbl_tcp_ipv6_rx_session(session, eth, ipv6); + bbl_tcp_ipv6_rx_session(session, ipv6); break; default: break; } session->stats.accounting_packets_rx++; - session->stats.accounting_bytes_rx += eth->length; + session->stats.accounting_bytes_rx += eth ? eth->length : ipv6->len; } static void @@ -807,15 +819,11 @@ bbl_access_l2tp(bbl_session_s *session, char *reply_message, uint8_t reply_messa static void bbl_access_rx_pap(bbl_access_interface_s *interface, bbl_session_s *session, - bbl_ethernet_header_s *eth) + bbl_pap_s *pap, + struct timespec *timestamp) { - bbl_pppoe_session_s *pppoes; - bbl_pap_s *pap; - - pppoes = (bbl_pppoe_session_s*)eth->next; - pap = (bbl_pap_s*)pppoes->next; - UNUSED(interface); + UNUSED(timestamp); if(session->session_state == BBL_PPP_AUTH) { switch(pap->code) { @@ -863,17 +871,13 @@ bbl_access_rx_pap(bbl_access_interface_s *interface, static void bbl_access_rx_chap(bbl_access_interface_s *interface, bbl_session_s *session, - bbl_ethernet_header_s *eth) + bbl_chap_s *chap, + struct timespec *timestamp) { - bbl_pppoe_session_s *pppoes; - bbl_chap_s *chap; - MD5_CTX md5_ctx; UNUSED(interface); - - pppoes = (bbl_pppoe_session_s*)eth->next; - chap = (bbl_chap_s*)pppoes->next; + UNUSED(timestamp); if(session->session_state == BBL_PPP_AUTH) { switch(chap->code) { @@ -966,12 +970,12 @@ bbl_access_ncp_success(uint8_t state) * * @param interface receiving interface * @param session corresponding session - * @param eth received packet + * @param timestamp received packet timestamp */ void bbl_access_rx_established_pppoe(bbl_access_interface_s *interface, bbl_session_s *session, - bbl_ethernet_header_s *eth) + struct timespec *timestamp) { UNUSED(interface); @@ -981,8 +985,12 @@ bbl_access_rx_established_pppoe(bbl_access_interface_s *interface, if(ipcp && ip6cp) { if(session->session_state != BBL_ESTABLISHED) { if(g_ctx->sessions_established_max < g_ctx->sessions) { - g_ctx->stats.last_session_established.tv_sec = eth->timestamp.tv_sec; - g_ctx->stats.last_session_established.tv_nsec = eth->timestamp.tv_nsec; + if(timestamp) { + g_ctx->stats.last_session_established.tv_sec = timestamp->tv_sec; + g_ctx->stats.last_session_established.tv_nsec = timestamp->tv_nsec; + } else { + clock_gettime(CLOCK_MONOTONIC, &g_ctx->stats.last_session_established); + } } bbl_session_update_state(session, BBL_ESTABLISHED); if(g_ctx->config.pppoe_session_time) { @@ -1005,10 +1013,10 @@ bbl_access_rx_established_pppoe(bbl_access_interface_s *interface, static void bbl_access_rx_ip6cp(bbl_access_interface_s *interface, bbl_session_s *session, - bbl_ethernet_header_s *eth) + bbl_ip6cp_s *ip6cp, + struct timespec *timestamp) { - bbl_pppoe_session_s *pppoes; - bbl_ip6cp_s *ip6cp; + UNUSED(interface); if(session->lcp_state != BBL_PPP_OPENED) { return; @@ -1026,8 +1034,6 @@ bbl_access_rx_ip6cp(bbl_access_interface_s *interface, return; } - pppoes = (bbl_pppoe_session_s*)eth->next; - ip6cp = (bbl_ip6cp_s*)pppoes->next; switch(ip6cp->code) { case PPP_CODE_CONF_REQUEST: @@ -1046,7 +1052,7 @@ bbl_access_rx_ip6cp(bbl_access_interface_s *interface, break; case BBL_PPP_LOCAL_ACK: session->ip6cp_state = BBL_PPP_OPENED; - bbl_access_rx_established_pppoe(interface, session, eth); + bbl_access_rx_established_pppoe(interface, session, timestamp); session->link_local_ipv6_address[0] = 0xfe; session->link_local_ipv6_address[1] = 0x80; *(uint64_t*)&session->link_local_ipv6_address[8] = session->ip6cp_ipv6_identifier; @@ -1078,7 +1084,7 @@ bbl_access_rx_ip6cp(bbl_access_interface_s *interface, break; case BBL_PPP_PEER_ACK: session->ip6cp_state = BBL_PPP_OPENED; - bbl_access_rx_established_pppoe(interface, session, eth); + bbl_access_rx_established_pppoe(interface, session, timestamp); session->link_local_ipv6_address[0] = 0xfe; session->link_local_ipv6_address[1] = 0x80; *(uint64_t*)&session->link_local_ipv6_address[8] = session->ip6cp_ipv6_identifier; @@ -1137,10 +1143,10 @@ bbl_access_rx_ipcp_conf_reject(bbl_session_s *session, bbl_ipcp_s *ipcp) static void bbl_access_rx_ipcp(bbl_access_interface_s *interface, bbl_session_s *session, - bbl_ethernet_header_s *eth) + bbl_ipcp_s *ipcp, + struct timespec *timestamp) { - bbl_pppoe_session_s *pppoes; - bbl_ipcp_s *ipcp; + UNUSED(interface); if(session->lcp_state != BBL_PPP_OPENED) { return; @@ -1158,8 +1164,7 @@ bbl_access_rx_ipcp(bbl_access_interface_s *interface, return; } - pppoes = (bbl_pppoe_session_s*)eth->next; - ipcp = (bbl_ipcp_s*)pppoes->next; + switch(ipcp->code) { case PPP_CODE_CONF_REQUEST: @@ -1182,7 +1187,7 @@ bbl_access_rx_ipcp(bbl_access_interface_s *interface, break; case BBL_PPP_LOCAL_ACK: session->ipcp_state = BBL_PPP_OPENED; - bbl_access_rx_established_pppoe(interface, session, eth); + bbl_access_rx_established_pppoe(interface, session, timestamp); ACTIVATE_ENDPOINT(session->endpoint.ipv4); session->version++; LOG(IP, "IPv4 (ID: %u) address %s\n", session->session_id, @@ -1241,7 +1246,7 @@ bbl_access_rx_ipcp(bbl_access_interface_s *interface, break; case BBL_PPP_PEER_ACK: session->ipcp_state = BBL_PPP_OPENED; - bbl_access_rx_established_pppoe(interface, session, eth); + bbl_access_rx_established_pppoe(interface, session, timestamp); ACTIVATE_ENDPOINT(session->endpoint.ipv4); session->version++; LOG(IP, "IPv4 (ID: %u) address %s\n", session->session_id, @@ -1385,16 +1390,11 @@ bbl_access_lcp_opened(bbl_session_s *session) static void bbl_access_rx_lcp(bbl_access_interface_s *interface, bbl_session_s *session, - bbl_ethernet_header_s *eth) + bbl_lcp_s *lcp, + struct timespec *timestamp) { - bbl_pppoe_session_s *pppoes; - bbl_lcp_s *lcp; - UNUSED(interface); - pppoes = (bbl_pppoe_session_s*)eth->next; - lcp = (bbl_lcp_s*)pppoes->next; - if(session->session_state < BBL_PPP_LINK) { return; } @@ -1582,7 +1582,7 @@ bbl_access_rx_lcp(bbl_access_interface_s *interface, LOG(PPPOE, "LCP PROTOCOL REJECT (ID: %u) Protocol 0x%04x reject received\n", session->session_id, lcp->protocol); } - bbl_access_rx_established_pppoe(interface, session, eth); + bbl_access_rx_established_pppoe(interface, session, timestamp); break; default: session->lcp_response_code = PPP_CODE_CODE_REJECT; @@ -1600,52 +1600,64 @@ bbl_access_rx_lcp(bbl_access_interface_s *interface, } } -static void -bbl_access_rx_session(bbl_access_interface_s *interface, - bbl_session_s *session, - bbl_ethernet_header_s *eth) +void +bbl_ppp_rx(bbl_access_interface_s *interface, + bbl_session_s *session, + bbl_ethernet_header_s *eth, + uint16_t protocol, + void *next) { - bbl_pppoe_session_s *pppoes; - - pppoes = (bbl_pppoe_session_s*)eth->next; - if(pppoes->session_id != session->pppoe_session_id || - memcmp(session->server_mac, eth->src, ETH_ADDR_LEN) != 0) { - return; - } + struct timespec *timestamp = eth ? ð->timestamp : NULL; - switch(pppoes->protocol) { + switch(protocol) { case PROTOCOL_LCP: - bbl_access_rx_lcp(interface, session, eth); - interface->stats.lcp_rx++; + bbl_access_rx_lcp(interface, session, (bbl_lcp_s*)next, timestamp); + if(interface) interface->stats.lcp_rx++; break; case PROTOCOL_IPCP: - bbl_access_rx_ipcp(interface, session, eth); - interface->stats.ipcp_rx++; + bbl_access_rx_ipcp(interface, session, (bbl_ipcp_s*)next, timestamp); + if(interface) interface->stats.ipcp_rx++; break; case PROTOCOL_IP6CP: - bbl_access_rx_ip6cp(interface, session, eth); - interface->stats.ip6cp_rx++; + bbl_access_rx_ip6cp(interface, session, (bbl_ip6cp_s*)next, timestamp); + if(interface) interface->stats.ip6cp_rx++; break; case PROTOCOL_PAP: - bbl_access_rx_pap(interface, session, eth); - interface->stats.pap_rx++; + bbl_access_rx_pap(interface, session, (bbl_pap_s*)next, timestamp); + if(interface) interface->stats.pap_rx++; break; case PROTOCOL_CHAP: - bbl_access_rx_chap(interface, session, eth); - interface->stats.chap_rx++; + bbl_access_rx_chap(interface, session, (bbl_chap_s*)next, timestamp); + if(interface) interface->stats.chap_rx++; break; case PROTOCOL_IPV4: - bbl_access_rx_ipv4(interface, session, eth, (bbl_ipv4_s*)pppoes->next); + bbl_access_rx_ipv4(interface, session, eth, (bbl_ipv4_s*)next); break; case PROTOCOL_IPV6: - bbl_access_rx_ipv6(interface, session, eth, (bbl_ipv6_s*)pppoes->next); + bbl_access_rx_ipv6(interface, session, eth, (bbl_ipv6_s*)next); break; default: - interface->stats.unknown++; + if(interface) interface->stats.unknown++; break; } } +static void +bbl_access_rx_session(bbl_access_interface_s *interface, + bbl_session_s *session, + bbl_ethernet_header_s *eth) +{ + bbl_pppoe_session_s *pppoes; + + pppoes = (bbl_pppoe_session_s*)eth->next; + if(pppoes->session_id != session->pppoe_session_id || + memcmp(session->server_mac, eth->src, ETH_ADDR_LEN) != 0) { + return; + } + + bbl_ppp_rx(interface, session, eth, pppoes->protocol, pppoes->next); +} + void bbl_access_lcp_start_delay(timer_s *timer) { diff --git a/code/bngblaster/src/bbl_access.h b/code/bngblaster/src/bbl_access.h index 73a479e0..88769b99 100644 --- a/code/bngblaster/src/bbl_access.h +++ b/code/bngblaster/src/bbl_access.h @@ -14,6 +14,7 @@ typedef struct bbl_access_interface_ { char *name; /* interface name */ uint32_t ifindex; /* interface index */ + access_type_t access_type; /* pppoe, ipoe or l2tp */ /* parent */ bbl_interface_s *interface; @@ -133,12 +134,19 @@ bbl_access_rx_established_ipoe(bbl_access_interface_s *interface, void bbl_access_rx_established_pppoe(bbl_access_interface_s *interface, bbl_session_s *session, - bbl_ethernet_header_s *eth); + struct timespec *timestamp); void bbl_access_rx_handler(bbl_access_interface_s *interface, bbl_ethernet_header_s *eth); +void +bbl_ppp_rx(bbl_access_interface_s *interface, + bbl_session_s *session, + bbl_ethernet_header_s *eth, + uint16_t protocol, + void *next); + int bbl_access_ctrl_interfaces(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments __attribute__((unused))); diff --git a/code/bngblaster/src/bbl_config.c b/code/bngblaster/src/bbl_config.c index a2445a65..8f554c72 100644 --- a/code/bngblaster/src/bbl_config.c +++ b/code/bngblaster/src/bbl_config.c @@ -1066,7 +1066,7 @@ json_parse_access_interface(json_t *access_interface, bbl_access_config_s *acces "http-client-group-id", "icmp-client-group-id", "cfm-cc", "cfm-level", "cfm-interval", "cfm-md-name", "cfm-md-name-format", "cfm-ma-id", "cfm-ma-name", "cfm-ma-name-format", "cfm-seq", - "cfm-vlan-priority" + "cfm-vlan-priority", "l2tp-client-group-id" }; if(!schema_validate(access_interface, "access", schema, sizeof(schema)/sizeof(schema[0]))) { @@ -1128,6 +1128,8 @@ json_parse_access_interface(json_t *access_interface, bbl_access_config_s *acces access_config->access_type = ACCESS_TYPE_IPOE; access_config->ipv4_enable = g_ctx->config.ipoe_ipv4_enable; access_config->ipv6_enable = g_ctx->config.ipoe_ipv6_enable; + } else if(strcmp(s, "pppol2tp") == 0) { + access_config->access_type = ACCESS_TYPE_PPPOL2TP; } else { fprintf(stderr, "JSON config error: Invalid value for access->type\n"); return false; @@ -1449,6 +1451,11 @@ json_parse_access_interface(json_t *access_interface, bbl_access_config_s *acces } } + JSON_OBJ_GET_NUMBER(access_interface, value, "access", "l2tp-client-group-id", 0, 65535); + if(value) { + access_config->l2tp_client_group_id = json_number_value(value); + } + if(access_config->access_type == ACCESS_TYPE_PPPOE) { /* Disable IPv4 on PPPoE if IPCP is disabled. */ if(!access_config->ipcp_enable) { @@ -3281,6 +3288,7 @@ json_parse_config(json_t *root) bbl_access_line_profile_s *access_line_profile = NULL; bbl_l2tp_server_s *l2tp_server = NULL; + bbl_l2tp_client_s *l2tp_client = NULL; bbl_lag_config_s *lag_config = NULL; bbl_link_config_s *link_config = NULL; @@ -3311,7 +3319,7 @@ json_parse_config(json_t *root) "isis", "ospf", "bgp", "bgp-raw-update-files", "ldp", "ldp-raw-update-files", - "l2tp-server", "icmp-client", + "l2tp-server", "l2tp-client", "icmp-client", "http-client", "http-server", "arp-client" }; @@ -4528,6 +4536,143 @@ json_parse_config(json_t *root) fprintf(stderr, "JSON config error: List expected in L2TP server configuration but dictionary found\n"); } + /* L2TP Client Configuration (LAC) */ + section = json_object_get(root, "l2tp-client"); + if(json_is_array(section)) { + if(!g_ctx->config.network_config) { + fprintf(stderr, "JSON config error: Failed to add L2TP client 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); + + const char *schema[] = { + "group-id", "name", "secret", "server-address", "client-address", + "network-interface", "receive-window-size", "max-retry", "congestion-mode", + "data-control-priority", "data-length", "data-offset", "control-tos", + "data-control-tos", "hello-interval", "lcp-padding", "calling-number", + "called-number" + }; + if(!schema_validate(sub, "l2tp-client", schema, sizeof(schema)/sizeof(schema[0]))) { + return false; + } + + if(!l2tp_client) { + g_ctx->config.l2tp_client = calloc(1, sizeof(bbl_l2tp_client_s)); + l2tp_client = g_ctx->config.l2tp_client; + } else { + l2tp_client->next = calloc(1, sizeof(bbl_l2tp_client_s)); + l2tp_client = l2tp_client->next; + } + JSON_OBJ_GET_NUMBER(sub, value, "l2tp-client", "group-id", 1, 65535); + if(value) { + l2tp_client->group_id = json_number_value(value); + } else { + fprintf(stderr, "JSON config error: Missing value for l2tp-client->group-id\n"); + return false; + } + if(json_unpack(sub, "{s:s}", "name", &s) == 0) { + l2tp_client->name = strdup(s); + } else { + fprintf(stderr, "JSON config error: Missing value for l2tp-client->name\n"); + return false; + } + if(json_unpack(sub, "{s:s}", "network-interface", &s) == 0) { + l2tp_client->network_interface = strdup(s); + } else { + fprintf(stderr, "JSON config error: Missing value for l2tp-client->network-interface\n"); + return false; + } + if(json_unpack(sub, "{s:s}", "client-address", &s) == 0) { + if(!inet_pton(AF_INET, s, &ipv4)) { + fprintf(stderr, "JSON config error: Invalid value for l2tp-client->client-address\n"); + return false; + } + l2tp_client->client_address = ipv4; + } + if(json_unpack(sub, "{s:s}", "secret", &s) == 0) { + l2tp_client->secret = strdup(s); + } + if(json_unpack(sub, "{s:s}", "server-address", &s) == 0) { + if(!inet_pton(AF_INET, s, &ipv4)) { + fprintf(stderr, "JSON config error: Invalid value for l2tp-client->server-address\n"); + return false; + } + l2tp_client->server_ip = ipv4; + CIRCLEQ_INIT(&l2tp_client->tunnel_qhead); + } else { + fprintf(stderr, "JSON config error: Missing value for l2tp-client->server-address\n"); + return false; + } + JSON_OBJ_GET_NUMBER(sub, value, "l2tp-client", "receive-window-size", 1, 65535); + if(value) { + l2tp_client->receive_window = json_number_value(value); + } else { + l2tp_client->receive_window = 16; + } + JSON_OBJ_GET_NUMBER(sub, value, "l2tp-client", "max-retry", 1, 65535); + if(value) { + l2tp_client->max_retry = json_number_value(value); + } else { + l2tp_client->max_retry = 5; + } + if(json_unpack(sub, "{s:s}", "congestion-mode", &s) == 0) { + if(strcmp(s, "default") == 0) { + l2tp_client->congestion_mode = BBL_L2TP_CONGESTION_DEFAULT; + } else if(strcmp(s, "slow") == 0) { + l2tp_client->congestion_mode = BBL_L2TP_CONGESTION_SLOW; + } else if(strcmp(s, "aggressive") == 0) { + l2tp_client->congestion_mode = BBL_L2TP_CONGESTION_AGGRESSIVE; + } else { + fprintf(stderr, "JSON config error: Invalid value for l2tp-client->congestion-mode\n"); + return false; + } + } else { + l2tp_client->congestion_mode = BBL_L2TP_CONGESTION_DEFAULT; + } + JSON_OBJ_GET_BOOL(sub, value, "l2tp-client", "data-control-priority"); + if(value) { + l2tp_client->data_control_priority = json_boolean_value(value); + } + JSON_OBJ_GET_BOOL(sub, value, "l2tp-client", "data-length"); + if(value) { + l2tp_client->data_length = json_boolean_value(value); + } + JSON_OBJ_GET_BOOL(sub, value, "l2tp-client", "data-offset"); + if(value) { + l2tp_client->data_offset = json_boolean_value(value); + } + + JSON_OBJ_GET_NUMBER(sub, value, "l2tp-client", "control-tos", 0, 255); + if(value) { + l2tp_client->control_tos = json_number_value(value); + } + JSON_OBJ_GET_NUMBER(sub, value, "l2tp-client", "data-control-tos", 0, 255); + if(value) { + l2tp_client->data_control_tos = json_number_value(value); + } + JSON_OBJ_GET_NUMBER(sub, value, "l2tp-client", "hello-interval", 0, 65535); + if(value) { + l2tp_client->hello_interval = json_number_value(value); + } else { + l2tp_client->hello_interval = 30; + } + JSON_OBJ_GET_NUMBER(sub, value, "l2tp-client", "lcp-padding", 0, 65535); + if(value) { + l2tp_client->lcp_padding = json_number_value(value); + } + if(json_unpack(sub, "{s:s}", "calling-number", &s) == 0) { + l2tp_client->calling_number = strdup(s); + } + if(json_unpack(sub, "{s:s}", "called-number", &s) == 0) { + l2tp_client->called_number = strdup(s); + } + } + } else if(json_is_object(section)) { + fprintf(stderr, "JSON config error: List expected in L2TP client configuration but dictionary found\n"); + } + /* ARP Client Configuration */ sub = json_object_get(root, "arp-client"); if(json_is_array(sub)) { diff --git a/code/bngblaster/src/bbl_config.h b/code/bngblaster/src/bbl_config.h index d85f1db6..f025c72a 100644 --- a/code/bngblaster/src/bbl_config.h +++ b/code/bngblaster/src/bbl_config.h @@ -48,9 +48,10 @@ typedef struct bbl_access_config_ char *network_interface; char *a10nsp_interface; - access_type_t access_type; /* pppoe or ipoe */ + access_type_t access_type; /* pppoe, ipoe or pppol2tp */ vlan_mode_t vlan_mode; /* 1:1 (default) or N:1 */ + uint16_t l2tp_client_group_id; uint16_t stream_group_id; uint16_t session_group_id; uint16_t http_client_group_id; diff --git a/code/bngblaster/src/bbl_ctx.h b/code/bngblaster/src/bbl_ctx.h index 0ad090e2..91869bed 100644 --- a/code/bngblaster/src/bbl_ctx.h +++ b/code/bngblaster/src/bbl_ctx.h @@ -35,6 +35,7 @@ typedef struct bbl_ctx_ uint32_t interfaces; uint32_t sessions; uint32_t sessions_pppoe; + uint32_t sessions_pppol2tp; uint32_t sessions_ipoe; uint32_t sessions_established; uint32_t sessions_established_max; @@ -374,6 +375,9 @@ typedef struct bbl_ctx_ /* L2TP Server Config (LNS) */ bbl_l2tp_server_s *l2tp_server; + + /* L2TP Client Config (LAC) */ + struct bbl_l2tp_client_ *l2tp_client; } config; } bbl_ctx_s; diff --git a/code/bngblaster/src/bbl_def.h b/code/bngblaster/src/bbl_def.h index afd9c822..81f21771 100644 --- a/code/bngblaster/src/bbl_def.h +++ b/code/bngblaster/src/bbl_def.h @@ -135,7 +135,8 @@ typedef enum { typedef enum { ACCESS_TYPE_PPPOE = 0, - ACCESS_TYPE_IPOE + ACCESS_TYPE_IPOE, + ACCESS_TYPE_PPPOL2TP, } __attribute__ ((__packed__)) access_type_t; typedef enum { @@ -188,6 +189,7 @@ typedef enum { BBL_PPP_LINK, /* send LCP requests */ BBL_PPP_AUTH, /* send authentication requests */ BBL_PPP_NETWORK, /* send NCP requests */ + BBL_L2TP_WAIT, /* wait for L2TP session */ BBL_ESTABLISHED, /* established */ BBL_PPP_TERMINATING, /* send LCP terminate requests */ BBL_TERMINATING, /* send PADT */ diff --git a/code/bngblaster/src/bbl_dhcp.c b/code/bngblaster/src/bbl_dhcp.c index 545227c2..da38372f 100644 --- a/code/bngblaster/src/bbl_dhcp.c +++ b/code/bngblaster/src/bbl_dhcp.c @@ -238,10 +238,10 @@ bbl_dhcp_rx(bbl_session_s *session, bbl_ethernet_header_s *eth, bbl_dhcp_s *dhcp session->dhcp_address = dhcp->header->yiaddr; session->dhcp_server = dhcp->header->siaddr; session->dhcp_server_identifier = dhcp->server_identifier; - memcpy(session->dhcp_server_mac, eth->src, ETH_ADDR_LEN); + memcpy(session->dhcp_server_mac, eth ? eth->src : session->server_mac, ETH_ADDR_LEN); session->dhcp_lease_time = dhcp->lease_time; - session->dhcp_lease_timestamp.tv_sec = eth->timestamp.tv_sec; - session->dhcp_lease_timestamp.tv_nsec = eth->timestamp.tv_nsec; + session->dhcp_lease_timestamp.tv_sec = eth ? eth->timestamp.tv_sec : 0; + session->dhcp_lease_timestamp.tv_nsec = eth ? eth->timestamp.tv_nsec : 0; if(!(session->dhcp_address && session->dhcp_server_identifier && session->dhcp_lease_time)) { LOG(ERROR, "DHCP (ID: %u) Invalid DHCP-Offer!\n", session->session_id); bbl_dhcp_restart(session); @@ -260,10 +260,10 @@ bbl_dhcp_rx(bbl_session_s *session, bbl_ethernet_header_s *eth, bbl_dhcp_s *dhcp session->dhcp_address = dhcp->header->yiaddr; session->dhcp_server = dhcp->header->siaddr; session->dhcp_server_identifier = dhcp->server_identifier; - memcpy(session->dhcp_server_mac, eth->src, ETH_ADDR_LEN); + memcpy(session->dhcp_server_mac, eth ? eth->src : session->server_mac, ETH_ADDR_LEN); session->dhcp_lease_time = dhcp->lease_time; - session->dhcp_lease_timestamp.tv_sec = eth->timestamp.tv_sec; - session->dhcp_lease_timestamp.tv_nsec = eth->timestamp.tv_nsec; + session->dhcp_lease_timestamp.tv_sec = eth ? eth->timestamp.tv_sec : 0; + session->dhcp_lease_timestamp.tv_nsec = eth ? eth->timestamp.tv_nsec : 0; if(!(session->dhcp_address && session->dhcp_server_identifier && session->dhcp_lease_time)) { LOG(ERROR, "DHCP (ID: %u) Invalid DHCP-ACK!\n", session->session_id); bbl_dhcp_restart(session); @@ -346,8 +346,8 @@ bbl_dhcp_rx(bbl_session_s *session, bbl_ethernet_header_s *eth, bbl_dhcp_s *dhcp session->dhcp_server = dhcp->header->siaddr; session->dhcp_server_identifier = dhcp->server_identifier; session->dhcp_lease_time = dhcp->lease_time; - session->dhcp_lease_timestamp.tv_sec = eth->timestamp.tv_sec; - session->dhcp_lease_timestamp.tv_nsec = eth->timestamp.tv_nsec; + session->dhcp_lease_timestamp.tv_sec = eth ? eth->timestamp.tv_sec : 0; + session->dhcp_lease_timestamp.tv_nsec = eth ? eth->timestamp.tv_nsec : 0; if(!(session->dhcp_address && session->dhcp_server_identifier && session->dhcp_lease_time)) { LOG(ERROR, "DHCP (ID: %u) Invalid DHCP-ACK!\n", session->session_id); bbl_dhcp_restart(session); diff --git a/code/bngblaster/src/bbl_interactive.c b/code/bngblaster/src/bbl_interactive.c index 2201857f..8c107907 100644 --- a/code/bngblaster/src/bbl_interactive.c +++ b/code/bngblaster/src/bbl_interactive.c @@ -314,7 +314,8 @@ bbl_interactive_window_job(timer_s *timer) if(g_view_selected == UI_VIEW_DEFAULT) { VISIBLE((g_ctx->sessions)) { - wprintw(stats_win, "\nSessions %10u (%u PPPoE / %u IPoE)\n", g_ctx->sessions, g_ctx->sessions_pppoe, g_ctx->sessions_ipoe); + wprintw(stats_win, "\nSessions %10u (%u PPPoE / %u PPPoL2TP / %u IPoE)\n", + g_ctx->sessions, g_ctx->sessions_pppoe, g_ctx->sessions_pppol2tp, g_ctx->sessions_ipoe); /* Progress bar established sessions */ wprintw(stats_win, " Established %10u [", g_ctx->sessions_established); @@ -388,8 +389,8 @@ bbl_interactive_window_job(timer_s *timer) wprintw(stats_win, " RX Packets %10lu (%7lu PPS)\n", g_network_if->stats.li_rx, g_network_if->stats.rate_li_rx.avg); } - VISIBLE((g_ctx->config.l2tp_server)) { - wprintw(stats_win, "\nL2TP LNS Statistics\n"); + VISIBLE((g_ctx->config.l2tp_server || g_ctx->config.l2tp_client)) { + wprintw(stats_win, "\nL2TP LNS/LAC Statistics\n"); wprintw(stats_win, " Tunnels %10u\n", g_ctx->l2tp_tunnels); wprintw(stats_win, " Established %10u\n", g_ctx->l2tp_tunnels_established); wprintw(stats_win, " Sessions %10u\n", g_ctx->l2tp_sessions); diff --git a/code/bngblaster/src/bbl_l2tp.c b/code/bngblaster/src/bbl_l2tp.c index 74f6171d..bcef7c1e 100644 --- a/code/bngblaster/src/bbl_l2tp.c +++ b/code/bngblaster/src/bbl_l2tp.c @@ -16,6 +16,16 @@ void bbl_l2tp_send(bbl_l2tp_tunnel_s *l2tp_tunnel, bbl_l2tp_session_s *l2tp_session, l2tp_message_t l2tp_type); +const char* +l2tp_tunnel_hostname(bbl_l2tp_tunnel_s *l2tp_tunnel) +{ + if(l2tp_tunnel->is_lac) { + return l2tp_tunnel->client->name; + } else { + return l2tp_tunnel->server->host_name; + } +} + const char* l2tp_message_string(l2tp_message_t type) { @@ -158,11 +168,14 @@ bbl_l2tp_force_stop(bbl_l2tp_tunnel_s *l2tp_tunnel) void bbl_l2tp_session_delete(bbl_l2tp_session_s *l2tp_session) { + bbl_l2tp_tunnel_s *l2tp_tunnel; + if(l2tp_session) { + l2tp_tunnel = l2tp_session->tunnel; 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_session->tunnel->server->host_name, l2tp_session->tunnel->tunnel_id, l2tp_session->key.session_id); + l2tp_tunnel_hostname(l2tp_session->tunnel), l2tp_session->tunnel->tunnel_id, l2tp_session->key.session_id); if(g_ctx->l2tp_sessions) g_ctx->l2tp_sessions--; } @@ -174,9 +187,35 @@ bbl_l2tp_session_delete(bbl_l2tp_session_s *l2tp_session) /* Remove session from dict */ dict_remove(g_ctx->l2tp_session_dict, &l2tp_session->key); - /* Remove session from PPPoE session */ + /* Remove session from PPPoE/PPPoL2TP session */ if(l2tp_session->pppoe_session) { - l2tp_session->pppoe_session->l2tp_session = NULL; + bbl_session_s *session = l2tp_session->pppoe_session; + l2tp_session->pppoe_session = NULL; + session->l2tp_session = NULL; + /* In LAC mode the PPP session lifecycle is tied to the L2TP session: + * terminate it so that it can be restarted or counted as terminated. */ + if(l2tp_session->tunnel->is_lac && + session->session_state != BBL_TERMINATED && + session->session_state != BBL_IDLE) { + bbl_session_update_state(session, BBL_TERMINATED); + } + } + + /* If this was the last real session on an established tunnel, close it. + * The only entry left in session_qhead is the dummy tunnel session (id 0). */ + if(l2tp_tunnel->state == BBL_L2TP_TUNNEL_ESTABLISHED) { + bbl_l2tp_session_s *s; + bool has_sessions = false; + CIRCLEQ_FOREACH(s, &l2tp_tunnel->session_qhead, session_qnode) { + if(s->key.session_id != 0) { + has_sessions = true; + break; + } + } + if(!has_sessions) { + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); + } } /* Free tunnel memory */ @@ -205,7 +244,7 @@ bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_s *l2tp_tunnel) if(l2tp_tunnel) { if(l2tp_tunnel->tunnel_id) { LOG(DEBUG, "L2TP Debug (%s) Tunnel %u deleted\n", - l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id); + l2tp_tunnel_hostname(l2tp_tunnel), l2tp_tunnel->tunnel_id); } if(g_ctx->l2tp_tunnels) g_ctx->l2tp_tunnels--; @@ -216,9 +255,13 @@ bbl_l2tp_tunnel_delete(bbl_l2tp_tunnel_s *l2tp_tunnel) while (!CIRCLEQ_EMPTY(&l2tp_tunnel->session_qhead)) { bbl_l2tp_session_delete(CIRCLEQ_FIRST(&l2tp_tunnel->session_qhead)); } - /* Remove tunnel from server object */ + /* Remove tunnel from server/client object */ if(CIRCLEQ_NEXT(l2tp_tunnel, tunnel_qnode) != NULL) { - CIRCLEQ_REMOVE(&l2tp_tunnel->server->tunnel_qhead, l2tp_tunnel, tunnel_qnode); + if(l2tp_tunnel->is_lac) { + CIRCLEQ_REMOVE(&l2tp_tunnel->client->tunnel_qhead, l2tp_tunnel, tunnel_qnode); + } else { + CIRCLEQ_REMOVE(&l2tp_tunnel->server->tunnel_qhead, l2tp_tunnel, tunnel_qnode); + } CIRCLEQ_NEXT(l2tp_tunnel, tunnel_qnode) = NULL; } /* Cleanup send queues */ @@ -256,7 +299,7 @@ bbl_l2tp_tunnel_update_state(bbl_l2tp_tunnel_s *l2tp_tunnel, l2tp_tunnel_state_t if(l2tp_tunnel->state != state) { /* State has changed */ 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_hostname(l2tp_tunnel), l2tp_tunnel->tunnel_id, l2tp_tunnel_state_string(l2tp_tunnel->state), l2tp_tunnel_state_string(state)); @@ -267,7 +310,7 @@ bbl_l2tp_tunnel_update_state(bbl_l2tp_tunnel_s *l2tp_tunnel, l2tp_tunnel_state_t g_ctx->l2tp_tunnels_established_max = g_ctx->l2tp_tunnels_established; } LOG(L2TP, "L2TP Info (%s) Tunnel (%u) with %s (%s) established\n", - l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, + l2tp_tunnel_hostname(l2tp_tunnel), l2tp_tunnel->tunnel_id, l2tp_tunnel->peer_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); } else if(l2tp_tunnel->state == BBL_L2TP_TUNNEL_ESTABLISHED) { @@ -297,6 +340,8 @@ bbl_l2tp_tunnel_tx_job(timer_s *timer) int backoff; uint16_t max_ns = l2tp_tunnel->peer_nr + l2tp_tunnel->cwnd; + uint16_t max_retry = l2tp_tunnel->is_lac ? + l2tp_tunnel->client->max_retry : l2tp_tunnel->server->max_retry; l2tp_tunnel->timer_tx_active = false; if(l2tp_tunnel->state == BBL_L2TP_TUNNEL_SEND_STOPCCN) { @@ -311,6 +356,15 @@ bbl_l2tp_tunnel_tx_job(timer_s *timer) while(q != (const void *)(&l2tp_tunnel->tx_qhead)) { if(L2TP_SEQ_LT(q->ns, l2tp_tunnel->peer_nr)) { /* Delete acknowledged messages from queue. */ + /* If this entry was the ICCN for a PPP session, start the PPP + * state machine now that the LNS has acknowledged the ICCN. + * Waiting for the ACK gives the LNS time to complete its + * socket(AF_PPPOX)+connect() setup before the first PPP packet + * arrives, avoiding the kernel race where l2tp_session_find() + * returns NULL and silently drops the packet. */ + if(q->ppp_session) { + bbl_session_tx_qnode_insert(q->ppp_session); + } q_del = q; q = CIRCLEQ_NEXT(q, tunnel_tx_qnode); bbl_l2tp_tunnel_txq_remove(l2tp_tunnel, q_del); @@ -338,9 +392,9 @@ bbl_l2tp_tunnel_tx_job(timer_s *timer) if(q->retries) { l2tp_tunnel->stats.control_retry++; interface->stats.l2tp_control_retry++; - if(q->retries > l2tp_tunnel->server->max_retry) { + if(q->retries > max_retry) { LOG(ERROR, "L2TP Error (%s) Tunnel (%u) max retry to %s (%s)\n", - l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, + l2tp_tunnel_hostname(l2tp_tunnel), l2tp_tunnel->tunnel_id, l2tp_tunnel->peer_name, format_ipv4_address(&l2tp_tunnel->peer_ip)); l2tp_tunnel->result_code = 2; @@ -385,6 +439,9 @@ void bbl_l2tp_tunnel_control_job(timer_s *timer) { bbl_l2tp_tunnel_s *l2tp_tunnel = timer->data; + uint16_t hello_interval = l2tp_tunnel->is_lac ? + l2tp_tunnel->client->hello_interval : l2tp_tunnel->server->hello_interval; + l2tp_tunnel->state_seconds++; switch(l2tp_tunnel->state) { case BBL_L2TP_TUNNEL_WAIT_CTR_CONN: @@ -397,8 +454,8 @@ bbl_l2tp_tunnel_control_job(timer_s *timer) } break; case BBL_L2TP_TUNNEL_ESTABLISHED: - if(l2tp_tunnel->server->hello_interval) { - if(l2tp_tunnel->state_seconds % l2tp_tunnel->server->hello_interval == 0 && CIRCLEQ_EMPTY(&l2tp_tunnel->tx_qhead)) { + if(hello_interval) { + if(l2tp_tunnel->state_seconds % hello_interval == 0 && CIRCLEQ_EMPTY(&l2tp_tunnel->tx_qhead)) { bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_HELLO); } } @@ -457,9 +514,16 @@ bbl_l2tp_send(bbl_l2tp_tunnel_s *l2tp_tunnel, bbl_l2tp_session_s *l2tp_session, eth.type = ETH_TYPE_IPV4; eth.next = &ipv4; ipv4.dst = l2tp_tunnel->peer_ip; - ipv4.src = l2tp_tunnel->server->ip; + if(l2tp_tunnel->is_lac) { + ipv4.src = l2tp_tunnel->client->client_address ? + l2tp_tunnel->client->client_address : + l2tp_tunnel->interface->ip.address; + ipv4.tos = l2tp_tunnel->client->control_tos; + } else { + ipv4.src = l2tp_tunnel->server->ip; + ipv4.tos = l2tp_tunnel->server->control_tos; + } ipv4.ttl = 64; - ipv4.tos = l2tp_tunnel->server->control_tos; ipv4.protocol = PROTOCOL_IPV4_UDP; ipv4.next = &udp; udp.src = L2TP_UDP_PORT; @@ -525,11 +589,10 @@ bbl_l2tp_send(bbl_l2tp_tunnel_s *l2tp_tunnel, bbl_l2tp_session_s *l2tp_session, * @param protocol Payload type (IPCP, IPv4, ...). * @param next Payload structure. */ -static void +void bbl_l2tp_send_data(bbl_l2tp_session_s *l2tp_session, uint16_t protocol, void *next) { bbl_l2tp_tunnel_s *l2tp_tunnel = l2tp_session->tunnel; - bbl_l2tp_server_s *l2tp_server = l2tp_tunnel->server; bbl_network_interface_s *interface = l2tp_tunnel->interface; bbl_l2tp_queue_s *q = calloc(1, sizeof(bbl_l2tp_queue_s)); bbl_ethernet_header_s eth = {0}; @@ -543,7 +606,13 @@ bbl_l2tp_send_data(bbl_l2tp_session_s *l2tp_session, uint16_t protocol, void *ne eth.type = ETH_TYPE_IPV4; eth.next = &ipv4; ipv4.dst = l2tp_tunnel->peer_ip; - ipv4.src = l2tp_tunnel->server->ip; + if(l2tp_tunnel->is_lac) { + ipv4.src = l2tp_tunnel->client->client_address ? + l2tp_tunnel->client->client_address : + l2tp_tunnel->interface->ip.address; + } else { + ipv4.src = l2tp_tunnel->server->ip; + } ipv4.ttl = 64; ipv4.protocol = PROTOCOL_IPV4_UDP; ipv4.next = &udp; @@ -555,13 +624,24 @@ bbl_l2tp_send_data(bbl_l2tp_session_s *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_length; - l2tp.with_offset = l2tp_server->data_offset; - if(protocol != PROTOCOL_IPV4 && protocol != PROTOCOL_IPV6) { - if(l2tp_server->data_control_priority) { - l2tp.with_priority = true; + if(l2tp_tunnel->is_lac) { + l2tp.with_length = l2tp_tunnel->client->data_length; + l2tp.with_offset = l2tp_tunnel->client->data_offset; + if(protocol != PROTOCOL_IPV4 && protocol != PROTOCOL_IPV6) { + if(l2tp_tunnel->client->data_control_priority) { + l2tp.with_priority = true; + } + ipv4.tos = l2tp_tunnel->client->data_control_tos; + } + } else { + l2tp.with_length = l2tp_tunnel->server->data_length; + l2tp.with_offset = l2tp_tunnel->server->data_offset; + if(protocol != PROTOCOL_IPV4 && protocol != PROTOCOL_IPV6) { + if(l2tp_tunnel->server->data_control_priority) { + l2tp.with_priority = true; + } + ipv4.tos = l2tp_tunnel->server->data_control_tos; } - ipv4.tos = l2tp_tunnel->server->data_control_tos; } l2tp.next = next; if(encode_ethernet(q->packet, &len, ð) == PROTOCOL_SUCCESS) { @@ -633,15 +713,15 @@ bbl_l2tp_sccrq_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth l2tp_tunnel2->peer_tunnel_id == l2tp_tunnel->peer_tunnel_id) { if(l2tp_tunnel2->state > BBL_L2TP_TUNNEL_WAIT_CTR_CONN) { LOG(ERROR, "L2TP Error (%s) Tunnel (%u) SCCRQ in wrong state (%s) received from %s (%s)\n", - l2tp_tunnel2->server->host_name, l2tp_tunnel2->tunnel_id, - l2tp_tunnel_state_string(l2tp_tunnel2->state), + l2tp_tunnel_hostname(l2tp_tunnel2), l2tp_tunnel2->tunnel_id, + l2tp_tunnel_state_string(l2tp_tunnel2->state), l2tp_tunnel2->peer_name, format_ipv4_address(&ipv4->src)); bbl_l2tp_tunnel_update_state(l2tp_tunnel2, BBL_L2TP_TUNNEL_TERMINATED); } else { /* Seems to be an SCCRQ retry ... */ LOG(PACKET, "L2TP (%s) SCCRQ retry received from %s (%s)\n", - l2tp_tunnel2->server->host_name, l2tp_tunnel2->peer_name, + l2tp_tunnel_hostname(l2tp_tunnel2), l2tp_tunnel2->peer_name, format_ipv4_address(&ipv4->src)); } bbl_l2tp_tunnel_delete(l2tp_tunnel); @@ -655,7 +735,7 @@ bbl_l2tp_sccrq_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth l2tp_tunnel->server = l2tp_server; LOG(PACKET, "L2TP (%s) SCCRQ received from %s (%s)\n", - l2tp_server->host_name, l2tp_tunnel->peer_name, + l2tp_tunnel_hostname(l2tp_tunnel), l2tp_tunnel->peer_name, format_ipv4_address(&ipv4->src)); /* Add dummy tunnel session, this session is only used @@ -681,7 +761,7 @@ bbl_l2tp_sccrq_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth result = dict_insert(g_ctx->l2tp_session_dict, &l2tp_session->key); if(!result.inserted) { LOG(ERROR, "L2TP Error (%s) Failed to add tunnel session\n", - l2tp_tunnel->server->host_name); + l2tp_tunnel_hostname(l2tp_tunnel)); free(l2tp_session); bbl_l2tp_tunnel_delete(l2tp_tunnel); return; @@ -707,7 +787,7 @@ bbl_l2tp_sccrq_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth /* 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_hostname(l2tp_tunnel), format_ipv4_address(&l2tp_tunnel->peer_ip)); l2tp_tunnel->result_code = 2; l2tp_tunnel->error_code = 6; l2tp_tunnel->error_message = "missing challenge"; @@ -718,7 +798,7 @@ bbl_l2tp_sccrq_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth /* 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_hostname(l2tp_tunnel), format_ipv4_address(&l2tp_tunnel->peer_ip)); l2tp_tunnel->result_code = 2; l2tp_tunnel->error_code = 6; l2tp_tunnel->error_message = "no challenge expected"; @@ -760,7 +840,7 @@ bbl_l2tp_scccn_rx(bbl_network_interface_s *interface, 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_hostname(l2tp_tunnel), format_ipv4_address(&l2tp_tunnel->peer_ip)); l2tp_tunnel->result_code = 2; l2tp_tunnel->error_code = 6; l2tp_tunnel->error_message = "decode error"; @@ -778,7 +858,7 @@ bbl_l2tp_scccn_rx(bbl_network_interface_s *interface, 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_hostname(l2tp_tunnel), format_ipv4_address(&l2tp_tunnel->peer_ip)); l2tp_tunnel->result_code = 2; l2tp_tunnel->error_code = 6; l2tp_tunnel->error_message = "challenge authentication failed"; @@ -788,7 +868,7 @@ bbl_l2tp_scccn_rx(bbl_network_interface_s *interface, } } 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_hostname(l2tp_tunnel), format_ipv4_address(&l2tp_tunnel->peer_ip)); l2tp_tunnel->result_code = 2; l2tp_tunnel->error_code = 6; l2tp_tunnel->error_message = "missing challenge response"; @@ -810,6 +890,12 @@ bbl_l2tp_stopccn_rx(bbl_network_interface_s *interface, UNUSED(eth); UNUSED(l2tp); + /* RFC 2661 only requires a ZLB ACK in response to StopCCN, but many + * implementations expect a StopCCN back to complete the teardown + * immediately rather than waiting for their own timer to expire. */ + if(l2tp_tunnel->state < BBL_L2TP_TUNNEL_SEND_STOPCCN) { + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); + } bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_RCVD_STOPCCN); } @@ -866,7 +952,7 @@ bbl_l2tp_icrq_rx(bbl_network_interface_s *interface, result = dict_insert(g_ctx->l2tp_session_dict, &l2tp_session->key); if(!result.inserted) { LOG(ERROR, "L2TP Error (%s) Failed to add session\n", - l2tp_tunnel->server->host_name); + l2tp_tunnel_hostname(l2tp_tunnel)); free(l2tp_session); return; } @@ -904,7 +990,7 @@ bbl_l2tp_iccn_rx(bbl_network_interface_s *interface, 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) established\n", - l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, + l2tp_tunnel_hostname(l2tp_tunnel), l2tp_tunnel->tunnel_id, l2tp_tunnel->peer_name, format_ipv4_address(&l2tp_tunnel->peer_ip), l2tp_session->key.session_id); @@ -930,7 +1016,7 @@ bbl_l2tp_cdn_rx(bbl_network_interface_s *interface, 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_hostname(l2tp_tunnel), l2tp_tunnel->tunnel_id, l2tp_tunnel->peer_name, format_ipv4_address(&l2tp_tunnel->peer_ip), l2tp_session->key.session_id); @@ -1047,6 +1133,7 @@ bbl_l2tp_data_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth, bbl_l2tp_s *l2tp) { bbl_lcp_s *lcp_rx; + bbl_lcp_s lcp_tx; bbl_pap_s *pap_rx; bbl_pap_s pap_tx; bbl_chap_s *chap_rx; @@ -1066,6 +1153,17 @@ bbl_l2tp_data_rx(bbl_network_interface_s *interface, } l2tp_session->stats.data_rx++; + + /* LAC mode: route received PPP packets to the generic PPP client RX path. */ + if(l2tp_session->tunnel->is_lac) { + bbl_session_s *session = l2tp_session->pppoe_session; + if(session) { + bbl_ppp_rx(session->access_interface, session, NULL, + l2tp->protocol, l2tp->next); + } + return; + } + switch(l2tp->protocol) { case PROTOCOL_LCP: lcp_rx = (bbl_lcp_s*)l2tp->next; @@ -1079,7 +1177,40 @@ bbl_l2tp_data_rx(bbl_network_interface_s *interface, l2tp_session->disconnect_direction = 1; bbl_l2tp_send(l2tp_session->tunnel, l2tp_session, L2TP_MESSAGE_CDN); bbl_l2tp_session_delete(l2tp_session); - } + } else if(lcp_rx->code == PPP_CODE_CONF_REQUEST) { + /* Accept whatever the peer proposes. */ + lcp_rx->code = PPP_CODE_CONF_ACK; + bbl_l2tp_send_data(l2tp_session, PROTOCOL_LCP, lcp_rx); + if(l2tp_session->lcp_state == BBL_PPP_LOCAL_ACK) { + l2tp_session->lcp_state = BBL_PPP_OPENED; + } else if(l2tp_session->lcp_state != BBL_PPP_OPENED) { + memset(&lcp_tx, 0x0, sizeof(bbl_lcp_s)); + l2tp_session->lcp_state = BBL_PPP_PEER_ACK; + lcp_tx.code = PPP_CODE_CONF_REQUEST; + lcp_tx.identifier = 1; + lcp_tx.auth = PROTOCOL_PAP; + lcp_tx.magic = (uint32_t)l2tp_session->key.tunnel_id << 16 | + l2tp_session->key.session_id; + if(!lcp_tx.magic) lcp_tx.magic = 1; + lcp_tx.padding = l2tp_session->tunnel->server->lcp_padding; + bbl_l2tp_send_data(l2tp_session, PROTOCOL_LCP, &lcp_tx); + } + } else if(lcp_rx->code == PPP_CODE_CONF_ACK) { + if(l2tp_session->lcp_state == BBL_PPP_PEER_ACK) { + l2tp_session->lcp_state = BBL_PPP_OPENED; + } else if(l2tp_session->lcp_state != BBL_PPP_OPENED) { + memset(&lcp_tx, 0x0, sizeof(bbl_lcp_s)); + l2tp_session->lcp_state = BBL_PPP_LOCAL_ACK; + lcp_tx.code = PPP_CODE_CONF_REQUEST; + lcp_tx.identifier = 1; + lcp_tx.auth = PROTOCOL_PAP; + lcp_tx.magic = (uint32_t)l2tp_session->key.tunnel_id << 16 | + l2tp_session->key.session_id; + if(!lcp_tx.magic) lcp_tx.magic = 1; + lcp_tx.padding = l2tp_session->tunnel->server->lcp_padding; + bbl_l2tp_send_data(l2tp_session, PROTOCOL_LCP, &lcp_tx); + } + } break; case PROTOCOL_PAP: memset(&pap_tx, 0x0, sizeof(bbl_pap_s)); @@ -1186,6 +1317,420 @@ bbl_l2tp_data_rx(bbl_network_interface_s *interface, } } +/** + * bbl_l2tp_client_session_connect + * + * Initiate a new L2TP session within an established LAC tunnel by sending ICRQ. + */ +void +bbl_l2tp_client_session_connect(bbl_l2tp_tunnel_s *l2tp_tunnel, bbl_session_s *session) +{ + bbl_l2tp_session_s *l2tp_session; + dict_insert_result result; + void **search; + + if(l2tp_tunnel->state != BBL_L2TP_TUNNEL_ESTABLISHED) { + /* Queue session to pending list */ + CIRCLEQ_INSERT_TAIL(&l2tp_tunnel->pending_session_qhead, session, session_l2tp_qnode); + return; + } + + l2tp_session = calloc(1, sizeof(bbl_l2tp_session_s)); + g_ctx->l2tp_sessions++; + l2tp_session->tunnel = l2tp_tunnel; + l2tp_session->state = BBL_L2TP_SESSION_WAIT_CONN; + + l2tp_session->key.tunnel_id = l2tp_tunnel->tunnel_id; + + /* Assign session id ... */ + while(true) { + l2tp_session->key.session_id = l2tp_tunnel->next_session_id++; + if(l2tp_session->key.session_id == 0) continue; /* skip session 0 */ + search = dict_search(g_ctx->l2tp_session_dict, &l2tp_session->key); + if(search) { + /* Used, try next ... */ + continue; + } else { + break; + } + } + result = dict_insert(g_ctx->l2tp_session_dict, &l2tp_session->key); + if(!result.inserted) { + LOG(ERROR, "L2TP Error (%s) Failed to add session\n", + l2tp_tunnel_hostname(l2tp_tunnel)); + free(l2tp_session); + return; + } + *result.datum_ptr = l2tp_session; + CIRCLEQ_INSERT_TAIL(&l2tp_tunnel->session_qhead, l2tp_session, session_qnode); + if(g_ctx->l2tp_sessions > g_ctx->l2tp_sessions_max) { + g_ctx->l2tp_sessions_max = g_ctx->l2tp_sessions; + } + /* Link the PPP session and the L2TP session to each other. */ + l2tp_session->pppoe_session = session; + session->l2tp_session = l2tp_session; + bbl_l2tp_send(l2tp_tunnel, l2tp_session, L2TP_MESSAGE_ICRQ); +} + +/** + * bbl_l2tp_sccrp_rx + * + * Handle SCCRP received from LNS (LAC mode). + */ +static void +bbl_l2tp_sccrp_rx(bbl_network_interface_s *interface, + bbl_l2tp_tunnel_s *l2tp_tunnel, + bbl_ethernet_header_s *eth, bbl_l2tp_s *l2tp) +{ + uint8_t digest[L2TP_MD5_DIGEST_LEN]; + MD5_CTX md5_ctx; + uint8_t l2tp_type; + + UNUSED(interface); + UNUSED(eth); + + if(l2tp_tunnel->state != BBL_L2TP_TUNNEL_WAIT_CTR_CONN) { + return; + } + if(!bbl_l2tp_avp_decode_tunnel(l2tp, l2tp_tunnel)) { + LOG(ERROR, "L2TP Error (%s) Invalid SCCRP received from %s\n", + l2tp_tunnel_hostname(l2tp_tunnel), format_ipv4_address(&l2tp_tunnel->peer_ip)); + l2tp_tunnel->result_code = 2; + l2tp_tunnel->error_code = 6; + l2tp_tunnel->error_message = "decode error"; + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); + return; + } + /* Validate challenge response if secret is configured */ + if(l2tp_tunnel->client->secret) { + if(l2tp_tunnel->peer_challenge_response_len) { + l2tp_type = L2TP_MESSAGE_SCCRP; + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, &l2tp_type, 1); + MD5_Update(&md5_ctx, (unsigned char *)l2tp_tunnel->client->secret, strlen(l2tp_tunnel->client->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 SCCRP from %s\n", + l2tp_tunnel_hostname(l2tp_tunnel), format_ipv4_address(&l2tp_tunnel->peer_ip)); + l2tp_tunnel->result_code = 2; + l2tp_tunnel->error_code = 6; + l2tp_tunnel->error_message = "challenge authentication failed"; + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); + return; + } + } else { + LOG(ERROR, "L2TP Error (%s) Missing challenge response in SCCRP from %s\n", + l2tp_tunnel_hostname(l2tp_tunnel), format_ipv4_address(&l2tp_tunnel->peer_ip)); + l2tp_tunnel->result_code = 2; + l2tp_tunnel->error_code = 6; + l2tp_tunnel->error_message = "missing challenge response"; + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_SEND_STOPCCN); + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_STOPCCN); + return; + } + /* Compute challenge response for SCCCN */ + 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_SCCCN; + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, &l2tp_type, 1); + MD5_Update(&md5_ctx, (unsigned char *)l2tp_tunnel->client->secret, strlen(l2tp_tunnel->client->secret)); + MD5_Update(&md5_ctx, l2tp_tunnel->peer_challenge, l2tp_tunnel->peer_challenge_len); + MD5_Final(l2tp_tunnel->challenge_response, &md5_ctx); + } + } + /* Now that peer_tunnel_id is known, patch it into the pre-built ZLB packet. + * The ZLB was encoded in bbl_l2tp_client_connect() when peer_tunnel_id was + * still 0; the tunnel_id field sits 4 bytes before the Ns field (tunnel_id(2) + * + session_id(2)), so its offset is ns_offset - 4. */ + if(l2tp_tunnel->zlb_qnode) { + *(uint16_t*)(l2tp_tunnel->zlb_qnode->packet + l2tp_tunnel->zlb_qnode->ns_offset - 4) = + htobe16(l2tp_tunnel->peer_tunnel_id); + } + + /* Send SCCCN and transition to established */ + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_SCCCN); + bbl_l2tp_tunnel_update_state(l2tp_tunnel, BBL_L2TP_TUNNEL_ESTABLISHED); + + /* Process pending sessions */ + while(!CIRCLEQ_EMPTY(&l2tp_tunnel->pending_session_qhead)) { + bbl_session_s *session = CIRCLEQ_FIRST(&l2tp_tunnel->pending_session_qhead); + CIRCLEQ_REMOVE(&l2tp_tunnel->pending_session_qhead, session, session_l2tp_qnode); + bbl_l2tp_client_session_connect(l2tp_tunnel, session); + } +} + +/** + * bbl_l2tp_icrp_rx + * + * Handle ICRP received from LNS (LAC mode). + */ +static void +bbl_l2tp_icrp_rx(bbl_network_interface_s *interface, + bbl_l2tp_session_s *l2tp_session, + bbl_ethernet_header_s *eth, bbl_l2tp_s *l2tp) +{ + bbl_l2tp_tunnel_s *l2tp_tunnel = l2tp_session->tunnel; + + UNUSED(interface); + UNUSED(eth); + + if(!l2tp_session) { + return; + } + 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); + bbl_l2tp_session_delete(l2tp_session); + return; + } + if(l2tp_session->state == BBL_L2TP_SESSION_WAIT_CONN) { + /* Send ICCN */ + bbl_l2tp_send(l2tp_tunnel, l2tp_session, L2TP_MESSAGE_ICCN); + l2tp_session->state = BBL_L2TP_SESSION_ESTABLISHED; + LOG(L2TP, "L2TP Info (%s) Tunnel (%u) to %s (%s) session (%u) established\n", + l2tp_tunnel_hostname(l2tp_tunnel), l2tp_tunnel->tunnel_id, + l2tp_tunnel->peer_name, + format_ipv4_address(&l2tp_tunnel->peer_ip), + l2tp_session->key.session_id); + /* Prepare the PPP state machine but defer the TX insert until the ICCN + * has actually been submitted to the network interface TX queue. + * bbl_l2tp_tunnel_tx_job() will call bbl_session_tx_qnode_insert() once + * it appends the ICCN, guaranteeing ICCN is queued before the first LCP + * packet in network_interface->l2tp_tx_qhead. */ + if(l2tp_session->pppoe_session) { + bbl_session_s *session = l2tp_session->pppoe_session; + bbl_session_update_state(session, BBL_PPP_LINK); + session->lcp_state = BBL_PPP_INIT; + session->lcp_request_code = PPP_CODE_CONF_REQUEST; + session->send_requests |= BBL_SEND_LCP_REQUEST; + /* Tag the ICCN queue entry so that PPP is started only after the + * LNS acknowledges the ICCN, giving it time to complete its + * socket(AF_PPPOX)+connect() setup before the first PPP packet arrives. + * bbl_l2tp_tunnel_tx_job() removes the entry from tx_qhead when the + * ACK is received, so the PPP session is started exactly once. */ + bbl_l2tp_queue_s *iccn_q = CIRCLEQ_LAST(&l2tp_tunnel->tx_qhead); + if(iccn_q != (void *)&l2tp_tunnel->tx_qhead) { + iccn_q->ppp_session = session; + } + } + } +} + + +/** + * bbl_l2tp_client_connect + * + * Initiate a new L2TP tunnel as LAC by sending SCCRQ + * to the configured LNS server. + * + * @param l2tp_client L2TP client configuration. + * @return The new L2TP tunnel on success, or NULL on error. + */ +bbl_l2tp_tunnel_s * +bbl_l2tp_client_connect(bbl_l2tp_client_s *l2tp_client) +{ + bbl_network_interface_s *network_interface; + bbl_l2tp_tunnel_s *l2tp_tunnel; + bbl_l2tp_session_s *l2tp_session; + dict_insert_result result; + void **search; + + /* Find the network interface */ + network_interface = bbl_network_interface_get(l2tp_client->network_interface); + if(!network_interface) { + LOG(ERROR, "L2TP Error (%s) Network interface %s not found\n", + l2tp_client->name, l2tp_client->network_interface ? l2tp_client->network_interface : "default"); + return NULL; + } + + /* Register client_address for ARP replies if distinct from the interface address */ + if(l2tp_client->client_address && l2tp_client->client_address != network_interface->ip.address) { + bbl_secondary_ip_s *secondary_ip = network_interface->secondary_ip_addresses; + bool already_registered = false; + while(secondary_ip) { + if(secondary_ip->ip == l2tp_client->client_address) { + already_registered = true; + break; + } + secondary_ip = secondary_ip->next; + } + if(!already_registered) { + secondary_ip = calloc(1, sizeof(bbl_secondary_ip_s)); + secondary_ip->ip = l2tp_client->client_address; + secondary_ip->next = network_interface->secondary_ip_addresses; + network_interface->secondary_ip_addresses = secondary_ip; + } + } + + /* Create tunnel */ + l2tp_tunnel = calloc(1, sizeof(bbl_l2tp_tunnel_s)); + g_ctx->l2tp_tunnels++; + CIRCLEQ_INIT(&l2tp_tunnel->tx_qhead); + CIRCLEQ_INIT(&l2tp_tunnel->session_qhead); + CIRCLEQ_INIT(&l2tp_tunnel->pending_session_qhead); + + l2tp_tunnel->is_lac = true; + l2tp_tunnel->client = l2tp_client; + l2tp_tunnel->interface = network_interface; + l2tp_tunnel->peer_ip = l2tp_client->server_ip; + l2tp_tunnel->peer_receive_window = 4; + l2tp_tunnel->ssthresh = 4; + l2tp_tunnel->cwnd = 1; + l2tp_tunnel->state = BBL_L2TP_TUNNEL_WAIT_CTR_CONN; + + /* Add dummy tunnel session (session ID 0) for tunnel-level + * control message lookups in the session dict. */ + l2tp_session = calloc(1, sizeof(bbl_l2tp_session_s)); + l2tp_session->state = BBL_L2TP_SESSION_MAX; + l2tp_session->tunnel = l2tp_tunnel; + l2tp_session->key.session_id = 0; + + /* Assign tunnel id ... */ + while(true) { + l2tp_session->key.tunnel_id = g_ctx->next_tunnel_id++; + if(l2tp_session->key.tunnel_id == 0) continue; /* skip tunnel 0 */ + search = dict_search(g_ctx->l2tp_session_dict, &l2tp_session->key); + if(search) { + continue; + } else { + break; + } + } + l2tp_tunnel->tunnel_id = l2tp_session->key.tunnel_id; + result = dict_insert(g_ctx->l2tp_session_dict, &l2tp_session->key); + if(!result.inserted) { + LOG(ERROR, "L2TP Error (%s) Failed to add tunnel session\n", + l2tp_client->name); + free(l2tp_session); + free(l2tp_tunnel); + return NULL; + } + *result.datum_ptr = l2tp_session; + CIRCLEQ_INSERT_TAIL(&l2tp_tunnel->session_qhead, l2tp_session, session_qnode); + if(g_ctx->l2tp_tunnels > g_ctx->l2tp_tunnels_max) g_ctx->l2tp_tunnels_max = g_ctx->l2tp_tunnels; + + /* L2TP Challenge */ + if(l2tp_client->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); + } + + /* Add tunnel to client config */ + CIRCLEQ_INSERT_TAIL(&l2tp_client->tunnel_qhead, l2tp_tunnel, tunnel_qnode); + + /* Start control timer */ + timer_add_periodic(&g_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 SCCRQ */ + bbl_l2tp_send(l2tp_tunnel, NULL, L2TP_MESSAGE_SCCRQ); + /* Mark the start of session establishment for setup-time calculation. + * Equivalent to PADI in PPPoE: the first outbound control packet. */ + if(!g_ctx->stats.first_session_tx.tv_sec) { + clock_gettime(CLOCK_MONOTONIC, &g_ctx->stats.first_session_tx); + } + + LOG(L2TP, "L2TP Info (%s) Tunnel (%u) SCCRQ sent to %s\n", + l2tp_client->name, + l2tp_tunnel->tunnel_id, + format_ipv4_address(&l2tp_client->server_ip)); + + return l2tp_tunnel; +} + +/** + * bbl_l2tp_client_session_get_tunnel + * + * Return a live LAC tunnel for the session's group-id, creating one per + * l2tp-client config entry on demand. A tunnel is "live" when its state + * is below BBL_L2TP_TUNNEL_SEND_STOPCCN; a tearing-down tunnel is not + * reused so that reconnecting sessions get a fresh tunnel. + */ +bbl_l2tp_tunnel_s * +bbl_l2tp_client_session_get_tunnel(bbl_session_s *session) +{ + bbl_l2tp_client_s *l2tp_client; + bbl_l2tp_tunnel_s *l2tp_tunnel; + uint16_t group_id = session->access_config->l2tp_client_group_id; + uint32_t tunnel_count = 0; + uint32_t target; + bool has_live; + + if(!group_id) { + LOG(ERROR, "L2TP Error (ID: %u) no L2TP client group-id was specified\n", session->session_id); + bbl_session_update_state(session, BBL_TERMINATED); + return NULL; + } + + /* Ensure every l2tp-client config entry in the group has a live tunnel. + * If the entry's tunnel_qhead is empty, or all its tunnels are already + * tearing down, create a new one now. */ + l2tp_client = g_ctx->config.l2tp_client; + while(l2tp_client) { + if(l2tp_client->group_id == group_id) { + has_live = false; + CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_client->tunnel_qhead, tunnel_qnode) { + if(l2tp_tunnel->is_lac && l2tp_tunnel->state < BBL_L2TP_TUNNEL_SEND_STOPCCN) { + has_live = true; + break; + } + } + if(!has_live) { + bbl_l2tp_client_connect(l2tp_client); + } + } + l2tp_client = l2tp_client->next; + } + + /* First pass: count live LAC tunnels in the group. */ + l2tp_client = g_ctx->config.l2tp_client; + while(l2tp_client) { + if(l2tp_client->group_id == group_id) { + CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_client->tunnel_qhead, tunnel_qnode) { + if(l2tp_tunnel->is_lac && l2tp_tunnel->state < BBL_L2TP_TUNNEL_SEND_STOPCCN) { + tunnel_count++; + } + } + } + l2tp_client = l2tp_client->next; + } + + if(!tunnel_count) { + LOG(ERROR, "L2TP Error (ID: %u) no tunnel available for L2TP client group-id %u\n", + session->session_id, group_id); + bbl_session_update_state(session, BBL_TERMINATED); + return NULL; + } + + /* Second pass: pick tunnel at index session_id % tunnel_count, + * giving an even round-robin distribution without any stored state. */ + target = session->session_id % tunnel_count; + tunnel_count = 0; + l2tp_client = g_ctx->config.l2tp_client; + while(l2tp_client) { + if(l2tp_client->group_id == group_id) { + CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_client->tunnel_qhead, tunnel_qnode) { + if(!l2tp_tunnel->is_lac || l2tp_tunnel->state >= BBL_L2TP_TUNNEL_SEND_STOPCCN) continue; + if(tunnel_count == target) return l2tp_tunnel; + tunnel_count++; + } + } + l2tp_client = l2tp_client->next; + } + + return NULL; +} + /** * bbl_l2tp_handler_rx * @@ -1203,17 +1748,18 @@ bbl_l2tp_handler_rx(bbl_network_interface_s *interface, bbl_ipv4_s *ipv4 = (bbl_ipv4_s*)eth->next; bbl_l2tp_session_s *l2tp_session; bbl_l2tp_tunnel_s *l2tp_tunnel; - + void **search; l2tp_key_t key = {0}; - void **search = NULL; - if(!g_ctx->config.l2tp_server) { - /* No L2TP server configuration found! */ + if(!g_ctx->config.l2tp_server && !g_ctx->config.l2tp_client) { + /* No L2TP configuration found! */ return; } if(l2tp->type == L2TP_MESSAGE_SCCRQ) { - bbl_l2tp_sccrq_rx(interface, eth, l2tp); + if(g_ctx->config.l2tp_server) { + bbl_l2tp_sccrq_rx(interface, eth, l2tp); + } return; } @@ -1247,7 +1793,7 @@ bbl_l2tp_handler_rx(bbl_network_interface_s *interface, if(l2tp_tunnel->nr == l2tp->ns) { /* In-Order packet received */ LOG(PACKET, "L2TP (%s) Tunnel (%u) %s received from %s\n", - l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, + l2tp_tunnel_hostname(l2tp_tunnel), l2tp_tunnel->tunnel_id, l2tp_message_string(l2tp->type), format_ipv4_address(&ipv4->src)); /* Update tunnel */ @@ -1263,7 +1809,9 @@ bbl_l2tp_handler_rx(bbl_network_interface_s *interface, } } /* Reliable Delivery of Control Messages */ - switch(l2tp_tunnel->server->congestion_mode) { + l2tp_congestion_mode_t cmode = l2tp_tunnel->is_lac ? + l2tp_tunnel->client->congestion_mode : l2tp_tunnel->server->congestion_mode; + switch(cmode) { case BBL_L2TP_CONGESTION_AGGRESSIVE: l2tp_tunnel->cwnd = l2tp_tunnel->peer_receive_window; break; @@ -1305,6 +1853,12 @@ bbl_l2tp_handler_rx(bbl_network_interface_s *interface, /* Handle received packet */ if(l2tp_tunnel->state != BBL_L2TP_TUNNEL_TERMINATED) { switch(l2tp->type) { + case L2TP_MESSAGE_SCCRP: + if(l2tp_tunnel->is_lac) { + bbl_l2tp_sccrp_rx(interface, l2tp_tunnel, eth, l2tp); + return; + } + break; case L2TP_MESSAGE_SCCCN: bbl_l2tp_scccn_rx(interface, l2tp_tunnel, eth, l2tp); return; @@ -1314,6 +1868,12 @@ bbl_l2tp_handler_rx(bbl_network_interface_s *interface, case L2TP_MESSAGE_ICRQ: bbl_l2tp_icrq_rx(interface, l2tp_tunnel, eth, l2tp); return; + case L2TP_MESSAGE_ICRP: + if(l2tp_tunnel->is_lac && l2tp_session->key.session_id) { + bbl_l2tp_icrp_rx(interface, l2tp_session, eth, l2tp); + return; + } + break; case L2TP_MESSAGE_ICCN: if(l2tp_session->key.session_id) { bbl_l2tp_iccn_rx(interface, l2tp_session, eth, l2tp); @@ -1337,7 +1897,7 @@ bbl_l2tp_handler_rx(bbl_network_interface_s *interface, if(L2TP_SEQ_LT(l2tp->ns, l2tp_tunnel->nr)) { /* Duplicate packet received */ LOG(DEBUG, "L2TP Debug (%s) Tunnel (%u) Duplicate %s received with Ns. %u (expected %u) from %s\n", - l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, + l2tp_tunnel_hostname(l2tp_tunnel), l2tp_tunnel->tunnel_id, l2tp_message_string(l2tp->type), l2tp->ns, l2tp_tunnel->nr, format_ipv4_address(&ipv4->src)); @@ -1353,7 +1913,7 @@ bbl_l2tp_handler_rx(bbl_network_interface_s *interface, } else { /* Out-of-Order packet received */ LOG(DEBUG, "L2TP Debug (%s) Tunnel (%u) Out-of-Order %s received with Ns. %u (expected %u) from %s\n", - l2tp_tunnel->server->host_name, l2tp_tunnel->tunnel_id, + l2tp_tunnel_hostname(l2tp_tunnel), l2tp_tunnel->tunnel_id, l2tp_message_string(l2tp->type), l2tp->ns, l2tp_tunnel->nr, format_ipv4_address(&ipv4->src)); @@ -1668,6 +2228,7 @@ bbl_l2tp_ctrl_tunnels(int fd, uint32_t session_id __attribute__((unused)), json_ json_t *root, *tunnels, *tunnel; bbl_l2tp_server_s *l2tp_server = g_ctx->config.l2tp_server; + bbl_l2tp_client_s *l2tp_client = g_ctx->config.l2tp_client; bbl_l2tp_tunnel_s *l2tp_tunnel; tunnels = json_array(); @@ -1697,6 +2258,31 @@ bbl_l2tp_ctrl_tunnels(int fd, uint32_t session_id __attribute__((unused)), json_ l2tp_server = l2tp_server->next; } + while(l2tp_client) { + CIRCLEQ_FOREACH(l2tp_tunnel, &l2tp_client->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), + "client-name", l2tp_client->name, + "server-address", format_ipv4_address(&l2tp_client->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_client->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, + "data-packets-rx", l2tp_tunnel->stats.data_rx, + "data-packets-tx", l2tp_tunnel->stats.data_tx); + json_array_append_new(tunnels, tunnel); + } + l2tp_client = l2tp_client->next; + } + root = json_pack("{ss si so}", "status", "ok", "code", 200, diff --git a/code/bngblaster/src/bbl_l2tp.h b/code/bngblaster/src/bbl_l2tp.h index f192c40f..6b282f83 100644 --- a/code/bngblaster/src/bbl_l2tp.h +++ b/code/bngblaster/src/bbl_l2tp.h @@ -91,6 +91,38 @@ typedef struct bbl_l2tp_server_ CIRCLEQ_HEAD(tunnel_, bbl_l2tp_tunnel_) tunnel_qhead; } bbl_l2tp_server_s; +/* L2TP Client Configuration (LAC) */ +typedef struct bbl_l2tp_client_ +{ + uint16_t group_id; /* l2tp-client-group-id: ties this entry to access interfaces */ + uint32_t server_ip; /* LNS address used for outer L2TP/UDP packets */ + uint32_t client_address; /* LAC address used for outer L2TP/UDP packets */ + uint16_t hello_interval; + uint16_t receive_window; + uint16_t max_retry; + uint16_t lcp_padding; + + bool data_control_priority; + bool data_length; + bool data_offset; + + uint8_t control_tos; + uint8_t data_control_tos; + + l2tp_congestion_mode_t congestion_mode; + + char *name; + char *secret; + char *network_interface; + char *calling_number; /* Optional ICRQ Calling Number (AVP 22) */ + char *called_number; /* Optional ICRQ Called Number (AVP 21) */ + + void *next; /* Pointer to next L2TP client configuration */ + + /* List of L2TP tunnel instances for the corresponding client */ + CIRCLEQ_HEAD(client_tunnel_, bbl_l2tp_tunnel_) tunnel_qhead; +} bbl_l2tp_client_s; + /* L2TP Session Key */ typedef struct l2tp_key_ { uint16_t tunnel_id; @@ -121,6 +153,7 @@ typedef struct bbl_l2tp_queue_ uint16_t packet_len; struct timespec last_tx_time; struct bbl_l2tp_tunnel_ *tunnel; + struct bbl_session_ *ppp_session; /* If set, start PPP when this ctrl pkt is acknowledged by the peer */ CIRCLEQ_ENTRY(bbl_l2tp_queue_) tunnel_tx_qnode; /* Tunnel TX queue (ctrl packets only) */ CIRCLEQ_ENTRY(bbl_l2tp_queue_) interface_tx_qnode; /* Interface TX queue */ } bbl_l2tp_queue_s; @@ -131,13 +164,17 @@ typedef struct bbl_l2tp_tunnel_ CIRCLEQ_ENTRY(bbl_l2tp_tunnel_) tunnel_qnode; CIRCLEQ_HEAD(session_, bbl_l2tp_session_) session_qhead; + CIRCLEQ_HEAD(pending_session_, bbl_session_) pending_session_qhead; CIRCLEQ_HEAD(txq_, bbl_l2tp_queue_) tx_qhead; /* Pointer to corresponding network interface */ bbl_network_interface_s *interface; - /* Pointer to L2TP server configuration */ + bool is_lac; + /* Pointer to L2TP server configuration (LNS mode) */ bbl_l2tp_server_s *server; + /* Pointer to L2TP client configuration (LAC mode) */ + bbl_l2tp_client_s *client; /* RFC5515 CSURQ */ uint16_t *csurq_requests; @@ -248,6 +285,7 @@ typedef struct bbl_l2tp_session_ uint16_t proxy_auth_challenge_len; uint16_t proxy_auth_response_len; + uint8_t lcp_state; uint8_t ipcp_state; uint8_t ip6cp_state; @@ -274,6 +312,9 @@ typedef struct bbl_l2tp_session_ char *peer_aci; } bbl_l2tp_session_s; +const char* +l2tp_tunnel_hostname(bbl_l2tp_tunnel_s *l2tp_tunnel); + const char* l2tp_message_string(l2tp_message_t type); @@ -301,6 +342,9 @@ bbl_l2tp_handler_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *e void bbl_l2tp_stop_all_tunnel(); +bbl_l2tp_tunnel_s * +bbl_l2tp_client_connect(bbl_l2tp_client_s *l2tp_client); + json_t * l2tp_session_json(bbl_l2tp_session_s *l2tp_session); @@ -319,4 +363,13 @@ bbl_l2tp_ctrl_session_terminate(int fd, uint32_t session_id, json_t *arguments); int bbl_l2tp_ctrl_tunnels(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments __attribute__((unused))); +bbl_l2tp_tunnel_s * +bbl_l2tp_client_session_get_tunnel(bbl_session_s *session); + +void +bbl_l2tp_send_data(bbl_l2tp_session_s *l2tp_session, uint16_t protocol, void *next); + +void +bbl_l2tp_client_session_connect(bbl_l2tp_tunnel_s *l2tp_tunnel, bbl_session_s *session); + #endif diff --git a/code/bngblaster/src/bbl_l2tp_avp.c b/code/bngblaster/src/bbl_l2tp_avp.c index deaf925b..59df2879 100644 --- a/code/bngblaster/src/bbl_l2tp_avp.c +++ b/code/bngblaster/src/bbl_l2tp_avp.c @@ -7,6 +7,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ #include "bbl.h" +#include "bbl_l2tp.h" #include "bbl_l2tp_avp.h" #include #include @@ -191,12 +192,12 @@ bbl_l2tp_avp_unhide(bbl_l2tp_tunnel_s *l2tp_tunnel, bbl_l2tp_avp_t *avp, uint8_t if(!value) { LOG(L2TP, "L2TP Error (%s) Invalid hidden AVP\n", - l2tp_tunnel->server->host_name); + l2tp_tunnel_hostname(l2tp_tunnel)); return false; } if(!(random_vector && l2tp_tunnel->server->secret)) { LOG(L2TP, "L2TP Error (%s) Missing random-vector or secret\n", - l2tp_tunnel->server->host_name); + l2tp_tunnel_hostname(l2tp_tunnel)); return false; } @@ -216,7 +217,7 @@ bbl_l2tp_avp_unhide(bbl_l2tp_tunnel_s *l2tp_tunnel, bbl_l2tp_avp_t *avp, uint8_t if(len + 2 > avp->len) { LOG(L2TP, "L2TP Error (%s) Decrypted length %u > AVP length %u\n", - l2tp_tunnel->server->host_name, len, avp->len); + l2tp_tunnel_hostname(l2tp_tunnel), len, avp->len); return false; } @@ -255,7 +256,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), avp.type, l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -269,7 +270,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -279,7 +280,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -289,7 +290,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -299,7 +300,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -330,7 +331,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -340,7 +341,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -350,7 +351,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -360,7 +361,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -378,7 +379,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -403,7 +404,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -436,13 +437,13 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), 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_tunnel_hostname(l2tp_tunnel), avp.type, l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); } @@ -467,7 +468,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb 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_tunnel_hostname(l2tp_tunnel), avp.type, l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -477,7 +478,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb } 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_tunnel_hostname(l2tp_tunnel), avp.vendor, l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -485,7 +486,7 @@ bbl_l2tp_avp_decode_session(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel, bb } } else { LOG(L2TP, "L2TP (%s) Failed to decdoe session attributes in %s from %s\n", - l2tp_tunnel->server->host_name, + l2tp_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -509,7 +510,7 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) 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_tunnel_hostname(l2tp_tunnel), avp.type, l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -523,7 +524,7 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -532,7 +533,7 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -542,7 +543,7 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -552,7 +553,7 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -576,7 +577,7 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -586,7 +587,7 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -604,7 +605,7 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -622,13 +623,13 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) 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_tunnel_hostname(l2tp_tunnel), 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_tunnel_hostname(l2tp_tunnel), avp.type, l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); } @@ -637,13 +638,14 @@ bbl_l2tp_avp_decode_tunnel(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) } 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)); + l2tp_tunnel_hostname(l2tp_tunnel), 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)); + l2tp_tunnel_hostname(l2tp_tunnel), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; } } @@ -673,7 +675,7 @@ bbl_l2tp_avp_decode_csun(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) 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_tunnel_hostname(l2tp_tunnel), l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; @@ -691,13 +693,13 @@ bbl_l2tp_avp_decode_csun(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) 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_tunnel_hostname(l2tp_tunnel), 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_tunnel_hostname(l2tp_tunnel), avp.type, l2tp_message_string(l2tp->type), format_ipv4_address(&l2tp_tunnel->peer_ip)); } @@ -706,13 +708,14 @@ bbl_l2tp_avp_decode_csun(bbl_l2tp_s *l2tp, bbl_l2tp_tunnel_s *l2tp_tunnel) } 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)); + l2tp_tunnel_hostname(l2tp_tunnel), 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)); + l2tp_tunnel_hostname(l2tp_tunnel), format_ipv4_address(&l2tp_tunnel->peer_ip)); return false; } } @@ -748,6 +751,83 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_s *l2tp_tunnel, bbl_l2tp_session_ bbl_l2tp_avp_encode(&buf, len, &avp); switch (l2tp_type) { + case L2TP_MESSAGE_SCCRQ: + { + /* 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; + bbl_l2tp_avp_encode(&buf, len, &avp); + /* Bearer Capabilities */ + v32 = 3; /* A + 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_FIRMWARE_REVISION; + 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_hostname(l2tp_tunnel)); + avp.value_type = L2TP_AVP_VALUE_BYTES; + avp.value = (void*)l2tp_tunnel_hostname(l2tp_tunnel); + bbl_l2tp_avp_encode(&buf, len, &avp); + /* Vendor Name */ + avp.m = false; + avp.type = L2TP_AVP_VENDOR_NAME; + avp.len = sizeof("bngblaster") - 1; + avp.value_type = L2TP_AVP_VALUE_BYTES; + 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); + /* Receive Window Size */ + v16 = 4; + if(l2tp_tunnel->client->receive_window) { + v16 = l2tp_tunnel->client->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*)&v16; + bbl_l2tp_avp_encode(&buf, len, &avp); + /* Challenge */ + if(l2tp_tunnel->challenge_len) { + 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); + } + break; + } case L2TP_MESSAGE_SCCRP: /* Protocol Version */ v16 = 256; @@ -784,9 +864,9 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_s *l2tp_tunnel, bbl_l2tp_session_ /* Host Name */ avp.m = true; avp.type = L2TP_AVP_HOST_NAME; - avp.len = strlen(l2tp_tunnel->server->host_name); + avp.len = strlen(l2tp_tunnel_hostname(l2tp_tunnel)); avp.value_type = L2TP_AVP_VALUE_BYTES; - avp.value = (void*)(l2tp_tunnel->server->host_name); + avp.value = (void*)l2tp_tunnel_hostname(l2tp_tunnel); bbl_l2tp_avp_encode(&buf, len, &avp); /* Vendor Name */ avp.m = false; @@ -832,6 +912,17 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_s *l2tp_tunnel, bbl_l2tp_session_ bbl_l2tp_avp_encode(&buf, len, &avp); } break; + case L2TP_MESSAGE_SCCCN: + /* Challenge Response */ + if(l2tp_tunnel->challenge_response_len) { + 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); + } + break; case L2TP_MESSAGE_STOPCCN: /* Assigned Tunnel ID */ avp.m = true; @@ -846,6 +937,51 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_s *l2tp_tunnel, bbl_l2tp_session_ l2tp_tunnel->error_code, l2tp_tunnel->error_message); break; + case L2TP_MESSAGE_ICRQ: + /* 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); + /* Call Serial Number */ + v32 = l2tp_session->key.session_id; + avp.m = true; + avp.type = L2TP_AVP_CALL_SERIAL_NUMBER; + avp.len = 4; + avp.value_type = L2TP_AVP_VALUE_UINT32; + avp.value = (void*)&v32; + bbl_l2tp_avp_encode(&buf, len, &avp); + /* Bearer Type */ + v32 = 2; /* Analog */ + avp.m = true; + avp.type = L2TP_AVP_BEARER_TYPE; + avp.len = 4; + avp.value_type = L2TP_AVP_VALUE_UINT32; + avp.value = (void*)&v32; + bbl_l2tp_avp_encode(&buf, len, &avp); + /* Calling Number (optional, AVP 22) */ + if(l2tp_tunnel->client && l2tp_tunnel->client->calling_number) { + avp.m = false; + avp.type = L2TP_AVP_CALLING_NUMBER; + avp.len = strlen(l2tp_tunnel->client->calling_number); + avp.value_type = L2TP_AVP_VALUE_BYTES; + avp.value = (void*)l2tp_tunnel->client->calling_number; + bbl_l2tp_avp_encode(&buf, len, &avp); + } + /* Called Number (optional, AVP 21) */ + if(l2tp_tunnel->client && l2tp_tunnel->client->called_number) { + avp.m = false; + avp.type = L2TP_AVP_CALLED_NUMBER; + avp.len = strlen(l2tp_tunnel->client->called_number); + avp.value_type = L2TP_AVP_VALUE_BYTES; + avp.value = (void*)l2tp_tunnel->client->called_number; + bbl_l2tp_avp_encode(&buf, len, &avp); + } + } + break; case L2TP_MESSAGE_ICRP: /* Assigned Session ID */ if(l2tp_session) { @@ -857,6 +993,26 @@ bbl_l2tp_avp_encode_attributes(bbl_l2tp_tunnel_s *l2tp_tunnel, bbl_l2tp_session_ bbl_l2tp_avp_encode(&buf, len, &avp); } break; + case L2TP_MESSAGE_ICCN: + /* TX Connect Speed */ + if(l2tp_session) { + v32 = 100000000; + avp.m = true; + avp.type = L2TP_AVP_TX_CONNECT_SPEED; + avp.len = 4; + avp.value_type = L2TP_AVP_VALUE_UINT32; + avp.value = (void*)&v32; + bbl_l2tp_avp_encode(&buf, len, &avp); + /* Framing Type */ + v32 = 1; /* Synchronous */ + avp.m = true; + avp.type = L2TP_AVP_FRAMING_TYPE; + avp.len = 4; + avp.value_type = L2TP_AVP_VALUE_UINT32; + avp.value = (void*)&v32; + bbl_l2tp_avp_encode(&buf, len, &avp); + } + break; case L2TP_MESSAGE_CDN: /* Assigned Session ID */ if(l2tp_session) { diff --git a/code/bngblaster/src/bbl_network.c b/code/bngblaster/src/bbl_network.c index 5e3bbbc7..e6cea135 100644 --- a/code/bngblaster/src/bbl_network.c +++ b/code/bngblaster/src/bbl_network.c @@ -67,7 +67,7 @@ bbl_network_interfaces_add() LOG(ERROR, "Failed to add network interface %s (duplicate)\n", ifname); return false; } - if(interface->access && network_config->vlan == 0) { + if(interface->access && interface->access->access_type != ACCESS_TYPE_PPPOL2TP && network_config->vlan == 0) { LOG(ERROR, "Failed to add network interface %s (untagged not allowed on access interfaces)\n", ifname); return false; } diff --git a/code/bngblaster/src/bbl_protocols.h b/code/bngblaster/src/bbl_protocols.h index 31c3e975..7e46d4e9 100644 --- a/code/bngblaster/src/bbl_protocols.h +++ b/code/bngblaster/src/bbl_protocols.h @@ -324,6 +324,7 @@ static const uint8_t all_dr_routers_mac[ETH_ADDR_LEN] = {0x01, 0x00, 0x5e, 0x00, typedef enum protocol_error_ { PROTOCOL_SUCCESS = 0, + PROTOCOL_QUEUED, /* packet enqueued internally; caller must not transmit write_buf */ SEND_ERROR, DECODE_ERROR, ENCODE_ERROR, diff --git a/code/bngblaster/src/bbl_session.c b/code/bngblaster/src/bbl_session.c index 768ae010..9acadc42 100644 --- a/code/bngblaster/src/bbl_session.c +++ b/code/bngblaster/src/bbl_session.c @@ -39,6 +39,7 @@ session_state_string(uint32_t state) case BBL_PPP_LINK: return "PPP Link"; case BBL_PPP_AUTH: return "PPP Authentication"; case BBL_PPP_NETWORK: return "PPP Network"; + case BBL_L2TP_WAIT: return "L2TP Wait"; case BBL_ESTABLISHED: return "Established"; case BBL_PPP_TERMINATING: return "PPP Terminating"; case BBL_TERMINATING: return "Terminating"; @@ -633,7 +634,8 @@ bbl_session_update_state(bbl_session_s *session, session_state_t new_state) } /* Reset all states */ - if(session->access_type == ACCESS_TYPE_PPPOE) { + if(session->access_type == ACCESS_TYPE_PPPOE || + session->access_type == ACCESS_TYPE_PPPOL2TP) { session->lcp_state = BBL_PPP_CLOSED; if(session->ipcp_state > BBL_PPP_DISABLED) { session->ipcp_state = BBL_PPP_CLOSED; @@ -690,7 +692,27 @@ bbl_session_clear(bbl_session_s *session) { session_state_t new_state = BBL_TERMINATED; - if(session->access_type == ACCESS_TYPE_PPPOE) { + if(session->access_type == ACCESS_TYPE_PPPOL2TP) { + switch(session->session_state) { + case BBL_IDLE: + bbl_session_update_state(session, BBL_TERMINATED); + break; + case BBL_PPP_TERMINATING: + case BBL_TERMINATING: + case BBL_TERMINATED: + break; + default: + bbl_session_update_state(session, BBL_PPP_TERMINATING); + if(session->l2tp_session) { + bbl_l2tp_send(session->l2tp_session->tunnel, + session->l2tp_session, L2TP_MESSAGE_CDN); + bbl_l2tp_session_delete(session->l2tp_session); + } else { + bbl_session_update_state(session, BBL_TERMINATED); + } + return; + } + } else if(session->access_type == ACCESS_TYPE_PPPOE) { switch(session->session_state) { case BBL_IDLE: case BBL_PPPOE_INIT: @@ -1041,6 +1063,24 @@ bbl_sessions_init() if(g_ctx->config.pppoe_host_uniq) { session->pppoe_host_uniq = htobe64(i); } + } else if(session->access_type == ACCESS_TYPE_PPPOL2TP) { + session->mru = access_config->ppp_mru; + session->magic_number = htobe32(i); + session->lcp_state = BBL_PPP_CLOSED; + if(access_config->ipv4_enable && access_config->ipcp_enable) { + session->ipcp_state = BBL_PPP_CLOSED; + session->ipcp_request_dns1 = g_ctx->config.ipcp_request_dns1; + session->ipcp_request_dns2 = g_ctx->config.ipcp_request_dns2; + session->endpoint.ipv4 = ENDPOINT_ENABLED; + } + if(access_config->ipv6_enable && access_config->ip6cp_enable) { + session->ip6cp_state = BBL_PPP_CLOSED; + session->endpoint.ipv6 = ENDPOINT_ENABLED; + if(access_config->dhcpv6_enable) { + session->dhcpv6_state = BBL_DHCP_INIT; + session->endpoint.ipv6pd = ENDPOINT_ENABLED; + } + } } else if(session->access_type == ACCESS_TYPE_IPOE) { if(access_config->ipv4_enable) { session->endpoint.ipv4 = ENDPOINT_ENABLED; @@ -1086,6 +1126,8 @@ bbl_sessions_init() g_ctx->sessions++; if(session->access_type == ACCESS_TYPE_PPPOE) { g_ctx->sessions_pppoe++; + } else if(session->access_type == ACCESS_TYPE_PPPOL2TP) { + g_ctx->sessions_pppol2tp++; } else { g_ctx->sessions_ipoe++; } @@ -1603,12 +1645,13 @@ int bbl_session_ctrl_counters(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments __attribute__((unused))) { int result = 0; - json_t *root = json_pack("{ss si s{si si si si si si si si si si si si si si si sf sf sf sf si si si si}}", + json_t *root = json_pack("{ss si s{si si si si si si si si si si si si si si si si sf sf sf sf si si si si}}", "status", "ok", "code", 200, "session-counters", "sessions", g_ctx->config.sessions, "sessions-pppoe", g_ctx->sessions_pppoe, + "sessions-pppol2tp", g_ctx->sessions_pppol2tp, "sessions-ipoe", g_ctx->sessions_ipoe, "sessions-established", g_ctx->sessions_established, "sessions-established-max", g_ctx->sessions_established_max, diff --git a/code/bngblaster/src/bbl_session.h b/code/bngblaster/src/bbl_session.h index 953e03e1..e64ab171 100644 --- a/code/bngblaster/src/bbl_session.h +++ b/code/bngblaster/src/bbl_session.h @@ -34,6 +34,7 @@ typedef struct bbl_session_ CIRCLEQ_ENTRY(bbl_session_) session_tx_qnode; CIRCLEQ_ENTRY(bbl_session_) session_network_tx_qnode; CIRCLEQ_ENTRY(bbl_session_) session_a10nsp_tx_qnode; + CIRCLEQ_ENTRY(bbl_session_) session_l2tp_qnode; bbl_access_config_s *access_config; bbl_access_interface_s *access_interface; /* where this session is attached to */ diff --git a/code/bngblaster/src/bbl_stats.c b/code/bngblaster/src/bbl_stats.c index f2df23c4..c3049e9a 100644 --- a/code/bngblaster/src/bbl_stats.c +++ b/code/bngblaster/src/bbl_stats.c @@ -412,7 +412,8 @@ bbl_stats_stdout(bbl_stats_s *stats) { printf("\n==============================================================================\n"); printf("Test Duration: %lus\n", test_duration()); if(g_ctx->sessions) { - printf("Sessions PPPoE: %u IPoE: %u\n", g_ctx->sessions_pppoe, g_ctx->sessions_ipoe); + printf("Sessions PPPoE: %u PPPoL2TP: %u IPoE: %u\n", g_ctx->sessions_pppoe, g_ctx->sessions_pppol2tp, + g_ctx->sessions_ipoe); printf("Sessions established: %u/%u\n", g_ctx->sessions_established_max, g_ctx->sessions); printf("DHCPv6 sessions established: %u\n", g_ctx->dhcpv6_established_max); printf("Setup Time: %u ms\n", g_ctx->stats.setup_time); @@ -427,8 +428,8 @@ bbl_stats_stdout(bbl_stats_s *stats) { printf(" Flows: %10lu\n", dict_count(g_ctx->li_flow_dict)); printf(" RX Packets: %10lu\n", stats->li_rx); } - if(g_ctx->config.l2tp_server) { - printf("\nL2TP LNS Statistics:"); + if(g_ctx->config.l2tp_server || g_ctx->config.l2tp_client) { + printf("\nL2TP LNS/LAC Statistics:"); printf("\n------------------------------------------------------------------------------\n"); printf(" Tunnels: %10u\n", g_ctx->l2tp_tunnels_max); printf(" Established: %10u\n", g_ctx->l2tp_tunnels_established_max); @@ -823,6 +824,7 @@ bbl_stats_json(bbl_stats_s * stats) if(g_ctx->sessions) { json_object_set_new(jobj, "sessions", json_integer(g_ctx->config.sessions)); json_object_set_new(jobj, "sessions-pppoe", json_integer(g_ctx->sessions_pppoe)); + json_object_set_new(jobj, "sessions-pppol2tp", json_integer(g_ctx->sessions_pppol2tp)); json_object_set_new(jobj, "sessions-ipoe", json_integer(g_ctx->sessions_ipoe)); json_object_set_new(jobj, "sessions-established", json_integer(g_ctx->sessions_established_max)); json_object_set_new(jobj, "sessions-flapped", json_integer(g_ctx->sessions_flapped)); @@ -840,7 +842,7 @@ bbl_stats_json(bbl_stats_s * stats) json_object_set_new(jobj_sub, "rx-packets", json_integer(stats->li_rx)); json_object_set_new(jobj, "li-statistics", jobj_sub); } - if(g_ctx->config.l2tp_server) { + if(g_ctx->config.l2tp_server || g_ctx->config.l2tp_client) { jobj_sub = json_object(); json_object_set_new(jobj_sub, "tunnels", json_integer(g_ctx->l2tp_tunnels_max)); json_object_set_new(jobj_sub, "tunnels-established", json_integer(g_ctx->l2tp_tunnels_established_max)); diff --git a/code/bngblaster/src/bbl_stream.c b/code/bngblaster/src/bbl_stream.c index 2b3166a7..f34a9adb 100644 --- a/code/bngblaster/src/bbl_stream.c +++ b/code/bngblaster/src/bbl_stream.c @@ -854,6 +854,153 @@ bbl_stream_build_network_packet(bbl_stream_s *stream) return true; } +/* Build an upstream L2TP data stream packet (LAC -> LNS). + * Used for ACCESS_TYPE_PPPOL2TP sessions: the LAC is the sender, + * so the outer IP source is the LAC's network interface address and + * the inner PPP payload flows from the client toward the LNS. */ +static bool +bbl_stream_build_pppol2tp_packet(bbl_stream_s *stream) +{ + bbl_session_s *session = stream->session; + bbl_stream_config_s *config = stream->config; + + bbl_l2tp_session_s *l2tp_session = session->l2tp_session; + bbl_l2tp_tunnel_s *l2tp_tunnel = l2tp_session->tunnel; + bbl_network_interface_s *network_interface = l2tp_tunnel->interface; + + uint16_t buf_len = 0; + uint16_t tx_len = 0; + + bbl_ethernet_header_s eth = {0}; + bbl_ipv4_s l2tp_ipv4 = {0}; + bbl_udp_s l2tp_udp = {0}; + bbl_l2tp_s l2tp = {0}; + bbl_ipv4_s ipv4 = {0}; + bbl_ipv6_s ipv6 = {0}; + bbl_udp_s udp = {0}; + bbl_bbl_s bbl = {0}; + + eth.dst = network_interface->gateway_mac; + eth.src = network_interface->mac; + eth.vlan_outer = network_interface->vlan; + eth.vlan_inner = 0; + eth.type = ETH_TYPE_IPV4; + eth.next = &l2tp_ipv4; + l2tp_ipv4.src = network_interface->ip.address; + l2tp_ipv4.dst = l2tp_tunnel->peer_ip; + l2tp_ipv4.ttl = config->ttl; + l2tp_ipv4.tos = config->priority; + l2tp_ipv4.protocol = PROTOCOL_IPV4_UDP; + l2tp_ipv4.next = &l2tp_udp; + l2tp_udp.src = L2TP_UDP_PORT; + l2tp_udp.dst = L2TP_UDP_PORT; + l2tp_udp.protocol = UDP_PROTOCOL_L2TP; + 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.with_length = l2tp_tunnel->client->data_length; + l2tp.with_offset = l2tp_tunnel->client->data_offset; + udp.src = config->src_port; + udp.dst = config->dst_port; + udp.protocol = UDP_PROTOCOL_BBL; + udp.next = &bbl; + bbl.type = BBL_TYPE_UNICAST; + bbl.sub_type = stream->sub_type; + bbl.session_id = session->session_id; + bbl.ifindex = session->vlan_key.ifindex; + bbl.outer_vlan_id = session->vlan_key.outer_vlan_id; + bbl.inner_vlan_id = session->vlan_key.inner_vlan_id; + bbl.flow_id = stream->flow_id; + bbl.tos = config->priority; + bbl.direction = BBL_DIRECTION_UP; + + switch(stream->sub_type) { + case BBL_SUB_TYPE_IPV4: + l2tp.protocol = PROTOCOL_IPV4; + l2tp.next = &ipv4; + if(stream->config->ipv4_access_src_address) { + ipv4.src = stream->config->ipv4_access_src_address; + } else { + ipv4.src = session->ip_address; + } + if(stream->config->ipv4_destination_address) { + ipv4.dst = stream->config->ipv4_destination_address; + } else if(stream->config->ipv4_network_address) { + ipv4.dst = stream->config->ipv4_network_address; + } else { + ipv4.dst = MOCK_IP_LOCAL; + } + if(config->ipv4_df) { + ipv4.offset = IPV4_DF; + } + ipv4.ttl = config->ttl; + ipv4.tos = config->priority; + if(stream->tcp) { + ipv4.protocol = PROTOCOL_IPV4_TCP; + } else { + ipv4.protocol = PROTOCOL_IPV4_UDP; + } + ipv4.next = &udp; + if(config->length > 76) { + bbl.padding = config->length - 76; + } + stream->ipv4_src = ipv4.src; + stream->ipv4_dst = ipv4.dst; + break; + case BBL_SUB_TYPE_IPV6: + case BBL_SUB_TYPE_IPV6PD: + l2tp.protocol = PROTOCOL_IPV6; + l2tp.next = &ipv6; + if(*(uint64_t*)stream->config->ipv6_access_src_address) { + ipv6.src = stream->config->ipv6_access_src_address; + } else if(stream->sub_type == BBL_SUB_TYPE_IPV6) { + ipv6.src = session->ipv6_address; + } else { + ipv6.src = session->delegated_ipv6_address; + } + if(*(uint64_t*)stream->config->ipv6_destination_address) { + ipv6.dst = stream->config->ipv6_destination_address; + } else if(*(uint64_t*)stream->config->ipv6_network_address) { + ipv6.dst = stream->config->ipv6_network_address; + } else { + ipv6.dst = (void*)mock_ipv6_local; + } + ipv6.ttl = config->ttl; + ipv6.tos = config->priority; + if(stream->tcp) { + ipv6.protocol = IPV6_NEXT_HEADER_TCP; + } else { + ipv6.protocol = IPV6_NEXT_HEADER_UDP; + } + ipv6.next = &udp; + if(config->length > 96) { + bbl.padding = config->length - 96; + } + stream->ipv6_src = ipv6.src; + stream->ipv6_dst = ipv6.dst; + break; + default: + return false; + } + + buf_len = config->length + BBL_MAX_STREAM_OVERHEAD; + if(buf_len < 256) buf_len = 256; + stream->tx_buf = malloc(buf_len); + stream->tx_bbl_hdr_len = bbl.padding+BBL_HEADER_LEN; + if(encode_ethernet(stream->tx_buf, &tx_len, ð) != PROTOCOL_SUCCESS) { + free(stream->tx_buf); + stream->tx_buf = NULL; + return false; + } + stream->tx_len = tx_len; + return true; +} + +/* Build a downstream L2TP data stream packet (LNS -> LAC -> client). + * Used for ACCESS_TYPE_PPPOE sessions that are tunnelled via an LNS: + * the LNS is the sender, so the outer IP source is the LNS server address + * and the inner PPP payload flows from the LNS toward the client. */ static bool bbl_stream_build_l2tp_packet(bbl_stream_s *stream) { @@ -1029,6 +1176,10 @@ bbl_stream_build_packet(bbl_stream_s *stream) return bbl_stream_build_network_packet(stream); } } + } else if(stream->session->access_type == ACCESS_TYPE_PPPOL2TP) { + if(stream->session->l2tp_session && stream->direction == BBL_DIRECTION_UP) { + return bbl_stream_build_pppol2tp_packet(stream); + } } } return false; @@ -1805,16 +1956,18 @@ bbl_stream_session_add(bbl_stream_config_s *config, bbl_session_s *session) if(config->direction & BBL_DIRECTION_UP) { if(config->type == BBL_SUB_TYPE_IPV4) { - if(!((network_interface && network_interface->ip.address) || - config->ipv4_destination_address || + if(!(session->access_config->access_type == ACCESS_TYPE_PPPOL2TP || + (network_interface && network_interface->ip.address) || + config->ipv4_destination_address || config->ipv4_network_address || a10nsp_interface)) { LOG(ERROR, "Failed to add stream %s (upstream) because of missing IPv4 destination address\n", config->name); return false; } } else { - if(!((network_interface && *(uint64_t*)network_interface->ip6.address) || - *(uint64_t*)config->ipv6_destination_address || + if(!(session->access_config->access_type == ACCESS_TYPE_PPPOL2TP || + (network_interface && *(uint64_t*)network_interface->ip6.address) || + *(uint64_t*)config->ipv6_destination_address || *(uint64_t*)config->ipv6_network_address || a10nsp_interface)) { LOG(ERROR, "Failed to add stream %s (upstream) because of missing IPv6 destination address\n", config->name); @@ -1851,8 +2004,14 @@ bbl_stream_session_add(bbl_stream_config_s *config, bbl_session_s *session) if(stream_up->config->raw_tcp) { stream_up->tcp = true; } - stream_up->tx_access_interface = access_interface; - stream_up->tx_interface = access_interface->interface; + if(session->access_type == ACCESS_TYPE_PPPOL2TP) { + /* PPPoL2TP upstream goes through the L2TP tunnel's network interface */ + stream_up->tx_network_interface = network_interface; + stream_up->tx_interface = network_interface->interface; + } else { + stream_up->tx_access_interface = access_interface; + stream_up->tx_interface = access_interface->interface; + } if(session->streams.tail) { session->streams.tail->session_next = stream_up; } else { diff --git a/code/bngblaster/src/bbl_tcp.c b/code/bngblaster/src/bbl_tcp.c index cf7bb57a..2a13060c 100644 --- a/code/bngblaster/src/bbl_tcp.c +++ b/code/bngblaster/src/bbl_tcp.c @@ -886,15 +886,13 @@ bbl_tcp_ipv4_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth, /** * bbl_tcp_ipv4_rx_session * - * @param eth ethernet packet received * @param ipv4 ipv4 header received * @param session receiving session */ void -bbl_tcp_ipv4_rx_session(bbl_session_s *session, bbl_ethernet_header_s *eth, bbl_ipv4_s *ipv4) +bbl_tcp_ipv4_rx_session(bbl_session_s *session, bbl_ipv4_s *ipv4) { struct pbuf *pbuf; - UNUSED(eth); if(!(g_ctx->tcp && session->netif.state)) { /* TCP not enabled! */ @@ -1000,15 +998,13 @@ bbl_tcp_ipv6_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth, /** * bbl_tcp_ipv6_rx_session * - * @param eth ethernet packet received * @param ipv6 ipv6 header received * @param session receiving session */ void -bbl_tcp_ipv6_rx_session(bbl_session_s *session, bbl_ethernet_header_s *eth, bbl_ipv6_s *ipv6) +bbl_tcp_ipv6_rx_session(bbl_session_s *session, bbl_ipv6_s *ipv6) { struct pbuf *pbuf; - UNUSED(eth); if(!(g_ctx->tcp && session->netif.state)) { /* TCP not enabled! */ diff --git a/code/bngblaster/src/bbl_tcp.h b/code/bngblaster/src/bbl_tcp.h index 25285212..203c5896 100644 --- a/code/bngblaster/src/bbl_tcp.h +++ b/code/bngblaster/src/bbl_tcp.h @@ -118,13 +118,13 @@ void bbl_tcp_ipv4_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth, bbl_ipv4_s *ipv4); void -bbl_tcp_ipv4_rx_session(bbl_session_s *session, bbl_ethernet_header_s *eth, bbl_ipv4_s *ipv4); +bbl_tcp_ipv4_rx_session(bbl_session_s *session, bbl_ipv4_s *ipv4); void bbl_tcp_ipv6_rx(bbl_network_interface_s *interface, bbl_ethernet_header_s *eth, bbl_ipv6_s *ipv6); void -bbl_tcp_ipv6_rx_session(bbl_session_s *session, bbl_ethernet_header_s *eth, bbl_ipv6_s *ipv6); +bbl_tcp_ipv6_rx_session(bbl_session_s *session, bbl_ipv6_s *ipv6); bool bbl_tcp_send(bbl_tcp_ctx_s *tcpc, uint8_t *buf, uint32_t len); diff --git a/code/bngblaster/src/bbl_tx.c b/code/bngblaster/src/bbl_tx.c index 23015945..e9b2b397 100644 --- a/code/bngblaster/src/bbl_tx.c +++ b/code/bngblaster/src/bbl_tx.c @@ -12,6 +12,124 @@ #include "bbl_dhcp.h" #include "bbl_dhcpv6.h" +/** + * bbl_ppp_tx + * + * Generic PPP transmit helper — the TX counterpart of bbl_ppp_rx(). + * Wraps a PPP payload in the framing appropriate for this session type + * and queues or writes the resulting packet. + * + * Currently handles ACCESS_TYPE_PPPOE: builds an Ethernet + PPPoE session + * header and writes the complete frame to session->write_buf via + * encode_ethernet(). Support for additional transports (e.g. PPPoL2TP) + * will be added here without touching the individual protocol encoders. + * + * @param session the PPP session + * @param protocol PPP protocol number (PROTOCOL_LCP, PROTOCOL_IPCP, ...) + * @param payload pointer to the protocol-specific payload struct + */ +static protocol_error_t +bbl_ppp_tx(bbl_session_s *session, uint16_t protocol, void *payload) +{ + if(session->access_type == ACCESS_TYPE_PPPOL2TP) { + if(!session->l2tp_session) { + return EMPTY; + } + bbl_l2tp_send_data(session->l2tp_session, protocol, payload); + return PROTOCOL_QUEUED; + } + + bbl_ethernet_header_s eth = {0}; + bbl_pppoe_session_s pppoe = {0}; + + eth.dst = session->server_mac; + eth.src = session->client_mac; + eth.qinq = session->access_config->qinq; + eth.vlan_outer = session->vlan_key.outer_vlan_id; + eth.vlan_inner = session->vlan_key.inner_vlan_id; + eth.vlan_three = session->access_third_vlan; + eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; + eth.vlan_inner_priority = eth.vlan_outer_priority; + eth.type = ETH_TYPE_PPPOE_SESSION; + eth.next = &pppoe; + pppoe.session_id = session->pppoe_session_id; + pppoe.protocol = protocol; + pppoe.next = payload; + return encode_ethernet(session->write_buf, &session->write_idx, ð); +} + +/** + * bbl_ipoe_tx + * + * IPoE transmit helper — wraps an IP payload in Ethernet framing for IPoE + * sessions. The caller supplies the destination MAC, EtherType, VLAN + * priority, and the payload pointer (typically a bbl_ipv4_s / bbl_ipv6_s). + */ +static protocol_error_t +bbl_ipoe_tx(bbl_session_s *session, uint8_t *dst_mac, uint16_t eth_type, + uint8_t vlan_priority, void *payload) +{ + bbl_ethernet_header_s eth = {0}; + eth.dst = dst_mac; + eth.src = session->client_mac; + eth.qinq = session->access_config->qinq; + eth.vlan_outer = session->vlan_key.outer_vlan_id; + eth.vlan_inner = session->vlan_key.inner_vlan_id; + eth.vlan_three = session->access_third_vlan; + eth.vlan_outer_priority = vlan_priority; + eth.vlan_inner_priority = vlan_priority; + eth.type = eth_type; + eth.next = payload; + return encode_ethernet(session->write_buf, &session->write_idx, ð); +} + +/** + * bbl_pppoe_disc_tx + * + * PPPoE discovery transmit helper — wraps a bbl_pppoe_discovery_s payload + * in Ethernet framing (ETH_TYPE_PPPOE_DISCOVERY) for PADI / PADR / PADT. + */ +static protocol_error_t +bbl_pppoe_disc_tx(bbl_session_s *session, void *payload) +{ + bbl_ethernet_header_s eth = {0}; + eth.dst = session->server_mac; + eth.src = session->client_mac; + eth.qinq = session->access_config->qinq; + eth.vlan_outer = session->vlan_key.outer_vlan_id; + eth.vlan_inner = session->vlan_key.inner_vlan_id; + eth.vlan_three = session->access_third_vlan; + eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; + eth.vlan_inner_priority = eth.vlan_outer_priority; + eth.type = ETH_TYPE_PPPOE_DISCOVERY; + eth.next = payload; + return encode_ethernet(session->write_buf, &session->write_idx, ð); +} + +/** + * bbl_session_tx + * + * Transport-agnostic IP transmit helper — dispatches to bbl_ppp_tx() for + * ACCESS_TYPE_PPPOE and to bbl_ipoe_tx() (with ipoe_vlan_priority) for + * ACCESS_TYPE_IPOE. + * + * @param ppp_protocol PPP protocol number: PROTOCOL_IPV4 or PROTOCOL_IPV6. + * Also determines the EtherType for the IPoE path. + * @param dst_mac_ipoe Destination MAC used on the IPoE path (ignored for PPPoE). + */ +static protocol_error_t +bbl_session_tx(bbl_session_s *session, uint8_t *dst_mac_ipoe, + uint16_t ppp_protocol, void *ip_payload) +{ + if(session->access_type == ACCESS_TYPE_PPPOE || + session->access_type == ACCESS_TYPE_PPPOL2TP) { + return bbl_ppp_tx(session, ppp_protocol, ip_payload); + } + uint16_t eth_type = (ppp_protocol == PROTOCOL_IPV6) ? ETH_TYPE_IPV6 : ETH_TYPE_IPV4; + return bbl_ipoe_tx(session, dst_mac_ipoe, eth_type, + g_ctx->config.ipoe_vlan_priority, ip_payload); +} + void bbl_tx_igmp_timeout(timer_s *timer) { @@ -63,11 +181,8 @@ bbl_tx_encode_packet_igmp(bbl_session_s *session) { bbl_access_interface_s *access_interface = session->access_interface; - bbl_ethernet_header_s eth = {0}; - bbl_pppoe_session_s pppoe = {0}; bbl_ipv4_s ipv4 = {0}; bbl_igmp_s igmp = {0}; - uint8_t mac[ETH_ADDR_LEN]; bbl_igmp_group_record_s *gr; int i, i2; @@ -80,13 +195,6 @@ bbl_tx_encode_packet_igmp(bbl_session_s *session) struct timespec timestamp; clock_gettime(CLOCK_MONOTONIC, ×tamp); - eth.dst = session->server_mac; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - if(session->access_type == ACCESS_TYPE_PPPOE) { /* Check session and IPCP (PPP IPv4) state to prevent sending IGMP request * after session or IPCP has closed. */ @@ -94,25 +202,13 @@ bbl_tx_encode_packet_igmp(bbl_session_s *session) session->send_requests &= ~BBL_SEND_IGMP; return WRONG_PROTOCOL_STATE; } - eth.type = ETH_TYPE_PPPOE_SESSION; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.next = &pppoe; - pppoe.session_id = session->pppoe_session_id; - pppoe.protocol = PROTOCOL_IPV4; - pppoe.next = &ipv4; } else { /* IPoE */ if(session->session_state != BBL_ESTABLISHED) { session->send_requests &= ~BBL_SEND_IGMP; return WRONG_PROTOCOL_STATE; } - ipv4_multicast_mac(IPV4_MC_IGMP, mac); - eth.dst = mac; - eth.type = ETH_TYPE_IPV4; - eth.vlan_outer_priority = g_ctx->config.ipoe_vlan_priority; - eth.next = &ipv4; } - eth.vlan_inner_priority = eth.vlan_outer_priority; ipv4.dst = IPV4_MC_IGMP; ipv4.src = session->ip_address; ipv4.ttl = 1; @@ -193,11 +289,6 @@ bbl_tx_encode_packet_igmp(bbl_session_s *session) } else { ipv4.dst = group->group; igmp.group = group->group; - if(session->access_type != ACCESS_TYPE_PPPOE) { - /* IPoE */ - ipv4_multicast_mac(group->group, mac); - eth.dst = mac; - } if(session->igmp_version == IGMP_VERSION_2) { igmp.version = IGMP_VERSION_2; if(group->state == IGMP_GROUP_LEAVING) { @@ -252,7 +343,9 @@ bbl_tx_encode_packet_igmp(bbl_session_s *session) session->stats.igmp_tx++; access_interface->stats.igmp_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + uint8_t mac[ETH_ADDR_LEN]; + ipv4_multicast_mac(ipv4.dst, mac); + return bbl_session_tx(session, mac, PROTOCOL_IPV4, &ipv4); } void @@ -273,26 +366,8 @@ bbl_tx_pap_timeout(timer_s *timer) static protocol_error_t bbl_tx_encode_packet_pap_request(bbl_session_s *session) { - bbl_access_interface_s *access_interface = session->access_interface; - - bbl_ethernet_header_s eth = {0}; - bbl_pppoe_session_s pppoe = {0}; bbl_pap_s pap = {0}; - eth.dst = session->server_mac; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.vlan_inner_priority = eth.vlan_outer_priority; - eth.type = ETH_TYPE_PPPOE_SESSION; - eth.next = &pppoe; - pppoe.session_id = session->pppoe_session_id; - pppoe.protocol = PROTOCOL_PAP; - pppoe.next = &pap; - pap.code = PAP_CODE_REQUEST; pap.identifier = 1; pap.username = session->username; @@ -303,8 +378,8 @@ bbl_tx_encode_packet_pap_request(bbl_session_s *session) timer_add(&g_ctx->timer_root, &session->timer_auth, "Authentication Timeout", g_ctx->config.authentication_timeout, 0, session, &bbl_tx_pap_timeout); - access_interface->stats.pap_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + session->access_interface->stats.pap_tx++; + return bbl_ppp_tx(session, PROTOCOL_PAP, &pap); } void @@ -325,27 +400,8 @@ bbl_tx_chap_timeout(timer_s *timer) static protocol_error_t bbl_tx_encode_packet_chap_response(bbl_session_s *session) { - bbl_access_interface_s *access_interface = session->access_interface; - - bbl_ethernet_header_s eth = {0}; - bbl_pppoe_session_s pppoe = {0}; bbl_chap_s chap = {0}; - access_interface->stats.chap_tx++; - - eth.dst = session->server_mac; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.vlan_inner_priority = eth.vlan_outer_priority; - eth.type = ETH_TYPE_PPPOE_SESSION; - eth.next = &pppoe; - pppoe.session_id = session->pppoe_session_id; - pppoe.protocol = PROTOCOL_CHAP; - pppoe.next = &chap; chap.code = CHAP_CODE_RESPONSE; chap.identifier = session->chap_identifier; chap.challenge = session->chap_response; @@ -356,8 +412,8 @@ bbl_tx_encode_packet_chap_response(bbl_session_s *session) timer_add(&g_ctx->timer_root, &session->timer_auth, "Authentication Timeout", g_ctx->config.authentication_timeout, 0, session, &bbl_tx_chap_timeout); - access_interface->stats.chap_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + session->access_interface->stats.chap_tx++; + return bbl_ppp_tx(session, PROTOCOL_CHAP, &chap); } void @@ -376,38 +432,16 @@ bbl_tx_encode_packet_icmpv6_rs(bbl_session_s *session) { bbl_access_interface_s *access_interface = session->access_interface; - bbl_ethernet_header_s eth = {0}; - bbl_pppoe_session_s pppoe = {0}; bbl_ipv6_s ipv6 = {0}; bbl_icmpv6_s icmpv6 = {0}; uint8_t mac[ETH_ADDR_LEN]; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - if(session->access_type == ACCESS_TYPE_PPPOE) { - if(session->ip6cp_state != BBL_PPP_OPENED) { - return WRONG_PROTOCOL_STATE; - } - eth.dst = session->server_mac; - eth.type = ETH_TYPE_PPPOE_SESSION; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.next = &pppoe; - - pppoe.session_id = session->pppoe_session_id; - pppoe.protocol = PROTOCOL_IPV6; - pppoe.next = &ipv6; - } else { - /* IPoE */ - ipv6_multicast_mac(ipv6_multicast_all_routers, mac); - eth.dst = mac; - eth.type = ETH_TYPE_IPV6; - eth.vlan_outer_priority = g_ctx->config.ipoe_vlan_priority; - eth.next = &ipv6; + if((session->access_type == ACCESS_TYPE_PPPOE || + session->access_type == ACCESS_TYPE_PPPOL2TP) && + session->ip6cp_state != BBL_PPP_OPENED) { + return WRONG_PROTOCOL_STATE; } - eth.vlan_inner_priority = eth.vlan_outer_priority; + ipv6.dst = (void*)ipv6_multicast_all_routers; ipv6.src = (void*)session->link_local_ipv6_address; ipv6.ttl = 255; @@ -415,12 +449,13 @@ bbl_tx_encode_packet_icmpv6_rs(bbl_session_s *session) ipv6.next = &icmpv6; icmpv6.type = IPV6_ICMPV6_ROUTER_SOLICITATION; - timer_add(&g_ctx->timer_root, &session->timer_icmpv6, "ICMPv6", + timer_add(&g_ctx->timer_root, &session->timer_icmpv6, "ICMPv6", 5, 0, session, &bbl_icmpv6_timeout); session->stats.icmpv6_tx++; access_interface->stats.icmpv6_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + ipv6_multicast_mac(ipv6_multicast_all_routers, mac); + return bbl_session_tx(session, mac, PROTOCOL_IPV6, &ipv6); } void @@ -436,35 +471,22 @@ bbl_icmpv6_ns_timeout(timer_s *timer) static protocol_error_t bbl_tx_encode_packet_icmpv6_ns(bbl_session_s *session) { - bbl_ethernet_header_s eth = {0}; bbl_ipv6_s ipv6 = {0}; bbl_icmpv6_s icmpv6 = {0}; uint8_t mac[ETH_ADDR_LEN]; - ipv6addr_t ipv6_dst; if(!(session->access_type == ACCESS_TYPE_IPOE && ipv6_addr_not_zero(&session->icmpv6_ns_request))) { return WRONG_PROTOCOL_STATE; } - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.ipoe_vlan_priority; - eth.vlan_inner_priority = eth.vlan_outer_priority; - memcpy((uint8_t*)&ipv6_dst, &ipv6_solicited_node_multicast, sizeof(ipv6addr_t)); ((uint8_t*)ipv6_dst)[13] = ((uint8_t*)session->icmpv6_ns_request)[13]; ((uint8_t*)ipv6_dst)[14] = ((uint8_t*)session->icmpv6_ns_request)[14]; ((uint8_t*)ipv6_dst)[15] = ((uint8_t*)session->icmpv6_ns_request)[15]; ipv6_multicast_mac(ipv6_dst, mac); - eth.dst = mac; - eth.type = ETH_TYPE_IPV6; - eth.next = &ipv6; ipv6.dst = (void*)ipv6_dst; ipv6.src = (void*)session->link_local_ipv6_address; ipv6.ttl = 255; @@ -479,7 +501,7 @@ bbl_tx_encode_packet_icmpv6_ns(bbl_session_s *session) session->stats.icmpv6_tx++; session->access_interface->stats.icmpv6_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + return bbl_ipoe_tx(session, mac, ETH_TYPE_IPV6, g_ctx->config.ipoe_vlan_priority, &ipv6); } void @@ -510,8 +532,6 @@ bbl_tx_encode_packet_dhcpv6_request(bbl_session_s *session) { bbl_access_interface_s *access_interface = session->access_interface; - bbl_ethernet_header_s eth = {0}; - bbl_pppoe_session_s pppoe = {0}; bbl_ipv6_s ipv6 = {0}; bbl_udp_s udp = {0}; bbl_dhcpv6_s dhcpv6 = {0}; @@ -520,8 +540,10 @@ bbl_tx_encode_packet_dhcpv6_request(bbl_session_s *session) struct timespec now; struct timespec time_diff; time_t elapsed = 0; - uint8_t mac[ETH_ADDR_LEN]; + uint8_t vlan_priority = g_ctx->config.dhcpv6_vlan_priority ? + g_ctx->config.dhcpv6_vlan_priority : + g_ctx->config.ipoe_vlan_priority; if(session->dhcpv6_state == BBL_DHCP_INIT || session->dhcpv6_state == BBL_DHCP_BOUND) { @@ -555,34 +577,11 @@ bbl_tx_encode_packet_dhcpv6_request(bbl_session_s *session) udp.src = DHCPV6_UDP_CLIENT; } - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - if(session->access_type == ACCESS_TYPE_PPPOE) { - if(session->ip6cp_state != BBL_PPP_OPENED) { - return WRONG_PROTOCOL_STATE; - } - eth.dst = session->server_mac; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.type = ETH_TYPE_PPPOE_SESSION; - eth.next = &pppoe; - pppoe.session_id = session->pppoe_session_id; - pppoe.protocol = PROTOCOL_IPV6; - pppoe.next = &ipv6; - } else { - /* IPoE */ - ipv6_multicast_mac(ipv6_multicast_all_dhcp, mac); - eth.dst = mac; - eth.vlan_outer_priority = g_ctx->config.ipoe_vlan_priority; - if(g_ctx->config.dhcpv6_vlan_priority) { - eth.vlan_outer_priority = g_ctx->config.dhcpv6_vlan_priority; - } - eth.type = ETH_TYPE_IPV6; - eth.next = &ipv6; + if((session->access_type == ACCESS_TYPE_PPPOE || + session->access_type == ACCESS_TYPE_PPPOL2TP) && + session->ip6cp_state != BBL_PPP_OPENED) { + return WRONG_PROTOCOL_STATE; } - eth.vlan_inner_priority = eth.vlan_outer_priority; ipv6.dst = (void*)ipv6_multicast_all_dhcp; ipv6.src = (void*)session->link_local_ipv6_address; ipv6.ttl = 64; @@ -678,7 +677,13 @@ bbl_tx_encode_packet_dhcpv6_request(bbl_session_s *session) session->dhcpv6_retry++; session->stats.dhcpv6_tx++; access_interface->stats.dhcpv6_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + if(session->access_type == ACCESS_TYPE_PPPOE || + session->access_type == ACCESS_TYPE_PPPOL2TP) { + return bbl_ppp_tx(session, PROTOCOL_IPV6, &ipv6); + } else { + ipv6_multicast_mac(ipv6_multicast_all_dhcp, mac); + return bbl_ipoe_tx(session, mac, ETH_TYPE_IPV6, vlan_priority, &ipv6); + } } void @@ -705,30 +710,12 @@ bbl_tx_ip6cp_timeout(timer_s *timer) static protocol_error_t bbl_tx_encode_packet_ip6cp_request(bbl_session_s *session) { - bbl_access_interface_s *access_interface = session->access_interface; - - bbl_ethernet_header_s eth = {0}; - bbl_pppoe_session_s pppoe = {0}; bbl_ip6cp_s ip6cp = {0}; if(session->ip6cp_state == BBL_PPP_CLOSED || session->ip6cp_state == BBL_PPP_OPENED) { return WRONG_PROTOCOL_STATE; } - eth.dst = session->server_mac; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.vlan_inner_priority = eth.vlan_outer_priority; - eth.type = ETH_TYPE_PPPOE_SESSION; - eth.next = &pppoe; - pppoe.session_id = session->pppoe_session_id; - pppoe.protocol = PROTOCOL_IP6CP; - pppoe.next = &ip6cp; - ip6cp.code = session->ip6cp_request_code; ip6cp.identifier = ++session->ip6cp_identifier; if(ip6cp.code == PPP_CODE_CONF_REQUEST) { @@ -737,33 +724,15 @@ bbl_tx_encode_packet_ip6cp_request(bbl_session_s *session) timer_add(&g_ctx->timer_root, &session->timer_ip6cp, "IP6CP timeout", g_ctx->config.ip6cp_conf_request_timeout, 0, session, &bbl_tx_ip6cp_timeout); - access_interface->stats.ip6cp_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + session->access_interface->stats.ip6cp_tx++; + return bbl_ppp_tx(session, PROTOCOL_IP6CP, &ip6cp); } static protocol_error_t bbl_tx_encode_packet_ip6cp_response(bbl_session_s *session) { - bbl_access_interface_s *access_interface = session->access_interface; - - bbl_ethernet_header_s eth = {0}; - bbl_pppoe_session_s pppoe = {0}; bbl_ip6cp_s ip6cp = {0}; - eth.dst = session->server_mac; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.vlan_inner_priority = eth.vlan_outer_priority; - eth.type = ETH_TYPE_PPPOE_SESSION; - eth.next = &pppoe; - pppoe.session_id = session->pppoe_session_id; - pppoe.protocol = PROTOCOL_IP6CP; - pppoe.next = &ip6cp; - ip6cp.code = session->ip6cp_response_code; ip6cp.identifier = session->ip6cp_peer_identifier; if(session->ip6cp_options_len) { @@ -773,8 +742,8 @@ bbl_tx_encode_packet_ip6cp_response(bbl_session_s *session) ip6cp.ipv6_identifier = session->ip6cp_ipv6_identifier; } - access_interface->stats.ip6cp_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + session->access_interface->stats.ip6cp_tx++; + return bbl_ppp_tx(session, PROTOCOL_IP6CP, &ip6cp); } void @@ -801,30 +770,12 @@ bbl_ipcp_timeout(timer_s *timer) static protocol_error_t bbl_tx_encode_packet_ipcp_request(bbl_session_s *session) { - bbl_access_interface_s *access_interface = session->access_interface; - - bbl_ethernet_header_s eth = {0}; - bbl_pppoe_session_s pppoe = {0}; bbl_ipcp_s ipcp = {0}; if(session->ipcp_state == BBL_PPP_CLOSED || session->ipcp_state == BBL_PPP_OPENED) { return WRONG_PROTOCOL_STATE; } - eth.dst = session->server_mac; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.vlan_inner_priority = eth.vlan_outer_priority; - eth.type = ETH_TYPE_PPPOE_SESSION; - eth.next = &pppoe; - pppoe.session_id = session->pppoe_session_id; - pppoe.protocol = PROTOCOL_IPCP; - pppoe.next = &ipcp; - ipcp.code = session->ipcp_request_code; ipcp.identifier = ++session->ipcp_identifier; if(ipcp.code == PPP_CODE_CONF_REQUEST) { @@ -845,33 +796,15 @@ bbl_tx_encode_packet_ipcp_request(bbl_session_s *session) timer_add(&g_ctx->timer_root, &session->timer_ipcp, "IPCP timeout", g_ctx->config.ipcp_conf_request_timeout, 0, session, &bbl_ipcp_timeout); - access_interface->stats.ipcp_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + session->access_interface->stats.ipcp_tx++; + return bbl_ppp_tx(session, PROTOCOL_IPCP, &ipcp); } static protocol_error_t bbl_tx_encode_packet_ipcp_response(bbl_session_s *session) { - bbl_access_interface_s *access_interface = session->access_interface; - - bbl_ethernet_header_s eth = {0}; - bbl_pppoe_session_s pppoe = {0}; bbl_ipcp_s ipcp = {0}; - eth.dst = session->server_mac; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.vlan_inner_priority = eth.vlan_outer_priority; - eth.type = ETH_TYPE_PPPOE_SESSION; - eth.next = &pppoe; - pppoe.session_id = session->pppoe_session_id; - pppoe.protocol = PROTOCOL_IPCP; - pppoe.next = &ipcp; - ipcp.code = session->ipcp_response_code; ipcp.identifier = session->ipcp_peer_identifier; if(session->ipcp_options_len) { @@ -879,8 +812,8 @@ bbl_tx_encode_packet_ipcp_response(bbl_session_s *session) ipcp.options_len = session->ipcp_options_len; } - access_interface->stats.ipcp_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + session->access_interface->stats.ipcp_tx++; + return bbl_ppp_tx(session, PROTOCOL_IPCP, &ipcp); } void @@ -919,27 +852,9 @@ bbl_lcp_timeout(timer_s *timer) static protocol_error_t bbl_tx_encode_packet_lcp_request(bbl_session_s *session) { - bbl_access_interface_s *access_interface = session->access_interface; - - bbl_ethernet_header_s eth = {0}; - bbl_pppoe_session_s pppoe = {0}; bbl_lcp_s lcp = {0}; uint16_t timeout = 1; /* default timeout 1 second */ - eth.dst = session->server_mac; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.vlan_inner_priority = eth.vlan_outer_priority; - eth.type = ETH_TYPE_PPPOE_SESSION; - eth.next = &pppoe; - pppoe.session_id = session->pppoe_session_id; - pppoe.protocol = PROTOCOL_LCP; - pppoe.next = &lcp; - lcp.code = session->lcp_request_code; lcp.identifier = ++session->lcp_identifier; if(lcp.code == PPP_CODE_ECHO_REQUEST) { @@ -956,33 +871,15 @@ bbl_tx_encode_packet_lcp_request(bbl_session_s *session) timeout, 0, session, &bbl_lcp_timeout); } - access_interface->stats.lcp_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + session->access_interface->stats.lcp_tx++; + return bbl_ppp_tx(session, PROTOCOL_LCP, &lcp); } static protocol_error_t bbl_tx_encode_packet_lcp_response(bbl_session_s *session) { - bbl_access_interface_s *access_interface = session->access_interface; - - bbl_ethernet_header_s eth = {0}; - bbl_pppoe_session_s pppoe = {0}; bbl_lcp_s lcp = {0}; - eth.dst = session->server_mac; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.vlan_inner_priority = eth.vlan_outer_priority; - eth.type = ETH_TYPE_PPPOE_SESSION; - eth.next = &pppoe; - pppoe.session_id = session->pppoe_session_id; - pppoe.protocol = PROTOCOL_LCP; - pppoe.next = &lcp; - lcp.code = session->lcp_response_code; lcp.identifier = session->lcp_peer_identifier; @@ -999,8 +896,8 @@ bbl_tx_encode_packet_lcp_response(bbl_session_s *session) } } - access_interface->stats.lcp_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + session->access_interface->stats.lcp_tx++; + return bbl_ppp_tx(session, PROTOCOL_LCP, &lcp); } void @@ -1029,21 +926,9 @@ bbl_padr_timeout(timer_s *timer) static protocol_error_t bbl_encode_padi(bbl_session_s *session) { - bbl_ethernet_header_s eth = {0}; bbl_pppoe_discovery_s pppoe = {0}; access_line_s access_line = {0}; - eth.dst = session->server_mac; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.vlan_inner_priority = eth.vlan_outer_priority; - - eth.type = ETH_TYPE_PPPOE_DISCOVERY; - eth.next = &pppoe; pppoe.code = PPPOE_PADI; if(session->pppoe_service_name) { pppoe.service_name = session->pppoe_service_name; @@ -1066,26 +951,15 @@ bbl_encode_padi(bbl_session_s *session) access_line.profile = session->access_line_profile; pppoe.access_line = &access_line; } - return encode_ethernet(session->write_buf, &session->write_idx, ð); + return bbl_pppoe_disc_tx(session, &pppoe); } static protocol_error_t bbl_encode_padr(bbl_session_s *session) { - bbl_ethernet_header_s eth = {0}; bbl_pppoe_discovery_s pppoe = {0}; access_line_s access_line = {0}; - eth.dst = session->server_mac; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.vlan_inner_priority = eth.vlan_outer_priority; - eth.type = ETH_TYPE_PPPOE_DISCOVERY; - eth.next = &pppoe; pppoe.code = PPPOE_PADR; pppoe.ac_cookie = session->pppoe_ac_cookie; pppoe.ac_cookie_len = session->pppoe_ac_cookie_len; @@ -1110,28 +984,16 @@ bbl_encode_padr(bbl_session_s *session) access_line.profile = session->access_line_profile; pppoe.access_line = &access_line; } - return encode_ethernet(session->write_buf, &session->write_idx, ð); + return bbl_pppoe_disc_tx(session, &pppoe); } static protocol_error_t bbl_encode_padt(bbl_session_s *session) { - bbl_ethernet_header_s eth = {0}; bbl_pppoe_discovery_s pppoe = {0}; - - eth.dst = session->server_mac; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.pppoe_vlan_priority; - eth.vlan_inner_priority = eth.vlan_outer_priority; - eth.type = ETH_TYPE_PPPOE_DISCOVERY; - eth.next = &pppoe; pppoe.code = PPPOE_PADT; pppoe.session_id = session->pppoe_session_id; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + return bbl_pppoe_disc_tx(session, &pppoe); } protocol_error_t @@ -1196,7 +1058,6 @@ bbl_tx_encode_packet_dhcp(bbl_session_s *session) { bbl_access_interface_s *access_interface = session->access_interface; - bbl_ethernet_header_s eth = {0}; bbl_ipv4_s ipv4 = {0}; bbl_udp_s udp = {0}; struct dhcp_header header = {0}; @@ -1204,6 +1065,10 @@ bbl_tx_encode_packet_dhcp(bbl_session_s *session) access_line_s access_line = {0}; struct timespec now; time_t secs = 0; + uint8_t *dst_mac = NULL; + uint8_t vlan_priority = g_ctx->config.dhcp_vlan_priority ? + g_ctx->config.dhcp_vlan_priority : + g_ctx->config.ipoe_vlan_priority; if(session->dhcp_state == BBL_DHCP_INIT || session->dhcp_state == BBL_DHCP_BOUND) { @@ -1211,18 +1076,6 @@ bbl_tx_encode_packet_dhcp(bbl_session_s *session) } dhcp.header = &header; - eth.src = session->client_mac; - eth.qinq = session->access_config->qinq; - eth.vlan_outer = session->vlan_key.outer_vlan_id; - eth.vlan_inner = session->vlan_key.inner_vlan_id; - eth.vlan_three = session->access_third_vlan; - eth.vlan_outer_priority = g_ctx->config.ipoe_vlan_priority; - if(g_ctx->config.dhcp_vlan_priority) { - eth.vlan_outer_priority = g_ctx->config.dhcp_vlan_priority; - } - eth.vlan_inner_priority = eth.vlan_outer_priority; - eth.type = ETH_TYPE_IPV4; - eth.next = &ipv4; ipv4.src = session->ip_address; ipv4.ttl = 255; ipv4.tos = g_ctx->config.dhcp_tos; @@ -1282,7 +1135,7 @@ bbl_tx_encode_packet_dhcp(bbl_session_s *session) dhcp.type = DHCP_MESSAGE_DISCOVER; session->stats.dhcp_tx_discover++; LOG(DHCP, "DHCP (ID: %u) DHCP-Discover send\n", session->session_id); - eth.dst = (uint8_t*)broadcast_mac; + dst_mac = (uint8_t*)broadcast_mac; ipv4.dst = IPV4_BROADCAST; dhcp.parameter_request_list = true; dhcp.option_netmask = true; @@ -1307,7 +1160,7 @@ bbl_tx_encode_packet_dhcp(bbl_session_s *session) LOG(DHCP, "DHCP (ID: %u) DHCP-Request (init-reboot) send\n", session->session_id); dhcp.option_server_identifier = false; } - eth.dst = (uint8_t*)broadcast_mac; + dst_mac = (uint8_t*)broadcast_mac; ipv4.dst = IPV4_BROADCAST; dhcp.option_address = true; dhcp.address = session->dhcp_address; @@ -1323,7 +1176,7 @@ bbl_tx_encode_packet_dhcp(bbl_session_s *session) dhcp.type = DHCP_MESSAGE_REQUEST; session->stats.dhcp_tx_request++; LOG(DHCP, "DHCP (ID: %u) DHCP-Request (renewing) send\n", session->session_id); - eth.dst = session->dhcp_server_mac; + dst_mac = session->dhcp_server_mac; ipv4.dst = session->dhcp_server_identifier; header.ciaddr = session->ip_address; break; @@ -1331,7 +1184,7 @@ bbl_tx_encode_packet_dhcp(bbl_session_s *session) dhcp.type = DHCP_MESSAGE_RELEASE; session->stats.dhcp_tx_release++; LOG(DHCP, "DHCP (ID: %u) DHCP-Release send\n", session->session_id); - eth.dst = session->dhcp_server_mac; + dst_mac = session->dhcp_server_mac; ipv4.dst = session->dhcp_server_identifier; header.ciaddr = session->ip_address; dhcp.option_server_identifier = true; @@ -1359,7 +1212,7 @@ bbl_tx_encode_packet_dhcp(bbl_session_s *session) session->stats.dhcp_tx++; access_interface->stats.dhcp_tx++; - return encode_ethernet(session->write_buf, &session->write_idx, ð); + return bbl_ipoe_tx(session, dst_mac, ETH_TYPE_IPV4, vlan_priority, &ipv4); } void @@ -1534,7 +1387,8 @@ bbl_tx_encode_packet(bbl_session_s *session, uint8_t *buf, uint16_t *len) session->write_buf = buf; session->write_idx = 0; - if(session->send_requests & BBL_SEND_DISCOVERY) { + if(session->send_requests & BBL_SEND_DISCOVERY && + session->access_type != ACCESS_TYPE_PPPOL2TP) { result = bbl_tx_encode_packet_discovery(session); session->send_requests &= ~BBL_SEND_DISCOVERY; session->pppoe_retries++; @@ -1828,6 +1682,9 @@ bbl_tx(bbl_interface_s *interface, uint8_t *buf, uint16_t *len) access_interface->stats.bytes_tx += *len; session->stats.packets_tx++; session->stats.bytes_tx += *len; + } else if(result == PROTOCOL_QUEUED) { + /* Packet enqueued in a TX queue (ex: L2TP); nothing to transmit from write_buf. */ + result = EMPTY; } /* Remove only from TX queue if all requests are processed! */ bbl_session_tx_qnode_remove(session); diff --git a/docsrc/sources/access/l2tp.rst b/docsrc/sources/access/l2tp.rst index 4b87bcfc..a3463abe 100644 --- a/docsrc/sources/access/l2tp.rst +++ b/docsrc/sources/access/l2tp.rst @@ -1,7 +1,7 @@ .. _l2tp: -L2TP ----- +L2TP (LNS Mode) +--------------- The BNG Blaster can emulate L2TPv2 (RFC2661) LNS servers to be able to test the L2TPv2 LAC functionality of the BNG device under @@ -398,3 +398,180 @@ It is also possible to display a single session. ``$ sudo bngblaster-cli run.sock l2tp-sessions tunnel-id 1 session-id 1`` +.. _l2tp-lac: + +L2TP (LAC Mode) +--------------- + +The BNG Blaster can also act as an L2TPv2 LAC (L2TP Access Concentrator), +generating PPP sessions encapsulated in L2TP tunnels toward a real LNS device +under test. This makes it possible to test LNS functionality directly. + +Each access interface section with ``"type": "pppol2tp"`` creates PPP sessions +that are tunnelled through the L2TP clients specified in the ``"l2tp-client"`` +list. The L2TP sessions are spread accross multiple tunnels (the ones whose +``"group-id"`` field match the ``"l2tp-client-group-id"`` field of the access +interface). + +Configuration +~~~~~~~~~~~~~ + +.. code-block:: json + + { + "interfaces": { + "network": { + "interface": "eth1", + "address": "10.0.0.1/24", + "gateway": "10.0.0.2" + }, + "access": [ + { + "interface": "eth1", + "type": "pppol2tp", + "l2tp-client-group-id": 1, + "outer-vlan-min": 1, + "outer-vlan-max": 1000, + "inner-vlan": 7, + "authentication-protocol": "PAP" + } + ] + }, + "ppp": { + "mru": 1492, + "authentication": { + "username": "user{session}@example.com", + "password": "test" + }, + "lcp": { + "conf-request-timeout": 5, + "conf-request-retry": 30 + }, + "ipcp": { + "enable": true + }, + "ip6cp": { + "enable": true + } + }, + "l2tp-client": [ + { + "group-id": 1, + "name": "LAC1", + "network-interface": "eth1", + "client-address": "10.0.1.1", + "server-address": "10.0.0.100", + "secret": "test", + "receive-window-size": 16 + }, + { + "group-id": 1, + "name": "LAC2", + "network-interface": "eth1", + "client-address": "10.0.1.2", + "server-address": "10.0.0.100", + "secret": "test", + "receive-window-size": 16 + } + ] + } + +.. include:: ../configuration/lac.rst + +LAC vs LNS Self-test +~~~~~~~~~~~~~~~~~~~~ + +The LAC and LNS roles can be tested locally without external network +devices using a pair of virtual ethernet interfaces. + +.. code-block:: none + + sudo ip link add veth-lac type veth peer name veth-lns + sudo ip link set veth-lac up + sudo ip link set veth-lns up + +Start the LNS instance first (terminal 1): + +.. code-block:: json + + { + "interfaces": { + "network": { + "interface": "veth-lns", + "address": "10.0.0.2", + "gateway": "10.0.0.1" + } + }, + "l2tp-server": [ + { + "name": "bbl-lns", + "address": "10.0.0.2", + "secret": "test" + } + ], + "ppp": { + "authentication": { + "username": "user@test", + "password": "password" + }, + "ipcp": { + "enable": true, + "request-ip": true, + "request-dns1": true, + "request-dns2": true + } + } + } + +``sudo bngblaster -C lns.json -l info -l l2tp`` + +Then start the LAC instance (terminal 2): + +.. code-block:: json + + { + "interfaces": { + "network": { + "interface": "veth-lac", + "address": "10.0.0.1", + "gateway": "10.0.0.2" + }, + "access": [ + { + "interface": "veth-lac", + "type": "pppol2tp", + "l2tp-client-group-id": 1 + } + ] + }, + "l2tp-client": [ + { + "group-id": 1, + "name": "LAC1", + "network-interface": "veth-lac", + "server-address": "10.0.0.2", + "secret": "test", + "max-retry": 10, + "receive-window-size": 4 + } + ], + "sessions": { + "count": 1 + }, + "ppp": { + "authentication": { + "username": "user@test", + "password": "password" + }, + "lcp": { + "conf-request-timeout": 1, + "conf-request-retry": 10 + }, + "ipcp": { + "enable": true + } + } + } + +``sudo bngblaster -C lac.json -l info -l l2tp`` + diff --git a/docsrc/sources/configuration/index.rst b/docsrc/sources/configuration/index.rst index b84a0c12..ce680fe3 100644 --- a/docsrc/sources/configuration/index.rst +++ b/docsrc/sources/configuration/index.rst @@ -147,6 +147,10 @@ L2TPv2 Server (LNS) ------------------- .. include:: lns.rst +L2TPv2 Client (LAC) +------------------- +.. include:: lac.rst + Traffic ------- .. include:: traffic.rst diff --git a/docsrc/sources/configuration/interfaces_access.rst b/docsrc/sources/configuration/interfaces_access.rst index 73d90c31..4cd0bc4b 100644 --- a/docsrc/sources/configuration/interfaces_access.rst +++ b/docsrc/sources/configuration/interfaces_access.rst @@ -176,6 +176,12 @@ | **arp-client-group-id** | | Set ARP group identifier. | | | | Default: 0 Range: 0 - 65535 | +-----------------------------------+----------------------------------------------------------------------+ +| **l2tp-client-group-id** | | Set L2TP client group identifier (LAC mode, requires | +| | | ``"type": "pppol2tp"``). Sessions on this access interface are | +| | | spread evenly across all ``l2tp-client`` entries with the same | +| | | ``group-id``. | +| | | Default: 0 Range: 0 - 65535 | ++-----------------------------------+----------------------------------------------------------------------+ | **icmp-client-group-id",** | | Set ICMP group identifier. | | | | Default: 0 Range: 0 - 65535 | +-----------------------------------+----------------------------------------------------------------------+ diff --git a/docsrc/sources/configuration/lac.rst b/docsrc/sources/configuration/lac.rst new file mode 100644 index 00000000..7fead191 --- /dev/null +++ b/docsrc/sources/configuration/lac.rst @@ -0,0 +1,69 @@ +.. code-block:: json + + { "l2tp-client": [] } + ++-------------------------------------------+---------------------------------------------------------------------+ +| Attribute | Description | ++===========================================+=====================================================================+ +| **group-id** | | Mandatory group identifier. All ``l2tp-client`` entries with | +| | | the same ``group-id`` form a group. Access interface sections | +| | | reference the group via ``l2tp-client-group-id``. Sessions are | +| | | spread evenly across all tunnels in the group. | +| | | Range: 1 - 65535 | ++-------------------------------------------+---------------------------------------------------------------------+ +| **name** | | Mandatory L2TP LAC client name sent as hostname (AVP 7). | ++-------------------------------------------+---------------------------------------------------------------------+ +| **network-interface** | | Mandatory name of the network interface used to reach the LNS. | ++-------------------------------------------+---------------------------------------------------------------------+ +| **server-address** | | Mandatory IPv4 address of the LNS to connect to. | ++-------------------------------------------+---------------------------------------------------------------------+ +| **client-address** | | Optional source IPv4 address used for outgoing L2TP/UDP packets. | +| | | Defaults to the network interface address when not set. When | +| | | set, BNG Blaster automatically answers ARP requests for this | +| | | address on the network interface. For now, all l2tp-clients must | +| | | have a different address. | ++-------------------------------------------+---------------------------------------------------------------------+ +| **secret** | | Tunnel secret. | ++-------------------------------------------+---------------------------------------------------------------------+ +| **receive-window-size** | | Control messages receive window size. | +| | | Default: 16 Range: 1 - 65535 | ++-------------------------------------------+---------------------------------------------------------------------+ +| **max-retry** | | Control messages max retry. | +| | | Default: 5 Range: 1 - 65535 | ++-------------------------------------------+---------------------------------------------------------------------+ +| **congestion-mode** | | Control messages congestion mode (default, slow or aggressive). | +| | | 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. | +| | | Default: default | ++-------------------------------------------+---------------------------------------------------------------------+ +| **hello-interval** | | Set hello interval in seconds. | +| | | Default: 30 Range: 0 - 65535 | ++-------------------------------------------+---------------------------------------------------------------------+ +| **data-control-priority** | | Set the priority bit in the L2TP header for all non-IP data | +| | | packets (LCP, IPCP, ...). | +| | | Default: false | ++-------------------------------------------+---------------------------------------------------------------------+ +| **data-length** | | Set length bit for all data packets. | +| | | Default: false | ++-------------------------------------------+---------------------------------------------------------------------+ +| **data-offset** | | Set offset bit with offset zero for all data packets. | +| | | Default: false | ++-------------------------------------------+---------------------------------------------------------------------+ +| **control-tos** | | Set L2TP control traffic (SCCRQ, ICRQ, ...) TOS priority. | +| | | Default: 0 Range: 0 - 255 | ++-------------------------------------------+---------------------------------------------------------------------+ +| **data-control-tos** | | Set the L2TP tunnel TOS priority (outer IPv4 header) for all | +| | | non-IP data packets (LCP, IPCP, ...). | +| | | Default: 0 Range: 0 - 255 | ++-------------------------------------------+---------------------------------------------------------------------+ +| **lcp-padding** | | Add fixed padding to LCP packets sent from the LAC. | +| | | Default: 0 Range: 0 - 65535 | ++-------------------------------------------+---------------------------------------------------------------------+ +| **calling-number** | | Optional Calling Number string sent in ICRQ (AVP 22). | ++-------------------------------------------+---------------------------------------------------------------------+ +| **called-number** | | Optional Called Number string sent in ICRQ (AVP 21). | ++-------------------------------------------+---------------------------------------------------------------------+ diff --git a/schemas/bngblaster-config.json b/schemas/bngblaster-config.json index ed32d2dc..33f25507 100644 --- a/schemas/bngblaster-config.json +++ b/schemas/bngblaster-config.json @@ -1238,6 +1238,124 @@ } } }, + "l2tp-client": { + "type": "array", + "description": "L2TPv2 Client (LAC) configuration", + "items": { + "type": "object", + "required": [ + "group-id", + "name", + "network-interface", + "server-address" + ], + "properties": { + "group-id": { + "type": "integer", + "description": "L2TP client group identifier, matched against l2tp-client-group-id in access interfaces", + "minimum": 1, + "maximum": 65535 + }, + "name": { + "type": "string", + "description": "L2TP LAC tunnel name (AVP 7)" + }, + "network-interface": { + "type": "string", + "description": "Network interface used for L2TP tunnel" + }, + "server-address": { + "type": "string", + "description": "L2TP server (LNS) address", + "format": "ipv4" + }, + "client-address": { + "type": "string", + "description": "L2TP client source address (defaults to network interface address)", + "format": "ipv4" + }, + "secret": { + "type": "string", + "description": "Tunnel secret" + }, + "receive-window-size": { + "type": "integer", + "description": "Control messages receive window size", + "default": 16, + "minimum": 1, + "maximum": 65535 + }, + "max-retry": { + "type": "integer", + "description": "Maximum number of tunnel setup retries", + "default": 5, + "minimum": 1, + "maximum": 65535 + }, + "congestion-mode": { + "type": "string", + "description": "Congestion control mode", + "enum": [ + "default", + "slow", + "aggressive" + ], + "default": "default" + }, + "data-control-priority": { + "type": "boolean", + "description": "Set priority bit for non-IP data packets", + "default": false + }, + "data-length": { + "type": "boolean", + "description": "Set length bit for all data packets", + "default": false + }, + "data-offset": { + "type": "boolean", + "description": "Set offset bit with zero for all data packets", + "default": false + }, + "control-tos": { + "type": "integer", + "description": "L2TP control traffic TOS priority", + "default": 0, + "minimum": 0, + "maximum": 255 + }, + "data-control-tos": { + "type": "integer", + "description": "L2TP tunnel TOS for non-IP data packets", + "default": 0, + "minimum": 0, + "maximum": 255 + }, + "hello-interval": { + "type": "integer", + "description": "Hello (keepalive) interval in seconds", + "default": 30, + "minimum": 0, + "maximum": 65535 + }, + "lcp-padding": { + "type": "integer", + "description": "Add fixed padding to LCP packets", + "default": 0, + "minimum": 0, + "maximum": 65535 + }, + "calling-number": { + "type": "string", + "description": "Calling number AVP value" + }, + "called-number": { + "type": "string", + "description": "Called number AVP value" + } + } + } + }, "isis": { "oneOf": [ { @@ -1628,7 +1746,8 @@ "description": "Access type", "enum": [ "pppoe", - "ipoe" + "ipoe", + "pppol2tp" ], "default": "pppoe" }, @@ -1995,6 +2114,13 @@ "minimum": 0, "maximum": 65535 }, + "l2tp-client-group-id": { + "type": "integer", + "description": "L2TP client group identifier (used with type pppol2tp)", + "default": 0, + "minimum": 0, + "maximum": 65535 + }, "tun": { "type": "boolean", "description": "Create dedicated TUN interface for each session",