diff --git a/docs/config.md b/docs/config.md index 9e18a43d..68433805 100644 --- a/docs/config.md +++ b/docs/config.md @@ -449,6 +449,8 @@ Attribute | Description | Default `keepalive-interval` | LCP echo request interval in seconds (0 means disabled) | 30 `keepalive-retry` | PPP LCP echo request max retry | 3 `start-delay` | PPP LCP initial request delay in milliseconds | 0 +`ignore-vendor-specific` | Ignore LCP vendor specific requests | false +`connection-status-message` | Accept LCP connection status messages | false ### PPP IPCP diff --git a/docs/ipoe.md b/docs/ipoe.md index c6336b1c..a590025c 100644 --- a/docs/ipoe.md +++ b/docs/ipoe.md @@ -58,8 +58,10 @@ The most common case for IPoE is using DHCPv4/v6 as shown below. } ``` +## IPoE Session Information + The control socket command `session-info session-id ` provides -detailed information for IPOE sessions. +detailed information for IPoE sessions. `$ sudo bngblaster-cli run.sock session-info session-id 1 | jq .` ```json diff --git a/docs/pppoe.md b/docs/pppoe.md index efa912db..ad69d95d 100644 --- a/docs/pppoe.md +++ b/docs/pppoe.md @@ -96,8 +96,24 @@ detailed explained in the configuration section. } ``` +## LCP Vendor Extension + +This chapter refers to RFC 2153 PPP vendor extensions. + +Per default all LCP vendor specific requests will be rejected sending a +LCP code reject message. With `ppp->lcp->ignore-vendor-specific` enabled, +those messages will be ignored as required to emulate different CPE +behaviors. + +The option `ppp->lcp->connection-status-message` allows to accept LCP vendor requests +with any OUI if kind is set to `1` by responding with vendor request of +kind `2`. The OUI from request is copied to response in this case. +The value from request is stored in the session as `connection-status-message`. + +## PPPoE Session Information + The control socket command `session-info session-id ` provides -detailed information for IPOE sessions. +detailed information for PPPoE sessions. `$ sudo bngblaster-cli run.sock session-info session-id 1 | jq .` ```json diff --git a/src/bbl.h b/src/bbl.h index 9bca2470..3a90cbd1 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -630,6 +630,8 @@ typedef struct bbl_ctx_ uint16_t lcp_keepalive_interval; uint16_t lcp_keepalive_retry; uint16_t lcp_start_delay; + bool lcp_vendor_ignore; + bool lcp_connection_status_message; /* Authentication */ uint16_t authentication_timeout; @@ -885,6 +887,9 @@ typedef struct bbl_session_ uint16_t auth_protocol; /* PAP or CHAP */ uint8_t auth_retries; + char *reply_message; + char *connections_status_message; + /* IPCP */ ppp_state_t ipcp_state; uint8_t ipcp_response_code; diff --git a/src/bbl_config.c b/src/bbl_config.c index f9032f68..b29d1e5d 100644 --- a/src/bbl_config.c +++ b/src/bbl_config.c @@ -872,6 +872,14 @@ json_parse_config (json_t *root, bbl_ctx_s *ctx) { return false; } } + value = json_object_get(sub, "ignore-vendor-specific"); + if (json_is_boolean(value)) { + ctx->config.lcp_vendor_ignore = json_boolean_value(value); + } + value = json_object_get(sub, "connection-status-message"); + if (json_is_boolean(value)) { + ctx->config.lcp_connection_status_message = json_boolean_value(value); + } } sub = json_object_get(section, "ipcp"); if (json_is_object(sub)) { diff --git a/src/bbl_ctrl.c b/src/bbl_ctrl.c index 6a32530b..66d3f465 100644 --- a/src/bbl_ctrl.c +++ b/src/bbl_ctrl.c @@ -504,7 +504,7 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* argum } if(session->access_type == ACCESS_TYPE_PPPOE) { - root = json_pack("{ss si s{ss si ss ss si si ss ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* si si si so*}}", + root = json_pack("{ss si s{ss si ss ss si si ss ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* ss* si si si so*}}", "status", "ok", "code", 200, "session-info", @@ -518,6 +518,8 @@ bbl_ctrl_session_info(int fd, bbl_ctx_s *ctx, uint32_t session_id, json_t* argum "username", session->username, "agent-circuit-id", session->agent_circuit_id, "agent-remote-id", session->agent_remote_id, + "reply-message", session->reply_message, + "connection-status-message", session->connections_status_message, "lcp-state", ppp_state_string(session->lcp_state), "ipcp-state", ppp_state_string(session->ipcp_state), "ip6cp-state", ppp_state_string(session->ip6cp_state), diff --git a/src/bbl_interactive.c b/src/bbl_interactive.c index 246b9f0e..25ebbfd8 100644 --- a/src/bbl_interactive.c +++ b/src/bbl_interactive.c @@ -20,7 +20,7 @@ typedef enum { UI_VIEW_DEFAULT = 0, UI_VIEW_ACCESS_IF_STATS, - UI_VIEW_STREAMS, + UI_VIEW_SESSION, UI_VIEW_MAX, } __attribute__ ((__packed__)) bbl_ui_view; @@ -49,7 +49,7 @@ bbl_init_stats_win() wclear(stats_win); wattron(stats_win, COLOR_PAIR(COLOR_GREEN)); wprintw(stats_win, "F1: Select View F7/F8: Start/Stop Traffic F9: Terminate Sessions\n"); - if(g_view_selected == UI_VIEW_STREAMS) { + if(g_view_selected == UI_VIEW_SESSION) { wprintw(stats_win, "Left/Right: Select Session\n"); } else { wprintw(stats_win, "Left/Right: Select Interface\n"); @@ -93,7 +93,7 @@ bbl_read_key_job (timer_s *timer) bbl_init_stats_win(); break; case KEY_LEFT: - if(g_view_selected == UI_VIEW_STREAMS) { + if(g_view_selected == UI_VIEW_SESSION) { if(g_session_selected > 1) { g_session_selected--; } else { @@ -108,7 +108,7 @@ bbl_read_key_job (timer_s *timer) bbl_init_stats_win(); break; case KEY_RIGHT: - if(g_view_selected == UI_VIEW_STREAMS) { + if(g_view_selected == UI_VIEW_SESSION) { g_session_selected++; if(g_session_selected > ctx->sessions) { g_session_selected = 1; @@ -407,8 +407,8 @@ bbl_stats_job (timer_s *timer) wprintw(stats_win, "\nAccess Interface Protocol Stats"); } - } else if(g_view_selected == UI_VIEW_STREAMS) { - wprintw(stats_win, "\nSession Stream Stats ( Session-Id: ", g_session_selected); + } else if(g_view_selected == UI_VIEW_SESSION) { + wprintw(stats_win, "\nSession and Streams ( Session-Id: ", g_session_selected); wattron(stats_win, COLOR_PAIR(COLOR_GREEN)); wprintw(stats_win, "%u", g_session_selected); wattroff(stats_win, COLOR_PAIR(COLOR_GREEN)); @@ -425,6 +425,15 @@ bbl_stats_job (timer_s *timer) if(session->agent_remote_id) { wprintw(stats_win, " ACI: %s \n", session->agent_circuit_id); } + if(session->connections_status_message || session->reply_message) { + wprintw(stats_win, "\n"); + if(session->reply_message) { + wprintw(stats_win, " Reply-Message: %s \n", session->reply_message); + } + if(session->connections_status_message) { + wprintw(stats_win, " Connection-Status-Message: %s \n", session->connections_status_message); + } + } wprintw(stats_win, "\n Access Client Interface\n"); wprintw(stats_win, " Tx Packets %10lu | %7lu PPS | %10lu Kbps\n", session->stats.packets_tx, session->stats.rate_packets_tx.avg, diff --git a/src/bbl_protocols.c b/src/bbl_protocols.c index f10c1c2f..04f51d7e 100644 --- a/src/bbl_protocols.c +++ b/src/bbl_protocols.c @@ -2964,6 +2964,9 @@ decode_ppp_lcp(uint8_t *buf, uint16_t len, lcp = (bbl_lcp_t*)sp; BUMP_BUFFER(sp, sp_len, sizeof(bbl_lcp_t)); memset(lcp, 0x0, sizeof(bbl_lcp_t)); + lcp->start = buf; + lcp->len = len; + lcp->code = *buf; BUMP_BUFFER(buf, len, sizeof(uint8_t)); lcp->identifier = *buf; @@ -2990,6 +2993,21 @@ decode_ppp_lcp(uint8_t *buf, uint16_t len, lcp->options_len = lcp_len; switch(lcp->code) { + case PPP_CODE_VENDOR_SPECIFIC: + /* RFC 2153 */ + if(lcp_len < 8) { + return DECODE_ERROR; + } + lcp->magic = *(uint32_t*)buf; + BUMP_BUFFER(buf, lcp_len, 3); + lcp->vendor_oui = *(uint32_t*)buf; + lcp->vendor_oui &= 0xffffff; + BUMP_BUFFER(buf, lcp_len, sizeof(uint32_t)); + lcp->vendor_kind = *buf; + BUMP_BUFFER(buf, lcp_len, sizeof(uint8_t)); + lcp->vendor_value = buf; + lcp->vendor_value_len = lcp_len; + break; case PPP_CODE_ECHO_REQUEST: case PPP_CODE_ECHO_REPLY: if(lcp_len>=4) { diff --git a/src/bbl_protocols.h b/src/bbl_protocols.h index 41cf01cb..ff7da657 100644 --- a/src/bbl_protocols.h +++ b/src/bbl_protocols.h @@ -83,6 +83,7 @@ #define ICMP_TYPE_ECHO_REPLY 0x00 #define ICMP_TYPE_ECHO_REQUEST 0x08 +#define PPP_CODE_VENDOR_SPECIFIC 0 #define PPP_CODE_CONF_REQUEST 1 #define PPP_CODE_CONF_ACK 2 #define PPP_CODE_CONF_NAK 3 @@ -533,6 +534,12 @@ typedef struct bbl_lcp_ { uint16_t mru; uint16_t auth; uint32_t magic; + uint32_t vendor_oui; + uint8_t vendor_kind; + uint8_t *vendor_value; + uint16_t vendor_value_len; + uint8_t *start; + uint16_t len; } bbl_lcp_t; /* diff --git a/src/bbl_rx.c b/src/bbl_rx.c index 04b375fe..6cbe507a 100644 --- a/src/bbl_rx.c +++ b/src/bbl_rx.c @@ -756,6 +756,14 @@ bbl_rx_pap(bbl_ethernet_header_t *eth, bbl_interface_s *interface, bbl_session_s } } } + if(pap->reply_message_len) { + if(session->reply_message) { + free(session->reply_message); + } + session->reply_message = malloc(pap->reply_message_len+1); + memcpy(session->reply_message, pap->reply_message, pap->reply_message_len); + session->reply_message[pap->reply_message_len] = 0; + } bbl_session_update_state(ctx, session, BBL_PPP_NETWORK); if(ctx->config.ipcp_enable) { session->ipcp_state = BBL_PPP_INIT; @@ -841,6 +849,14 @@ bbl_rx_chap(bbl_ethernet_header_t *eth, bbl_interface_s *interface, bbl_session_ } } } + if(chap->reply_message_len) { + if(session->reply_message) { + free(session->reply_message); + } + session->reply_message = malloc(chap->reply_message_len+1); + memcpy(session->reply_message, chap->reply_message, chap->reply_message_len); + session->reply_message[chap->reply_message_len] = 0; + } bbl_session_update_state(ctx, session, BBL_PPP_NETWORK); if(ctx->config.ipcp_enable) { session->ipcp_state = BBL_PPP_INIT; @@ -1132,6 +1148,37 @@ bbl_rx_lcp(bbl_ethernet_header_t *eth, bbl_interface_s *interface, bbl_session_s lcp = (bbl_lcp_t*)pppoes->next; switch(lcp->code) { + case PPP_CODE_VENDOR_SPECIFIC: + if(ctx->config.lcp_vendor_ignore) { + return; + } + if(ctx->config.lcp_connection_status_message && + lcp->vendor_kind == 1 && lcp->vendor_value_len) { + if(session->connections_status_message) { + free(session->connections_status_message); + } + session->connections_status_message = malloc(lcp->vendor_value_len+1); + memcpy(session->connections_status_message, lcp->vendor_value, lcp->vendor_value_len); + session->connections_status_message[lcp->vendor_value_len] = 0; + session->lcp_response_code = PPP_CODE_VENDOR_SPECIFIC; + *(uint32_t*)(session->lcp_options+3) = lcp->vendor_oui; + *(uint32_t*)session->lcp_options = session->magic_number; + session->lcp_options[7] = 2; + session->lcp_options_len = 8; + } else { + session->lcp_response_code = PPP_CODE_CODE_REJECT; + if(lcp->len > PPP_OPTIONS_BUFFER) { + memcpy(session->lcp_options, lcp->start, PPP_OPTIONS_BUFFER); + session->lcp_options_len = PPP_OPTIONS_BUFFER; + } else { + memcpy(session->lcp_options, lcp->start, lcp->len); + session->lcp_options_len = lcp->len; + } + } + session->lcp_peer_identifier = lcp->identifier; + session->send_requests |= BBL_SEND_LCP_RESPONSE; + bbl_session_tx_qnode_insert(session); + break; case PPP_CODE_CONF_REQUEST: session->auth_protocol = lcp->auth; if(session->access_config->authentication_protocol) { @@ -1253,6 +1300,17 @@ bbl_rx_lcp(bbl_ethernet_header_t *eth, bbl_interface_s *interface, bbl_session_s bbl_session_tx_qnode_insert(session); break; default: + session->lcp_response_code = PPP_CODE_CODE_REJECT; + if(lcp->len > PPP_OPTIONS_BUFFER) { + memcpy(session->lcp_options, lcp->start, PPP_OPTIONS_BUFFER); + session->lcp_options_len = PPP_OPTIONS_BUFFER; + } else { + memcpy(session->lcp_options, lcp->start, lcp->len); + session->lcp_options_len = lcp->len; + } + session->lcp_peer_identifier = lcp->identifier; + session->send_requests |= BBL_SEND_LCP_RESPONSE; + bbl_session_tx_qnode_insert(session); break; } } diff --git a/src/bbl_session.c b/src/bbl_session.c index 52176462..4dff067b 100644 --- a/src/bbl_session.c +++ b/src/bbl_session.c @@ -186,6 +186,15 @@ bbl_session_update_state(bbl_ctx_s *ctx, bbl_session_s *session, session_state_t session->zapping_view_start_time.tv_sec = 0; session->zapping_view_start_time.tv_nsec = 0; + if(session->reply_message) { + free(session->reply_message); + session->reply_message = NULL; + } + if(session->connections_status_message) { + free(session->connections_status_message); + session->connections_status_message = NULL; + } + /* L2TP */ session->l2tp = false; session->l2tp_session = NULL;