diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..a3d28d4
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,111 @@
+image: "git.mesalab.cn:7443/mesa_platform/build-env:master"
+variables:
+ GIT_STRATEGY: "clone"
+ BUILD_PADDING_PREFIX: /tmp/padding_for_CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX_PREFIX/
+ INSTALL_PREFIX: "/opt/tsg/packetadapter"
+ TESTING_VERSION_BUILD: 0
+
+stages:
+- build
+
+.build_by_travis:
+ 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
+ script:
+ - yum makecache
+ - ./ci/travis.sh
+ tags:
+ - share
+
+branch_build_debug:
+ stage: build
+ extends: .build_by_travis
+ variables:
+ BUILD_TYPE: Debug
+ except:
+ - /^develop-.*$/i
+ - /^release-.*$/i
+ - tags
+
+branch_build_release:
+ stage: build
+ variables:
+ BUILD_TYPE: RelWithDebInfo
+ extends: .build_by_travis
+ except:
+ - /^develop-.*$/i
+ - /^release-.*$/i
+ - tags
+
+develop_build_debug:
+ stage: build
+ extends: .build_by_travis
+ variables:
+ TESTING_VERSION_BUILD: 1
+ UPLOAD_SYMBOL_FILES: 1
+ BUILD_TYPE: Debug
+ ASAN_OPTION: ADDRESS
+ PACKAGE: 1
+ PULP3_REPO_NAME: tsg-testing-x86_64.el7
+ PULP3_DIST_NAME: tsg-testing-x86_64.el7
+ artifacts:
+ name: "packetadapter-develop-$CI_COMMIT_REF_NAME-debug"
+ paths:
+ - build/*.rpm
+ only:
+ - /^develop-.*$/i
+ - /^release-.*$/i
+
+develop_build_release:
+ stage: build
+ extends: .build_by_travis
+ variables:
+ TESTING_VERSION_BUILD: 1
+ UPLOAD_SYMBOL_FILES: 1
+ # ASAN_OPTION: ADDRESS
+ BUILD_TYPE: RelWithDebInfo
+ PACKAGE: 1
+ PULP3_REPO_NAME: tsg-testing-x86_64.el7
+ PULP3_DIST_NAME: tsg-testing-x86_64.el7
+ artifacts:
+ name: "packetadapter-develop-$CI_COMMIT_REF_NAME-release"
+ paths:
+ - build/*.rpm
+ only:
+ - /^develop-.*$/i
+ - /^release-.*$/i
+
+release_build_debug:
+ stage: build
+ variables:
+ UPLOAD_SYMBOL_FILES: 1
+ BUILD_TYPE: Debug
+ PACKAGE: 1
+ PULP3_REPO_NAME: tsg-stable-x86_64.el7
+ PULP3_DIST_NAME: tsg-stable-x86_64.el7
+ extends: .build_by_travis
+ artifacts:
+ name: "packetadapter-install-$CI_COMMIT_REF_NAME-debug"
+ paths:
+ - build/*.rpm
+ only:
+ - tags
+
+release_build_release:
+ stage: build
+ variables:
+ BUILD_TYPE: RelWithDebInfo
+ UPLOAD_SYMBOL_FILES: 1
+ PACKAGE: 1
+ PULP3_REPO_NAME: tsg-stable-x86_64.el7
+ PULP3_DIST_NAME: tsg-stable-x86_64.el7
+ extends: .build_by_travis
+ artifacts:
+ name: "packetadapter-install-$CI_COMMIT_REF_NAME-release"
+ paths:
+ - build/*.rpm
+ only:
+ - tags
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..cadc605
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,61 @@
+cmake_minimum_required(VERSION 3.5)
+project(packetadapter)
+
+set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
+include(Version)
+include(Package)
+
+add_definitions(-D_GNU_SOURCE)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_C_STANDARD 11)
+
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE RelWithDebInfo)
+endif()
+
+if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set (CMAKE_INSTALL_PREFIX "/opt/tsg/packetadapter" CACHE PATH "default install path" FORCE )
+endif()
+
+# Global compile options
+option(ENABLE_PIC "Generate position independent code (necessary for shared libraries)" TRUE)
+option(ENABLE_WARNING_ALL "Enable all optional warnings which are desirable for normal code" TRUE)
+
+if(NOT ASAN_OPTION)
+ option(ENABLE_SANITIZE_ADDRESS "Enable AddressSanitizer" FALSE)
+else()
+ option(ENABLE_SANITIZE_ADDRESS "Enable AddressSanitizer" TRUE)
+endif()
+
+option(ENABLE_SANITIZE_THREAD "Enable ThreadSanitizer" FALSE)
+
+if(ENABLE_PIC)
+ set(CMAKE_POSITION_INDEPENDENT_CODE 1)
+endif()
+
+if(ENABLE_WARNING_ALL)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
+endif()
+
+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")
+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")
+endif()
+
+if(ENABLE_SANITIZE_ADDRESS AND ENABLE_SANITIZE_THREAD)
+ message(WARNING "Both ENABLE_SANITIZE_ADDRESS and ENABLE_SANITIZE_THREAD set, only ENABLE_SANITIZE_ADDRESS effected.")
+endif()
+
+add_custom_target("install-program" COMMAND ${CMAKE_COMMAND} ARGS -DCOMPONENT=Program -P cmake_install.cmake)
+add_custom_target("install-profile" COMMAND ${CMAKE_COMMAND} ARGS -DCOMPONENT=Profile -P cmake_install.cmake)
+
+enable_testing()
+add_subdirectory(common)
+add_subdirectory(platform)
+add_subdirectory(script)
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 0a179c8..0000000
--- a/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-all:
- gcc -Wall -o nfq_filter_gtp *.c -lnfnetlink -lnetfilter_queue
\ No newline at end of file
diff --git a/README.md b/README.md
index 0fdb4cf..124065c 100644
--- a/README.md
+++ b/README.md
@@ -1,70 +1,73 @@
-## 功能
+# PacketAdapter -- Packet Filtering & Adaptation tools
-并联环境下实现GTP路由封堵功能。
+## 简介
-**实现**
+PacketAdapter 是一个基于 iptables 的数据包过滤,转换/适配工具。
+PacketAdapter 并不会凭空产生数据包,而是将 iptables 过滤的数据包重新转换/适配后再回注到网络中。
+可用于 Overlay networks 中 Packet encapsulation and decapsulation,屏蔽端到端协议层之间的差异。
-将 “=> MAC => IPv4/IPv6 => UDP => GTP1 => IPv4/IPv6 => TCP/UDP“ 中的 "IPv4/IPv6 => UDP => GTP1" 协议层剥离。
+## 应用 -- 实现 GTP Overlay 数据包的解封装
-**例子**
+PacketAdapter 通过 iptables 将 Firewall 发送的 GTP RST 包进行过滤,然后将 GTP 数据解封装后回注到网络中。
+例如:将 “MAC/IPv4 or IPv6/UDP/GTP1/IPv4 or IPv6/TCP or UDP“ 中的 "/IPv4 or IPv6/UDP/GTP1" 协议层剥离。
-原数据包为:
-
-``` shel
-Frame 1: 90 bytes on wire (720 bits), 90 bytes captured (720 bits)
-Ethernet II, Src: JuniperN_4d:d3:51 (08:81:f4:4d:d3:51), Dst: c8:67:d9:18:80:c3 (c8:67:d9:18:80:c3)
-Internet Protocol Version 4, Src: 10.166.20.10, Dst: 10.2.3.35
-User Datagram Protocol, Src Port: 2152, Dst Port: 2152
-GPRS Tunneling Protocol
-Internet Protocol Version 4, Src: 10.58.121.62, Dst: 217.76.78.112
-Transmission Control Protocol, Src Port: 52144, Dst Port: 443, Seq: 1, Ack: 1, Len: 0
+```
++-----------+ +-----------+
+| TCP/UDP | | TCP/UDP |
++-----------+ +-----------+
+| IPv4/IPv6 | | IPv4/IPv6 |
++-----------+ +-----------+
+| GTP1 | | |
++-----------+ | |
+| UDP | ==> | |
++-----------+ | |
+| IPv4/IPv6 | | |
++-----------+ | |
+| MAC | | MAC |
++-----------+ +-----------+
```
-经过 NFQ 过滤后变为:
+注意:
+* /MAC/IPv6 的 first next header 必须为 UDP。
+* 目前不支持 GTP 扩展头。
-``` shel
-Frame 1: 90 bytes on wire (720 bits), 90 bytes captured (720 bits)
-Ethernet II, Src: JuniperN_4d:d3:51 (08:81:f4:4d:d3:51), Dst: c8:67:d9:18:80:c3 (c8:67:d9:18:80:c3)
-Internet Protocol Version 4, Src: 10.58.121.62, Dst: 217.76.78.112
-Transmission Control Protocol, Src Port: 52144, Dst Port: 443, Seq: 1, Ack: 1, Len: 0
-```
-
-## 构造测试环境
+## 运行环境
``` shell
+# yum install --downloadonly --downloaddir=./ libnetfilter_queue.x86_64
+# yum install --downloadonly --downloaddir=./ libnetfilter_queue-devel.x86_64
+# 安装 libnetfilter_queue
+yum install -y libnetfilter_queue
+
# 清空 iptables
-iptables -F -t nat
-iptables -F -t filter
-iptables -F -t mangle
-iptables -F -t raw
+iptables -F -t nat
+iptables -F -t filter
+iptables -F -t mangle
+iptables -F -t raw
+
ip6tables -F -t nat
ip6tables -F -t filter
ip6tables -F -t mangle
ip6tables -F -t raw
# 增加 iptables
-/usr/sbin/iptables -A OUTPUT -o eno2 -p udp --dport 2152 -j NFQUEUE --queue-num 1
+/usr/sbin/iptables -A OUTPUT -o eno2 -p udp --dport 2152 -j NFQUEUE --queue-num 1
/usr/sbin/ip6tables -A OUTPUT -o eno2 -p udp --dport 2152 -j NFQUEUE --queue-num 1
# 删除 iptables
-/usr/sbin/iptables -D OUTPUT -o eno2 -p udp --dport 2152 -j NFQUEUE --queue-num 1
+/usr/sbin/iptables -D OUTPUT -o eno2 -p udp --dport 2152 -j NFQUEUE --queue-num 1
/usr/sbin/ip6tables -D OUTPUT -o eno2 -p udp --dport 2152 -j NFQUEUE --queue-num 1
# 调试 iptables
-/usr/sbin/iptables -A OUTPUT -o eno2 -j LOG
-/usr/sbin/ip6tables -A OUTPUT -o eno2 -j LOG
+# /usr/sbin/iptables -A OUTPUT -o eno2 -j LOG
+# /usr/sbin/ip6tables -A OUTPUT -o eno2 -j LOG
-# make
-yum install -y libnetfilter_queue-devel
-make
-./nfq_filter_gtp
+# 启动服务
+systemctl enable nfq_filter_gtp
+systemctl start nfq_filter_gtp
```
-## 局限
-
-/MAC/IPv6 的 first next header 必须为 UDP,否则跳过。
-
## TODO
-// support service
-// support filestat
+* support service
+* support filestat
\ No newline at end of file
diff --git a/autorevision.sh b/autorevision.sh
new file mode 100644
index 0000000..595690b
--- /dev/null
+++ b/autorevision.sh
@@ -0,0 +1,1268 @@
+#!/bin/sh
+
+# Copyright (c) 2012 - 2016 dak180 and contributors. See
+# https://opensource.org/licenses/mit-license.php or the included
+# COPYING.md for licence terms.
+#
+# autorevision - extracts metadata about the head version from your
+# repository.
+
+# Usage message.
+arUsage() {
+ cat > "/dev/stderr" << EOF
+usage: autorevision {-t output-type | -s symbol} [-o cache-file [-f] ] [-V]
+ Options include:
+ -t output-type = specify output type
+ -s symbol = specify symbol output
+ -o cache-file = specify cache file location
+ -f = force the use of cache data
+ -U = check for untracked files in svn
+ -V = emit version and exit
+ -? = help message
+
+The following are valid output types:
+ clojure = clojure file
+ c = C/C++ file
+ h = Header for use with c/c++
+ hpp = Alternate C++ header strings with namespace
+ ini = INI file
+ java = Java file
+ javaprop = Java properties file
+ js = javascript file
+ json = JSON file
+ lua = Lua file
+ m4 = m4 file
+ matlab = matlab file
+ octave = octave file
+ php = PHP file
+ pl = Perl file
+ py = Python file
+ rpm = rpm file
+ scheme = scheme file
+ sh = Bash sytax
+ swift = Swift file
+ tex = (La)TeX file
+ xcode = Header useful for populating info.plist files
+ cmake = CMake file
+
+
+The following are valid symbols:
+ VCS_TYPE
+ VCS_BASENAME
+ VCS_UUID
+ VCS_NUM
+ VCS_DATE
+ VCS_BRANCH
+ VCS_TAG
+ VCS_TICK
+ VCS_EXTRA
+ VCS_FULL_HASH
+ VCS_SHORT_HASH
+ VCS_WC_MODIFIED
+ VCS_ACTION_STAMP
+EOF
+ exit 1
+}
+
+# Config
+ARVERSION="&&ARVERSION&&"
+TARGETFILE="/dev/stdout"
+while getopts ":t:o:s:VfU" OPTION; do
+ case "${OPTION}" in
+ t)
+ AFILETYPE="${OPTARG}"
+ ;;
+ o)
+ CACHEFILE="${OPTARG}"
+ ;;
+ f)
+ CACHEFORCE="1"
+ ;;
+ s)
+ VAROUT="${OPTARG}"
+ ;;
+ U)
+ UNTRACKEDFILES="1"
+ ;;
+ V)
+ echo "autorevision ${ARVERSION}"
+ exit 0
+ ;;
+ ?)
+ # If an unknown flag is used (or -?):
+ arUsage
+ ;;
+ esac
+done
+
+if [ ! -z "${VAROUT}" ] && [ ! -z "${AFILETYPE}" ]; then
+ # If both -s and -t are specified:
+ echo "error: Improper argument combination." 1>&2
+ exit 1
+elif [ -z "${VAROUT}" ] && [ -z "${AFILETYPE}" ]; then
+ # If neither -s or -t are specified:
+ arUsage
+elif [ -z "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then
+ # If -f is specified without -o:
+ arUsage
+elif [ ! -f "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then
+ # If we are forced to use the cache but it does not exist.
+ echo "error: Cache forced but no cache found." 1>&2
+ exit 1
+fi
+
+# Make sure that the path we are given is one we can source
+# (dash, we are looking at you).
+if [ ! -z "${CACHEFILE}" ] && ! echo "${CACHEFILE}" | grep -q '^\.*/'; then
+ CACHEFILE="./${CACHEFILE}"
+fi
+
+GENERATED_HEADER="Generated by autorevision - do not hand-hack!"
+
+# Functions to extract data from different repo types.
+# For git repos
+# shellcheck disable=SC2039,SC2164,SC2155
+gitRepo() {
+ local oldPath="${PWD}"
+
+ cd "$(git rev-parse --show-toplevel)"
+
+ VCS_TYPE="git"
+
+ VCS_BASENAME="$(basename "${PWD}")"
+
+ VCS_UUID="$(git rev-list --max-parents=0 --date-order --reverse HEAD 2>/dev/null | sed -n 1p)"
+ if [ -z "${VCS_UUID}" ]; then
+ VCS_UUID="$(git rev-list --topo-order HEAD | tail -n 1)"
+ fi
+
+ # Is the working copy clean?
+ test -z "$(git status --untracked-files=normal --porcelain)"
+ VCS_WC_MODIFIED="${?}"
+
+ # Enumeration of changesets
+ VCS_NUM="$(git rev-list --count HEAD 2>/dev/null)"
+ if [ -z "${VCS_NUM}" ]; then
+ echo "warning: Counting the number of revisions may be slower due to an outdated git version less than 1.7.2.3. If something breaks, please update it." 1>&2
+ VCS_NUM="$(git rev-list HEAD | wc -l)"
+ fi
+
+ # This may be a git-svn remote. If so, report the Subversion revision.
+ if [ -z "$(git config svn-remote.svn.url 2>/dev/null)" ]; then
+ # The full revision hash
+ VCS_FULL_HASH="$(git rev-parse HEAD)"
+
+ # The short hash
+ VCS_SHORT_HASH="$(echo "${VCS_FULL_HASH}" | cut -b 1-7)"
+ else
+ # The git-svn revision number
+ VCS_FULL_HASH="$(git svn find-rev HEAD)"
+ VCS_SHORT_HASH="${VCS_FULL_HASH}"
+ fi
+
+ # Current branch
+ VCS_BRANCH="$(git rev-parse --symbolic-full-name --verify "$(git name-rev --name-only --no-undefined HEAD 2>/dev/null)" 2>/dev/null | sed -e 's:refs/heads/::' | sed -e 's:refs/::')"
+
+ # Cache the description
+ local DESCRIPTION="$(git describe --long --tags 2>/dev/null)"
+
+ # Current or last tag ancestor (empty if no tags)
+ VCS_TAG="$(echo "${DESCRIPTION}" | sed -e "s:-g${VCS_SHORT_HASH}\$::" -e 's:-[0-9]*$::')"
+
+ # Distance to last tag or an alias of VCS_NUM if there is no tag
+ if [ ! -z "${DESCRIPTION}" ]; then
+ VCS_TICK="$(echo "${DESCRIPTION}" | sed -e "s:${VCS_TAG}-::" -e "s:-g${VCS_SHORT_HASH}::")"
+ else
+ VCS_TICK="${VCS_NUM}"
+ fi
+
+ # Date of the current commit
+ VCS_DATE="$(TZ=UTC git show -s --date=iso-strict-local --pretty=format:%ad | sed -e 's|+00:00|Z|')"
+ if [ -z "${VCS_DATE}" ]; then
+ echo "warning: Action stamps require git version 2.7+." 1>&2
+ VCS_DATE="$(git log -1 --pretty=format:%ci | sed -e 's: :T:' -e 's: ::' -e 's|+00:00|Z|')"
+ local ASdis="1"
+ fi
+
+ # Action Stamp
+ if [ -z "${ASdis}" ]; then
+ VCS_ACTION_STAMP="${VCS_DATE}!$(git show -s --pretty=format:%cE)"
+ else
+ VCS_ACTION_STAMP=""
+ fi
+
+ cd "${oldPath}"
+}
+
+# For hg repos
+# shellcheck disable=SC2039,SC2164
+hgRepo() {
+ local oldPath="${PWD}"
+
+ cd "$(hg root)"
+
+ VCS_TYPE="hg"
+
+ VCS_BASENAME="$(basename "${PWD}")"
+
+ VCS_UUID="$(hg log -r "0" -l 1 --template '{node}\n')"
+
+ # Is the working copy clean?
+ test -z "$(hg status -duram)"
+ VCS_WC_MODIFIED="${?}"
+
+ # Enumeration of changesets
+ VCS_NUM="$(hg id -n | tr -d '+')"
+
+ # The full revision hash
+ VCS_FULL_HASH="$(hg log -r "${VCS_NUM}" -l 1 --template '{node}\n')"
+
+ # The short hash
+ VCS_SHORT_HASH="$(hg id -i | tr -d '+')"
+
+ # Current bookmark (bookmarks are roughly equivalent to git's branches)
+ # or branch if no bookmark
+ VCS_BRANCH="$(hg id -B | cut -d ' ' -f 1)"
+ # Fall back to the branch if there are no bookmarks
+ if [ -z "${VCS_BRANCH}" ]; then
+ VCS_BRANCH="$(hg id -b)"
+ fi
+
+ # Current or last tag ancestor (excluding auto tags, empty if no tags)
+ VCS_TAG="$(hg log -r "${VCS_NUM}" -l 1 --template '{latesttag}\n' 2>/dev/null | sed -e 's:qtip::' -e 's:tip::' -e 's:qbase::' -e 's:qparent::' -e "s:$(hg --config 'extensions.color=' --config 'extensions.mq=' --color never qtop 2>/dev/null)::" | cut -d ' ' -f 1)"
+
+ # Distance to last tag or an alias of VCS_NUM if there is no tag
+ if [ ! -z "${VCS_TAG}" ]; then
+ VCS_TICK="$(hg log -r "${VCS_NUM}" -l 1 --template '{latesttagdistance}\n' 2>/dev/null)"
+ else
+ VCS_TICK="${VCS_NUM}"
+ fi
+
+ # Date of the current commit
+ VCS_DATE="$(hg log -r "${VCS_NUM}" -l 1 --template '{date|isodatesec}\n' 2>/dev/null | sed -e 's: :T:' -e 's: ::' -e 's|+00:00|Z|')"
+
+ # Action Stamp
+ VCS_ACTION_STAMP="$(TZ=UTC hg log -r "${VCS_NUM}" -l 1 --template '{date|localdate|rfc3339date}\n' 2>/dev/null | sed -e 's|+00:00|Z|')!$(hg log -r "${VCS_NUM}" -l 1 --template '{author|email}\n' 2>/dev/null)"
+
+ cd "${oldPath}"
+}
+
+# For bzr repos
+# shellcheck disable=SC2039,SC2164
+bzrRepo() {
+ local oldPath="${PWD}"
+
+ cd "$(bzr root)"
+
+ VCS_TYPE="bzr"
+
+ VCS_BASENAME="$(basename "${PWD}")"
+
+ # Currently unimplemented because more investigation is needed.
+ VCS_UUID=""
+
+ # Is the working copy clean?
+ bzr version-info --custom --template='{clean}\n' | grep -q '1'
+ VCS_WC_MODIFIED="${?}"
+
+ # Enumeration of changesets
+ VCS_NUM="$(bzr revno)"
+
+ # The full revision hash
+ VCS_FULL_HASH="$(bzr version-info --custom --template='{revision_id}\n')"
+
+ # The short hash
+ VCS_SHORT_HASH="${VCS_NUM}"
+
+ # Nick of the current branch
+ VCS_BRANCH="$(bzr nick)"
+
+ # Current or last tag ancestor (excluding auto tags, empty if no tags)
+ VCS_TAG="$(bzr tags --sort=time | sed '/?$/d' | tail -n1 | cut -d ' ' -f1)"
+
+ # Distance to last tag or an alias of VCS_NUM if there is no tag
+ if [ ! -z "${VCS_TAG}" ]; then
+ VCS_TICK="$(bzr log --line -r "tag:${VCS_TAG}.." | tail -n +2 | wc -l | sed -e 's:^ *::')"
+ else
+ VCS_TICK="${VCS_NUM}"
+ fi
+
+ # Date of the current commit
+ VCS_DATE="$(bzr version-info --custom --template='{date}\n' | sed -e 's: :T:' -e 's: ::')"
+
+ # Action Stamp
+ # Currently unimplemented because more investigation is needed.
+ VCS_ACTION_STAMP=""
+
+ cd "${oldPath}"
+}
+
+# For svn repos
+# shellcheck disable=SC2039,SC2164,SC2155
+svnRepo() {
+ local oldPath="${PWD}"
+
+ VCS_TYPE="svn"
+
+ case "${PWD}" in
+ /*trunk*|/*branches*|/*tags*)
+ local fn="${PWD}"
+ while [ "$(basename "${fn}")" != 'trunk' ] && [ "$(basename "${fn}")" != 'branches' ] && [ "$(basename "${fn}")" != 'tags' ] && [ "$(basename "${fn}")" != '/' ]; do
+ local fn="$(dirname "${fn}")"
+ done
+ local fn="$(dirname "${fn}")"
+ if [ "${fn}" = '/' ]; then
+ VCS_BASENAME="$(basename "${PWD}")"
+ else
+ VCS_BASENAME="$(basename "${fn}")"
+ fi
+ ;;
+ *) VCS_BASENAME="$(basename "${PWD}")" ;;
+ esac
+
+ VCS_UUID="$(svn info --xml | sed -n -e 's:::' -e 's:::p')"
+
+ # Cache svnversion output
+ local SVNVERSION="$(svnversion)"
+
+ # Is the working copy clean?
+ echo "${SVNVERSION}" | grep -q "M"
+ case "${?}" in
+ 0)
+ VCS_WC_MODIFIED="1"
+ ;;
+ 1)
+ if [ ! -z "${UNTRACKEDFILES}" ]; then
+ # `svnversion` does not detect untracked files and `svn status` is really slow, so only run it if we really have to.
+ if [ -z "$(svn status)" ]; then
+ VCS_WC_MODIFIED="0"
+ else
+ VCS_WC_MODIFIED="1"
+ fi
+ else
+ VCS_WC_MODIFIED="0"
+ fi
+ ;;
+ esac
+
+ # Enumeration of changesets
+ VCS_NUM="$(echo "${SVNVERSION}" | cut -d : -f 1 | sed -e 's:M::' -e 's:S::' -e 's:P::')"
+
+ # The full revision hash
+ VCS_FULL_HASH="${SVNVERSION}"
+
+ # The short hash
+ VCS_SHORT_HASH="${VCS_NUM}"
+
+ # Current branch
+ case "${PWD}" in
+ /*trunk*|/*branches*|/*tags*)
+ local lastbase=""
+ local fn="${PWD}"
+ while :
+ do
+ base="$(basename "${fn}")"
+ if [ "${base}" = 'trunk' ]; then
+ VCS_BRANCH='trunk'
+ break
+ elif [ "${base}" = 'branches' ] || [ "${base}" = 'tags' ]; then
+ VCS_BRANCH="${lastbase}"
+ break
+ elif [ "${base}" = '/' ]; then
+ VCS_BRANCH=""
+ break
+ fi
+ local lastbase="${base}"
+ local fn="$(dirname "${fn}")"
+ done
+ ;;
+ *) VCS_BRANCH="" ;;
+ esac
+
+ # Current or last tag ancestor (empty if no tags). But "current
+ # tag" can't be extracted reliably because Subversion doesn't
+ # have tags the way other VCSes do.
+ VCS_TAG=""
+ VCS_TICK=""
+
+ # Date of the current commit
+ VCS_DATE="$(svn info --xml | sed -n -e 's:::' -e 's:::p')"
+
+ # Action Stamp
+ VCS_ACTION_STAMP="${VCS_DATE}!$(svn log --xml -l 1 -r "${VCS_SHORT_HASH}" | sed -n -e 's:::' -e 's:::p')"
+
+ cd "${oldPath}"
+}
+
+
+# Functions to output data in different formats.
+# For bash output
+shOutput() {
+ cat > "${TARGETFILE}" << EOF
+# ${GENERATED_HEADER}
+
+VCS_TYPE="${VCS_TYPE}"
+VCS_BASENAME="${VCS_BASENAME}"
+VCS_UUID="${VCS_UUID}"
+VCS_NUM="${VCS_NUM}"
+VCS_DATE="${VCS_DATE}"
+VCS_BRANCH="${VCS_BRANCH}"
+VCS_TAG="${VCS_TAG}"
+VCS_TICK="${VCS_TICK}"
+VCS_EXTRA="${VCS_EXTRA}"
+
+VCS_ACTION_STAMP="${VCS_ACTION_STAMP}"
+VCS_FULL_HASH="${VCS_FULL_HASH}"
+VCS_SHORT_HASH="${VCS_SHORT_HASH}"
+
+VCS_WC_MODIFIED="${VCS_WC_MODIFIED}"
+
+# end
+EOF
+}
+
+# For source C output
+cOutput() {
+ cat > "${TARGETFILE}" << EOF
+/* ${GENERATED_HEADER} */
+
+const char *VCS_TYPE = "${VCS_TYPE}";
+const char *VCS_BASENAME = "${VCS_BASENAME}";
+const char *VCS_UUID = "${VCS_UUID}";
+const int VCS_NUM = ${VCS_NUM};
+const char *VCS_DATE = "${VCS_DATE}";
+const char *VCS_BRANCH = "${VCS_BRANCH}";
+const char *VCS_TAG = "${VCS_TAG}";
+const int VCS_TICK = ${VCS_TICK};
+const char *VCS_EXTRA = "${VCS_EXTRA}";
+
+const char *VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}";
+const char *VCS_FULL_HASH = "${VCS_FULL_HASH}";
+const char *VCS_SHORT_HASH = "${VCS_SHORT_HASH}";
+
+const int VCS_WC_MODIFIED = ${VCS_WC_MODIFIED};
+
+/* end */
+EOF
+}
+
+# For header output
+hOutput() {
+ cat > "${TARGETFILE}" << EOF
+/* ${GENERATED_HEADER} */
+#ifndef AUTOREVISION_H
+#define AUTOREVISION_H
+
+#define VCS_TYPE "${VCS_TYPE}"
+#define VCS_BASENAME "${VCS_BASENAME}"
+#define VCS_UUID "${VCS_UUID}"
+#define VCS_NUM ${VCS_NUM}
+#define VCS_DATE "${VCS_DATE}"
+#define VCS_BRANCH "${VCS_BRANCH}"
+#define VCS_TAG "${VCS_TAG}"
+#define VCS_TICK ${VCS_TICK}
+#define VCS_EXTRA "${VCS_EXTRA}"
+
+#define VCS_ACTION_STAMP "${VCS_ACTION_STAMP}"
+#define VCS_FULL_HASH "${VCS_FULL_HASH}"
+#define VCS_SHORT_HASH "${VCS_SHORT_HASH}"
+
+#define VCS_WC_MODIFIED ${VCS_WC_MODIFIED}
+
+#endif
+
+/* end */
+EOF
+}
+
+# A header output for use with xcode to populate info.plist strings
+xcodeOutput() {
+ cat > "${TARGETFILE}" << EOF
+/* ${GENERATED_HEADER} */
+#ifndef AUTOREVISION_H
+#define AUTOREVISION_H
+
+#define VCS_TYPE ${VCS_TYPE}
+#define VCS_BASENAME ${VCS_BASENAME}
+#define VCS_UUID ${VCS_UUID}
+#define VCS_NUM ${VCS_NUM}
+#define VCS_DATE ${VCS_DATE}
+#define VCS_BRANCH ${VCS_BRANCH}
+#define VCS_TAG ${VCS_TAG}
+#define VCS_TICK ${VCS_TICK}
+#define VCS_EXTRA ${VCS_EXTRA}
+
+#define VCS_ACTION_STAMP ${VCS_ACTION_STAMP}
+#define VCS_FULL_HASH ${VCS_FULL_HASH}
+#define VCS_SHORT_HASH ${VCS_SHORT_HASH}
+
+#define VCS_WC_MODIFIED ${VCS_WC_MODIFIED}
+
+#endif
+
+/* end */
+EOF
+}
+
+# For Swift output
+swiftOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 0) VCS_WC_MODIFIED="false" ;;
+ 1) VCS_WC_MODIFIED="true" ;;
+ esac
+ # For values that may not exist depending on the type of repo we
+ # have read from, set them to `nil` when they are empty.
+ if [ -z "${VCS_UUID}" ]; then
+ VCS_UUID="nil"
+ else
+ VCS_UUID="\"${VCS_UUID}\""
+ fi
+ if [ -z "${VCS_TAG}" ]; then
+ VCS_TAG="nil"
+ else
+ VCS_TAG="\"${VCS_TAG}\""
+ fi
+ : "${VCS_TICK:="nil"}"
+ if [ -z "${VCS_EXTRA}" ]; then
+ VCS_EXTRA="nil"
+ else
+ VCS_EXTRA="\"${VCS_EXTRA}\""
+ fi
+ if [ -z "${VCS_ACTION_STAMP}" ]; then
+ VCS_ACTION_STAMP="nil"
+ else
+ VCS_ACTION_STAMP="\"${VCS_ACTION_STAMP}\""
+ fi
+ cat > "${TARGETFILE}" << EOF
+/* ${GENERATED_HEADER} */
+
+let VCS_TYPE = "${VCS_TYPE}"
+let VCS_BASENAME = "${VCS_BASENAME}"
+let VCS_UUID: String? = ${VCS_UUID}
+let VCS_NUM: Int = ${VCS_NUM}
+let VCS_DATE = "${VCS_DATE}"
+let VCS_BRANCH: String = "${VCS_BRANCH}"
+let VCS_TAG: String? = ${VCS_TAG}
+let VCS_TICK: Int? = ${VCS_TICK}
+let VCS_EXTRA: String? = ${VCS_EXTRA}
+
+let VCS_ACTION_STAMP: String? = ${VCS_ACTION_STAMP}
+let VCS_FULL_HASH: String = "${VCS_FULL_HASH}"
+let VCS_SHORT_HASH: String = "${VCS_SHORT_HASH}"
+
+let VCS_WC_MODIFIED: Bool = ${VCS_WC_MODIFIED}
+
+/* end */
+EOF
+}
+
+# For Python output
+pyOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 0) VCS_WC_MODIFIED="False" ;;
+ 1) VCS_WC_MODIFIED="True" ;;
+ esac
+ cat > "${TARGETFILE}" << EOF
+# ${GENERATED_HEADER}
+
+VCS_TYPE = "${VCS_TYPE}"
+VCS_BASENAME = "${VCS_BASENAME}"
+VCS_UUID = "${VCS_UUID}"
+VCS_NUM = ${VCS_NUM}
+VCS_DATE = "${VCS_DATE}"
+VCS_BRANCH = "${VCS_BRANCH}"
+VCS_TAG = "${VCS_TAG}"
+VCS_TICK = ${VCS_TICK}
+VCS_EXTRA = "${VCS_EXTRA}"
+
+VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"
+VCS_FULL_HASH = "${VCS_FULL_HASH}"
+VCS_SHORT_HASH = "${VCS_SHORT_HASH}"
+
+VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}
+
+# end
+EOF
+}
+
+# For Perl output
+plOutput() {
+ cat << EOF
+# ${GENERATED_HEADER}
+
+\$VCS_TYPE = '${VCS_TYPE}';
+\$VCS_BASENAME = '${VCS_BASENAME}';
+\$VCS_UUID = '${VCS_UUID}';
+\$VCS_NUM = ${VCS_NUM};
+\$VCS_DATE = '${VCS_DATE}';
+\$VCS_BRANCH = '${VCS_BRANCH}';
+\$VCS_TAG = '${VCS_TAG}';
+\$VCS_TICK = ${VCS_TICK};
+\$VCS_EXTRA = '${VCS_EXTRA}';
+
+\$VCS_ACTION_STAMP = '${VCS_ACTION_STAMP}';
+\$VCS_FULL_HASH = '${VCS_FULL_HASH}';
+\$VCS_SHORT_HASH = '${VCS_SHORT_HASH}';
+
+\$VCS_WC_MODIFIED = ${VCS_WC_MODIFIED};
+
+# end
+1;
+EOF
+}
+
+# For lua output
+luaOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 0) VCS_WC_MODIFIED="false" ;;
+ 1) VCS_WC_MODIFIED="true" ;;
+ esac
+ cat > "${TARGETFILE}" << EOF
+-- ${GENERATED_HEADER}
+
+VCS_TYPE = "${VCS_TYPE}"
+VCS_BASENAME = "${VCS_BASENAME}"
+VCS_UUID = "${VCS_UUID}"
+VCS_NUM = ${VCS_NUM}
+VCS_DATE = "${VCS_DATE}"
+VCS_BRANCH = "${VCS_BRANCH}"
+VCS_TAG = "${VCS_TAG}"
+VCS_TICK = ${VCS_TICK}
+VCS_EXTRA = "${VCS_EXTRA}"
+
+VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"
+VCS_FULL_HASH = "${VCS_FULL_HASH}"
+VCS_SHORT_HASH = "${VCS_SHORT_HASH}"
+
+VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}
+
+-- end
+EOF
+}
+
+# For php output
+phpOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 0) VCS_WC_MODIFIED="false" ;;
+ 1) VCS_WC_MODIFIED="true" ;;
+ esac
+ cat > "${TARGETFILE}" << EOF
+ "${VCS_TYPE}",
+ "VCS_BASENAME" => "${VCS_BASENAME}",
+ "VCS_UUID" => "${VCS_UUID}",
+ "VCS_NUM" => ${VCS_NUM},
+ "VCS_DATE" => "${VCS_DATE}",
+ "VCS_BRANCH" => "${VCS_BRANCH}",
+ "VCS_TAG" => "${VCS_TAG}",
+ "VCS_TICK" => ${VCS_TICK},
+ "VCS_EXTRA" => "${VCS_EXTRA}",
+ "VCS_ACTION_STAMP" => "${VCS_ACTION_STAMP}",
+ "VCS_FULL_HASH" => "${VCS_FULL_HASH}",
+ "VCS_SHORT_HASH" => "${VCS_SHORT_HASH}",
+ "VCS_WC_MODIFIED" => ${VCS_WC_MODIFIED}
+);
+
+# end
+?>
+EOF
+}
+
+# For ini output
+iniOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 0) VCS_WC_MODIFIED="false" ;;
+ 1) VCS_WC_MODIFIED="true" ;;
+ esac
+ cat > "${TARGETFILE}" << EOF
+; ${GENERATED_HEADER}
+[VCS]
+VCS_TYPE = "${VCS_TYPE}"
+VCS_BASENAME = "${VCS_BASENAME}"
+VCS_UUID = "${VCS_UUID}"
+VCS_NUM = ${VCS_NUM}
+VCS_DATE = "${VCS_DATE}"
+VCS_BRANCH = "${VCS_BRANCH}"
+VCS_TAG = "${VCS_TAG}"
+VCS_TICK = ${VCS_TICK}
+VCS_EXTRA = "${VCS_EXTRA}"
+VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}"
+VCS_FULL_HASH = "${VCS_FULL_HASH}"
+VCS_SHORT_HASH = "${VCS_SHORT_HASH}"
+VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}
+; end
+EOF
+}
+
+# For javascript output
+jsOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 1) VCS_WC_MODIFIED="true" ;;
+ 0) VCS_WC_MODIFIED="false" ;;
+ esac
+ cat > "${TARGETFILE}" << EOF
+/** ${GENERATED_HEADER} */
+
+var autorevision = {
+ VCS_TYPE: "${VCS_TYPE}",
+ VCS_BASENAME: "${VCS_BASENAME}",
+ VCS_UUID: "${VCS_UUID}",
+ VCS_NUM: ${VCS_NUM},
+ VCS_DATE: "${VCS_DATE}",
+ VCS_BRANCH: "${VCS_BRANCH}",
+ VCS_TAG: "${VCS_TAG}",
+ VCS_TICK: ${VCS_TICK},
+ VCS_EXTRA: "${VCS_EXTRA}",
+
+ VCS_ACTION_STAMP: "${VCS_ACTION_STAMP}",
+ VCS_FULL_HASH: "${VCS_FULL_HASH}",
+ VCS_SHORT_HASH: "${VCS_SHORT_HASH}",
+
+ VCS_WC_MODIFIED: ${VCS_WC_MODIFIED}
+};
+
+/** Node.js compatibility */
+if (typeof module !== 'undefined') {
+ module.exports = autorevision;
+}
+
+/** end */
+EOF
+}
+
+# For JSON output
+jsonOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 1) VCS_WC_MODIFIED="true" ;;
+ 0) VCS_WC_MODIFIED="false" ;;
+ esac
+ cat > "${TARGETFILE}" << EOF
+{
+ "_comment": "${GENERATED_HEADER}",
+ "VCS_TYPE": "${VCS_TYPE}",
+ "VCS_BASENAME": "${VCS_BASENAME}",
+ "VCS_UUID": "${VCS_UUID}",
+ "VCS_NUM": ${VCS_NUM},
+ "VCS_DATE": "${VCS_DATE}",
+ "VCS_BRANCH":"${VCS_BRANCH}",
+ "VCS_TAG": "${VCS_TAG}",
+ "VCS_TICK": ${VCS_TICK},
+ "VCS_EXTRA": "${VCS_EXTRA}",
+
+ "VCS_ACTION_STAMP": "${VCS_ACTION_STAMP}",
+ "VCS_FULL_HASH": "${VCS_FULL_HASH}",
+ "VCS_SHORT_HASH": "${VCS_SHORT_HASH}",
+
+ "VCS_WC_MODIFIED": ${VCS_WC_MODIFIED}
+}
+EOF
+}
+
+# For Java output
+javaOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 1) VCS_WC_MODIFIED="true" ;;
+ 0) VCS_WC_MODIFIED="false" ;;
+ esac
+ cat > "${TARGETFILE}" << EOF
+/* ${GENERATED_HEADER} */
+
+public class autorevision {
+ public static final String VCS_TYPE = "${VCS_TYPE}";
+ public static final String VCS_BASENAME = "${VCS_BASENAME}";
+ public static final String VCS_UUID = "${VCS_UUID}";
+ public static final long VCS_NUM = ${VCS_NUM};
+ public static final String VCS_DATE = "${VCS_DATE}";
+ public static final String VCS_BRANCH = "${VCS_BRANCH}";
+ public static final String VCS_TAG = "${VCS_TAG}";
+ public static final long VCS_TICK = ${VCS_TICK};
+ public static final String VCS_EXTRA = "${VCS_EXTRA}";
+
+ public static final String VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}";
+ public static final String VCS_FULL_HASH = "${VCS_FULL_HASH}";
+ public static final String VCS_SHORT_HASH = "${VCS_SHORT_HASH}";
+
+ public static final boolean VCS_WC_MODIFIED = ${VCS_WC_MODIFIED};
+}
+EOF
+}
+
+# For Java properties output
+javapropOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 1) VCS_WC_MODIFIED="true" ;;
+ 0) VCS_WC_MODIFIED="false" ;;
+ esac
+ cat > "${TARGETFILE}" << EOF
+# ${GENERATED_HEADER}
+
+VCS_TYPE=${VCS_TYPE}
+VCS_BASENAME=${VCS_BASENAME}
+VCS_UUID=${VCS_UUID}
+VCS_NUM=${VCS_NUM}
+VCS_DATE=${VCS_DATE}
+VCS_BRANCH=${VCS_BRANCH}
+VCS_TAG=${VCS_TAG}
+VCS_TICK=${VCS_TICK}
+VCS_EXTRA=${VCS_EXTRA}
+
+VCS_ACTION_STAMP=${VCS_ACTION_STAMP}
+VCS_FULL_HASH=${VCS_FULL_HASH}
+VCS_SHORT_HASH=${VCS_SHORT_HASH}
+
+VCS_WC_MODIFIED=${VCS_WC_MODIFIED}
+EOF
+}
+
+# For m4 output
+m4Output() {
+ cat > "${TARGETFILE}" << EOF
+dnl ${GENERATED_HEADER}
+define(\`VCS_TYPE', \`${VCS_TYPE}')dnl
+define(\`VCS_BASENAME', \`${VCS_BASENAME}')dnl
+define(\`VCS_UUID', \`${VCS_UUID}')dnl
+define(\`VCS_NUM', \`${VCS_NUM}')dnl
+define(\`VCS_DATE', \`${VCS_DATE}')dnl
+define(\`VCS_BRANCH', \`${VCS_BRANCH}')dnl
+define(\`VCS_TAG', \`${VCS_TAG}')dnl
+define(\`VCS_TICK', \`${VCS_TICK}')dnl
+define(\`VCS_EXTRA', \`${VCS_EXTRA}')dnl
+define(\`VCS_ACTIONSTAMP', \`${VCS_ACTION_STAMP}')dnl
+define(\`VCS_FULLHASH', \`${VCS_FULL_HASH}')dnl
+define(\`VCS_SHORTHASH', \`${VCS_SHORT_HASH}')dnl
+define(\`VCS_WC_MODIFIED', \`${VCS_WC_MODIFIED}')dnl
+EOF
+}
+
+# For (La)TeX output
+texOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 0) VCS_WC_MODIFIED="false" ;;
+ 1) VCS_WC_MODIFIED="true" ;;
+ esac
+ cat > "${TARGETFILE}" << EOF
+% ${GENERATED_HEADER}
+\def \vcsType {${VCS_TYPE}}
+\def \vcsBasename {${VCS_BASENAME}}
+\def \vcsUUID {${VCS_UUID}}
+\def \vcsNum {${VCS_NUM}}
+\def \vcsDate {${VCS_DATE}}
+\def \vcsBranch {${VCS_BRANCH}}
+\def \vcsTag {${VCS_TAG}}
+\def \vcsTick {${VCS_TICK}}
+\def \vcsExtra {${VCS_EXTRA}}
+\def \vcsACTIONSTAMP {${VCS_ACTION_STAMP}}
+\def \vcsFullHash {${VCS_FULL_HASH}}
+\def \vcsShortHash {${VCS_SHORT_HASH}}
+\def \vcsWCModified {${VCS_WC_MODIFIED}}
+\endinput
+EOF
+}
+
+# For scheme output
+schemeOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 0) VCS_WC_MODIFIED="#f" ;;
+ 1) VCS_WC_MODIFIED="#t" ;;
+ esac
+ cat > "${TARGETFILE}" << EOF
+;; ${GENERATED_HEADER}
+(define VCS_TYPE "${VCS_TYPE}")
+(define VCS_BASENAME "${VCS_BASENAME}")
+(define VCS_UUID "${VCS_UUID}")
+(define VCS_NUM ${VCS_NUM})
+(define VCS_DATE "${VCS_DATE}")
+(define VCS_BRANCH "${VCS_BRANCH}")
+(define VCS_TAG "${VCS_TAG}")
+(define VCS_TICK ${VCS_TICK})
+(define VCS_EXTRA "${VCS_EXTRA}")
+
+(define VCS_ACTION_STAMP "${VCS_ACTION_STAMP}")
+(define VCS_FULL_HASH "${VCS_FULL_HASH}")
+(define VCS_SHORT_HASH "${VCS_SHORT_HASH}")
+
+(define VCS_WC_MODIFIED ${VCS_WC_MODIFIED})
+;; end
+EOF
+}
+
+# For clojure output
+clojureOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 0) VCS_WC_MODIFIED="false" ;;
+ 1) VCS_WC_MODIFIED="true" ;;
+ esac
+ cat > "${TARGETFILE}" << EOF
+;; ${GENERATED_HEADER}
+(def VCS_TYPE "${VCS_TYPE}")
+(def VCS_BASENAME "${VCS_BASENAME}")
+(def VCS_UUID "${VCS_UUID}")
+(def VCS_NUM ${VCS_NUM})
+(def VCS_DATE "${VCS_DATE}")
+(def VCS_BRANCH "${VCS_BRANCH}")
+(def VCS_TAG "${VCS_TAG}")
+(def VCS_TICK ${VCS_TICK})
+(def VCS_EXTRA "${VCS_EXTRA}")
+
+(def VCS_ACTION_STAMP "${VCS_ACTION_STAMP}")
+(def VCS_FULL_HASH "${VCS_FULL_HASH}")
+(def VCS_SHORT_HASH "${VCS_SHORT_HASH}")
+
+(def VCS_WC_MODIFIED ${VCS_WC_MODIFIED})
+;; end
+EOF
+}
+
+# For rpm spec file output
+rpmOutput() {
+ cat > "${TARGETFILE}" << EOF
+# ${GENERATED_HEADER}
+$([ "${VCS_TYPE}" ] && echo "%define vcs_type ${VCS_TYPE}")
+$([ "${VCS_BASENAME}" ] && echo "%define vcs_basename ${VCS_BASENAME}")
+$([ "${VCS_UUID}" ] && echo "%define vcs_uuid ${VCS_UUID}")
+$([ "${VCS_NUM}" ] && echo "%define vcs_num ${VCS_NUM}")
+$([ "${VCS_DATE}" ] && echo "%define vcs_date ${VCS_DATE}")
+$([ "${VCS_BRANCH}" ] && echo "%define vcs_branch ${VCS_BRANCH}")
+$([ "${VCS_TAG}" ] && echo "%define vcs_tag ${VCS_TAG}")
+$([ "${VCS_TICK}" ] && echo "%define vcs_tick ${VCS_TICK}")
+$([ "${VCS_EXTRA}" ] && echo "%define vcs_extra ${VCS_EXTRA}")
+
+$([ "${VCS_ACTION_STAMP}" ] && echo "%define vcs_action_stamp ${VCS_ACTION_STAMP}")
+$([ "${VCS_FULL_HASH}" ] && echo "%define vcs_full_hash ${VCS_FULL_HASH}")
+$([ "${VCS_SHORT_HASH}" ] && echo "%define vcs_short_hash ${VCS_SHORT_HASH}")
+
+$([ "${VCS_WC_MODIFIED}" ] && echo "%define vcs_wc_modified ${VCS_WC_MODIFIED}")
+# end
+EOF
+}
+
+# shellcheck disable=SC2155,SC2039
+hppOutput() {
+ local NAMESPACE="$(echo "${VCS_BASENAME}" | sed -e 's:_::g' | tr '[:lower:]' '[:upper:]')"
+ cat > "${TARGETFILE}" << EOF
+/* ${GENERATED_HEADER} */
+
+#ifndef ${NAMESPACE}_AUTOREVISION_H
+#define ${NAMESPACE}_AUTOREVISION_H
+
+#include
+
+namespace $(echo "${NAMESPACE}" | tr '[:upper:]' '[:lower:]')
+{
+ const std::string VCS_TYPE = "${VCS_TYPE}";
+ const std::string VCS_BASENAME = "${VCS_BASENAME}";
+ const std::string VCS_UUID = "${VCS_UUID}";
+ const int VCS_NUM = ${VCS_NUM};
+ const std::string VCS_DATE = "${VCS_DATE}";
+ const std::string VCS_BRANCH = "${VCS_BRANCH}";
+ const std::string VCS_TAG = "${VCS_TAG}";
+ const int VCS_TICK = ${VCS_TICK};
+ const std::string VCS_EXTRA = "${VCS_EXTRA}";
+
+ const std::string VCS_ACTION_STAMP = "${VCS_ACTION_STAMP}";
+ const std::string VCS_FULL_HASH = "${VCS_FULL_HASH}";
+ const std::string VCS_SHORT_HASH = "${VCS_SHORT_HASH}";
+
+ const int VCS_WC_MODIFIED = ${VCS_WC_MODIFIED};
+}
+
+#endif
+
+/* end */
+EOF
+}
+
+matlabOutput() {
+ case "${VCS_WC_MODIFIED}" in
+ 0) VCS_WC_MODIFIED="FALSE" ;;
+ 1) VCS_WC_MODIFIED="TRUE" ;;
+ esac
+ cat > "${TARGETFILE}" << EOF
+% ${GENERATED_HEADER}
+
+VCS_TYPE = '${VCS_TYPE}';
+VCS_BASENAME = '${VCS_BASENAME}';
+VCS_UUID = '${VCS_UUID}';
+VCS_NUM = ${VCS_NUM};
+VCS_DATE = '${VCS_DATE}';
+VCS_BRANCH = '${VCS_BRANCH}';
+VCS_TAG = '${VCS_TAG}';
+VCS_TICK = ${VCS_TICK};
+VCS_EXTRA = '${VCS_EXTRA}';
+
+VCS_ACTION_STAMP = '${VCS_ACTION_STAMP}';
+VCS_FULL_HASH = '${VCS_FULL_HASH}';
+VCS_SHORT_HASH = '${VCS_SHORT_HASH}';
+
+VCS_WC_MODIFIED = ${VCS_WC_MODIFIED};
+
+% end
+EOF
+}
+
+octaveOutput() {
+ cat > "${TARGETFILE}" << EOF
+% ${GENERATED_HEADER}
+
+VCS_TYPE = '${VCS_TYPE}';
+VCS_BASENAME = '${VCS_BASENAME}';
+VCS_UUID = '${VCS_UUID}';
+VCS_NUM = ${VCS_NUM};
+VCS_DATE = '${VCS_DATE}';
+VCS_BRANCH = '${VCS_BRANCH}';
+VCS_TAG = '${VCS_TAG}';
+VCS_TICK = ${VCS_TICK};
+VCS_EXTRA = '${VCS_EXTRA}';
+
+VCS_ACTION_STAMP = '${VCS_ACTION_STAMP}';
+VCS_FULL_HASH = '${VCS_FULL_HASH}';
+VCS_SHORT_HASH = '${VCS_SHORT_HASH}';
+
+VCS_WC_MODIFIED = ${VCS_WC_MODIFIED};
+
+% end
+EOF
+}
+
+cmakeOutput() {
+ cat > "${TARGETFILE}" << EOF
+# ${GENERATED_HEADER}
+
+set(VCS_TYPE ${VCS_TYPE})
+set(VCS_BASENAME ${VCS_BASENAME})
+set(VCS_UUID ${VCS_UUID})
+set(VCS_NUM ${VCS_NUM})
+set(VCS_DATE ${VCS_DATE})
+set(VCS_BRANCH ${VCS_BRANCH})
+set(VCS_TAG ${VCS_TAG})
+set(VCS_TICK ${VCS_TICK})
+set(VCS_EXTRA ${VCS_EXTRA})
+
+set(VCS_ACTION_STAMP ${VCS_ACTION_STAMP})
+set(VCS_FULL_HASH ${VCS_FULL_HASH})
+set(VCS_SHORT_HASH ${VCS_SHORT_HASH})
+
+set(VCS_WC_MODIFIED ${VCS_WC_MODIFIED})
+
+# end
+EOF
+}
+
+
+# Helper functions
+# Count path segments
+# shellcheck disable=SC2039
+pathSegment() {
+ local pathz="${1}"
+ local depth="0"
+
+ if [ ! -z "${pathz}" ]; then
+ # Continue until we are at / or there are no path separators left.
+ while [ ! "${pathz}" = "/" ] && [ ! "${pathz}" = "$(echo "${pathz}" | sed -e 's:/::')" ]; do
+ pathz="$(dirname "${pathz}")"
+ depth="$((depth+1))"
+ done
+ fi
+ echo "${depth}"
+}
+
+# Largest of four numbers
+# shellcheck disable=SC2039
+multiCompare() {
+ local larger="${1}"
+ local numA="${2}"
+ local numB="${3}"
+ local numC="${4}"
+
+ [ "${numA}" -gt "${larger}" ] && larger="${numA}"
+ [ "${numB}" -gt "${larger}" ] && larger="${numB}"
+ [ "${numC}" -gt "${larger}" ] && larger="${numC}"
+ echo "${larger}"
+}
+
+# Test for repositories
+# shellcheck disable=SC2155,SC2039
+repoTest() {
+ REPONUM="0"
+ if [ ! -z "$(git rev-parse HEAD 2>/dev/null)" ]; then
+ local gitPath="$(git rev-parse --show-toplevel)"
+ local gitDepth="$(pathSegment "${gitPath}")"
+ REPONUM="$((REPONUM+1))"
+ else
+ local gitDepth="0"
+ fi
+ if [ ! -z "$(hg root 2>/dev/null)" ]; then
+ local hgPath="$(hg root 2>/dev/null)"
+ local hgDepth="$(pathSegment "${hgPath}")"
+ REPONUM="$((REPONUM+1))"
+ else
+ local hgDepth="0"
+ fi
+ if [ ! -z "$(bzr root 2>/dev/null)" ]; then
+ local bzrPath="$(bzr root 2>/dev/null)"
+ local bzrDepth="$(pathSegment "${bzrPath}")"
+ REPONUM="$((REPONUM+1))"
+ else
+ local bzrDepth="0"
+ fi
+ if [ ! -z "$(svn info 2>/dev/null)" ]; then
+ local stringz=""
+ local stringx=""
+ local svnPath="$(svn info --xml | sed -n -e "s:${stringz}::" -e "s:${stringx}::p")"
+ # An old enough svn will not be able give us a path; default
+ # to 1 for that case.
+ if [ -z "${svnPath}" ]; then
+ local svnDepth="1"
+ else
+ local svnDepth="$(pathSegment "${svnPath}")"
+ fi
+ REPONUM="$((REPONUM+1))"
+ else
+ local svnDepth="0"
+ fi
+
+ # Do not do more work then we have to.
+ if [ "${REPONUM}" = "0" ]; then
+ return
+ fi
+
+ # Figure out which repo is the deepest and use it.
+ local wonRepo="$(multiCompare "${gitDepth}" "${hgDepth}" "${bzrDepth}" "${svnDepth}")"
+ if [ "${wonRepo}" = "${gitDepth}" ]; then
+ gitRepo
+ elif [ "${wonRepo}" = "${hgDepth}" ]; then
+ hgRepo
+ elif [ "${wonRepo}" = "${bzrDepth}" ]; then
+ bzrRepo
+ elif [ "${wonRepo}" = "${svnDepth}" ]; then
+ svnRepo
+ fi
+}
+
+
+
+# Detect which repos we are in and gather data.
+# shellcheck source=/dev/null
+if [ -f "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then
+ # When requested only read from the cache to populate our symbols.
+ . "${CACHEFILE}"
+else
+ # If a value is not set through the environment set VCS_EXTRA to nothing.
+ : "${VCS_EXTRA:=""}"
+ repoTest
+
+ if [ -f "${CACHEFILE}" ] && [ "${REPONUM}" = "0" ]; then
+ # We are not in a repo; try to use a previously generated cache to populate our symbols.
+ . "${CACHEFILE}"
+ # Do not overwrite the cache if we know we are not going to write anything new.
+ CACHEFORCE="1"
+ elif [ "${REPONUM}" = "0" ]; then
+ echo "error: No repo or cache detected." 1>&2
+ exit 1
+ fi
+fi
+
+
+# -s output is handled here.
+if [ ! -z "${VAROUT}" ]; then
+ if [ "${VAROUT}" = "VCS_TYPE" ]; then
+ echo "${VCS_TYPE}"
+ elif [ "${VAROUT}" = "VCS_BASENAME" ]; then
+ echo "${VCS_BASENAME}"
+ elif [ "${VAROUT}" = "VCS_NUM" ]; then
+ echo "${VCS_NUM}"
+ elif [ "${VAROUT}" = "VCS_DATE" ]; then
+ echo "${VCS_DATE}"
+ elif [ "${VAROUT}" = "VCS_BRANCH" ]; then
+ echo "${VCS_BRANCH}"
+ elif [ "${VAROUT}" = "VCS_TAG" ]; then
+ echo "${VCS_TAG}"
+ elif [ "${VAROUT}" = "VCS_TICK" ]; then
+ echo "${VCS_TICK}"
+ elif [ "${VAROUT}" = "VCS_FULL_HASH" ]; then
+ echo "${VCS_FULL_HASH}"
+ elif [ "${VAROUT}" = "VCS_SHORT_HASH" ]; then
+ echo "${VCS_SHORT_HASH}"
+ elif [ "${VAROUT}" = "VCS_WC_MODIFIED" ]; then
+ echo "${VCS_WC_MODIFIED}"
+ elif [ "${VAROUT}" = "VCS_ACTION_STAMP" ]; then
+ echo "${VCS_ACTION_STAMP}"
+ else
+ echo "error: Not a valid output symbol." 1>&2
+ exit 1
+ fi
+fi
+
+
+# Detect requested output type and use it.
+if [ ! -z "${AFILETYPE}" ]; then
+ if [ "${AFILETYPE}" = "c" ]; then
+ cOutput
+ elif [ "${AFILETYPE}" = "h" ]; then
+ hOutput
+ elif [ "${AFILETYPE}" = "xcode" ]; then
+ xcodeOutput
+ elif [ "${AFILETYPE}" = "swift" ]; then
+ swiftOutput
+ elif [ "${AFILETYPE}" = "sh" ]; then
+ shOutput
+ elif [ "${AFILETYPE}" = "py" ] || [ "${AFILETYPE}" = "python" ]; then
+ pyOutput
+ elif [ "${AFILETYPE}" = "pl" ] || [ "${AFILETYPE}" = "perl" ]; then
+ plOutput
+ elif [ "${AFILETYPE}" = "lua" ]; then
+ luaOutput
+ elif [ "${AFILETYPE}" = "php" ]; then
+ phpOutput
+ elif [ "${AFILETYPE}" = "ini" ]; then
+ iniOutput
+ elif [ "${AFILETYPE}" = "js" ]; then
+ jsOutput
+ elif [ "${AFILETYPE}" = "json" ]; then
+ jsonOutput
+ elif [ "${AFILETYPE}" = "java" ]; then
+ javaOutput
+ elif [ "${AFILETYPE}" = "javaprop" ]; then
+ javapropOutput
+ elif [ "${AFILETYPE}" = "tex" ]; then
+ texOutput
+ elif [ "${AFILETYPE}" = "m4" ]; then
+ m4Output
+ elif [ "${AFILETYPE}" = "scheme" ]; then
+ schemeOutput
+ elif [ "${AFILETYPE}" = "clojure" ]; then
+ clojureOutput
+ elif [ "${AFILETYPE}" = "rpm" ]; then
+ rpmOutput
+ elif [ "${AFILETYPE}" = "hpp" ]; then
+ hppOutput
+ elif [ "${AFILETYPE}" = "matlab" ]; then
+ matlabOutput
+ elif [ "${AFILETYPE}" = "octave" ]; then
+ octaveOutput
+ elif [ "${AFILETYPE}" = "cmake" ]; then
+ cmakeOutput
+ else
+ echo "error: Not a valid output type." 1>&2
+ exit 1
+ fi
+fi
+
+
+# If requested, make a cache file.
+if [ ! -z "${CACHEFILE}" ] && [ ! "${CACHEFORCE}" = "1" ]; then
+ TARGETFILE="${CACHEFILE}.tmp"
+ shOutput
+
+ # Check to see if there have been any actual changes.
+ if [ ! -f "${CACHEFILE}" ]; then
+ mv -f "${CACHEFILE}.tmp" "${CACHEFILE}"
+ elif cmp -s "${CACHEFILE}.tmp" "${CACHEFILE}"; then
+ rm -f "${CACHEFILE}.tmp"
+ else
+ mv -f "${CACHEFILE}.tmp" "${CACHEFILE}"
+ fi
+fi
diff --git a/ci/get-nprocessors.sh b/ci/get-nprocessors.sh
new file mode 100644
index 0000000..43635e7
--- /dev/null
+++ b/ci/get-nprocessors.sh
@@ -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
diff --git a/ci/perpare_pulp3_netrc.sh b/ci/perpare_pulp3_netrc.sh
new file mode 100644
index 0000000..8414bbb
--- /dev/null
+++ b/ci/perpare_pulp3_netrc.sh
@@ -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
diff --git a/ci/travis.sh b/ci/travis.sh
new file mode 100644
index 0000000..29324f6
--- /dev/null
+++ b/ci/travis.sh
@@ -0,0 +1,65 @@
+#!/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
+yum install -y libasan
+#yum install -y libmnl-devel libnfnetlink-devel
+yum install -y libnetfilter_queue-devel
+
+if [ $ASAN_OPTION ];then
+ source /opt/rh/devtoolset-7/enable
+fi
+
+mkdir build || true
+cd build
+
+cmake3 -DCMAKE_CXX_FLAGS=$CXX_FLAGS \
+ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
+ -DASAN_OPTION=$ASAN_OPTION \
+ -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \
+ -DVERSION_DAILY_BUILD=$TESTING_VERSION_BUILD \
+ ..
+make
+
+if [ -n "${PACKAGE}" ]; then
+ make package
+ 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 packetadapter*debuginfo*.rpm
+ cp /usr/lib/debug/opt/tsg/packetadapter/bin/packetadapter.debug /tmp/packetadapter.debuginfo.${CI_COMMIT_SHORT_SHA}
+ sentry-cli upload-dif -t elf /tmp/packetadapter.debuginfo.${CI_COMMIT_SHORT_SHA}
+fi
diff --git a/cmake/FindNFNETLINK.cmake b/cmake/FindNFNETLINK.cmake
new file mode 100644
index 0000000..e0e7742
--- /dev/null
+++ b/cmake/FindNFNETLINK.cmake
@@ -0,0 +1,39 @@
+# - Find nfnetlinkDaemon
+# Find the nfnetlink daemon library
+#
+# This module defines the following variables:
+# NFNETLINK_FOUND - True if library and include directory are found
+# If set to TRUE, the following are also defined:
+# NFNETLINK_INCLUDE_DIRS - The directory where to find the header file
+# NFNETLINK_LIBRARIES - Where to find the library file
+#
+# For conveniance, these variables are also set. They have the same values
+# than the variables above. The user can thus choose his/her prefered way
+# to write them.
+# NFNETLINK_LIBRARY
+# NFNETLINK_INCLUDE_DIR
+#
+# This file is in the public domain
+
+include(FindPkgConfig)
+pkg_check_modules(NFNETLINK libnfnetlink)
+
+if(NOT NFNETLINK_FOUND)
+ find_path(NFNETLINK_INCLUDE_DIRS NAMES nlibnfnetlink/libnfnetlink.h
+ DOC "The nfnetlink include directory")
+
+ find_library(NFNETLINK_LIBRARIES NAMES libnfnetlink
+ DOC "The nfnetlink library")
+
+ # Use some standard module to handle the QUIETLY and REQUIRED arguments, and
+ # set NFNETLINK_FOUND to TRUE if these two variables are set.
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(NFNETLINK REQUIRED_VARS NFNETLINK_LIBRARIES NFNETLINK_INCLUDE_DIRS)
+
+ if(NFNETLINK_FOUND)
+ set(NFNETLINK_LIBRARY ${NFNETLINK_LIBRARIES})
+ set(NFNETLINK_INCLUDE_DIR ${NFNETLINK_INCLUDE_DIRS})
+ endif()
+endif()
+
+mark_as_advanced(NFNETLINK_INCLUDE_DIRS NFNETLINK_LIBRARIES)
\ No newline at end of file
diff --git a/cmake/Package.cmake b/cmake/Package.cmake
new file mode 100644
index 0000000..154cfcd
--- /dev/null
+++ b/cmake/Package.cmake
@@ -0,0 +1,31 @@
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(CPACK_PACKAGE_NAME "packetadapter-debug")
+else()
+ set(CPACK_PACKAGE_NAME "packetadapter")
+endif()
+
+message(STATUS "Package: ${CPACK_PACKAGE_NAME}")
+
+set(CPACK_PACKAGE_VENDOR "MESASOFT")
+set(CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}")
+set(CPACK_PACKAGE_VERSION_PATCH "${VERSION_PATCH}.${DESCRIBE}")
+set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
+
+# 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_AUTOREQPROV "no")
+set(CPACK_RPM_PACKAGE_RELEASE_DIST on)
+set(CPACK_RPM_DEBUGINFO_PACKAGE on)
+set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PostInstall.in)
+set(CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PostUninstall.in)
+set(CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE ${CMAKE_SOURCE_DIR}/cmake/PreUninstall.in)
+
+# Must uninstall the debug package before install release package
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ set(CPACK_RPM_PACKAGE_CONFLICTS "packetadapter")
+else()
+ set(CPACK_RPM_PACKAGE_CONFLICTS "packetadapter-debug")
+endif()
diff --git a/cmake/PostInstall.in b/cmake/PostInstall.in
new file mode 100644
index 0000000..c261a95
--- /dev/null
+++ b/cmake/PostInstall.in
@@ -0,0 +1,2 @@
+%systemd_post packetadapter.service
+/sbin/ldconfig
\ No newline at end of file
diff --git a/cmake/PostUninstall.in b/cmake/PostUninstall.in
new file mode 100644
index 0000000..f8a26ad
--- /dev/null
+++ b/cmake/PostUninstall.in
@@ -0,0 +1,2 @@
+%systemd_postun_with_restart packetadapter.service
+/sbin/ldconfig
\ No newline at end of file
diff --git a/cmake/PreUninstall.in b/cmake/PreUninstall.in
new file mode 100644
index 0000000..306e90a
--- /dev/null
+++ b/cmake/PreUninstall.in
@@ -0,0 +1 @@
+%systemd_preun packetadapter.service
\ No newline at end of file
diff --git a/cmake/Version.cmake b/cmake/Version.cmake
new file mode 100644
index 0000000..1fb595b
--- /dev/null
+++ b/cmake/Version.cmake
@@ -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_SOURCE_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})
+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}")
+string(REGEX REPLACE "[T\\:\\+\\-]" "" VERSION_DATE "${VCS_DATE}")
+
+if(VERSION_DAILY_BUILD)
+ set(VERSION_PATCH ${VERSION_PATCH}.${VERSION_DATE})
+endif()
+
+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(DESCRIBE "${VCS_SHORT_HASH}")
+set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
+set(GIT_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${DESCRIBE}")
+
+# Replace .- with _
+string(REGEX REPLACE "[\\.\\-]" "_" VAR_VERSION "${GIT_VERSION}")
+
+# print information
+message(STATUS "Welcome to Packet Adapter, Version: ${GIT_VERSION}")
+add_definitions(-DGIT_VERSION=\"${GIT_VERSION}\")
+add_definitions(-DVAR_VERSION=${VAR_VERSION})
\ No newline at end of file
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
new file mode 100644
index 0000000..c9d83f3
--- /dev/null
+++ b/common/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_library(common src/decode_ipv4.c src/decode_ipv6.c src/decode_tcp.c src/decode_udp.c src/decode_gtp.c)
+target_include_directories(common PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
\ No newline at end of file
diff --git a/common/include/decode_gtp.h b/common/include/decode_gtp.h
new file mode 100644
index 0000000..87d9fd9
--- /dev/null
+++ b/common/include/decode_gtp.h
@@ -0,0 +1,36 @@
+#ifndef _DECODE_GTP_H
+#define _DECODE_GTP_H
+
+#ifdef __cpluscplus
+extern "C"
+{
+#endif
+
+#include "public.h"
+
+ /* According to 3GPP TS 29.060. */
+ typedef struct gtp1_header_s
+ {
+ uint8_t flags;
+ uint8_t type;
+ uint16_t length;
+ uint32_t tid;
+ } __attribute__((packed)) gtp1_header_t;
+
+ typedef struct gtp_info_s
+ {
+ gtp1_header_t *hdr;
+ uint8_t *payload;
+
+ uint32_t hdr_len;
+ uint32_t payload_len;
+ } gtp_info_t;
+
+ int decode_gtp(gtp_info_t *packet, const uint8_t *data, uint32_t len);
+ void dump_gtp_info(uint32_t pkt_id, gtp_info_t *packet);
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/common/include/decode_ipv4.h b/common/include/decode_ipv4.h
new file mode 100644
index 0000000..f3e3fa8
--- /dev/null
+++ b/common/include/decode_ipv4.h
@@ -0,0 +1,55 @@
+#ifndef _DECODE_IPV4_H
+#define _DECODE_IPV4_H
+
+#ifdef __cpluscplus
+extern "C"
+{
+#endif
+
+#include "public.h"
+
+#define IPV4_HEADER_LEN 20
+
+ typedef struct ipv4_header_s
+ {
+ uint8_t ip_verhl; // version & header length
+ uint8_t ip_tos;
+ uint16_t ip_len;
+ uint16_t ip_id;
+ uint16_t ip_off;
+ uint8_t ip_ttl;
+ uint8_t ip_proto;
+ uint16_t ip_csum;
+ union
+ {
+ struct
+ {
+ struct in_addr ip_src;
+ struct in_addr ip_dst;
+ } ip4_un1;
+ uint16_t ip_addrs[4];
+ } ip4_hdrun1;
+ } __attribute__((__packed__)) ipv4_header_t;
+
+ typedef struct ipv4_info_s
+ {
+ char src_addr[INET_ADDRSTRLEN];
+ char dst_addr[INET_ADDRSTRLEN];
+
+ ipv4_header_t *hdr;
+ uint8_t *payload;
+ uint8_t next_protocol;
+
+ uint32_t hdr_len;
+ uint32_t opts_len;
+ uint32_t payload_len;
+ } ipv4_info_t;
+
+ int decode_ipv4(ipv4_info_t *packet, const uint8_t *data, uint32_t len);
+ void dump_ipv4_info(uint32_t pkt_id, ipv4_info_t *packet);
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/common/include/decode_ipv6.h b/common/include/decode_ipv6.h
new file mode 100644
index 0000000..9bb0d36
--- /dev/null
+++ b/common/include/decode_ipv6.h
@@ -0,0 +1,58 @@
+#ifndef _DECODE_IPV6_H
+#define _DECODE_IPV6_H
+
+#ifdef __cpluscplus
+extern "C"
+{
+#endif
+
+#include "public.h"
+
+#define IPV6_HEADER_LEN 40
+
+ typedef struct ipv6_header_s
+ {
+ union
+ {
+ struct ip6_un1_
+ {
+ uint32_t ip6_un1_flow; /* 20 bits of flow-ID */
+ uint16_t ip6_un1_plen; /* payload length */
+ uint8_t ip6_un1_nxt; /* next header */
+ uint8_t ip6_un1_hlim; /* hop limit */
+ } ip6_un1;
+ uint8_t ip6_un2_vfc; /* 4 bits version, top 4 bits class */
+ } ip6_hdrun;
+
+ union
+ {
+ struct
+ {
+ uint32_t ip6_src[4];
+ uint32_t ip6_dst[4];
+ } ip6_un2;
+ uint16_t ip6_addrs[16];
+ } ip6_hdrun2;
+ } __attribute__((__packed__)) ipv6_header_t;
+
+ typedef struct ipv6_info_s
+ {
+ char src_addr[INET6_ADDRSTRLEN];
+ char dst_addr[INET6_ADDRSTRLEN];
+
+ ipv6_header_t *hdr;
+ uint8_t *payload;
+ uint8_t next_protocol;
+
+ uint32_t hdr_len;
+ uint32_t payload_len;
+ } ipv6_info_t;
+
+ int decode_ipv6(ipv6_info_t *packet, const uint8_t *data, uint32_t len);
+ void dump_ipv6_info(uint32_t pkt_id, ipv6_info_t *packet);
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/common/include/decode_tcp.h b/common/include/decode_tcp.h
new file mode 100644
index 0000000..27cc14b
--- /dev/null
+++ b/common/include/decode_tcp.h
@@ -0,0 +1,46 @@
+#ifndef _DECODE_TCP_H
+#define _DECODE_TCP_H
+
+#ifdef __cpluscplus
+extern "C"
+{
+#endif
+
+#include "public.h"
+
+#define TCP_HEADER_LEN 20
+
+ typedef struct tcp_header_s
+ {
+ uint16_t th_sport; /**< source port */
+ uint16_t th_dport; /**< destination port */
+ uint32_t th_seq; /**< sequence number */
+ uint32_t th_ack; /**< acknowledgement number */
+ uint8_t th_offx2; /**< offset and reserved */
+ uint8_t th_flags; /**< pkt flags */
+ uint16_t th_win; /**< pkt window */
+ uint16_t th_sum; /**< checksum */
+ uint16_t th_urp; /**< urgent pointer */
+ } __attribute__((__packed__)) tcp_header_t;
+
+ typedef struct tcp_info_s
+ {
+ uint16_t src_port;
+ uint16_t dst_port;
+
+ tcp_header_t *hdr;
+ uint8_t *payload;
+
+ uint32_t opt_len;
+ uint32_t hdr_len;
+ uint32_t payload_len;
+ } tcp_info_t;
+
+ int decode_tcp(tcp_info_t *packet, const uint8_t *data, uint32_t len);
+ void dump_tcp_info(uint32_t pkt_id, tcp_info_t *packet);
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/common/include/decode_udp.h b/common/include/decode_udp.h
new file mode 100644
index 0000000..7f8ba45
--- /dev/null
+++ b/common/include/decode_udp.h
@@ -0,0 +1,40 @@
+#ifndef _DECODE_UDP_H
+#define _DECODE_UDP_H
+
+#ifdef __cpluscplus
+extern "C"
+{
+#endif
+
+#include "public.h"
+
+#define UDP_HEADER_LEN 8
+
+ typedef struct udp_header_s
+ {
+ uint16_t udp_src_port;
+ uint16_t udp_dst_port;
+ uint16_t udp_len;
+ uint16_t udp_sum;
+ } __attribute__((__packed__)) udp_header_t;
+
+ typedef struct udp_info_s
+ {
+ uint16_t src_port;
+ uint16_t dst_port;
+
+ udp_header_t *hdr;
+ uint8_t *payload;
+
+ uint32_t hdr_len;
+ uint32_t payload_len;
+ } udp_info_t;
+
+ int decode_udp(udp_info_t *packet, const uint8_t *data, uint32_t len);
+ void dump_udp_info(uint32_t pkt_id, udp_info_t *packet);
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/log.h b/common/include/public.h
similarity index 72%
rename from log.h
rename to common/include/public.h
index bd033e6..60f3048 100644
--- a/log.h
+++ b/common/include/public.h
@@ -1,5 +1,5 @@
-#ifndef _LOG_H
-#define _LOG_H
+#ifndef _PUBLIC_H
+#define _PUBLIC_H
#ifdef __cpluscplus
extern "C"
@@ -7,8 +7,16 @@ extern "C"
#endif
#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define IP_GET_RAW_VER(raw_pkt) ((((raw_pkt)[0] & 0xf0) >> 4))
#define PRINT_FILE_INFO 0
diff --git a/common/src/decode_gtp.c b/common/src/decode_gtp.c
new file mode 100644
index 0000000..92dc528
--- /dev/null
+++ b/common/src/decode_gtp.c
@@ -0,0 +1,56 @@
+#include "decode_gtp.h"
+
+#define GTP_TPDU 255
+#define GTP1U_PORT 2152
+#define GTP1_F_MASK 0x07
+
+#define GTP1_GET_TYPE(gtp1_hdr) ((gtp1_hdr)->type)
+#define GTP1_GET_FLAGS(gtp1_hdr) ((gtp1_hdr)->flags >> 5)
+#define GTP1_GET_HLEN(gtp1_hdr) (((gtp1_hdr)->flags & GTP1_F_MASK) > 0 ? 12 : 8)
+
+enum gtp_version_e
+{
+ GTP_V0 = 0,
+ GTP_V1,
+};
+
+int decode_gtp(gtp_info_t *packet, const uint8_t *data, uint32_t len)
+{
+ if (len < sizeof(gtp1_header_t))
+ {
+ LOG_ERROR("Parser GTP Header: packet length too small %d", len);
+ return -1;
+ }
+
+ packet->hdr = (gtp1_header_t *)data;
+ if (GTP1_GET_FLAGS(packet->hdr) != GTP_V1)
+ {
+ LOG_ERROR("Parser GTP Header: invalid gtp flags %d", GTP1_GET_FLAGS(packet->hdr));
+ return -1;
+ }
+
+ if (GTP1_GET_TYPE(packet->hdr) != GTP_TPDU)
+ {
+ LOG_ERROR("Parser GTP Header: invalid gtp type %d", GTP1_GET_TYPE(packet->hdr));
+ return -1;
+ }
+
+ /* From 29.060: "This field shall be present if and only if any one or
+ * more of the S, PN and E flags are set.".
+ *
+ * If any of the bit is set, then the remaining ones also have to be set.
+ */
+ packet->hdr_len = GTP1_GET_HLEN(packet->hdr);
+ packet->payload = (uint8_t *)data + packet->hdr_len;
+ packet->payload_len = len - packet->hdr_len;
+
+ return 0;
+}
+
+void dump_gtp_info(uint32_t pkt_id, gtp_info_t *packet)
+{
+ LOG_DEBUG("id: %u, gtp_info: {hdr_len: %u, data_len: %u}",
+ pkt_id,
+ packet->hdr_len,
+ packet->payload_len);
+}
\ No newline at end of file
diff --git a/common/src/decode_ipv4.c b/common/src/decode_ipv4.c
new file mode 100644
index 0000000..8b98975
--- /dev/null
+++ b/common/src/decode_ipv4.c
@@ -0,0 +1,68 @@
+#include "decode_ipv4.h"
+
+#define IPV4_GET_HLEN(ip4_hdr) (((ip4_hdr)->ip_verhl & 0x0f) << 2)
+#define IPV4_GET_IPPROTO(ip4_hdr) ((ip4_hdr)->ip_proto)
+#define IPV4_GET_IPLEN(ip4_hdr) ((uint16_t)ntohs((ip4_hdr)->ip_len))
+#define IPV4_GET_SRC_ADDR(ip4_hdr) ((ip4_hdr)->ip4_hdrun1.ip4_un1.ip_src)
+#define IPV4_GET_DST_ADDR(ip4_hdr) ((ip4_hdr)->ip4_hdrun1.ip4_un1.ip_dst)
+
+int decode_ipv4(ipv4_info_t *packet, const uint8_t *data, uint32_t len)
+{
+ // 检查包长是否大于 IPv4 header
+ if (len < IPV4_HEADER_LEN)
+ {
+ LOG_ERROR("Parser IPv4 Header: packet length too small %d", len);
+ return -1;
+ }
+
+ // 检查 IPv4 header version
+ if (IP_GET_RAW_VER(data) != 4)
+ {
+ LOG_ERROR("Parser IPv4 Header: invalid IP version %d", IP_GET_RAW_VER(data));
+ return -1;
+ }
+
+ packet->hdr = (ipv4_header_t *)data;
+ // 检查 IPv4 header length
+ if (IPV4_GET_HLEN(packet->hdr) < IPV4_HEADER_LEN)
+ {
+ LOG_ERROR("Parser IPv4 Header: invalid IP header length %d", IPV4_GET_HLEN(packet->hdr));
+ return -1;
+ }
+
+ // 检查 IPv4 header total length
+ if (IPV4_GET_IPLEN(packet->hdr) < IPV4_GET_HLEN(packet->hdr))
+ {
+ LOG_ERROR("Parser IPv4 Header: invalid IP header total length %d", IPV4_GET_IPLEN(packet->hdr));
+ return -1;
+ }
+
+ // 检查是否 IP 分片
+ if (len < IPV4_GET_IPLEN(packet->hdr))
+ {
+ LOG_ERROR("Parser IPv4 Header: trunc packet");
+ return -1;
+ }
+
+ inet_ntop(AF_INET, &IPV4_GET_SRC_ADDR(packet->hdr), packet->src_addr, sizeof(packet->src_addr));
+ inet_ntop(AF_INET, &IPV4_GET_DST_ADDR(packet->hdr), packet->dst_addr, sizeof(packet->dst_addr));
+
+ packet->next_protocol = IPV4_GET_IPPROTO(packet->hdr);
+ packet->hdr_len = IPV4_GET_HLEN(packet->hdr);
+ packet->opts_len = packet->hdr_len - IPV4_HEADER_LEN;
+ packet->payload_len = len - packet->hdr_len;
+ packet->payload = (uint8_t *)data + packet->hdr_len;
+
+ return 0;
+}
+
+void dump_ipv4_info(uint32_t pkt_id, ipv4_info_t *packet)
+{
+ LOG_DEBUG("id: %u, ipv4_info: {src_addr: %s, dst_addr: %s, hdr_len: %u, opt_len: %u, data_len: %u}",
+ pkt_id,
+ packet->src_addr,
+ packet->dst_addr,
+ packet->hdr_len,
+ packet->opts_len,
+ packet->payload_len);
+}
\ No newline at end of file
diff --git a/common/src/decode_ipv6.c b/common/src/decode_ipv6.c
new file mode 100644
index 0000000..a583d54
--- /dev/null
+++ b/common/src/decode_ipv6.c
@@ -0,0 +1,55 @@
+#include "decode_ipv6.h"
+
+#define IPV6_GET_PLEN(ip6_hdr) ((uint16_t)ntohs((ip6_hdr)->ip6_hdrun.ip6_un1.ip6_un1_plen))
+#define IPV6_GET_NH(ip6_hdr) ((ip6_hdr)->ip6_hdrun.ip6_un1.ip6_un1_nxt)
+#define IPV6_GET_SRC_ADDR(ip6_hdr) ((ip6_hdr)->ip6_hdrun2.ip6_un2.ip6_src)
+#define IPV6_GET_DST_ADDR(ip6_hdr) ((ip6_hdr)->ip6_hdrun2.ip6_un2.ip6_dst)
+
+int decode_ipv6(ipv6_info_t *packet, const uint8_t *data, uint32_t len)
+{
+ if (len < IPV6_HEADER_LEN)
+ {
+ LOG_ERROR("Parser IPv6 Header: packet length too small %d", len);
+ return -1;
+ }
+
+ // 检查 IPv6 header version
+ if (IP_GET_RAW_VER(data) != 6)
+ {
+ LOG_ERROR("Parser IPv6 Header: invalid IP version %d", IP_GET_RAW_VER(data));
+ return -1;
+ }
+
+ packet->hdr = (ipv6_header_t *)data;
+ if (len < (IPV6_HEADER_LEN + IPV6_GET_PLEN(packet->hdr)))
+ {
+ LOG_ERROR("Parser IPv6 Header: trunc packet");
+ return -1;
+ }
+
+ if (len != (IPV6_HEADER_LEN + IPV6_GET_PLEN(packet->hdr)))
+ {
+ LOG_ERROR("Parser IPv6 Header: invalid payload length %d", IPV6_GET_PLEN(packet->hdr));
+ return -1;
+ }
+
+ inet_ntop(AF_INET6, &IPV6_GET_SRC_ADDR(packet->hdr), packet->src_addr, sizeof(packet->src_addr));
+ inet_ntop(AF_INET6, &IPV6_GET_DST_ADDR(packet->hdr), packet->dst_addr, sizeof(packet->dst_addr));
+
+ packet->next_protocol = IPV6_GET_NH(packet->hdr);
+ packet->hdr_len = IPV6_HEADER_LEN;
+ packet->payload = (uint8_t *)data + packet->hdr_len;
+ packet->payload_len = len - packet->hdr_len;
+
+ return 0;
+}
+
+void dump_ipv6_info(uint32_t pkt_id, ipv6_info_t *packet)
+{
+ LOG_DEBUG("id: %u, ipv6_info: {src_addr: %s, dst_addr: %s, hdr_len: %u, data_len: %u}",
+ pkt_id,
+ packet->src_addr,
+ packet->dst_addr,
+ packet->hdr_len,
+ packet->payload_len);
+}
\ No newline at end of file
diff --git a/common/src/decode_tcp.c b/common/src/decode_tcp.c
new file mode 100644
index 0000000..75a1ab8
--- /dev/null
+++ b/common/src/decode_tcp.c
@@ -0,0 +1,52 @@
+#include "decode_tcp.h"
+
+#define TCP_OPTLENMAX 40
+
+#define TCP_GET_HLEN(tcp_hdr) ((((tcp_hdr)->th_offx2 & 0xf0) >> 4) << 2)
+#define TCP_GET_SRC_PORT(tcp_hdr) ((uint16_t)ntohs((tcp_hdr)->th_sport))
+#define TCP_GET_DST_PORT(tcp_hdr) ((uint16_t)ntohs((tcp_hdr)->th_dport))
+
+int decode_tcp(tcp_info_t *packet, const uint8_t *data, uint32_t len)
+{
+ if (len < TCP_HEADER_LEN)
+ {
+ LOG_ERROR("Parser TCP Header: packet length too small %d", len);
+ return -1;
+ }
+
+ packet->hdr = (tcp_header_t *)data;
+ uint8_t hlen = TCP_GET_HLEN(packet->hdr);
+ if (len < hlen)
+ {
+ LOG_ERROR("Parser TCP Header: TCP packet too small %d", len);
+ return -1;
+ }
+
+ uint8_t tcp_opt_len = hlen - TCP_HEADER_LEN;
+ if (tcp_opt_len > TCP_OPTLENMAX)
+ {
+ LOG_ERROR("Parser TCP Header: invalid opt length %d", tcp_opt_len);
+ return -1;
+ }
+
+ packet->opt_len = tcp_opt_len;
+ packet->src_port = TCP_GET_SRC_PORT(packet->hdr);
+ packet->dst_port = TCP_GET_DST_PORT(packet->hdr);
+
+ packet->hdr_len = hlen;
+ packet->payload = (uint8_t *)data + packet->hdr_len;
+ packet->payload_len = len - packet->hdr_len;
+
+ return 0;
+}
+
+void dump_tcp_info(uint32_t pkt_id, tcp_info_t *packet)
+{
+ LOG_DEBUG("id: %u, tcp_info: {src_port: %u, dst_port: %u, hdr_len: %u, opt_len: %u, data_len:%u}",
+ pkt_id,
+ packet->src_port,
+ packet->dst_port,
+ packet->hdr_len,
+ packet->opt_len,
+ packet->payload_len);
+}
\ No newline at end of file
diff --git a/common/src/decode_udp.c b/common/src/decode_udp.c
new file mode 100644
index 0000000..bebe337
--- /dev/null
+++ b/common/src/decode_udp.c
@@ -0,0 +1,48 @@
+#include "decode_udp.h"
+
+#define UDP_GET_LEN(udp_hdr) ((uint16_t)ntohs((udp_hdr)->udp_len))
+#define UDP_GET_SRC_PORT(udp_hdr) ((uint16_t)ntohs((udp_hdr)->udp_src_port))
+#define UDP_GET_DST_PORT(udp_hdr) ((uint16_t)ntohs((udp_hdr)->udp_dst_port))
+
+int decode_udp(udp_info_t *packet, const uint8_t *data, uint32_t len)
+{
+ if (len < UDP_HEADER_LEN)
+ {
+ LOG_ERROR("Parser UDP Header: packet length too small %d", len);
+ return -1;
+ }
+
+ packet->hdr = (udp_header_t *)data;
+ // 检查 UDP header len
+ if (len < UDP_GET_LEN(packet->hdr))
+ {
+ LOG_ERROR("Parser UDP Header: UDP packet too small %d", len);
+ return -1;
+ }
+
+ // 检查 UDP header len
+ if (len != UDP_GET_LEN(packet->hdr))
+ {
+ LOG_ERROR("Parser UDP Header: invalid UDP header length %d", UDP_GET_LEN(packet->hdr));
+ return -1;
+ }
+
+ packet->src_port = UDP_GET_SRC_PORT(packet->hdr);
+ packet->dst_port = UDP_GET_DST_PORT(packet->hdr);
+
+ packet->hdr_len = UDP_HEADER_LEN;
+ packet->payload = (uint8_t *)data + UDP_HEADER_LEN;
+ packet->payload_len = len - UDP_HEADER_LEN;
+
+ return 0;
+}
+
+void dump_udp_info(uint32_t pkt_id, udp_info_t *packet)
+{
+ LOG_DEBUG("id: %u, udp_info: {src_port: %u, dst_port: %u, hdr_len: %u, data_len: %u}",
+ pkt_id,
+ packet->src_port,
+ packet->dst_port,
+ packet->hdr_len,
+ packet->payload_len);
+}
diff --git a/nfqnl_test.c b/nfqnl_test.c
deleted file mode 100644
index a6b20c8..0000000
--- a/nfqnl_test.c
+++ /dev/null
@@ -1,869 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#include
-#include // for NF_ACCEPT
-#include
-
-#include "log.h"
-
-#define GTP_TPDU 255
-#define GTP1U_PORT 2152
-#define GTP1_F_MASK 0x07
-
-#define UDP_HEADER_LEN 8
-#define TCP_HEADER_LEN 20
-#define TCP_OPTLENMAX 40
-
-#define IPV4_HEADER_LEN 20
-#define IPV6_HEADER_LEN 40
-
-#define IP_GET_RAW_VER(raw_pkt) ((((raw_pkt)[0] & 0xf0) >> 4))
-
-#define IPV6_GET_PLEN(ip6_hdr) ((uint16_t)ntohs((ip6_hdr)->ip6_hdrun.ip6_un1.ip6_un1_plen))
-#define IPV6_GET_NH(ip6_hdr) ((ip6_hdr)->ip6_hdrun.ip6_un1.ip6_un1_nxt)
-#define IPV6_GET_SRC_ADDR(ip6_hdr) ((ip6_hdr)->ip6_hdrun2.ip6_un2.ip6_src)
-#define IPV6_GET_DST_ADDR(ip6_hdr) ((ip6_hdr)->ip6_hdrun2.ip6_un2.ip6_dst)
-
-#define IPV4_GET_HLEN(ip4_hdr) (((ip4_hdr)->ip_verhl & 0x0f) << 2)
-#define IPV4_GET_IPPROTO(ip4_hdr) ((ip4_hdr)->ip_proto)
-#define IPV4_GET_IPLEN(ip4_hdr) ((uint16_t)ntohs((ip4_hdr)->ip_len))
-#define IPV4_GET_SRC_ADDR(ip4_hdr) ((ip4_hdr)->ip4_hdrun1.ip4_un1.ip_src)
-#define IPV4_GET_DST_ADDR(ip4_hdr) ((ip4_hdr)->ip4_hdrun1.ip4_un1.ip_dst)
-
-#define UDP_GET_LEN(udp_hdr) ((uint16_t)ntohs((udp_hdr)->udp_len))
-#define UDP_GET_SRC_PORT(udp_hdr) ((uint16_t)ntohs((udp_hdr)->udp_src_port))
-#define UDP_GET_DST_PORT(udp_hdr) ((uint16_t)ntohs((udp_hdr)->udp_dst_port))
-
-#define TCP_GET_HLEN(tcp_hdr) ((((tcp_hdr)->th_offx2 & 0xf0) >> 4) << 2)
-#define TCP_GET_SRC_PORT(tcp_hdr) ((uint16_t)ntohs((tcp_hdr)->th_sport))
-#define TCP_GET_DST_PORT(tcp_hdr) ((uint16_t)ntohs((tcp_hdr)->th_dport))
-
-#define GTP1_GET_TYPE(gtp1_hdr) ((gtp1_hdr)->type)
-#define GTP1_GET_FLAGS(gtp1_hdr) ((gtp1_hdr)->flags >> 5)
-#define GTP1_GET_HLEN(gtp1_hdr) (((gtp1_hdr)->flags & GTP1_F_MASK) > 0 ? 12 : 8)
-
-///////////////////////////////////////////////////////////////////////////////
-// IPv4 IPv6 UDP GPT Header Struct
-///////////////////////////////////////////////////////////////////////////////
-
-typedef struct ipv6_header_s
-{
- union
- {
- struct ip6_un1_
- {
- uint32_t ip6_un1_flow; /* 20 bits of flow-ID */
- uint16_t ip6_un1_plen; /* payload length */
- uint8_t ip6_un1_nxt; /* next header */
- uint8_t ip6_un1_hlim; /* hop limit */
- } ip6_un1;
- uint8_t ip6_un2_vfc; /* 4 bits version, top 4 bits class */
- } ip6_hdrun;
-
- union
- {
- struct
- {
- uint32_t ip6_src[4];
- uint32_t ip6_dst[4];
- } ip6_un2;
- uint16_t ip6_addrs[16];
- } ip6_hdrun2;
-} ipv6_header_t;
-
-typedef struct ipv4_header_s
-{
- uint8_t ip_verhl; // version & header length
- uint8_t ip_tos;
- uint16_t ip_len;
- uint16_t ip_id;
- uint16_t ip_off;
- uint8_t ip_ttl;
- uint8_t ip_proto;
- uint16_t ip_csum;
- union
- {
- struct
- {
- struct in_addr ip_src;
- struct in_addr ip_dst;
- } ip4_un1;
- uint16_t ip_addrs[4];
- } ip4_hdrun1;
-} ipv4_header_t;
-
-typedef struct tcp_header_s
-{
- uint16_t th_sport; /**< source port */
- uint16_t th_dport; /**< destination port */
- uint32_t th_seq; /**< sequence number */
- uint32_t th_ack; /**< acknowledgement number */
- uint8_t th_offx2; /**< offset and reserved */
- uint8_t th_flags; /**< pkt flags */
- uint16_t th_win; /**< pkt window */
- uint16_t th_sum; /**< checksum */
- uint16_t th_urp; /**< urgent pointer */
-} __attribute__((__packed__)) tcp_header_t;
-
-typedef struct udp_header_s
-{
- uint16_t udp_src_port;
- uint16_t udp_dst_port;
- uint16_t udp_len;
- uint16_t udp_sum;
-} __attribute__((__packed__)) udp_header_t;
-
-enum gtp_version_e
-{
- GTP_V0 = 0,
- GTP_V1,
-};
-
-/* According to 3GPP TS 29.060. */
-typedef struct gtp1_header_s
-{
- uint8_t flags;
- uint8_t type;
- uint16_t length;
- uint32_t tid;
-} __attribute__((packed)) gtp1_header_t;
-
-///////////////////////////////////////////////////////////////////////////////
-// IPv4 IPv6 UDP GPT Header Parser
-///////////////////////////////////////////////////////////////////////////////
-
-typedef enum network_mode_s
-{
- NONE = 0,
- IPv4 = 1,
- IPv6 = 2,
- TCP = 3,
- UDP = 4,
-} network_mode_t;
-
-typedef struct pkt_info_s
-{
- uint32_t id;
- uint8_t *payload;
- uint32_t payload_len;
-} pkt_info_t;
-
-typedef struct ipv4_info_s
-{
- char src_addr[INET_ADDRSTRLEN];
- char dst_addr[INET_ADDRSTRLEN];
-
- ipv4_header_t *hdr;
- uint8_t *payload;
-
- uint32_t hdr_len;
- uint32_t opts_len;
- uint32_t payload_len;
-} ipv4_info_t;
-
-typedef struct ipv6_info_s
-{
- char src_addr[INET6_ADDRSTRLEN];
- char dst_addr[INET6_ADDRSTRLEN];
-
- ipv6_header_t *hdr;
- uint8_t *payload;
-
- uint32_t hdr_len;
- uint32_t payload_len;
-} ipv6_info_t;
-
-typedef struct udp_info_s
-{
- uint16_t src_port;
- uint16_t dst_port;
-
- udp_header_t *hdr;
- uint8_t *payload;
-
- uint32_t hdr_len;
- uint32_t payload_len;
-} udp_info_t;
-
-typedef struct tcp_info_s
-{
- uint16_t src_port;
- uint16_t dst_port;
-
- tcp_header_t *hdr;
- uint8_t *payload;
-
- uint32_t opt_len;
- uint32_t hdr_len;
- uint32_t payload_len;
-} tcp_info_t;
-
-typedef struct gtp_info_s
-{
- gtp1_header_t *hdr;
- uint8_t *payload;
-
- uint32_t hdr_len;
- uint32_t payload_len;
-} gtp_info_t;
-
-typedef struct pkt_paser_s
-{
- pkt_info_t raw_pkt;
-
- ipv4_info_t external_ipv4;
- ipv6_info_t external_ipv6;
- udp_info_t external_udp;
- gtp_info_t external_gtp;
- ipv4_info_t internal_ipv4;
- ipv6_info_t internal_ipv6;
- udp_info_t internal_udp;
- tcp_info_t internal_tcp;
-
- network_mode_t external_ip_version;
- network_mode_t external_l4_version;
- network_mode_t internal_ip_version;
- network_mode_t internal_l4_version;
-
-} pkt_paser_t;
-
-///////////////////////////////////////////////////////////////////////////////
-// Packet Parser API
-///////////////////////////////////////////////////////////////////////////////
-
-static void dump_info(pkt_paser_t *pkt_parser)
-{
-
- LOG_DEBUG("raw_pkt: {id: %u, data_len: %u}", pkt_parser->raw_pkt.id, pkt_parser->raw_pkt.payload_len);
-
- if (pkt_parser->external_ipv4.hdr)
- {
- LOG_DEBUG("id: %u, external_ipv4: {src_addr:%s,dst_addr:%s,hdr_len: %u, opt_len: %u, data_len: %u}",
- pkt_parser->raw_pkt.id,
- pkt_parser->external_ipv4.src_addr,
- pkt_parser->external_ipv4.dst_addr,
- pkt_parser->external_ipv4.hdr_len,
- pkt_parser->external_ipv4.opts_len,
- pkt_parser->external_ipv4.payload_len);
- }
- if (pkt_parser->external_ipv6.hdr)
- {
- LOG_DEBUG("id: %u, external_ipv6: {src_addr:%s,dst_addr:%s,hdr_len: %u, data_len: %u}",
- pkt_parser->raw_pkt.id,
- pkt_parser->external_ipv6.src_addr,
- pkt_parser->external_ipv6.dst_addr,
- pkt_parser->external_ipv6.hdr_len,
- pkt_parser->external_ipv6.payload_len);
- }
- if (pkt_parser->external_udp.hdr)
- {
- LOG_DEBUG("id: %u, external_udp: {src_port: %u, dst_port: %u, hdr_len: %u, data_len: %u}",
- pkt_parser->raw_pkt.id,
- pkt_parser->external_udp.src_port,
- pkt_parser->external_udp.dst_port,
- pkt_parser->external_udp.hdr_len,
- pkt_parser->external_udp.payload_len);
- }
-
- if (pkt_parser->external_gtp.hdr)
- {
- LOG_DEBUG("id: %u, external_gtp: {hdr_len: %u, data_len: %u}",
- pkt_parser->raw_pkt.id,
- pkt_parser->external_gtp.hdr_len,
- pkt_parser->external_gtp.payload_len);
- }
- if (pkt_parser->internal_ipv4.hdr)
- {
- LOG_DEBUG("id: %u, internal_ipv4: {src_addr:%s,dst_addr:%s,hdr_len: %u, opt_len: %u, data_len: %u}",
- pkt_parser->raw_pkt.id,
- pkt_parser->internal_ipv4.src_addr,
- pkt_parser->internal_ipv4.dst_addr,
- pkt_parser->internal_ipv4.hdr_len,
- pkt_parser->internal_ipv4.opts_len,
- pkt_parser->internal_ipv4.payload_len);
- }
- if (pkt_parser->internal_ipv6.hdr)
- {
- LOG_DEBUG("id: %u, interna_ipv6: {src_addr:%s,dst_addr:%s,hdr_len: %u, data_len: %u}",
- pkt_parser->raw_pkt.id,
- pkt_parser->internal_ipv6.src_addr,
- pkt_parser->internal_ipv6.dst_addr,
- pkt_parser->internal_ipv6.hdr_len,
- pkt_parser->internal_ipv6.payload_len);
- }
- if (pkt_parser->internal_udp.hdr)
- {
- LOG_DEBUG("id: %u, internal_udp: {src_port: %u, dst_port: %u, hdr_len: %u, data_len: %u}",
- pkt_parser->raw_pkt.id,
- pkt_parser->internal_udp.src_port,
- pkt_parser->internal_udp.dst_port,
- pkt_parser->internal_udp.hdr_len,
- pkt_parser->internal_udp.payload_len);
- }
- if (pkt_parser->internal_tcp.hdr)
- {
- LOG_DEBUG("id: %u, internal_tcp: {src_port: %u, dst_port: %u, hdr_len: %u, data_len:%u}",
- pkt_parser->raw_pkt.id,
- pkt_parser->internal_tcp.src_port,
- pkt_parser->internal_tcp.dst_port,
- pkt_parser->internal_tcp.hdr_len,
- pkt_parser->internal_tcp.payload_len);
- }
-}
-
-static int decode_gtp(gtp_info_t *packet, const uint8_t *data, uint32_t len)
-{
- if (len < sizeof(gtp1_header_t))
- {
- LOG_ERROR("Parser GTP Header: packet length too small %d", len);
- return -1;
- }
-
- packet->hdr = (gtp1_header_t *)data;
- if (GTP1_GET_FLAGS(packet->hdr) != GTP_V1)
- {
- LOG_ERROR("Parser GTP Header: invalid gtp flags %d", GTP1_GET_FLAGS(packet->hdr));
- return -1;
- }
-
- if (GTP1_GET_TYPE(packet->hdr) != GTP_TPDU)
- {
- LOG_ERROR("Parser GTP Header: invalid gtp type %d", GTP1_GET_TYPE(packet->hdr));
- return -1;
- }
-
- /* From 29.060: "This field shall be present if and only if any one or
- * more of the S, PN and E flags are set.".
- *
- * If any of the bit is set, then the remaining ones also have to be set.
- */
- packet->hdr_len = GTP1_GET_HLEN(packet->hdr);
- packet->payload = (uint8_t *)data + packet->hdr_len;
- packet->payload_len = len - packet->hdr_len;
-
- return 0;
-}
-
-static int decode_udp(udp_info_t *packet, const uint8_t *data, uint32_t len)
-{
- if (len < UDP_HEADER_LEN)
- {
- LOG_ERROR("Parser UDP Header: packet length too small %d", len);
- return -1;
- }
-
- packet->hdr = (udp_header_t *)data;
- // 检查 UDP header len
- if (len < UDP_GET_LEN(packet->hdr))
- {
- LOG_ERROR("Parser UDP Header: UDP packet too small %d", len);
- return -1;
- }
-
- // 检查 UDP header len
- if (len != UDP_GET_LEN(packet->hdr))
- {
- LOG_ERROR("Parser UDP Header: invalid UDP header length %d", UDP_GET_LEN(packet->hdr));
- return -1;
- }
-
- packet->src_port = UDP_GET_SRC_PORT(packet->hdr);
- packet->dst_port = UDP_GET_DST_PORT(packet->hdr);
-
- packet->hdr_len = UDP_HEADER_LEN;
- packet->payload = (uint8_t *)data + UDP_HEADER_LEN;
- packet->payload_len = len - UDP_HEADER_LEN;
-
- return 0;
-}
-
-static int decode_tcp(tcp_info_t *packet, const uint8_t *data, uint32_t len)
-{
- if (len < TCP_HEADER_LEN)
- {
- LOG_ERROR("Parser TCP Header: packet length too small %d", len);
- return -1;
- }
-
- packet->hdr = (tcp_header_t *)data;
- uint8_t hlen = TCP_GET_HLEN(packet->hdr);
- if (len < hlen)
- {
- LOG_ERROR("Parser TCP Header: TCP packet too small %d", len);
- return -1;
- }
-
- uint8_t tcp_opt_len = hlen - TCP_HEADER_LEN;
- if (tcp_opt_len > TCP_OPTLENMAX)
- {
- LOG_ERROR("Parser TCP Header: invalid opt length %d", tcp_opt_len);
- return -1;
- }
-
- packet->opt_len = tcp_opt_len;
- packet->src_port = TCP_GET_SRC_PORT(packet->hdr);
- packet->dst_port = TCP_GET_DST_PORT(packet->hdr);
-
- packet->hdr_len = hlen;
- packet->payload = (uint8_t *)data + packet->hdr_len;
- packet->payload_len = len = packet->hdr_len;
-
- return 0;
-}
-
-static int decode_ipv6(ipv6_info_t *packet, const uint8_t *data, uint32_t len)
-{
- if (len < IPV6_HEADER_LEN)
- {
- LOG_ERROR("Parser IPv6 Header: packet length too small %d", len);
- return -1;
- }
-
- // 检查 IPv6 header version
- if (IP_GET_RAW_VER(data) != 6)
- {
- LOG_ERROR("Parser IPv6 Header: unknown IP version %d", IP_GET_RAW_VER(data));
- return -1;
- }
-
- packet->hdr = (ipv6_header_t *)data;
- if (len < (IPV6_HEADER_LEN + IPV6_GET_PLEN(packet->hdr)))
- {
- LOG_ERROR("Parser IPv6 Header: trunc packet");
- return -1;
- }
-
- if (len != (IPV6_HEADER_LEN + IPV6_GET_PLEN(packet->hdr)))
- {
- LOG_ERROR("Parser IPv6 Header: invalid payload length %d", IPV6_GET_PLEN(packet->hdr));
- return -1;
- }
-
- inet_ntop(AF_INET6, &IPV6_GET_SRC_ADDR(packet->hdr), packet->src_addr, sizeof(packet->src_addr));
- inet_ntop(AF_INET6, &IPV6_GET_DST_ADDR(packet->hdr), packet->dst_addr, sizeof(packet->dst_addr));
-
- packet->hdr_len = IPV6_HEADER_LEN;
- packet->payload = (uint8_t *)data + packet->hdr_len;
- packet->payload_len = len - packet->hdr_len;
-
- return 0;
-}
-
-static int decode_ipv4(ipv4_info_t *packet, const uint8_t *data, uint32_t len)
-{
- // 检查包长是否大于 IPv4 header
- if (len < IPV4_HEADER_LEN)
- {
- LOG_ERROR("Parser IPv4 Header: packet length too small %d", len);
- return -1;
- }
-
- // 检查 IPv4 header version
- if (IP_GET_RAW_VER(data) != 4)
- {
- LOG_ERROR("Parser IPv4 Header: unknown IP version %d", IP_GET_RAW_VER(data));
- return -1;
- }
-
- packet->hdr = (ipv4_header_t *)data;
- // 检查 IPv4 header length
- if (IPV4_GET_HLEN(packet->hdr) < IPV4_HEADER_LEN)
- {
- LOG_ERROR("Parser IPv4 Header: invalid IP header length %d", IPV4_GET_HLEN(packet->hdr));
- return -1;
- }
-
- // 检查 IPv4 header total length
- if (IPV4_GET_IPLEN(packet->hdr) < IPV4_GET_HLEN(packet->hdr))
- {
- LOG_ERROR("Parser IPv4 Header: invalid IP header total length %d", IPV4_GET_IPLEN(packet->hdr));
- return -1;
- }
-
- // 检查是否 IP 分片
- if (len < IPV4_GET_IPLEN(packet->hdr))
- {
- LOG_ERROR("Parser IPv4 Header: trunc packet");
- return -1;
- }
-
- inet_ntop(AF_INET, &IPV4_GET_SRC_ADDR(packet->hdr), packet->src_addr, sizeof(packet->src_addr));
- inet_ntop(AF_INET, &IPV4_GET_DST_ADDR(packet->hdr), packet->dst_addr, sizeof(packet->dst_addr));
-
- packet->hdr_len = IPV4_GET_HLEN(packet->hdr);
- packet->opts_len = packet->hdr_len - IPV4_HEADER_LEN;
- packet->payload_len = len - packet->hdr_len;
- packet->payload = (uint8_t *)data + packet->hdr_len;
-
- return 0;
-}
-
-static int decode_internal_ip_pkt(pkt_paser_t *pkt_parser, const uint8_t *data, uint32_t len)
-{
- int next_protocol = 0;
- uint8_t *payload = NULL;
- uint32_t payload_len = 0;
-
- // IPv4/IPv6
- if (len < IPV4_HEADER_LEN)
- {
- LOG_ERROR("Parser Internal Raw header: packet length too small %d", len);
- return -1;
- }
-
- if (IP_GET_RAW_VER(data) == 4)
- {
- if (decode_ipv4(&(pkt_parser->internal_ipv4), data, len) == -1)
- {
- return -1;
- }
-
- pkt_parser->internal_ip_version = IPv4;
- payload = pkt_parser->internal_ipv4.payload;
- payload_len = pkt_parser->internal_ipv4.payload_len;
- next_protocol = IPV4_GET_IPPROTO(pkt_parser->internal_ipv4.hdr);
- }
- else if (IP_GET_RAW_VER(data) == 6)
- {
- if (decode_ipv6(&(pkt_parser->internal_ipv6), data, len) == -1)
- {
- return -1;
- }
- pkt_parser->internal_ip_version = IPv6;
- payload = pkt_parser->internal_ipv6.payload;
- payload_len = pkt_parser->internal_ipv6.payload_len;
- next_protocol = IPV6_GET_NH(pkt_parser->internal_ipv6.hdr);
- }
- else
- {
- LOG_ERROR("Unknown Internal IP version %d", IP_GET_RAW_VER(data));
- return -1;
- }
-
- // TCP/UDP
- if (next_protocol == IPPROTO_UDP)
- {
- if (decode_udp(&(pkt_parser->internal_udp), payload, payload_len) == -1)
- {
- return -1;
- }
- pkt_parser->internal_l4_version = UDP;
- return 0;
- }
- else if (next_protocol == IPPROTO_TCP)
- {
- if (decode_tcp(&(pkt_parser->internal_tcp), payload, payload_len) == -1)
- {
- return -1;
- }
- pkt_parser->internal_l4_version = TCP;
- return 0;
- }
- else
- {
- LOG_ERROR("Unknown Internal L4 next_protocol version %d", next_protocol);
- return -1;
- }
-}
-
-static int decode_external_ip_pkt(pkt_paser_t *pkt_parser, const uint8_t *data, uint32_t len)
-{
- int next_protocol = 0;
- uint8_t *payload = NULL;
- uint32_t payload_len = 0;
-
- // IPv4/IPv6
- if (len < IPV4_HEADER_LEN)
- {
- LOG_ERROR("Parser External Raw header: packet length too small %d", len);
- return -1;
- }
-
- if (IP_GET_RAW_VER(data) == 4)
- {
- if (decode_ipv4(&(pkt_parser->external_ipv4), data, len) == -1)
- {
- return -1;
- }
- pkt_parser->external_ip_version = IPv4;
- payload = pkt_parser->external_ipv4.payload;
- payload_len = pkt_parser->external_ipv4.payload_len;
- next_protocol = IPV4_GET_IPPROTO(pkt_parser->external_ipv4.hdr);
- }
- else if (IP_GET_RAW_VER(data) == 6)
- {
- if (decode_ipv6(&(pkt_parser->external_ipv6), data, len) == -1)
- {
- return -1;
- }
- pkt_parser->external_ip_version = IPv6;
- payload = pkt_parser->external_ipv6.payload;
- payload_len = pkt_parser->external_ipv6.payload_len;
- next_protocol = IPV6_GET_NH(pkt_parser->external_ipv6.hdr);
- }
- else
- {
- LOG_ERROR("Unknown External IP version %d", IP_GET_RAW_VER(data));
- return -1;
- }
-
- // TCP/UDP
- if (next_protocol == IPPROTO_UDP)
- {
- if (decode_udp(&(pkt_parser->external_udp), payload, payload_len) == -1)
- {
- return -1;
- }
- pkt_parser->external_l4_version = UDP;
- }
- else if (next_protocol == IPPROTO_TCP)
- {
- pkt_parser->external_l4_version = TCP;
- LOG_ERROR("Unknown External L4 next_protocol version %d", next_protocol);
- return -1;
- }
- else
- {
- LOG_ERROR("Unknown External L4 next_protocol version %d", next_protocol);
- return -1;
- }
-
- // GTP
- if (decode_gtp(&(pkt_parser->external_gtp), pkt_parser->external_udp.payload, pkt_parser->external_udp.payload_len) == -1)
- {
- return -1;
- }
-
- return 0;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// NFQ API
-///////////////////////////////////////////////////////////////////////////////
-
-/*
- * nfmsg : message objetc that contains the packet
- * nfa : Netlink packet data handle
- */
-static int packet_handler_cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data)
-{
- int offest = 0;
- int raw_ip_fd = 0;
- int packet_len = 0;
- pkt_paser_t pkt_parser = {0};
- unsigned char *packet_data = NULL;
- struct nfqnl_msg_packet_hdr *packet_hdr = NULL;
-
- packet_hdr = nfq_get_msg_packet_hdr(nfa);
- if (packet_hdr == NULL)
- {
- LOG_ERROR("Failed at nfq_get_msg_packet_hdr()");
- goto end;
- }
-
- packet_len = nfq_get_payload(nfa, &packet_data);
- if (packet_len <= 0)
- {
- LOG_ERROR("Failed at nfq_get_payload()");
- goto end;
- }
-
- pkt_parser.raw_pkt.id = ntohl(packet_hdr->packet_id);
- pkt_parser.raw_pkt.payload = packet_data;
- pkt_parser.raw_pkt.payload_len = packet_len;
- if (decode_external_ip_pkt(&pkt_parser, pkt_parser.raw_pkt.payload, pkt_parser.raw_pkt.payload_len) == -1)
- {
- goto end;
- }
-
- if (decode_internal_ip_pkt(&pkt_parser, pkt_parser.external_gtp.payload, pkt_parser.external_gtp.payload_len) == -1)
- {
- goto end;
- }
-
- /*
- * NF_DROP : discarded the packet
- * NF_ACCEPT : the packet passes, continue iterations
- * NF_QUEUE : inject the packet into a different queue (the target queue number is in the high 16 bits of the verdict)
- * NF_REPEAT : iterate the same cycle once more
- * NF_STOP : accept, but don't continue iterations
- */
- // nfq_set_verdict()
- // nfq_set_verdict2()
- // nfq_set_verdict_batch()
- // nfq_set_verdict_batch2()
- // nfq_set_verdict_mark()
-
- if (pkt_parser.external_ip_version == IPv4)
- {
- offest += pkt_parser.external_ipv4.hdr_len;
- }
- if (pkt_parser.external_ip_version == IPv6)
- {
- offest += pkt_parser.external_ipv6.hdr_len;
- }
-
- offest += pkt_parser.external_udp.hdr_len;
- offest += pkt_parser.external_gtp.hdr_len;
-
- dump_info(&pkt_parser);
- LOG_DEBUG("Offset : %d", offest);
-
- if (offest > 0)
- {
- if ((pkt_parser.external_ip_version == IPv4 && pkt_parser.internal_ip_version == IPv4) || (pkt_parser.external_ip_version == IPv6 && pkt_parser.internal_ip_version == IPv6))
- {
- return nfq_set_verdict(qh, pkt_parser.raw_pkt.id, NF_ACCEPT, packet_len - offest, packet_data + offest);
- }
- if (pkt_parser.external_ip_version == IPv4 && pkt_parser.internal_ip_version == IPv6)
- {
- struct sockaddr_in6 saddr6 = {0};
- saddr6.sin6_family = PF_INET6;
- memcpy(saddr6.sin6_addr.s6_addr, pkt_parser.internal_ipv6.hdr->ip6_hdrun2.ip6_un2.ip6_dst, sizeof(struct in6_addr));
- raw_ip_fd = socket(PF_INET6, SOCK_RAW, IPPROTO_RAW);
- if (sendto(raw_ip_fd, packet_data + offest, packet_len - offest, 0, (struct sockaddr *)&saddr6, sizeof(saddr6)) == -1)
- {
- LOG_ERROR("Failed at send(), %d: %s", errno, strerror(errno));
- close(raw_ip_fd);
- goto end;
- }
- close(raw_ip_fd);
- }
- else if (pkt_parser.external_ip_version == IPv6 && pkt_parser.internal_ip_version == IPv4)
- {
- struct sockaddr_in saddr4 = {0};
- saddr4.sin_family = PF_INET;
- saddr4.sin_addr.s_addr = inet_addr(pkt_parser.internal_ipv4.dst_addr);
- raw_ip_fd = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
- if (sendto(raw_ip_fd, packet_data + offest, packet_len - offest, 0, (struct sockaddr *)&saddr4, sizeof(saddr4)) == -1)
- {
- LOG_ERROR("Failed at send(), %d: %s", errno, strerror(errno));
- close(raw_ip_fd);
- goto end;
- }
- close(raw_ip_fd);
- }
- return nfq_set_verdict(qh, pkt_parser.raw_pkt.id, NF_DROP, 0, NULL);
- }
-
-end:
- return nfq_set_verdict(qh, pkt_parser.raw_pkt.id, NF_ACCEPT, 0, NULL);
-}
-
-/*
- * doc : http://www.netfilter.org/projects/libnetfilter_queue/doxygen/html/
- * Library setup : http://www.netfilter.org/projects/libnetfilter_queue/doxygen/html/group__LibrarySetup.html
- * Queue handling : http://www.netfilter.org/projects/libnetfilter_queue/doxygen/html/group__Queue.html
- * Message parsing : http://www.netfilter.org/projects/libnetfilter_queue/doxygen/html/group__Parsing.html
- */
-int main(int argc, char **argv)
-{
- int fd;
- int rv;
- uint32_t queue = 1;
- struct nfq_handle *handle;
- struct nfq_q_handle *q_handle;
- char buf[4096] __attribute__((aligned));
-
- if (argc == 2)
- {
- queue = atoi(argv[1]);
- if (queue > 65535)
- {
- fprintf(stderr, "Usage: %s [<0-65535>]\n", argv[0]);
- return 0;
- }
- }
- LOG_DEBUG("Using queue: %d", queue);
-
- handle = nfq_open();
- if (handle == NULL)
- {
- LOG_ERROR("Failed at nfq_open(), %d: %s", errno, strerror(errno));
- goto error;
- }
-
- if (nfq_unbind_pf(handle, AF_INET) < 0)
- {
- LOG_ERROR("Failed at nfq_unbind_pf(), %d: %s", errno, strerror(errno));
- goto error;
- }
-
- if (nfq_bind_pf(handle, AF_INET) < 0)
- {
- LOG_ERROR("Failed at nfq_bind_pf(), %d: %s", errno, strerror(errno));
- goto error;
- }
-
- q_handle = nfq_create_queue(handle, queue, &packet_handler_cb, NULL);
- if (q_handle == NULL)
- {
- LOG_ERROR("Failed at nfq_create_queue(), %d: %s", errno, strerror(errno));
- goto error;
- }
-
- /*
- * NFQNL_COPY_NONE - noop, do not use it
- * NFQNL_COPY_META - copy only packet metadata
- * NFQNL_COPY_PACKET - copy entire packet
- */
- if (nfq_set_mode(q_handle, NFQNL_COPY_PACKET, 0xffff) < 0)
- {
- LOG_ERROR("Failed at nfq_set_mode(NFQNL_COPY_PACKET), %d: %s", errno, strerror(errno));
- goto error;
- }
-
- if (nfq_set_queue_maxlen(q_handle, 65535) < 0)
- {
- LOG_ERROR("Failed at nfq_set_queue_maxlen(65535), %d: %s", errno, strerror(errno));
- goto error;
- }
-
- LOG_DEBUG("Waiting for packets...");
-
- fd = nfq_fd(handle);
- for (;;)
- {
- if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0)
- {
- nfq_handle_packet(handle, buf, rv);
- continue;
- }
- /*
- * if your application is too slow to digest the packets that
- * are sent from kernel-space, the socket buffer that we use
- * to enqueue packets may fill up returning ENOBUFS. Depending
- * on your application, this error may be ignored. Please, see
- * the doxygen documentation of this library on how to improve
- * this situation.
- */
- if (rv < 0 && errno == ENOBUFS)
- {
- LOG_ERROR("Losing packets !!!");
- continue;
- }
- LOG_ERROR("Failed at recv(), %d: %s", errno, strerror(errno));
- break;
- }
-
-error:
- if (q_handle)
- {
- nfq_destroy_queue(q_handle);
- }
-
- if (handle)
- {
- nfq_close(handle);
- }
-
- return 0;
-}
diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt
new file mode 100644
index 0000000..6037773
--- /dev/null
+++ b/platform/CMakeLists.txt
@@ -0,0 +1,10 @@
+find_package(NFNETLINK REQUIRED)
+
+add_executable(packetadapter src/inject_pkt.c src/system.c src/packet_adapter.c)
+
+target_include_directories(packetadapter PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/)
+
+target_link_libraries(packetadapter common)
+target_link_libraries(packetadapter netfilter_queue)
+
+install(TARGETS packetadapter RUNTIME DESTINATION bin COMPONENT Program)
\ No newline at end of file
diff --git a/platform/include/inject_pkt.h b/platform/include/inject_pkt.h
new file mode 100644
index 0000000..a89aece
--- /dev/null
+++ b/platform/include/inject_pkt.h
@@ -0,0 +1,18 @@
+#ifndef _INJECT_PKT_H
+#define _INJECT_PKT_H
+
+#ifdef __cpluscplus
+extern "C"
+{
+#endif
+
+#include "public.h"
+
+ int inject_ipv4_pkt(char *ip4_addr, uint8_t *data, uint32_t len);
+ int inject_ipv6_pkt(char *ip6_addr, uint8_t *data, uint32_t len);
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/platform/include/system.h b/platform/include/system.h
new file mode 100644
index 0000000..bf20541
--- /dev/null
+++ b/platform/include/system.h
@@ -0,0 +1,17 @@
+#ifndef _SYSTEM_H
+#define _SYSTEM_H
+
+#ifdef __cpluscplus
+extern "C"
+{
+#endif
+
+#include "public.h"
+
+ int run_daemon(void);
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/platform/src/inject_pkt.c b/platform/src/inject_pkt.c
new file mode 100644
index 0000000..9e336c0
--- /dev/null
+++ b/platform/src/inject_pkt.c
@@ -0,0 +1,53 @@
+#include "inject_pkt.h"
+
+int inject_ipv4_pkt(char *ip4_addr, uint8_t *data, uint32_t len)
+{
+ int fd = 0;
+ struct sockaddr_in saddr4 = {0};
+
+ saddr4.sin_family = PF_INET;
+ saddr4.sin_addr.s_addr = inet_addr(ip4_addr);
+
+ fd = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
+ if (fd == -1)
+ {
+ LOG_ERROR("Failed at socket(PF_INET, SOCK_RAW), %d: %s", errno, strerror(errno));
+ return -1;
+ }
+
+ if (sendto(fd, data, len, 0, (struct sockaddr *)&saddr4, sizeof(saddr4)) == -1)
+ {
+ LOG_ERROR("Failed at send(), %d: %s", errno, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+int inject_ipv6_pkt(char *ip6_addr, uint8_t *data, uint32_t len)
+{
+ int fd = 0;
+ struct sockaddr_in6 saddr6 = {0};
+
+ saddr6.sin6_family = PF_INET6;
+ inet_pton(AF_INET6, ip6_addr, &saddr6.sin6_addr);
+
+ fd = socket(PF_INET6, SOCK_RAW, IPPROTO_RAW);
+ if (fd == -1)
+ {
+ LOG_ERROR("Failed at socket(PF_INET6, SOCK_RAW), %d: %s", errno, strerror(errno));
+ return -1;
+ }
+
+ if (sendto(fd, data, len, 0, (struct sockaddr *)&saddr6, sizeof(saddr6)) == -1)
+ {
+ LOG_ERROR("Failed at send(), %d: %s", errno, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
\ No newline at end of file
diff --git a/platform/src/packet_adapter.c b/platform/src/packet_adapter.c
new file mode 100644
index 0000000..205cd87
--- /dev/null
+++ b/platform/src/packet_adapter.c
@@ -0,0 +1,449 @@
+#include "decode_ipv4.h"
+#include "decode_ipv6.h"
+#include "decode_tcp.h"
+#include "decode_udp.h"
+#include "decode_gtp.h"
+#include "inject_pkt.h"
+#include "system.h"
+
+#include // for NF_ACCEPT
+#include
+
+#ifdef Packet_Adapter_GIT_VERSION
+static __attribute__((__used__)) const char *Packet_Adapter_Version = Packet_Adapter_GIT_VERSION;
+#else
+static __attribute__((__used__)) const char *Packet_Adapter_Version = "Unknown";
+#endif
+
+typedef struct pkt_info_s
+{
+ uint32_t id; // unique ID of packet in queue
+ uint16_t protocol; // hw protocol
+ uint8_t hook; // netfilter hook
+ u_int32_t mark;
+ u_int32_t indev;
+ u_int32_t outdev;
+ u_int32_t phys_indev;
+ u_int32_t phys_outdev;
+
+ uint8_t *payload;
+ uint32_t payload_len;
+
+ char src_addr[512];
+} pkt_info_t;
+
+typedef struct union_info_s
+{
+ ipv4_info_t ipv4;
+ ipv6_info_t ipv6;
+ tcp_info_t tcp;
+ udp_info_t udp;
+} union_info_t;
+
+typedef struct pkt_paser_s
+{
+ pkt_info_t raw;
+ union_info_t external;
+ gtp_info_t gtp;
+ union_info_t internal;
+} pkt_paser_t;
+
+static void dump_info(pkt_paser_t *parser)
+{
+ uint32_t pkt_id = parser->raw.id;
+ LOG_DEBUG("raw: {id: %u, protocol: %u, hook: %u, mark: %u, indev: %u, outdev: %u, phys_indev: %u, phys_outdev: %u, src_addr: %s, data_len: %u}",
+ parser->raw.id,
+ parser->raw.protocol,
+ parser->raw.hook,
+ parser->raw.mark,
+ parser->raw.indev,
+ parser->raw.outdev,
+ parser->raw.phys_indev,
+ parser->raw.phys_outdev,
+ parser->raw.src_addr,
+ parser->raw.payload_len);
+
+ // external
+ if (parser->external.ipv4.hdr)
+ {
+ dump_ipv4_info(pkt_id, &(parser->external.ipv4));
+ }
+ if (parser->external.ipv6.hdr)
+ {
+ dump_ipv6_info(pkt_id, &(parser->external.ipv6));
+ }
+ if (parser->external.udp.hdr)
+ {
+ dump_udp_info(pkt_id, &(parser->external.udp));
+ }
+ if (parser->external.tcp.hdr)
+ {
+ dump_tcp_info(pkt_id, &(parser->external.tcp));
+ }
+
+ // gtp
+ if (parser->gtp.hdr)
+ {
+ dump_gtp_info(pkt_id, &(parser->gtp));
+ }
+
+ // internal
+ if (parser->internal.ipv4.hdr)
+ {
+ dump_ipv4_info(pkt_id, &(parser->internal.ipv4));
+ }
+ if (parser->internal.ipv6.hdr)
+ {
+ dump_ipv6_info(pkt_id, &(parser->internal.ipv6));
+ }
+ if (parser->internal.udp.hdr)
+ {
+ dump_udp_info(pkt_id, &(parser->internal.udp));
+ }
+ if (parser->internal.tcp.hdr)
+ {
+ dump_tcp_info(pkt_id, &(parser->internal.tcp));
+ }
+}
+
+static int decode_ip_tcp_udp(union_info_t *parser, const uint8_t *data, uint32_t len)
+{
+ int next_protocol = 0;
+ uint8_t *payload = NULL;
+ uint32_t payload_len = 0;
+
+ if (len < IPV4_HEADER_LEN)
+ {
+ LOG_ERROR("Parser IP header: packet length too small %d", len);
+ return -1;
+ }
+
+ if (IP_GET_RAW_VER(data) == 4)
+ {
+ if (decode_ipv4(&(parser->ipv4), data, len) == -1)
+ {
+ return -1;
+ }
+
+ payload = parser->ipv4.payload;
+ payload_len = parser->ipv4.payload_len;
+ next_protocol = parser->ipv4.next_protocol;
+ }
+ else if (IP_GET_RAW_VER(data) == 6)
+ {
+ if (decode_ipv6(&(parser->ipv6), data, len) == -1)
+ {
+ return -1;
+ }
+ payload = parser->ipv6.payload;
+ payload_len = parser->ipv6.payload_len;
+ next_protocol = parser->ipv6.next_protocol;
+ }
+ else
+ {
+ LOG_ERROR("Unknown IP version %d", IP_GET_RAW_VER(data));
+ return -1;
+ }
+
+ if (next_protocol == IPPROTO_UDP)
+ {
+ if (decode_udp(&(parser->udp), payload, payload_len) == -1)
+ {
+ return -1;
+ }
+ return 0;
+ }
+ else if (next_protocol == IPPROTO_TCP)
+ {
+ if (decode_tcp(&(parser->tcp), payload, payload_len) == -1)
+ {
+ return -1;
+ }
+ return 0;
+ }
+ else
+ {
+ LOG_ERROR("Unknown Internal L4 next_protocol version %d", next_protocol);
+ return -1;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// NFQ API
+///////////////////////////////////////////////////////////////////////////////
+
+static int decode_pkt(pkt_info_t *packet, struct nfgenmsg *nfmsg, struct nfq_data *nfa)
+{
+ struct nfqnl_msg_packet_hdr *packet_hdr = NULL;
+ struct nfqnl_msg_packet_hw *packet_hw = NULL;
+
+ packet_hdr = nfq_get_msg_packet_hdr(nfa);
+ if (packet_hdr == NULL)
+ {
+ LOG_ERROR("Failed at nfq_get_msg_packet_hdr()");
+ return 0;
+ }
+ packet->id = ntohl(packet_hdr->packet_id);
+
+ packet->payload_len = nfq_get_payload(nfa, &packet->payload);
+ if (packet->payload_len <= 0)
+ {
+ LOG_ERROR("Failed at nfq_get_payload()");
+ return packet->id;
+ }
+ packet->protocol = ntohs(packet_hdr->hw_protocol);
+ packet->hook = packet_hdr->hook;
+
+ packet_hw = nfq_get_packet_hw(nfa);
+ if (packet_hw)
+ {
+ int i = 0;
+ int offset = 0;
+ int len = sizeof(packet->src_addr);
+ int hlen = ntohs(packet_hw->hw_addrlen);
+
+ for (i = 0; i < hlen - 1; i++)
+ {
+ offset += snprintf(packet->src_addr + offset, len - offset, "%02x:", packet_hw->hw_addr[i]);
+ }
+ snprintf(packet->src_addr + offset, len - offset, "%02x", packet_hw->hw_addr[hlen - 1]);
+ }
+
+ packet->mark = nfq_get_nfmark(nfa);
+ packet->indev = nfq_get_indev(nfa);
+ packet->outdev = nfq_get_outdev(nfa);
+ packet->phys_indev = nfq_get_physindev(nfa);
+ packet->phys_outdev = nfq_get_physoutdev(nfa);
+
+ return packet->id;
+}
+/*
+ * nfmsg : message objetc that contains the packet
+ * nfa : Netlink packet data handle
+ */
+static int packet_handler_cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data)
+{
+ int offest = 0;
+ pkt_paser_t parser = {0};
+ int packet_id = decode_pkt(&(parser.raw), nfmsg, nfa);
+
+ // external
+ if (decode_ip_tcp_udp(&(parser.external), parser.raw.payload, parser.raw.payload_len) == -1)
+ {
+ goto end;
+ }
+
+ if (parser.external.udp.hdr == NULL)
+ {
+ LOG_ERROR("External L4 protocol not UDP");
+ goto end;
+ }
+
+ // decode GTP
+ if (decode_gtp(&(parser.gtp), parser.external.udp.payload, parser.external.udp.payload_len) == -1)
+ {
+ return -1;
+ }
+
+ // internal
+ if (decode_ip_tcp_udp(&(parser.internal), parser.gtp.payload, parser.gtp.payload_len) == -1)
+ {
+ goto end;
+ }
+
+ /*
+ * NF_DROP : discarded the packet
+ * NF_ACCEPT : the packet passes, continue iterations
+ * NF_QUEUE : inject the packet into a different queue (the target queue number is in the high 16 bits of the verdict)
+ * NF_REPEAT : iterate the same cycle once more
+ * NF_STOP : accept, but don't continue iterations
+ */
+ // nfq_set_verdict()
+ // nfq_set_verdict2()
+ // nfq_set_verdict_batch()
+ // nfq_set_verdict_batch2()
+ // nfq_set_verdict_mark()
+
+ if (parser.external.ipv4.hdr)
+ {
+ offest += parser.external.ipv4.hdr_len;
+ }
+ if (parser.external.ipv6.hdr)
+ {
+ offest += parser.external.ipv6.hdr_len;
+ }
+
+ offest += parser.external.udp.hdr_len;
+ offest += parser.gtp.hdr_len;
+
+ dump_info(&parser);
+ LOG_DEBUG("Offset : %d", offest);
+
+ uint8_t *inject_data = parser.raw.payload + offest;
+ uint32_t inject_data_len = parser.raw.payload_len - offest;
+
+ if (offest > 0)
+ {
+ if ((parser.external.ipv4.hdr && parser.internal.ipv4.hdr) || (parser.external.ipv6.hdr && parser.internal.ipv6.hdr))
+ {
+ return nfq_set_verdict(qh, packet_id, NF_ACCEPT, inject_data_len, inject_data);
+ }
+
+ if (parser.external.ipv4.hdr && parser.internal.ipv6.hdr)
+ {
+ if (inject_ipv6_pkt(parser.internal.ipv6.dst_addr, inject_data, inject_data_len) == -1)
+ {
+ goto end;
+ }
+ return nfq_set_verdict(qh, packet_id, NF_DROP, 0, NULL);
+ }
+
+ if (parser.external.ipv6.hdr && parser.internal.ipv4.hdr)
+ {
+ if (inject_ipv4_pkt(parser.internal.ipv4.dst_addr, inject_data, inject_data_len) == -1)
+ {
+ goto end;
+ }
+ return nfq_set_verdict(qh, packet_id, NF_DROP, 0, NULL);
+ }
+ }
+
+end:
+ return nfq_set_verdict(qh, packet_id, NF_ACCEPT, 0, NULL);
+}
+
+static void usage(char *cmd)
+{
+ fprintf(stderr, "USAGE: %s [OPTIONS]\n", cmd);
+ fprintf(stderr, " -v -- show version\n");
+ fprintf(stderr, " -i id -- set queue id\n");
+ fprintf(stderr, " -d -- run daemon\n");
+ fprintf(stderr, " -h -- show help\n");
+}
+
+/*
+ * doc : http://www.netfilter.org/projects/libnetfilter_queue/doxygen/html/
+ * Library setup : http://www.netfilter.org/projects/libnetfilter_queue/doxygen/html/group__LibrarySetup.html
+ * Queue handling : http://www.netfilter.org/projects/libnetfilter_queue/doxygen/html/group__Queue.html
+ * Message parsing : http://www.netfilter.org/projects/libnetfilter_queue/doxygen/html/group__Parsing.html
+ */
+int main(int argc, char **argv)
+{
+ int fd;
+ int rv;
+ int opt;
+ uint16_t queue = 1;
+ struct nfq_handle *handle;
+ struct nfq_q_handle *q_handle;
+ char buf[65535] __attribute__((aligned));
+
+ while ((opt = getopt(argc, argv, "vi:dh")) != -1)
+ {
+ switch (opt)
+ {
+ case 'v':
+ fprintf(stderr, "Packet Adapter Version: %s\n", Packet_Adapter_Version);
+ return 0;
+ case 'i':
+ queue = atoi(optarg);
+ if (queue < 0 || queue > 65535)
+ {
+ fprintf(stderr, "Usage: %s queueid %d out of range [0, 65535]\n", argv[0], queue);
+ return 0;
+ }
+ break;
+ case 'd':
+ run_daemon();
+ break;
+ case 'h': /* fall through */
+ default:
+ usage(argv[0]);
+ return 0;
+ }
+ }
+
+ LOG_DEBUG("Using queue: %d", queue);
+
+ handle = nfq_open();
+ if (handle == NULL)
+ {
+ LOG_ERROR("Failed at nfq_open(), %d: %s", errno, strerror(errno));
+ goto error;
+ }
+
+ if (nfq_unbind_pf(handle, AF_INET) < 0)
+ {
+ LOG_ERROR("Failed at nfq_unbind_pf(), %d: %s", errno, strerror(errno));
+ goto error;
+ }
+
+ if (nfq_bind_pf(handle, AF_INET) < 0)
+ {
+ LOG_ERROR("Failed at nfq_bind_pf(), %d: %s", errno, strerror(errno));
+ goto error;
+ }
+
+ q_handle = nfq_create_queue(handle, queue, &packet_handler_cb, NULL);
+ if (q_handle == NULL)
+ {
+ LOG_ERROR("Failed at nfq_create_queue(), %d: %s", errno, strerror(errno));
+ goto error;
+ }
+
+ /*
+ * NFQNL_COPY_NONE - noop, do not use it
+ * NFQNL_COPY_META - copy only packet metadata
+ * NFQNL_COPY_PACKET - copy entire packet
+ */
+ if (nfq_set_mode(q_handle, NFQNL_COPY_PACKET, 0xffff) < 0)
+ {
+ LOG_ERROR("Failed at nfq_set_mode(NFQNL_COPY_PACKET), %d: %s", errno, strerror(errno));
+ goto error;
+ }
+
+ if (nfq_set_queue_maxlen(q_handle, 65535) < 0)
+ {
+ LOG_ERROR("Failed at nfq_set_queue_maxlen(65535), %d: %s", errno, strerror(errno));
+ goto error;
+ }
+
+ LOG_DEBUG("Waiting for packets...");
+
+ fd = nfq_fd(handle);
+ for (;;)
+ {
+ if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0)
+ {
+ nfq_handle_packet(handle, buf, rv);
+ continue;
+ }
+ /*
+ * if your application is too slow to digest the packets that
+ * are sent from kernel-space, the socket buffer that we use
+ * to enqueue packets may fill up returning ENOBUFS. Depending
+ * on your application, this error may be ignored. Please, see
+ * the doxygen documentation of this library on how to improve
+ * this situation.
+ */
+ if (rv < 0 && errno == ENOBUFS)
+ {
+ LOG_ERROR("Losing packets !!!");
+ continue;
+ }
+
+ LOG_ERROR("Failed at recv(), %d: %s", errno, strerror(errno));
+ }
+
+error:
+ if (q_handle)
+ {
+ nfq_destroy_queue(q_handle);
+ }
+
+ if (handle)
+ {
+ nfq_close(handle);
+ }
+
+ return 0;
+}
diff --git a/platform/src/system.c b/platform/src/system.c
new file mode 100644
index 0000000..76141de
--- /dev/null
+++ b/platform/src/system.c
@@ -0,0 +1,69 @@
+#include "system.h"
+
+int run_daemon(void)
+{
+ int fd;
+
+ switch (fork())
+ {
+ // 失败
+ case -1:
+ LOG_ERROR("Failed at fork(), %d: %s", errno, strerror(errno));
+ return -1;
+ // 子进程
+ case 0:
+ break;
+ // 父进程
+ default:
+ exit(0);
+ }
+
+ if (setsid() == -1)
+ {
+ LOG_ERROR("Failed at setsid(), %d: %s", errno, strerror(errno));
+ return -1;
+ }
+
+ umask(0);
+
+ // 以读写模式打开 /dev/null
+ fd = open("/dev/null", O_RDWR);
+ if (fd == -1)
+ {
+ LOG_ERROR("Failed at open(/dev/null), %d: %s", errno, strerror(errno));
+ return -1;
+ }
+
+ // 将标准输入关联到 /dev/null
+ if (dup2(fd, STDIN_FILENO) == -1)
+ {
+ LOG_ERROR("Failed at dup2(STDIN_FILENO), %d: %s", errno, strerror(errno));
+ return -1;
+ }
+
+ // 将标准输出关联到 /dev/null
+ if (dup2(fd, STDOUT_FILENO) == -1)
+ {
+ LOG_ERROR("Failed at dup2(STDOUT_FILENO), %d: %s", errno, strerror(errno));
+ return -1;
+ }
+
+ // 将标准错误关联到 /dev/null
+ if (dup2(fd, STDERR_FILENO) == -1)
+ {
+ LOG_ERROR("Failed at dup2(STDERR_FILENO), %d: %s", errno, strerror(errno));
+ return -1;
+ }
+
+ // 关闭 /dev/null 的文件句柄
+ if (fd > STDERR_FILENO)
+ {
+ if (close(fd) == -1)
+ {
+ LOG_ERROR("Failed at close(), %d: %s", errno, strerror(errno));
+ return -1;
+ }
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/script/CMakeLists.txt b/script/CMakeLists.txt
new file mode 100644
index 0000000..c389b62
--- /dev/null
+++ b/script/CMakeLists.txt
@@ -0,0 +1 @@
+install(FILES service/packetadapter.service DESTINATION /usr/lib/systemd/system/ COMPONENT Program)
\ No newline at end of file
diff --git a/script/service/packetadapter.service b/script/service/packetadapter.service
new file mode 100644
index 0000000..558ed38
--- /dev/null
+++ b/script/service/packetadapter.service
@@ -0,0 +1,10 @@
+[Unit]
+Description=Packet Adapter
+After=network.target
+
+[Service]
+ExecStartPre=/usr/sbin/iptables -A OUTPUT -o eno4 -p udp --dport 2152 -j NFQUEUE --queue-num 1
+ExecStartPre=/usr/sbin/ip6tables -A OUTPUT -o eno4 -p udp --dport 2152 -j NFQUEUE --queue-num 1
+ExecStart=/opt/tsg/packetadapter/bin/packetadapter
+ExecStopPost=/usr/sbin/iptables -D OUTPUT -o eno4 -p udp --dport 2152 -j NFQUEUE --queue-num 1
+ExecStopPost=/usr/sbin/ip6tables -D OUTPUT -o eno4 -p udp --dport 2152 -j NFQUEUE --queue-num 1
\ No newline at end of file