aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--example/ndpiReader.c39
-rw-r--r--example/reader_util.c69
2 files changed, 80 insertions, 28 deletions
diff --git a/example/ndpiReader.c b/example/ndpiReader.c
index ce86c6fcd..b744a8708 100644
--- a/example/ndpiReader.c
+++ b/example/ndpiReader.c
@@ -91,6 +91,10 @@ static ndpi_serialization_format serialization_format = ndpi_serialization_forma
static char* domain_to_check = NULL;
static char* ip_port_to_check = NULL;
static u_int8_t ignore_vlanid = 0;
+
+FILE *fingerprint_fp = NULL; /**< for flow fingerprint export */
+
+
/** User preferences **/
u_int8_t enable_realtime_output = 0, enable_protocol_guess = NDPI_GIVEUP_GUESS_BY_PORT | NDPI_GIVEUP_GUESS_BY_IP, enable_payload_analyzer = 0, num_bin_clusters = 0, extcap_exit = 0;
u_int8_t verbose = 0, enable_flow_stats = 0;
@@ -617,7 +621,7 @@ static void help(u_int long_help) {
"-i <file|device> "
#endif
"[-f <filter>][-s <duration>][-m <duration>][-b <num bin clusters>]\n"
- " [-p <protos>][-l <loops> [-q][-d][-h][-H][-D][-e <len>][-E][-t][-v <level>]\n"
+ " [-p <protos>][-l <loops> [-q][-d][-h][-H][-D][-e <len>][-E <path>][-t][-v <level>]\n"
" [-n <threads>][-w <file>][-c <file>][-C <file>][-j <file>][-x <file>]\n"
" [-r <file>][-R][-j <file>][-S <file>][-T <num>][-U <num>] [-x <domain>]\n"
" [-a <mode>][-B proto_list]\n\n"
@@ -657,6 +661,7 @@ static void help(u_int long_help) {
" | Default: %u:%u:%u:%u:%u\n"
" -c <path> | Load custom categories from the specified file\n"
" -C <path> | Write output in CSV format on the specified file\n"
+ " -E <path> | Write flow fingerprints on the specified file\n"
" -r <path> | Load risky domain file\n"
" -R | Print detected realtime protocols\n"
" -j <path> | Load malicious JA3 fingeprints\n"
@@ -938,6 +943,14 @@ void extcap_capture(int datalink_type) {
/* ********************************** */
+void printFingerprintHeader() {
+ if(!fingerprint_fp) return;
+
+ fprintf(fingerprint_fp, "#protocol|src_ip|dst_ip|dst_port|family|fingerprint\n");
+}
+
+/* ********************************** */
+
void printCSVHeader() {
if(!csv_fp) return;
@@ -1074,7 +1087,7 @@ static void parseOptions(int argc, char **argv) {
#endif
while((opt = getopt_long(argc, argv,
- "a:Ab:B:e:c:C:dDFf:g:G:i:Ij:k:K:S:hHp:pP:l:r:Rs:tu:v:V:n:rp:x:X:w:q0123:456:7:89:m:MT:U:",
+ "a:Ab:B:e:E:c:C:dDFf:g:G:i:Ij:k:K:S:hHp:pP:l:r:Rs:tu:v:V:n:rp:x:X:w:q0123:456:7:89:m:MT:U:",
longopts, &option_idx)) != EOF) {
#ifdef DEBUG_TRACE
if(trace) fprintf(trace, " #### Handling option -%c [%s] #### \n", opt, optarg ? optarg : "");
@@ -1110,6 +1123,19 @@ static void parseOptions(int argc, char **argv) {
human_readeable_string_len = atoi(optarg);
break;
+ case 'E':
+ errno = 0;
+ if((fingerprint_fp = fopen(optarg, "w")) == NULL) {
+ printf("Unable to write on fingerprint file %s: %s\n", optarg, strerror(errno));
+ exit(1);
+ }
+
+ if(reader_add_cfg("tls", "metadata.ja4r_fingerprint", "1", 1) == -1) {
+ printf("Unable to enable JA4r fingerprints\n");
+ exit(1);
+ }
+ break;
+
case 'i':
case '3':
_pcap_file[0] = optarg;
@@ -1433,9 +1459,9 @@ static void parseOptions(int argc, char **argv) {
if(extcap_exit)
exit(0);
- if(csv_fp)
- printCSVHeader();
-
+ printCSVHeader();
+ printFingerprintHeader();
+
#ifndef USE_DPDK
if(do_extcap_capture) {
quiet_mode = 1;
@@ -6463,7 +6489,8 @@ int main(int argc, char **argv) {
if(extcap_dumper) pcap_dump_close(extcap_dumper);
if(extcap_fifo_h) pcap_close(extcap_fifo_h);
if(enable_malloc_bins) ndpi_free_bin(&malloc_bins);
- if(csv_fp) fclose(csv_fp);
+ if(csv_fp) fclose(csv_fp);
+ if(fingerprint_fp) fclose(fingerprint_fp);
ndpi_free(_disabled_protocols);
diff --git a/example/reader_util.c b/example/reader_util.c
index 014574503..d2b60859d 100644
--- a/example/reader_util.c
+++ b/example/reader_util.c
@@ -78,6 +78,7 @@ extern u_int8_t enable_flow_stats, enable_payload_analyzer;
extern u_int8_t verbose, human_readeable_string_len;
extern u_int8_t max_num_udp_dissected_pkts /* 24 */, max_num_tcp_dissected_pkts /* 80 */;
static u_int32_t flow_id = 0;
+extern FILE *fingerprint_fp;
u_int8_t enable_doh_dot_detection = 0;
@@ -462,7 +463,7 @@ static void ndpi_free_flow_tls_data(struct ndpi_flow_info *flow) {
ndpi_free(flow->dhcp_fingerprint);
flow->dhcp_fingerprint = NULL;
}
-
+
if(flow->dhcp_class_ident) {
ndpi_free(flow->dhcp_class_ident);
flow->dhcp_class_ident = NULL;
@@ -477,7 +478,7 @@ static void ndpi_free_flow_tls_data(struct ndpi_flow_info *flow) {
ndpi_free(flow->telnet.username);
flow->telnet.username = NULL;
}
-
+
if(flow->telnet.password) {
ndpi_free(flow->telnet.password);
flow->telnet.password = NULL;
@@ -784,7 +785,7 @@ static struct ndpi_flow_info *get_ndpi_flow_info(struct ndpi_workflow * workflow
flow.protocol = iph->protocol, flow.vlan_id = vlan_id;
flow.src_ip = iph->saddr, flow.dst_ip = iph->daddr;
flow.src_port = htons(*sport), flow.dst_port = htons(*dport);
- flow.hashval = hashval = flow.protocol + ntohl(flow.src_ip) + ntohl(flow.dst_ip)
+ flow.hashval = hashval = flow.protocol + ntohl(flow.src_ip) + ntohl(flow.dst_ip)
+ ntohs(flow.src_port) + ntohs(flow.dst_port);
#if 0
@@ -1041,10 +1042,31 @@ u_int8_t plen2slot(u_int16_t plen) {
/* ****************************************************** */
+static void dump_raw_fingerprint(struct ndpi_workflow * workflow,
+ struct ndpi_flow_info *flow,
+ char *fingerprint_family,
+ char *fingerprint) {
+ char buf[64];
+
+ fprintf(fingerprint_fp, "%u|%s|%s|%u|%s|%s|%s\n",
+ flow->protocol,flow->src_name, flow->dst_name, ntohs(flow->dst_port),
+ ndpi_protocol2name(workflow->ndpi_struct, flow->detected_protocol, buf, sizeof(buf)),
+ fingerprint_family, fingerprint);
+}
+
+/* ****************************************************** */
+
+static void dump_flow_fingerprint(struct ndpi_workflow * workflow, struct ndpi_flow_info *flow) {
+ if(flow->ndpi_flow->protos.tls_quic.ja4_client_raw != NULL)
+ dump_raw_fingerprint(workflow, flow, "JA4r", flow->ndpi_flow->protos.tls_quic.ja4_client_raw);
+}
+
+/* ****************************************************** */
+
void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_flow_info *flow) {
u_int i, is_quic = 0;
char out[128], *s;
-
+
if(!flow->ndpi_flow) return;
flow->info_type = INFO_INVALID;
@@ -1052,8 +1074,8 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl
s = ndpi_get_flow_risk_info(flow->ndpi_flow, out, sizeof(out), 0 /* text */);
if(s != NULL)
- flow->risk_str = ndpi_strdup(s);
-
+ flow->risk_str = ndpi_strdup(s);
+
flow->confidence = flow->ndpi_flow->confidence;
flow->fpc = flow->ndpi_flow->fpc;
@@ -1067,7 +1089,7 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl
ndpi_snprintf(flow->mining.currency, sizeof(flow->mining.currency), "%s",
flow->ndpi_flow->protos.mining.currency);
}
-
+
flow->risk = flow->ndpi_flow->risk;
if(is_ndpi_proto(flow, NDPI_PROTOCOL_DHCP)) {
@@ -1084,7 +1106,7 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl
if(flow->ndpi_flow->protos.bittorrent.hash[0] != '\0') {
u_int avail = sizeof(flow->ndpi_flow->protos.bittorrent.hash) * 2 + 1;
flow->bittorent_hash = ndpi_malloc(avail);
-
+
if(flow->bittorent_hash) {
for(i=0, j = 0; i < sizeof(flow->ndpi_flow->protos.bittorrent.hash); i++) {
@@ -1093,7 +1115,7 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl
j += 2;
}
-
+
flow->bittorent_hash[j] = '\0';
}
}
@@ -1125,7 +1147,7 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl
/* SERVICE_LOCATION */
else if(is_ndpi_proto(flow, NDPI_PROTOCOL_SERVICE_LOCATION)) {
size_t i;
-
+
flow->info_type = INFO_GENERIC;
flow->info[0] = 0;
if (flow->ndpi_flow->protos.slp.url_count > 0)
@@ -1133,7 +1155,7 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl
for (i = 0; i < flow->ndpi_flow->protos.slp.url_count; ++i) {
size_t length = strlen(flow->info);
-
+
strncat(flow->info + length, flow->ndpi_flow->protos.slp.url[i],
sizeof(flow->info) - length);
length = strlen(flow->info);
@@ -1325,7 +1347,7 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl
memcpy(&flow->stun.other_address, &flow->ndpi_flow->stun.other_address, sizeof(ndpi_address_port));
flow->multimedia_flow_type = flow->ndpi_flow->flow_multimedia_type;
-
+
/* HTTP metadata are "global" not in `flow->ndpi_flow->protos` union; for example, we can have
HTTP/BitTorrent and in that case we want to export also HTTP attributes */
if(is_ndpi_proto(flow, NDPI_PROTOCOL_HTTP)
@@ -1358,18 +1380,21 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl
LOG(NDPI_LOG_ERROR, "flow2json failed\n");
return;
}
-
+
ndpi_serialize_string_uint32(&flow->ndpi_flow_serializer, "detection_completed", flow->detection_completed);
ndpi_serialize_string_uint32(&flow->ndpi_flow_serializer, "check_extra_packets", flow->check_extra_packets);
}
- if(flow->detection_completed && (!flow->check_extra_packets)) {
+ if(flow->detection_completed && (!flow->check_extra_packets)) {
flow->flow_payload = flow->ndpi_flow->flow_payload, flow->flow_payload_len = flow->ndpi_flow->flow_payload_len;
flow->ndpi_flow->flow_payload = NULL; /* We'll free the memory */
if(workflow->flow_callback != NULL)
workflow->flow_callback(workflow, flow, workflow->flow_callback_userdata);
+ if(fingerprint_fp)
+ dump_flow_fingerprint(workflow, flow);
+
ndpi_free_flow_info_half(flow);
}
}
@@ -1693,7 +1718,7 @@ static struct ndpi_proto packet_processing(struct ndpi_workflow * workflow,
}
malloc_size_stats = 0;
}
-
+
#if 0
if(flow->risk != 0) {
FILE *r = fopen("/tmp/e", "a");
@@ -1746,7 +1771,7 @@ static bool ndpi_is_valid_vxlan(const struct pcap_pkthdr *header, const u_char *
struct ndpi_udphdr *udp = (struct ndpi_udphdr *)&packet[ip_offset+ip_len];
u_int offset = ip_offset + ip_len + sizeof(struct ndpi_udphdr);
/**
- * rfc-7348
+ * rfc-7348
* VXLAN Header: This is an 8-byte field that has:
- Flags (8 bits): where the I flag MUST be set to 1 for a valid
@@ -1784,8 +1809,8 @@ static inline u_int ndpi_skip_vxlan(u_int16_t ip_offset, u_int16_t ip_len){
return ip_offset + ip_len + sizeof(struct ndpi_udphdr) + sizeof(struct ndpi_vxlanhdr);
}
-static uint32_t ndpi_is_valid_gre_tunnel(const struct pcap_pkthdr *header,
- const u_char *packet, const u_int16_t ip_offset,
+static uint32_t ndpi_is_valid_gre_tunnel(const struct pcap_pkthdr *header,
+ const u_char *packet, const u_int16_t ip_offset,
const u_int16_t ip_len) {
if(header->caplen < ip_offset + ip_len + sizeof(struct ndpi_gre_basehdr))
return 0; /* Too short for GRE header*/
@@ -1829,7 +1854,7 @@ static uint32_t ndpi_is_valid_gre_tunnel(const struct pcap_pkthdr *header,
return 0;
if(NDPI_GRE_IS_STRICT(grehdr->flags))
return 0;
- if(grehdr->protocol != NDPI_GRE_PROTO_PPP)
+ if(grehdr->protocol != NDPI_GRE_PROTO_PPP)
return 0;
/*key field*/
if(header->caplen < offset + 4)
@@ -2364,11 +2389,11 @@ struct ndpi_proto ndpi_workflow_process_packet(struct ndpi_workflow * workflow,
if((offset = ndpi_is_valid_gre_tunnel(header, packet, ip_offset, ip_len))) {
tunnel_type = ndpi_gre_tunnel;
struct ndpi_gre_basehdr *grehdr = (struct ndpi_gre_basehdr*)&packet[ip_offset + ip_len];
- if(grehdr->protocol == ntohs(ETH_P_IP) || grehdr->protocol == ntohs(ETH_P_IPV6)) {
+ if(grehdr->protocol == ntohs(ETH_P_IP) || grehdr->protocol == ntohs(ETH_P_IPV6)) {
ip_offset = offset;
- goto iph_check;
+ goto iph_check;
} else if(grehdr->protocol == NDPI_GRE_PROTO_PPP) { // ppp protocol
- ip_offset = offset + NDPI_PPP_HDRLEN;
+ ip_offset = offset + NDPI_PPP_HDRLEN;
goto iph_check;
} else {
eth_offset = offset;