Skip to content

Commit

Permalink
RTP/STUN: look for STUN packets after RTP/RTCP classification (#2465)
Browse files Browse the repository at this point in the history
After a flow has been classified as RTP or RTCP, nDPI might analyse more
packets to look for STUN/DTLS packets, i.e. to try to tell if this flow
is a "pure" RTP/RTCP flow or if the RTP/RTCP packets are multiplexed with
STUN/DTLS.
Useful for proper (sub)classification when the beginning of the flows
are not captured or if there are lost packets in the the captured traffic.

Disabled by default
  • Loading branch information
IvanNardi authored Jun 7, 2024
1 parent 070a090 commit b90d39c
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 13 deletions.
1 change: 1 addition & 0 deletions doc/configuration_parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@ TODO
| "http" | "process_response" | enable | NULL | NULL | Enable/disable processing of HTTP responses. By default, HTTP flows are usually fully classified after the first request/response pair. If this parameter is disabled, the flows are fully classified after the first request (or after the first response, if the request is missing); in that case, some flow risks are not checked and some metadata are not exported |
| "ookla" | "dpi.aggressiveness", | 0x01 | 0x00 | 0x01 | Detection aggressiveness for Ookla. The value is a bitmask. Values: 0x0 = disabled; 0x01 = enable heuristic for detection over TLS (via Ookla LRU cache) |
| "zoom" | "max_packets_extra_dissection" | 4 | 0 | 255 | After a flow has been classified has Zoom, nDPI might analyse more packets to look for a sub-classification or for metadata. This parameter set the upper limit on the number of these packets |
| "rtp" | "search_for_stun" | disable | NULL | NULL | After a flow has been classified as RTP or RTCP, nDPI might analyse more packets to look for STUN/DTLS packets, i.e. to try to tell if this flow is a "pure" RTP/RTCP flow or if the RTP/RTCP packets are multiplexed with STUN/DTLS. Useful for proper (sub)classification when the beginning of the flows are not captured or if there are lost packets in the the captured traffic. If enabled, nDPI requires more packets to process for each RTP/RTCP flow. |
| $PROTO_NAME | "log" | disable | NULL | NULL | Enable/disable logging/debug for specific protocol. Use "any" as protocol name if you want to easily enable/disable logging/debug for all protocols |
| $PROTO_NAME | "ip_list.load" | 1 | NULL | NULL | Enable/disable loading of internal list of IP addresses (used for (sub)classification) specific to that protocol. Use "any" as protocol name if you want to easily enable/disable all lists. This knob is valid only for the following protocols: Alibaba, Amazon AWS, Apple, Avast, Bloomberg, Cachefly, Cloudflare, Discord, Disney+, Dropbox, Edgecast, EpicGames, Ethereum, Facebook, Github, Google, Google Cloud, GoTo, Hotspot Shield, Hulu, Line, Microsoft 365, Microsoft Azure, Microsoft One Drive, Microsoft Outlook, Mullvad, Netflix, Nvidia, OpenDNS, ProtonVPN, RiotGames, Roblox, Skype/Teams, Starcraft, Steam, Teamviewer, Telegram, Tencent, Threema, TOR, Twitch, Twitter, UbuntuONE, VK, Yandex, Yandex Cloud, Webex, Whatsapp, Zoom |
5 changes: 5 additions & 0 deletions fuzz/fuzz_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
snprintf(cfg_value, sizeof(cfg_value), "%d", value);
ndpi_set_config(ndpi_info_mod, "zoom", "max_packets_extra_dissection", cfg_value);
}
if(fuzzed_data.ConsumeBool()) {
value = fuzzed_data.ConsumeIntegralInRange(0, 0x01 + 1);
snprintf(cfg_value, sizeof(cfg_value), "%d", value);
ndpi_set_config(ndpi_info_mod, "rtp", "search_for_stun", cfg_value);
}
if(fuzzed_data.ConsumeBool()) {
value = fuzzed_data.ConsumeIntegralInRange(0, 1 + 1);
snprintf(cfg_value, sizeof(cfg_value), "%d", value);
Expand Down
1 change: 1 addition & 0 deletions fuzz/fuzz_ndpi_reader.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
ndpi_set_config(workflow->ndpi_struct, NULL, "tcp_ack_payload_heuristic", "1");
ndpi_set_config(workflow->ndpi_struct, "tls", "application_blocks_tracking", "1");
ndpi_set_config(workflow->ndpi_struct, "stun", "max_packets_extra_dissection", "255");
ndpi_set_config(workflow->ndpi_struct, "rtp", "search_for_stun", "1");

ndpi_finalize_initialization(workflow->ndpi_struct);

Expand Down
3 changes: 3 additions & 0 deletions src/include/ndpi_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ struct ndpi_detection_module_config_struct {

int zoom_max_packets_extra_dissection;

int rtp_search_for_stun;

NDPI_PROTOCOL_BITMASK debug_bitmask;
NDPI_PROTOCOL_BITMASK ip_list_bitmask;

Expand Down Expand Up @@ -666,6 +668,7 @@ int stun_search_into_zoom_cache(struct ndpi_detection_module_struct *ndpi_struct
int is_stun(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow,
u_int16_t *app_proto);
void switch_extra_dissection_to_stun(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow);

/* TPKT */
int tpkt_verify_hdr(const struct ndpi_packet_struct * const packet);
Expand Down
2 changes: 2 additions & 0 deletions src/lib/ndpi_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -11174,6 +11174,8 @@ static const struct cfg_param {

{ "zoom", "max_packets_extra_dissection", "4", "0", "255", CFG_PARAM_INT, __OFF(zoom_max_packets_extra_dissection), NULL },

{ "rtp", "search_for_stun", "disable", NULL, NULL, CFG_PARAM_ENABLE_DISABLE, __OFF(rtp_search_for_stun), NULL },

{ "$PROTO_NAME_OR_ID", "log", "disable", NULL, NULL, CFG_PARAM_PROTOCOL_ENABLE_DISABLE, __OFF(debug_bitmask), NULL },
{ "$PROTO_NAME_OR_ID", "ip_list.load", "1", NULL, NULL, CFG_PARAM_PROTOCOL_ENABLE_DISABLE, __OFF(ip_list_bitmask), NULL },

Expand Down
28 changes: 21 additions & 7 deletions src/lib/protocols/rtp.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,24 @@ int is_rtp_or_rtcp(struct ndpi_detection_module_struct *ndpi_struct,
return NO_RTP_RTCP;
}


static void ndpi_int_rtp_add_connection(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow,
u_int16_t proto)
{
ndpi_set_detected_protocol(ndpi_struct, flow,
NDPI_PROTOCOL_UNKNOWN, proto,
NDPI_CONFIDENCE_DPI);
if(ndpi_struct->cfg.rtp_search_for_stun) {
/* It makes sense to look for STUN only if we didn't capture the entire flow,
from the beginning */
if(!(flow->l4_proto == IPPROTO_TCP && ndpi_seen_flow_beginning(flow))) {
NDPI_LOG_DBG(ndpi_struct, "Enabling (STUN) extra dissection\n");
switch_extra_dissection_to_stun(ndpi_struct, flow);
}
}
}

/* *************************************************************** */

static void ndpi_rtp_search(struct ndpi_detection_module_struct *ndpi_struct,
Expand Down Expand Up @@ -186,9 +204,7 @@ static void ndpi_rtp_search(struct ndpi_detection_module_struct *ndpi_struct,
rtp_get_stream_type(payload[1] & 0x7F, &flow->flow_multimedia_type);

NDPI_LOG_INFO(ndpi_struct, "Found RTP\n");
ndpi_set_detected_protocol(ndpi_struct, flow,
NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_RTP,
NDPI_CONFIDENCE_DPI);
ndpi_int_rtp_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_RTP);
}
return;
}
Expand All @@ -202,16 +218,14 @@ static void ndpi_rtp_search(struct ndpi_detection_module_struct *ndpi_struct,
} else if(is_rtp == IS_RTCP && flow->rtp_stage == 0) {
if(flow->rtcp_stage == 3) {
NDPI_LOG_INFO(ndpi_struct, "Found RTCP\n");
ndpi_set_detected_protocol(ndpi_struct, flow,
NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_RTCP,
NDPI_CONFIDENCE_DPI);
ndpi_int_rtp_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_RTCP);
return;
}
flow->rtcp_stage += 1;
} else {
if(flow->rtp_stage || flow->rtcp_stage) {
u_int16_t app_proto; /* unused */
u_int32_t unused;
u_int16_t app_proto = NDPI_PROTOCOL_UNKNOWN;

/* TODO: we should switch to the demultiplexing-code in stun dissector */
if(is_stun(ndpi_struct, flow, &app_proto) != 0 &&
Expand Down
39 changes: 34 additions & 5 deletions src/lib/protocols/stun.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ static int stun_search_again(struct ndpi_detection_module_struct *ndpi_struct,


/* Valid classifications:
* STUN, DTLS, STUN/RTP, DTLS/SRTP
* STUN, DTLS, STUN/RTP, DTLS/SRTP, RTP or RTCP (the last two, only from RTP dissector)
* STUN/APP, DTLS/APP, SRTP/APP ["real" sub-classification]
The idea is:
* the specific "real" application (WA/FB/Signal/...), if present, should
Expand Down Expand Up @@ -807,6 +807,7 @@ static int stun_search_again(struct ndpi_detection_module_struct *ndpi_struct,
rtp_get_stream_type(packet->payload[1] & 0x7F, &flow->flow_multimedia_type);

if(flow->detected_protocol_stack[0] != NDPI_PROTOCOL_RTP &&
flow->detected_protocol_stack[0] != NDPI_PROTOCOL_RTCP &&
flow->detected_protocol_stack[1] != NDPI_PROTOCOL_SRTP) {

if(flow->detected_protocol_stack[1] != NDPI_PROTOCOL_UNKNOWN) {
Expand All @@ -824,6 +825,11 @@ static int stun_search_again(struct ndpi_detection_module_struct *ndpi_struct,
__get_master(flow) == NDPI_PROTOCOL_STUN ? NDPI_PROTOCOL_RTP: NDPI_PROTOCOL_SRTP,
__get_master(flow));
}
} else if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_RTCP &&
flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN) {
/* From RTP dissector; if we have RTP and RTCP multiplexed together (but not STUN, yet) we always
use RTP, as we do in RTP dissector */
ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_RTP, NDPI_CONFIDENCE_DPI);
}
} else if(rtp_rtcp == IS_RTCP) {
NDPI_LOG_DBG(ndpi_struct, "RTCP\n");
Expand Down Expand Up @@ -971,9 +977,22 @@ static void ndpi_int_stun_add_connection(struct ndpi_detection_module_struct *nd
confidence = NDPI_CONFIDENCE_DPI_CACHE;
if(app_proto == NDPI_PROTOCOL_RTP)
master_proto = NDPI_PROTOCOL_SRTP; /* STUN/RTP --> SRTP/APP */
if(master_proto == NDPI_PROTOCOL_RTP || master_proto == NDPI_PROTOCOL_RTCP)
master_proto = NDPI_PROTOCOL_SRTP; /* RTP|RTCP --> SRTP/APP */
app_proto = new_app_proto;
}
}

/* From RTP dissector */
if(master_proto == NDPI_PROTOCOL_RTP || master_proto == NDPI_PROTOCOL_RTCP) {
if(app_proto == NDPI_PROTOCOL_UNKNOWN) {
app_proto = NDPI_PROTOCOL_RTP;
master_proto = NDPI_PROTOCOL_STUN; /* RTP|RTCP ->STUN/RTP */
} else {
master_proto = NDPI_PROTOCOL_SRTP;
}
}

/* Adding only real subclassifications */
if(is_subclassification_real_by_proto(app_proto))
add_to_caches(ndpi_struct, flow, app_proto);
Expand All @@ -991,10 +1010,20 @@ static void ndpi_int_stun_add_connection(struct ndpi_detection_module_struct *nd
}
}

if(!flow->extra_packets_func && keep_extra_dissection(ndpi_struct, flow)) {
NDPI_LOG_DBG(ndpi_struct, "Enabling extra dissection\n");
flow->max_extra_packets_to_check = ndpi_struct->cfg.stun_max_packets_extra_dissection;
flow->extra_packets_func = stun_search_again;
switch_extra_dissection_to_stun(ndpi_struct, flow);
}

/* ************************************************************ */

void switch_extra_dissection_to_stun(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow)
{
if(!flow->extra_packets_func) {
if(keep_extra_dissection(ndpi_struct, flow)) {
NDPI_LOG_DBG(ndpi_struct, "Enabling extra dissection\n");
flow->max_extra_packets_to_check = ndpi_struct->cfg.stun_max_packets_extra_dissection;
flow->extra_packets_func = stun_search_again;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion tests/cfgs/stun_extra_dissection/config.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
--cfg=stun,max_packets_extra_dissection,255 -U 0 -T 0 --cfg=packets_limit_per_flow,255
--cfg=stun,max_packets_extra_dissection,255 -U 0 -T 0 --cfg=packets_limit_per_flow,255 --cfg=rtp,search_for_stun,1
Loading

0 comments on commit b90d39c

Please sign in to comment.