6 Commits

Author SHA1 Message Date
zhuzhenjun
64697d137e v1.0.0 2023-10-09 17:27:01 +08:00
zhuzhenjun
56e3dec5f0 v0.0.5 2023-10-09 15:23:59 +08:00
zhuzhenjun
1a559eba99 v0.0.4 2023-09-27 19:40:42 +08:00
zhuzhenjun
15d4a2d271 v0.0.3 2023-09-27 11:45:58 +08:00
zhuzhenjun
eeb4cc0b6b v0.0.2 2023-09-26 13:18:37 +08:00
zhuzhenjun
554867aa4e v0.0.1 2023-09-22 18:44:27 +08:00
44 changed files with 507410 additions and 1530 deletions

22
CMakeLists.txt.in Normal file
View File

@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 2.8)
project(libosfp)
# 添加其他CMake配置
# 生成RPM包
set(CPACK_GENERATOR "RPM")
set(CPACK_PACKAGE_NAME "libosfp")
set(CPACK_PACKAGE_VENDOR "Geedge")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Libosfp is a C library for OS fingerprinting.")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_RELEASE 1)
set(CPACK_PACKAGE_CONTACT "zhuzhenjun@geedgenetworks.com")
set(CPACK_RPM_PACKAGE_AUTOREQPROV "no")
install(DIRECTORY @prefix@/include DESTINATION . USE_SOURCE_PERMISSIONS)
install(DIRECTORY @prefix@/lib/ DESTINATION ./lib64 USE_SOURCE_PERMISSIONS FILES_MATCHING PATTERN "libosfp*")
install(DIRECTORY @prefix@/bin DESTINATION . USE_SOURCE_PERMISSIONS FILES_MATCHING PATTERN "osfp*")
install(FILES fp.json DESTINATION /var/lib/libosfp)
include(CPack)

2288
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +1,2 @@
SUBDIRS = src example SUBDIRS = src example test
ACLOCAL_AMFLAGS=-I m4 ACLOCAL_AMFLAGS=-I m4

View File

@@ -1,4 +1,26 @@
# libosfp # libosfp
Libosfp is a C libaray for OS fingerprinting. Libosfp is a C library for OS fingerprinting.
## install
```
# osfp_example depends on libpcap
yum install -y libpcap-devel
# build and install
./build.sh
./package.sh
yum install package/*.rpm
```
## library usage
```
gcc example/sample.c -o sample -losfp; cat example/sample.c
```
## run example
```
# load the fingerprint file and capture on eth0, filter tcp port 8888
osfp_example -f /var/lib/libosfp/fp.json -i eth0 "tcp port 8888"
```

6
build.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
./autogen.sh;
./configure --prefix=$(pwd)/target;
make -j
make install

View File

@@ -1,4 +1,4 @@
AC_INIT([libosfp],[0.0.0],[zhuzhenjun@geedgenetworks.com]) AC_INIT([libosfp],[1.0.0],[zhuzhenjun@geedgenetworks.com])
AM_INIT_AUTOMAKE([foreign]) AM_INIT_AUTOMAKE([foreign])
#m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])])AM_SILENT_RULES([yes]) #m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])])AM_SILENT_RULES([yes])
@@ -7,9 +7,12 @@ AC_CONFIG_MACRO_DIR([m4])
AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [enable debug info])], [enable_debug=$enableval], [enable_debug=no]) AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [enable debug info])], [enable_debug=$enableval], [enable_debug=no])
AS_IF([test "x$enable_debug" = xyes], AS_IF([test "x$enable_debug" = xyes],
[CFLAGS="-ggdb3 -O0"], [CFLAGS="-DDEBUGLOG -ggdb3 -O0 -fsanitize=address -fno-omit-frame-pointer"],
[CFLAGS="-g -O2"]) [CFLAGS="-g -O2"])
AC_ARG_ENABLE([test], [AS_HELP_STRING([--enable-test], [enable test])], [enable_test=$enableval], [enable_test=yes])
AS_IF([test "x$enable_test" = xyes],
[CFLAGS="${CFLAGS} -DUNITTEST"])
AC_PROG_CC AC_PROG_CC
AC_PROG_CPP AC_PROG_CPP
@@ -19,5 +22,5 @@ AC_PROG_MAKE_SET
AC_PROG_LIBTOOL AC_PROG_LIBTOOL
AC_CONFIG_FILES([libosfp-config Makefile src/Makefile example/Makefile]) AC_CONFIG_FILES([CMakeLists.txt libosfp-config Makefile src/Makefile example/Makefile test/Makefile])
AC_OUTPUT AC_OUTPUT

362639
data.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -11,3 +11,4 @@ osfp_example_LDFLAGS = \
osfp_example_CFLAGS = \ osfp_example_CFLAGS = \
-I../src -I../src

View File

@@ -14,10 +14,13 @@
#include <pcap.h> #include <pcap.h>
#include "libosfp.h" #include "osfp_common.h"
#include "libosfp_fingerprint.h" #include "osfp.h"
#include "osfp_log.h"
#include "osfp_fingerprint.h"
#include "osfp_score_db.h"
#define DEFAULT_FP_FILE "./fp.json" #define DEFAULT_FP_FILE_PATH "./fp.json"
#define ETHERNET_HEADER_LEN 14 #define ETHERNET_HEADER_LEN 14
#define VLAN_HEADER_LEN 4 #define VLAN_HEADER_LEN 4
@@ -131,6 +134,9 @@ typedef struct Packet_ {
struct ipv6hdr *ip6h; struct ipv6hdr *ip6h;
struct tcphdr *tcph; struct tcphdr *tcph;
char srcip[46];
char dstip[46];
Address src; Address src;
Address dst; Address dst;
union { union {
@@ -154,7 +160,11 @@ typedef struct Packet_ {
} Packet; } Packet;
unsigned char *fp_file; unsigned char *fp_file_path;
unsigned char *fp_output_file_path;
FILE *fingerprinting_output_fp;
unsigned int debug_enable;
unsigned char *if_name; unsigned char *if_name;
unsigned char *pcap_file_name; unsigned char *pcap_file_name;
unsigned char *bpf_string; unsigned char *bpf_string;
@@ -172,7 +182,7 @@ void usage(void) {
" -i iface - listen on the specified network interface\n" " -i iface - listen on the specified network interface\n"
" -r file - read offline pcap data from a given file\n" " -r file - read offline pcap data from a given file\n"
" -f file - read fingerprint database from 'file' (%s)\n", " -f file - read fingerprint database from 'file' (%s)\n",
DEFAULT_FP_FILE); DEFAULT_FP_FILE_PATH);
exit(1); exit(1);
} }
@@ -427,90 +437,54 @@ const char *PrintInet(int af, const void *src, char *dst, socklen_t size)
return NULL; return NULL;
} }
void example_header_match(libosfp_context_t *libosfp_context, Packet *p) void example_detect(struct osfp_db *osfp_db, Packet *p)
{ {
// tcp/ip header match
int ret; int ret;
char str_buf[1024]; char str_buf[1024];
//unsigned char *iph = (unsigned char *)(p->iph != NULL ? (void *)p->iph : (void *)p->ip6h);
struct iphdr *iph;
struct ipv6hdr *ip6h;
struct tcphdr *tcph;
unsigned int tcph_len;
struct osfp_result *result = NULL;
unsigned char *iph = (unsigned char *)(p->iph != NULL ? (void *)p->iph : (void *)p->ip6h); printf("Example ipv4 header detect: --------------------------\n");
unsigned char *tcph = (unsigned char *)p->tcph;
libosfp_result_t result;
printf("Example header match: --------------------------\n"); iph = (struct iphdr *)p->iph;
ip6h = (struct ipv6hdr *)p->ip6h;
tcph = (struct tcphdr *)p->tcph;
tcph_len = tcph->doff << 2;
ret = libosfp_header_match(libosfp_context, iph, tcph, &result); if (iph) {
if (ret != 0) { result = osfp_ipv4_identify(osfp_db, iph, tcph, tcph_len);
printf("libosfp header match failed, erro: %s\n", "?"); } else if (ip6h) {
result = osfp_ipv6_identify(osfp_db, ip6h, tcph, tcph_len);
} else {
goto exit; goto exit;
} }
char srcip[46] = {0}, dstip[46] = {0}; if (result == NULL) {
Port sp, dp; printf("osfp header match failed, erro: %s\n", "?");
if (p->iph) { goto exit;
PrintInet(AF_INET, (const void *)&(p->src.addr_data32[0]), srcip, sizeof(srcip));
PrintInet(AF_INET, (const void *)&(p->dst.addr_data32[0]), dstip, sizeof(dstip));
} else if (p->ip6h) {
PrintInet(AF_INET6, (const void *)&(p->src.address), srcip, sizeof(srcip));
PrintInet(AF_INET6, (const void *)&(p->dst.address), dstip, sizeof(dstip));
} }
sp = p->sp;
dp = p->dp;
printf("Connection info: %s:%d -> %s:%d\n", srcip, sp, dstip, dp); printf("Connection info: %s:%d -> %s:%d\n", p->srcip, p->sp, p->dstip, p->dp);
printf("Most likely os class: %s\n", libosfp_result_likely_os_class_name_get(&result)); printf("Most likely os class: %s\n", osfp_result_os_name_get(result));
printf("Likely score: %u/100\n", libosfp_result_likely_os_class_score_get(&result));
libosfp_result_to_buf(&result, str_buf, sizeof(str_buf)); printf("Details:\n");
fprintf(stdout, "%s\n", str_buf); printf("%s\n", osfp_result_score_detail_export(result));
exit: exit:
return; if (result) {
} osfp_result_free(result);
void example_fingerprint_match(libosfp_context_t *libosfp_context, Packet *p)
{
// fingerprint match
int ret;
char str_buf[1024];
unsigned char *iph = (unsigned char *)(p->iph != NULL ? (void *)p->iph : (void *)p->ip6h);
unsigned char *tcph = (unsigned char *)p->tcph;
libosfp_result_t result;
libosfp_fingerprint_t fp;
printf("Example fingerprint match: --------------------------\n");
ret = libosfp_fingerprinting(iph, tcph, &fp);
if (ret != 0) {
printf("libosfp fingerprinting failed\n");
goto exit;
} }
libosfp_fingerprint_to_json_buf(&fp, str_buf, sizeof(str_buf));
fprintf(stdout, "%s\n", str_buf);
ret = libosfp_score_db_score(libosfp_context, &fp, &result);
if (ret != 0) {
printf("libosfp fingerprint score failed, error: %d\n", ret);
goto exit;
}
printf("Connection info: %s\n", "");
printf("Most likely os class: %s\n", libosfp_result_likely_os_class_name_get(&result));
printf("Likely score: %u/100\n", libosfp_result_likely_os_class_score_get(&result));
libosfp_result_to_buf(&result, str_buf, sizeof(str_buf));
fprintf(stdout, "%s\n", str_buf);
exit:
return; return;
} }
void process_packet(char *user, struct pcap_pkthdr *h, u_char *pkt) void process_packet(char *user, struct pcap_pkthdr *h, u_char *pkt)
{ {
int ret; int ret;
libosfp_context_t *libosfp_context = (libosfp_context_t *)user; struct osfp_db *osfp_db = (struct osfp_db *)user;
Packet packet = {0}, *p = &packet; Packet packet = {0}, *p = &packet;
// decode packet // decode packet
@@ -520,13 +494,26 @@ void process_packet(char *user, struct pcap_pkthdr *h, u_char *pkt)
} }
// only for tcp syn request packet // only for tcp syn request packet
if (!p->tcph->syn || p->tcph->ack) { if (!p->tcph->syn) {
goto exit; goto exit;
} }
example_header_match(libosfp_context, p); if (p->tcph->ack) {
printf("--------------------------- SYN/ACK\n");
} else {
printf("--------------------------- SYN\n");
}
example_fingerprint_match(libosfp_context, p); if (p->iph) {
PrintInet(AF_INET, (const void *)&(p->src.addr_data32[0]), p->srcip, sizeof(p->srcip));
PrintInet(AF_INET, (const void *)&(p->dst.addr_data32[0]), p->dstip, sizeof(p->dstip));
} else if (p->ip6h) {
PrintInet(AF_INET6, (const void *)&(p->src.address), p->srcip, sizeof(p->srcip));
PrintInet(AF_INET6, (const void *)&(p->dst.address), p->dstip, sizeof(p->dstip));
}
// tcp/ip header detect example for user
example_detect(osfp_db, p);
printf("--------------------------- processed packet count %d\n", ++processed_packet); printf("--------------------------- processed packet count %d\n", ++processed_packet);
@@ -538,14 +525,14 @@ int main(int argc, char *argv[])
{ {
int r; int r;
while ((r = getopt(argc, argv, "+f:i:r:")) != -1) { while ((r = getopt(argc, argv, "+f:i:r:o:d")) != -1) {
switch(r) { switch(r) {
case 'f': case 'f':
if (fp_file) { if (fp_file_path) {
printf("Multiple -f options not supported.\n"); printf("Multiple -f options not supported.\n");
exit(1); exit(1);
} }
fp_file = (unsigned char*)optarg; fp_file_path = (unsigned char*)optarg;
break; break;
case 'i': case 'i':
if (if_name) { if (if_name) {
@@ -561,6 +548,16 @@ int main(int argc, char *argv[])
} }
pcap_file_name = (unsigned char*)optarg; pcap_file_name = (unsigned char*)optarg;
break; break;
case 'o':
if (fp_output_file_path) {
printf("Multiple -o options not supported.\n");
exit(1);
}
fp_output_file_path = (unsigned char*)optarg;
break;
case 'd':
debug_enable = 1;
break;
default: default:
usage(); usage();
break; break;
@@ -576,6 +573,15 @@ int main(int argc, char *argv[])
} }
} }
// fingerprinting out file create
if (fp_output_file_path) {
fingerprinting_output_fp = fopen(fp_output_file_path, "a+");
if (!fingerprinting_output_fp) {
printf("No such file: %s\n", fp_output_file_path);
exit(1);
}
}
// prepare pcap handle // prepare pcap handle
char pcap_err[PCAP_ERRBUF_SIZE]; char pcap_err[PCAP_ERRBUF_SIZE];
@@ -620,35 +626,34 @@ int main(int argc, char *argv[])
// get link type // get link type
link_type = pcap_datalink(pcap_handle); link_type = pcap_datalink(pcap_handle);
// create libosfp context // create osfp db
if (fp_file == NULL) { if (fp_file_path == NULL) {
fp_file = DEFAULT_FP_FILE; fp_file_path = DEFAULT_FP_FILE_PATH;
} }
libosfp_context_t *libosfp_context = libosfp_context_create(fp_file); if (debug_enable) {
if (libosfp_context == NULL) { osfp_log_level_set(OSFP_LOG_LEVEL_DEBUG);
printf("could not create libosfp context. fingerprints file: %s\n", fp_file); }
struct osfp_db *osfp_db = osfp_db_new(fp_file_path);
if (osfp_db == NULL) {
printf("could not create osfp context. fingerprints file: %s\n", fp_file_path);
exit(1); exit(1);
} }
// setup libosfp context osfp_score_db_debug_print(osfp_db->score_db);
r = libosfp_context_setup(libosfp_context);
if (r != LIBOSFP_NOERR) {
printf("could not setup libosfp context. error: %d\n", LIBOSFP_NOERR);
exit(1);
}
// loop // loop
while (1) { while (1) {
int r = pcap_dispatch(pcap_handle, 0, (pcap_handler)process_packet, (void*)libosfp_context); int r = pcap_dispatch(pcap_handle, 0, (pcap_handler)process_packet, (void*)osfp_db);
if (r < 0) { if (r < 0) {
printf("error code: %d, error: %s\n", r, pcap_geterr(pcap_handle)); printf("error code: %d, error: %s\n", r, pcap_geterr(pcap_handle));
break; break;
} }
} }
// create libosfp context // destroy osfp db
libosfp_context_destroy(libosfp_context); osfp_db_free(osfp_db);
return 0; return 0;
} }

35
example/sample.c Normal file
View File

@@ -0,0 +1,35 @@
#include "stdio.h"
#include "libosfp/osfp.h"
char iph[] = {
0x45, 0x00, 0x00, 0x34, 0x51, 0xc4, 0x40, 0x00,
0x80, 0x06, 0xe7, 0x27, 0xc0, 0xa8, 0x73, 0x08,
0x6a, 0xb9, 0x23, 0x6e
};
char tcph[] = {
0xc1, 0xbd, 0x00, 0x50, 0x3d, 0x58, 0x51, 0x60,
0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20, 0x00,
0x3d, 0x3a, 0x00, 0x00, 0x02, 0x04, 0x04, 0xec,
0x01, 0x03, 0x03, 0x08, 0x01, 0x01, 0x04, 0x02
};
int main(int argc, char **argv)
{
const char *json_file_path = "./fp.json";
struct iphdr *l3_hdr = (struct iphdr *)iph;
struct tcphdr *l4_hdr = (struct tcphdr *)tcph;
size_t l4_hdr_len = sizeof(tcph);
struct osfp_db *db = osfp_db_new(json_file_path);
if (db) {
struct osfp_result *result = osfp_ipv4_identify(db, l3_hdr, l4_hdr, l4_hdr_len);
if (result) {
printf("likely os: %s\n", osfp_result_os_name_get(result));
printf("details: \n%s\n", osfp_result_score_detail_export(result));
osfp_result_free(result);
osfp_db_free(db);
}
}
}

72594
fp.json Normal file

File diff suppressed because it is too large Load Diff

8
package.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
rm -rf package
mkdir package
cd package
cmake ..
cpack
cd -

BIN
pcap/linux_ipv4_syn.pcap Normal file

Binary file not shown.

Binary file not shown.

BIN
pcap/test.pcap Normal file

Binary file not shown.

BIN
pcap/unknown_ipv6_syn.pcap Normal file

Binary file not shown.

Binary file not shown.

BIN
pcap/windows_ipv4_syn.pcap Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -9,11 +9,15 @@ libosfp_la_SOURCES = \
utstring.h \ utstring.h \
cJSON.h \ cJSON.h \
cJSON.c \ cJSON.c \
libosfp.h \ osfp_common.h \
libosfp.c \ osfp_common.c \
libosfp_fingerprint.h \ osfp.h \
libosfp_fingerprint.c \ osfp.c \
libosfp_score_db.h \ osfp_fingerprint.h \
libosfp_score_db.c osfp_fingerprint.c \
osfp_log.h \
osfp_log.c \
osfp_score_db.h \
osfp_score_db.c
pkginclude_HEADERS = libosfp.h libosfp_fingerprint.h pkginclude_HEADERS = osfp.h osfp_fingerprint.h osfp_score_db.h osfp_common.h

View File

@@ -1,207 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <malloc.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include "libosfp.h"
#include "libosfp_fingerprint.h"
#include "libosfp_score_db.h"
#include "libosfp_log.h"
#define LIBOSFP_OS_CLASS_NAME_WINDOWS "Windows"
#define LIBOSFP_OS_CLASS_NAME_LINUX "Linux"
#define LIBOSFP_OS_CLASS_NAME_MAC_OS "Mac OS"
#define LIBOSFP_OS_CLASS_NAME_IOS "iOS"
#define LIBOSFP_OS_CLASS_NAME_ANDROID "Android"
#define LIBOSFP_WRITE_STRING_TO_BUF(ret, buf, size, off, ...) do { \
ret = snprintf((char *)buf + off, \
size - off, \
__VA_ARGS__); \
if (ret >= 0) { \
if ( (off + ret) >= size) { \
off = size - 1; \
} else { \
off += ret; \
} \
} \
} while (0)
const char *os_class_name[LIBOSFP_OS_CLASS_MAX] = {
LIBOSFP_OS_CLASS_NAME_WINDOWS,
LIBOSFP_OS_CLASS_NAME_LINUX,
LIBOSFP_OS_CLASS_NAME_MAC_OS,
LIBOSFP_OS_CLASS_NAME_IOS,
LIBOSFP_OS_CLASS_NAME_ANDROID
};
libosfp_os_class_id_t libosfp_os_class_name_to_id(char *name)
{
libosfp_os_class_id_t os_class;
if (0 == strncmp(name, LIBOSFP_OS_CLASS_NAME_WINDOWS, strlen(LIBOSFP_OS_CLASS_NAME_WINDOWS))) {
os_class = LIBOSFP_OS_CLASS_WINDOWS;
} else if (0 == strncmp(name, LIBOSFP_OS_CLASS_NAME_LINUX, strlen(LIBOSFP_OS_CLASS_NAME_LINUX))) {
os_class = LIBOSFP_OS_CLASS_Linux;
} else if (0 == strncmp(name, LIBOSFP_OS_CLASS_NAME_MAC_OS, strlen(LIBOSFP_OS_CLASS_NAME_MAC_OS))) {
os_class = LIBOSFP_OS_CLASS_MAC_OS;
} else if (0 == strncmp(name, LIBOSFP_OS_CLASS_NAME_IOS, strlen(LIBOSFP_OS_CLASS_NAME_IOS))) {
os_class = LIBOSFP_OS_CLASS_IOS;
} else if (0 == strncmp(name, LIBOSFP_OS_CLASS_NAME_ANDROID, strlen(LIBOSFP_OS_CLASS_NAME_ANDROID))) {
os_class = LIBOSFP_OS_CLASS_ANDROID;
} else {
os_class = LIBOSFP_OS_CLASS_MAX;
}
return os_class;
}
const char *libosfp_os_class_id_to_name(libosfp_os_class_id_t os_class)
{
if (os_class < 0 || os_class >= LIBOSFP_OS_CLASS_MAX) {
return NULL;
}
return os_class_name[os_class];
}
const char *libosfp_result_likely_os_class_name_get(libosfp_result_t *result)
{
libosfp_os_class_id_t os_class;
if (result == NULL) {
return NULL;
}
os_class = result->likely_os_class;
if (os_class < 0 || os_class >= LIBOSFP_OS_CLASS_MAX) {
return NULL;
}
return os_class_name[os_class];
}
unsigned int libosfp_result_likely_os_class_score_get(libosfp_result_t *result)
{
if (result == NULL) {
return 0;
}
return result->likely_score;
}
int libosfp_result_to_buf(libosfp_result_t *result, char *strbuf, unsigned int buf_len)
{
int ret, offset = 0, i;
if (result == NULL || strbuf == NULL || buf_len == 0) {
return 0;
}
LIBOSFP_WRITE_STRING_TO_BUF(ret, strbuf, buf_len, offset,
"Most likely os class: %s\nLikely score: %u/100\n",
os_class_name[result->likely_os_class], result->likely_score);
LIBOSFP_WRITE_STRING_TO_BUF(ret, strbuf, buf_len, offset,"Details:\n");
for (i = 0; i < LIBOSFP_OS_CLASS_MAX; i++) {
LIBOSFP_WRITE_STRING_TO_BUF(ret, strbuf, buf_len, offset,"%s score: %u\n",
os_class_name[i], result->score.os_class_score[i]);
}
exit:
return offset;
}
libosfp_error_code_t libosfp_header_match(libosfp_context_t *libosfp_context, unsigned char *ip_hdr, unsigned char *tcp_hdr, libosfp_result_t *result)
{
int ret = LIBOSFP_EINVAL;
libosfp_fingerprint_t fp = {0};
if (libosfp_context == NULL || ip_hdr == NULL || tcp_hdr == NULL || result == NULL) {
goto exit;
}
ret = libosfp_fingerprinting(ip_hdr, tcp_hdr, &fp);
if (ret != 0) {
goto exit;
}
ret = libosfp_score_db_score(libosfp_context, &fp, result);
if (ret != 0) {
goto exit;
}
return LIBOSFP_NOERR;
exit:
return ret;
}
libosfp_error_code_t libosfp_context_setup(libosfp_context_t *libosfp_context)
{
int ret = LIBOSFP_EINVAL;
if (libosfp_context == NULL) {
goto exit;
}
ret = libosfp_score_db_load((libosfp_score_db_t *)libosfp_context->score_db, libosfp_context->fp_file);
if (ret != LIBOSFP_NOERR) {
goto exit;
}
return LIBOSFP_NOERR;
exit:
return ret;
}
libosfp_context_t *libosfp_context_create(char *fp_file)
{
libosfp_context_t *libosfp_context = NULL;
if (fp_file == NULL || 0 != access(fp_file, R_OK)) {
goto exit;
}
libosfp_context = malloc(sizeof(libosfp_context_t));
if (libosfp_context == NULL) {
goto exit;
}
libosfp_context->fp_file = strdup((const char*)fp_file);
if (libosfp_context->fp_file == NULL) {
goto exit;
}
libosfp_context->score_db = (void *)libosfp_score_db_create();
if (libosfp_context->score_db == NULL) {
goto exit;
}
return libosfp_context;
exit:
if (libosfp_context) {
libosfp_context_destroy(libosfp_context);
}
return NULL;
}
void libosfp_context_destroy(libosfp_context_t *libosfp_context)
{
if (libosfp_context) {
if (libosfp_context->fp_file) {
free(libosfp_context->fp_file);
}
if (libosfp_context->score_db) {
libosfp_score_db_destroy(libosfp_context->score_db);
}
free(libosfp_context);
}
}

View File

@@ -1,50 +0,0 @@
#ifndef __LIBOSFP_H__
#define __LIBOSFP_H__
typedef enum libosfp_error_code {
LIBOSFP_NOERR,
LIBOSFP_EINVAL,
LIBOSFP_ERR_READ_FILE,
LIBOSFP_ERR_PARSE_FILE,
} libosfp_error_code_t;
typedef enum libosfp_os_class_id {
LIBOSFP_OS_CLASS_WINDOWS,
LIBOSFP_OS_CLASS_Linux,
LIBOSFP_OS_CLASS_MAC_OS,
LIBOSFP_OS_CLASS_IOS,
LIBOSFP_OS_CLASS_ANDROID,
LIBOSFP_OS_CLASS_MAX,
} libosfp_os_class_id_t;
typedef struct libosfp_score {
unsigned int os_class_score[LIBOSFP_OS_CLASS_MAX];
} libosfp_score_t;
typedef struct libosfp_result {
enum libosfp_error_code err;
enum libosfp_os_class_id likely_os_class; // top rated os class
unsigned int likely_score;
unsigned int perfect_score;
libosfp_score_t score;
} libosfp_result_t;
typedef struct libosfp_context {
char *fp_file;
void *score_db;
} libosfp_context_t;
libosfp_os_class_id_t libosfp_os_class_name_to_id(char *name);
const char *libosfp_os_class_id_to_name(libosfp_os_class_id_t os_class);
int libosfp_result_to_buf(libosfp_result_t *result, char *strbuf, unsigned int buf_len);
unsigned int libosfp_result_likely_os_class_score_get(libosfp_result_t *result);
const char *libosfp_result_likely_os_class_name_get(libosfp_result_t *result);
libosfp_error_code_t libosfp_header_match(libosfp_context_t *libosfp_context, unsigned char *ip_hdr, unsigned char *tcp_hdr, libosfp_result_t *result);
libosfp_error_code_t libosfp_context_setup(libosfp_context_t *libosfp_context);
libosfp_context_t *libosfp_context_create(char *fp_file);
void libosfp_context_destroy(libosfp_context_t *libosfp_context);
#endif

View File

@@ -1,415 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <malloc.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include "cJSON.h"
#include "libosfp.h"
#include "libosfp_fingerprint.h"
#include "libosfp_log.h"
#define LIBOSFP_ETHERNET_HEADER_LEN 14
#define LIBOSFP_VLAN_HEADER_LEN 4
#define LIBOSFP_IPV4_HEADER_LEN 20
#define LIBOSFP_IPV6_HEADER_LEN 40
#define LIBOSFP_TCP_HEADER_LEN 20
#define LIBOSFP_FINGERPRINT_FIELD_NAME_IP_ID "ip_id"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_IP_TOS "ip_tos"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_IP_TOTAL_LENGHT "ip_total_length"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_IP_TTL "ip_ttl"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_OFF "tcp_off"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP "tcp_timestamp"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP_ECHO_REPLY "tcp_timestamp_echo_reply"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_WCALING "tcp_window_scaling"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_SIZE "tcp_window_size"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_FLAGS "tcp_flags"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_MSS "tcp_mss"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS "tcp_options"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS_ORDERED "tcp_options_ordered"
#define LIBOSFP_FINGERPRINT_FIELD_NAME_OS "os"
typedef struct libosfp_tcp_opt {
unsigned char type;
unsigned char len;
const unsigned char *data;
} libosfp_tcp_opt_t;
libosfp_fingerprint_field_t fp_fields[LIBOSFP_FIELD_MAX] = {
{LIBOSFP_FINGERPRINT_FIELD_NAME_IP_ID, 1, LIBOSFP_FIELD_TYPE_UINT, 150, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_IP_TOS, 1, LIBOSFP_FIELD_TYPE_UINT, 25, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_IP_TOTAL_LENGHT, 1, LIBOSFP_FIELD_TYPE_UINT, 250, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_IP_TTL, 1, LIBOSFP_FIELD_TYPE_UINT, 200, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_OFF, 1, LIBOSFP_FIELD_TYPE_UINT, 250, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP, 0, LIBOSFP_FIELD_TYPE_UINT, 0, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP_ECHO_REPLY,1, LIBOSFP_FIELD_TYPE_UINT, 200, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_WCALING, 1, LIBOSFP_FIELD_TYPE_UINT, 200, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_SIZE, 1, LIBOSFP_FIELD_TYPE_UINT, 200, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_FLAGS, 1, LIBOSFP_FIELD_TYPE_UINT, 25, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_MSS, 1, LIBOSFP_FIELD_TYPE_UINT, 150, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS, 1, LIBOSFP_FIELD_TYPE_STRING, 400, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS_ORDERED, 1, LIBOSFP_FIELD_TYPE_STRING, 250, NULL, 0},
{LIBOSFP_FINGERPRINT_FIELD_NAME_OS, 0, LIBOSFP_FIELD_TYPE_STRING, 0, NULL, 0},
};
static char option_to_ascii(unsigned char type)
{
switch (type) {
case LIBOSFP_TCP_OPT_EOL:
return 'E';
case LIBOSFP_TCP_OPT_NOP:
return 'N';
case LIBOSFP_TCP_OPT_MSS:
return 'M';
case LIBOSFP_TCP_OPT_WSCALE:
return 'W';
case LIBOSFP_TCP_OPT_SACKOK:
return 'S';
case LIBOSFP_TCP_OPT_SACK:
return 'K';
case LIBOSFP_TCP_OPT_ECHO:
return 'J';
case LIBOSFP_TCP_OPT_ECHOREPLY:
return 'F';
case LIBOSFP_TCP_OPT_TIMESTAMP:
return 'T';
case LIBOSFP_TCP_OPT_POCONN:
return 'P';
case LIBOSFP_TCP_OPT_POSVC:
return 'R';
default:
return 'U';
}
}
static unsigned int compute_ip_ttl(unsigned int ip_ttl)
{
if (ip_ttl >= 0 && ip_ttl <= 32) {
ip_ttl = 32;
} else if (ip_ttl > 32 && ip_ttl <= 64) {
ip_ttl = 64;
} else if (ip_ttl > 64 && ip_ttl <= 128) {
ip_ttl = 128;
} else if (ip_ttl > 128) {
ip_ttl = 255;
}
return ip_ttl;
}
static unsigned int decode_tcp_options(libosfp_tcp_opt_t *tcp_opts, unsigned int max_opt_cnt, unsigned char *data, unsigned int len)
{
unsigned int offset = 0;
unsigned int tcp_opt_cnt = 0;
while (offset < len && tcp_opt_cnt < max_opt_cnt) {
unsigned char type;
unsigned char olen;
unsigned char *odata;
type = *(data + offset);
if (type == LIBOSFP_TCP_OPT_EOL || type == LIBOSFP_TCP_OPT_NOP) {
olen = 1;
} else {
olen = *(data + offset + 1);
if (olen < 2) {
break;
}
}
if (offset + olen > len) {
break;
}
odata = (olen > 2) ? (data + offset + 2) : NULL;
tcp_opts[tcp_opt_cnt].type = type;
tcp_opts[tcp_opt_cnt].len = olen;
tcp_opts[tcp_opt_cnt].data = odata;
offset += olen;
tcp_opt_cnt++;
}
return tcp_opt_cnt;
}
int libosfp_fingerprint_to_json_buf(libosfp_fingerprint_t *fp, char *strbuf, unsigned int buf_len)
{
int rlen = 0, ret, i;
cJSON *root;
if (fp == NULL || strbuf == NULL || buf_len == 0) {
return 0;
}
root = cJSON_CreateObject();
if (root == NULL) {
return 0;
}
for (i = 0; i < LIBOSFP_FIELD_MAX; i++) {
if (fp->fields[i].enabled) {
switch (fp_fields[i].type) {
case LIBOSFP_FIELD_TYPE_UINT:
cJSON_AddNumberToObject(root, fp_fields[i].name, *(unsigned int *)fp->fields[i].value);
break;
case LIBOSFP_FIELD_TYPE_STRING:
cJSON_AddStringToObject(root, fp_fields[i].name, (char *)fp->fields[i].value);
break;
default:
break;
}
} else {
cJSON_AddNullToObject(root, fp_fields[i].name);
}
}
if (!cJSON_PrintPreallocated(root, strbuf, buf_len, 1)) {
return 0;
}
cJSON_Delete(root);
return strlen(strbuf) + 1;
}
unsigned int libosfp_fingerprint_get_field_enabled(libosfp_field_id_t field_id)
{
return fp_fields[field_id].enabled;
}
unsigned int libosfp_fingerprint_get_field_importance(libosfp_field_id_t field_id)
{
return fp_fields[field_id].importance;
}
char *libosfp_fingerprint_get_field_name(libosfp_field_id_t field_id)
{
return fp_fields[field_id].name;
}
unsigned int libosfp_fingerprint_get_field_type(libosfp_field_id_t field_id)
{
return fp_fields[field_id].type;
}
void libosfp_fingerprint_setup_field(libosfp_fingerprint_t *fp, libosfp_field_id_t field_id, void *value, unsigned int len)
{
fp->fields[field_id].name = libosfp_fingerprint_get_field_name(field_id);
fp->fields[field_id].enabled = 1;
if (fp->value_buffer_used + len <= sizeof(fp->value_buffer)) {
memcpy(fp->value_buffer + fp->value_buffer_used, value, len);
fp->fields[field_id].value = fp->value_buffer + fp->value_buffer_used;
fp->fields[field_id].value_len = len;
fp->value_buffer_used += len;
} else {
fp->fields[field_id].value = NULL;
fp->fields[field_id].value_len = 0;
}
}
void libosfp_fingerprinting_tcp_option(unsigned char *pkt, unsigned int pktlen, libosfp_fingerprint_t *fp)
{
int ret,i;
char options[LIBOSFP_TCP_OPTLENMAX];
char options_ordered[LIBOSFP_TCP_OPTLENMAX];
unsigned int maxoffset = sizeof(options) - 3;
unsigned int ordered_maxoffset = sizeof(options_ordered) - 1;
unsigned int offset = 0;
unsigned int ordered_offset = 0;
unsigned int tcp_mss;
unsigned int tcp_ws;
unsigned int tcp_ter;
unsigned int tcp_opt_cnt;
libosfp_tcp_opt_t tcp_opts[LIBOSFP_TCP_OPTMAX];
tcp_opt_cnt = decode_tcp_options(tcp_opts, LIBOSFP_TCP_OPTMAX, pkt, pktlen);
for (i = 0; i < tcp_opt_cnt && offset < maxoffset && ordered_offset < ordered_maxoffset; i++) {
libosfp_tcp_opt_t *opt = &tcp_opts[i];
char letter = option_to_ascii(opt->type);
options[offset++] = letter;
options_ordered[ordered_offset++] = letter;
switch (opt->type) {
case LIBOSFP_TCP_OPT_EOL:
case LIBOSFP_TCP_OPT_NOP:
break;
case LIBOSFP_TCP_OPT_MSS:
if (opt->len != LIBOSFP_TCP_OPT_MSS_LEN) {
break;
}
tcp_mss = (unsigned int)ntohs(*(unsigned short *)opt->data);
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_MSS, &tcp_mss, sizeof(tcp_mss));
ret = snprintf(options + offset, sizeof(options), "%u", tcp_mss);
if (ret < 0 || offset + ret > maxoffset) {
break;
}
offset += ret;
break;
case LIBOSFP_TCP_OPT_WSCALE:
if (opt->len != LIBOSFP_TCP_OPT_WS_LEN) {
break;
}
tcp_ws = (unsigned int)*(unsigned char *)opt->data;
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_WINDOW_SCALING, &tcp_ws, sizeof(tcp_ws));
ret = snprintf(options + offset, sizeof(options), "%u", tcp_ws);
if (ret < 0 || offset + ret > maxoffset) {
break;
}
offset += ret;
break;
case LIBOSFP_TCP_OPT_TIMESTAMP:
if (opt->len != LIBOSFP_TCP_OPT_TS_LEN) {
break;
}
tcp_ter = ntohl(*(unsigned int *)(opt->data + 4));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY, &tcp_ter, sizeof(tcp_ter));
ret = snprintf(options + offset, sizeof(options), "%u", tcp_ter);
if (ret < 0 || offset + ret > maxoffset) {
break;
}
offset += ret;
break;
case LIBOSFP_TCP_OPT_SACKOK:
case LIBOSFP_TCP_OPT_SACK:
case LIBOSFP_TCP_OPT_ECHO:
case LIBOSFP_TCP_OPT_ECHOREPLY:
case LIBOSFP_TCP_OPT_POCONN:
case LIBOSFP_TCP_OPT_POSVC:
break;
default:
ret = snprintf(options + offset, sizeof(options), "%u", opt->type);
if (ret < 0 || offset + ret > maxoffset) {
break;
}
offset += ret;
}
options[offset++] = ',';
options[offset] = 0;
options_ordered[ordered_offset] = 0;
}
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_OPTIONS, options, strlen(options) + 1);
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_OPTIONS_ORDERED, options_ordered, strlen(options_ordered) + 1);
return;
}
int libosfp_fingerprinting_tcp(struct tcphdr *tcph, libosfp_fingerprint_t *fp)
{
if (tcph == NULL || fp == NULL) {
goto exit;
}
unsigned int tcp_off = tcph->doff << 2;
unsigned int tcp_window_size = ntohs(tcph->window);
unsigned int tcp_flags = *((unsigned char *)&tcph->window - 1);
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_OFF, &tcp_off, sizeof(tcp_off));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_WINDOW_SIZE, &tcp_window_size, sizeof(tcp_window_size));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_TCP_FLAGS, &tcp_flags, sizeof(tcp_flags));
// tcp options
if (tcp_off > LIBOSFP_TCP_HEADER_LEN) {
libosfp_fingerprinting_tcp_option((unsigned char *)tcph + LIBOSFP_TCP_HEADER_LEN, tcp_off - LIBOSFP_TCP_HEADER_LEN, fp);
}
return 0;
exit:
return -1;
}
int libosfp_fingerprinting_ipv4(struct iphdr *iph, libosfp_fingerprint_t *fp)
{
if (iph == NULL || fp == NULL) {
goto exit;
}
unsigned int ip_id = !!iph->id;
unsigned int ip_tos = iph->tos;
unsigned int ip_total_length = ntohs(iph->tot_len);
unsigned int ip_ttl = compute_ip_ttl(iph->ttl);
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_ID, &ip_id, sizeof(ip_id));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_TOS, &ip_tos, sizeof(ip_tos));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl));
return 0;
exit:
return -1;
}
int libosfp_fingerprinting_ipv6(struct ipv6hdr *iph, libosfp_fingerprint_t *fp)
{
if (iph == NULL || fp == NULL) {
goto exit;
}
//unsigned int ip_id = 0;
//unsigned int ip_tos = 0;
unsigned int ip_total_length = LIBOSFP_IPV6_HEADER_LEN + ntohs(iph->payload_len);
unsigned int ip_ttl = compute_ip_ttl(iph->hop_limit);
//libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_ID, &ip_id, sizeof(ip_id));
//libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_TOS, &ip_tos, sizeof(ip_tos));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length));
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl));
return 0;
exit:
return -1;
}
int libosfp_fingerprinting(unsigned char *iph, unsigned char *tcph, libosfp_fingerprint_t *fp)
{
int ret, ip_version;
if (iph == NULL || tcph == NULL || fp == NULL) {
goto exit;
}
memset(fp, 0, sizeof(libosfp_fingerprint_t));
ip_version = ((iph)[0] & 0xf0) >> 4;
switch (ip_version) {
case 4:
ret = libosfp_fingerprinting_ipv4((struct iphdr *)iph, fp);
break;
case 6:
ret = libosfp_fingerprinting_ipv6((struct ipv6hdr *)iph, fp);
break;
default:
ret = -1;
goto exit;
}
if (ret != 0) {
goto exit;
}
ret = libosfp_fingerprinting_tcp((struct tcphdr *)tcph, fp);
if (ret != 0) {
goto exit;
}
libosfp_fingerprint_setup_field(fp, LIBOSFP_FIELD_OS, "", strlen("") + 1);
return 0;
exit:
return -1;
}

View File

@@ -1,106 +0,0 @@
#ifndef __LIBOSFP_FINGERPRINT_H__
#define __LIBOSFP_FINGERPRINT_H__
#include "libosfp.h"
#define LIBOSFP_FINGERPRINT_VALUE_BUFFER_MAX 128
#define LIBOSFP_TCP_OPTLENMAX 64
#define LIBOSFP_TCP_OPTMAX 20
//# TCP Options (opt_type) - http://www.iana.org/assignments/tcp-parameters
#define LIBOSFP_TCP_OPT_EOL 0 //# end of option list
#define LIBOSFP_TCP_OPT_NOP 1 //# no operation
#define LIBOSFP_TCP_OPT_MSS 2 //# maximum segment size
#define LIBOSFP_TCP_OPT_WSCALE 3 //# window scale factor, RFC 1072
#define LIBOSFP_TCP_OPT_SACKOK 4 //# SACK permitted, RFC 2018
#define LIBOSFP_TCP_OPT_SACK 5 //# SACK, RFC 2018
#define LIBOSFP_TCP_OPT_ECHO 6 //# echo (obsolete), RFC 1072
#define LIBOSFP_TCP_OPT_ECHOREPLY 7 //# echo reply (obsolete), RFC 1072
#define LIBOSFP_TCP_OPT_TIMESTAMP 8 //# timestamps, RFC 1323
#define LIBOSFP_TCP_OPT_POCONN 9 //# partial order conn, RFC 1693
#define LIBOSFP_TCP_OPT_POSVC 10 //# partial order service, RFC 1693
#define LIBOSFP_TCP_OPT_CC 11 //# connection count, RFC 1644
#define LIBOSFP_TCP_OPT_CCNEW 12 //# CC.NEW, RFC 1644
#define LIBOSFP_TCP_OPT_CCECHO 13 //# CC.ECHO, RFC 1644
#define LIBOSFP_TCP_OPT_ALTSUM 14 //# alt checksum request, RFC 1146
#define LIBOSFP_TCP_OPT_ALTSUMDATA 15 //# alt checksum data, RFC 1146
#define LIBOSFP_TCP_OPT_SKEETER 16 //# Skeeter
#define LIBOSFP_TCP_OPT_BUBBA 17 //# Bubba
#define LIBOSFP_TCP_OPT_TRAILSUM 18 //# trailer checksum
#define LIBOSFP_TCP_OPT_MD5 19 //# MD5 signature, RFC 2385
#define LIBOSFP_TCP_OPT_SCPS 20 //# SCPS capabilities
#define LIBOSFP_TCP_OPT_SNACK 21 //# selective negative acks
#define LIBOSFP_TCP_OPT_REC 22 //# record boundaries
#define LIBOSFP_TCP_OPT_CORRUPT 23 //# corruption experienced
#define LIBOSFP_TCP_OPT_SNAP 24 //# SNAP
#define LIBOSFP_TCP_OPT_TCPCOMP 26 //# TCP compression filter
#define LIBOSFP_TCP_OPT_MAX 27 //# Quick-Start Response
#define LIBOSFP_TCP_OPT_USRTO 28 //# User Timeout Option (also, other known unauthorized use) [***][1] [RFC5482]
#define LIBOSFP_TCP_OPT_AUTH 29 //# TCP Authentication Option (TCP-AO) [RFC5925]
#define LIBOSFP_TCP_OPT_MULTIPATH 30 //# Multipath TCP (MPTCP)
#define LIBOSFP_TCP_OPT_FASTOPEN 34 //# TCP Fast Open Cookie [RFC7413]
#define LIBOSFP_TCP_OPY_ENCNEG 69 //# Encryption Negotiation (TCP-ENO) [RFC8547]
#define LIBOSFP_TCP_OPT_EXP1 253 //# RFC3692-style Experiment 1 (also improperly used for shipping products)
#define LIBOSFP_TCP_OPT_EXP2 254 //# RFC3692-style Experiment 2 (also improperly used for shipping products)
#define LIBOSFP_TCP_OPT_SACKOK_LEN 2
#define LIBOSFP_TCP_OPT_WS_LEN 3
#define LIBOSFP_TCP_OPT_TS_LEN 10
#define LIBOSFP_TCP_OPT_MSS_LEN 4
#define LIBOSFP_TCP_OPT_SACK_MIN_LEN 10 /* hdr 2, 1 pair 8 = 10 */
#define LIBOSFP_TCP_OPT_SACK_MAX_LEN 34 /* hdr 2, 4 pair 32= 34 */
#define LIBOSFP_TCP_OPT_TFO_MIN_LEN 4 /* kind, len, 2 bytes cookie: 4 */
#define LIBOSFP_TCP_OPT_TFO_MAX_LEN 18 /* kind, len, 18 */
typedef enum libosfp_field_id {
LIBOSFP_FIELD_IP_ID,
LIBOSFP_FIELD_IP_TOS,
LIBOSFP_FIELD_IP_TOTAL_LENGTH,
LIBOSFP_FIELD_IP_TTL,
LIBOSFP_FIELD_TCP_OFF,
LIBOSFP_FIELD_TCP_TIMESTAMP,
LIBOSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY,
LIBOSFP_FIELD_TCP_WINDOW_SCALING,
LIBOSFP_FIELD_TCP_WINDOW_SIZE,
LIBOSFP_FIELD_TCP_FLAGS,
LIBOSFP_FIELD_TCP_MSS,
LIBOSFP_FIELD_TCP_OPTIONS,
LIBOSFP_FIELD_TCP_OPTIONS_ORDERED,
LIBOSFP_FIELD_OS,
LIBOSFP_FIELD_MAX,
} libosfp_field_id_t;
typedef enum libosfp_field_type {
LIBOSFP_FIELD_TYPE_UNKNOWN,
LIBOSFP_FIELD_TYPE_UINT,
LIBOSFP_FIELD_TYPE_STRING,
LIBOSFP_FIELD_TYPE_MAX
} libosfp_field_type_t;
typedef struct libosfp_fingerprint_field {
char *name;
unsigned int enabled;
unsigned int type;
unsigned int importance;
void *value;
unsigned int value_len;
} libosfp_fingerprint_field_t;
typedef struct libosfp_fingerprint {
libosfp_fingerprint_field_t fields[LIBOSFP_FIELD_MAX];
char value_buffer[LIBOSFP_FINGERPRINT_VALUE_BUFFER_MAX];
unsigned value_buffer_used;
} libosfp_fingerprint_t;
char *libosfp_fingerprint_get_field_name(libosfp_field_id_t field_id);
unsigned int libosfp_fingerprint_get_field_enabled(libosfp_field_id_t field_id);
unsigned int libosfp_fingerprint_get_field_importance(libosfp_field_id_t field_id);
unsigned int libosfp_fingerprint_get_field_type(libosfp_field_id_t field_id);
int libosfp_fingerprint_to_json_buf(libosfp_fingerprint_t *fp, char *strbuf, unsigned int buf_len);
int libosfp_fingerprinting(unsigned char *iphdr, unsigned char *tcphdr, libosfp_fingerprint_t *fp);
#endif

View File

@@ -1,58 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include "libosfp.h"
#include "libosfp_log.h"
/* The maximum length of the log message */
#define LIBOSFP_MAX_LOG_MSG_LEN 2048
unsigned int libosfp_log_level = LIBOSFP_LOG_LEVEL_INFO;
void libosfp_log_message(unsigned int x, const char *file, const int line, const char *func, const char *msg)
{
char buffer[LIBOSFP_MAX_LOG_MSG_LEN] = "";
char log_time_buf[128];
time_t now;
struct tm tm_now;
time(&now);
localtime_r(&now, &tm_now);
strftime(log_time_buf, sizeof(log_time_buf), "%Y-%m-%d %T", &tm_now);
switch (x) {
case LIBOSFP_LOG_LEVEL_DEBUG:
snprintf(buffer, sizeof(buffer), "[%s][DEBUG][%s:%d %s] %s", log_time_buf, file, line, func, msg);
break;
case LIBOSFP_LOG_LEVEL_INFO:
snprintf(buffer, sizeof(buffer), "[%s][INFO][%s:%d %s] %s", log_time_buf, file, line, func, msg);
break;
case LIBOSFP_LOG_LEVEL_WARNING:
snprintf(buffer, sizeof(buffer), "[%s][WARN][%s:%d %s] %s", log_time_buf, file, line, func, msg);
break;
case LIBOSFP_LOG_LEVEL_ERROR:
snprintf(buffer, sizeof(buffer), "[%s][ERROR][%s:%d %s] %s", log_time_buf, file, line, func, msg);
break;
}
}
void libosfp_log(unsigned int x, const char *file, const char *func, const int line, const char *fmt, ...)
{
if (libosfp_log_level >= x ) {
char msg[LIBOSFP_MAX_LOG_MSG_LEN];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
libosfp_log_message(x, file, line, func, msg);
}
}
void libosfp_log_level_set(libosfp_log_level_t level)
{
libosfp_log_level = level;
}

View File

@@ -1,20 +0,0 @@
#ifndef __LIBOSFP_LOG_H__
#define __LIBOSFP_LOG_H__
typedef enum libosfp_log_level {
LIBOSFP_LOG_LEVEL_DEBUG,
LIBOSFP_LOG_LEVEL_INFO,
LIBOSFP_LOG_LEVEL_WARNING,
LIBOSFP_LOG_LEVEL_ERROR
} libosfp_log_level_t;
#define libosfp_log_debug(...) libosfp_log(LIBOSFP_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define libosfp_log_info(...) libosfp_log(LIBOSFP_LOG_LEVEL_INFO, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define libosfp_log_warning(...) libosfp_log(LIBOSFP_LOG_LEVEL_WARNING, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define libosfp_log_error(...) libosfp_log(LIBOSFP_LOG_LEVEL_ERROR, __FILE__, __LINE__, __FUNCTION__,__VA_ARGS__)
void libosfp_log_level_set(libosfp_log_level_t level);
void libosfp_log(unsigned int x, const char *file, const char *func, const int line, const char *fmt, ...);
#endif

View File

@@ -1,539 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <malloc.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "uthash.h"
#include "cJSON.h"
#include "libosfp.h"
#include "libosfp_fingerprint.h"
#include "libosfp_score_db.h"
#include "libosfp_log.h"
#define LIBOSFP_SCORE_DB_FIELD_UINT_VALUE_MAX 65536
typedef struct libosfp_score_db_array_data {
libosfp_score_t *array_head;
unsigned int array_len;
} libosfp_score_db_array_data_t;
typedef struct libosfp_score_db_hash_element {
char *key;
unsigned int keylen;
libosfp_score_t *score;
UT_hash_handle hh;
} libosfp_score_db_hash_element_t;
typedef struct libosfp_score_db_hash_data {
libosfp_score_db_hash_element_t *hash_head;
} libosfp_score_db_hash_data_t;
int libosfp_score_db_array_add(void *data, libosfp_score_t *score, void *value, unsigned int len)
{
int ret = -1, i;
unsigned int index;
libosfp_score_db_array_data_t *array_data = (libosfp_score_db_array_data_t *)data;
if (array_data == NULL || score == NULL || value == NULL || len != sizeof(unsigned int)) {
goto exit;
}
if (array_data->array_head == NULL || array_data->array_len == 0) {
goto exit;
}
index = *(unsigned int *)value;
if (index >= array_data->array_len) {
goto exit;
}
for (i = 0; i < LIBOSFP_OS_CLASS_MAX; i++) {
array_data->array_head[index].os_class_score[i] += score->os_class_score[i];
}
return 0;
exit:
return ret;
}
libosfp_score_t *libosfp_score_db_array_match(void *data, void *value, unsigned int len)
{
unsigned int index;
libosfp_score_db_array_data_t *array_data = (libosfp_score_db_array_data_t *)data;
if (array_data == NULL || value == NULL || len != sizeof(unsigned int)) {
return NULL;
}
if (array_data->array_head == NULL || array_data->array_len == 0) {
return NULL;
}
index = *(unsigned int *)value;
if (index >= array_data->array_len) {
return NULL;
}
return &((array_data->array_head)[index]);
}
void *libosfp_score_db_array_create(void)
{
libosfp_score_db_array_data_t *array_data = calloc(1, sizeof(libosfp_score_db_array_data_t));
if (array_data == NULL) {
return NULL;
}
array_data->array_head = calloc(LIBOSFP_SCORE_DB_FIELD_UINT_VALUE_MAX, sizeof(libosfp_score_t));
if (array_data->array_head == NULL) {
free(array_data);
return NULL;
}
array_data->array_len = LIBOSFP_SCORE_DB_FIELD_UINT_VALUE_MAX;
return (void *)array_data;
}
void libosfp_score_db_array_destroy(void *data) {
libosfp_score_db_array_data_t *array_data = (libosfp_score_db_array_data_t *)data;
if (array_data) {
if (array_data->array_head) {
free(array_data->array_head);
}
free(array_data);
}
}
int libosfp_score_db_hash_add(void *data, libosfp_score_t *score, void *value, unsigned int len)
{
int ret = -1, i;
libosfp_score_db_hash_data_t *hash_data = (libosfp_score_db_hash_data_t *)data;
libosfp_score_db_hash_element_t *element = NULL;
if (hash_data == NULL || score == NULL || value == NULL || len == 0) {
goto exit;
}
HASH_FIND(hh, hash_data->hash_head, value, len, element);
if (element == NULL) {
element = (libosfp_score_db_hash_element_t *)calloc(1, sizeof(libosfp_score_db_hash_element_t));
if (element == NULL) {
goto exit;
}
element->key = strdup(value);
element->keylen = len;
element->score = (libosfp_score_t *)calloc(1, sizeof(libosfp_score_t));
if (element->score == NULL) {
free(element);
element = NULL;
goto exit;
}
HASH_ADD_KEYPTR(hh, hash_data->hash_head, element->key, element->keylen, element);
}
for (i = 0; i < LIBOSFP_OS_CLASS_MAX; i++) {
element->score->os_class_score[i] += score->os_class_score[i];
}
return 0;
exit:
return ret;
}
libosfp_score_t *libosfp_score_db_hash_match(void *data, void *value, unsigned int len)
{
libosfp_score_db_hash_data_t *hash_data = (libosfp_score_db_hash_data_t *)data;
libosfp_score_db_hash_element_t *element = NULL;
if (data == NULL || value == NULL || len == 0) {
return NULL;
}
if (hash_data->hash_head == NULL) {
return NULL;
}
HASH_FIND(hh, hash_data->hash_head, value, len, element);
if (element == NULL) {
return NULL;
}
return element->score;
}
void *libosfp_score_db_hash_create(void)
{
return (void*)calloc(1, sizeof(libosfp_score_db_hash_data_t));
}
void libosfp_score_db_hash_destroy(void *data) {
libosfp_score_db_hash_data_t *hash_data = (libosfp_score_db_hash_data_t *)data;
libosfp_score_db_hash_element_t *element = NULL;
libosfp_score_db_hash_element_t *tmp = NULL;
if (hash_data) {
if (hash_data->hash_head) {
HASH_ITER(hh, hash_data->hash_head, element, tmp) {
HASH_DELETE(hh, hash_data->hash_head, element);
if (element) {
if (element->key) {
free(element->key);
}
if (element->score) {
free(element->score);
}
free(element);
}
}
}
free(hash_data);
}
}
libosfp_score_t *libosfp_score_db_filed_match(libosfp_field_score_db_t *db, void *value, unsigned int len)
{
if (db == NULL || value == NULL || len == 0) {
return NULL;
}
return db->match(db->data, value, len);
}
char *libosfp_score_db_read_file(char *fp_file)
{
int ret = -1;
char *file_buffer = NULL;
unsigned int file_len = 0;
FILE *fp = NULL;
struct stat st;
if (0 > stat(fp_file, &st)) {
printf("stat() on '%s' failed.\n", fp_file);
goto exit;
}
if (st.st_size == 0) {
printf("Empty file: %s.\n", fp_file);
goto exit;
}
file_len = (unsigned int)st.st_size;
file_buffer = malloc(file_len);
if (file_buffer == NULL) {
printf("Not enough memory for file buffer. file name: %s\n",fp_file);
goto exit;
}
fp = fopen(fp_file, "r");
if (fp == NULL) {
printf("Cannot open '%s' for reading.\n", fp_file);
goto exit;
}
ret = fread(file_buffer, 1, file_len, fp);
if (ret < 0) {
free(file_buffer);
fclose(fp);
goto exit;
}
fclose(fp);
return file_buffer;
exit:
return NULL;
}
int libosfp_score_db_load_field(libosfp_field_score_db_t *db, cJSON *field, libosfp_os_class_id_t os_class)
{
int ret = -1;
libosfp_score_t score = {0};
void *value_ptr;
unsigned int value_len;
switch (field->type) {
case cJSON_Number:
value_ptr = (void *)&field->valueint;
value_len = sizeof(field->valueint);
break;
case cJSON_String:
value_ptr = (void *)field->valuestring;
value_len = strlen(field->valuestring) + 1;
break;
case cJSON_NULL:
ret = 0;
goto exit;
default:
goto exit;
}
score.os_class_score[os_class] = 1;
ret = db->add(db->data, &score, value_ptr, value_len);
if (ret != 0) {
goto exit;
}
db->entry_count++;
return 0;
exit:
return ret;
}
int libosfp_score_db_load_entry(libosfp_score_db_t *score_db, cJSON *entry)
{
int ret = -1, i;
cJSON *field = NULL;
if (score_db == NULL || entry == NULL) {
goto exit;
}
field = cJSON_GetObjectItem(entry, libosfp_fingerprint_get_field_name(LIBOSFP_FIELD_OS));
if (field == NULL || field->valuestring == NULL) {
goto exit;
}
libosfp_os_class_id_t os_class = libosfp_os_class_name_to_id(field->valuestring);
if (os_class >= LIBOSFP_OS_CLASS_MAX) {
goto exit;
}
for (i = 0; i < LIBOSFP_FIELD_OS; i++) {
libosfp_field_score_db_t *db = &score_db->field_score_dbs[i];
if (db == NULL) {
goto exit;
}
if (!db->enabled) {
continue;
}
field = cJSON_GetObjectItem(entry, libosfp_fingerprint_get_field_name(i));
if (field == NULL) {
printf("json entry missing field: %s\n%s\n",
libosfp_fingerprint_get_field_name(i), cJSON_Print(entry));
continue;
}
ret = libosfp_score_db_load_field(db, field, os_class);
if (ret != 0) {
printf("json entry field load failed. field: %s\n%s\n",
libosfp_fingerprint_get_field_name(i), cJSON_Print(entry));
continue;
}
}
score_db->entry_count++;
score_db->os_class_entry_count[os_class]++;
return 0;
exit:
return ret;
}
int libosfp_score_db_load(libosfp_score_db_t *score_db, char *fp_file)
{
int ret = LIBOSFP_EINVAL, i;
char *file_buffer;
libosfp_field_score_db_t *field_score_db;
cJSON *root = NULL;
cJSON *entry = NULL;
if (score_db == NULL || fp_file == NULL) {
goto exit;
}
file_buffer = libosfp_score_db_read_file(fp_file);
if (file_buffer == NULL) {
ret = LIBOSFP_ERR_READ_FILE;
goto exit;
}
root = cJSON_Parse(file_buffer);
if (root == NULL) {
ret = LIBOSFP_ERR_PARSE_FILE;
goto exit;
}
score_db->entry_count = cJSON_GetArraySize(root);
for (i = 0; i < LIBOSFP_FIELD_MAX; i++) {
field_score_db = &score_db->field_score_dbs[i];
if (field_score_db->enabled) {
score_db->perfect_score += libosfp_fingerprint_get_field_importance(i);
}
}
for (i = 0; i < score_db->entry_count; i++) {
entry = cJSON_GetArrayItem(root, i);
if (entry) {
ret = libosfp_score_db_load_entry(score_db, entry);
if (ret != 0) {
printf("json entry load failed.\n%s\n", cJSON_Print(entry));
continue;
}
}
}
cJSON_Delete(root);
return 0;
exit:
return ret;
}
int libosfp_score_db_score(libosfp_context_t *libosfp_context, libosfp_fingerprint_t *fp, libosfp_result_t *result)
{
int ret = -1, i, j;
void *field_value;
unsigned int os_class_score;
unsigned int perfect_score;
unsigned int entry_count;
unsigned int importance;
unsigned int field_len;
libosfp_score_t *score;
libosfp_score_db_t *score_db;
libosfp_field_score_db_t *field_score_db;
if (libosfp_context == NULL || fp == NULL || result == NULL) {
goto exit;
}
memset(result, 0, sizeof(libosfp_result_t));
score_db = (libosfp_score_db_t*)libosfp_context->score_db;
for (i = 0; i < LIBOSFP_FIELD_MAX; i++) {
if (!fp->fields[i].enabled) {
continue;
}
field_score_db = &score_db->field_score_dbs[i];
if (!field_score_db->enabled) {
continue;
}
field_value = fp->fields[i].value;
field_len = fp->fields[i].value_len;
score = libosfp_score_db_filed_match(field_score_db, field_value, field_len);
if (score == NULL) {
continue;
}
importance = libosfp_fingerprint_get_field_importance(i);
for (j = 0; j < LIBOSFP_OS_CLASS_MAX; j++) {
result->score.os_class_score[j] += score->os_class_score[j] * importance;
}
if (i == LIBOSFP_FIELD_TCP_OPTIONS) {
i++;
}
}
perfect_score = score_db->perfect_score;
result->perfect_score = perfect_score;
for (j = 0; j < LIBOSFP_OS_CLASS_MAX; j++) {
entry_count = score_db->os_class_entry_count[j];
os_class_score = result->score.os_class_score[j];
if (entry_count == 0 || perfect_score == 0) {
continue;
}
os_class_score = 100 * os_class_score / entry_count;
os_class_score = (unsigned int)((float)os_class_score / (float)perfect_score);
if (result->likely_score < os_class_score) {
result->likely_score = os_class_score;
result->likely_os_class = j;
}
result->score.os_class_score[j] = os_class_score;
}
return 0;
exit:
return ret;
}
libosfp_score_db_t *libosfp_score_db_create(void)
{
int ret = -1, i;
libosfp_score_db_t *score_db;
libosfp_field_score_db_t *db;
score_db = calloc(1, sizeof(libosfp_score_db_t));
for (i = 0; i < LIBOSFP_FIELD_MAX; i++) {
db = &score_db->field_score_dbs[i];
db->enabled = libosfp_fingerprint_get_field_enabled(i);
db->type = libosfp_fingerprint_get_field_type(i);
switch (score_db->field_score_dbs[i].type) {
case LIBOSFP_FIELD_TYPE_UINT:
score_db->field_score_dbs[i].create = libosfp_score_db_array_create;
score_db->field_score_dbs[i].destroy = libosfp_score_db_array_destroy;
score_db->field_score_dbs[i].add = libosfp_score_db_array_add;
score_db->field_score_dbs[i].match = libosfp_score_db_array_match;
break;
case LIBOSFP_FIELD_TYPE_STRING:
score_db->field_score_dbs[i].create = libosfp_score_db_hash_create;
score_db->field_score_dbs[i].destroy = libosfp_score_db_hash_destroy;
score_db->field_score_dbs[i].add = libosfp_score_db_hash_add;
score_db->field_score_dbs[i].match = libosfp_score_db_hash_match;
break;
default:
goto exit;
}
db->data = score_db->field_score_dbs[i].create();
if (db->data == NULL) {
continue;
}
}
return score_db;
exit:
if (score_db) {
libosfp_score_db_destroy(score_db);
}
return NULL;
}
void libosfp_score_db_destroy(libosfp_score_db_t *score_db)
{
int i;
libosfp_field_score_db_t *db;
if (score_db) {
for (i = 0; i < LIBOSFP_FIELD_MAX; i++) {
db = &score_db->field_score_dbs[i];
db->destroy(db->data);
db->data = NULL;
}
free(score_db);
}
}

View File

@@ -1,34 +0,0 @@
#ifndef __LIBOSFP_SCORE_DB_H__
#define __LIBOSFP_SCORE_DB_H__
#include "libosfp.h"
#include "libosfp_fingerprint.h"
typedef struct libosfp_field_score_db {
unsigned int enabled;
unsigned int type;
unsigned int entry_count;
void *data;
void *(*create)(void);
void (*destroy)(void *);
int (*add)(void *data, libosfp_score_t *, void *, unsigned int);
libosfp_score_t *(*match)(void *, void *, unsigned int);
} libosfp_field_score_db_t;
typedef struct libosfp_score_db {
unsigned int entry_count;
unsigned int perfect_score;
unsigned int os_class_entry_count[LIBOSFP_OS_CLASS_MAX];
libosfp_field_score_db_t field_score_dbs[LIBOSFP_FIELD_MAX];
} libosfp_score_db_t;
int libosfp_score_db_load(libosfp_score_db_t *score_db, char *fp_file);
int libosfp_score_db_score(libosfp_context_t *libosfp_context, libosfp_fingerprint_t *fp, libosfp_result_t *result);
libosfp_score_db_t *libosfp_score_db_create(void);
void libosfp_score_db_destroy(libosfp_score_db_t *score_db);
#endif

298
src/osfp.c Normal file
View File

@@ -0,0 +1,298 @@
#include "osfp_common.h"
#include "osfp.h"
#include "osfp_fingerprint.h"
#include "osfp_score_db.h"
#include "osfp_log.h"
#define OSFP_DEFAULT_RESULT_BUFLEN_MAX 512
#define OSFP_LOWEST_SCORE_LIMIT 20
static struct osfp_result *osfp_result_build(struct osfp_os_class_score *os_class_score)
{
int i;
unsigned int tmp_score;
unsigned int sum_score;
unsigned int likely_score;
enum osfp_os_class_id likely_os_class;
struct osfp_result *result;
result = calloc(1, sizeof(struct osfp_result));
if (result == NULL) {
goto exit;
}
likely_score = 0;
likely_os_class = OSFP_OS_CLASS_OTHERS;
sum_score = 0;
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
tmp_score = os_class_score->scores[i];
if (likely_score < tmp_score) {
likely_score = tmp_score;
likely_os_class = i;
}
result->details[i].score = tmp_score;
sum_score += tmp_score;
}
if (sum_score) {
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
result->details[i].possibility = OSFP_PERCENTILE * result->details[i].score / sum_score;
}
}
if (likely_score < OSFP_LOWEST_SCORE_LIMIT) {
likely_os_class = OSFP_OS_CLASS_OTHERS;
} else {
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
if (likely_os_class == i) {
continue;
}
if (likely_score == os_class_score->scores[i]) {
if (likely_os_class == OSFP_OS_CLASS_LINUX && i == OSFP_OS_CLASS_ANDROID) {
continue;
} else if (likely_os_class == OSFP_OS_CLASS_MAC_OS && i == OSFP_OS_CLASS_IOS) {
continue;
} else {
likely_os_class = OSFP_OS_CLASS_UNKNOWN;
break;
}
}
}
}
result->likely_os_class = likely_os_class;
return result;
exit:
return NULL;
}
const char *osfp_result_os_name_get(struct osfp_result *result)
{
enum osfp_os_class_id os_class;
if (result == NULL) {
return NULL;
}
os_class = result->likely_os_class;
if (os_class < 0 || os_class >= OSFP_OS_CLASS_MAX) {
return NULL;
}
return osfp_os_class_id_to_name(os_class);
}
char *osfp_result_score_detail_export(struct osfp_result *result)
{
int i;
char *result_str = NULL;
cJSON *root = NULL;
cJSON *array;
cJSON *os_score;
if (result == NULL) {
goto exit;
}
if (result->json_str != NULL) {
result_str = result->json_str;
goto exit;
}
root = cJSON_CreateObject();
if (root == NULL) {
goto exit;
}
os_score = cJSON_AddObjectToObject(root, "likely");
if (os_score) {
cJSON_AddStringToObject(os_score, "name", osfp_os_class_id_to_name(result->likely_os_class));
cJSON_AddNumberToObject(os_score, "score", result->details[result->likely_os_class].score);
//cJSON_AddNumberToObject(os_score, "possibility", result->details[result->likely_os_class].possibility);
}
array = cJSON_AddArrayToObject(root, "details");
if (array) {
for (i = OSFP_OS_CLASS_WINDOWS; i < OSFP_OS_CLASS_OTHERS; i++) {
os_score = cJSON_CreateObject();
if (os_score) {
cJSON_AddStringToObject(os_score, "name", osfp_os_class_id_to_name(i));
cJSON_AddNumberToObject(os_score, "score", result->details[i].score);
//cJSON_AddNumberToObject(os_score, "possibility", result->details[i].possibility);
cJSON_AddItemToArray(array, os_score);
}
}
}
result_str = cJSON_Print(root);
if (result_str == NULL) {
goto exit;
}
result->json_str = result_str;
exit:
if (root) {
cJSON_Delete(root);
}
return result_str;
}
void osfp_result_free(struct osfp_result *result)
{
if (result) {
if (result->json_str) {
free(result->json_str);
}
free(result);
}
}
struct osfp_result *osfp_ipv4_identify(struct osfp_db *db, struct iphdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len)
{
int ret = OSFP_EINVAL;
struct osfp_fingerprint fp;
struct osfp_os_class_score os_class_score;
struct osfp_result *result;
if (db == NULL || l3_hdr == NULL || l4_hdr == NULL || l4_hdr == 0) {
goto exit;
}
ret = osfp_fingerprinting((unsigned char *)l3_hdr, (unsigned char *)l4_hdr, (unsigned int)l4_hdr_len, &fp, 4);
if (ret != 0) {
goto exit;
}
ret = osfp_score_db_score(db->score_db, 0, &fp, &os_class_score);
if (ret != 0) {
goto exit;
}
result = osfp_result_build(&os_class_score);
if (result == NULL) {
goto exit;
}
return result;
exit:
return NULL;
}
struct osfp_result *osfp_ipv6_identify(struct osfp_db *db, struct ipv6hdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len)
{
int ret = OSFP_EINVAL;
struct osfp_fingerprint fp;
struct osfp_os_class_score os_class_score;
struct osfp_result *result;
if (db == NULL || l3_hdr == NULL || l4_hdr == NULL) {
goto exit;
}
ret = osfp_fingerprinting((unsigned char *)l3_hdr, (unsigned char *)l4_hdr, (unsigned int)l4_hdr_len, &fp, 6);
if (ret != 0) {
goto exit;
}
ret = osfp_score_db_score(db->score_db, 0, &fp, &os_class_score);
if (ret != 0) {
goto exit;
}
result = osfp_result_build(&os_class_score);
if (result == NULL) {
goto exit;
}
return result;
exit:
return NULL;
}
struct osfp_result *osfp_json_identify(struct osfp_db *db, const char *json_str)
{
int ret = OSFP_EINVAL;
struct osfp_fingerprint fp;
struct osfp_os_class_score os_class_score;
struct osfp_result *result;
if (db == NULL) {
goto exit;
}
ret = osfp_fingerprint_from_json(&fp, json_str);
if (ret != 0) {
goto exit;
}
ret = osfp_score_db_score(db->score_db, 0, &fp, &os_class_score);
if (ret != 0) {
goto exit;
}
result = osfp_result_build(&os_class_score);
if (result == NULL) {
goto exit;
}
return result;
exit:
return NULL;
}
struct osfp_db *osfp_db_new(const char *db_json_file)
{
int ret;
struct osfp_db *db;
db = calloc(1, sizeof(struct osfp_db));
if (db == NULL) {
goto exit;
}
if (db_json_file != NULL) {
if (0 != access(db_json_file, R_OK)) {
goto exit;
}
db->db_json_path = strdup((const char*)db_json_file);
if (db->db_json_path == NULL) {
goto exit;
}
}
db->score_db = (void *)osfp_score_db_create();
if (db->score_db == NULL) {
goto exit;
}
ret = osfp_score_db_load((struct osfp_score_db *)db->score_db, db->db_json_path);
if (ret != 0) {
goto exit;
}
return db;
exit:
if (db) {
osfp_db_free(db);
}
return NULL;
}
void osfp_db_free(struct osfp_db *db)
{
if (db) {
if (db->db_json_path) {
free(db->db_json_path);
}
if (db->score_db) {
osfp_score_db_destroy(db->score_db);
}
free(db);
}
}

130
src/osfp.h Normal file
View File

@@ -0,0 +1,130 @@
#ifndef __OSFP_H__
#define __OSFP_H__
#include <stddef.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
/**
* @brief 定义操作系统类别的名称常量。
*/
#define OSFP_OS_CLASS_NAME_UNKNOWN "Unknown"
#define OSFP_OS_CLASS_NAME_WINDOWS "Windows"
#define OSFP_OS_CLASS_NAME_LINUX "Linux"
#define OSFP_OS_CLASS_NAME_MAC_OS "Mac OS"
#define OSFP_OS_CLASS_NAME_IOS "iOS"
#define OSFP_OS_CLASS_NAME_ANDROID "Android"
#define OSFP_OS_CLASS_NAME_OTHERS "Others"
/**
* @brief 枚举表示不同的操作系统类别。
*/
enum osfp_os_class_id {
OSFP_OS_CLASS_UNKNOWN, // 未知
OSFP_OS_CLASS_WINDOWS, // Windows
OSFP_OS_CLASS_LINUX, // Linux
OSFP_OS_CLASS_MAC_OS, // Mac OS
OSFP_OS_CLASS_IOS, // iOS
OSFP_OS_CLASS_ANDROID, // Android
OSFP_OS_CLASS_OTHERS, // 其他
OSFP_OS_CLASS_MAX,
};
/**
* @brief 结构体用于 osfp_result 中的详细结果。
*/
struct osfp_result_detail {
unsigned int score; // 得分
unsigned int possibility; // 可能性
};
/**
* @brief 结构体用于表示操作系统识别结果。
*/
struct osfp_result {
char *json_str; // JSON 字符串
enum osfp_os_class_id likely_os_class; // 最可能的操作系统类别
struct osfp_result_detail details[OSFP_OS_CLASS_MAX]; // 详细结果数组
};
/**
* @brief 结构体用于表示操作系统指纹库。
*/
struct osfp_db {
char *db_json_path; // 操作系统指纹库 JSON 文件路径
void *score_db; // 分数数据库指针
};
/**
* @brief 创建一个新的操作系统指纹库。
*
* @param db_json_path 操作系统指纹库 JSON 文件的路径。
* @return 指向新创建的操作系统指纹库的指针。
*/
struct osfp_db *osfp_db_new(const char *db_json_path);
/**
* @brief 释放操作系统指纹库占用的内存。
*
* @param db 指向要释放的操作系统指纹库的指针。
*/
void osfp_db_free(struct osfp_db *db);
/**
* @brief 通过 IPv4 头部和 TCP 头部识别操作系统。
*
* @param db 操作系统指纹库。
* @param l3_hdr 指向 IPv4 头部的指针。
* @param l4_hdr 指向 TCP 头部的指针。
* @param l4_hdr_len TCP 头部的长度注意包含TCP选项部分
* @return 指向操作系统识别结果的指针。
*/
struct osfp_result *osfp_ipv4_identify(struct osfp_db *db, struct iphdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len);
/**
* @brief 通过 IPv6 头部和 TCP 头部识别操作系统。
*
* @param db 操作系统指纹库。
* @param l3_hdr 指向 IPv6 头部的指针。
* @param l4_hdr 指向 TCP 头部的指针。
* @param l4_hdr_len TCP 头部的长度注意包含TCP选项部分
* @return 指向操作系统识别结果的指针(注意:内存需要使用者释放)。
*/
struct osfp_result *osfp_ipv6_identify(struct osfp_db *db, struct ipv6hdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len);
/**
* @brief 通过 json 格式的指纹识别操作系统。
*
* @param db 操作系统指纹库。
* @param json_str 指纹字符串。
* @return 指向操作系统识别结果的指针(注意:内存需要使用者释放)。
*/
struct osfp_result *osfp_json_identify(struct osfp_db *db, const char *json_str);
/**
* @brief 获取操作系统识别结果的操作系统名称。
*
* @param result 操作系统识别结果。
* @return 指向操作系统名称的常量字符指针注意这块内存将由osfp_result_free释放
*/
const char *osfp_result_os_name_get(struct osfp_result *result);
/**
* @brief 导出操作系统识别结果的得分详情。
*
* @param result 操作系统识别结果。
* @return 指向得分详情字符串的指针(注意:内存需要使用者释放)。
*/
char *osfp_result_score_detail_export(struct osfp_result *result);
/**
* @brief 释放操作系统识别结果占用的内存。
*
* @param result 操作系统识别结果。
*/
void osfp_result_free(struct osfp_result *result);
#endif

40
src/osfp_common.c Normal file
View File

@@ -0,0 +1,40 @@
#include "osfp_common.h"
#include "osfp.h"
const char *os_class_name[OSFP_OS_CLASS_MAX] = {
OSFP_OS_CLASS_NAME_UNKNOWN,
OSFP_OS_CLASS_NAME_WINDOWS,
OSFP_OS_CLASS_NAME_LINUX,
OSFP_OS_CLASS_NAME_MAC_OS,
OSFP_OS_CLASS_NAME_IOS,
OSFP_OS_CLASS_NAME_ANDROID,
OSFP_OS_CLASS_NAME_OTHERS
};
enum osfp_os_class_id osfp_os_class_name_to_id(char *name)
{
enum osfp_os_class_id os_class;
if (0 == strncmp(name, OSFP_OS_CLASS_NAME_WINDOWS, strlen(OSFP_OS_CLASS_NAME_WINDOWS))) {
os_class = OSFP_OS_CLASS_WINDOWS;
} else if (0 == strncmp(name, OSFP_OS_CLASS_NAME_LINUX, strlen(OSFP_OS_CLASS_NAME_LINUX))) {
os_class = OSFP_OS_CLASS_LINUX;
} else if (0 == strncmp(name, OSFP_OS_CLASS_NAME_MAC_OS, strlen(OSFP_OS_CLASS_NAME_MAC_OS))) {
os_class = OSFP_OS_CLASS_MAC_OS;
} else if (0 == strncmp(name, OSFP_OS_CLASS_NAME_IOS, strlen(OSFP_OS_CLASS_NAME_IOS))) {
os_class = OSFP_OS_CLASS_IOS;
} else if (0 == strncmp(name, OSFP_OS_CLASS_NAME_ANDROID, strlen(OSFP_OS_CLASS_NAME_ANDROID))) {
os_class = OSFP_OS_CLASS_ANDROID;
} else {
os_class = OSFP_OS_CLASS_MAX;
}
return os_class;
}
const char *osfp_os_class_id_to_name(enum osfp_os_class_id os_class)
{
return os_class_name[os_class];
}

133
src/osfp_common.h Normal file
View File

@@ -0,0 +1,133 @@
#ifndef __OSFP_COMMON_H__
#define __OSFP_COMMON_H__
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <malloc.h>
#include <stdarg.h>
#include <time.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "utarray.h"
#include "uthash.h"
#include "utlist.h"
#include "utringbuffer.h"
#include "utstack.h"
#include "utstring.h"
#include "cJSON.h"
#include "osfp.h"
#define OSFP_TCP_OPTLENMAX 64
#define OSFP_TCP_OPTMAX 20
#define OSFP_ETHERNET_HEADER_LEN 14
#define OSFP_VLAN_HEADER_LEN 4
#define OSFP_IPV4_HEADER_LEN 20
#define OSFP_IPV6_HEADER_LEN 40
#define OSFP_TCP_HEADER_LEN 20
#define OSFP_TCP_DATA_OFF_MAX 60
//# TCP Options (opt_type) - http://www.iana.org/assignments/tcp-parameters
#define OSFP_TCP_OPT_EOL 0 //# end of option list
#define OSFP_TCP_OPT_NOP 1 //# no operation
#define OSFP_TCP_OPT_MSS 2 //# maximum segment size
#define OSFP_TCP_OPT_WSCALE 3 //# window scale factor, RFC 1072
#define OSFP_TCP_OPT_SACKOK 4 //# SACK permitted, RFC 2018
#define OSFP_TCP_OPT_SACK 5 //# SACK, RFC 2018
#define OSFP_TCP_OPT_ECHO 6 //# echo (obsolete), RFC 1072
#define OSFP_TCP_OPT_ECHOREPLY 7 //# echo reply (obsolete), RFC 1072
#define OSFP_TCP_OPT_TIMESTAMP 8 //# timestamps, RFC 1323
#define OSFP_TCP_OPT_POCONN 9 //# partial order conn, RFC 1693
#define OSFP_TCP_OPT_POSVC 10 //# partial order service, RFC 1693
#define OSFP_TCP_OPT_CC 11 //# connection count, RFC 1644
#define OSFP_TCP_OPT_CCNEW 12 //# CC.NEW, RFC 1644
#define OSFP_TCP_OPT_CCECHO 13 //# CC.ECHO, RFC 1644
#define OSFP_TCP_OPT_ALTSUM 14 //# alt checksum request, RFC 1146
#define OSFP_TCP_OPT_ALTSUMDATA 15 //# alt checksum data, RFC 1146
#define OSFP_TCP_OPT_SKEETER 16 //# Skeeter
#define OSFP_TCP_OPT_BUBBA 17 //# Bubba
#define OSFP_TCP_OPT_TRAILSUM 18 //# trailer checksum
#define OSFP_TCP_OPT_MD5 19 //# MD5 signature, RFC 2385
#define OSFP_TCP_OPT_SCPS 20 //# SCPS capabilities
#define OSFP_TCP_OPT_SNACK 21 //# selective negative acks
#define OSFP_TCP_OPT_REC 22 //# record boundaries
#define OSFP_TCP_OPT_CORRUPT 23 //# corruption experienced
#define OSFP_TCP_OPT_SNAP 24 //# SNAP
#define OSFP_TCP_OPT_TCPCOMP 26 //# TCP compression filter
#define OSFP_TCP_OPT_MAX 27 //# Quick-Start Response
#define OSFP_TCP_OPT_USRTO 28 //# User Timeout Option (also, other known unauthorized use) [***][1] [RFC5482]
#define OSFP_TCP_OPT_AUTH 29 //# TCP Authentication Option (TCP-AO) [RFC5925]
#define OSFP_TCP_OPT_MULTIPATH 30 //# Multipath TCP (MPTCP)
#define OSFP_TCP_OPT_FASTOPEN 34 //# TCP Fast Open Cookie [RFC7413]
#define OSFP_TCP_OPY_ENCNEG 69 //# Encryption Negotiation (TCP-ENO) [RFC8547]
#define OSFP_TCP_OPT_EXP1 253 //# RFC3692-style Experiment 1 (also improperly used for shipping products)
#define OSFP_TCP_OPT_EXP2 254 //# RFC3692-style Experiment 2 (also improperly used for shipping products)
#define OSFP_TCP_OPT_SACKOK_LEN 2
#define OSFP_TCP_OPT_WS_LEN 3
#define OSFP_TCP_OPT_TS_LEN 10
#define OSFP_TCP_OPT_MSS_LEN 4
#define OSFP_TCP_OPT_SACK_MIN_LEN 10 /* hdr 2, 1 pair 8 = 10 */
#define OSFP_TCP_OPT_SACK_MAX_LEN 34 /* hdr 2, 4 pair 32= 34 */
#define OSFP_TCP_OPT_TFO_MIN_LEN 4 /* kind, len, 2 bytes cookie: 4 */
#define OSFP_TCP_OPT_TFO_MAX_LEN 18 /* kind, len, 18 */
static inline unsigned long long osfp_rdtsc(void)
{
union {
unsigned long long tsc_64;
struct {
unsigned int lo_32;
unsigned int hi_32;
};
} tsc;
asm volatile("rdtsc" :
"=a" (tsc.lo_32),
"=d" (tsc.hi_32));
return tsc.tsc_64;
}
#define osfp_profile_cycle(x) volatile unsigned long long x = 0
#define osfp_profile_get_cycle(x) do { \
x = osfp_rdtsc(); \
} while(0)
#define OSFP_BIT_U32(n) (1UL << (n))
enum osfp_error_code {
OSFP_NOERR,
OSFP_EINVAL,
OSFP_ENOMEM,
OSFP_ERR_SCORE_DB_READ_FILE,
OSFP_ERR_SCORE_DB_PARSE_FILE,
OSFP_ERR_SCORE_DB_UNSUPPORTED,
OSFP_ERR_FINGERPRINTING_UNSUPPORTED,
};
#define OSFP_OS_CLASS_FLAG_WINDOWS OSFP_BIT_U32(OSFP_OS_CLASS_WINDOWS)
#define OSFP_OS_CLASS_FLAG_LINUX OSFP_BIT_U32(OSFP_OS_CLASS_LINUX)
#define OSFP_OS_CLASS_FLAG_MAC_OS OSFP_BIT_U32(OSFP_OS_CLASS_MAC_OS)
#define OSFP_OS_CLASS_FLAG_IOS OSFP_BIT_U32(OSFP_OS_CLASS_IOS)
#define OSFP_OS_CLASS_FLAG_ANDROID OSFP_BIT_U32(OSFP_OS_CLASS_ANDROID)
#define OSFP_PERCENTILE 100
const char *osfp_os_class_id_to_name(enum osfp_os_class_id os_class);
enum osfp_os_class_id osfp_os_class_name_to_id(char *name);
#endif

590
src/osfp_fingerprint.c Normal file
View File

@@ -0,0 +1,590 @@
#include "osfp_common.h"
#include "osfp.h"
#include "osfp_fingerprint.h"
#include "osfp_log.h"
#define OSFP_FINGERPRINT_FIELD_NAME_IP_ID "ip_id"
#define OSFP_FINGERPRINT_FIELD_NAME_IP_TOS "ip_tos"
#define OSFP_FINGERPRINT_FIELD_NAME_IP_TOTAL_LENGHT "ip_total_length"
#define OSFP_FINGERPRINT_FIELD_NAME_IP_TTL "ip_ttl"
#define OSFP_FINGERPRINT_FIELD_NAME_TCP_OFF "tcp_off"
#define OSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP "tcp_timestamp"
#define OSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP_ECHO_REPLY "tcp_timestamp_echo_reply"
#define OSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_WCALING "tcp_window_scaling"
#define OSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_SIZE "tcp_window_size"
#define OSFP_FINGERPRINT_FIELD_NAME_TCP_FLAGS "tcp_flags"
#define OSFP_FINGERPRINT_FIELD_NAME_TCP_MSS "tcp_mss"
#define OSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS "tcp_options"
#define OSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS_ORDERED "tcp_options_ordered"
#define OSFP_FINGERPRINT_FIELD_NAME_OS "os"
#define OSFP_FINGERPRINT_DEFAULT_OS_CLASS_NAME "OSFP_UNKNOWN"
struct osfp_tcp_opt {
unsigned char type;
unsigned char len;
const unsigned char *data;
} osfp_tcp_opt;
struct osfp_fingerprint_field fp_fields[OSFP_FIELD_MAX] = {
{OSFP_FINGERPRINT_FIELD_NAME_IP_ID, 1, OSFP_FIELD_TYPE_UINT, 150, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_IP_TOS, 1, OSFP_FIELD_TYPE_UINT, 25, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_IP_TOTAL_LENGHT, 1, OSFP_FIELD_TYPE_UINT, 250, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_IP_TTL, 1, OSFP_FIELD_TYPE_UINT, 200, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_OFF, 1, OSFP_FIELD_TYPE_UINT, 250, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP, 0, OSFP_FIELD_TYPE_UINT, 0, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_TIMESTAMP_ECHO_REPLY,1, OSFP_FIELD_TYPE_UINT, 200, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_WCALING, 1, OSFP_FIELD_TYPE_UINT, 200, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_WINDOW_SIZE, 1, OSFP_FIELD_TYPE_UINT, 200, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_FLAGS, 1, OSFP_FIELD_TYPE_UINT, 25, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_MSS, 1, OSFP_FIELD_TYPE_UINT, 150, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS, 1, OSFP_FIELD_TYPE_STRING, 400, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_TCP_OPTIONS_ORDERED, 0, OSFP_FIELD_TYPE_STRING, 250, NULL, 0},
{OSFP_FINGERPRINT_FIELD_NAME_OS, 0, OSFP_FIELD_TYPE_STRING, 0, NULL, 0},
};
static char option_to_ascii(unsigned char type)
{
switch (type) {
case OSFP_TCP_OPT_EOL:
return 'E';
case OSFP_TCP_OPT_NOP:
return 'N';
case OSFP_TCP_OPT_MSS:
return 'M';
case OSFP_TCP_OPT_WSCALE:
return 'W';
case OSFP_TCP_OPT_SACKOK:
return 'S';
case OSFP_TCP_OPT_SACK:
return 'K';
case OSFP_TCP_OPT_ECHO:
return 'J';
case OSFP_TCP_OPT_ECHOREPLY:
return 'F';
case OSFP_TCP_OPT_TIMESTAMP:
return 'T';
case OSFP_TCP_OPT_POCONN:
return 'P';
case OSFP_TCP_OPT_POSVC:
return 'R';
default:
return 'U';
}
}
static unsigned int compute_ip_ttl(unsigned int ip_ttl)
{
if (ip_ttl >= 0 && ip_ttl <= 32) {
ip_ttl = 32;
} else if (ip_ttl > 32 && ip_ttl <= 64) {
ip_ttl = 64;
} else if (ip_ttl > 64 && ip_ttl <= 128) {
ip_ttl = 128;
} else {
ip_ttl = 255;
}
return ip_ttl;
}
static unsigned int decode_tcp_options(struct osfp_tcp_opt *tcp_opts, unsigned int max_opt_cnt, unsigned char *data, unsigned int len)
{
unsigned int offset = 0;
unsigned int tcp_opt_cnt = 0;
unsigned char type;
unsigned char olen;
unsigned char *odata;
while (offset < len && tcp_opt_cnt < max_opt_cnt) {
type = *(data + offset);
if (type == OSFP_TCP_OPT_EOL || type == OSFP_TCP_OPT_NOP) {
olen = 1;
} else {
olen = *(data + offset + 1);
if (olen < 2) {
break;
}
}
if (offset + olen > len) {
break;
}
odata = (olen > 2) ? (data + offset + 2) : NULL;
tcp_opts[tcp_opt_cnt].type = type;
tcp_opts[tcp_opt_cnt].len = olen;
tcp_opts[tcp_opt_cnt].data = odata;
offset += olen;
tcp_opt_cnt++;
}
return tcp_opt_cnt;
}
int osfp_fingerprint_to_json_buf(struct osfp_fingerprint *fp, char *strbuf, unsigned int buf_len, unsigned int format)
{
int rlen = 0, ret, i;
cJSON *root;
if (fp == NULL || strbuf == NULL || buf_len == 0) {
return 0;
}
strbuf[0] = 0;
root = cJSON_CreateObject();
if (root == NULL) {
return 0;
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
if (fp->fields[i].enabled) {
switch (fp_fields[i].type) {
case OSFP_FIELD_TYPE_UINT:
cJSON_AddNumberToObject(root, fp_fields[i].name, *(unsigned int *)fp->fields[i].value);
break;
case OSFP_FIELD_TYPE_STRING:
cJSON_AddStringToObject(root, fp_fields[i].name, (char *)fp->fields[i].value);
break;
default:
break;
}
} else {
cJSON_AddNullToObject(root, fp_fields[i].name);
}
}
if (!cJSON_PrintPreallocated(root, strbuf, buf_len, format)) {
return 0;
}
cJSON_Delete(root);
return strlen(strbuf) + 1;
}
int osfp_fingerprint_from_json(struct osfp_fingerprint *fp, char *json_str)
{
int ret, i;
cJSON *root;
cJSON *field;
void *value_ptr;
unsigned int value_len;
if (fp == NULL || json_str == NULL) {
goto exit;
}
memset(fp, 0, sizeof(struct osfp_fingerprint));
root = cJSON_Parse(json_str);
if (root == NULL) {
osfp_log_error("parse json: '%s'\n", json_str);
goto exit;
}
for (i = 0; i < OSFP_FIELD_OS; i++) {
if (!fp_fields[i].enabled) {
continue;
}
field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(i));
if (field == NULL) {
goto exit;
}
switch (field->type) {
case cJSON_Number:
value_ptr = (void *)&field->valueint;
value_len = sizeof(field->valueint);
osfp_fingerprint_setup_field(fp, i, value_ptr, value_len);
break;
case cJSON_String:
value_ptr = (void *)field->valuestring;
value_len = strlen(field->valuestring) + 1;
osfp_fingerprint_setup_field(fp, i, value_ptr, value_len);
break;
case cJSON_NULL:
//printf("fingerprint parse error: %s\n%s\n", field->string, cJSON_Print(root));
break;
default:
goto exit;
}
}
return 0;
exit:
return ret;
}
unsigned int osfp_fingerprint_get_field_enabled(enum osfp_field_id field_id)
{
return fp_fields[field_id].enabled;
}
unsigned int osfp_fingerprint_get_field_importance(enum osfp_field_id field_id)
{
return fp_fields[field_id].importance;
}
const char *osfp_fingerprint_get_field_name(enum osfp_field_id field_id)
{
return fp_fields[field_id].name;
}
unsigned int osfp_fingerprint_get_field_type(enum osfp_field_id field_id)
{
return fp_fields[field_id].type;
}
void osfp_fingerprint_setup_field(struct osfp_fingerprint *fp, enum osfp_field_id field_id, void *value, unsigned int len)
{
fp->fields[field_id].name = osfp_fingerprint_get_field_name(field_id);
fp->fields[field_id].enabled = 1;
if (fp->value_buffer_used + len <= sizeof(fp->value_buffer)) {
memcpy(fp->value_buffer + fp->value_buffer_used, value, len);
fp->fields[field_id].value = fp->value_buffer + fp->value_buffer_used;
fp->fields[field_id].value_len = len;
fp->value_buffer_used += len;
} else {
fp->fields[field_id].value = NULL;
fp->fields[field_id].value_len = 0;
}
}
int osfp_fingerprinting_tcp_option(unsigned char *opt_data, unsigned int opt_len, struct osfp_fingerprint *fp)
{
int ret,i;
unsigned int tcp_mss;
unsigned int tcp_ws;
unsigned int tcp_ter;
unsigned int tcp_opt_cnt;
struct osfp_tcp_opt tcp_opts[OSFP_TCP_OPTMAX];
char options[OSFP_TCP_OPTLENMAX];
char options_ordered[OSFP_TCP_OPTLENMAX];
unsigned int offset = 0;
unsigned int maxoffset = sizeof(options) - 3; //for shortest "E,"
unsigned int ordered_offset = 0;
unsigned int ordered_maxoffset = sizeof(options_ordered) - 1;
if (opt_data == NULL || opt_len == 0 || fp == NULL) {
goto exit;
}
tcp_opt_cnt = decode_tcp_options(tcp_opts, OSFP_TCP_OPTMAX, opt_data, opt_len);
for (i = 0; i < tcp_opt_cnt && offset < maxoffset && ordered_offset < ordered_maxoffset; i++) {
struct osfp_tcp_opt *opt = &tcp_opts[i];
char letter = option_to_ascii(opt->type);
options[offset++] = letter;
options_ordered[ordered_offset++] = letter;
switch (opt->type) {
case OSFP_TCP_OPT_EOL:
case OSFP_TCP_OPT_NOP:
break;
case OSFP_TCP_OPT_MSS:
if (opt->len != OSFP_TCP_OPT_MSS_LEN) {
break;
}
tcp_mss = (unsigned int)ntohs(*(unsigned short *)opt->data);
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_MSS, &tcp_mss, sizeof(tcp_mss));
ret = snprintf(options + offset, sizeof(options), "%u", tcp_mss);
if (ret < 0 || offset + ret > maxoffset) {
break;
}
offset += ret;
break;
case OSFP_TCP_OPT_WSCALE:
if (opt->len != OSFP_TCP_OPT_WS_LEN) {
break;
}
tcp_ws = (unsigned int)*(unsigned char *)opt->data;
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_WINDOW_SCALING, &tcp_ws, sizeof(tcp_ws));
ret = snprintf(options + offset, sizeof(options), "%u", tcp_ws);
if (ret < 0 || offset + ret > maxoffset) {
break;
}
offset += ret;
break;
case OSFP_TCP_OPT_TIMESTAMP:
if (opt->len != OSFP_TCP_OPT_TS_LEN) {
break;
}
tcp_ter = ntohl(*(unsigned int *)(opt->data + 4));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY, &tcp_ter, sizeof(tcp_ter));
ret = snprintf(options + offset, sizeof(options), "%u", tcp_ter);
if (ret < 0 || offset + ret > maxoffset) {
break;
}
offset += ret;
break;
case OSFP_TCP_OPT_SACKOK:
case OSFP_TCP_OPT_SACK:
case OSFP_TCP_OPT_ECHO:
case OSFP_TCP_OPT_ECHOREPLY:
case OSFP_TCP_OPT_POCONN:
case OSFP_TCP_OPT_POSVC:
break;
default:
ret = snprintf(options + offset, sizeof(options), "%u", opt->type);
if (ret < 0 || offset + ret > maxoffset) {
break;
}
offset += ret;
}
options[offset++] = ',';
options[offset] = 0;
options_ordered[ordered_offset] = 0;
}
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_OPTIONS, options, strlen(options) + 1);
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_OPTIONS_ORDERED, options_ordered, strlen(options_ordered) + 1);
return 0;
exit:
return -1;
}
int osfp_fingerprinting_tcp(struct tcphdr *tcph, unsigned int tcph_len, struct osfp_fingerprint *fp)
{
unsigned int tcp_off;
unsigned int tcp_window_size;
unsigned int tcp_flags;
if (tcph == NULL || tcph_len > OSFP_TCP_DATA_OFF_MAX || fp == NULL) {
goto exit;
}
tcp_off = tcph->doff << 2;
tcp_window_size = ntohs(tcph->window);
tcp_flags = *((unsigned char *)&tcph->window - 1);
if (tcp_off != tcph_len) {
goto exit;
}
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_OFF, &tcp_off, sizeof(tcp_off));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_WINDOW_SIZE, &tcp_window_size, sizeof(tcp_window_size));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_TCP_FLAGS, &tcp_flags, sizeof(tcp_flags));
// tcp options
if (tcp_off > OSFP_TCP_HEADER_LEN) {
osfp_fingerprinting_tcp_option((unsigned char *)tcph + OSFP_TCP_HEADER_LEN, tcp_off - OSFP_TCP_HEADER_LEN, fp);
}
return 0;
exit:
return -1;
}
int osfp_fingerprinting_ipv4(struct iphdr *iph, struct osfp_fingerprint *fp)
{
if (iph == NULL || fp == NULL) {
goto exit;
}
unsigned int ip_id = !!iph->id;
unsigned int ip_tos = iph->tos;
unsigned int ip_total_length = ntohs(iph->tot_len);
unsigned int ip_ttl = compute_ip_ttl(iph->ttl);
osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_ID, &ip_id, sizeof(ip_id));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_TOS, &ip_tos, sizeof(ip_tos));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl));
return 0;
exit:
return -1;
}
int osfp_fingerprinting_ipv6(struct ipv6hdr *iph, struct osfp_fingerprint *fp)
{
if (iph == NULL || fp == NULL) {
goto exit;
}
//unsigned int ip_id = 0;
//unsigned int ip_tos = 0;
unsigned int ip_total_length = OSFP_IPV6_HEADER_LEN + ntohs(iph->payload_len);
unsigned int ip_ttl = compute_ip_ttl(iph->hop_limit);
//osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_ID, &ip_id, sizeof(ip_id));
//osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_TOS, &ip_tos, sizeof(ip_tos));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length));
osfp_fingerprint_setup_field(fp, OSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl));
return 0;
exit:
return -1;
}
int osfp_fingerprinting(unsigned char *iph, unsigned char *tcph, unsigned int tcph_len, struct osfp_fingerprint *fp, unsigned int ip_version)
{
int ret = OSFP_EINVAL;
if (iph == NULL || tcph == NULL || fp == NULL) {
goto exit;
}
memset(fp, 0, sizeof(struct osfp_fingerprint));
switch (ip_version) {
case 4:
ret = osfp_fingerprinting_ipv4((struct iphdr *)iph, fp);
break;
case 6:
ret = osfp_fingerprinting_ipv6((struct ipv6hdr *)iph, fp);
break;
default:
ret = -1;
goto exit;
}
if (ret != 0) {
goto exit;
}
ret = osfp_fingerprinting_tcp((struct tcphdr *)tcph, tcph_len, fp);
if (ret != 0) {
goto exit;
}
osfp_fingerprint_setup_field(fp, OSFP_FIELD_OS, OSFP_FINGERPRINT_DEFAULT_OS_CLASS_NAME, strlen(OSFP_FINGERPRINT_DEFAULT_OS_CLASS_NAME) + 1);
return 0;
exit:
return -1;
}
#ifdef UNITTEST
int test_osfp_fingerprinting_ipv4(void)
{
int ret;
char iph[] = {
0x45, 0x00, 0x00, 0x34, 0x51, 0xc4, 0x40, 0x00,
0x80, 0x06, 0xe7, 0x27, 0xc0, 0xa8, 0x73, 0x08,
0x6a, 0xb9, 0x23, 0x6e
};
char tcph[] = {
0xc1, 0xbd, 0x00, 0x50, 0x3d, 0x58, 0x51, 0x60,
0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20, 0x00,
0x3d, 0x3a, 0x00, 0x00, 0x02, 0x04, 0x04, 0xec,
0x01, 0x03, 0x03, 0x08, 0x01, 0x01, 0x04, 0x02
};
char str_buf[2048] = "";
const char *target = "{\"ip_id\":1,\"ip_tos\":0,\"ip_total_length\":52,\"ip_ttl\":128,\"tcp_off\":32,\"tcp_timestamp\":null,\"tcp_timestamp_echo_reply\":null,\"tcp_window_scaling\":8,\"tcp_window_size\":8192,\"tcp_flags\":2,\"tcp_mss\":1260,\"tcp_options\":\"M1260,N,W8,N,N,S,\",\"tcp_options_ordered\":\"MNWNNS\",\"os\":\"OSFP_UNKNOWN\"}";
struct osfp_fingerprint fp = {0};
ret = osfp_fingerprinting(iph, tcph, 32, &fp, 4);
if (ret != 0) {
goto exit;
}
ret = osfp_fingerprint_to_json_buf(&fp, str_buf, 2048, 0);
if (ret <= 0) {
goto exit;
}
if (0 != memcmp(str_buf, target, strlen(target))) {
goto exit;
}
return 0;
exit:
return ret;
}
int test_osfp_fingerprinting_ipv6(void)
{
int ret;
char iph[] = {
0x45, 0x00, 0x00, 0x34, 0x51, 0xc4, 0x40, 0x00,
0x80, 0x06, 0xe7, 0x27, 0xc0, 0xa8, 0x73, 0x08,
0x6a, 0xb9, 0x23, 0x6e
};
char tcph[] = {
0xc1, 0xbd, 0x00, 0x50, 0x3d, 0x58, 0x51, 0x60,
0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20, 0x00,
0x3d, 0x3a, 0x00, 0x00, 0x02, 0x04, 0x04, 0xec,
0x01, 0x03, 0x03, 0x08, 0x01, 0x01, 0x04, 0x02
};
char str_buf[2048] = "";
const char *target = "{\"ip_id\":1,\"ip_tos\":0,\"ip_total_length\":52,\"ip_ttl\":128,\"tcp_off\":32,\"tcp_timestamp\":null,\"tcp_timestamp_echo_reply\":null,\"tcp_window_scaling\":8,\"tcp_window_size\":8192,\"tcp_flags\":2,\"tcp_mss\":1260,\"tcp_options\":\"M1260,N,W8,N,N,S,\",\"tcp_options_ordered\":\"MNWNNS\",\"os\":\"OSFP_UNKNOWN\"}";
struct osfp_fingerprint fp = {0};
ret = osfp_fingerprinting(iph, tcph, 32, &fp, 4);
if (ret != 0) {
goto exit;
}
ret = osfp_fingerprint_to_json_buf(&fp, str_buf, 2048, 0);
if (ret <= 0) {
goto exit;
}
if (0 != memcmp(str_buf, target, strlen(target))) {
goto exit;
}
return 0;
exit:
return ret;
}
int test_osfp_fingerprinting_tcp_option(void)
{
int ret;
char tcp_opt[] = {
0x02, 0x04, 0x04, 0xec,0x01, 0x03, 0x03, 0x08,
0x01, 0x01, 0x04, 0x02
};
char str_buf[2048] = "";
const char *target_options = "M1260,N,W8,N,N,S,";
const char *target_options_ordered = "MNWNNS";
struct osfp_fingerprint fp = {0};
ret = osfp_fingerprinting_tcp_option((unsigned char *)tcp_opt, 12, &fp);
if (ret != 0) {
goto exit;
}
if (fp.fields[OSFP_FIELD_TCP_OPTIONS].value_len != strlen(target_options) + 1)
{
goto exit;
}
if (0 != memcmp(fp.fields[OSFP_FIELD_TCP_OPTIONS].value, target_options, strlen(target_options) + 1)) {
goto exit;
}
if (fp.fields[OSFP_FIELD_TCP_OPTIONS_ORDERED].value_len != strlen(target_options_ordered) + 1) {
goto exit;
}
if (0 != memcmp(fp.fields[OSFP_FIELD_TCP_OPTIONS_ORDERED].value, target_options_ordered, strlen(target_options_ordered) + 1)) {
goto exit;
}
return 0;
exit:
return ret;
}
#endif

66
src/osfp_fingerprint.h Normal file
View File

@@ -0,0 +1,66 @@
#ifndef __OSFP_FINGERPRINT_H__
#define __OSFP_FINGERPRINT_H__
#define OSFP_FINGERPRINT_VALUE_BUFFER_MAX 128
enum osfp_field_id {
OSFP_FIELD_IP_ID,
OSFP_FIELD_IP_TOS,
OSFP_FIELD_IP_TOTAL_LENGTH,
OSFP_FIELD_IP_TTL,
OSFP_FIELD_TCP_OFF,
OSFP_FIELD_TCP_TIMESTAMP,
OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY,
OSFP_FIELD_TCP_WINDOW_SCALING,
OSFP_FIELD_TCP_WINDOW_SIZE,
OSFP_FIELD_TCP_FLAGS,
OSFP_FIELD_TCP_MSS,
OSFP_FIELD_TCP_OPTIONS,
OSFP_FIELD_TCP_OPTIONS_ORDERED,
OSFP_FIELD_OS,
OSFP_FIELD_MAX,
};
enum osfp_field_type {
OSFP_FIELD_TYPE_UNKNOWN,
OSFP_FIELD_TYPE_UINT,
OSFP_FIELD_TYPE_STRING,
OSFP_FIELD_TYPE_MAX
};
struct osfp_fingerprint_field {
const char *name;
unsigned int enabled;
unsigned int type;
unsigned int importance;
void *value;
unsigned int value_len;
};
struct osfp_fingerprint {
struct osfp_fingerprint_field fields[OSFP_FIELD_MAX];
char value_buffer[OSFP_FINGERPRINT_VALUE_BUFFER_MAX];
unsigned int value_buffer_used;
};
int osfp_fingerprint_to_json_buf(struct osfp_fingerprint *fp, char *strbuf, unsigned int buf_len, unsigned int format);
void osfp_fingerprint_setup_field(struct osfp_fingerprint *fp, enum osfp_field_id field_id, void *value, unsigned int len);
const char *osfp_fingerprint_get_field_name(enum osfp_field_id field_id);
unsigned int osfp_fingerprint_get_field_enabled(enum osfp_field_id field_id);
unsigned int osfp_fingerprint_get_field_importance(enum osfp_field_id field_id);
unsigned int osfp_fingerprint_get_field_type(enum osfp_field_id field_id);
int osfp_fingerprinting_tcp_option(unsigned char *pkt, unsigned int pktlen, struct osfp_fingerprint *fp);
int osfp_fingerprinting_tcp(struct tcphdr *tcph, unsigned int tcph_len, struct osfp_fingerprint *fp);
int osfp_fingerprinting_ipv4(struct iphdr *iph, struct osfp_fingerprint *fp);
int osfp_fingerprinting_ipv6(struct ipv6hdr *iph, struct osfp_fingerprint *fp);
int osfp_fingerprinting(unsigned char *iph, unsigned char *tcph, unsigned int tcph_len, struct osfp_fingerprint *fp, unsigned int ip_version);
#ifdef UNITTEST
int test_osfp_fingerprinting_ipv4(void);
int test_osfp_fingerprinting_ipv6(void);
int test_osfp_fingerprinting_tcp_option(void);
#endif
#endif

57
src/osfp_log.c Normal file
View File

@@ -0,0 +1,57 @@
#include "osfp_common.h"
#include "osfp_log.h"
/* The maximum length of the log message */
#define OSFP_MAX_LOG_MSG_LEN 2048
unsigned int g_osfp_log_level = OSFP_LOG_LEVEL_WARNING;
void osfp_log_message(unsigned int x, const char *file, const int line, const char *func, const char *msg)
{
char buffer[OSFP_MAX_LOG_MSG_LEN] = "";
char log_time_buf[128];
time_t now;
struct tm tm_now;
time(&now);
localtime_r(&now, &tm_now);
strftime(log_time_buf, sizeof(log_time_buf), "%Y-%m-%d %T", &tm_now);
switch (x) {
case OSFP_LOG_LEVEL_DEBUG:
snprintf(buffer, sizeof(buffer), "[%s][DEBUG][%s:%d %s] %s\n", log_time_buf, file, line, func, msg);
break;
case OSFP_LOG_LEVEL_INFO:
snprintf(buffer, sizeof(buffer), "[%s][INFO][%s:%d %s] %s\n", log_time_buf, file, line, func, msg);
break;
case OSFP_LOG_LEVEL_WARNING:
snprintf(buffer, sizeof(buffer), "[%s][WARN][%s:%d %s] %s\n", log_time_buf, file, line, func, msg);
break;
case OSFP_LOG_LEVEL_ERROR:
snprintf(buffer, sizeof(buffer), "[%s][ERROR][%s:%d %s] %s\n", log_time_buf, file, line, func, msg);
break;
}
if (fprintf(stdout, "%s\n", buffer) < 0) {
printf("Error writing to stream using fprintf\n");
}
fflush(stdout);
}
void osfp_log(unsigned int x, const char *file, const int line, const char *func, const char *fmt, ...)
{
if (g_osfp_log_level <= x ) {
char msg[OSFP_MAX_LOG_MSG_LEN];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
osfp_log_message(x, file, line, func, msg);
}
}
void osfp_log_level_set(enum osfp_log_level level)
{
g_osfp_log_level = level;
}

24
src/osfp_log.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef __OSFP_LOG_H__
#define __OSFP_LOG_H__
enum osfp_log_level {
OSFP_LOG_LEVEL_DEBUG,
OSFP_LOG_LEVEL_INFO,
OSFP_LOG_LEVEL_WARNING,
OSFP_LOG_LEVEL_ERROR
};
#ifndef DEBUGLOG
#define osfp_log_debug(...) do { } while (0)
#else
#define osfp_log_debug(...) osfp_log(OSFP_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#endif
#define osfp_log_info(...) osfp_log(OSFP_LOG_LEVEL_INFO, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define osfp_log_warning(...) osfp_log(OSFP_LOG_LEVEL_WARNING, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
#define osfp_log_error(...) osfp_log(OSFP_LOG_LEVEL_ERROR, __FILE__, __LINE__, __FUNCTION__,__VA_ARGS__)
void osfp_log_level_set(enum osfp_log_level level);
void osfp_log(unsigned int x, const char *file, const int line, const char *func, const char *fmt, ...);
#endif

622
src/osfp_score_db.c Normal file
View File

@@ -0,0 +1,622 @@
#include "osfp_common.h"
#include "osfp.h"
#include "osfp_fingerprint.h"
#include "osfp_score_db.h"
#include "osfp_log.h"
#define PERFECT_SCORE_EXPECTED_RATE 0.5f
#define FIELD_VALUE_DUP_RATE_MAX 0.5f
#define OSFP_SCORE_DB_FIELD_UINT_VALUE_MAX 65536
struct osfp_score_db_array_data {
struct osfp_os_class_score *array_head;
unsigned int array_len;
};
struct osfp_score_db_hash_element {
char *key;
unsigned int keylen;
struct osfp_os_class_score *score;
UT_hash_handle hh;
};
struct osfp_score_db_hash_data {
struct osfp_score_db_hash_element *hash_head;
};
int osfp_score_db_array_add(void *data, struct osfp_os_class_score *os_class_score, void *value, unsigned int len)
{
int ret = -1, i;
unsigned int index;
struct osfp_score_db_array_data *array_data = (struct osfp_score_db_array_data *)data;
if (array_data == NULL || os_class_score == NULL || value == NULL || len != sizeof(unsigned int)) {
goto exit;
}
if (array_data->array_head == NULL || array_data->array_len == 0) {
goto exit;
}
index = *(unsigned int *)value;
if (index >= array_data->array_len) {
goto exit;
}
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
array_data->array_head[index].scores[i] += os_class_score->scores[i];
}
return 0;
exit:
return ret;
}
struct osfp_os_class_score *osfp_score_db_array_match(void *data, void *value, unsigned int len)
{
unsigned int index;
struct osfp_score_db_array_data *array_data = (struct osfp_score_db_array_data *)data;
if (array_data == NULL || value == NULL || len != sizeof(unsigned int)) {
return NULL;
}
if (array_data->array_head == NULL || array_data->array_len == 0) {
return NULL;
}
index = *(unsigned int *)value;
if (index >= array_data->array_len) {
return NULL;
}
return &((array_data->array_head)[index]);
}
void *osfp_score_db_array_create(void)
{
struct osfp_score_db_array_data *array_data = calloc(1, sizeof(struct osfp_score_db_array_data));
if (array_data == NULL) {
return NULL;
}
array_data->array_head = calloc(OSFP_SCORE_DB_FIELD_UINT_VALUE_MAX, sizeof(struct osfp_os_class_score));
if (array_data->array_head == NULL) {
free(array_data);
return NULL;
}
array_data->array_len = OSFP_SCORE_DB_FIELD_UINT_VALUE_MAX;
return (void *)array_data;
}
void osfp_score_db_array_destroy(void *data) {
struct osfp_score_db_array_data *array_data = (struct osfp_score_db_array_data *)data;
if (array_data) {
if (array_data->array_head) {
free(array_data->array_head);
}
free(array_data);
}
}
int osfp_score_db_hash_add(void *data, struct osfp_os_class_score *os_class_score, void *value, unsigned int len)
{
int ret = -1, i;
struct osfp_score_db_hash_data *hash_data = (struct osfp_score_db_hash_data *)data;
struct osfp_score_db_hash_element *element = NULL;
if (hash_data == NULL || os_class_score == NULL || value == NULL || len == 0) {
goto exit;
}
HASH_FIND(hh, hash_data->hash_head, value, len, element);
if (element == NULL) {
element = (struct osfp_score_db_hash_element *)calloc(1, sizeof(struct osfp_score_db_hash_element));
if (element == NULL) {
goto exit;
}
element->key = strdup(value);
element->keylen = len;
element->score = (struct osfp_os_class_score *)calloc(1, sizeof(struct osfp_os_class_score));
if (element->score == NULL) {
free(element);
element = NULL;
goto exit;
}
HASH_ADD_KEYPTR(hh, hash_data->hash_head, element->key, element->keylen, element);
}
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
element->score->scores[i] += os_class_score->scores[i];
}
return 0;
exit:
return ret;
}
struct osfp_os_class_score *osfp_score_db_hash_match(void *data, void *value, unsigned int len)
{
struct osfp_score_db_hash_data *hash_data = (struct osfp_score_db_hash_data *)data;
struct osfp_score_db_hash_element *element = NULL;
if (data == NULL || value == NULL || len == 0) {
return NULL;
}
if (hash_data->hash_head == NULL) {
return NULL;
}
HASH_FIND(hh, hash_data->hash_head, value, len, element);
if (element == NULL) {
return NULL;
}
return element->score;
}
void *osfp_score_db_hash_create(void)
{
return (void*)calloc(1, sizeof(struct osfp_score_db_hash_data));
}
void osfp_score_db_hash_destroy(void *data) {
struct osfp_score_db_hash_data *hash_data = (struct osfp_score_db_hash_data *)data;
struct osfp_score_db_hash_element *element = NULL;
struct osfp_score_db_hash_element *tmp = NULL;
if (hash_data) {
if (hash_data->hash_head) {
HASH_ITER(hh, hash_data->hash_head, element, tmp) {
HASH_DELETE(hh, hash_data->hash_head, element);
if (element) {
if (element->key) {
free(element->key);
}
if (element->score) {
free(element->score);
}
free(element);
}
}
}
free(hash_data);
}
}
char *osfp_score_db_read_file(char *fp_file)
{
int ret = -1;
char *file_buffer = NULL;
unsigned int file_len = 0;
FILE *fp = NULL;
struct stat st;
if (0 > stat(fp_file, &st)) {
osfp_log_error("stat() failed on '%s'.\n", fp_file);
goto exit;
}
if (st.st_size == 0) {
osfp_log_error("Empty file: '%s'.\n", fp_file);
goto exit;
}
file_len = (unsigned int)st.st_size;
file_buffer = malloc(file_len + 1);
if (file_buffer == NULL) {
osfp_log_error("Not enough memory for file buffer. file name: '%s'\n",fp_file);
goto exit;
}
fp = fopen(fp_file, "r");
if (fp == NULL) {
osfp_log_error("Cannot open '%s' for reading.\n", fp_file);
goto exit;
}
ret = fread(file_buffer, 1, file_len, fp);
if (ret < 0) {
osfp_log_error("fread() failed on '%s'.\n", fp_file);
free(file_buffer);
fclose(fp);
goto exit;
}
fclose(fp);
file_buffer[file_len] = 0;
return file_buffer;
exit:
return NULL;
}
int osfp_score_db_load_field(struct osfp_field_score_db *db, cJSON *field, enum osfp_os_class_id os_class)
{
int ret = -1;
struct osfp_os_class_score os_class_score = {0};
void *value_ptr;
unsigned int value_len;
switch (field->type) {
case cJSON_Number:
value_ptr = (void *)&field->valueint;
value_len = sizeof(field->valueint);
break;
case cJSON_String:
value_ptr = (void *)field->valuestring;
value_len = strlen(field->valuestring) + 1;
break;
case cJSON_NULL:
ret = 0;
goto exit;
default:
goto exit;
}
os_class_score.scores[os_class] = 1;
ret = db->add(db->data, &os_class_score, value_ptr, value_len);
if (ret != 0) {
goto exit;
}
db->entry_count++;
return 0;
exit:
return ret;
}
int osfp_score_db_load_entry(struct osfp_score_db *score_db, cJSON *entry)
{
int ret = -1, i;
cJSON *field = NULL;
struct osfp_field_score_db *db;
enum osfp_os_class_id os_class;
if (score_db == NULL || entry == NULL) {
goto exit;
}
field = cJSON_GetObjectItem(entry, osfp_fingerprint_get_field_name(OSFP_FIELD_OS));
if (field == NULL || field->valuestring == NULL) {
goto exit;
}
os_class = osfp_os_class_name_to_id(field->valuestring);
if (os_class >= OSFP_OS_CLASS_MAX) {
goto exit;
}
for (i = 0; i < OSFP_FIELD_OS; i++) {
db = &score_db->field_score_dbs[i];
if (db == NULL) {
goto exit;
}
if (!db->enabled) {
continue;
}
field = cJSON_GetObjectItem(entry, osfp_fingerprint_get_field_name(i));
if (field == NULL) {
osfp_log_info("json entry missing field: %s\n%s\n",
osfp_fingerprint_get_field_name(i), cJSON_Print(entry));
continue;
}
ret = osfp_score_db_load_field(db, field, os_class);
if (ret != 0) {
osfp_log_info("json entry field load failed. field: %s\n%s\n",
osfp_fingerprint_get_field_name(i), cJSON_Print(entry));
continue;
}
}
score_db->entry_count++;
score_db->os_class_entry_count[os_class]++;
return 0;
exit:
return ret;
}
int osfp_score_db_load(struct osfp_score_db *score_db, char *fp_file)
{
int ret = OSFP_EINVAL, i, count;
char *file_buffer;
cJSON *root = NULL;
cJSON *entry;
struct osfp_field_score_db *field_score_db;
if (score_db == NULL) {
goto exit;
}
file_buffer = osfp_score_db_read_file(fp_file);
if (file_buffer == NULL) {
osfp_log_error("read file: '%s'\n", fp_file);
ret = OSFP_ERR_SCORE_DB_READ_FILE;
goto exit;
}
root = cJSON_Parse(file_buffer);
if (root == NULL) {
osfp_log_error("parse json: '%s'\n", fp_file);
ret = OSFP_ERR_SCORE_DB_PARSE_FILE;
goto exit;
}
count = cJSON_GetArraySize(root);
for (i = 0; i < count; i++) {
entry = cJSON_GetArrayItem(root, i);
if (entry) {
ret = osfp_score_db_load_entry(score_db, entry);
if (ret != 0) {
osfp_log_debug("json entry load failed.\n%s\n", cJSON_Print(entry));
continue;
}
}
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
field_score_db = &score_db->field_score_dbs[i];
if (field_score_db->enabled && i != OSFP_FIELD_TCP_OPTIONS) {
score_db->perfect_score += osfp_fingerprint_get_field_importance(i);
}
}
score_db->perfect_score = score_db->perfect_score * PERFECT_SCORE_EXPECTED_RATE;
ret = OSFP_NOERR;
exit:
if (root) {
cJSON_Delete(root);
}
if (file_buffer) {
free((char*)file_buffer);
}
return ret;
}
int osfp_score_db_score(struct osfp_score_db *score_db, unsigned int flags, struct osfp_fingerprint *fp, struct osfp_os_class_score *result_score)
{
int ret = OSFP_EINVAL, i, j;
unsigned int tmp_score;
unsigned int perfect_score;
unsigned int entry_count;
unsigned int importance;
struct osfp_os_class_score *os_class_score_matched;
enum osfp_os_class_id os_class_id;
struct osfp_fingerprint_field *field;
struct osfp_field_score_db *field_score_db;
if (score_db == NULL || fp == NULL || result_score == NULL) {
goto exit;
}
memset(result_score, 0, sizeof(struct osfp_os_class_score));
perfect_score = score_db->perfect_score;
if (perfect_score == 0) {
goto exit;
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
field_score_db = &score_db->field_score_dbs[i];
if (!field_score_db->enabled) {
continue;
}
field = &fp->fields[i];
if (!field->enabled) {
continue;
}
os_class_score_matched = field_score_db->match(field_score_db->data, field->value, field->value_len);
if (os_class_score_matched == NULL) {
continue;
}
importance = osfp_fingerprint_get_field_importance(i);
for (j = 0; j < OSFP_OS_CLASS_MAX; j++) {
entry_count = score_db->os_class_entry_count[j];
tmp_score = os_class_score_matched->scores[j];
tmp_score = tmp_score < (entry_count * FIELD_VALUE_DUP_RATE_MAX) ? tmp_score : (entry_count * FIELD_VALUE_DUP_RATE_MAX);
if (entry_count == 0 || tmp_score == 0) {
continue;
}
if (0 == flags || flags & OSFP_BIT_U32(j)) {
osfp_log_debug("%s %s: ((%d * %u / %u) * %u ) / %u\n", osfp_fingerprint_get_field_name(i), osfp_os_class_id_to_name(j), OSFP_PERCENTILE, importance, perfect_score, os_class_score_matched->scores[j], entry_count);
result_score->scores[j] += ((OSFP_PERCENTILE * importance / perfect_score) * tmp_score) / entry_count;
}
}
}
return OSFP_NOERR;
exit:
return ret;
}
void osfp_score_db_debug_print(struct osfp_score_db *score_db)
{
int i;
printf("score_db:\n");
printf("entry_count: %u\n", score_db->entry_count);
printf("perfect_score: %u\n", score_db->perfect_score);
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
const char *name = osfp_os_class_id_to_name(i);
printf("os class %s ", name);
printf("entry_count: %u\n", score_db->os_class_entry_count[i]);
printf("os class %s entry_count: %u\n", osfp_os_class_id_to_name(i), score_db->os_class_entry_count[i]);
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
printf("field %s enabled: %u\n", osfp_fingerprint_get_field_name(i), score_db->field_score_dbs[i].enabled);
printf("field %s type: %u\n", osfp_fingerprint_get_field_name(i), score_db->field_score_dbs[i].type);
printf("field %s entry_count: %u\n", osfp_fingerprint_get_field_name(i), score_db->field_score_dbs[i].entry_count);
}
}
struct osfp_score_db *osfp_score_db_create(void)
{
int i;
struct osfp_score_db *score_db;
struct osfp_field_score_db *db;
score_db = calloc(1, sizeof(struct osfp_score_db));
if (score_db == NULL) {
goto exit;
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
db = &score_db->field_score_dbs[i];
db->enabled = osfp_fingerprint_get_field_enabled(i);
if (!db->enabled) {
osfp_log_info("fingerprint field disabled: %s", osfp_fingerprint_get_field_name(i));
continue;
}
db->type = osfp_fingerprint_get_field_type(i);
switch (db->type) {
case OSFP_FIELD_TYPE_UINT:
db->create = osfp_score_db_array_create;
db->destroy = osfp_score_db_array_destroy;
db->add = osfp_score_db_array_add;
db->match = osfp_score_db_array_match;
break;
case OSFP_FIELD_TYPE_STRING:
db->create = osfp_score_db_hash_create;
db->destroy = osfp_score_db_hash_destroy;
db->add = osfp_score_db_hash_add;
db->match = osfp_score_db_hash_match;
break;
default:
osfp_log_debug("fingerprint field unsupported type: %u", db->type);
goto exit;
}
db->data = db->create();
if (db->data == NULL) {
osfp_log_debug("field db create failed. field: %s", osfp_fingerprint_get_field_name(i));
goto exit;
}
}
return score_db;
exit:
if (score_db) {
osfp_score_db_destroy(score_db);
}
return NULL;
}
void osfp_score_db_destroy(struct osfp_score_db *score_db)
{
int i;
struct osfp_field_score_db *db;
if (score_db) {
for (i = 0; i < OSFP_FIELD_MAX; i++) {
db = &score_db->field_score_dbs[i];
if (db->destroy && db->data) {
db->destroy(db->data);
db->data = NULL;
}
}
free(score_db);
}
}
#ifdef UNITTEST
int test_osfp_score_db(void)
{
int ret, i;
struct osfp_score_db *db;
const char *test_file_path = "./.fp.json";
const char *fingerprint_file_content = "["
" {"
" \"tcp_options\": \"M1432,S,T,N,W9,\","
" \"tcp_options_ordered\": \"MSTNW\","
" \"ip_total_length\": 60,"
" \"tcp_off\": 10,"
" \"tcp_window_scaling\": 9,"
" \"tcp_window_size\": 65535,"
" \"ip_ttl\": 64,"
" \"ip_id\": 1,"
" \"tcp_timestamp\": 1,"
" \"tcp_timestamp_echo_reply\": 0,"
" \"tcp_mss\": 1432,"
" \"tcp_flags\": 2,"
" \"ip_tos\": 0,"
" \"os\": \"Android\""
" }"
"]";
db = (void *)osfp_score_db_create();
if (db == NULL) {
goto exit;
}
FILE *fingerprint_file_ptr = fopen(test_file_path, "w");
if (fingerprint_file_ptr == NULL) {
goto exit;
}
fprintf(fingerprint_file_ptr, "%s", fingerprint_file_content);
fflush(fingerprint_file_ptr);
fclose(fingerprint_file_ptr);
ret = osfp_score_db_load(db, (char *)test_file_path);
remove(test_file_path);
if (ret != 0) {
goto exit;
}
if (db->entry_count != 1) {
goto exit;
}
if (db->os_class_entry_count[OSFP_OS_CLASS_ANDROID] != 1) {
goto exit;
}
for (i = 0; i < OSFP_FIELD_MAX; i++) {
if (db->field_score_dbs[i].enabled != osfp_fingerprint_get_field_enabled(i)) {
goto exit;
}
if (db->field_score_dbs[i].enabled && db->field_score_dbs[i].type != osfp_fingerprint_get_field_type(i)) {
goto exit;
}
if (db->field_score_dbs[i].enabled && db->field_score_dbs[i].entry_count != 1) {
goto exit;
}
}
osfp_score_db_destroy(db);
return 0;
exit:
return -1;
}
#endif

44
src/osfp_score_db.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef __OSFP_SCORE_DB_H__
#define __OSFP_SCORE_DB_H__
#include "osfp.h"
#include "osfp_fingerprint.h"
struct osfp_os_class_score {
unsigned int scores[OSFP_OS_CLASS_MAX];
};
struct osfp_field_score_db {
unsigned int enabled;
unsigned int type;
unsigned int entry_count;
void *data;
void *(*create)(void);
void (*destroy)(void *);
int (*add)(void *data, struct osfp_os_class_score *, void *, unsigned int);
struct osfp_os_class_score *(*match)(void *, void *, unsigned int);
};
struct osfp_score_db {
unsigned int entry_count;
unsigned int perfect_score;
unsigned int os_class_entry_count[OSFP_OS_CLASS_MAX];
struct osfp_field_score_db field_score_dbs[OSFP_FIELD_MAX];
};
char *osfp_score_db_read_file(char *fp_file);
void osfp_score_db_debug_print(struct osfp_score_db *score_db);
int osfp_score_db_load(struct osfp_score_db *score_db, char *fp_file);
int osfp_score_db_score(struct osfp_score_db *score_db, unsigned int flags, struct osfp_fingerprint *fp, struct osfp_os_class_score *result_score);
struct osfp_score_db *osfp_score_db_create(void);
void osfp_score_db_destroy(struct osfp_score_db *score_db);
#ifdef UNITTEST
int test_osfp_score_db(void);
#endif
#endif

11
test/Makefile.am Normal file
View File

@@ -0,0 +1,11 @@
bin_PROGRAMS = osfp_test
osfp_test_SOURCES = \
test.c
osfp_test_LDADD = \
../src/.libs/libosfp.la
osfp_test_CFLAGS = \
-I../src

11
test/README.md Normal file
View File

@@ -0,0 +1,11 @@
# test
```
./.libs/osfp_test -f ../fp.json -t ../data.json
```
# output
```
total 12505, failed 0, pass 9582, wrong 2923, other 107, unknown 37
miss rate: 24%
details in: ./osfp_test.log
```

228
test/osfp_test Executable file
View File

@@ -0,0 +1,228 @@
#! /bin/sh
# osfp_test - temporary wrapper script for .libs/osfp_test
# Generated by libtool (GNU libtool) 2.4.2
#
# The osfp_test program cannot be directly executed until all the libtool
# libraries that it depends on are installed.
#
# This wrapper script should never be moved out of the build directory.
# If it is, it will not operate correctly.
# Sed substitution that helps us do robust quoting. It backslashifies
# metacharacters that are still active within double-quoted strings.
sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
# Be Bourne compatible
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
emulate sh
NULLCMD=:
# Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
else
case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
fi
BIN_SH=xpg4; export BIN_SH # for Tru64
DUALCASE=1; export DUALCASE # for MKS sh
# The HP-UX ksh and POSIX shell print the target directory to stdout
# if CDPATH is set.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
relink_command="(cd /root/geedge/libosfp/test; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; { test -z \"\${LD_LIBRARY_PATH+set}\" || unset LD_LIBRARY_PATH || { LD_LIBRARY_PATH=; export LD_LIBRARY_PATH; }; }; PATH=/root/.cargo/bin:/usr/lib64/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin; export PATH; gcc -I../src -g -O2 -DUNITTEST -o \$progdir/\$file osfp_test-test.o ../src/.libs/libosfp.so -Wl,-rpath -Wl,/root/geedge/libosfp/src/.libs -Wl,-rpath -Wl,/root/geedge/libosfp/target/lib)"
# This environment variable determines our operation mode.
if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then
# install mode needs the following variables:
generated_by_libtool_version='2.4.2'
notinst_deplibs=' ../src/.libs/libosfp.la'
else
# When we are sourced in execute mode, $file and $ECHO are already set.
if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
file="$0"
# A function that is used when there is no print builtin or printf.
func_fallback_echo ()
{
eval 'cat <<_LTECHO_EOF
$1
_LTECHO_EOF'
}
ECHO="printf %s\\n"
fi
# Very basic option parsing. These options are (a) specific to
# the libtool wrapper, (b) are identical between the wrapper
# /script/ and the wrapper /executable/ which is used only on
# windows platforms, and (c) all begin with the string --lt-
# (application programs are unlikely to have options which match
# this pattern).
#
# There are only two supported options: --lt-debug and
# --lt-dump-script. There is, deliberately, no --lt-help.
#
# The first argument to this parsing function should be the
# script's ../libtool value, followed by no.
lt_option_debug=
func_parse_lt_options ()
{
lt_script_arg0=$0
shift
for lt_opt
do
case "$lt_opt" in
--lt-debug) lt_option_debug=1 ;;
--lt-dump-script)
lt_dump_D=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%/[^/]*$%%'`
test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=.
lt_dump_F=`$ECHO "X$lt_script_arg0" | /usr/bin/sed -e 's/^X//' -e 's%^.*/%%'`
cat "$lt_dump_D/$lt_dump_F"
exit 0
;;
--lt-*)
$ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2
exit 1
;;
esac
done
# Print the debug banner immediately:
if test -n "$lt_option_debug"; then
echo "osfp_test:osfp_test:${LINENO}: libtool wrapper (GNU libtool) 2.4.2" 1>&2
fi
}
# Used when --lt-debug. Prints its arguments to stdout
# (redirection is the responsibility of the caller)
func_lt_dump_args ()
{
lt_dump_args_N=1;
for lt_arg
do
$ECHO "osfp_test:osfp_test:${LINENO}: newargv[$lt_dump_args_N]: $lt_arg"
lt_dump_args_N=`expr $lt_dump_args_N + 1`
done
}
# Core function for launching the target application
func_exec_program_core ()
{
if test -n "$lt_option_debug"; then
$ECHO "osfp_test:osfp_test:${LINENO}: newargv[0]: $progdir/$program" 1>&2
func_lt_dump_args ${1+"$@"} 1>&2
fi
exec "$progdir/$program" ${1+"$@"}
$ECHO "$0: cannot exec $program $*" 1>&2
exit 1
}
# A function to encapsulate launching the target application
# Strips options in the --lt-* namespace from $@ and
# launches target application with the remaining arguments.
func_exec_program ()
{
case " $* " in
*\ --lt-*)
for lt_wr_arg
do
case $lt_wr_arg in
--lt-*) ;;
*) set x "$@" "$lt_wr_arg"; shift;;
esac
shift
done ;;
esac
func_exec_program_core ${1+"$@"}
}
# Parse options
func_parse_lt_options "$0" ${1+"$@"}
# Find the directory that this script lives in.
thisdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
test "x$thisdir" = "x$file" && thisdir=.
# Follow symbolic links until we get to the real thisdir.
file=`ls -ld "$file" | /usr/bin/sed -n 's/.*-> //p'`
while test -n "$file"; do
destdir=`$ECHO "$file" | /usr/bin/sed 's%/[^/]*$%%'`
# If there was a directory component, then change thisdir.
if test "x$destdir" != "x$file"; then
case "$destdir" in
[\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;;
*) thisdir="$thisdir/$destdir" ;;
esac
fi
file=`$ECHO "$file" | /usr/bin/sed 's%^.*/%%'`
file=`ls -ld "$thisdir/$file" | /usr/bin/sed -n 's/.*-> //p'`
done
# Usually 'no', except on cygwin/mingw when embedded into
# the cwrapper.
WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
# special case for '.'
if test "$thisdir" = "."; then
thisdir=`pwd`
fi
# remove .libs from thisdir
case "$thisdir" in
*[\\/].libs ) thisdir=`$ECHO "$thisdir" | /usr/bin/sed 's%[\\/][^\\/]*$%%'` ;;
.libs ) thisdir=. ;;
esac
fi
# Try to get the absolute directory name.
absdir=`cd "$thisdir" && pwd`
test -n "$absdir" && thisdir="$absdir"
program=lt-'osfp_test'
progdir="$thisdir/.libs"
if test ! -f "$progdir/$program" ||
{ file=`ls -1dt "$progdir/$program" "$progdir/../$program" 2>/dev/null | /usr/bin/sed 1q`; \
test "X$file" != "X$progdir/$program"; }; then
file="$$-$program"
if test ! -d "$progdir"; then
mkdir "$progdir"
else
rm -f "$progdir/$file"
fi
# relink executable if necessary
if test -n "$relink_command"; then
if relink_command_output=`eval $relink_command 2>&1`; then :
else
printf %s\n "$relink_command_output" >&2
rm -f "$progdir/$file"
exit 1
fi
fi
mv -f "$progdir/$file" "$progdir/$program" 2>/dev/null ||
{ rm -f "$progdir/$program";
mv -f "$progdir/$file" "$progdir/$program"; }
rm -f "$progdir/$file"
fi
if test -f "$progdir/$program"; then
if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then
# Run the actual program with our arguments.
func_exec_program ${1+"$@"}
fi
else
# The program doesn't exist.
$ECHO "$0: error: \`$progdir/$program' does not exist" 1>&2
$ECHO "This script is just a wrapper for $program." 1>&2
$ECHO "See the libtool documentation for more information." 1>&2
exit 1
fi
fi

67229
test/osfp_test.log Normal file

File diff suppressed because it is too large Load Diff

199
test/test.c Normal file
View File

@@ -0,0 +1,199 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "cJSON.h"
#include "osfp.h"
#include "osfp_fingerprint.h"
#include "osfp_score_db.h"
#include "osfp_log.h"
#include "osfp_common.h"
#define DATA_FILE_PATH "./data.json"
#define DB_FILE_PATH "./db.json"
#define TEST_FILE_PATH "./test.json"
#define LOG_FILE_PATH "./osfp_test.log"
unsigned char *data_file_path = DATA_FILE_PATH;
unsigned char *db_file_path;
unsigned char *test_file_path;
FILE *data_file_ptr;
FILE *db_file_ptr;
FILE *test_file_ptr;
FILE *log_file_ptr;
unsigned int debug_enable;
void test_data_prepare()
{
char *file_buffer;
if (test_file_path == NULL) {
file_buffer = osfp_score_db_read_file(data_file_path);
if (file_buffer == NULL) {
osfp_log_error("read file: '%s'\n", data_file_path);
exit(1);
}
cJSON *data;
data = cJSON_Parse(file_buffer);
if (data == NULL) {
exit(1);
}
cJSON *test;
test = cJSON_CreateArray();
unsigned int data_count = cJSON_GetArraySize(data);
unsigned int db_count = data_count * 8 / 10;
unsigned int test_count = data_count - db_count;
srand(time(0));
int i;
for (i = 0; i < test_count; i++) {
cJSON_AddItemToArray(test, cJSON_DetachItemFromArray(data, rand() % data_count));
data_count--;
}
db_file_ptr = fopen(DB_FILE_PATH, "w");
test_file_ptr = fopen(TEST_FILE_PATH, "w");
fprintf(db_file_ptr, "%s", cJSON_Print(data));
fprintf(test_file_ptr, "%s", cJSON_Print(test));
fflush(db_file_ptr);
fflush(test_file_ptr);
db_file_path = DB_FILE_PATH;
test_file_path = TEST_FILE_PATH;
}else{
db_file_path = data_file_path;
}
}
void test_miss_rate()
{
int i;
unsigned int other_count = 0;
unsigned int unknown_count = 0;
unsigned int identify_failed_count = 0;
unsigned int wrong_count = 0;
unsigned int verified_count = 0;
unsigned int fingerprint_count = 0;
char *file_buffer;
file_buffer = osfp_score_db_read_file(test_file_path);
if (file_buffer == NULL) {
osfp_log_error("read file: '%s'\n", test_file_path);
exit(1);
}
cJSON *root = cJSON_Parse(file_buffer);
if (root == NULL) {
exit(1);
}
fingerprint_count = cJSON_GetArraySize(root);
if (debug_enable) {
osfp_log_level_set(OSFP_LOG_LEVEL_DEBUG);
}
struct osfp_db *osfp_db = osfp_db_new(db_file_path);
if (osfp_db == NULL) {
printf("could not create osfp context. fingerprints file: %s\n", db_file_path);
exit(1);
}
//osfp_score_db_debug_print(osfp_db->score_db);
FILE *log_file_ptr = fopen(LOG_FILE_PATH, "w");
for (i = 0; i < fingerprint_count; i++) {
cJSON *entry = cJSON_GetArrayItem(root, i);
if (entry) {
cJSON *os_class_json = cJSON_GetObjectItem(entry, "os");
int os_class = osfp_os_class_name_to_id(os_class_json->valuestring);
const char *fp_str = cJSON_PrintUnformatted(entry);
struct osfp_result *result = osfp_json_identify(osfp_db, fp_str);
if (result == NULL) {
identify_failed_count++;
continue;
}
if (os_class == result->likely_os_class) {
verified_count++;
osfp_result_free(result);
continue;
}
wrong_count++;
if (result->likely_os_class == OSFP_OS_CLASS_OTHERS) {
other_count++;
}
if (result->likely_os_class == OSFP_OS_CLASS_UNKNOWN) {
unknown_count++;
}
fprintf(log_file_ptr, "expect: %s, result: %s\n", os_class_json->valuestring, osfp_result_os_name_get(result));
char *result_json = osfp_result_score_detail_export(result);
if (result_json) {
fprintf(log_file_ptr, "%s\n", result_json);
} else {
fprintf(log_file_ptr, "result detail error:%p\n", result);
}
fflush(log_file_ptr);
osfp_result_free(result);
}
}
printf("total %u, failed %u, pass %u, wrong %u, other %u, unknown %u\n",
fingerprint_count, identify_failed_count, verified_count, wrong_count, other_count, unknown_count);
printf("miss rate: %d%%\n", 100 - (verified_count * 100 / fingerprint_count));
printf("details in: %s\n", LOG_FILE_PATH);
osfp_db_free(osfp_db);
}
int main(int argc, char **argv)
{
int r;
while ((r = getopt(argc, argv, "+f:t:o:d")) != -1) {
switch(r) {
case 'f':
data_file_path = (unsigned char*)optarg;
break;
case 't':
test_file_path = (unsigned char*)optarg;
break;
case 'd':
debug_enable = 1;
break;
default:
break;
}
}
assert(0 == test_osfp_fingerprinting_ipv4());
assert(0 == test_osfp_fingerprinting_ipv6());
assert(0 == test_osfp_fingerprinting_tcp_option());
assert(0 == test_osfp_score_db());
test_data_prepare();
test_miss_rate();
return 0;
}