Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af67397fc9 | ||
|
|
7a2d2b5020 | ||
|
|
13721781f7 | ||
|
|
c9247d713d | ||
|
|
8832411f29 | ||
|
|
4cd2fdb881 | ||
|
|
e80ae01d68 | ||
|
|
50e455bf8e | ||
|
|
7a5b2e9f07 | ||
|
|
c0cfa2e61d | ||
|
|
77a47deb92 | ||
|
|
1de666a56e | ||
|
|
168f931da6 | ||
|
|
e62ff39d81 | ||
|
|
cfaf003d4f | ||
|
|
8b8627b0d8 | ||
|
|
7e1849233b | ||
|
|
b7ee724244 | ||
|
|
9056ffb00f | ||
|
|
f6af0204eb | ||
|
|
f203d15fae | ||
|
|
64697d137e | ||
|
|
56e3dec5f0 | ||
|
|
1a559eba99 | ||
|
|
15d4a2d271 | ||
|
|
eeb4cc0b6b | ||
|
|
554867aa4e |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -21,3 +21,7 @@ missing
|
||||
compile
|
||||
libosfp-config
|
||||
osfp_match
|
||||
osfp_example
|
||||
osfp_test
|
||||
sample
|
||||
osfp_test.log
|
||||
|
||||
257
.gitlab-ci.yml
Normal file
257
.gitlab-ci.yml
Normal file
@@ -0,0 +1,257 @@
|
||||
variables:
|
||||
GIT_STRATEGY: "clone"
|
||||
BUILD_IMAGE_CENTOS7: "git.mesalab.cn:7443/mesa_platform/build-env:master"
|
||||
BUILD_IMAGE_CENTOS8: "git.mesalab.cn:7443/mesa_platform/build-env:rockylinux"
|
||||
BUILD_PADDING_PREFIX: /tmp/padding_for_CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX/
|
||||
INSTALL_DEPENDENCY_LIBRARY: libasan libpcap-devel
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
|
||||
.build_before_script:
|
||||
before_script:
|
||||
- mkdir -p $BUILD_PADDING_PREFIX/$CI_PROJECT_NAMESPACE/
|
||||
- ln -s $CI_PROJECT_DIR $BUILD_PADDING_PREFIX/$CI_PROJECT_PATH
|
||||
- cd $BUILD_PADDING_PREFIX/$CI_PROJECT_PATH
|
||||
- chmod +x ./ci/travis.sh
|
||||
- yum makecache
|
||||
- yum install -y elfutils-libelf-devel
|
||||
|
||||
|
||||
.build_by_travis_for_centos7:
|
||||
stage: build
|
||||
image: $BUILD_IMAGE_CENTOS7
|
||||
extends: .build_before_script
|
||||
script:
|
||||
- yum install -y libmnl-devel
|
||||
- yum install -y libnfnetlink-devel
|
||||
- ./ci/travis.sh
|
||||
- cd build
|
||||
tags:
|
||||
- share
|
||||
|
||||
.build_by_travis_for_centos8:
|
||||
stage: build
|
||||
image: $BUILD_IMAGE_CENTOS8
|
||||
extends: .build_before_script
|
||||
script:
|
||||
- dnf --enablerepo=powertools install -y libmnl-devel
|
||||
- dnf --enablerepo=powertools install -y libnfnetlink-devel
|
||||
- ./ci/travis.sh
|
||||
tags:
|
||||
- share
|
||||
|
||||
run_test_for_centos7:
|
||||
stage: test
|
||||
image: $BUILD_IMAGE_CENTOS7
|
||||
extends: .build_by_travis_for_centos7
|
||||
script:
|
||||
- yum makecache
|
||||
- ./ci/travis.sh
|
||||
- cd build
|
||||
- ctest --verbose
|
||||
|
||||
run_test_for_centos8:
|
||||
stage: test
|
||||
image: $BUILD_IMAGE_CENTOS8
|
||||
extends: .build_by_travis_for_centos8
|
||||
script:
|
||||
- yum makecache
|
||||
- ./ci/travis.sh
|
||||
- cd build
|
||||
- ctest --verbose
|
||||
|
||||
branch_build_debug_for_centos7:
|
||||
stage: build
|
||||
extends: .build_by_travis_for_centos7
|
||||
variables:
|
||||
BUILD_TYPE: Debug
|
||||
except:
|
||||
- /^develop.*$/i
|
||||
- /^master.*$/i
|
||||
- /^dev*.*$/i
|
||||
- /^rel*.*$/i
|
||||
- tags
|
||||
|
||||
branch_build_release_for_centos7:
|
||||
stage: build
|
||||
variables:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
extends: .build_by_travis_for_centos7
|
||||
except:
|
||||
- /^develop.*$/i
|
||||
- /^master.*$/i
|
||||
- /^dev*.*$/i
|
||||
- /^rel*.*$/i
|
||||
- tags
|
||||
|
||||
develop_build_debug_for_centos7:
|
||||
stage: build
|
||||
extends: .build_by_travis_for_centos7
|
||||
variables:
|
||||
BUILD_TYPE: Debug
|
||||
PACKAGE: 1
|
||||
UPLOAD_RPM: 1
|
||||
ASAN_OPTION: ADDRESS
|
||||
TESTING_VERSION_BUILD: 1
|
||||
PULP3_REPO_NAME: framework-testing-x86_64.el7
|
||||
PULP3_DIST_NAME: framework-testing-x86_64.el7
|
||||
artifacts:
|
||||
name: "libosfp-$CI_COMMIT_REF_NAME-debug"
|
||||
paths:
|
||||
- build/*.rpm
|
||||
only:
|
||||
- /^develop.*$/i
|
||||
- /^master.*$/i
|
||||
- /^dev*.*$/i
|
||||
- /^rel*.*$/i
|
||||
|
||||
develop_build_release_for_centos7:
|
||||
stage: build
|
||||
extends: .build_by_travis_for_centos7
|
||||
variables:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
PACKAGE: 1
|
||||
UPLOAD_RPM: 1
|
||||
TESTING_VERSION_BUILD: 1
|
||||
PULP3_REPO_NAME: framework-testing-x86_64.el7
|
||||
PULP3_DIST_NAME: framework-testing-x86_64.el7
|
||||
artifacts:
|
||||
name: "libosfp-$CI_COMMIT_REF_NAME-release"
|
||||
paths:
|
||||
- build/*.rpm
|
||||
only:
|
||||
- /^develop.*$/i
|
||||
- /^master.*$/i
|
||||
- /^dev*.*$/i
|
||||
- /^rel*.*$/i
|
||||
|
||||
release_build_debug_for_centos7:
|
||||
stage: build
|
||||
variables:
|
||||
BUILD_TYPE: Debug
|
||||
PACKAGE: 1
|
||||
UPLOAD_RPM: 1
|
||||
PULP3_REPO_NAME: framework-stable-x86_64.el7
|
||||
PULP3_DIST_NAME: framework-stable-x86_64.el7
|
||||
extends: .build_by_travis_for_centos7
|
||||
artifacts:
|
||||
name: "libosfp-$CI_COMMIT_REF_NAME-debug"
|
||||
paths:
|
||||
- build/*.rpm
|
||||
only:
|
||||
- tags
|
||||
|
||||
release_build_release_for_centos7:
|
||||
stage: build
|
||||
variables:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
PACKAGE: 1
|
||||
UPLOAD_RPM: 1
|
||||
PULP3_REPO_NAME: framework-stable-x86_64.el7
|
||||
PULP3_DIST_NAME: framework-stable-x86_64.el7
|
||||
extends: .build_by_travis_for_centos7
|
||||
artifacts:
|
||||
name: "libosfp-$CI_COMMIT_REF_NAME-release"
|
||||
paths:
|
||||
- build/*.rpm
|
||||
only:
|
||||
- tags
|
||||
|
||||
branch_build_debug_for_centos8:
|
||||
stage: build
|
||||
extends: .build_by_travis_for_centos8
|
||||
variables:
|
||||
BUILD_TYPE: Debug
|
||||
except:
|
||||
- /^develop.*$/i
|
||||
- /^master.*$/i
|
||||
- /^dev*.*$/i
|
||||
- /^rel*.*$/i
|
||||
- tags
|
||||
|
||||
branch_build_release_for_centos8:
|
||||
stage: build
|
||||
variables:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
extends: .build_by_travis_for_centos8
|
||||
except:
|
||||
- /^develop.*$/i
|
||||
- /^master.*$/i
|
||||
- /^dev*.*$/i
|
||||
- /^rel*.*$/i
|
||||
- tags
|
||||
|
||||
develop_build_debug_for_centos8:
|
||||
stage: build
|
||||
extends: .build_by_travis_for_centos8
|
||||
variables:
|
||||
BUILD_TYPE: Debug
|
||||
PACKAGE: 1
|
||||
UPLOAD_RPM: 1
|
||||
ASAN_OPTION: ADDRESS
|
||||
TESTING_VERSION_BUILD: 1
|
||||
PULP3_REPO_NAME: framework-testing-x86_64.el8
|
||||
PULP3_DIST_NAME: framework-testing-x86_64.el8
|
||||
artifacts:
|
||||
name: "libosfp-$CI_COMMIT_REF_NAME-debug"
|
||||
paths:
|
||||
- build/*.rpm
|
||||
only:
|
||||
- /^develop.*$/i
|
||||
- /^master.*$/i
|
||||
- /^dev*.*$/i
|
||||
- /^rel*.*$/i
|
||||
|
||||
develop_build_release_for_centos8:
|
||||
stage: build
|
||||
extends: .build_by_travis_for_centos8
|
||||
variables:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
PACKAGE: 1
|
||||
UPLOAD_RPM: 1
|
||||
TESTING_VERSION_BUILD: 1
|
||||
PULP3_REPO_NAME: framework-testing-x86_64.el8
|
||||
PULP3_DIST_NAME: framework-testing-x86_64.el8
|
||||
artifacts:
|
||||
name: "libosfp-$CI_COMMIT_REF_NAME-release"
|
||||
paths:
|
||||
- build/*.rpm
|
||||
only:
|
||||
- /^develop.*$/i
|
||||
- /^master.*$/i
|
||||
- /^dev*.*$/i
|
||||
- /^rel*.*$/i
|
||||
|
||||
release_build_debug_for_centos8:
|
||||
stage: build
|
||||
variables:
|
||||
BUILD_TYPE: Debug
|
||||
PACKAGE: 1
|
||||
UPLOAD_RPM: 1
|
||||
PULP3_REPO_NAME: framework-stable-x86_64.el8
|
||||
PULP3_DIST_NAME: framework-stable-x86_64.el8
|
||||
extends: .build_by_travis_for_centos8
|
||||
artifacts:
|
||||
name: "libosfp-$CI_COMMIT_REF_NAME-debug"
|
||||
paths:
|
||||
- build/*.rpm
|
||||
only:
|
||||
- tags
|
||||
|
||||
release_build_release_for_centos8:
|
||||
stage: build
|
||||
variables:
|
||||
BUILD_TYPE: RelWithDebInfo
|
||||
PACKAGE: 1
|
||||
UPLOAD_RPM: 1
|
||||
PULP3_REPO_NAME: framework-stable-x86_64.el8
|
||||
PULP3_DIST_NAME: framework-stable-x86_64.el8
|
||||
extends: .build_by_travis_for_centos8
|
||||
artifacts:
|
||||
name: "libosfp-$CI_COMMIT_REF_NAME-release"
|
||||
paths:
|
||||
- build/*.rpm
|
||||
only:
|
||||
- tags
|
||||
81
CMakeLists.txt
Normal file
81
CMakeLists.txt
Normal file
@@ -0,0 +1,81 @@
|
||||
cmake_minimum_required (VERSION 2.8)
|
||||
|
||||
set(lib_name osfp)
|
||||
|
||||
project (${lib_name})
|
||||
|
||||
set(LIB_MAJOR_VERSION 1)
|
||||
set(LIB_MINOR_VERSION 3)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
|
||||
include(Version)
|
||||
|
||||
set(CMAKE_MACOSX_RPATH 0)
|
||||
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -Wall)
|
||||
set(CMAKE_INSTALL_PREFIX /opt/MESA)
|
||||
|
||||
#for ASAN
|
||||
option(ENABLE_SANITIZE_ADDRESS "Enable AddressSanitizer" FALSE)
|
||||
option(ENABLE_SANITIZE_THREAD "Enable ThreadSanitizer" FALSE)
|
||||
|
||||
if(ENABLE_SANITIZE_ADDRESS)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
|
||||
elseif(ENABLE_SANITIZE_THREAD)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread -fno-omit-frame-pointer")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread -fno-omit-frame-pointer")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
|
||||
endif()
|
||||
# end of for ASAN
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third_party/cJSON/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third_party/uthash/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/src/)
|
||||
|
||||
aux_source_directory(${PROJECT_SOURCE_DIR}/third_party/cJSON/ SRCLIST)
|
||||
aux_source_directory(${PROJECT_SOURCE_DIR}/src SRCLIST)
|
||||
|
||||
# Shared Library Output
|
||||
add_library(${lib_name}_shared SHARED ${SRCLIST})
|
||||
set_target_properties(${lib_name}_shared PROPERTIES LINK_FLAGS
|
||||
"-Wl,--version-script=${PROJECT_SOURCE_DIR}/src/version.map")
|
||||
|
||||
if(DEFINED MESA_SHARED_INSTALL_DIR)
|
||||
set_target_properties(${lib_name}_shared PROPERTIES OUTPUT_NAME ${lib_name} LIBRARY_OUTPUT_DIRECTORY ${MESA_SHARED_INSTALL_DIR})
|
||||
else()
|
||||
set_target_properties(${lib_name}_shared PROPERTIES OUTPUT_NAME ${lib_name})
|
||||
endif()
|
||||
|
||||
set_target_properties(${lib_name}_shared PROPERTIES VERSION ${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION})
|
||||
set_target_properties(${lib_name}_shared PROPERTIES SOVERSION ${LIB_MAJOR_VERSION})
|
||||
|
||||
# static Library Output
|
||||
add_library(${lib_name}_static STATIC ${SRCLIST})
|
||||
set_target_properties(${lib_name}_static PROPERTIES OUTPUT_NAME ${lib_name})
|
||||
|
||||
set(CMAKE_INSTALL_PREFIX /opt/MESA)
|
||||
|
||||
install(FILES src/osfp.h DESTINATION
|
||||
${CMAKE_INSTALL_PREFIX}/include/MESA COMPONENT devel)
|
||||
install(TARGETS ${lib_name}_shared LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib COMPONENT LIBRARIES)
|
||||
install(FILES src/osfp.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/MESA COMPONENT HEADER)
|
||||
install(FILES fp.json DESTINATION /var/lib/libosfp COMPONENT PROFILE)
|
||||
|
||||
add_executable(${lib_name}_sample example/sample.c)
|
||||
target_link_libraries(${lib_name}_sample ${lib_name}_shared)
|
||||
|
||||
add_executable(${lib_name}_example example/osfp_example.c)
|
||||
target_link_libraries(${lib_name}_example ${lib_name}_static)
|
||||
target_link_libraries(${lib_name}_example pcap)
|
||||
|
||||
add_executable(${lib_name}_test test/test.c)
|
||||
target_link_libraries(${lib_name}_test ${lib_name}_static)
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_test(NAME test COMMAND ${lib_name}_test -f ../fp.json -t ../data.json)
|
||||
|
||||
include(Package)
|
||||
@@ -1,2 +0,0 @@
|
||||
SUBDIRS = src example
|
||||
ACLOCAL_AMFLAGS=-I m4
|
||||
23
README.md
23
README.md
@@ -1,4 +1,25 @@
|
||||
# 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_TYPE=Debug PACKAGE=1 ./ci/travis.sh
|
||||
yum install -y ./build/*.rpm
|
||||
```
|
||||
|
||||
## library usage
|
||||
```
|
||||
gcc -I./src example/sample.c -o sample -L./build -losfp; cat example/sample.c
|
||||
LD_LIBRARY_PATH=${PWD}/build ./sample
|
||||
```
|
||||
|
||||
## run example
|
||||
|
||||
```
|
||||
# load the fingerprint file and capture on eth0, filter tcp port 8888
|
||||
./build/osfp_example -f /var/lib/libosfp/fp.json -i eth0 "tcp port 8888"
|
||||
```
|
||||
|
||||
14
autogen.sh
14
autogen.sh
@@ -1,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Run this to generate all the initial makefiles, etc.
|
||||
if which libtoolize > /dev/null; then
|
||||
echo "Found libtoolize"
|
||||
libtoolize -c
|
||||
elif which glibtoolize > /dev/null; then
|
||||
echo "Found glibtoolize"
|
||||
glibtoolize -c
|
||||
else
|
||||
echo "Failed to find libtoolize or glibtoolize, please ensure it is installed and accessible via your PATH env variable"
|
||||
exit 1
|
||||
fi;
|
||||
autoreconf -ifv || exit 1
|
||||
echo "You can now run \"./configure\" and then \"make\"."
|
||||
32
autorelease.sh
Normal file
32
autorelease.sh
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
if [ $# -lt 6 ] ; then
|
||||
echo "USAGE: ./autorelease.sh [API_V4_URL] [PROJECT_URL]
|
||||
[PROJECT_ID] [TOKEN]
|
||||
[COMMIT_TAG] [JOB] [PROJECT_NAME]"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
CI_API_V4_URL=$1
|
||||
CI_PROJECT_URL=$2
|
||||
CI_PROJECT_ID=$3
|
||||
CI_TOKEN=$4
|
||||
CI_COMMIT_TAG=$5
|
||||
ARTIFACTS_JOB=$6
|
||||
CI_PROJECT_NAME=$7
|
||||
|
||||
res=`echo -e "curl --header \"PRIVATE-TOKEN: $CI_TOKEN\" $CI_API_V4_URL/projects/$CI_PROJECT_ID/releases/$CI_COMMIT_TAG -o /dev/null -s -w %{http_code}"| /bin/bash`
|
||||
|
||||
if [[ $res == "200" ]]; then
|
||||
eval $(echo -e "curl --request POST --header \"PRIVATE-TOKEN: $CI_TOKEN\" \
|
||||
--data name=\"$CI_PROJECT_NAME-$CI_COMMIT_TAG-artifacts.zip\" \
|
||||
--data url=\"$CI_PROJECT_URL/-/jobs/artifacts/$CI_COMMIT_TAG/download?job=$ARTIFACTS_JOB\"\
|
||||
$CI_API_V4_URL/projects/$CI_PROJECT_ID/releases/$CI_COMMIT_TAG/assets/links")
|
||||
else
|
||||
eval $(echo -e "curl --header 'Content-Type: application/json' --header \
|
||||
\"PRIVATE-TOKEN: $CI_TOKEN\" --data '{ \"name\": \"$CI_COMMIT_TAG\", \
|
||||
\"tag_name\": \"$CI_COMMIT_TAG\", \"description\": \"auto_release\",\
|
||||
\"assets\": { \"links\": [{ \"name\": \
|
||||
\"$CI_PROJECT_NAME-$CI_COMMIT_TAG-artifacts.zip\", \"url\": \
|
||||
\"$CI_PROJECT_URL/-/jobs/artifacts/$CI_COMMIT_TAG/download?job=$ARTIFACTS_JOB\"\
|
||||
}] } }' --request POST $CI_API_V4_URL/projects/$CI_PROJECT_ID/releases/")
|
||||
fi
|
||||
1268
autorevision.sh
Normal file
1268
autorevision.sh
Normal file
File diff suppressed because it is too large
Load Diff
48
ci/get-nprocessors.sh
Executable file
48
ci/get-nprocessors.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2017 Google Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# This file is typically sourced by another script.
|
||||
# if possible, ask for the precise number of processors,
|
||||
# otherwise take 2 processors as reasonable default; see
|
||||
# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization
|
||||
if [ -x /usr/bin/getconf ]; then
|
||||
NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN)
|
||||
else
|
||||
NPROCESSORS=2
|
||||
fi
|
||||
|
||||
# as of 2017-09-04 Travis CI reports 32 processors, but GCC build
|
||||
# crashes if parallelized too much (maybe memory consumption problem),
|
||||
# so limit to 4 processors for the time being.
|
||||
if [ $NPROCESSORS -gt 4 ] ; then
|
||||
echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4."
|
||||
NPROCESSORS=4
|
||||
fi
|
||||
3
ci/perpare_pulp3_netrc.sh
Executable file
3
ci/perpare_pulp3_netrc.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env sh
|
||||
set -evx
|
||||
echo "machine ${PULP3_SERVER_URL}\nlogin ${PULP3_SERVER_LOGIN}\npassword ${PULP3_SERVER_PASSWORD}\n" > ~/.netrc
|
||||
71
ci/travis.sh
Executable file
71
ci/travis.sh
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env sh
|
||||
set -evx
|
||||
|
||||
chmod +x ci/get-nprocessors.sh
|
||||
. ci/get-nprocessors.sh
|
||||
|
||||
# if possible, ask for the precise number of processors,
|
||||
# otherwise take 2 processors as reasonable default; see
|
||||
# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization
|
||||
if [ -x /usr/bin/getconf ]; then
|
||||
NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN)
|
||||
else
|
||||
NPROCESSORS=2
|
||||
fi
|
||||
|
||||
# as of 2017-09-04 Travis CI reports 32 processors, but GCC build
|
||||
# crashes if parallelized too much (maybe memory consumption problem),
|
||||
# so limit to 4 processors for the time being.
|
||||
if [ $NPROCESSORS -gt 4 ] ; then
|
||||
echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4."
|
||||
NPROCESSORS=4
|
||||
fi
|
||||
|
||||
# Tell make to use the processors. No preceding '-' required.
|
||||
MAKEFLAGS="j${NPROCESSORS}"
|
||||
export MAKEFLAGS
|
||||
|
||||
env | sort
|
||||
|
||||
# Set default values to OFF for these variables if not specified.
|
||||
: "${NO_EXCEPTION:=OFF}"
|
||||
: "${NO_RTTI:=OFF}"
|
||||
: "${COMPILER_IS_GNUCXX:=OFF}"
|
||||
|
||||
# Install dependency from YUM
|
||||
if [ -n "${INSTALL_DEPENDENCY_LIBRARY}" ]; then
|
||||
yum install -y $INSTALL_DEPENDENCY_LIBRARY
|
||||
fi
|
||||
|
||||
if [ $ASAN_OPTION ] && [ -f "/opt/rh/devtoolset-7/enable" ] ;then
|
||||
source /opt/rh/devtoolset-7/enable
|
||||
fi
|
||||
|
||||
mkdir build || true
|
||||
cd build
|
||||
|
||||
cmake3 -DCMAKE_CXX_FLAGS=$CXX_FLAGS \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \
|
||||
-DENABLE_DEVEL=$ENABLE_DEVEL_SWITCH \
|
||||
-DASAN_OPTION=$ASAN_OPTION \
|
||||
-DVERSION_DAILY_BUILD=$TESTING_VERSION_BUILD \
|
||||
..
|
||||
|
||||
make
|
||||
|
||||
if [ -n "${PACKAGE}" ]; then
|
||||
make package
|
||||
fi
|
||||
|
||||
if [ -n "${UPLOAD_RPM}" ]; then
|
||||
cp ~/rpm_upload_tools.py ./
|
||||
python3 rpm_upload_tools.py ${PULP3_REPO_NAME} ${PULP3_DIST_NAME} *.rpm
|
||||
fi
|
||||
|
||||
if [ -n "${UPLOAD_SYMBOL_FILES}" ]; then
|
||||
rpm -i $SYMBOL_TARGET*debuginfo*.rpm
|
||||
_symbol_file=`find /usr/lib/debug/ -name "$SYMBOL_TARGET*.so*.debug"`
|
||||
cp $_symbol_file ${_symbol_file}info.${CI_COMMIT_SHORT_SHA}
|
||||
sentry-cli upload-dif -t elf ${_symbol_file}info.${CI_COMMIT_SHORT_SHA}
|
||||
fi
|
||||
57
cmake/Package.cmake
Normal file
57
cmake/Package.cmake
Normal file
@@ -0,0 +1,57 @@
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(MY_RPM_NAME_PREFIX "lib${lib_name}-debug")
|
||||
else()
|
||||
set(MY_RPM_NAME_PREFIX "lib${lib_name}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Package: ${MY_RPM_NAME_PREFIX}")
|
||||
|
||||
set(CPACK_PACKAGE_VECDOR "MESA")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "${VERSION_PATCH}.${VERSION_BUILD}")
|
||||
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
|
||||
set(CPACK_PACKAGE_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}.${VERSION_BUILD}")
|
||||
|
||||
execute_process(COMMAND sh changelog.sh ${CMAKE_BINARY_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/cmake)
|
||||
SET(CPACK_RPM_CHANGELOG_FILE ${CMAKE_BINARY_DIR}/changelog.txt)
|
||||
|
||||
# RPM Build
|
||||
set(CPACK_GENERATOR "RPM")
|
||||
set(CPACK_RPM_AUTO_GENERATED_FILE_NAME ON)
|
||||
set(CPACK_RPM_FILE_NAME "RPM-DEFAULT")
|
||||
set(CPACK_RPM_PACKAGE_VENDOR "MESA")
|
||||
set(CPACK_RPM_PACKAGE_AUTOREQPROV "yes")
|
||||
set(CPACK_RPM_PACKAGE_RELEASE_DIST "on")
|
||||
set(CPACK_RPM_DEBUGINFO_PACKAGE "on")
|
||||
|
||||
set(CPACK_RPM_COMPONENT_INSTALL ON)
|
||||
set(CPACK_COMPONENTS_IGNORE_GROUPS 1)
|
||||
set(CPACK_COMPONENTS_GROUPING ONE_PER_GROUP)
|
||||
set(CPACK_COMPONENT_HEADER_DISPLAY_NAME "develop")
|
||||
|
||||
set(CPACK_COMPONENT_LIBRARIES_REQUIRED TRUE)
|
||||
set(CPACK_RPM_LIBRARIES_PACKAGE_NAME ${MY_RPM_NAME_PREFIX})
|
||||
set(CPACK_COMPONENT_LIBRARIES_GROUP "LIBRARIES")
|
||||
set(CPACK_COMPONENT_EXECUTABLE_GROUP "LIBRARIES")
|
||||
set(CPACK_COMPONENT_PROFILE_GROUP "LIBRARIES")
|
||||
|
||||
set(CPACK_COMPONENT_HEADER_REQUIRED TRUE)
|
||||
set(CPACK_RPM_HEADER_PACKAGE_NAME "${MY_RPM_NAME_PREFIX}-devel")
|
||||
set(CPACK_COMPONENT_HEADER_GROUP "header")
|
||||
|
||||
set(CPACK_RPM_HEADER_PACKAGE_REQUIRES_PRE ${CPACK_RPM_LIBRARIES_PACKAGE_NAME})
|
||||
set(CPACK_RPM_HEADER_PACKAGE_CONFLICTS ${CPACK_RPM_HEADER_PACKAGE_NAME})
|
||||
|
||||
set(CPACK_COMPONENTS_ALL LIBRARIES HEADER EXECUTABLE PROFILE)
|
||||
|
||||
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PostInstall.in)
|
||||
set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PostInstall.in)
|
||||
|
||||
set(CPACK_BUILD_SOURCE_DIRS "${CMAKE_SOURCE_DIR}")
|
||||
|
||||
# Must uninstall the debug package before install release package
|
||||
set(CPACK_RPM_PACKAGE_CONFLICTS ${MY_RPM_NAME_PREFIX})
|
||||
|
||||
# set(CPACK_STRIP_FILES TRUE)
|
||||
include(CPack)
|
||||
49
cmake/Version.cmake
Normal file
49
cmake/Version.cmake
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
# Using autorevision.sh to generate version information
|
||||
|
||||
set(__SOURCE_AUTORESIVISION ${CMAKE_SOURCE_DIR}/autorevision.sh)
|
||||
set(__AUTORESIVISION ${CMAKE_BINARY_DIR}/autorevision.sh)
|
||||
set(__VERSION_CACHE ${CMAKE_BINARY_DIR}/version.txt)
|
||||
set(__VERSION_CONFIG ${CMAKE_BINARY_DIR}/version.cmake)
|
||||
|
||||
file(COPY ${__SOURCE_AUTORESIVISION} DESTINATION ${CMAKE_BINARY_DIR}
|
||||
FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE
|
||||
WORLD_READ WORLD_EXECUTE)
|
||||
|
||||
# execute autorevision.sh to generate version information
|
||||
execute_process(COMMAND ${__AUTORESIVISION} -t cmake -o ${__VERSION_CACHE}
|
||||
OUTPUT_FILE ${__VERSION_CONFIG} ERROR_QUIET)
|
||||
include(${__VERSION_CONFIG})
|
||||
|
||||
# extract major, minor, patch version from git tag
|
||||
string(REGEX REPLACE "^v([0-9]+)\\..*" "\\1" VERSION_MAJOR "${VCS_TAG}")
|
||||
string(REGEX REPLACE "^v[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${VCS_TAG}")
|
||||
string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERSION_PATCH "${VCS_TAG}")
|
||||
|
||||
if(NOT VERSION_MAJOR)
|
||||
set(VERSION_MAJOR 1)
|
||||
endif()
|
||||
|
||||
if(NOT VERSION_MINOR)
|
||||
set(VERSION_MINOR 0)
|
||||
endif()
|
||||
|
||||
if(NOT VERSION_PATCH)
|
||||
set(VERSION_PATCH 0)
|
||||
endif()
|
||||
|
||||
set(VERSION "${VERSION_MAJOR}_${VERSION_MINOR}_${VERSION_PATCH}")
|
||||
set(VERSION_BUILD "${VCS_SHORT_HASH}")
|
||||
|
||||
# print information
|
||||
message(STATUS "Version: ${VERSION}-${VERSION_BUILD}")
|
||||
|
||||
option(DEFINE_GIT_VERSION "Set DEFINE_GIT_VERSION to TRUE or FALSE" TRUE)
|
||||
|
||||
if(DEFINE_GIT_VERSION)
|
||||
set(GIT_VERSION
|
||||
"${VERSION}-${CMAKE_BUILD_TYPE}-${VERSION_BUILD}-${VCS_BRANCH}-${VCS_TAG}-${VCS_DATE}")
|
||||
string(REGEX REPLACE "[-:+/\\.]" "_" GIT_VERSION ${GIT_VERSION})
|
||||
|
||||
add_definitions(-DGIT_VERSION=${GIT_VERSION})
|
||||
endif()
|
||||
4
cmake/changelog.sh
Normal file
4
cmake/changelog.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
work_path=$1
|
||||
branch=`git status | grep branch | awk '{print $NF}'`
|
||||
git log --branches=$branch --no-merges --date=local --show-signature --pretty="* %ad %an %ae %nhash: %H%ncommit:%n%B" | awk -F"-" '{print "- "$0}' | sed 's/- \*/\*/g' | sed 's/- $//g' | sed 's/-/ -/g' | sed 's/[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}//g' > $work_path/changelog.txt
|
||||
23
configure.ac
23
configure.ac
@@ -1,23 +0,0 @@
|
||||
AC_INIT([libosfp],[0.0.0],[zhuzhenjun@geedgenetworks.com])
|
||||
AM_INIT_AUTOMAKE([foreign])
|
||||
|
||||
#m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])])AM_SILENT_RULES([yes])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
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],
|
||||
[CFLAGS="-ggdb3 -O0"],
|
||||
[CFLAGS="-g -O2"])
|
||||
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CPP
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_LN_S
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
|
||||
AC_CONFIG_FILES([libosfp-config Makefile src/Makefile example/Makefile])
|
||||
AC_OUTPUT
|
||||
@@ -1,13 +0,0 @@
|
||||
bin_PROGRAMS = osfp_example
|
||||
|
||||
osfp_example_SOURCES = \
|
||||
osfp_example.c
|
||||
|
||||
osfp_example_LDADD = \
|
||||
../src/.libs/libosfp.la
|
||||
|
||||
osfp_example_LDFLAGS = \
|
||||
-lpcap
|
||||
|
||||
osfp_example_CFLAGS = \
|
||||
-I../src
|
||||
@@ -3,21 +3,19 @@
|
||||
#include <string.h>
|
||||
#include <unistd.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 <signal.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <pcap.h>
|
||||
|
||||
#include "libosfp.h"
|
||||
#include "libosfp_fingerprint.h"
|
||||
#include "osfp_common.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 VLAN_HEADER_LEN 4
|
||||
@@ -27,6 +25,7 @@
|
||||
|
||||
#define VLAN_MAX_LAYER 2
|
||||
|
||||
struct osfp_db *g_osfp_db;
|
||||
|
||||
/* Port is just a uint16_t */
|
||||
typedef uint16_t Port;
|
||||
@@ -49,14 +48,6 @@ typedef struct Address_ {
|
||||
#define addr_data8 address.address_un_data8
|
||||
#define addr_in6addr address.address_un_in6
|
||||
|
||||
#define COPY_ADDRESS(a, b) do { \
|
||||
(b)->family = (a)->family; \
|
||||
(b)->addr_data32[0] = (a)->addr_data32[0]; \
|
||||
(b)->addr_data32[1] = (a)->addr_data32[1]; \
|
||||
(b)->addr_data32[2] = (a)->addr_data32[2]; \
|
||||
(b)->addr_data32[3] = (a)->addr_data32[3]; \
|
||||
} while (0)
|
||||
|
||||
/* Set the IPv4 addresses into the Addrs of the Packet.
|
||||
* Make sure p->ip4h is initialized and validated.
|
||||
*
|
||||
@@ -82,18 +73,18 @@ typedef struct Address_ {
|
||||
* Make sure p->ip6h is initialized and validated. */
|
||||
#define SET_IPV6_SRC_ADDR(p, a) do { \
|
||||
(a)->family = AF_INET6; \
|
||||
(a)->addr_data32[0] = (p)->ip6h->saddr.s6_addr32[0]; \
|
||||
(a)->addr_data32[1] = (p)->ip6h->saddr.s6_addr32[1]; \
|
||||
(a)->addr_data32[2] = (p)->ip6h->saddr.s6_addr32[2]; \
|
||||
(a)->addr_data32[3] = (p)->ip6h->saddr.s6_addr32[3]; \
|
||||
(a)->addr_data32[0] = (p)->ip6h->ip6_src.__in6_u.__u6_addr32[0]; \
|
||||
(a)->addr_data32[1] = (p)->ip6h->ip6_src.__in6_u.__u6_addr32[1]; \
|
||||
(a)->addr_data32[2] = (p)->ip6h->ip6_src.__in6_u.__u6_addr32[2]; \
|
||||
(a)->addr_data32[3] = (p)->ip6h->ip6_src.__in6_u.__u6_addr32[3]; \
|
||||
} while (0)
|
||||
|
||||
#define SET_IPV6_DST_ADDR(p, a) do { \
|
||||
(a)->family = AF_INET6; \
|
||||
(a)->addr_data32[0] = (p)->ip6h->daddr.s6_addr32[0]; \
|
||||
(a)->addr_data32[1] = (p)->ip6h->daddr.s6_addr32[1]; \
|
||||
(a)->addr_data32[2] = (p)->ip6h->daddr.s6_addr32[2]; \
|
||||
(a)->addr_data32[3] = (p)->ip6h->daddr.s6_addr32[3]; \
|
||||
(a)->addr_data32[0] = (p)->ip6h->ip6_dst.__in6_u.__u6_addr32[0]; \
|
||||
(a)->addr_data32[1] = (p)->ip6h->ip6_dst.__in6_u.__u6_addr32[1]; \
|
||||
(a)->addr_data32[2] = (p)->ip6h->ip6_dst.__in6_u.__u6_addr32[2]; \
|
||||
(a)->addr_data32[3] = (p)->ip6h->ip6_dst.__in6_u.__u6_addr32[3]; \
|
||||
} while (0)
|
||||
|
||||
#define TCP_GET_RAW_SRC_PORT(tcph) ntohs((tcph)->source)
|
||||
@@ -128,9 +119,12 @@ typedef struct Address_ {
|
||||
typedef struct Packet_ {
|
||||
struct ethhdr *ethh;
|
||||
struct iphdr *iph;
|
||||
struct ipv6hdr *ip6h;
|
||||
struct ip6_hdr *ip6h;
|
||||
struct tcphdr *tcph;
|
||||
|
||||
char srcip[46];
|
||||
char dstip[46];
|
||||
|
||||
Address src;
|
||||
Address dst;
|
||||
union {
|
||||
@@ -153,8 +147,17 @@ typedef struct Packet_ {
|
||||
int vlan_layer;
|
||||
} Packet;
|
||||
|
||||
typedef struct EthernetHdr_ {
|
||||
uint8_t eth_dst[6];
|
||||
uint8_t eth_src[6];
|
||||
uint16_t eth_type;
|
||||
} __attribute__((__packed__)) EthernetHdr;
|
||||
|
||||
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 *pcap_file_name;
|
||||
unsigned char *bpf_string;
|
||||
@@ -163,6 +166,12 @@ pcap_t *pcap_handle;
|
||||
int processed_packet;
|
||||
int link_type;
|
||||
|
||||
struct osfp_profile_counter identify_profile;
|
||||
|
||||
unsigned int identify_failed_count = 0;
|
||||
unsigned int identify_count = 0;
|
||||
unsigned int result_os_count[OSFP_OS_CLASS_MAX];
|
||||
|
||||
void usage(void) {
|
||||
fprintf(stderr,
|
||||
"Usage: osfp_match [ ...options... ] [ 'filter rule' ]\n"
|
||||
@@ -172,17 +181,10 @@ void usage(void) {
|
||||
" -i iface - listen on the specified network interface\n"
|
||||
" -r file - read offline pcap data from a given file\n"
|
||||
" -f file - read fingerprint database from 'file' (%s)\n",
|
||||
DEFAULT_FP_FILE);
|
||||
DEFAULT_FP_FILE_PATH);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
typedef struct EthernetHdr_ {
|
||||
uint8_t eth_dst[6];
|
||||
uint8_t eth_src[6];
|
||||
uint16_t eth_type;
|
||||
} __attribute__((__packed__)) EthernetHdr;
|
||||
|
||||
|
||||
int packet_decode_tcp(Packet *p, const unsigned char *data, unsigned int len)
|
||||
{
|
||||
int ret = -1;
|
||||
@@ -258,15 +260,15 @@ int packet_decode_ipv6(Packet *p, const unsigned char *data, unsigned int len)
|
||||
int ret = -1;
|
||||
unsigned short ip6_payload_len;
|
||||
unsigned char ip6_nexthdr;
|
||||
struct ipv6hdr *ip6h;
|
||||
struct ip6_hdr *ip6h;
|
||||
|
||||
if (len < IPV6_HEADER_LEN) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ip6h = (struct ipv6hdr *)data;
|
||||
ip6_payload_len = ntohs(ip6h->payload_len);
|
||||
ip6_nexthdr = ip6h->nexthdr;
|
||||
ip6h = (struct ip6_hdr *)data;
|
||||
ip6_payload_len = ntohs(ip6h->ip6_ctlun.ip6_un1.ip6_un1_plen);
|
||||
ip6_nexthdr = ip6h->ip6_ctlun.ip6_un1.ip6_un1_nxt;
|
||||
|
||||
if (len < IPV6_HEADER_LEN + ip6_payload_len) {
|
||||
goto exit;
|
||||
@@ -427,90 +429,87 @@ const char *PrintInet(int af, const void *src, char *dst, socklen_t size)
|
||||
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;
|
||||
char str_buf[1024];
|
||||
char str_buf[2048] = "";
|
||||
//unsigned char *iph = (unsigned char *)(p->iph != NULL ? (void *)p->iph : (void *)p->ip6h);
|
||||
struct iphdr *iph;
|
||||
struct ip6_hdr *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);
|
||||
unsigned char *tcph = (unsigned char *)p->tcph;
|
||||
libosfp_result_t result;
|
||||
iph = (struct iphdr *)p->iph;
|
||||
ip6h = (struct ip6_hdr *)p->ip6h;
|
||||
tcph = (struct tcphdr *)p->tcph;
|
||||
tcph_len = tcph->doff << 2;
|
||||
|
||||
printf("Example header match: --------------------------\n");
|
||||
osfp_profile_cycle(c1);
|
||||
osfp_profile_cycle(c2);
|
||||
|
||||
ret = libosfp_header_match(libosfp_context, iph, tcph, &result);
|
||||
if (ret != 0) {
|
||||
printf("libosfp header match failed, erro: %s\n", "?");
|
||||
|
||||
struct osfp_fingerprint fp = {0};
|
||||
if (iph) {
|
||||
osfp_fingerprinting((unsigned char*)iph, (unsigned char*)tcph, tcph_len, &fp, 4);
|
||||
} else if (ip6h) {
|
||||
osfp_fingerprinting((unsigned char*)iph, (unsigned char*)tcph, tcph_len, &fp, 6);
|
||||
} else {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
char srcip[46] = {0}, dstip[46] = {0};
|
||||
Port sp, dp;
|
||||
if (p->iph) {
|
||||
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));
|
||||
osfp_profile_get_cycle(c1);
|
||||
if (iph) {
|
||||
result = osfp_ipv4_identify(osfp_db, iph, tcph, tcph_len);
|
||||
} else if (ip6h) {
|
||||
result = osfp_ipv6_identify(osfp_db, ip6h, tcph, tcph_len);
|
||||
} else {
|
||||
goto exit;
|
||||
}
|
||||
sp = p->sp;
|
||||
dp = p->dp;
|
||||
osfp_profile_get_cycle(c2);
|
||||
osfp_profile_counter_update(&identify_profile, c2 - c1);
|
||||
|
||||
printf("Connection info: %s:%d -> %s:%d\n", srcip, sp, dstip, dp);
|
||||
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));
|
||||
identify_count++;
|
||||
|
||||
libosfp_result_to_buf(&result, str_buf, sizeof(str_buf));
|
||||
fprintf(stdout, "%s\n", str_buf);
|
||||
if (result == NULL) {
|
||||
identify_failed_count++;
|
||||
printf("osfp header match failed, erro: %s\n", "?");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
result_os_count[result->likely_os_class]++;
|
||||
|
||||
char *json = osfp_result_score_detail_export(result);
|
||||
|
||||
if (debug_enable) {
|
||||
if (p->tcph->ack) {
|
||||
printf("--------------------------- SYN/ACK\n");
|
||||
} else {
|
||||
printf("--------------------------- SYN\n");
|
||||
}
|
||||
osfp_fingerprint_to_json_buf(&fp, str_buf, 2048, 0);
|
||||
printf("%s\n", str_buf);
|
||||
printf("Example ipv4 header detect: --------------------------\n");
|
||||
printf("Connection info: %s:%d -> %s:%d\n", p->srcip, p->sp, p->dstip, p->dp);
|
||||
printf("Most likely os class: %s\n", osfp_result_os_name_get(result));
|
||||
printf("Details:\n");
|
||||
printf("%s\n", json);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
free(json);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
if (result) {
|
||||
osfp_result_free(result);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void process_packet(char *user, struct pcap_pkthdr *h, u_char *pkt)
|
||||
{
|
||||
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;
|
||||
|
||||
// decode packet
|
||||
@@ -520,13 +519,24 @@ void process_packet(char *user, struct pcap_pkthdr *h, u_char *pkt)
|
||||
}
|
||||
|
||||
// only for tcp syn request packet
|
||||
if (!p->tcph->syn || p->tcph->ack) {
|
||||
if (!p->tcph->syn) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
example_header_match(libosfp_context, p);
|
||||
|
||||
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
|
||||
int i;
|
||||
for (i = 0; i < 1000; i++) {
|
||||
example_detect(osfp_db, p);
|
||||
}
|
||||
|
||||
printf("--------------------------- processed packet count %d\n", ++processed_packet);
|
||||
|
||||
@@ -534,18 +544,47 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
static void signal_handler(int signum)
|
||||
{
|
||||
printf("profile identify: avg: %lu max: %lu min: %lu curr: %lu total: %lu count: %lu\n",
|
||||
identify_profile.total_cycle / identify_profile.count,
|
||||
identify_profile.max_cycle,
|
||||
identify_profile.min_cycle,
|
||||
identify_profile.curr_cycle,
|
||||
identify_profile.total_cycle,
|
||||
identify_profile.count);
|
||||
|
||||
osfp_profile_print_stats();
|
||||
|
||||
printf("total %u, failed %u\n",
|
||||
identify_count, identify_failed_count);
|
||||
int i;
|
||||
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
|
||||
printf("%s: %u\n", osfp_os_class_id_to_name(i), result_os_count[i]);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
osfp_db_free(g_osfp_db);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int r;
|
||||
|
||||
while ((r = getopt(argc, argv, "+f:i:r:")) != -1) {
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
while ((r = getopt(argc, argv, "+f:i:r:o:d")) != -1) {
|
||||
switch(r) {
|
||||
case 'f':
|
||||
if (fp_file) {
|
||||
if (fp_file_path) {
|
||||
printf("Multiple -f options not supported.\n");
|
||||
exit(1);
|
||||
}
|
||||
fp_file = (unsigned char*)optarg;
|
||||
fp_file_path = (unsigned char*)optarg;
|
||||
break;
|
||||
case 'i':
|
||||
if (if_name) {
|
||||
@@ -561,6 +600,16 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
pcap_file_name = (unsigned char*)optarg;
|
||||
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:
|
||||
usage();
|
||||
break;
|
||||
@@ -576,6 +625,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
|
||||
|
||||
char pcap_err[PCAP_ERRBUF_SIZE];
|
||||
@@ -620,35 +678,36 @@ int main(int argc, char *argv[])
|
||||
// get link type
|
||||
link_type = pcap_datalink(pcap_handle);
|
||||
|
||||
// create libosfp context
|
||||
if (fp_file == NULL) {
|
||||
fp_file = DEFAULT_FP_FILE;
|
||||
// create osfp db
|
||||
if (fp_file_path == NULL) {
|
||||
fp_file_path = DEFAULT_FP_FILE_PATH;
|
||||
}
|
||||
|
||||
libosfp_context_t *libosfp_context = libosfp_context_create(fp_file);
|
||||
if (libosfp_context == NULL) {
|
||||
printf("could not create libosfp context. fingerprints file: %s\n", fp_file);
|
||||
if (debug_enable) {
|
||||
osfp_log_level_set(OSFP_LOG_LEVEL_DEBUG);
|
||||
}
|
||||
|
||||
osfp_profile_set(1);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// setup libosfp context
|
||||
r = libosfp_context_setup(libosfp_context);
|
||||
if (r != LIBOSFP_NOERR) {
|
||||
printf("could not setup libosfp context. error: %d\n", LIBOSFP_NOERR);
|
||||
exit(1);
|
||||
}
|
||||
g_osfp_db = osfp_db;
|
||||
|
||||
// loop
|
||||
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) {
|
||||
printf("error code: %d, error: %s\n", r, pcap_geterr(pcap_handle));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// create libosfp context
|
||||
libosfp_context_destroy(libosfp_context);
|
||||
// destroy osfp db
|
||||
osfp_db_free(osfp_db);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
39
example/sample.c
Normal file
39
example/sample.c
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "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";
|
||||
char *detail_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));
|
||||
detail_json = osfp_result_score_detail_export(result);
|
||||
printf("details: \n%s\n", detail_json);
|
||||
free(detail_json);
|
||||
osfp_result_free(result);
|
||||
}
|
||||
osfp_db_free(db);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
BIN
pcap/linux_ipv4_syn.pcap
Normal file
BIN
pcap/linux_ipv4_syn.pcap
Normal file
Binary file not shown.
BIN
pcap/linux_ipv4_syn_ack.pcap
Normal file
BIN
pcap/linux_ipv4_syn_ack.pcap
Normal file
Binary file not shown.
BIN
pcap/synmerge.pcap
Normal file
BIN
pcap/synmerge.pcap
Normal file
Binary file not shown.
BIN
pcap/test.pcap
Normal file
BIN
pcap/test.pcap
Normal file
Binary file not shown.
BIN
pcap/unknown_ipv6_syn.pcap
Normal file
BIN
pcap/unknown_ipv6_syn.pcap
Normal file
Binary file not shown.
BIN
pcap/unknown_ipv6_syn_ack.pcap
Normal file
BIN
pcap/unknown_ipv6_syn_ack.pcap
Normal file
Binary file not shown.
BIN
pcap/windows_ipv4_syn.pcap
Normal file
BIN
pcap/windows_ipv4_syn.pcap
Normal file
Binary file not shown.
BIN
pcap/windows_ipv6_malformed.pcap
Normal file
BIN
pcap/windows_ipv6_malformed.pcap
Normal file
Binary file not shown.
BIN
pcap/windows_ipv6_syn_ack.pcap
Normal file
BIN
pcap/windows_ipv6_syn_ack.pcap
Normal file
Binary file not shown.
@@ -1,19 +0,0 @@
|
||||
lib_LTLIBRARIES = libosfp.la
|
||||
|
||||
libosfp_la_SOURCES = \
|
||||
utarray.h \
|
||||
uthash.h \
|
||||
utlist.h \
|
||||
utringbuffer.h \
|
||||
utstack.h \
|
||||
utstring.h \
|
||||
cJSON.h \
|
||||
cJSON.c \
|
||||
libosfp.h \
|
||||
libosfp.c \
|
||||
libosfp_fingerprint.h \
|
||||
libosfp_fingerprint.c \
|
||||
libosfp_score_db.h \
|
||||
libosfp_score_db.c
|
||||
|
||||
pkginclude_HEADERS = libosfp.h libosfp_fingerprint.h
|
||||
207
src/libosfp.c
207
src/libosfp.c
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
201
src/osfp.c
Normal file
201
src/osfp.c
Normal file
@@ -0,0 +1,201 @@
|
||||
#include <sys/fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cJSON.h"
|
||||
|
||||
#include "osfp_common.h"
|
||||
|
||||
#include "osfp.h"
|
||||
#include "osfp_fingerprint.h"
|
||||
#include "osfp_score_db.h"
|
||||
#include "osfp_log.h"
|
||||
|
||||
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;
|
||||
cJSON *root;
|
||||
cJSON *array;
|
||||
cJSON *os_score;
|
||||
cJSON *matched;
|
||||
|
||||
osfp_profile_cycle(c1);
|
||||
osfp_profile_cycle(c2);
|
||||
|
||||
osfp_profile_get_cycle(c1);
|
||||
|
||||
if (result == NULL) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result->matched) {
|
||||
matched = cJSON_AddObjectToObject(root, "matched");
|
||||
if (matched) {
|
||||
cJSON_AddStringToObject(matched, "fingerprint", result->matched);
|
||||
}
|
||||
}
|
||||
|
||||
result_str = cJSON_Print(root);
|
||||
if (result_str == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
|
||||
osfp_profile_get_cycle(c2);
|
||||
osfp_profile_counter_update(&osfp_profile_result_export, c2 - c1);
|
||||
|
||||
return result_str;
|
||||
exit:
|
||||
if (root) {
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void osfp_result_free(struct osfp_result *result)
|
||||
{
|
||||
if (result) {
|
||||
free(result);
|
||||
}
|
||||
}
|
||||
|
||||
struct osfp_result *osfp_ip_identify(struct osfp_db *db, unsigned char *l3_hdr, unsigned char *l4_hdr, unsigned int l4_hdr_len, unsigned int ip_version)
|
||||
{
|
||||
int ret;
|
||||
struct osfp_fingerprint fp;
|
||||
struct osfp_os_class_score os_class_score;
|
||||
struct osfp_result *result;
|
||||
const char *matched;
|
||||
|
||||
if (db == NULL || l3_hdr == NULL || l4_hdr == NULL || l4_hdr_len == 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = osfp_fingerprinting(l3_hdr, l4_hdr, l4_hdr_len, &fp, ip_version);
|
||||
if (ret != 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
matched = osfp_score_db_prefilter(db->score_db, &fp, &os_class_score);
|
||||
if (matched == NULL) {
|
||||
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, matched);
|
||||
if (result == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
return result;
|
||||
exit:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct osfp_result *osfp_ipv4_identify(struct osfp_db *db, struct iphdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len)
|
||||
{
|
||||
return osfp_ip_identify(db, (unsigned char *)l3_hdr, (unsigned char *)l4_hdr, (unsigned int)l4_hdr_len, 4);
|
||||
}
|
||||
|
||||
struct osfp_result *osfp_ipv6_identify(struct osfp_db *db, struct ip6_hdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len)
|
||||
{
|
||||
return osfp_ip_identify(db, (unsigned char *)l3_hdr, (unsigned char *)l4_hdr, (unsigned int)l4_hdr_len, 6);
|
||||
}
|
||||
|
||||
struct osfp_db *osfp_db_new(const char *fp_path)
|
||||
{
|
||||
int ret;
|
||||
struct osfp_db *db;
|
||||
|
||||
if (fp_path == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (0 != access(fp_path, R_OK)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
db = calloc(1, sizeof(struct osfp_db));
|
||||
if (db == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
db->db_json_path = strdup((const char*)fp_path);
|
||||
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);
|
||||
}
|
||||
}
|
||||
95
src/osfp.h
Normal file
95
src/osfp.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#ifndef __OSFP_H__
|
||||
#define __OSFP_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
struct osfp_result_detail;
|
||||
struct osfp_result;
|
||||
struct osfp_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 通过 IP 头部和 TCP 头部识别操作系统。
|
||||
*
|
||||
* @param db 操作系统指纹库。
|
||||
* @param l3_hdr 指向 IP 头部的指针。
|
||||
* @param l4_hdr 指向 TCP 头部的指针。
|
||||
* @param l4_hdr_len TCP 头部的长度(注意:包含TCP选项部分)。
|
||||
* @param ip_version IP头版本号,4 或 6)。
|
||||
* @return 指向操作系统识别结果的指针。
|
||||
*/
|
||||
struct osfp_result *osfp_ip_identify(struct osfp_db *db, unsigned char *l3_hdr, unsigned char *l4_hdr, unsigned int l4_hdr_len, unsigned int ip_version);
|
||||
|
||||
/**
|
||||
* @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 ip6_hdr* l3_hdr, struct tcphdr *l4_hdr, size_t l4_hdr_len);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
206
src/osfp_common.c
Normal file
206
src/osfp_common.c
Normal file
@@ -0,0 +1,206 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/fcntl.h>
|
||||
|
||||
#include "osfp_common.h"
|
||||
|
||||
#include "osfp.h"
|
||||
#include "osfp_log.h"
|
||||
|
||||
unsigned int osfp_profile_enable;
|
||||
|
||||
const char *osfp_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
|
||||
};
|
||||
|
||||
struct osfp_profile_counter osfp_profile_fingerprinting;
|
||||
struct osfp_profile_counter osfp_profile_prefilter;
|
||||
struct osfp_profile_counter osfp_profile_score;
|
||||
struct osfp_profile_counter osfp_profile_result_build;
|
||||
struct osfp_profile_counter osfp_profile_result_export;
|
||||
|
||||
const char *osfp_os_class_id_to_name(enum osfp_os_class_id os_class)
|
||||
{
|
||||
return osfp_os_class_name[os_class];
|
||||
}
|
||||
|
||||
enum osfp_os_class_id osfp_os_class_name_to_id(char *name)
|
||||
{
|
||||
int i, namelen;
|
||||
const char *os_class_name;
|
||||
|
||||
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
|
||||
os_class_name = osfp_os_class_id_to_name(i);
|
||||
if (0 == strncmp(name, os_class_name, strlen(os_class_name))) {
|
||||
return (enum osfp_os_class_id)i;
|
||||
}
|
||||
}
|
||||
|
||||
return OSFP_OS_CLASS_MAX;
|
||||
}
|
||||
|
||||
struct osfp_result *osfp_result_build(struct osfp_os_class_score *os_class_score, const char *matched)
|
||||
{
|
||||
int i;
|
||||
unsigned int tmp_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;
|
||||
|
||||
// likely os score
|
||||
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;
|
||||
}
|
||||
|
||||
// prefiltered
|
||||
if (likely_score == OSFP_PERCENTILE) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
// too low to tell os class
|
||||
if (likely_score < OSFP_LOWEST_SCORE_LIMIT) {
|
||||
likely_os_class = OSFP_OS_CLASS_OTHERS;
|
||||
goto end;
|
||||
}
|
||||
|
||||
// when the tied likely scores appear between win/apple-like/unix-like, we throw unknown
|
||||
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
|
||||
if (likely_os_class == i) {
|
||||
continue;
|
||||
}
|
||||
if (likely_score != os_class_score->scores[i]) {
|
||||
continue;
|
||||
}
|
||||
if (likely_os_class == OSFP_OS_CLASS_LINUX && i == OSFP_OS_CLASS_ANDROID) {
|
||||
continue;
|
||||
}
|
||||
if (likely_os_class == OSFP_OS_CLASS_MAC_OS && i == OSFP_OS_CLASS_IOS) {
|
||||
continue;
|
||||
}
|
||||
likely_os_class = OSFP_OS_CLASS_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
end:
|
||||
result->likely_os_class = likely_os_class;
|
||||
result->matched = matched;
|
||||
return result;
|
||||
exit:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void osfp_profile_counter_print(struct osfp_profile_counter *profile, const char *name)
|
||||
{
|
||||
printf("profile %s: avg: %lu max: %lu min: %lu curr: %lu total: %lu count: %lu\n",
|
||||
name,
|
||||
profile->total_cycle / profile->count,
|
||||
profile->max_cycle,
|
||||
profile->min_cycle,
|
||||
profile->curr_cycle,
|
||||
profile->total_cycle,
|
||||
profile->count);
|
||||
}
|
||||
void osfp_profile_counter_update(struct osfp_profile_counter *profile, unsigned long long curr_cycle)
|
||||
{
|
||||
profile->count++;
|
||||
profile->curr_cycle = curr_cycle;
|
||||
profile->total_cycle += curr_cycle;
|
||||
|
||||
if (profile->min_cycle == 0) {
|
||||
profile->min_cycle = curr_cycle;
|
||||
} else {
|
||||
if (profile->min_cycle > curr_cycle) {
|
||||
profile->min_cycle = curr_cycle;
|
||||
}
|
||||
}
|
||||
|
||||
if (profile->max_cycle < curr_cycle) {
|
||||
profile->max_cycle = curr_cycle;
|
||||
}
|
||||
}
|
||||
|
||||
void osfp_profile_print_stats(void)
|
||||
{
|
||||
osfp_profile_counter_print(&osfp_profile_fingerprinting, "fingerprinting");
|
||||
osfp_profile_counter_print(&osfp_profile_prefilter, "prefilter");
|
||||
osfp_profile_counter_print(&osfp_profile_score, "score");
|
||||
osfp_profile_counter_print(&osfp_profile_result_build, "result build");
|
||||
osfp_profile_counter_print(&osfp_profile_result_export, "result export");
|
||||
}
|
||||
|
||||
void osfp_profile_set(unsigned int enabled)
|
||||
{
|
||||
osfp_profile_enable = enabled;
|
||||
}
|
||||
|
||||
char *osfp_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;
|
||||
}
|
||||
|
||||
186
src/osfp_common.h
Normal file
186
src/osfp_common.h
Normal file
@@ -0,0 +1,186 @@
|
||||
#ifndef __OSFP_COMMON_H__
|
||||
#define __OSFP_COMMON_H__
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
extern unsigned int osfp_profile_enable;
|
||||
|
||||
#define osfp_profile_cycle(x) volatile unsigned long long x = 0
|
||||
#define osfp_profile_get_cycle(x) do { \
|
||||
if (__builtin_expect(!!(osfp_profile_enable), 0)) { \
|
||||
x = osfp_rdtsc(); \
|
||||
} else { \
|
||||
x = 0; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
struct osfp_profile_counter {
|
||||
unsigned long long count;
|
||||
unsigned long long curr_cycle;
|
||||
unsigned long long max_cycle;
|
||||
unsigned long long min_cycle;
|
||||
unsigned long long total_cycle;
|
||||
};
|
||||
|
||||
extern struct osfp_profile_counter osfp_profile_fingerprinting;
|
||||
extern struct osfp_profile_counter osfp_profile_prefilter;
|
||||
extern struct osfp_profile_counter osfp_profile_score;
|
||||
extern struct osfp_profile_counter osfp_profile_result_build;
|
||||
extern struct osfp_profile_counter osfp_profile_result_export;
|
||||
|
||||
void osfp_profile_counter_update(struct osfp_profile_counter *profile, unsigned long long curr_cycle);
|
||||
void osfp_profile_print_stats(void);
|
||||
void osfp_profile_set(unsigned int enabled);
|
||||
|
||||
|
||||
#define OSFP_BIT_U32(n) (1UL << (n))
|
||||
|
||||
#define OSFP_PERCENTILE 100
|
||||
|
||||
#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
|
||||
#define OSFP_TCP_OPTLENMAX 64
|
||||
#define OSFP_TCP_OPTMAX 20
|
||||
//# 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 */
|
||||
|
||||
|
||||
/**
|
||||
* @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,
|
||||
};
|
||||
|
||||
|
||||
#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_LOWEST_SCORE_LIMIT 20
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
struct osfp_os_class_score {
|
||||
unsigned int scores[OSFP_OS_CLASS_MAX];
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 结构体用于 osfp_result 中的详细结果。
|
||||
*/
|
||||
struct osfp_result_detail {
|
||||
unsigned int score; // 得分
|
||||
unsigned int possibility; // 可能性
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 结构体用于表示操作系统识别结果。
|
||||
*/
|
||||
struct osfp_result {
|
||||
enum osfp_os_class_id likely_os_class; // 最可能的操作系统类别
|
||||
struct osfp_result_detail details[OSFP_OS_CLASS_MAX]; // 详细结果数组
|
||||
const char *matched; // 精确匹配到的指纹特征
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 结构体用于表示操作系统指纹库。
|
||||
*/
|
||||
struct osfp_db {
|
||||
char *db_json_path; // 操作系统指纹库 JSON 文件路径
|
||||
void *score_db; // 分数数据库指针
|
||||
};
|
||||
|
||||
enum osfp_os_class_id osfp_os_class_name_to_id(char *name);
|
||||
const char *osfp_os_class_id_to_name(enum osfp_os_class_id os_class);
|
||||
struct osfp_result *osfp_result_build(struct osfp_os_class_score *os_class_score, const char *matched);
|
||||
char *osfp_read_file(char *fp_file);
|
||||
|
||||
#endif
|
||||
721
src/osfp_fingerprint.c
Normal file
721
src/osfp_fingerprint.c
Normal file
@@ -0,0 +1,721 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "cJSON.h"
|
||||
|
||||
#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"
|
||||
|
||||
#define OSFP_FP_SET_FIELD(fp, field_id, v, l) do { \
|
||||
(fp)->fields[(field_id)].name = osfp_fingerprint_get_field_name(field_id); \
|
||||
(fp)->fields[(field_id)].enabled = 1; \
|
||||
if ((fp)->value_buffer_used + (l) <= sizeof((fp)->value_buffer)) { \
|
||||
memcpy(fp->value_buffer + (fp)->value_buffer_used, (v), (l)); \
|
||||
(fp)->fields[(field_id)].value = (fp)->value_buffer + (fp)->value_buffer_used; \
|
||||
(fp)->fields[(field_id)].value_len = (l); \
|
||||
(fp)->value_buffer_used += (l); \
|
||||
} else { \
|
||||
(fp)->fields[(field_id)].value = NULL; \
|
||||
(fp)->fields[(field_id)].value_len = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define OSFP_FP_INIT_FIELD(fp, field_id) do { \
|
||||
(fp)->fields[(field_id)].name = osfp_fingerprint_get_field_name((field_id)); \
|
||||
(fp)->fields[(field_id)].enabled = 0; \
|
||||
(fp)->fields[(field_id)].value = NULL; \
|
||||
(fp)->fields[(field_id)].value_len = 0; \
|
||||
} while (0)
|
||||
|
||||
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, 1, 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;
|
||||
}
|
||||
|
||||
static char *osfp_fingerprint_tcp_options_to_ordered(char *tcp_options, unsigned int len)
|
||||
{
|
||||
int i;
|
||||
char *tcp_options_ordered;
|
||||
unsigned tcp_options_ordered_offset;
|
||||
unsigned tcp_options_offset;
|
||||
|
||||
if (tcp_options == NULL && len == 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
tcp_options_ordered = malloc(len + 1);
|
||||
if (tcp_options_ordered == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
tcp_options_offset = 0;
|
||||
tcp_options_ordered_offset = 0;
|
||||
while(tcp_options_offset < len) {
|
||||
if (isalpha(tcp_options[tcp_options_offset])) {
|
||||
tcp_options_ordered[tcp_options_ordered_offset] = tcp_options[tcp_options_offset];
|
||||
tcp_options_ordered_offset++;
|
||||
}
|
||||
tcp_options_offset++;
|
||||
}
|
||||
|
||||
tcp_options_ordered[tcp_options_ordered_offset] = 0;
|
||||
|
||||
return tcp_options_ordered;
|
||||
exit:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
struct osfp_fingerprint *osfp_fingerprint_from_cjson(void *cjson_obj)
|
||||
{
|
||||
int i;
|
||||
cJSON *root;
|
||||
cJSON *field;
|
||||
void *value_ptr;
|
||||
unsigned int value_len;
|
||||
struct osfp_fingerprint *fp;
|
||||
|
||||
root = (cJSON *)cjson_obj;
|
||||
|
||||
if (root == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fp = calloc(1, sizeof(struct osfp_fingerprint));
|
||||
if (fp == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < OSFP_FIELD_OS; i++) {
|
||||
if (0 == osfp_fingerprint_get_field_enabled(i)) {
|
||||
OSFP_FP_INIT_FIELD(fp, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(i));
|
||||
if (field == NULL) {
|
||||
OSFP_FP_INIT_FIELD(fp, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (field->type) {
|
||||
case cJSON_Number:
|
||||
value_ptr = (void *)&field->valueint;
|
||||
value_len = sizeof(field->valueint);
|
||||
OSFP_FP_SET_FIELD(fp, i, value_ptr, value_len);
|
||||
if (i == OSFP_FIELD_IP_ID ||
|
||||
i == OSFP_FIELD_TCP_TIMESTAMP ||
|
||||
i == OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY) {
|
||||
*(unsigned int*)(fp->fields[i].value) = !!field->valueint;
|
||||
}
|
||||
if (i == OSFP_FIELD_IP_TTL) {
|
||||
*(unsigned int*)(fp->fields[i].value) = compute_ip_ttl((unsigned int)field->valueint);
|
||||
}
|
||||
break;
|
||||
case cJSON_String:
|
||||
value_ptr = (void *)field->valuestring;
|
||||
value_len = strlen(field->valuestring) + 1;
|
||||
OSFP_FP_SET_FIELD(fp, i, value_ptr, value_len);
|
||||
break;
|
||||
case cJSON_NULL:
|
||||
//osfp_log_debug("fingerprint parse error: %s\n%s\n", field->string, cJSON_Print(root));
|
||||
break;
|
||||
default:
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_OS);
|
||||
|
||||
return fp;
|
||||
exit:
|
||||
if (fp) {
|
||||
free(fp);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(OSFP_FIELD_TCP_OPTIONS_ORDERED));
|
||||
if (field == NULL) {
|
||||
field = cJSON_GetObjectItem(root, osfp_fingerprint_get_field_name(OSFP_FIELD_TCP_OPTIONS));
|
||||
if (field) {
|
||||
char *tcp_options_ordered_str = osfp_fingerprint_tcp_options_to_ordered(field->valuestring, strlen(field->valuestring));
|
||||
if (tcp_options_ordered_str) {
|
||||
cJSON_AddItemToObject(root, osfp_fingerprint_get_field_name(OSFP_FIELD_TCP_OPTIONS_ORDERED),
|
||||
cJSON_CreateString(tcp_options_ordered_str));
|
||||
free(tcp_options_ordered_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < OSFP_FIELD_OS; i++) {
|
||||
if (0 == osfp_fingerprint_get_field_enabled(i)) {
|
||||
OSFP_FP_INIT_FIELD(fp, i);
|
||||
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_FP_SET_FIELD(fp, i, value_ptr, value_len);
|
||||
if (i == OSFP_FIELD_IP_ID ||
|
||||
i == OSFP_FIELD_TCP_TIMESTAMP ||
|
||||
i == OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY) {
|
||||
*(unsigned int*)(fp->fields[i].value) = !!field->valueint;
|
||||
}
|
||||
if (i == OSFP_FIELD_IP_TTL) {
|
||||
*(unsigned int*)(fp->fields[i].value) = compute_ip_ttl((unsigned int)field->valueint);
|
||||
}
|
||||
break;
|
||||
case cJSON_String:
|
||||
value_ptr = (void *)field->valuestring;
|
||||
value_len = strlen(field->valuestring) + 1;
|
||||
OSFP_FP_SET_FIELD(fp, i, value_ptr, value_len);
|
||||
break;
|
||||
case cJSON_NULL:
|
||||
//osfp_log_debug("fingerprint parse error: %s\n%s\n", field->string, cJSON_Print(root));
|
||||
break;
|
||||
default:
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
exit:
|
||||
if (root) {
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static 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_ts;
|
||||
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_FP_SET_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_FP_SET_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_ts = !!ntohl(*(unsigned int *)(opt->data));
|
||||
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_TIMESTAMP, &tcp_ts, sizeof(tcp_ts));
|
||||
tcp_ter = !!ntohl(*(unsigned int *)(opt->data + 4));
|
||||
OSFP_FP_SET_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_FP_SET_FIELD(fp, OSFP_FIELD_TCP_OPTIONS, options, strlen(options) + 1);
|
||||
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_OPTIONS_ORDERED, options_ordered, strlen(options_ordered) + 1);
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static 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->ack_seq + 5);
|
||||
|
||||
if (tcp_off != tcph_len) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_OFF, &tcp_off, sizeof(tcp_off));
|
||||
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_WINDOW_SIZE, &tcp_window_size, sizeof(tcp_window_size));
|
||||
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_TCP_FLAGS, &tcp_flags, sizeof(tcp_flags));
|
||||
|
||||
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_TIMESTAMP);
|
||||
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_TIMESTAMP_ECHO_REPLY);
|
||||
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_WINDOW_SCALING);
|
||||
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_MSS);
|
||||
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_OPTIONS);
|
||||
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_TCP_OPTIONS_ORDERED);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
static 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_FP_SET_FIELD(fp, OSFP_FIELD_IP_ID, &ip_id, sizeof(ip_id));
|
||||
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TOS, &ip_tos, sizeof(ip_tos));
|
||||
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length));
|
||||
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl));
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int osfp_fingerprinting_ipv6(struct ip6_hdr *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->ip6_ctlun.ip6_un1.ip6_un1_plen);
|
||||
unsigned int ip_ttl = compute_ip_ttl(iph->ip6_ctlun.ip6_un1.ip6_un1_hlim);
|
||||
|
||||
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TOTAL_LENGTH, &ip_total_length, sizeof(ip_total_length));
|
||||
OSFP_FP_SET_FIELD(fp, OSFP_FIELD_IP_TTL, &ip_ttl, sizeof(ip_ttl));
|
||||
|
||||
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_IP_ID);
|
||||
OSFP_FP_INIT_FIELD(fp, OSFP_FIELD_IP_TOS);
|
||||
|
||||
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 || tcph_len == 0 || fp == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fp->value_buffer_used = 0;
|
||||
|
||||
switch (ip_version) {
|
||||
case 4:
|
||||
ret = osfp_fingerprinting_ipv4((struct iphdr *)iph, fp);
|
||||
break;
|
||||
case 6:
|
||||
ret = osfp_fingerprinting_ipv6((struct ip6_hdr *)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_FP_INIT_FIELD(fp, OSFP_FIELD_OS);
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
return -1;
|
||||
}
|
||||
|
||||
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\":null}";
|
||||
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;
|
||||
}
|
||||
|
||||
ret = -1;
|
||||
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\":null}";
|
||||
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;
|
||||
}
|
||||
|
||||
ret = -1;
|
||||
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;
|
||||
}
|
||||
|
||||
ret = -1;
|
||||
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;
|
||||
}
|
||||
77
src/osfp_fingerprint.h
Normal file
77
src/osfp_fingerprint.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#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;
|
||||
};
|
||||
|
||||
extern struct osfp_fingerprint_field fp_fields[OSFP_FIELD_MAX];
|
||||
|
||||
static inline unsigned int osfp_fingerprint_get_field_enabled(enum osfp_field_id field_id)
|
||||
{
|
||||
return fp_fields[field_id].enabled;
|
||||
}
|
||||
|
||||
static inline unsigned int osfp_fingerprint_get_field_importance(enum osfp_field_id field_id)
|
||||
{
|
||||
return fp_fields[field_id].importance;
|
||||
}
|
||||
|
||||
static inline const char *osfp_fingerprint_get_field_name(enum osfp_field_id field_id)
|
||||
{
|
||||
return fp_fields[field_id].name;
|
||||
}
|
||||
|
||||
static inline unsigned int osfp_fingerprint_get_field_type(enum osfp_field_id field_id)
|
||||
{
|
||||
return fp_fields[field_id].type;
|
||||
}
|
||||
|
||||
struct osfp_fingerprint *osfp_fingerprint_from_cjson(void *root);
|
||||
int osfp_fingerprint_from_json(struct osfp_fingerprint *fp, char *json_str);
|
||||
int osfp_fingerprint_to_json_buf(struct osfp_fingerprint *fp, char *strbuf, unsigned int buf_len, unsigned int format);
|
||||
|
||||
int osfp_fingerprinting(unsigned char *iph, unsigned char *tcph, unsigned int tcph_len, struct osfp_fingerprint *fp, unsigned int ip_version);
|
||||
|
||||
int test_osfp_fingerprinting_ipv4(void);
|
||||
int test_osfp_fingerprinting_ipv6(void);
|
||||
int test_osfp_fingerprinting_tcp_option(void);
|
||||
#endif
|
||||
62
src/osfp_log.c
Normal file
62
src/osfp_log.c
Normal file
@@ -0,0 +1,62 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#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
24
src/osfp_log.h
Normal 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
|
||||
668
src/osfp_score_db.c
Normal file
668
src/osfp_score_db.c
Normal file
@@ -0,0 +1,668 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "cJSON.h"
|
||||
|
||||
#include "osfp_common.h"
|
||||
|
||||
#include "osfp.h"
|
||||
#include "osfp_fingerprint.h"
|
||||
#include "osfp_score_db.h"
|
||||
#include "osfp_log.h"
|
||||
|
||||
/*
|
||||
* 一个字段的值,最多能命中单个操作系统的,指纹总数的百分比
|
||||
* 例如:linux 总指纹数量 100,其中 ip_id 字段值为 1 的指纹数为80,那么 ip_id
|
||||
* 字段的得分最大为 FIELD_VALUE_DUP_RATE_MAX * 100 = 50
|
||||
*/
|
||||
#define FIELD_VALUE_DUP_RATE_MAX 0.5f
|
||||
|
||||
/*
|
||||
* 由于 FIELD_VALUE_DUP_RATE_MAX 设置了单个字段得分的最大百分比,所以在归一化时,应除以这个百分比
|
||||
*/
|
||||
#define PERFECT_SCORE_EXPECTED_RATE (FIELD_VALUE_DUP_RATE_MAX)
|
||||
|
||||
#define OSFP_SCORE_DB_FIELD_UINT_VALUE_MAX 65536
|
||||
|
||||
struct osfp_score_db_array_data {
|
||||
struct osfp_field_value_count *array_head;
|
||||
unsigned int array_len;
|
||||
};
|
||||
|
||||
struct osfp_score_db_hash_element {
|
||||
char *key;
|
||||
unsigned int keylen;
|
||||
struct osfp_field_value_count *fvc;
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
struct osfp_score_db_hash_data {
|
||||
struct osfp_score_db_hash_element *hash_head;
|
||||
};
|
||||
|
||||
static int osfp_score_db_array_add(void *data, struct osfp_field_value_count *fvc, 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 || fvc == 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].counts[i] += fvc->counts[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct osfp_field_value_count *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]);
|
||||
}
|
||||
|
||||
static 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_field_value_count));
|
||||
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;
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
}
|
||||
|
||||
static int osfp_score_db_hash_add(void *data, struct osfp_field_value_count *fvc, 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 || fvc == 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->fvc = (struct osfp_field_value_count *)calloc(1, sizeof(struct osfp_field_value_count));
|
||||
if (element->fvc == 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->fvc->counts[i] += fvc->counts[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct osfp_field_value_count *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->fvc;
|
||||
}
|
||||
|
||||
static void *osfp_score_db_hash_create(void)
|
||||
{
|
||||
return (void*)calloc(1, sizeof(struct osfp_score_db_hash_data));
|
||||
}
|
||||
|
||||
static 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->fvc) {
|
||||
free(element->fvc);
|
||||
}
|
||||
free(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(hash_data);
|
||||
}
|
||||
}
|
||||
|
||||
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("total_weight: %u\n", score_db->total_weight);
|
||||
|
||||
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_entry_count[i]);
|
||||
printf("os class %s entry_count: %u\n", osfp_os_class_id_to_name(i), score_db->os_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);
|
||||
}
|
||||
}
|
||||
|
||||
const char *osfp_score_db_prefilter(struct osfp_score_db *score_db, struct osfp_fingerprint *fp, struct osfp_os_class_score *result_score)
|
||||
{
|
||||
int ret, i;
|
||||
unsigned int value_buffer_used = 0;
|
||||
char value_buffer[OSFP_FINGERPRINT_VALUE_BUFFER_MAX];
|
||||
struct osfp_prefilter_hash_element *element = NULL;
|
||||
|
||||
if (score_db->prefilter_head == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < OSFP_FIELD_OS; i++) {
|
||||
if (0 == osfp_fingerprint_get_field_enabled(i)) {
|
||||
continue;
|
||||
}
|
||||
if (fp->fields[i].value && fp->fields[i].value_len != 0) {
|
||||
memcpy(value_buffer + value_buffer_used, fp->fields[i].value, fp->fields[i].value_len);
|
||||
value_buffer_used += fp->fields[i].value_len;
|
||||
}
|
||||
}
|
||||
|
||||
HASH_FIND(hh, score_db->prefilter_head, value_buffer, value_buffer_used, element);
|
||||
if (element == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (element->repeated) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(result_score, 0, sizeof(struct osfp_os_class_score));
|
||||
result_score->scores[element->os_class] = OSFP_PERCENTILE;
|
||||
|
||||
return (const char *)element->fp_json;
|
||||
}
|
||||
|
||||
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 = -1, i, j;
|
||||
|
||||
unsigned int coefficient;
|
||||
unsigned int matched_count;
|
||||
unsigned int os_entry_count;
|
||||
unsigned int total_weight;
|
||||
unsigned int field_weight;
|
||||
|
||||
struct osfp_fingerprint_field *field;
|
||||
struct osfp_field_value_count *fvc; // field_value_count
|
||||
struct osfp_field_score_db *fdb;
|
||||
|
||||
if (score_db == NULL || fp == NULL || result_score == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// score
|
||||
memset(result_score, 0, sizeof(struct osfp_os_class_score));
|
||||
|
||||
total_weight = score_db->total_weight;
|
||||
if (total_weight == 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < OSFP_FIELD_MAX; i++) {
|
||||
fdb = &score_db->field_score_dbs[i];
|
||||
if (!fdb->enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
field = &fp->fields[i];
|
||||
if (!field->enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fvc = fdb->match(fdb->data, field->value, field->value_len);
|
||||
if (fvc == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
coefficient = fdb->coefficient;
|
||||
|
||||
for (j = 0; j < OSFP_OS_CLASS_MAX; j++) {
|
||||
os_entry_count = score_db->os_entry_count[j];
|
||||
matched_count = MIN(fvc->counts[j], os_entry_count * FIELD_VALUE_DUP_RATE_MAX);
|
||||
|
||||
if (os_entry_count == 0 || matched_count == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (0 == flags || flags & OSFP_BIT_U32(j)) {
|
||||
result_score->scores[j] += coefficient * matched_count / os_entry_count;
|
||||
}
|
||||
}
|
||||
|
||||
// if tcp options matched tcp options ordered is not needed
|
||||
if (i == OSFP_FIELD_TCP_OPTIONS) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static 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_field_value_count field_value_count = {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;
|
||||
}
|
||||
|
||||
field_value_count.counts[os_class] = 1;
|
||||
|
||||
ret = db->add(db->data, &field_value_count, value_ptr, value_len);
|
||||
if (ret != 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
db->entry_count++;
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
// get os class
|
||||
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;
|
||||
}
|
||||
|
||||
// prefileter
|
||||
struct osfp_prefilter_hash_element *element = NULL;
|
||||
struct osfp_fingerprint *fp = osfp_fingerprint_from_cjson(entry);
|
||||
if (fp == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
HASH_FIND(hh, score_db->prefilter_head, fp->value_buffer, fp->value_buffer_used, element);
|
||||
if (element == NULL) {
|
||||
element = (struct osfp_prefilter_hash_element *)calloc(1, sizeof(struct osfp_prefilter_hash_element));
|
||||
if (element == NULL) {
|
||||
free(fp);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
element->fp_json = cJSON_Print(entry);
|
||||
element->fp = fp;
|
||||
element->os_class = os_class;
|
||||
HASH_ADD_KEYPTR(hh, score_db->prefilter_head, fp->value_buffer, fp->value_buffer_used, element);
|
||||
} else {
|
||||
// TODO: same fingerprints with different os should not insert into prefilter hash table, now just tag
|
||||
element->repeated++;
|
||||
free(fp);
|
||||
}
|
||||
|
||||
// field score db
|
||||
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_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 = -1, i, count;
|
||||
char *file_buffer;
|
||||
cJSON *entry;
|
||||
cJSON *root = NULL;
|
||||
struct osfp_field_score_db *field_score_db;
|
||||
|
||||
if (score_db == NULL || fp_file == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
file_buffer = osfp_read_file(fp_file);
|
||||
if (file_buffer == NULL) {
|
||||
osfp_log_error("read file: '%s'\n", fp_file);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
root = cJSON_Parse(file_buffer);
|
||||
if (root == NULL) {
|
||||
osfp_log_error("parse json: '%s'\n", fp_file);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
count = cJSON_GetArraySize(root);
|
||||
for (i = 0; i < count; i++) {
|
||||
entry = cJSON_GetArrayItem(root, i);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
exit:
|
||||
if (root) {
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
if (file_buffer) {
|
||||
free(file_buffer);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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->weight = osfp_fingerprint_get_field_importance(i);
|
||||
// tcp options ordered and tcp options overlap
|
||||
if (i != OSFP_FIELD_TCP_OPTIONS_ORDERED) {
|
||||
score_db->total_weight += db->weight;
|
||||
}
|
||||
|
||||
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_error("fingerprint field unsupported type: %u", db->type);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
db->data = db->create();
|
||||
if (db->data == NULL) {
|
||||
osfp_log_error("field db create failed. field: %s", osfp_fingerprint_get_field_name(i));
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < OSFP_FIELD_MAX; i++) {
|
||||
db = &score_db->field_score_dbs[i];
|
||||
db->coefficient = (OSFP_PERCENTILE / PERFECT_SCORE_EXPECTED_RATE) * ((float)db->weight / (float)score_db->total_weight);
|
||||
}
|
||||
|
||||
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;
|
||||
struct osfp_prefilter_hash_element *element = NULL;
|
||||
struct osfp_prefilter_hash_element *tmp = NULL;
|
||||
|
||||
if (score_db) {
|
||||
// prefilter
|
||||
if (score_db->prefilter_head) {
|
||||
HASH_ITER(hh, score_db->prefilter_head, element, tmp) {
|
||||
HASH_DELETE(hh, score_db->prefilter_head, element);
|
||||
if (element) {
|
||||
if (element->fp) {
|
||||
free(element->fp);
|
||||
}
|
||||
if (element->fp_json) {
|
||||
free(element->fp_json);
|
||||
}
|
||||
free(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// field 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);
|
||||
}
|
||||
}
|
||||
|
||||
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_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;
|
||||
}
|
||||
63
src/osfp_score_db.h
Normal file
63
src/osfp_score_db.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef __OSFP_SCORE_DB_H__
|
||||
#define __OSFP_SCORE_DB_H__
|
||||
|
||||
#include "uthash.h"
|
||||
|
||||
#include "osfp.h"
|
||||
#include "osfp_fingerprint.h"
|
||||
#include "osfp_common.h"
|
||||
|
||||
struct osfp_field_value_count {
|
||||
unsigned int counts[OSFP_OS_CLASS_MAX];
|
||||
};
|
||||
|
||||
struct osfp_field_score_db {
|
||||
unsigned int enabled;
|
||||
unsigned int type;
|
||||
unsigned int entry_count;
|
||||
|
||||
unsigned int weight;
|
||||
float coefficient;
|
||||
|
||||
void *data;
|
||||
|
||||
void *(*create)(void);
|
||||
void (*destroy)(void *);
|
||||
int (*add)(void *data, struct osfp_field_value_count *, void *, unsigned int);
|
||||
struct osfp_field_value_count *(*match)(void *, void *, unsigned int);
|
||||
};
|
||||
|
||||
struct osfp_prefilter_hash_element {
|
||||
unsigned int os_class;
|
||||
unsigned int repeated;
|
||||
|
||||
struct osfp_fingerprint *fp;
|
||||
char *fp_json;
|
||||
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
struct osfp_score_db {
|
||||
struct osfp_prefilter_hash_element *prefilter_head;
|
||||
|
||||
unsigned int entry_count;
|
||||
unsigned int os_entry_count[OSFP_OS_CLASS_MAX];
|
||||
|
||||
unsigned int total_weight;
|
||||
|
||||
struct osfp_field_score_db field_score_dbs[OSFP_FIELD_MAX];
|
||||
};
|
||||
|
||||
void osfp_score_db_debug_print(struct osfp_score_db *score_db);
|
||||
|
||||
const char *osfp_score_db_prefilter(struct osfp_score_db *score_db, struct osfp_fingerprint *fp, struct osfp_os_class_score *result_score);
|
||||
|
||||
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);
|
||||
|
||||
int test_osfp_score_db(void);
|
||||
|
||||
#endif
|
||||
12
src/version.map
Normal file
12
src/version.map
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
global:
|
||||
osfp_db_new;
|
||||
osfp_db_free;
|
||||
osfp_ip_identify;
|
||||
osfp_ipv4_identify;
|
||||
osfp_ipv6_identify;
|
||||
osfp_result_os_name_get;
|
||||
osfp_result_score_detail_export;
|
||||
osfp_result_free;
|
||||
local: *;
|
||||
};
|
||||
11
test/README.md
Normal file
11
test/README.md
Normal 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
|
||||
```
|
||||
357
test/test.c
Normal file
357
test/test.c
Normal file
@@ -0,0 +1,357 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <assert.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"
|
||||
|
||||
#define OSFP_OS_CLASS_MERGED_MAX (OSFP_OS_CLASS_MAX - 2)
|
||||
#define EntryWidth 8
|
||||
|
||||
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;
|
||||
|
||||
//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,
|
||||
//};
|
||||
// merged classes: unknown 0 windows-like 1 unix-like 2 apple-like 3 others 4
|
||||
|
||||
unsigned int testresult[OSFP_OS_CLASS_MAX][OSFP_OS_CLASS_MAX] = {0};
|
||||
unsigned int testresult_merged[OSFP_OS_CLASS_MERGED_MAX][OSFP_OS_CLASS_MERGED_MAX] = {0};
|
||||
|
||||
static const char *class_to_merged_name(unsigned int class)
|
||||
{
|
||||
switch (class) {
|
||||
case 0:
|
||||
return "Unknown";
|
||||
case 1:
|
||||
return "Windows-Like";
|
||||
case 2:
|
||||
return "Unix-Like";
|
||||
case 3:
|
||||
return "Apple-Like";
|
||||
case 4:
|
||||
return "Others";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static unsigned int class_to_merged_class(unsigned int class)
|
||||
{
|
||||
switch (class) {
|
||||
case OSFP_OS_CLASS_UNKNOWN:
|
||||
return 0;
|
||||
case OSFP_OS_CLASS_WINDOWS:
|
||||
return 1;
|
||||
case OSFP_OS_CLASS_LINUX:
|
||||
return 2;
|
||||
case OSFP_OS_CLASS_MAC_OS:
|
||||
return 3;
|
||||
case OSFP_OS_CLASS_IOS:
|
||||
return 3;
|
||||
case OSFP_OS_CLASS_ANDROID:
|
||||
return 2;
|
||||
case OSFP_OS_CLASS_OTHERS:
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void testresult_class_merge()
|
||||
{
|
||||
int i,j;
|
||||
for (i = 0; i < OSFP_OS_CLASS_MAX; i++) {
|
||||
for (j = 0; j < OSFP_OS_CLASS_MAX; j++) {
|
||||
testresult_merged[class_to_merged_class(i)][class_to_merged_class(j)] += testresult[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_confusion_matrix(unsigned int *result, unsigned int os_class_max, const char *(*get_name)(unsigned int))
|
||||
{
|
||||
int i,j;
|
||||
int matched = 0, missed = 0;
|
||||
|
||||
for (i = 0; i < os_class_max; i++) {
|
||||
printf("%*s(%c)", EntryWidth-3, " ", 'a' + i);
|
||||
}
|
||||
printf(" <-" " classified as" "\n");
|
||||
|
||||
for (i = 0; i < os_class_max; i++) {
|
||||
printf("%*.*s", EntryWidth, EntryWidth-2, "----------");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
|
||||
for (i = 0; i < os_class_max; i++) {
|
||||
for (j = 0; j < os_class_max; j++) {
|
||||
if (i == j) {
|
||||
matched += *(result + os_class_max * i + j);
|
||||
} else {
|
||||
missed += *(result + os_class_max * i + j);
|
||||
}
|
||||
printf(" %*d", EntryWidth-1, *(result + os_class_max * i + j));
|
||||
}
|
||||
printf(" (%c): " "class" " %s\n", 'a' + i, get_name(i));
|
||||
}
|
||||
|
||||
printf("miss rate: %u%%\n", 100 * missed / (matched + missed));
|
||||
}
|
||||
|
||||
struct osfp_result *test_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;
|
||||
const char *matched;
|
||||
|
||||
if (db == NULL || json_str == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = osfp_fingerprint_from_json(&fp, (char *)json_str);
|
||||
if (ret != 0) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
matched = osfp_score_db_prefilter(db->score_db, &fp, &os_class_score);
|
||||
if (matched == NULL) {
|
||||
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, matched);
|
||||
if (result == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
return result;
|
||||
exit:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void test_data_prepare()
|
||||
{
|
||||
char *file_buffer;
|
||||
|
||||
if (test_file_path == NULL) {
|
||||
file_buffer = osfp_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 prefiltered_count = 0;
|
||||
unsigned int prefiltered_wrong_count = 0;
|
||||
unsigned int wrong_count = 0;
|
||||
unsigned int verified_count = 0;
|
||||
unsigned int fingerprint_count = 0;
|
||||
char *file_buffer;
|
||||
|
||||
file_buffer = osfp_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_fingerprint fp = {0};
|
||||
osfp_fingerprint_from_json(&fp, (char*)fp_str);
|
||||
char str_buf[2048] = "";
|
||||
osfp_fingerprint_to_json_buf(&fp, str_buf, 2048, 0);
|
||||
fprintf(log_file_ptr, "%s\n", str_buf);
|
||||
|
||||
struct osfp_result *result = test_osfp_json_identify(osfp_db, fp_str);
|
||||
if (result == NULL) {
|
||||
identify_failed_count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
testresult[result->likely_os_class][os_class]++;
|
||||
|
||||
if (result->details[result->likely_os_class].score == 100) {
|
||||
prefiltered_count++;
|
||||
}
|
||||
|
||||
if (os_class == result->likely_os_class) {
|
||||
verified_count++;
|
||||
osfp_result_free(result);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result->details[result->likely_os_class].score == 100) {
|
||||
prefiltered_wrong_count++;
|
||||
}
|
||||
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);
|
||||
free(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, prefiltered %u (wrong: %u), wrong %u, other %u, unknown %u\n",
|
||||
fingerprint_count, identify_failed_count, verified_count, prefiltered_count, prefiltered_wrong_count, wrong_count, other_count, unknown_count);
|
||||
|
||||
//printf("miss rate: %d%%\n", 100 - (verified_count * 100 / fingerprint_count));
|
||||
|
||||
testresult_class_merge();
|
||||
|
||||
print_confusion_matrix((unsigned int *)testresult, OSFP_OS_CLASS_MAX, osfp_os_class_id_to_name);
|
||||
|
||||
print_confusion_matrix((unsigned int *)testresult_merged, OSFP_OS_CLASS_MERGED_MAX, class_to_merged_name);
|
||||
|
||||
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;
|
||||
}
|
||||
0
src/cJSON.c → third_party/cJSON/cJSON.c
vendored
0
src/cJSON.c → third_party/cJSON/cJSON.c
vendored
0
src/cJSON.h → third_party/cJSON/cJSON.h
vendored
0
src/cJSON.h → third_party/cJSON/cJSON.h
vendored
Reference in New Issue
Block a user