Skip to content

Commit

Permalink
Improved TCP fingepring calculation
Browse files Browse the repository at this point in the history
Adde basidc OS detection based on TCP fingerprint
  • Loading branch information
lucaderi committed Oct 18, 2024
1 parent 819291b commit 0cc84e4
Show file tree
Hide file tree
Showing 345 changed files with 4,834 additions and 4,772 deletions.
9 changes: 7 additions & 2 deletions example/reader_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1586,9 +1586,14 @@ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_fl

flow->multimedia_flow_type = flow->ndpi_flow->flow_multimedia_type;

if(flow->ndpi_flow->tcp.fingerprint)
flow->tcp_fingerprint = ndpi_strdup(flow->ndpi_flow->tcp.fingerprint);
if(flow->ndpi_flow->tcp.fingerprint) {
char buf[128];

snprintf(buf, sizeof(buf), "%s/%s", flow->ndpi_flow->tcp.fingerprint,
ndpi_print_os_hint(flow->ndpi_flow->tcp.os_hint));
flow->tcp_fingerprint = ndpi_strdup(buf);
}

/* 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)
Expand Down
4 changes: 4 additions & 0 deletions src/include/ndpi_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -2330,6 +2330,10 @@ extern "C" {

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

const char* ndpi_print_os_hint(u_int8_t os_hint);

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

bool ndpi_serialize_flow_fingerprint(struct ndpi_detection_module_struct *ndpi_str,
struct ndpi_flow_struct *flow, ndpi_serializer *serializer);

Expand Down
21 changes: 19 additions & 2 deletions src/include/ndpi_typedefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,22 @@ struct ndpi_metadata_monitoring {
} protos;
};

enum operating_system_hint {
os_unknown = 0,
os_windows = 1,
os_macos = 2,
os_ios_ipad_os = 3,
os_android = 4,
os_linux = 5,
os_freebsd = 6,
os_unused2 = 7
};

struct os_fingerprint {
const char *fingerprint;
enum operating_system_hint os;
};

struct ndpi_flow_struct {
u_int16_t detected_protocol_stack[NDPI_PROTOCOL_SIZE];

Expand Down Expand Up @@ -1315,6 +1331,7 @@ struct ndpi_flow_struct {

struct {
char *fingerprint;
u_int8_t os_hint;
} tcp;

/*
Expand Down Expand Up @@ -1591,8 +1608,8 @@ struct ndpi_flow_struct {
_Static_assert(sizeof(((struct ndpi_flow_struct *)0)->protos) <= 264,
"Size of the struct member protocols increased to more than 264 bytes, "
"please check if this change is necessary.");
_Static_assert(sizeof(struct ndpi_flow_struct) <= 1184,
"Size of the flow struct increased to more than 1184 bytes, "
_Static_assert(sizeof(struct ndpi_flow_struct) <= 1185,
"Size of the flow struct increased to more than 1186 bytes, "
"please check if this change is necessary.");
#endif
#endif
Expand Down
46 changes: 34 additions & 12 deletions src/lib/ndpi_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,19 @@ static void (*_ndpi_flow_free)(void *ptr);

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

static struct os_fingerprint tcp_fps[] = {
{ "32770_128_64240_6bb88f5575fd", os_windows },
{ "45058_64_65535_dd5737e4fedb", os_macos },
{ "45250_64_65535_dd5737e4fedb", os_macos },
{ "40962_64_65535_d876f498b09e", os_android },
{ "45250_64_65535_63970bc57fac", os_ios_ipad_os },
{ "40962_64_65535_8bf9e292397e", os_freebsd },
{ "40962_64_64800_83b2f9a5576c", os_linux },
{ "40962_64_64240_2e3cee914fc1", os_linux },
{ "40962_64_29200_2e3cee914fc1", os_linux },
{ NULL, os_unknown },
};

static ndpi_risk_info ndpi_known_risks[] = {
{ NDPI_NO_RISK, NDPI_RISK_LOW, CLIENT_FAIR_RISK_PERCENTAGE, NDPI_NO_ACCOUNTABILITY },
{ NDPI_URL_POSSIBLE_XSS, NDPI_RISK_SEVERE, CLIENT_HIGH_RISK_PERCENTAGE, NDPI_CLIENT_ACCOUNTABLE },
Expand Down Expand Up @@ -6923,10 +6936,11 @@ static int ndpi_init_packet(struct ndpi_detection_module_struct *ndpi_str,
flow->tcp.fingerprint == NULL) {
u_int8_t *t = (u_int8_t*)packet->tcp;
u_int16_t flags = ntohs(*((u_int16_t*)&t[12]));

if((flags & (TH_SYN | TH_ECE | TH_CWR)) == TH_SYN) {
u_int16_t syn_mask = TH_SYN | TH_ECE | TH_CWR;

if((flags & syn_mask) && ((flags & TH_ACK) == 0)) {
char fingerprint[128], options_fp[128];
u_int8_t i, fp_idx = 0, options_fp_idx = 0;
u_int8_t i, fp_idx = 0, options_fp_len = 0;

if(tcp_header_len > sizeof(struct ndpi_tcphdr)) {
u_int8_t *options = (u_int8_t*)(&t[sizeof(struct ndpi_tcphdr)]);
Expand All @@ -6946,7 +6960,7 @@ static int ndpi_init_packet(struct ndpi_detection_module_struct *ndpi_str,
else if(ip_ttl <= 192) ip_ttl = 192;
else ip_ttl = 255;

fp_idx = snprintf(fingerprint, sizeof(fingerprint), "%u_%u_", ip_ttl, tcp_win);
fp_idx = snprintf(fingerprint, sizeof(fingerprint), "%u_%u_%u_", flags, ip_ttl, tcp_win);

for(i=0; i<options_len; ) {
u_int8_t kind = options[i];
Expand All @@ -6955,10 +6969,10 @@ static int ndpi_init_packet(struct ndpi_detection_module_struct *ndpi_str,
#ifdef DEBUG_TCP_OPTIONS
printf("Option kind: %u\n", kind);
#endif
rc = snprintf(&options_fp[options_fp_idx], sizeof(options_fp)-options_fp_idx, "%02x", kind);
if((rc < 0) || ((int)(options_fp_idx + rc) == sizeof(options_fp))) break;
rc = snprintf(&options_fp[options_fp_len], sizeof(options_fp)-options_fp_len, "%02x", kind);
if((rc < 0) || ((int)(options_fp_len + rc) == sizeof(options_fp))) break;

options_fp_idx += rc;
options_fp_len += rc;

if(kind == 0) /* EOF */
break;
Expand All @@ -6980,10 +6994,10 @@ static int ndpi_init_packet(struct ndpi_detection_module_struct *ndpi_str,
u_int8_t opt_len = len - 2;

while((opt_len > 0) && (j < options_len)) {
rc = snprintf(&options_fp[options_fp_idx], sizeof(options_fp)-options_fp_idx, "%02x", options[j]);
if((rc < 0) || ((int)(options_fp_idx + rc) == sizeof(options_fp))) break;
rc = snprintf(&options_fp[options_fp_len], sizeof(options_fp)-options_fp_len, "%02x", options[j]);
if((rc < 0) || ((int)(options_fp_len + rc) == sizeof(options_fp))) break;

options_fp_idx += rc;
options_fp_len += rc;
j++, opt_len--;
}
}
Expand All @@ -6992,12 +7006,20 @@ static int ndpi_init_packet(struct ndpi_detection_module_struct *ndpi_str,
}
} /* for */

ndpi_sha256((const u_char*)options_fp, options_fp_idx, sha_hash);
ndpi_sha256((const u_char*)options_fp, options_fp_len, sha_hash);

snprintf(&fingerprint[fp_idx], sizeof(fingerprint)-fp_idx, "%02x%02x%02x%02x%02x%02x",
sha_hash[0], sha_hash[1], sha_hash[2],
sha_hash[3], sha_hash[4], sha_hash[5]);

flow->tcp.fingerprint = ndpi_strdup(fingerprint);
flow->tcp.fingerprint = ndpi_strdup(fingerprint), flow->tcp.os_hint = os_unknown;

for(i=0; tcp_fps[i].fingerprint != NULL; i++) {
if(strcmp(tcp_fps[i].fingerprint, fingerprint) == 0) {
flow->tcp.os_hint = tcp_fps[i].os;
break;
}
}
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions src/lib/ndpi_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -3845,3 +3845,17 @@ char* ndpi_quick_decrypt(const char *encrypted_msg,
return(decoded_string);
}

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

const char* ndpi_print_os_hint(u_int8_t os_hint) {
switch(os_hint) {
case os_windows: return("Win");
case os_macos: return("macOS");
case os_ios_ipad_os: return("iOS/iPad");
case os_android: return("Android");
case os_linux: return("Linux");
case os_freebsd: return("FreeBSD");
}

return("Unknown");
}
12 changes: 6 additions & 6 deletions tests/cfgs/caches_cfg/result/ookla.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ JA3 Host Stats:
1 192.168.1.128 2


1 TCP 192.168.1.128:35830 <-> 89.96.108.170:8080 [proto: 91/TLS][IP: 0/Unknown][Encrypted][Confidence: DPI][FPC: 0/Unknown, Confidence: Unknown][DPI packets: 6][cat: Web/5][21 pkts/21216 bytes <-> 8 pkts/1950 bytes][Goodput ratio: 93/72][0.32 sec][Hostname/SNI: spd-pub-mi-01-01.fastwebnet.it][(Advertised) ALPNs: h2;http/1.1][TLS Supported Versions: TLSv1.3;TLSv1.2][bytes ratio: 0.832 (Upload)][IAT c2s/s2c min/avg/max/stddev: 0/0 17/61 274/280 62/109][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 1010/244 1514/387 612/138][Risk: ** Known Proto on Non Std Port **][Risk Score: 50][Risk Info: Expected on port 443][TCP Fingerprint: 64_64240_2e3cee914fc1][TLSv1.3][JA3C: c279b0189edb9269da7bc43dea5e0c36][JA4: t13d1714h2_5b57614c22b0_8f66f9ee9c6c][JA3S: fcb2d4d0991292272fcb1e464eedfd43][Firefox][Cipher: TLS_AES_128_GCM_SHA256][Plen Bins: 0,0,4,0,0,0,0,4,9,0,9,0,0,0,0,0,4,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,0,0]
2 TCP 192.168.1.128:48854 <-> 104.16.209.12:443 [proto: 91.191/TLS.Ookla][IP: 220/Cloudflare][Encrypted][Confidence: DPI][FPC: 220/Cloudflare, Confidence: IP address][DPI packets: 6][cat: Network/14][8 pkts/1620 bytes <-> 6 pkts/3818 bytes][Goodput ratio: 67/89][0.06 sec][Hostname/SNI: www.speedtest.net][(Advertised) ALPNs: h2;http/1.1][TLS Supported Versions: TLSv1.3;TLSv1.2][bytes ratio: -0.404 (Download)][IAT c2s/s2c min/avg/max/stddev: 0/0 7/5 18/15 7/6][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 202/636 583/1514 181/646][TCP Fingerprint: 64_64240_2e3cee914fc1][TLSv1.3][JA3C: 579ccef312d18482fc42e2b822ca2430][JA4: t13d1715h2_5b57614c22b0_3d5424432f57][JA3S: eb1d94daa7e0344597e756a1fb6e7054][Firefox][Cipher: TLS_AES_128_GCM_SHA256][PLAIN TEXT (@oTAgOeedtest.net)][Plen Bins: 0,0,14,0,0,14,0,0,0,0,14,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0,0]
3 TCP 192.168.1.7:51207 <-> 46.44.253.187:80 [proto: 7.191/HTTP.Ookla][IP: 0/Unknown][ClearText][Confidence: DPI][FPC: 0/Unknown, Confidence: Unknown][DPI packets: 6][cat: Network/14][12 pkts/2238 bytes <-> 8 pkts/2082 bytes][Goodput ratio: 64/74][5.33 sec][Hostname/SNI: massarosa-1.speedtest.welcomeitalia.it][bytes ratio: 0.036 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/4 528/47 5005/84 1493/28][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 186/260 430/523 168/194][URL: massarosa-1.speedtest.welcomeitalia.it/crossdomain.xml][StatusCode: 200][Content-Type: application/xml][Server: Apache/2.2.22 (Ubuntu)][User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8][Risk: ** HTTP Obsolete Server **][Risk Score: 50][Risk Info: Obsolete Apache server 2.2.22][TCP Fingerprint: 64_65535_63970bc57fac][PLAIN TEXT (GET /crossdomain.xml HTTP/1.1)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,12,75,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
4 TCP 192.168.1.192:51156 <-> 89.96.108.170:8080 [proto: 131/HTTP_Proxy][IP: 0/Unknown][ClearText][Confidence: Match by port][FPC: 0/Unknown, Confidence: Unknown][DPI packets: 10][cat: Web/5][6 pkts/591 bytes <-> 4 pkts/1784 bytes][Goodput ratio: 32/85][0.05 sec][bytes ratio: -0.502 (Download)][IAT c2s/s2c min/avg/max/stddev: 0/0 9/10 15/20 6/8][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 98/446 143/1514 31/617][Risk: ** Fully Encrypted Flow **][Risk Score: 50][TCP Fingerprint: 64_64240_2e3cee914fc1][PLAIN TEXT (gKRZvA)][Plen Bins: 0,40,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,0,0]
5 TCP 192.168.1.7:51215 <-> 46.44.253.187:8080 [proto: 191/Ookla][IP: 0/Unknown][ClearText][Confidence: DPI][FPC: 0/Unknown, Confidence: Unknown][DPI packets: 6][cat: Network/14][19 pkts/1421 bytes <-> 11 pkts/920 bytes][Goodput ratio: 11/20][0.80 sec][bytes ratio: 0.214 (Upload)][IAT c2s/s2c min/avg/max/stddev: 26/0 44/75 103/137 23/41][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 75/84 85/100 9/8][TCP Fingerprint: 64_65535_63970bc57fac][PLAIN TEXT ( 6HELLO 2.4 2016)][Plen Bins: 94,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
6 TCP 192.168.1.192:37790 <-> 185.157.229.246:8080 [proto: 191/Ookla][IP: 0/Unknown][ClearText][Confidence: DPI][FPC: 0/Unknown, Confidence: Unknown][DPI packets: 6][cat: Network/14][6 pkts/454 bytes <-> 4 pkts/317 bytes][Goodput ratio: 11/14][0.06 sec][bytes ratio: 0.178 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/0 12/5 46/9 17/4][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 76/79 106/108 14/17][TCP Fingerprint: 64_64240_2e3cee914fc1][PLAIN TEXT (HELLO 2.9 )][Plen Bins: 50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
1 TCP 192.168.1.128:35830 <-> 89.96.108.170:8080 [proto: 91/TLS][IP: 0/Unknown][Encrypted][Confidence: DPI][FPC: 0/Unknown, Confidence: Unknown][DPI packets: 6][cat: Web/5][21 pkts/21216 bytes <-> 8 pkts/1950 bytes][Goodput ratio: 93/72][0.32 sec][Hostname/SNI: spd-pub-mi-01-01.fastwebnet.it][(Advertised) ALPNs: h2;http/1.1][TLS Supported Versions: TLSv1.3;TLSv1.2][bytes ratio: 0.832 (Upload)][IAT c2s/s2c min/avg/max/stddev: 0/0 17/61 274/280 62/109][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 1010/244 1514/387 612/138][Risk: ** Known Proto on Non Std Port **][Risk Score: 50][Risk Info: Expected on port 443][TCP Fingerprint: 40962_64_64240_2e3cee914fc1/Linux][TLSv1.3][JA3C: c279b0189edb9269da7bc43dea5e0c36][JA4: t13d1714h2_5b57614c22b0_8f66f9ee9c6c][JA3S: fcb2d4d0991292272fcb1e464eedfd43][Firefox][Cipher: TLS_AES_128_GCM_SHA256][Plen Bins: 0,0,4,0,0,0,0,4,9,0,9,0,0,0,0,0,4,0,0,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,0,0]
2 TCP 192.168.1.128:48854 <-> 104.16.209.12:443 [proto: 91.191/TLS.Ookla][IP: 220/Cloudflare][Encrypted][Confidence: DPI][FPC: 220/Cloudflare, Confidence: IP address][DPI packets: 6][cat: Network/14][8 pkts/1620 bytes <-> 6 pkts/3818 bytes][Goodput ratio: 67/89][0.06 sec][Hostname/SNI: www.speedtest.net][(Advertised) ALPNs: h2;http/1.1][TLS Supported Versions: TLSv1.3;TLSv1.2][bytes ratio: -0.404 (Download)][IAT c2s/s2c min/avg/max/stddev: 0/0 7/5 18/15 7/6][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 202/636 583/1514 181/646][TCP Fingerprint: 40962_64_64240_2e3cee914fc1/Linux][TLSv1.3][JA3C: 579ccef312d18482fc42e2b822ca2430][JA4: t13d1715h2_5b57614c22b0_3d5424432f57][JA3S: eb1d94daa7e0344597e756a1fb6e7054][Firefox][Cipher: TLS_AES_128_GCM_SHA256][PLAIN TEXT (@oTAgOeedtest.net)][Plen Bins: 0,0,14,0,0,14,0,0,0,0,14,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,0,0]
3 TCP 192.168.1.7:51207 <-> 46.44.253.187:80 [proto: 7.191/HTTP.Ookla][IP: 0/Unknown][ClearText][Confidence: DPI][FPC: 0/Unknown, Confidence: Unknown][DPI packets: 6][cat: Network/14][12 pkts/2238 bytes <-> 8 pkts/2082 bytes][Goodput ratio: 64/74][5.33 sec][Hostname/SNI: massarosa-1.speedtest.welcomeitalia.it][bytes ratio: 0.036 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/4 528/47 5005/84 1493/28][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 186/260 430/523 168/194][URL: massarosa-1.speedtest.welcomeitalia.it/crossdomain.xml][StatusCode: 200][Content-Type: application/xml][Server: Apache/2.2.22 (Ubuntu)][User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8][Risk: ** HTTP Obsolete Server **][Risk Score: 50][Risk Info: Obsolete Apache server 2.2.22][TCP Fingerprint: 45058_64_65535_63970bc57fac/Unknown][PLAIN TEXT (GET /crossdomain.xml HTTP/1.1)][Plen Bins: 0,0,0,0,0,0,0,0,0,0,12,75,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
4 TCP 192.168.1.192:51156 <-> 89.96.108.170:8080 [proto: 131/HTTP_Proxy][IP: 0/Unknown][ClearText][Confidence: Match by port][FPC: 0/Unknown, Confidence: Unknown][DPI packets: 10][cat: Web/5][6 pkts/591 bytes <-> 4 pkts/1784 bytes][Goodput ratio: 32/85][0.05 sec][bytes ratio: -0.502 (Download)][IAT c2s/s2c min/avg/max/stddev: 0/0 9/10 15/20 6/8][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 98/446 143/1514 31/617][Risk: ** Fully Encrypted Flow **][Risk Score: 50][TCP Fingerprint: 40962_64_64240_2e3cee914fc1/Linux][PLAIN TEXT (gKRZvA)][Plen Bins: 0,40,40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,0,0]
5 TCP 192.168.1.7:51215 <-> 46.44.253.187:8080 [proto: 191/Ookla][IP: 0/Unknown][ClearText][Confidence: DPI][FPC: 0/Unknown, Confidence: Unknown][DPI packets: 6][cat: Network/14][19 pkts/1421 bytes <-> 11 pkts/920 bytes][Goodput ratio: 11/20][0.80 sec][bytes ratio: 0.214 (Upload)][IAT c2s/s2c min/avg/max/stddev: 26/0 44/75 103/137 23/41][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 75/84 85/100 9/8][TCP Fingerprint: 45058_64_65535_63970bc57fac/Unknown][PLAIN TEXT ( 6HELLO 2.4 2016)][Plen Bins: 94,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
6 TCP 192.168.1.192:37790 <-> 185.157.229.246:8080 [proto: 191/Ookla][IP: 0/Unknown][ClearText][Confidence: DPI][FPC: 0/Unknown, Confidence: Unknown][DPI packets: 6][cat: Network/14][6 pkts/454 bytes <-> 4 pkts/317 bytes][Goodput ratio: 11/14][0.06 sec][bytes ratio: 0.178 (Mixed)][IAT c2s/s2c min/avg/max/stddev: 0/0 12/5 46/9 17/4][Pkt Len c2s/s2c min/avg/max/stddev: 66/66 76/79 106/108 14/17][TCP Fingerprint: 40962_64_64240_2e3cee914fc1/Linux][PLAIN TEXT (HELLO 2.9 )][Plen Bins: 50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
Loading

0 comments on commit 0cc84e4

Please sign in to comment.