Skip to content

Commit 83d8577

Browse files
authored
Provide an explicit state for the flow classification process (#2942)
Application should keep calling nDPI until flow state became `NDPI_STATE_CLASSIFIED`. The main loop in the application is simplified to something like: ``` res = ndpi_detection_process_packet(...); if(res->state == NDPI_STATE_CLASSIFIED) { /* Done: you can get finale classification and all metadata. nDPI doesn't need more packets for this flow */ } else { /* nDPI needs more packets for this flow. The provided classification is not final and more metadata might be extracted. If `res->state` is `NDPI_STATE_PARTIAL`, partial/initial classification is available in `res->proto` as usual but it can be updated later. */ } /* Example A (QUIC flow): pkt 1: proto QUIC state NDPI_STATE_PARTIAL pkt 2: proto QUIC/Youtube state NDPI_STATE_CLASSIFIED Example B (GoogleMeet call): pkt 1: proto STUN state NDPI_STATE_PARTIAL pkt N: proto DTLS state NDPI_STATE_PARTIAL pkt N+M: proto DTLS/GoogleCall state NDPI_STATE_CLASSIFIED Example C (standard TLS flow): pkt 1: proto Unknown state NDPI_STATE_INSPECTING pkt 2: proto Unknown state NDPI_STATE_INSPECTING pkt 3: proto Unknown state NDPI_STATE_INSPECTING pkt 4: proto TLS/Facebook state NDPI_STATE_PARTIAL pkt N: proto TLS/Facebook state NDPI_STATE_CLASSIFIED */ } ``` You can take a look at `ndpiReader` for a slightly more complex example. API changes: * remove the third parameter from `ndpi_detection_giveup()`. If you need to know if the classification flow has been guessed, you can access `flow->protocol_was_guessed` * remove `ndpi_extra_dissection_possible()` * change some prototypes from accepting `ndpi_protocol foo` to `ndpi_master_app_protocol bar`. The update is trivial: from `foo` to `foo.proto`
1 parent 6ab3389 commit 83d8577

File tree

14 files changed

+141
-168
lines changed

14 files changed

+141
-168
lines changed

example/ndpiReader.c

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ void ndpiCheckHostStringMatch(char *testChar) {
563563
detected_protocol.proto.app_protocol,
564564
detected_protocol.breed,
565565
detected_protocol.category,
566-
ndpi_protocol2name(ndpi_str, detected_protocol, appBufStr,
566+
ndpi_protocol2name(ndpi_str, detected_protocol.proto, appBufStr,
567567
sizeof(appBufStr)),
568568
ndpi_get_proto_breed_name(detected_protocol.breed),
569569
ndpi_category_get_name(ndpi_str, detected_protocol.category));
@@ -640,7 +640,7 @@ void ndpiCheckHostsFileStringMatch(const char *domains_file) {
640640

641641
printf("Domain [%s] -> %s %s %s\n",
642642
line,
643-
ndpi_protocol2name(ndpi_str, detected_protocol, appBufStr,
643+
ndpi_protocol2name(ndpi_str, detected_protocol.proto, appBufStr,
644644
sizeof(appBufStr)),
645645
ndpi_get_proto_breed_name(detected_protocol.breed),
646646
ndpi_category_get_name(ndpi_str, detected_protocol.category));
@@ -725,7 +725,7 @@ static void ndpiCheckIPMatch(char *testChar) {
725725
memset(&detected_protocol, 0, sizeof(ndpi_protocol));
726726
detected_protocol.proto.app_protocol = ndpi_map_ndpi_id_to_user_proto_id(ndpi_str, ret);
727727

728-
ndpi_protocol2name(ndpi_str, detected_protocol, appBufStr,
728+
ndpi_protocol2name(ndpi_str, detected_protocol.proto, appBufStr,
729729
sizeof(appBufStr));
730730

731731
printf("Match Found for IP %s, port %d -> %s (%d)\n",
@@ -1947,11 +1947,11 @@ static void printFlow(u_int32_t id, struct ndpi_flow_info *flow, u_int16_t threa
19471947
);
19481948

19491949
fprintf(csv_fp, "%s|",
1950-
ndpi_protocol2id(flow->detected_protocol, buf, sizeof(buf)));
1950+
ndpi_protocol2id(flow->detected_protocol.proto, buf, sizeof(buf)));
19511951

19521952
fprintf(csv_fp, "%s|%s|%s|%s|",
19531953
ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct,
1954-
flow->detected_protocol, buf, sizeof(buf)),
1954+
flow->detected_protocol.proto, buf, sizeof(buf)),
19551955
ndpi_stack2str(ndpi_thread_info[thread_id].workflow->ndpi_struct,
19561956
&flow->detected_protocol.protocol_stack, buf2, sizeof(buf2)),
19571957
ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct,
@@ -2090,7 +2090,7 @@ static void printFlow(u_int32_t id, struct ndpi_flow_info *flow, u_int16_t threa
20902090
flow->detected_protocol.proto.master_protocol,
20912091
flow->detected_protocol.proto.app_protocol,
20922092
ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct,
2093-
flow->detected_protocol, buf1, sizeof(buf1))
2093+
flow->detected_protocol.proto, buf1, sizeof(buf1))
20942094
);
20952095
}
20962096
}
@@ -2132,14 +2132,14 @@ static void printFlow(u_int32_t id, struct ndpi_flow_info *flow, u_int16_t threa
21322132

21332133
#ifdef NDPI_EXTENDED_SANITY_CHECKS
21342134
/* Be sure new stack logic is compatible with legacy code */
2135-
assert(ndpi_stack_get_upper_proto(&flow->detected_protocol.protocol_stack) == ndpi_get_upper_proto(flow->detected_protocol));
2136-
assert(ndpi_stack_get_lower_proto(&flow->detected_protocol.protocol_stack) == ndpi_get_lower_proto(flow->detected_protocol));
2135+
assert(ndpi_stack_get_upper_proto(&flow->detected_protocol.protocol_stack) == ndpi_get_upper_proto(flow->detected_protocol.proto));
2136+
assert(ndpi_stack_get_lower_proto(&flow->detected_protocol.protocol_stack) == ndpi_get_lower_proto(flow->detected_protocol.proto));
21372137
#endif
21382138

21392139
fprintf(out, "%s/%s][Stack: %s][IP: %u/%s]",
2140-
ndpi_protocol2id(flow->detected_protocol, buf, sizeof(buf)),
2140+
ndpi_protocol2id(flow->detected_protocol.proto, buf, sizeof(buf)),
21412141
ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct,
2142-
flow->detected_protocol, buf1, sizeof(buf1)),
2142+
flow->detected_protocol.proto, buf1, sizeof(buf1)),
21432143
ndpi_stack2str(ndpi_thread_info[thread_id].workflow->ndpi_struct,
21442144
&flow->detected_protocol.protocol_stack, buf2, sizeof(buf2)),
21452145
flow->detected_protocol.protocol_by_ip,
@@ -2173,7 +2173,7 @@ static void printFlow(u_int32_t id, struct ndpi_flow_info *flow, u_int16_t threa
21732173

21742174
fprintf(out, "[%s]",
21752175
ndpi_is_encrypted_proto(ndpi_thread_info[thread_id].workflow->ndpi_struct,
2176-
flow->detected_protocol) ? "Encrypted" : "ClearText");
2176+
flow->detected_protocol.proto) ? "Encrypted" : "ClearText");
21772177

21782178
fprintf(out, "[Confidence: %s]", ndpi_confidence_get_name(flow->confidence));
21792179

@@ -2748,14 +2748,13 @@ static void node_proto_guess_walker(const void *node, ndpi_VISIT which, int dept
27482748

27492749
if((which == ndpi_preorder) || (which == ndpi_leaf)) { /* Avoid walking the same node multiple times */
27502750
if((!flow->detection_completed) && flow->ndpi_flow) {
2751-
u_int8_t proto_guessed;
27522751

27532752
malloc_size_stats = 1;
27542753
flow->detected_protocol = ndpi_detection_giveup(ndpi_thread_info[thread_id].workflow->ndpi_struct,
2755-
flow->ndpi_flow, &proto_guessed);
2754+
flow->ndpi_flow);
27562755
malloc_size_stats = 0;
27572756

2758-
if(proto_guessed) ndpi_thread_info[thread_id].workflow->stats.guessed_flow_protocols++;
2757+
if(flow->ndpi_flow->protocol_was_guessed) ndpi_thread_info[thread_id].workflow->stats.guessed_flow_protocols++;
27592758
}
27602759

27612760
process_ndpi_collected_info(ndpi_thread_info[thread_id].workflow, flow);
@@ -3157,7 +3156,7 @@ static void port_stats_walker(const void *node, ndpi_VISIT which, int depth, voi
31573156
/* get app level protocol */
31583157
if(flow->detected_protocol.proto.master_protocol) {
31593158
ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct,
3160-
flow->detected_protocol, proto, sizeof(proto));
3159+
flow->detected_protocol.proto, proto, sizeof(proto));
31613160
} else {
31623161
strncpy(proto, ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct,
31633162
flow->detected_protocol.proto.app_protocol),sizeof(proto) - 1);
@@ -3265,7 +3264,7 @@ static void dump_realtime_protocol(struct ndpi_workflow * workflow, struct ndpi_
32653264
snprintf(dstip, sizeof(dstip), "[%s]", flow->dst_name ? flow->dst_name : "");
32663265
}
32673266

3268-
ndpi_protocol2name(workflow->ndpi_struct, flow->detected_protocol, app_name, sizeof(app_name));
3267+
ndpi_protocol2name(workflow->ndpi_struct, flow->detected_protocol.proto, app_name, sizeof(app_name));
32693268

32703269
if (ret == 1) {
32713270
fprintf(out, "Detected Realtime protocol %s --> [%s] %s:%d <--> %s:%d app=%s <%s>\n",
@@ -4114,7 +4113,7 @@ static void printFlowsStats() {
41144113
fprintf(out, "\t%u\t%-10s\t%s:%u <-> %s:%u\t[",
41154114
i,
41164115
ndpi_protocol2name(ndpi_thread_info[0].workflow->ndpi_struct,
4117-
all_flows[i].flow->detected_protocol, buf, sizeof(buf)),
4116+
all_flows[i].flow->detected_protocol.proto, buf, sizeof(buf)),
41184117
all_flows[i].flow->src_name ? all_flows[i].flow->src_name : "",
41194118
ntohs(all_flows[i].flow->src_port),
41204119
all_flows[i].flow->dst_name ? all_flows[i].flow->dst_name : "",
@@ -5113,7 +5112,7 @@ static void ndpi_process_packet(u_char *args,
51135112
}
51145113
trailer->flow_risk_info[sizeof(trailer->flow_risk_info) - 1] = '\0';
51155114
trailer->proto.master_protocol = htons(p.proto.master_protocol), trailer->proto.app_protocol = htons(p.proto.app_protocol);
5116-
ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct, p, trailer->name, sizeof(trailer->name));
5115+
ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct, p.proto, trailer->name, sizeof(trailer->name));
51175116

51185117
/* Metadata */
51195118
/* Metadata are (all) available in `flow` only after nDPI completed its work!

example/ndpiSimpleIntegration.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -876,12 +876,10 @@ static void ndpi_process_packet(uint8_t * const args,
876876
return;
877877
} else if (flow_to_process->ndpi_flow->num_processed_pkts == 0xFE) {
878878
/* last chance to guess something, better then nothing */
879-
uint8_t protocol_was_guessed = 0;
880879
flow_to_process->guessed_protocol =
881880
ndpi_detection_giveup(workflow->ndpi_struct,
882-
flow_to_process->ndpi_flow,
883-
&protocol_was_guessed);
884-
if (protocol_was_guessed != 0) {
881+
flow_to_process->ndpi_flow);
882+
if (flow_to_process->ndpi_flow->protocol_was_guessed != 0) {
885883
printf("[%8llu, %d, %4d][GUESSED] protocol: %s | app protocol: %s | category: %s\n",
886884
workflow->packets_captured,
887885
reader_thread->array_index,

example/reader_util.c

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,7 +1118,7 @@ static void dump_flow_fingerprint(struct ndpi_workflow * workflow,
11181118
ndpi_serialize_string_uint32(&serializer, "srv_port", ntohs(flow->dst_port));
11191119
ndpi_serialize_string_string(&serializer, "proto",
11201120
ndpi_protocol2name(workflow->ndpi_struct,
1121-
flow->detected_protocol,
1121+
flow->detected_protocol.proto,
11221122
buf, sizeof(buf)));
11231123

11241124
if(flow->server_hostname)
@@ -1165,7 +1165,7 @@ static void process_ndpi_monitoring_info(struct ndpi_flow_info *flow) {
11651165
return;
11661166

11671167
if(flow->monitoring_state == 0 &&
1168-
flow->ndpi_flow->monitoring) {
1168+
flow->ndpi_flow->state == NDPI_STATE_MONITORING) {
11691169
/* We just moved to monitoring state */
11701170
flow->monitoring_state = 1;
11711171
flow->num_packets_before_monitoring = flow->ndpi_flow->packet_direction_complete_counter[0] + flow->ndpi_flow->packet_direction_complete_counter[1];
@@ -1680,7 +1680,7 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl
16801680
ndpi_serialize_string_uint32(&flow->ndpi_flow_serializer, "detection_completed", flow->detection_completed);
16811681
ndpi_serialize_string_uint32(&flow->ndpi_flow_serializer, "check_extra_packets", flow->check_extra_packets);
16821682

1683-
if(flow->ndpi_flow->monitoring) {
1683+
if(flow->ndpi_flow->state == NDPI_STATE_MONITORING) {
16841684
serialize_monitoring_metadata(flow);
16851685
}
16861686

@@ -1994,26 +1994,19 @@ static struct ndpi_proto packet_processing(struct ndpi_workflow * workflow,
19941994
ipsize, time_ms, &input_info);
19951995
if(monitoring_enabled)
19961996
process_ndpi_monitoring_info(flow);
1997-
enough_packets |= ndpi_flow->fail_with_unknown;
1998-
if(enough_packets || (flow->detected_protocol.proto.app_protocol != NDPI_PROTOCOL_UNKNOWN)) {
1999-
if((!enough_packets)
2000-
&& ndpi_extra_dissection_possible(workflow->ndpi_struct, ndpi_flow))
2001-
; /* Wait for further metadata */
2002-
else {
2003-
/* New protocol detected or give up */
2004-
flow->detection_completed = 1;
2005-
2006-
if(flow->detected_protocol.proto.app_protocol == NDPI_PROTOCOL_UNKNOWN) {
2007-
u_int8_t proto_guessed;
2008-
2009-
flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct, flow->ndpi_flow,
2010-
&proto_guessed);
2011-
if(proto_guessed) workflow->stats.guessed_flow_protocols++;
2012-
}
1997+
if(flow->detected_protocol.state == NDPI_STATE_CLASSIFIED ||
1998+
enough_packets) {
1999+
2000+
flow->detection_completed = 1;
20132001

2014-
process_ndpi_collected_info(workflow, flow);
2002+
if(flow->detected_protocol.state != NDPI_STATE_CLASSIFIED) {
2003+
flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct, flow->ndpi_flow);
20152004
}
2005+
2006+
if(flow->ndpi_flow->protocol_was_guessed) workflow->stats.guessed_flow_protocols++;
2007+
process_ndpi_collected_info(workflow, flow);
20162008
}
2009+
20172010
/* Let's try to save client-server direction */
20182011
flow->current_pkt_from_client_to_server = input_info.in_pkt_dir;
20192012

fuzz/fuzz_config.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
1212
FuzzedDataProvider fuzzed_data(data, size);
1313
struct ndpi_detection_module_struct *ndpi_info_mod;
1414
struct ndpi_flow_struct flow;
15-
u_int8_t protocol_was_guessed, unused;
15+
u_int8_t unused;
1616
u_int32_t i, ret;
1717
u_int16_t bool_value;
1818
struct ndpi_lru_cache_stats lru_stats;
@@ -833,15 +833,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
833833
ndpi_detection_get_l4(pkt.data(), pkt.size(), &l4_return, &l4_len_return, &l4_protocol_return, NDPI_DETECTION_ONLY_IPV4);
834834

835835
ndpi_detection_process_packet(ndpi_info_mod, &flow, pkt.data(), pkt.size(), 0, &input_info);
836-
p = ndpi_detection_giveup(ndpi_info_mod, &flow, &protocol_was_guessed);
836+
p = ndpi_detection_giveup(ndpi_info_mod, &flow);
837837

838838
assert(p.proto.master_protocol == ndpi_get_flow_masterprotocol(&flow));
839839
assert(p.proto.app_protocol == ndpi_get_flow_appprotocol(&flow));
840840
assert(p.category == ndpi_get_flow_category(&flow));
841841
ndpi_is_master_only_protocol(ndpi_info_mod, p.proto.app_protocol);
842842
ndpi_normalize_protocol(ndpi_info_mod, &p.proto);
843-
assert(ndpi_stack_get_upper_proto(&p.protocol_stack) == ndpi_get_upper_proto(p));
844-
assert(ndpi_stack_get_lower_proto(&p.protocol_stack) == ndpi_get_lower_proto(p));
843+
assert(ndpi_stack_get_upper_proto(&p.protocol_stack) == ndpi_get_upper_proto(p.proto));
844+
assert(ndpi_stack_get_lower_proto(&p.protocol_stack) == ndpi_get_lower_proto(p.proto));
845845
ndpi_get_flow_error_code(&flow);
846846
ndpi_get_flow_risk_info(&flow, out, sizeof(out), 1);
847847
ndpi_get_flow_ndpi_proto(&flow, &p2);

fuzz/fuzz_ndpi_reader.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,8 @@ static void node_cleanup_walker(const void *node, ndpi_VISIT which, int depth, v
4747

4848
if((which == ndpi_preorder) || (which == ndpi_leaf)) { /* Avoid walking the same node multiple times */
4949
if((!flow->detection_completed) && flow->ndpi_flow) {
50-
u_int8_t proto_guessed;
51-
5250
flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct,
53-
flow->ndpi_flow, &proto_guessed);
51+
flow->ndpi_flow);
5452
}
5553

5654
process_ndpi_collected_info(workflow, flow);

fuzz/fuzz_process_packet.c

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ static ndpi_serializer json_serializer = {};
1010
static ndpi_serializer csv_serializer = {};
1111

1212
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
13-
uint8_t protocol_was_guessed;
14-
1513
if (ndpi_info_mod == NULL) {
1614
fuzz_init_detection_module(&ndpi_info_mod, NULL);
1715

@@ -22,19 +20,12 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
2220
memset(&ndpi_flow, 0, SIZEOF_FLOW_STRUCT);
2321
ndpi_protocol detected_protocol =
2422
ndpi_detection_process_packet(ndpi_info_mod, &ndpi_flow, Data, Size, 0, NULL);
25-
ndpi_protocol guessed_protocol =
26-
ndpi_detection_giveup(ndpi_info_mod, &ndpi_flow, &protocol_was_guessed);
23+
detected_protocol = ndpi_detection_giveup(ndpi_info_mod, &ndpi_flow);
2724

2825
ndpi_reset_serializer(&json_serializer);
2926
ndpi_reset_serializer(&csv_serializer);
30-
if (protocol_was_guessed == 0)
31-
{
32-
ndpi_dpi2json(ndpi_info_mod, &ndpi_flow, detected_protocol, &json_serializer);
33-
ndpi_dpi2json(ndpi_info_mod, &ndpi_flow, detected_protocol, &csv_serializer);
34-
} else {
35-
ndpi_dpi2json(ndpi_info_mod, &ndpi_flow, guessed_protocol, &json_serializer);
36-
ndpi_dpi2json(ndpi_info_mod, &ndpi_flow, guessed_protocol, &csv_serializer);
37-
}
27+
ndpi_dpi2json(ndpi_info_mod, &ndpi_flow, detected_protocol, &json_serializer);
28+
ndpi_dpi2json(ndpi_info_mod, &ndpi_flow, detected_protocol, &csv_serializer);
3829
ndpi_free_flow_data(&ndpi_flow);
3930

4031
return 0;

python/ndpi/ndpi.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ def process_packet(self, flow, packet, packet_time_ms, input_info):
5959

6060
def giveup(self, flow):
6161
p = lib.ndpi_detection_giveup(self._detection_module,
62-
flow.C,
63-
ffi.new("uint8_t*", 0))
62+
flow.C)
6463
return ndpi_protocol(C=p,
6564
master_protocol=p.proto.master_protocol,
6665
app_protocol=p.proto.app_protocol,

python/ndpi/ndpi_build.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,10 @@
5656
const u_int64_t packet_time_ms,
5757
struct ndpi_flow_input_info *input_info);
5858
ndpi_protocol ndpi_detection_giveup(struct ndpi_detection_module_struct *ndpi_struct,
59-
struct ndpi_flow_struct *flow,
60-
u_int8_t *protocol_was_guessed);
59+
struct ndpi_flow_struct *flow);
6160
void ndpi_py_setup_detection_module(struct ndpi_detection_module_struct *mod);
6261
struct ndpi_flow_struct * ndpi_py_initialize_flow(void);
63-
char* ndpi_protocol2name(struct ndpi_detection_module_struct *ndpi_mod, ndpi_protocol proto, char *buf, u_int buf_len);
62+
char* ndpi_protocol2name(struct ndpi_detection_module_struct *ndpi_mod, ndpi_master_app_protocol proto, char *buf, u_int buf_len);
6463
const char* ndpi_category_get_name(struct ndpi_detection_module_struct *ndpi_mod, ndpi_protocol_category_t category);
6564
const char* ndpi_confidence_get_name(ndpi_confidence_t confidence);
6665
"""

python/ndpi_example.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ def parse_arguments():
151151
flow.detected_protocol = nDPI.giveup(flow.ndpi_flow) # We try to guess it (port matching, LRU, etc.)
152152
FLOW_EXPORT = FLOW_STR.format(flow.index,
153153
key,
154-
nDPI.protocol_name(flow.detected_protocol),
155-
nDPI.protocol_category_name(flow.detected_protocol),
154+
nDPI.protocol_name(flow.detected_protocol.proto),
155+
nDPI.protocol_category_name(flow.detected_protocol.proto),
156156
flow.ndpi_flow.confidence.name,
157157
flow.pkts,
158158
flow.bytes)

0 commit comments

Comments
 (0)