From 5dd3e4c19a3cd9b7aed65f142c7a0cc015e6c09a Mon Sep 17 00:00:00 2001 From: Christian Giese Date: Tue, 23 Feb 2021 12:47:32 +0100 Subject: [PATCH] #10 LI Flow Support --- src/bbl.c | 4 ++ src/bbl.h | 6 +++ src/bbl_ctrl.c | 41 +++++++++++++++++++++ src/bbl_interactive.c | 6 +++ src/bbl_li.c | 85 +++++++++++++++++++++++++++++++++++++++++++ src/bbl_li.h | 32 ++++++++++++++++ src/bbl_protocols.c | 35 +++++++++++++++++- src/bbl_protocols.h | 14 +++++++ src/bbl_rx.c | 2 + src/bbl_stats.c | 15 ++++++++ 10 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 src/bbl_li.c create mode 100644 src/bbl_li.h diff --git a/src/bbl.c b/src/bbl.c index 24f00057..02ecef66 100644 --- a/src/bbl.c +++ b/src/bbl.c @@ -653,6 +653,10 @@ bbl_add_ctx (void) bbl_l2tp_session_hash, BBL_SESSION_HASHTABLE_SIZE); + ctx->li_flow_dict = hashtable2_dict_new((dict_compare_func)bbl_compare_l2tp_session, + bbl_l2tp_session_hash, + BBL_SESSION_HASHTABLE_SIZE); + return ctx; } diff --git a/src/bbl.h b/src/bbl.h index 5d770098..10396501 100644 --- a/src/bbl.h +++ b/src/bbl.h @@ -45,6 +45,7 @@ #include "bbl_tx.h" #include "bbl_l2tp.h" #include "bbl_l2tp_avp.h" +#include "bbl_li.h" #define WRITE_BUF_LEN 1514 #define SCRATCHPAD_LEN 1514 @@ -271,6 +272,9 @@ typedef struct bbl_interface_ uint64_t l2tp_data_tx; bbl_rate_s rate_l2tp_data_rx; bbl_rate_s rate_l2tp_data_tx; + + uint64_t li_rx; + bbl_rate_s rate_li_rx; } stats; struct timer_ *tx_job; @@ -373,6 +377,8 @@ typedef struct bbl_ctx_ dict *session_dict; /* hashtable for sessions */ dict *l2tp_session_dict; /* hashtable for L2TP sessions */ + dict *li_flow_dict; /* hashtable for LI flows */ + uint16_t next_tunnel_id; uint64_t flow_id; diff --git a/src/bbl_ctrl.c b/src/bbl_ctrl.c index dc2f9b61..16d8e617 100644 --- a/src/bbl_ctrl.c +++ b/src/bbl_ctrl.c @@ -650,6 +650,46 @@ bbl_ctrl_session_ip6cp_close(int fd, bbl_ctx_s *ctx, session_key_t *key, json_t* return bbl_ctrl_session_ncp_open_close(fd, ctx, key, false, false); } +ssize_t +bbl_ctrl_li_flows(int fd, bbl_ctx_s *ctx, session_key_t *key __attribute__((unused)), json_t* arguments __attribute__((unused))) { + ssize_t result = 0; + json_t *root, *flows, *flow; + bbl_li_flow_t *li_flow; + struct dict_itor *itor; + + flows = json_array(); + itor = dict_itor_new(ctx->li_flow_dict); + dict_itor_first(itor); + for (; dict_itor_valid(itor); dict_itor_next(itor)) { + li_flow = (bbl_li_flow_t*)*dict_itor_datum(itor); + if(li_flow) { + flow = json_pack("{si si si si si si si si si si}", + "direction", li_flow->direction, + "packet-type", li_flow->packet_type, + "sub-packet-type", li_flow->sub_packet_type, + "liid", li_flow->liid, + "bytes-rx", li_flow->bytes_rx, + "packets-rx", li_flow->packets_rx, + "packets-rx-ipv4", li_flow->packets_rx_ipv4, + "packets-rx-ipv4-tcp", li_flow->packets_rx_ipv4_tcp, + "packets-rx-ipv4-udp", li_flow->packets_rx_ipv4_udp, + "packets-rx-ipv4-host-internal", li_flow->packets_rx_ipv4_internal); + json_array_append(flows, flow); + } + } + root = json_pack("{ss si so}", + "status", "ok", + "code", 200, + "li-flows", flows); + if(root) { + result = json_dumpfd(root, fd, 0); + json_decref(root); + } else { + bbl_ctrl_status(fd, "error", 500, "internal error"); + json_decref(flows); + } + return result; +} struct action { char *name; callback_function *fn; @@ -673,6 +713,7 @@ struct action actions[] = { {"igmp-join", bbl_ctrl_igmp_join}, {"igmp-leave", bbl_ctrl_igmp_leave}, {"igmp-info", bbl_ctrl_igmp_info}, + {"li-flows", bbl_ctrl_li_flows}, {NULL, NULL}, }; diff --git a/src/bbl_interactive.c b/src/bbl_interactive.c index 1ba5051e..f6c47491 100644 --- a/src/bbl_interactive.c +++ b/src/bbl_interactive.c @@ -178,6 +178,12 @@ bbl_stats_job (timer_s *timer) } if (ctx->op.network_if) { + if(dict_count(ctx->li_flow_dict)) { + wprintw(stats_win, "\nLI Statistics\n"); + wprintw(stats_win, " Flows %10lu\n", dict_count(ctx->li_flow_dict)); + wprintw(stats_win, " Rx Packets %10lu (%7lu PPS)\n", + ctx->op.network_if->stats.li_rx, ctx->op.network_if->stats.rate_li_rx.avg); + } if(ctx->config.l2tp_server) { wprintw(stats_win, "\nL2TP LNS Statistics\n"); wprintw(stats_win, " Tunnels %10lu\n", ctx->l2tp_tunnels); diff --git a/src/bbl_li.c b/src/bbl_li.c new file mode 100644 index 00000000..e7ecf7cb --- /dev/null +++ b/src/bbl_li.c @@ -0,0 +1,85 @@ +/* + * BNG Blaster (BBL) - LI Functions + * + * Christian Giese, February 2021 + * + * Copyright (C) 2020-2021, RtBrick, Inc. + */ + +#include "bbl.h" +#include "bbl_logging.h" + +/** + * bbl_l2tp_handler_rx + * + * This function handles all received L2TPv2 traffic. + * + * @param eth Received ethernet packet. + * @param l2tp L2TP header of received ethernet packet. + * @param interface Receiving interface. + */ +void +bbl_qmx_li_handler_rx(bbl_ethernet_header_t *eth, bbl_qmx_li_t *qmx_li, bbl_interface_s *interface) { + bbl_ctx_s *ctx = interface->ctx; + bbl_ipv4_t *ipv4 = (bbl_ipv4_t*)eth->next; + bbl_li_flow_t *li_flow; + + bbl_ethernet_header_t *inner_eth; + bbl_pppoe_session_t *inner_pppoe; + bbl_ipv4_t *inner_ipv4 = NULL; + + dict_insert_result result; + void **search = NULL; + + UNUSED(eth); + + search = dict_search(ctx->li_flow_dict, &qmx_li->header); + if(search) { + li_flow = *search; + } else { + /* New flow ... */ + li_flow = calloc(1, sizeof(bbl_li_flow_t)); + li_flow->direction = qmx_li->direction; + li_flow->packet_type = qmx_li->packet_type; + li_flow->sub_packet_type = qmx_li->sub_packet_type; + li_flow->liid = qmx_li->liid; + result = dict_insert(ctx->li_flow_dict, &qmx_li->header); + if (!result.inserted) { + free(li_flow); + return; + } + *result.datum_ptr = li_flow; + } + + interface->stats.li_rx++; + li_flow->packets_rx++; + li_flow->bytes_rx += qmx_li->payload_len; + + inner_eth = (bbl_ethernet_header_t*)qmx_li->next; + if(inner_eth->type == ETH_TYPE_PPPOE_SESSION) { + inner_pppoe = (bbl_pppoe_session_t*)inner_eth->next; + if(inner_pppoe->protocol == PROTOCOL_IPV4) { + inner_ipv4 = (bbl_ipv4_t*)inner_pppoe->next; + + } + } else if(inner_eth->type == ETH_TYPE_IPV4) { + inner_ipv4 = (bbl_ipv4_t*)eth->next; + } + + if(inner_ipv4) { + li_flow->packets_rx_ipv4++; + switch(ipv4->protocol) { + case PROTOCOL_IPV4_TCP: + li_flow->packets_rx_ipv4_tcp++; + break; + case PROTOCOL_IPV4_UDP: + li_flow->packets_rx_ipv4_udp++; + break; + case PROTOCOL_IPV4_INTERNAL: + li_flow->packets_rx_ipv4_internal++; + break; + default: + break; + } + } +} diff --git a/src/bbl_li.h b/src/bbl_li.h new file mode 100644 index 00000000..e7de936e --- /dev/null +++ b/src/bbl_li.h @@ -0,0 +1,32 @@ +/* + * BNG Blaster (BBL) - LI Functions + * + * Christian Giese, February 2021 + * + * Copyright (C) 2020-2021, RtBrick, Inc. + */ + +#ifndef __BBL_LI_H__ +#define __BBL_LI_H__ + +typedef struct bbl_interface_ bbl_interface_s; + +typedef struct bbl_li_flow_ +{ + uint8_t direction; + uint8_t packet_type; + uint8_t sub_packet_type; + uint32_t liid; + + uint64_t packets_rx; + uint64_t bytes_rx; + uint64_t packets_rx_ipv4; + uint64_t packets_rx_ipv4_tcp; + uint64_t packets_rx_ipv4_udp; + uint64_t packets_rx_ipv4_internal; +} bbl_li_flow_t; + + +void bbl_qmx_li_handler_rx(bbl_ethernet_header_t *eth, bbl_qmx_li_t *qmx_li, bbl_interface_s *interface); + +#endif \ No newline at end of file diff --git a/src/bbl_protocols.c b/src/bbl_protocols.c index 68a16e99..835cb65e 100644 --- a/src/bbl_protocols.c +++ b/src/bbl_protocols.c @@ -1422,6 +1422,30 @@ decode_bbl(uint8_t *buf, uint len, return PROTOCOL_SUCCESS; } +protocol_error_t +decode_qmx_li(uint8_t *buf, uint len, + uint8_t *sp, uint sp_len, + bbl_qmx_li_t **_qmx_li) { + + bbl_qmx_li_t *qmx_li; + + if(len < 4 || sp_len < sizeof(bbl_qmx_li_t)) { + return DECODE_ERROR; + } + /* Init QMX LI header */ + qmx_li = (bbl_qmx_li_t*)sp; BUMP_BUFFER(sp, sp_len, sizeof(bbl_qmx_li_t)); + qmx_li->header = *(uint32_t*)buf; + qmx_li->direction = (*buf >> 5) & 0x7; + qmx_li->packet_type = (*buf >> 1) & 0xf; + qmx_li->sub_packet_type =(*(uint16_t*)buf >> 6) & 0x0f; + qmx_li->liid =*(uint32_t*)buf & 0x3FFFFF; + BUMP_BUFFER(buf, len, sizeof(uint32_t)); + qmx_li->payload = buf; + qmx_li->payload_len = len; + *_qmx_li = qmx_li; + return decode_ethernet(buf, len, sp, sp_len, (bbl_ethernet_header_t**)&qmx_li->next); +} + protocol_error_t decode_udp(uint8_t *buf, uint len, uint8_t *sp, uint sp_len, @@ -1467,8 +1491,17 @@ decode_udp(uint8_t *buf, uint len, udp->protocol = UDP_PROTOCOL_L2TP; ret_val = decode_l2tp(buf, len, sp, sp_len, (bbl_l2tp_t**)&udp->next); break; + case QMX_LI_UDP_PORT: + udp->protocol = UDP_PROTOCOL_QMX_LI; + ret_val = decode_qmx_li(buf, len, sp, sp_len, (bbl_qmx_li_t**)&udp->next); + break; default: - udp->next = NULL; + if(udp->src == QMX_LI_UDP_PORT) { + udp->protocol = UDP_PROTOCOL_QMX_LI; + ret_val = decode_qmx_li(buf, len, sp, sp_len, (bbl_qmx_li_t**)&udp->next); + } else { + udp->next = NULL; + } break; } diff --git a/src/bbl_protocols.h b/src/bbl_protocols.h index 9cdddf8f..686048bd 100644 --- a/src/bbl_protocols.h +++ b/src/bbl_protocols.h @@ -65,6 +65,7 @@ #define PROTOCOL_IPV4_IGMP 0x02 #define PROTOCOL_IPV4_TCP 0x06 #define PROTOCOL_IPV4_UDP 0x11 +#define PROTOCOL_IPV4_INTERNAL 0x3D #define ICMP_TYPE_ECHO_REPLY 0x00 #define ICMP_TYPE_ECHO_REQUEST 0x08 @@ -134,6 +135,7 @@ #define UDP_PROTOCOL_DHCPV6 1 #define UDP_PROTOCOL_BBL 2 #define UDP_PROTOCOL_L2TP 3 +#define UDP_PROTOCOL_QMX_LI 4 #define IPV6_NEXT_HEADER_UDP 17 #define IPV6_NEXT_HEADER_ICMPV6 58 @@ -175,6 +177,7 @@ #define L2TP_NH_TYPE_VALUE 18 +#define QMX_LI_UDP_PORT 49152 #define MAX_VLANS 3 @@ -580,6 +583,17 @@ typedef struct bbl_bbl_ { uint64_t timestamp; } bbl_bbl_t; +typedef struct bbl_qmx_li_ { + uint32_t header; + uint8_t direction; + uint8_t packet_type; + uint8_t sub_packet_type; + uint32_t liid; + void *next; // next header + void *payload; // LI payload + uint16_t payload_len; // LI payload length +} bbl_qmx_li_t; + /* * decode_ethernet */ diff --git a/src/bbl_rx.c b/src/bbl_rx.c index d5660d20..fe915b88 100644 --- a/src/bbl_rx.c +++ b/src/bbl_rx.c @@ -1793,6 +1793,8 @@ bbl_rx_handler_network(bbl_ethernet_header_t *eth, bbl_interface_s *interface) { udp = (bbl_udp_t*)ipv4->next; if(udp->protocol == UDP_PROTOCOL_BBL) { bbl = (bbl_bbl_t*)udp->next; + } else if(udp->protocol == UDP_PROTOCOL_QMX_LI) { + return bbl_qmx_li_handler_rx(eth, (bbl_qmx_li_t*)udp->next, interface); } else if(udp->protocol == UDP_PROTOCOL_L2TP) { return bbl_l2tp_handler_rx(eth, (bbl_l2tp_t*)udp->next, interface); } diff --git a/src/bbl_stats.c b/src/bbl_stats.c index 8c097480..c87e4315 100644 --- a/src/bbl_stats.c +++ b/src/bbl_stats.c @@ -165,6 +165,11 @@ bbl_stats_stdout (bbl_ctx_s *ctx, bbl_stats_t * stats) { printf("Flapped: %u\n", ctx->sessions_flapped); if(ctx->op.network_if) { + if(dict_count(ctx->li_flow_dict)) { + printf("\nLI Statistics:\n"); + printf(" Flows: %10lu\n", dict_count(ctx->li_flow_dict)); + printf(" RX Packets: %10lu\n", ctx->op.network_if->stats.li_rx); + } if(ctx->config.l2tp_server) { printf("\nL2TP LNS Statistics:\n"); printf(" Tunnels: %10u\n", ctx->l2tp_tunnels_max); @@ -312,6 +317,7 @@ bbl_stats_json (bbl_ctx_s *ctx, bbl_stats_t * stats) { json_t *jobj_access_if = NULL; json_t *jobj_network_if = NULL; json_t *jobj_l2tp = NULL; + json_t *jobj_li = NULL; json_t *jobj_straffic = NULL; json_t *jobj_multicast = NULL; json_t *jobj_protocols = NULL; @@ -335,6 +341,12 @@ bbl_stats_json (bbl_ctx_s *ctx, bbl_stats_t * stats) { jobj_array = json_array(); if (ctx->op.network_if) { + if(dict_count(ctx->li_flow_dict)) { + jobj_li = json_object(); + json_object_set(jobj_li, "flows", json_integer(dict_count(ctx->li_flow_dict))); + json_object_set(jobj_li, "rx-packets", json_integer(ctx->op.network_if->stats.li_rx)); + json_object_set(jobj, "li-statistics", jobj_li); + } if(ctx->config.l2tp_server) { jobj_l2tp = json_object(); json_object_set(jobj_l2tp, "tunnels", json_integer(ctx->l2tp_tunnels_max)); @@ -505,6 +517,8 @@ bbl_compute_avg_rate (bbl_rate_s *rate, uint64_t current_value) uint idx, div; uint64_t sum; + if(current_value == 0) return; + rate->diff_value[rate->cursor] = current_value - rate->last_value; sum = 0; @@ -547,5 +561,6 @@ bbl_compute_interface_rate_job (timer_s *timer) bbl_compute_avg_rate(&interface->stats.rate_mc_tx, interface->stats.mc_tx); bbl_compute_avg_rate(&interface->stats.rate_l2tp_data_rx, interface->stats.l2tp_data_rx); bbl_compute_avg_rate(&interface->stats.rate_l2tp_data_tx, interface->stats.l2tp_data_tx); + bbl_compute_avg_rate(&interface->stats.rate_li_rx, interface->stats.li_rx); } }