From fffce4d4a50dead4c0d9e5d5be624b720d52fca9 Mon Sep 17 00:00:00 2001 From: "xiaming.cxm" Date: Thu, 10 Jul 2025 10:50:10 +0800 Subject: [PATCH 1/3] enhance ci; add examples --- .github/workflows/ci.yml | 100 +--- CMakeLists.txt | 21 +- README.md | 169 +++--- examples/example_pkt2flow.c | 43 ++ flow_db.cpp | 209 -------- format_code.sh | 151 ++++++ pkt2flow.cpp | 519 ------------------- sample/200722_win_scale_examples_anon.pcapng | Bin 0 -> 2976 bytes sample/README.md | 4 + src/flow_db.cpp | 201 +++++++ src/pkt2flow.cpp | 473 +++++++++++++++++ pkt2flow.h => src/pkt2flow.h | 72 +-- utilities.c => src/utilities.cpp | 62 ++- tests/test_flow_db.cpp | 214 ++++---- tests/test_main.cpp | 28 +- tests/test_utilities.cpp | 297 +++++------ 16 files changed, 1321 insertions(+), 1242 deletions(-) create mode 100644 examples/example_pkt2flow.c delete mode 100644 flow_db.cpp create mode 100755 format_code.sh delete mode 100644 pkt2flow.cpp create mode 100644 sample/200722_win_scale_examples_anon.pcapng create mode 100644 sample/README.md create mode 100644 src/flow_db.cpp create mode 100644 src/pkt2flow.cpp rename pkt2flow.h => src/pkt2flow.h (75%) rename utilities.c => src/utilities.cpp (64%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8866923..9b9063e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,13 +7,13 @@ on: branches: [ main, master, develop ] jobs: - build-and-test: + build-test-analyze: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] - build_type: [Release, Debug] + build_type: [Release] steps: - uses: actions/checkout@v4 @@ -27,7 +27,9 @@ jobs: libgoogle-glog-dev \ libgtest-dev \ cmake \ - build-essential + build-essential \ + cppcheck \ + clang-format - name: Install dependencies (macOS) if: matrix.os == 'macos-latest' @@ -37,9 +39,29 @@ jobs: libpcap \ glog \ googletest \ - cmake + cmake \ + cppcheck \ + clang-format + + - name: Run cppcheck + env: + PKG_CONFIG_PATH: ${{ matrix.os == 'macos-latest' && '/opt/homebrew/opt/libpcap/lib/pkgconfig' || '' }} + run: | + cppcheck --enable=all --error-exitcode=1 --suppress=missingIncludeSystem \ + --suppress=unusedFunction \ + --inline-suppr \ + src/*.h src/*.cpp + + - name: Check code formatting + env: + PKG_CONFIG_PATH: ${{ matrix.os == 'macos-latest' && '/opt/homebrew/opt/libpcap/lib/pkgconfig' || '' }} + run: | + chmod +x format_code.sh + ./format_code.sh --check - name: Configure CMake + env: + PKG_CONFIG_PATH: ${{ matrix.os == 'macos-latest' && '/opt/homebrew/opt/libpcap/lib/pkgconfig' || '' }} run: | cmake -B build \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ @@ -58,34 +80,6 @@ jobs: which pkt2flow pkt2flow -h || true # Show help (exits with non-zero) - static-analysis: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - libpcap-dev \ - libgoogle-glog-dev \ - libgtest-dev \ - cmake \ - build-essential \ - cppcheck \ - clang-format - - - name: Run cppcheck - run: | - cppcheck --enable=all --error-exitcode=1 --suppress=missingIncludeSystem \ - --suppress=unusedFunction \ - --inline-suppr \ - *.c *.h - - - name: Check code formatting - run: | - clang-format --dry-run --Werror *.c *.h - build-docs: runs-on: ubuntu-latest steps: @@ -99,44 +93,4 @@ jobs: - name: Generate documentation run: | doxygen --version - # Documentation generation can be added when Doxyfile is created - - cross-compile: - runs-on: ubuntu-latest - strategy: - matrix: - target: [x86_64, aarch64] - steps: - - uses: actions/checkout@v4 - - - name: Install cross-compilation tools - run: | - sudo apt-get update - sudo apt-get install -y gcc-aarch64-linux-gnu - - - name: Install dependencies - run: | - sudo apt-get install -y \ - libpcap-dev \ - libgoogle-glog-dev \ - cmake \ - build-essential - - - name: Configure CMake (x86_64) - if: matrix.target == 'x86_64' - run: | - cmake -B build \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_TESTS=OFF - - - name: Configure CMake (aarch64) - if: matrix.target == 'aarch64' - run: | - cmake -B build \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_TESTS=OFF \ - -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \ - -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ - - - name: Build - run: cmake --build build -j $(nproc) \ No newline at end of file + # Documentation generation can be added when Doxyfile is created \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index f47355c..87a969a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,9 +50,9 @@ find_package(glog REQUIRED) # Source files set(PKT2FLOW_SOURCES - pkt2flow.cpp - flow_db.cpp - utilities.c + src/pkt2flow.cpp + src/flow_db.cpp + src/utilities.cpp ) # Create the main executable @@ -66,7 +66,7 @@ target_link_libraries(pkt2flow # Include directories target_include_directories(pkt2flow PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/src ${PCAP_INCLUDE_DIRS} ) @@ -108,7 +108,7 @@ if(BUILD_TESTS) # Include directories for tests target_include_directories(pkt2flow_tests PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/src ${PCAP_INCLUDE_DIRS} ) @@ -120,12 +120,12 @@ if(BUILD_TESTS) # Create a library from the source files for testing add_library(pkt2flow_lib STATIC - flow_db.cpp - utilities.c + src/flow_db.cpp + src/utilities.cpp ) target_include_directories(pkt2flow_lib PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/src ${PCAP_INCLUDE_DIRS} ) @@ -140,6 +140,11 @@ if(BUILD_TESTS) target_link_libraries(pkt2flow_tests pkt2flow_lib) endif() +# Build example in examples/ +add_executable(example_pkt2flow examples/example_pkt2flow.c) +target_link_libraries(example_pkt2flow pkt2flow_lib) +target_include_directories(example_pkt2flow PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) + # CPack configuration for packaging include(CPack) set(CPACK_PACKAGE_NAME "pkt2flow") diff --git a/README.md b/README.md index ace6938..593599e 100644 --- a/README.md +++ b/README.md @@ -1,152 +1,126 @@ -pkt2flow -======== +# pkt2flow -by chenxm, Shanghai Jiao Tong Univ. -chenxm35@gmail.com +[![Build Status](https://github.com/caesar0301/pkt2flow/actions/workflows/ci.yml/badge.svg)](https://github.com/caesar0301/pkt2flow/actions) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macOS-blue)]() -2012-2024 +A simple, cross-platform utility to classify packets into flows using only the essential 4-tuple (src_ip, dst_ip, src_port, dst_port). Each flow is saved as a separate pcap file, named with its 4-tuple and the timestamp of its first packet. No payload reassembly or extra processing is performed—just pure flow separation for your analysis needs. -**©MIT LICENSED** +## Why pkt2flow? -A simple utility to classify packets into flows. It's so simple that only one task -is aimed to finish. +Existing tools like `tcpflow`, `tcpslice`, and `tcpsplit` either reduce trace volume or reassemble payloads, which may not fit all research or analysis needs. `pkt2flow` fills the gap by simply splitting packets into flows, making it ideal for deep packet inspection, flow classification, and traffic research. -For Deep Packet Inspection or flow classification, it's so common to analyze the -feature of one specific flow. I have make the attempt to use made-ready tools like -`tcpflows`, `tcpslice`, `tcpsplit`, but all these tools try to either decrease the -trace volume (under requirement) or resemble the packets into flow payloads (over -requirement). I have not found a simple tool to classify the packets into flows without -further processing. This is why this program is born. - -The inner function of this program behaves using the 4-tuple (src_ip, dst_ip, src_port, dst_port) -to seperate the packets into TCP or UDP flows. Each flow will be saved into a pcap -file named with 4-tuple and the timestamp of the first packet of the flow. The packets are -saved in the order as read from the source. Any further processing like TCP resembling is -not performed. The flow timeout is considered as 30 minutes which can be changed in pkt2flow.h. - -## Features - -- **Cross-platform**: Supports both Linux and macOS -- **Modern build system**: Uses CMake instead of SCons -- **Structured logging**: Integrated with Google glog for better debugging -- **Unit testing**: Comprehensive test suite using Google Test -- **CI/CD**: Automated testing with GitHub Actions -- **Static analysis**: Code quality checks with cppcheck - -How to compile ----------- - -This program now uses CMake as the build system. You can follow these steps to compile: +## Installation & Build ### Prerequisites **Ubuntu/Debian:** ```bash sudo apt-get update -sudo apt-get install -y \ - libpcap-dev \ - libgoogle-glog-dev \ - libgtest-dev \ - cmake \ - build-essential +sudo apt-get install -y libpcap-dev libgoogle-glog-dev libgtest-dev cmake build-essential ``` -**macOS:** +**macOS (Intel/Apple Silicon):** ```bash -brew install \ - libpcap \ - glog \ - googletest \ - cmake +brew install libpcap glog googletest cmake ``` +> **Note for Apple Silicon (M1/M2):** +> If you encounter issues with `libpcap` not being found, set: +> ```bash +> export PKG_CONFIG_PATH="/opt/homebrew/opt/libpcap/lib/pkgconfig" +> ``` -### Building +### Build Steps 1. Clone the repository: -```bash -git clone https://github.com/caesar0301/pkt2flow.git -cd pkt2flow -``` - + ```bash + git clone https://github.com/caesar0301/pkt2flow.git + cd pkt2flow + ``` 2. Configure and build: -```bash -mkdir build -cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -make -j$(nproc) -``` + ```bash + mkdir build && cd build + cmake .. -DCMAKE_BUILD_TYPE=Release + make + ``` +3. (Optional) Run tests: + ```bash + ctest --verbose + ``` +4. (Optional) Install: + ```bash + sudo make install + ``` + +#### Build Options +- `BUILD_TESTS`: Enable/disable unit tests (default: ON) +- `CMAKE_BUILD_TYPE`: Set build type (Debug, Release, etc.) -3. Run tests (optional): +Example: ```bash -ctest --verbose +cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=OFF ``` -4. Install (optional): +## Usage + ```bash -sudo make install +./pkt2flow [-huvx] [-o outdir] pcapfile + +Options: + -h Print this help and exit + -u Also dump (U)DP flows + -v Also dump in(v)alid TCP flows without the SYN option + -x Also dump non-UDP/non-TCP IP flows + -o (O)utput directory ``` -### Build Options +### Example -- `BUILD_TESTS`: Enable/disable unit tests (default: ON) -- `CMAKE_BUILD_TYPE`: Set build type (Debug, Release, RelWithDebInfo, MinSizeRel) - -Example: ```bash -cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=OFF +./pkt2flow -u -o output_flows/ input.pcap ``` +This will split all TCP and UDP flows from `input.pcap` into separate files in the `output_flows/` directory. ## Logging -The application now uses Google glog for structured logging. Logs are written to: +Google glog is used for structured logging. Logs are written to: - Console (stderr) for important messages -- Log files in `./logs/` directory for detailed debugging +- Log files in `./logs/` for detailed debugging -You can control logging verbosity with environment variables: +Control verbosity with environment variables: ```bash export GLOG_v=2 # Increase verbosity export GLOG_log_dir=/custom/log/path ./pkt2flow input.pcap ``` -Usage --------- -```bash -Usage: ./pkt2flow [-huvx] [-o outdir] pcapfile - - Options: - -h print this help and exit - -u also dump (U)DP flows - -v also dump the in(v)alid TCP flows without the SYN option - -x also dump non-UDP/non-TCP IP flows - -o (o)utput directory -``` - ## Development ### Running Tests - ```bash -# Run all tests cd build && ctest - -# Run specific test -./build/pkt2flow_tests --gtest_filter="FlowDbTest.*" +# Or run a specific test: +./pkt2flow_tests --gtest_filter="FlowDbTest.*" ``` ### Code Quality - -The project includes static analysis tools: - ```bash -# Run cppcheck cppcheck --enable=all *.c *.h - -# Check formatting clang-format --dry-run *.c *.h ``` -### Contributing +## Troubleshooting + +- **libpcap not found on macOS (Apple Silicon):** + Set the PKG_CONFIG_PATH before running cmake: + ```bash + export PKG_CONFIG_PATH="/opt/homebrew/opt/libpcap/lib/pkgconfig" + ``` + +- **Linker warnings about /usr/local/opt/llvm/lib:** + These are harmless if you are not using a custom LLVM install. You can ignore them or remove the path from your environment. + +## Contributing 1. Fork the repository 2. Create a feature branch @@ -154,8 +128,7 @@ clang-format --dry-run *.c *.h 4. Ensure all tests pass and code follows the style guide 5. Submit a pull request -Contributors --------- +## Contributors [![Contributors](https://contrib.rocks/image?repo=caesar0301/pkt2flow "pkt2flow contributors")](https://github.com/caesar0301/pkt2flow/graphs/contributors) diff --git a/examples/example_pkt2flow.c b/examples/example_pkt2flow.c new file mode 100644 index 0000000..af45ff6 --- /dev/null +++ b/examples/example_pkt2flow.c @@ -0,0 +1,43 @@ +#include "pkt2flow.h" +#include +#include + +int main(void) { + // Initialize the hash table + init_hash_table(); + + // Create a 6-tuple for a flow (IPv4, TCP, src: 1.2.3.4:1234, dst: 5.6.7.8:80) + struct af_6tuple tuple; + memset(&tuple, 0, sizeof(tuple)); + tuple.af_family = AF_INET; + tuple.protocol = 6; // TCP + tuple.ip1.v4.s_addr = 0x04030201; // 1.2.3.4 + tuple.ip2.v4.s_addr = 0x08070605; // 5.6.7.8 + tuple.port1 = 1234; + tuple.port2 = 80; + tuple.is_vlan = 0; + + // Register the flow + struct ip_pair *pair = register_ip_pair(tuple); + if (pair) { + printf("Flow registered: src=%x:%u dst=%x:%u\n", + pair->af_6tuple.ip1.v4.s_addr, pair->af_6tuple.port1, + pair->af_6tuple.ip2.v4.s_addr, pair->af_6tuple.port2); + } + + // Find the flow + struct ip_pair *found = find_ip_pair(tuple); + if (found) { + printf("Flow found!\n"); + } else { + printf("Flow not found!\n"); + } + + // Reset the flow's packet dump file + reset_pdf(&pair->pdf); + + // Free the hash table + free_hash_table(); + + return 0; +} \ No newline at end of file diff --git a/flow_db.cpp b/flow_db.cpp deleted file mode 100644 index ba4e313..0000000 --- a/flow_db.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* pkt2flow - * Xiaming Chen (chen_xm@sjtu.edu.cn) - * - * Copyright (c) 2012 - * Copyright (c) 2014 Sven Eckelmann - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * The names and trademarks of copyright holders may not be used in - * advertising or publicity pertaining to the software without specific - * prior permission. Title to copyright in this software and any - * associated documentation will at all times remain with the copyright - * holders. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "pkt2flow.h" - -struct ip_pair *pairs [HASH_TBL_SIZE]; - -void init_hash_table(void) -{ - VLOG(1) << "Initializing hash table with size " << HASH_TBL_SIZE; - memset(pairs, 0, sizeof(struct ip_pair *) * HASH_TBL_SIZE); -} - -void free_hash_table(void) -{ - size_t b; - struct ip_pair *curp; - int total_pairs = 0; - - VLOG(1) << "Freeing hash table"; - for (b = 0; b < HASH_TBL_SIZE; b++) { - while (pairs[b]) { - curp = pairs[b]; - pairs[b] = pairs[b]->next; - reset_pdf(&curp->pdf); - free(curp); - total_pairs++; - } - } - - LOG(INFO) << "Freed " << total_pairs << " flow pairs from hash table"; - init_hash_table(); -} - -static unsigned int hashf(const void *key, size_t sz, unsigned int hash) -{ - unsigned int h; - unsigned int i; - const unsigned char *array = static_cast(key); - - h = hash; - for (i = 0; i < sz; i++) - h = (h * HASH_MULTIPLIER) + array[i]; - return (h); -} - -void reset_pdf(struct pkt_dump_file *f) -{ - f->pkts = 0; - f->start_time = 0; - f->status = STS_UNSET; - free(f->file_name); - f->file_name = NULL; -} - -static unsigned int hash_5tuple(struct af_6tuple af_6tuple) -{ - unsigned int hash1 = 0; - unsigned int hash2 = 0; - int i; - - for (i = 0; i < 2; i++) { - if (i == 0) { - switch (af_6tuple.af_family) { - case AF_INET: - hash1 = hashf(&af_6tuple.ip1.v4, 4, hash1); - hash1 = hashf(&af_6tuple.ip2.v4, 4, hash1); - break; - case AF_INET6: - hash1 = hashf(&af_6tuple.ip1.v6, 16, hash1); - hash1 = hashf(&af_6tuple.ip2.v6, 16, hash1); - break; - } - if (af_6tuple.port1) - hash1 = hashf(&af_6tuple.port1, 2, hash1); - if (af_6tuple.port2) - hash1 = hashf(&af_6tuple.port2, 2, hash1); - } else { - switch (af_6tuple.af_family) { - case AF_INET: - hash2 = hashf(&af_6tuple.ip2.v4, 4, hash2); - hash2 = hashf(&af_6tuple.ip1.v4, 4, hash2); - break; - case AF_INET6: - hash2 = hashf(&af_6tuple.ip2.v6, 16, hash2); - hash2 = hashf(&af_6tuple.ip1.v6, 16, hash2); - break; - } - if (af_6tuple.port2) - hash2 = hashf(&af_6tuple.port2, 2, hash2); - if (af_6tuple.port1) - hash2 = hashf(&af_6tuple.port1, 2, hash2); - } - } - - return (hash1 + hash2) % HASH_TBL_SIZE; -} - -static int compare_5tuple(struct af_6tuple af1, struct af_6tuple af2) -{ - if (af1.af_family != af2.af_family) - return 0; - - if (af1.protocol != af2.protocol) - return 0; - - switch (af1.af_family) { - case AF_INET: - if (memcmp(&af1.ip1.v4, &af2.ip1.v4, sizeof(af1.ip1.v4)) == 0 && - memcmp(&af1.ip2.v4, &af2.ip2.v4, sizeof(af1.ip2.v4)) == 0 && - af1.port1 == af2.port1 && af1.port2 == af2.port2) - return 1; - if (memcmp(&af1.ip1.v4, &af2.ip2.v4, sizeof(af1.ip1.v4)) == 0 && - memcmp(&af1.ip2.v4, &af2.ip1.v4, sizeof(af1.ip2.v4)) == 0 && - af1.port1 == af2.port2 && af1.port2 == af2.port1) - return 1; - break; - case AF_INET6: - if (memcmp(&af1.ip1.v6, &af2.ip1.v6, sizeof(af1.ip1.v6)) == 0 && - memcmp(&af1.ip2.v6, &af2.ip2.v6, sizeof(af1.ip2.v6)) == 0 && - af1.port1 == af2.port1 && af1.port2 == af2.port2) - return 1; - if (memcmp(&af1.ip1.v6, &af2.ip2.v6, sizeof(af1.ip1.v6)) == 0 && - memcmp(&af1.ip2.v6, &af2.ip1.v6, sizeof(af1.ip2.v6)) == 0 && - af1.port1 == af2.port2 && af1.port2 == af2.port1) - return 1; - break; - } - - return 0; -} - -struct ip_pair *find_ip_pair(struct af_6tuple af_6tuple) -{ - struct ip_pair *p; - unsigned int hash; - - hash = hash_5tuple(af_6tuple); - if (pairs[hash]) { - for (p = pairs [hash]; p != NULL; p = p->next) { - if (compare_5tuple(p->af_6tuple, af_6tuple)) - return p; - } - } - - return NULL; -} - -struct ip_pair *register_ip_pair(struct af_6tuple af_6tuple) -{ - struct ip_pair *newp; - unsigned int hash; - - hash = hash_5tuple(af_6tuple); - - newp = (struct ip_pair *)malloc(sizeof(struct ip_pair)); - if (!newp) { - LOG(FATAL) << "Not enough memory to allocate another IP pair"; - exit(1); - } - - newp->af_6tuple = af_6tuple; - newp->pdf.file_name = NULL; - newp->next = pairs [hash]; - pairs [hash] = newp; - reset_pdf((struct pkt_dump_file *) & (newp->pdf)); - - VLOG(2) << "Registered new IP pair in hash bucket " << hash - << " for protocol " << af_6tuple.protocol; - - return newp; -} diff --git a/format_code.sh b/format_code.sh new file mode 100755 index 0000000..8ae24f3 --- /dev/null +++ b/format_code.sh @@ -0,0 +1,151 @@ +#!/bin/bash + +# format_code.sh - Format C and C++ code using clang-format +# Usage: ./format_code.sh [--check] [--fix] + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Default values +CHECK_ONLY=false +FIX_CODE=false + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --check) + CHECK_ONLY=true + shift + ;; + --fix) + FIX_CODE=true + shift + ;; + -h|--help) + echo "Usage: $0 [--check] [--fix]" + echo " --check: Only check formatting without making changes" + echo " --fix: Fix formatting issues (default if no flag provided)" + echo " -h, --help: Show this help message" + exit 0 + ;; + *) + echo "Unknown option: $1" + echo "Use -h or --help for usage information" + exit 1 + ;; + esac +done + +# If no flags provided, default to fixing +if [[ "$CHECK_ONLY" == false && "$FIX_CODE" == false ]]; then + FIX_CODE=true +fi + +# Check if clang-format is available +if ! command -v clang-format &> /dev/null; then + echo -e "${RED}Error: clang-format is not installed${NC}" + echo "Install it with:" + echo " macOS: brew install clang-format" + echo " Ubuntu/Debian: sudo apt-get install clang-format" + exit 1 +fi + +# Get clang-format version +CLANG_FORMAT_VERSION=$(clang-format --version | head -n1 | cut -d' ' -f3 | cut -d'.' -f1) +echo -e "${YELLOW}Using clang-format version: $(clang-format --version | head -n1)${NC}" + +# Define source directories and file patterns +SOURCE_DIRS=("src" "tests" "examples") +FILE_PATTERNS=("*.cpp" "*.h" "*.hpp") + +# Collect all files to format +FILES_TO_FORMAT=() +for dir in "${SOURCE_DIRS[@]}"; do + if [[ -d "$dir" ]]; then + for pattern in "${FILE_PATTERNS[@]}"; do + while IFS= read -r -d '' file; do + FILES_TO_FORMAT+=("$file") + done < <(find "$dir" -name "$pattern" -type f -print0 2>/dev/null) + done + fi +done + +if [[ ${#FILES_TO_FORMAT[@]} -eq 0 ]]; then + echo -e "${YELLOW}No source files found to format${NC}" + exit 0 +fi + +echo -e "${YELLOW}Found ${#FILES_TO_FORMAT[@]} files to process:${NC}" +for file in "${FILES_TO_FORMAT[@]}"; do + echo " $file" +done +echo + +# Function to check formatting +check_formatting() { + local has_issues=false + + for file in "${FILES_TO_FORMAT[@]}"; do + if [[ -f "$file" ]]; then + if ! clang-format --dry-run --Werror "$file" >/dev/null 2>&1; then + echo -e "${RED}✗ $file${NC}" + has_issues=true + else + echo -e "${GREEN}✓ $file${NC}" + fi + fi + done + + if [[ "$has_issues" == true ]]; then + echo -e "\n${RED}Formatting issues found!${NC}" + echo "Run '$0 --fix' to automatically fix formatting issues." + exit 1 + else + echo -e "\n${GREEN}All files are properly formatted!${NC}" + fi +} + +# Function to fix formatting +fix_formatting() { + local fixed_count=0 + + for file in "${FILES_TO_FORMAT[@]}"; do + if [[ -f "$file" ]]; then + echo -n "Formatting $file... " + + # Create a temporary file + temp_file=$(mktemp) + + # Format the file + if clang-format "$file" > "$temp_file"; then + # Check if the file actually changed + if ! cmp -s "$file" "$temp_file"; then + mv "$temp_file" "$file" + echo -e "${GREEN}✓${NC}" + ((fixed_count++)) + else + echo -e "${YELLOW}no changes${NC}" + fi + else + echo -e "${RED}✗ error${NC}" + rm -f "$temp_file" + fi + fi + done + + echo -e "\n${GREEN}Formatting complete! Fixed $fixed_count files.${NC}" +} + +# Main execution +if [[ "$CHECK_ONLY" == true ]]; then + echo -e "${YELLOW}Checking code formatting...${NC}" + check_formatting +elif [[ "$FIX_CODE" == true ]]; then + echo -e "${YELLOW}Fixing code formatting...${NC}" + fix_formatting +fi \ No newline at end of file diff --git a/pkt2flow.cpp b/pkt2flow.cpp deleted file mode 100644 index 0aac307..0000000 --- a/pkt2flow.cpp +++ /dev/null @@ -1,519 +0,0 @@ -/* pkt2flow - * Xiaming Chen (chen_xm@sjtu.edu.cn) - * - * Copyright (c) 2012 - * Copyright (C) 2014 Sven Eckelmann - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * The names and trademarks of copyright holders may not be used in - * advertising or publicity pertaining to the software without specific - * prior permission. Title to copyright in this software and any - * associated documentation will at all times remain with the copyright - * holders. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pkt2flow.h" - -static uint32_t dump_allowed; -static char *readfile = NULL; -//char *interface = NULL; -static const char *outputdir = "pkt2flow.out"; -static pcap_t *inputp = NULL; - -static void usage(char *progname) -{ - fprintf(stderr, "Name: %s\n", __GLOBAL_NAME__); - fprintf(stderr, "Version: %s\n", __SOURCE_VERSION__); - fprintf(stderr, "Author: %s\n", __AUTHOR__); - fprintf(stderr, "Program to seperate the packets into flows (UDP or TCP).\n\n"); - fprintf(stderr, "Usage: %s [-huvx] [-o outdir] pcapfile\n\n", progname); - fprintf(stderr, "Options:\n"); - fprintf(stderr, " -h print this help and exit\n"); - fprintf(stderr, " -u also dump (U)DP flows\n"); - fprintf(stderr, " -v also dump the in(v)alid TCP flows without the SYN option\n"); - fprintf(stderr, " -x also dump non-UDP/non-TCP IP flows\n"); - fprintf(stderr, " -o (o)utput directory\n"); -} - - -static void parseargs(int argc, char *argv[]) -{ - int opt; - const char *optstr = "uvxo:h"; - while ((opt = getopt(argc, argv, optstr)) != -1) { - switch (opt) { - case 'h': - usage(argv [0]); - exit(-1); - case 'o': - outputdir = optarg; - break; - case 'u': - dump_allowed |= DUMP_UDP_ALLOWED; - break; - case 'v': - dump_allowed |= DUMP_TCP_NOSYN_ALLOWED; - break; - case 'x': - dump_allowed |= DUMP_OTHER_ALLOWED; - break; - default: - usage(argv [0]); - exit(-1); - } - } - - if (optind < argc) - readfile = argv[optind]; - if (readfile == NULL) { - fprintf(stderr, "pcap file not given\n"); - usage(argv[0]); - exit(1); - } -} - -static void open_trace_file(void) -{ - char errbuf [PCAP_ERRBUF_SIZE]; - - LOG(INFO) << "Opening trace file: " << readfile; - inputp = pcap_open_offline(readfile, errbuf); - if (!inputp) { - LOG(FATAL) << "Error opening tracefile " << readfile << ": " << errbuf; - exit(1); - } - LOG(INFO) << "Successfully opened trace file"; -} - -static char *resemble_file_path(struct pkt_dump_file *pdf) -{ - char *cwd = getcwd(NULL, 0); // backup the current working directory - char *folder = NULL; - int check; - struct stat statBuff; - int ret; - const char *type_folder; - char *outputpath; - - switch (pdf->status) { - case STS_TCP_SYN: - type_folder = "tcp_syn"; - break; - case STS_TCP_NOSYN: - type_folder = "tcp_nosyn"; - break; - case STS_UDP: - type_folder = "udp"; - break; - case STS_UNSET: - type_folder = "others"; - break; - } - - ret = asprintf(&outputpath, "%s/%s", outputdir, type_folder); - if (ret < 0) - return NULL; - - // Check the path folder and create the folders if they are not there - ret = stat(outputpath, &statBuff); - if (!(ret != -1 && S_ISDIR(statBuff.st_mode))) { - /* handle absolute path */ - if (outputpath[0] == '/') - chdir("/"); - - folder = strtok(outputpath, "/"); - while (folder != NULL) { - ret = stat(folder, &statBuff); - if (!(ret != -1 && S_ISDIR(statBuff.st_mode))) { - check = mkdir(folder, S_IRWXU); - if (check != 0) { - LOG(ERROR) << "Failed to create directory: " << folder; - exit(-1); - } - } - chdir(folder); - folder = strtok(NULL, "/"); - } - } - chdir(cwd); - free(cwd); - free(outputpath); - - ret = asprintf(&outputpath, "%s/%s/%s", outputdir, type_folder, - pdf->file_name); - if (ret < 0) - return NULL; - - return outputpath; -} - -static int pcap_handle_layer4(struct af_6tuple *af_6tuple, const u_char *bytes, - size_t len, uint8_t proto) -{ - struct tcphdr *tcphdr; - struct udphdr *udphdr; - - switch (proto) { - case IPPROTO_UDP: - if (len < sizeof(*udphdr)) - return -1; - - udphdr = (struct udphdr *)bytes; - af_6tuple->protocol = IPPROTO_UDP; -#ifdef darwin - af_6tuple->port1 = ntohs(udphdr->uh_sport); - af_6tuple->port2 = ntohs(udphdr->uh_dport); -#else - af_6tuple->port1 = ntohs(udphdr->source); - af_6tuple->port2 = ntohs(udphdr->dest); -#endif - return 0; - case IPPROTO_TCP: - if (len < sizeof(*tcphdr)) - return -1; - - tcphdr = (struct tcphdr *)bytes; - af_6tuple->protocol = IPPROTO_TCP; -#ifdef darwin - af_6tuple->port1 = ntohs(tcphdr->th_sport); - af_6tuple->port2 = ntohs(tcphdr->th_dport); -#else - af_6tuple->port1 = ntohs(tcphdr->source); - af_6tuple->port2 = ntohs(tcphdr->dest); -#endif - -#ifdef darwin - if (tcphdr->th_flags == TH_SYN) -#else - if (tcphdr->syn) -#endif - return 1; - else - return 0; - default: - af_6tuple->protocol = 0; - af_6tuple->port1 = 0; - af_6tuple->port2 = 0; - return 0; - } -} - -static int pcap_handle_ipv4(struct af_6tuple *af_6tuple, const u_char *bytes, - size_t len) -{ - struct ip *iphdr; - - if (len < sizeof(*iphdr)) - return -1; - - iphdr = (struct ip *)bytes; - if (len > ntohs(iphdr->ip_len)) - len = ntohs(iphdr->ip_len); - - if (len < 4 * iphdr->ip_hl) - return -1; - - len -= 4 * iphdr->ip_hl; - bytes += 4 * iphdr->ip_hl; - - af_6tuple->af_family = AF_INET; - af_6tuple->ip1.v4 = iphdr->ip_src; - af_6tuple->ip2.v4 = iphdr->ip_dst; - - return pcap_handle_layer4(af_6tuple, bytes, len, iphdr->ip_p); -} - -static int pcap_handle_ipv6(struct af_6tuple *af_6tuple, const u_char *bytes, - size_t len) -{ - struct ip6_hdr *iphdr; - struct ip6_opt *opthdr; - int curheader = 255; - uint8_t nexthdr; - - while (1) { - switch (curheader) { - case 255: - if (len < sizeof(*iphdr)) - return -1; - iphdr = (struct ip6_hdr *)bytes; - bytes += sizeof(*iphdr); - len -= sizeof(*iphdr); - nexthdr = iphdr->ip6_ctlun.ip6_un1.ip6_un1_nxt; - - af_6tuple->af_family = AF_INET6; - af_6tuple->ip1.v6 = iphdr->ip6_src; - af_6tuple->ip2.v6 = iphdr->ip6_dst; - break; - case IPPROTO_HOPOPTS: - case IPPROTO_ROUTING: - case IPPROTO_DSTOPTS: - if (len < sizeof(*opthdr)) - return -1; - nexthdr = bytes[0]; - - opthdr = (struct ip6_opt *)bytes; - if (len < ((1u + opthdr->ip6o_len) * 8u)) - return -1; - bytes += (1u + opthdr->ip6o_len) * 8u; - len -= (1u + opthdr->ip6o_len) * 8u; - break; - case IPPROTO_FRAGMENT: - if (len < 1) - return -1; - nexthdr = bytes[0]; - if (len < 8) - return -1; - bytes += 8; - len -= 8; - break; - case IPPROTO_NONE: - return -1; - default: - return pcap_handle_layer4(af_6tuple, bytes, len, - nexthdr); - }; - curheader = nexthdr; - } -} - -static int pcap_handle_ip(struct af_6tuple *af_6tuple, const u_char *bytes, - size_t len) -{ - if (len < 1) - return -1; - - /* IP header */ - if ((bytes[0] >> 4) == 4) - return pcap_handle_ipv4(af_6tuple, bytes, len); - - if ((bytes[0] >> 4) == 6) - return pcap_handle_ipv6(af_6tuple, bytes, len); - - return -1; -} - -static int pcap_handle_ethernet(struct af_6tuple *af_6tuple, - const struct pcap_pkthdr *h, - const u_char *bytes) -{ - size_t len = h->caplen; - struct ether_header *ethhdr; - - /* Ethernet header */ - if (len < sizeof(*ethhdr)) - return - 1; - - ethhdr = (struct ether_header *)bytes; - len -= sizeof(*ethhdr); - bytes += sizeof(*ethhdr); - - struct vlan_header *vlanhdr; - uint16_t etype = ntohs(ethhdr->ether_type); - - /* VLAN header, IEEE 802.1Q */ - if (etype == ETHERTYPE_VLAN) { - vlanhdr = (struct vlan_header *)bytes; - etype = ntohs(vlanhdr->tpid); - bytes += sizeof(*vlanhdr); - len -= sizeof(*vlanhdr); - af_6tuple->is_vlan = 1; - } else { - af_6tuple->is_vlan = 0; - } - - if (etype != ETHERTYPE_IP && etype != ETHERTYPE_IPV6) - return -1; - - return pcap_handle_ip(af_6tuple, bytes, len); -} - -static void process_trace(void) -{ - struct pcap_pkthdr hdr; - int syn_detected; - struct ip_pair *pair = NULL; - pcap_dumper_t *dumper = NULL; - u_char *pkt = NULL; - char *fname = NULL; - struct af_6tuple af_6tuple; - - while ((pkt = (u_char *)pcap_next(inputp, &hdr)) != NULL) { - syn_detected = pcap_handle_ethernet(&af_6tuple, &hdr, pkt); - if (syn_detected < 0) - continue; - - switch (af_6tuple.protocol) { - case IPPROTO_TCP: - /* always accept tcp */ - break; - case IPPROTO_UDP: - if (!isset_bits(dump_allowed, DUMP_UDP_ALLOWED)) - // Omit the UDP packets - continue; - break; - default: - if (!isset_bits(dump_allowed, DUMP_OTHER_ALLOWED)) - // Omit the other packets - continue; - break; - } - - // Search for the ip_pair of specific six-tuple - pair = find_ip_pair(af_6tuple); - if (pair == NULL) { - if ((af_6tuple.protocol == IPPROTO_TCP) && - !syn_detected && - !isset_bits(dump_allowed, DUMP_TCP_NOSYN_ALLOWED)) { - // No SYN detected and don't create a new flow - continue; - } - pair = register_ip_pair(af_6tuple); - switch (af_6tuple.protocol) { - case IPPROTO_TCP: - if (syn_detected) - pair->pdf.status = STS_TCP_SYN; - else - pair->pdf.status = STS_TCP_NOSYN; - break; - case IPPROTO_UDP: - pair->pdf.status = STS_UDP; - break; - default: - pair->pdf.status = STS_UNSET; - break; - } - } - - // Fill the ip_pair with information of the current flow - if (pair->pdf.pkts == 0) { - // A new flow item reated with empty dump file object - fname = new_file_name(af_6tuple, hdr.ts.tv_sec); - pair->pdf.file_name = fname; - pair->pdf.start_time = hdr.ts.tv_sec; - } else { - if (hdr.ts.tv_sec - pair->pdf.start_time >= FLOW_TIMEOUT) { - // Rest the pair to start a new flow with the same 6-tuple, but with - // the different name and timestamp - reset_pdf(&(pair->pdf)); - fname = new_file_name(af_6tuple, hdr.ts.tv_sec); - pair->pdf.file_name = fname; - pair->pdf.start_time = hdr.ts.tv_sec; - - switch (af_6tuple.protocol) { - case IPPROTO_TCP: - if (syn_detected) - pair->pdf.status = STS_TCP_SYN; - else - pair->pdf.status = STS_TCP_NOSYN; - break; - case IPPROTO_UDP: - pair->pdf.status = STS_UDP; - break; - default: - pair->pdf.status = STS_UNSET; - break; - } - } - } - - // Dump the packet to file and close the file - fname = resemble_file_path(&(pair->pdf)); - FILE *f = fopen(fname, "ab"); - if (!f) { - LOG(ERROR) << "Failed to open output file: " << fname; - goto skip_dump_write; - } - - if (pair->pdf.pkts == 0) { - // Call the pcap_dump_fopen to write the pcap file header first - // to the new file - dumper = pcap_dump_fopen(inputp, f); - } else { - // Write the packet only - dumper = (pcap_dumper_t *)f; - } - // Dump the packet now - pcap_dump((u_char *)dumper, &hdr, (unsigned char *)pkt); - pcap_dump_close(dumper); - -skip_dump_write: - free(fname); - pair->pdf.pkts++; - } -} - - -static void close_trace_files(void) -{ - pcap_close(inputp); -} - - -int main(int argc, char *argv[]) -{ - // Initialize Google Logging - google::InitGoogleLogging(argv[0]); - - // Set logging to stderr and files - FLAGS_alsologtostderr = true; - FLAGS_log_dir = "./logs"; - - LOG(INFO) << "Starting " << __GLOBAL_NAME__ << " version " << __SOURCE_VERSION__; - - parseargs(argc, argv); - open_trace_file(); - init_hash_table(); - - LOG(INFO) << "Processing trace file: " << readfile; - LOG(INFO) << "Output directory: " << outputdir; - - process_trace(); - close_trace_files(); - free_hash_table(); - - LOG(INFO) << "Processing completed successfully"; - - // Cleanup Google Logging - google::ShutdownGoogleLogging(); - - exit(0); -} diff --git a/sample/200722_win_scale_examples_anon.pcapng b/sample/200722_win_scale_examples_anon.pcapng new file mode 100644 index 0000000000000000000000000000000000000000..0cc70372b42107506d53947c4986e79262f2fd63 GIT binary patch literal 2976 zcmb7`O=uHA7=~xJv8mP4q#)KtaY2OGn3$hUny5&zY1)e#e|xEvHd!<^M2RuhpJ*be z7Y_=0uwHtyg<3qcdT1}=$$~`KvHEVj+9GW06GUv=kP4P6_+s-65$X-W@v>mEuCuZu8i@LeGgv zG%WZ;AJP5i^ME30n8>_Z_$06jPn@LFFC`-(sdHyA&~>IY5U@L(9=|ov;%~KzO?{bZQ|v&6wn9} zO`&Fzu6z=IF!bm$%oPny`t0l8iAH=l%`bkbTR{k~TQbH~R8(=Cj)yzYSRBYv@>XU` zygVsS&GQIpCe4-R7sx`UKHJc=wreT~OiQ|M@yoqjfd?#L>a}^{!V93jBf#^(Jx-NA zvnfpVr4;%sz}y`)==i#&_%pdCTpKkV#xGzXgL``R{8qTesbbhn5@jSc8TG)Zh1H~J z^k}u5gX=3fcc)7nm3QX&FnV|2qwsDwc;^TjxcSew@bz%~i}TRdmM-ei;M}1cwWl#@0QUyfzJLs@ZFx>HU@>Z8kZRkk&^89V`Je&zp|bjv zKQ);t*0st%n`wwLT$+q}V3ggb?Uza%m2c*B3j1d$tnh6fd>aQXv$fB`8an>fr`N9! zUMT9(;GCnJkS50n9N0_$&XH&H6*!D+U_XE}0{4Ub8IiBMamZ$YosFE)af8Vip$t)z zVJtWQ*#VYc8hwiOya2gO&|IFe{F3+Sn$Epfi+!rfMJ#aSXPmD$`;iPJM|a^F*Pki% zO?hWrAMhCYyrtf8f4uYQv&aG_$KFTfXWSexinXTEJMWP3cu|w_UuWFKSc#+Z&iwfe z*0hfYPVxT%ddGpD{kWIr+F2RN^-O(gyjIks!2!>JgPy55aK`1@SR8p>ubXYW#>nP9 u_3fv^xBZ}rXJ;d4jIbFWDC37FqwF*8-~!7pjo!grc*x}d%_Z + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * The names and trademarks of copyright holders may not be used in + * advertising or publicity pertaining to the software without specific + * prior permission. Title to copyright in this software and any + * associated documentation will at all times remain with the copyright + * holders. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "pkt2flow.h" +#include +#include +#include +#include +#include +#include +#include + +struct ip_pair *pairs[HASH_TBL_SIZE]; + +void init_hash_table(void) { + VLOG(1) << "Initializing hash table with size " << HASH_TBL_SIZE; + memset(pairs, 0, sizeof(struct ip_pair *) * HASH_TBL_SIZE); +} + +void free_hash_table(void) { + size_t b; + struct ip_pair *curp; + int total_pairs = 0; + + VLOG(1) << "Freeing hash table"; + for (b = 0; b < HASH_TBL_SIZE; b++) { + while (pairs[b]) { + curp = pairs[b]; + pairs[b] = pairs[b]->next; + reset_pdf(&curp->pdf); + free(curp); + total_pairs++; + } + } + + LOG(INFO) << "Freed " << total_pairs << " flow pairs from hash table"; + init_hash_table(); +} + +static unsigned int hashf(const void *key, size_t sz, unsigned int hash) { + unsigned int h; + unsigned int i; + const unsigned char *array = static_cast(key); + + h = hash; + for (i = 0; i < sz; i++) + h = (h * HASH_MULTIPLIER) + array[i]; + return (h); +} + +void reset_pdf(struct pkt_dump_file *f) { + f->pkts = 0; + f->start_time = 0; + f->status = STS_UNSET; + free(f->file_name); + f->file_name = NULL; +} + +static unsigned int hash_5tuple(struct af_6tuple af_6tuple) { + unsigned int hash1 = 0; + unsigned int hash2 = 0; + int i; + + for (i = 0; i < 2; i++) { + if (i == 0) { + switch (af_6tuple.af_family) { + case AF_INET: + hash1 = hashf(&af_6tuple.ip1.v4, 4, hash1); + hash1 = hashf(&af_6tuple.ip2.v4, 4, hash1); + break; + case AF_INET6: + hash1 = hashf(&af_6tuple.ip1.v6, 16, hash1); + hash1 = hashf(&af_6tuple.ip2.v6, 16, hash1); + break; + } + if (af_6tuple.port1) + hash1 = hashf(&af_6tuple.port1, 2, hash1); + if (af_6tuple.port2) + hash1 = hashf(&af_6tuple.port2, 2, hash1); + } else { + switch (af_6tuple.af_family) { + case AF_INET: + hash2 = hashf(&af_6tuple.ip2.v4, 4, hash2); + hash2 = hashf(&af_6tuple.ip1.v4, 4, hash2); + break; + case AF_INET6: + hash2 = hashf(&af_6tuple.ip2.v6, 16, hash2); + hash2 = hashf(&af_6tuple.ip1.v6, 16, hash2); + break; + } + if (af_6tuple.port2) + hash2 = hashf(&af_6tuple.port2, 2, hash2); + if (af_6tuple.port1) + hash2 = hashf(&af_6tuple.port1, 2, hash2); + } + } + + return (hash1 + hash2) % HASH_TBL_SIZE; +} + +static int compare_5tuple(struct af_6tuple af1, struct af_6tuple af2) { + if (af1.af_family != af2.af_family) + return 0; + + if (af1.protocol != af2.protocol) + return 0; + + switch (af1.af_family) { + case AF_INET: + if (memcmp(&af1.ip1.v4, &af2.ip1.v4, sizeof(af1.ip1.v4)) == 0 && + memcmp(&af1.ip2.v4, &af2.ip2.v4, sizeof(af1.ip2.v4)) == 0 && + af1.port1 == af2.port1 && af1.port2 == af2.port2) + return 1; + if (memcmp(&af1.ip1.v4, &af2.ip2.v4, sizeof(af1.ip1.v4)) == 0 && + memcmp(&af1.ip2.v4, &af2.ip1.v4, sizeof(af1.ip2.v4)) == 0 && + af1.port1 == af2.port2 && af1.port2 == af2.port1) + return 1; + break; + case AF_INET6: + if (memcmp(&af1.ip1.v6, &af2.ip1.v6, sizeof(af1.ip1.v6)) == 0 && + memcmp(&af1.ip2.v6, &af2.ip2.v6, sizeof(af1.ip2.v6)) == 0 && + af1.port1 == af2.port1 && af1.port2 == af2.port2) + return 1; + if (memcmp(&af1.ip1.v6, &af2.ip2.v6, sizeof(af1.ip1.v6)) == 0 && + memcmp(&af1.ip2.v6, &af2.ip1.v6, sizeof(af1.ip2.v6)) == 0 && + af1.port1 == af2.port2 && af1.port2 == af2.port1) + return 1; + break; + } + + return 0; +} + +struct ip_pair *find_ip_pair(struct af_6tuple af_6tuple) { + struct ip_pair *p; + unsigned int hash; + + hash = hash_5tuple(af_6tuple); + if (pairs[hash]) { + for (p = pairs[hash]; p != NULL; p = p->next) { + if (compare_5tuple(p->af_6tuple, af_6tuple)) + return p; + } + } + + return NULL; +} + +struct ip_pair *register_ip_pair(struct af_6tuple af_6tuple) { + struct ip_pair *newp; + unsigned int hash; + + hash = hash_5tuple(af_6tuple); + + newp = new struct ip_pair; + if (!newp) { + LOG(FATAL) << "Not enough memory to allocate another IP pair"; + exit(1); + } + + newp->af_6tuple = af_6tuple; + newp->pdf.file_name = NULL; + newp->next = pairs[hash]; + pairs[hash] = newp; + reset_pdf(&(newp->pdf)); + + VLOG(2) << "Registered new IP pair in hash bucket " << hash + << " for protocol " << af_6tuple.protocol; + + return newp; +} diff --git a/src/pkt2flow.cpp b/src/pkt2flow.cpp new file mode 100644 index 0000000..904815f --- /dev/null +++ b/src/pkt2flow.cpp @@ -0,0 +1,473 @@ +/* pkt2flow + * + * Copyright (c) 2012 Xiaming Chen + * Copyright (c) 2014 Sven Eckelmann + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * The names and trademarks of copyright holders may not be used in + * advertising or publicity pertaining to the software without specific + * prior permission. Title to copyright in this software and any + * associated documentation will at all times remain with the copyright + * holders. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "pkt2flow.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint32_t dump_allowed; +static char *readfile = NULL; +// char *interface = NULL; +static const char *outputdir = "pkt2flow.out"; +static pcap_t *inputp = NULL; + +static void usage(const char *progname) { + fprintf(stderr, "Name: %s\n", __GLOBAL_NAME__); + fprintf(stderr, "Version: %s\n", __SOURCE_VERSION__); + fprintf(stderr, "Author: %s\n", __AUTHOR__); + fprintf(stderr, + "Program to seperate the packets into flows (UDP or TCP).\n\n"); + fprintf(stderr, "Usage: %s [-huvx] [-o outdir] pcapfile\n\n", progname); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -h print this help and exit\n"); + fprintf(stderr, " -u also dump (U)DP flows\n"); + fprintf( + stderr, + " -v also dump the in(v)alid TCP flows without the SYN option\n"); + fprintf(stderr, " -x also dump non-UDP/non-TCP IP flows\n"); + fprintf(stderr, " -o (o)utput directory\n"); +} + +static void parseargs(int argc, char *argv[]) { + int opt; + const char *optstr = "uvxo:h"; + while ((opt = getopt(argc, argv, optstr)) != -1) { + switch (opt) { + case 'h': + usage(argv[0]); + exit(-1); + case 'o': + outputdir = optarg; + break; + case 'u': + dump_allowed |= DUMP_UDP_ALLOWED; + break; + case 'v': + dump_allowed |= DUMP_TCP_NOSYN_ALLOWED; + break; + case 'x': + dump_allowed |= DUMP_OTHER_ALLOWED; + break; + default: + usage(argv[0]); + exit(-1); + } + } + + if (optind < argc) + readfile = argv[optind]; + if (readfile == NULL) { + fprintf(stderr, "pcap file not given\n"); + usage(argv[0]); + exit(1); + } +} + +static void open_trace_file(void) { + char errbuf[PCAP_ERRBUF_SIZE]; + + LOG(INFO) << "Opening trace file: " << readfile; + inputp = pcap_open_offline(readfile, errbuf); + if (!inputp) { + LOG(FATAL) << "Error opening tracefile " << readfile << ": " << errbuf; + exit(1); + } + LOG(INFO) << "Successfully opened trace file"; +} + +static char *resemble_file_path(struct pkt_dump_file *pdf) { + char *cwd = getcwd(NULL, 0); // backup the current working directory + char *folder = NULL; + struct stat statBuff; + int ret; + const char *type_folder; + char *outputpath; + + switch (pdf->status) { + case STS_TCP_SYN: + type_folder = "tcp_syn"; + break; + case STS_TCP_NOSYN: + type_folder = "tcp_nosyn"; + break; + case STS_UDP: + type_folder = "udp"; + break; + case STS_UNSET: + type_folder = "others"; + break; + } + + ret = asprintf(&outputpath, "%s/%s", outputdir, type_folder); + if (ret < 0) + return NULL; + + // Check the path folder and create the folders if they are not there + ret = stat(outputpath, &statBuff); + if (!(ret != -1 && S_ISDIR(statBuff.st_mode))) { + /* handle absolute path */ + if (outputpath[0] == '/') + chdir("/"); + + folder = strtok(outputpath, "/"); + while (folder != NULL) { + ret = stat(folder, &statBuff); + if (!(ret != -1 && S_ISDIR(statBuff.st_mode))) { + int check = mkdir(folder, S_IRWXU); + if (check != 0) { + LOG(ERROR) << "Failed to create directory: " << folder; + exit(-1); + } + } + chdir(folder); + folder = strtok(NULL, "/"); + } + } + chdir(cwd); + free(cwd); + free(outputpath); + + ret = + asprintf(&outputpath, "%s/%s/%s", outputdir, type_folder, pdf->file_name); + if (ret < 0) + return NULL; + + return outputpath; +} + +static int pcap_handle_layer4(struct af_6tuple *af_6tuple, const u_char *bytes, + size_t len, uint8_t proto) { + switch (proto) { + case IPPROTO_UDP: { + if (len < sizeof(struct udphdr)) + return -1; + struct udphdr *udphdr = + reinterpret_cast(const_cast(bytes)); + af_6tuple->protocol = IPPROTO_UDP; +#ifdef darwin + af_6tuple->port1 = ntohs(udphdr->uh_sport); + af_6tuple->port2 = ntohs(udphdr->uh_dport); +#else + af_6tuple->port1 = ntohs(udphdr->source); + af_6tuple->port2 = ntohs(udphdr->dest); +#endif + break; + } + case IPPROTO_TCP: { + if (len < sizeof(struct tcphdr)) + return -1; + struct tcphdr *tcphdr = + reinterpret_cast(const_cast(bytes)); + af_6tuple->protocol = IPPROTO_TCP; +#ifdef darwin + af_6tuple->port1 = ntohs(tcphdr->th_sport); + af_6tuple->port2 = ntohs(tcphdr->th_dport); +#else + af_6tuple->port1 = ntohs(tcphdr->source); + af_6tuple->port2 = ntohs(tcphdr->dest); +#endif + break; + } + default: + af_6tuple->protocol = proto; + af_6tuple->port1 = 0; + af_6tuple->port2 = 0; + break; + } + return 0; +} + +static int pcap_handle_ipv4(struct af_6tuple *af_6tuple, const u_char *bytes, + size_t len) { + if (len < sizeof(struct ip)) + return -1; + struct ip *iphdr = reinterpret_cast(const_cast(bytes)); + if (len > ntohs(iphdr->ip_len)) + len = ntohs(iphdr->ip_len); + + if (len < 4 * iphdr->ip_hl) + return -1; + + len -= 4 * iphdr->ip_hl; + bytes += 4 * iphdr->ip_hl; + + af_6tuple->af_family = AF_INET; + af_6tuple->ip1.v4 = iphdr->ip_src; + af_6tuple->ip2.v4 = iphdr->ip_dst; + + return pcap_handle_layer4(af_6tuple, bytes, len, iphdr->ip_p); +} + +static int pcap_handle_ipv6(struct af_6tuple *af_6tuple, const u_char *bytes, + size_t len) { + if (len < sizeof(struct ip6_hdr)) + return -1; + struct ip6_hdr *iphdr = + reinterpret_cast(const_cast(bytes)); + const struct ip6_opt *opthdr = nullptr; + int curheader = 255; + uint8_t nexthdr; + + while (1) { + switch (curheader) { + case 255: + if (len < sizeof(*iphdr)) + return -1; + bytes += sizeof(*iphdr); + len -= sizeof(*iphdr); + nexthdr = iphdr->ip6_ctlun.ip6_un1.ip6_un1_nxt; + + af_6tuple->af_family = AF_INET6; + af_6tuple->ip1.v6 = iphdr->ip6_src; + af_6tuple->ip2.v6 = iphdr->ip6_dst; + break; + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: + if (len < sizeof(*opthdr)) + return -1; + nexthdr = bytes[0]; + + opthdr = reinterpret_cast(bytes); + if (len < ((1u + opthdr->ip6o_len) * 8u)) + return -1; + bytes += (1u + opthdr->ip6o_len) * 8u; + len -= (1u + opthdr->ip6o_len) * 8u; + break; + case IPPROTO_FRAGMENT: + if (len < 1) + return -1; + nexthdr = bytes[0]; + if (len < 8) + return -1; + bytes += 8; + len -= 8; + break; + case IPPROTO_NONE: + return -1; + default: + return pcap_handle_layer4(af_6tuple, bytes, len, nexthdr); + }; + curheader = nexthdr; + } +} + +static int pcap_handle_ip(struct af_6tuple *af_6tuple, const u_char *bytes, + size_t len) { + if (len < 1) + return -1; + + /* IP header */ + if ((bytes[0] >> 4) == 4) + return pcap_handle_ipv4(af_6tuple, bytes, len); + + if ((bytes[0] >> 4) == 6) + return pcap_handle_ipv6(af_6tuple, bytes, len); + + return -1; +} + +static int pcap_handle_ethernet(struct af_6tuple *af_6tuple, + const struct pcap_pkthdr *h, + const u_char *bytes) { + if (h->caplen < sizeof(struct ether_header)) + return -1; + struct ether_header *ethhdr = + reinterpret_cast(const_cast(bytes)); + size_t len = h->caplen; + struct vlan_header *vlanhdr = nullptr; + uint16_t etype = ntohs(ethhdr->ether_type); + + /* VLAN header, IEEE 802.1Q */ + if (etype == ETHERTYPE_VLAN) { + vlanhdr = + reinterpret_cast(const_cast(bytes)); + etype = ntohs(vlanhdr->tpid); + bytes += sizeof(*vlanhdr); + len -= sizeof(*vlanhdr); + af_6tuple->is_vlan = 1; + } else { + af_6tuple->is_vlan = 0; + } + + if (etype != ETHERTYPE_IP && etype != ETHERTYPE_IPV6) + return -1; + + return pcap_handle_ip(af_6tuple, bytes, len); +} + +static void process_trace(void) { + struct pcap_pkthdr hdr; + int syn_detected; + struct ip_pair *pair = NULL; + pcap_dumper_t *dumper = NULL; + const u_char *pkt = NULL; + char *fname = NULL; + struct af_6tuple af_6tuple; + + while ((pkt = pcap_next(inputp, &hdr)) != NULL) { + syn_detected = pcap_handle_ethernet(&af_6tuple, &hdr, pkt); + if (syn_detected < 0) + continue; + + switch (af_6tuple.protocol) { + case IPPROTO_TCP: + /* always accept tcp */ + break; + case IPPROTO_UDP: + if (!isset_bits(dump_allowed, DUMP_UDP_ALLOWED)) + // Omit the UDP packets + continue; + break; + default: + if (!isset_bits(dump_allowed, DUMP_OTHER_ALLOWED)) + // Omit the other packets + continue; + break; + } + + // Search for the ip_pair of specific six-tuple + pair = find_ip_pair(af_6tuple); + if (pair == NULL) { + if ((af_6tuple.protocol == IPPROTO_TCP) && !syn_detected && + !isset_bits(dump_allowed, DUMP_TCP_NOSYN_ALLOWED)) { + // No SYN detected and don't create a new flow + continue; + } + pair = register_ip_pair(af_6tuple); + switch (af_6tuple.protocol) { + case IPPROTO_TCP: + if (syn_detected) + pair->pdf.status = STS_TCP_SYN; + else + pair->pdf.status = STS_TCP_NOSYN; + break; + case IPPROTO_UDP: + pair->pdf.status = STS_UDP; + break; + default: + pair->pdf.status = STS_UNSET; + break; + } + } + + // Generate the file name for the flow + if (pair->pdf.file_name == NULL) { + pair->pdf.file_name = new_file_name(af_6tuple, hdr.ts.tv_sec); + pair->pdf.start_time = hdr.ts.tv_sec; + } + + // Open the file for writing + fname = resemble_file_path(&pair->pdf); + if (fname == NULL) { + LOG(ERROR) << "Failed to generate file path for flow"; + continue; + } + + FILE *f = fopen(fname, "ab"); + if (f == NULL) { + LOG(ERROR) << "Failed to open file for writing: " << fname; + free(fname); + continue; + } + + // Write the packet to the file + if (pair->pdf.pkts == 0) { + // First packet, write pcap file header + dumper = pcap_dump_open(inputp, fname); + if (dumper == NULL) { + LOG(ERROR) << "Failed to create pcap dumper for: " << fname; + fclose(f); + free(fname); + continue; + } + } else { + // Write the packet only + dumper = reinterpret_cast(f); + } + // Dump the packet now + pcap_dump(reinterpret_cast(dumper), &hdr, pkt); + pcap_dump_close(dumper); + + pair->pdf.pkts++; + free(fname); + } +} + +static void close_trace_files(void) { pcap_close(inputp); } + +int main(int argc, char *argv[]) { + // Initialize Google Logging + google::InitGoogleLogging(argv[0]); + + // Set logging to stderr and files + FLAGS_alsologtostderr = true; + FLAGS_log_dir = "./logs"; + + LOG(INFO) << "Starting " << __GLOBAL_NAME__ << " version " + << __SOURCE_VERSION__; + + parseargs(argc, argv); + open_trace_file(); + init_hash_table(); + + LOG(INFO) << "Processing trace file: " << readfile; + LOG(INFO) << "Output directory: " << outputdir; + + process_trace(); + close_trace_files(); + free_hash_table(); + + LOG(INFO) << "Processing completed successfully"; + + // Cleanup Google Logging + google::ShutdownGoogleLogging(); + + exit(0); +} diff --git a/pkt2flow.h b/src/pkt2flow.h similarity index 75% rename from pkt2flow.h rename to src/pkt2flow.h index e79c0cf..829010c 100644 --- a/pkt2flow.h +++ b/src/pkt2flow.h @@ -1,8 +1,7 @@ /* pkt2flow - * Xiaming Chen (chen_xm@sjtu.edu.cn) * - * Copyright (c) 2012 + * Copyright (c) 2012 Xiaming Chen * Copyright (c) 2014 Sven Eckelmann * * Permission is hereby granted, free of charge, to any person @@ -35,69 +34,73 @@ #ifndef PKT2FLOW_H #define PKT2FLOW_H -#include #include #include +#include #ifdef __cplusplus extern "C" { #endif -#define __SOURCE_VERSION__ "1.2" -#define __AUTHOR__ "X. Chen (chenxm35@gmail.com)" -#define __GLOBAL_NAME__ "pkt2flow" -#define FLOW_TIMEOUT 1800 // seconds -#define HASH_MULTIPLIER 37 -#define HASH_TBL_SIZE 48611 +#define __SOURCE_VERSION__ "1.2" +#define __AUTHOR__ "X. Chen (chenxm35@gmail.com)" +#define __GLOBAL_NAME__ "pkt2flow" +#define FLOW_TIMEOUT 1800 // seconds +#define HASH_MULTIPLIER 37 +#define HASH_TBL_SIZE 48611 #define BIT(bitnr) (1ULL << (bitnr)) -#define isset_bits(x, bitmask) ({ typeof(bitmask) _bitmask = (bitmask); \ - (_bitmask & (x)) == _bitmask; }) +#ifndef PKT2FLOW_INLINE_ISSET_BITS +#define PKT2FLOW_INLINE_ISSET_BITS +static inline int isset_bits(uint64_t x, uint64_t bitmask) { + return (x & bitmask) == bitmask; +} +#endif enum dump_allow_flags { - DUMP_OTHER_ALLOWED = BIT(0), - DUMP_TCP_NOSYN_ALLOWED = BIT(1), - DUMP_UDP_ALLOWED = BIT(2), + DUMP_OTHER_ALLOWED = BIT(0), + DUMP_TCP_NOSYN_ALLOWED = BIT(1), + DUMP_UDP_ALLOWED = BIT(2), }; enum pkt_dump_file_status { - STS_UNSET, - STS_TCP_SYN, - STS_TCP_NOSYN, - STS_UDP, + STS_UNSET, + STS_TCP_SYN, + STS_TCP_NOSYN, + STS_UDP, }; struct pkt_dump_file { - char *file_name; - unsigned long pkts; + char *file_name; + unsigned long pkts; - enum pkt_dump_file_status status; - unsigned long start_time; + enum pkt_dump_file_status status; + unsigned long start_time; }; /* VLAN header, IEEE 802.1Q */ struct vlan_header { - uint16_t tci; /* Priority 3bits, CFI 1bit, ID 12bits */ - uint16_t tpid; + uint16_t tci; /* Priority 3bits, CFI 1bit, ID 12bits */ + uint16_t tpid; }; union ip_address { - struct in_addr v4; - struct in6_addr v6; + struct in_addr v4; + struct in6_addr v6; }; struct af_6tuple { - int af_family; - int protocol; - union ip_address ip1, ip2; - uint16_t port1, port2; - uint8_t is_vlan; + int af_family; + int protocol; + union ip_address ip1, ip2; + uint16_t port1, port2; + uint8_t is_vlan; }; struct ip_pair { - struct af_6tuple af_6tuple; - struct pkt_dump_file pdf; - struct ip_pair *next; + struct af_6tuple af_6tuple; + struct pkt_dump_file pdf; + struct ip_pair *next; }; /* pkt2flow.c */ @@ -151,4 +154,3 @@ void reset_pdf(struct pkt_dump_file *f); #endif #endif /* PKT2FLOW_H */ - diff --git a/utilities.c b/src/utilities.cpp similarity index 64% rename from utilities.c rename to src/utilities.cpp index c59b44b..73feeab 100644 --- a/utilities.c +++ b/src/utilities.cpp @@ -31,47 +31,45 @@ * SOFTWARE. */ +#include "pkt2flow.h" #include #include #include #include #include #include -#include "pkt2flow.h" -char *new_file_name(struct af_6tuple af_6tuple, unsigned long timestamp) -{ - char *fname; - char src_ip_str[INET6_ADDRSTRLEN]; - char dst_ip_str[INET6_ADDRSTRLEN]; - int ret; +char *new_file_name(struct af_6tuple af_6tuple, unsigned long timestamp) { + char *fname; + char src_ip_str[INET6_ADDRSTRLEN]; + char dst_ip_str[INET6_ADDRSTRLEN]; + int ret; - switch (af_6tuple.af_family) { - case AF_INET: - inet_ntop(AF_INET, &af_6tuple.ip1.v4, src_ip_str, INET_ADDRSTRLEN); - inet_ntop(AF_INET, &af_6tuple.ip2.v4, dst_ip_str, INET_ADDRSTRLEN); - break; - case AF_INET6: - inet_ntop(AF_INET6, &af_6tuple.ip1.v6, src_ip_str, INET6_ADDRSTRLEN); - inet_ntop(AF_INET6, &af_6tuple.ip2.v6, dst_ip_str, INET6_ADDRSTRLEN); - break; - } + switch (af_6tuple.af_family) { + case AF_INET: + inet_ntop(AF_INET, &af_6tuple.ip1.v4, src_ip_str, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &af_6tuple.ip2.v4, dst_ip_str, INET_ADDRSTRLEN); + break; + case AF_INET6: + inet_ntop(AF_INET6, &af_6tuple.ip1.v6, src_ip_str, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &af_6tuple.ip2.v6, dst_ip_str, INET6_ADDRSTRLEN); + break; + } - switch (af_6tuple.is_vlan) { - case 0: - ret = asprintf(&fname, "%s_%"PRIu16"_%s_%"PRIu16"_%lu.pcap", - src_ip_str, af_6tuple.port1, dst_ip_str, af_6tuple.port2, - timestamp); - break; - case 1: - ret = asprintf(&fname, "%s_%"PRIu16"_%s_%"PRIu16"_%lu_vlan.pcap", - src_ip_str, af_6tuple.port1, dst_ip_str, af_6tuple.port2, - timestamp); - break; - } + switch (af_6tuple.is_vlan) { + case 0: + ret = asprintf(&fname, "%s_%" PRIu16 "_%s_%" PRIu16 "_%lu.pcap", src_ip_str, + af_6tuple.port1, dst_ip_str, af_6tuple.port2, timestamp); + break; + case 1: + ret = asprintf(&fname, "%s_%" PRIu16 "_%s_%" PRIu16 "_%lu_vlan.pcap", + src_ip_str, af_6tuple.port1, dst_ip_str, af_6tuple.port2, + timestamp); + break; + } - if (ret < 0) - fname = NULL; + if (ret < 0) + fname = NULL; - return fname; + return fname; } diff --git a/tests/test_flow_db.cpp b/tests/test_flow_db.cpp index c684be5..e82126a 100644 --- a/tests/test_flow_db.cpp +++ b/tests/test_flow_db.cpp @@ -1,8 +1,8 @@ -#include -#include -#include #include #include +#include +#include +#include extern "C" { #include "pkt2flow.h" @@ -10,126 +10,126 @@ extern "C" { class FlowDbTest : public ::testing::Test { protected: - void SetUp() override { - init_hash_table(); - } - - void TearDown() override { - free_hash_table(); - } - - struct af_6tuple create_test_tuple_ipv4(const char* src_ip, uint16_t src_port, - const char* dst_ip, uint16_t dst_port, - int protocol = IPPROTO_TCP) { - struct af_6tuple tuple; - memset(&tuple, 0, sizeof(tuple)); - - tuple.af_family = AF_INET; - tuple.protocol = protocol; - tuple.port1 = src_port; - tuple.port2 = dst_port; - tuple.is_vlan = 0; - - inet_pton(AF_INET, src_ip, &tuple.ip1.v4); - inet_pton(AF_INET, dst_ip, &tuple.ip2.v4); - - return tuple; - } + void SetUp() override { init_hash_table(); } + + void TearDown() override { free_hash_table(); } + + struct af_6tuple create_test_tuple_ipv4(const char *src_ip, uint16_t src_port, + const char *dst_ip, uint16_t dst_port, + int protocol = IPPROTO_TCP) { + struct af_6tuple tuple; + memset(&tuple, 0, sizeof(tuple)); + + tuple.af_family = AF_INET; + tuple.protocol = protocol; + tuple.port1 = src_port; + tuple.port2 = dst_port; + tuple.is_vlan = 0; + + inet_pton(AF_INET, src_ip, &tuple.ip1.v4); + inet_pton(AF_INET, dst_ip, &tuple.ip2.v4); + + return tuple; + } }; TEST_F(FlowDbTest, InitHashTable) { - // Hash table should be initialized with NULL pointers - for (int i = 0; i < HASH_TBL_SIZE; i++) { - extern struct ip_pair *pairs[]; - EXPECT_EQ(pairs[i], nullptr); - } + // Hash table should be initialized with NULL pointers + for (int i = 0; i < HASH_TBL_SIZE; i++) { + extern struct ip_pair *pairs[]; + EXPECT_EQ(pairs[i], nullptr); + } } TEST_F(FlowDbTest, RegisterAndFindIpPair) { - // Create a test tuple - auto tuple = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.2", 8080); - - // Initially, the pair should not exist - struct ip_pair *pair = find_ip_pair(tuple); - EXPECT_EQ(pair, nullptr); - - // Register the pair - pair = register_ip_pair(tuple); - EXPECT_NE(pair, nullptr); - EXPECT_EQ(pair->af_6tuple.af_family, AF_INET); - EXPECT_EQ(pair->af_6tuple.protocol, IPPROTO_TCP); - EXPECT_EQ(pair->af_6tuple.port1, 80); - EXPECT_EQ(pair->af_6tuple.port2, 8080); - - // Now we should be able to find it - struct ip_pair *found_pair = find_ip_pair(tuple); - EXPECT_EQ(found_pair, pair); + // Create a test tuple + auto tuple = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.2", 8080); + + // Initially, the pair should not exist + struct ip_pair *pair = find_ip_pair(tuple); + EXPECT_EQ(pair, nullptr); + + // Register the pair + pair = register_ip_pair(tuple); + EXPECT_NE(pair, nullptr); + EXPECT_EQ(pair->af_6tuple.af_family, AF_INET); + EXPECT_EQ(pair->af_6tuple.protocol, IPPROTO_TCP); + EXPECT_EQ(pair->af_6tuple.port1, 80); + EXPECT_EQ(pair->af_6tuple.port2, 8080); + + // Now we should be able to find it + struct ip_pair *found_pair = find_ip_pair(tuple); + EXPECT_EQ(found_pair, pair); } TEST_F(FlowDbTest, BidirectionalFlow) { - // Create forward tuple - auto forward_tuple = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.2", 8080); - - // Create reverse tuple (swapped IPs and ports) - auto reverse_tuple = create_test_tuple_ipv4("192.168.1.2", 8080, "192.168.1.1", 80); - - // Register forward flow - struct ip_pair *forward_pair = register_ip_pair(forward_tuple); - EXPECT_NE(forward_pair, nullptr); - - // Finding with reverse tuple should return the same pair (bidirectional) - struct ip_pair *reverse_pair = find_ip_pair(reverse_tuple); - EXPECT_EQ(forward_pair, reverse_pair); + // Create forward tuple + auto forward_tuple = + create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.2", 8080); + + // Create reverse tuple (swapped IPs and ports) + auto reverse_tuple = + create_test_tuple_ipv4("192.168.1.2", 8080, "192.168.1.1", 80); + + // Register forward flow + struct ip_pair *forward_pair = register_ip_pair(forward_tuple); + EXPECT_NE(forward_pair, nullptr); + + // Finding with reverse tuple should return the same pair (bidirectional) + struct ip_pair *reverse_pair = find_ip_pair(reverse_tuple); + EXPECT_EQ(forward_pair, reverse_pair); } TEST_F(FlowDbTest, MultipleFlows) { - // Create multiple different flows - auto tuple1 = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.2", 8080); - auto tuple2 = create_test_tuple_ipv4("192.168.1.3", 443, "192.168.1.4", 9090); - auto tuple3 = create_test_tuple_ipv4("10.0.0.1", 22, "10.0.0.2", 2222, IPPROTO_UDP); - - // Register all flows - struct ip_pair *pair1 = register_ip_pair(tuple1); - struct ip_pair *pair2 = register_ip_pair(tuple2); - struct ip_pair *pair3 = register_ip_pair(tuple3); - - EXPECT_NE(pair1, nullptr); - EXPECT_NE(pair2, nullptr); - EXPECT_NE(pair3, nullptr); - - // All pairs should be different - EXPECT_NE(pair1, pair2); - EXPECT_NE(pair1, pair3); - EXPECT_NE(pair2, pair3); - - // Find each flow - EXPECT_EQ(find_ip_pair(tuple1), pair1); - EXPECT_EQ(find_ip_pair(tuple2), pair2); - EXPECT_EQ(find_ip_pair(tuple3), pair3); + // Create multiple different flows + auto tuple1 = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.2", 8080); + auto tuple2 = create_test_tuple_ipv4("192.168.1.3", 443, "192.168.1.4", 9090); + auto tuple3 = + create_test_tuple_ipv4("10.0.0.1", 22, "10.0.0.2", 2222, IPPROTO_UDP); + + // Register all flows + struct ip_pair *pair1 = register_ip_pair(tuple1); + struct ip_pair *pair2 = register_ip_pair(tuple2); + struct ip_pair *pair3 = register_ip_pair(tuple3); + + EXPECT_NE(pair1, nullptr); + EXPECT_NE(pair2, nullptr); + EXPECT_NE(pair3, nullptr); + + // All pairs should be different + EXPECT_NE(pair1, pair2); + EXPECT_NE(pair1, pair3); + EXPECT_NE(pair2, pair3); + + // Find each flow + EXPECT_EQ(find_ip_pair(tuple1), pair1); + EXPECT_EQ(find_ip_pair(tuple2), pair2); + EXPECT_EQ(find_ip_pair(tuple3), pair3); } TEST_F(FlowDbTest, ResetPdf) { - struct pkt_dump_file pdf; - pdf.pkts = 100; - pdf.start_time = 12345; - pdf.status = STS_TCP_SYN; - pdf.file_name = strdup("test_file.pcap"); - - reset_pdf(&pdf); - - EXPECT_EQ(pdf.pkts, 0); - EXPECT_EQ(pdf.start_time, 0); - EXPECT_EQ(pdf.status, STS_UNSET); - EXPECT_EQ(pdf.file_name, nullptr); + struct pkt_dump_file pdf; + pdf.pkts = 100; + pdf.start_time = 12345; + pdf.status = STS_TCP_SYN; + pdf.file_name = strdup("test_file.pcap"); + + reset_pdf(&pdf); + + EXPECT_EQ(pdf.pkts, 0); + EXPECT_EQ(pdf.start_time, 0); + EXPECT_EQ(pdf.status, STS_UNSET); + EXPECT_EQ(pdf.file_name, nullptr); } TEST_F(FlowDbTest, UdpFlow) { - auto udp_tuple = create_test_tuple_ipv4("192.168.1.1", 53, "8.8.8.8", 53, IPPROTO_UDP); - - struct ip_pair *pair = register_ip_pair(udp_tuple); - EXPECT_NE(pair, nullptr); - EXPECT_EQ(pair->af_6tuple.protocol, IPPROTO_UDP); - - struct ip_pair *found_pair = find_ip_pair(udp_tuple); - EXPECT_EQ(found_pair, pair); + auto udp_tuple = + create_test_tuple_ipv4("192.168.1.1", 53, "8.8.8.8", 53, IPPROTO_UDP); + + struct ip_pair *pair = register_ip_pair(udp_tuple); + EXPECT_NE(pair, nullptr); + EXPECT_EQ(pair->af_6tuple.protocol, IPPROTO_UDP); + + struct ip_pair *found_pair = find_ip_pair(udp_tuple); + EXPECT_EQ(found_pair, pair); } \ No newline at end of file diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 8e2f686..17e1e90 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -1,18 +1,18 @@ -#include #include +#include int main(int argc, char **argv) { - // Initialize Google Logging - google::InitGoogleLogging(argv[0]); - - // Initialize Google Test - ::testing::InitGoogleTest(&argc, argv); - - // Run all tests - int result = RUN_ALL_TESTS(); - - // Cleanup Google Logging - google::ShutdownGoogleLogging(); - - return result; + // Initialize Google Logging + google::InitGoogleLogging(argv[0]); + + // Initialize Google Test + ::testing::InitGoogleTest(&argc, argv); + + // Run all tests + int result = RUN_ALL_TESTS(); + + // Cleanup Google Logging + google::ShutdownGoogleLogging(); + + return result; } \ No newline at end of file diff --git a/tests/test_utilities.cpp b/tests/test_utilities.cpp index 770a560..4552d85 100644 --- a/tests/test_utilities.cpp +++ b/tests/test_utilities.cpp @@ -1,9 +1,9 @@ -#include -#include -#include #include -#include #include +#include +#include +#include +#include extern "C" { #include "pkt2flow.h" @@ -11,168 +11,171 @@ extern "C" { class UtilitiesTest : public ::testing::Test { protected: - struct af_6tuple create_test_tuple_ipv4(const char* src_ip, uint16_t src_port, - const char* dst_ip, uint16_t dst_port, - int protocol = IPPROTO_TCP, - uint8_t is_vlan = 0) { - struct af_6tuple tuple; - memset(&tuple, 0, sizeof(tuple)); - - tuple.af_family = AF_INET; - tuple.protocol = protocol; - tuple.port1 = src_port; - tuple.port2 = dst_port; - tuple.is_vlan = is_vlan; - - inet_pton(AF_INET, src_ip, &tuple.ip1.v4); - inet_pton(AF_INET, dst_ip, &tuple.ip2.v4); - - return tuple; - } - - struct af_6tuple create_test_tuple_ipv6(const char* src_ip, uint16_t src_port, - const char* dst_ip, uint16_t dst_port, - int protocol = IPPROTO_TCP, - uint8_t is_vlan = 0) { - struct af_6tuple tuple; - memset(&tuple, 0, sizeof(tuple)); - - tuple.af_family = AF_INET6; - tuple.protocol = protocol; - tuple.port1 = src_port; - tuple.port2 = dst_port; - tuple.is_vlan = is_vlan; - - inet_pton(AF_INET6, src_ip, &tuple.ip1.v6); - inet_pton(AF_INET6, dst_ip, &tuple.ip2.v6); - - return tuple; - } + struct af_6tuple create_test_tuple_ipv4(const char *src_ip, uint16_t src_port, + const char *dst_ip, uint16_t dst_port, + int protocol = IPPROTO_TCP, + uint8_t is_vlan = 0) { + struct af_6tuple tuple; + memset(&tuple, 0, sizeof(tuple)); + + tuple.af_family = AF_INET; + tuple.protocol = protocol; + tuple.port1 = src_port; + tuple.port2 = dst_port; + tuple.is_vlan = is_vlan; + + inet_pton(AF_INET, src_ip, &tuple.ip1.v4); + inet_pton(AF_INET, dst_ip, &tuple.ip2.v4); + + return tuple; + } + + struct af_6tuple create_test_tuple_ipv6(const char *src_ip, uint16_t src_port, + const char *dst_ip, uint16_t dst_port, + int protocol = IPPROTO_TCP, + uint8_t is_vlan = 0) { + struct af_6tuple tuple; + memset(&tuple, 0, sizeof(tuple)); + + tuple.af_family = AF_INET6; + tuple.protocol = protocol; + tuple.port1 = src_port; + tuple.port2 = dst_port; + tuple.is_vlan = is_vlan; + + inet_pton(AF_INET6, src_ip, &tuple.ip1.v6); + inet_pton(AF_INET6, dst_ip, &tuple.ip2.v6); + + return tuple; + } }; TEST_F(UtilitiesTest, NewFileNameIPv4) { - auto tuple = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.2", 8080); - unsigned long timestamp = 1234567890; - - char *filename = new_file_name(tuple, timestamp); - ASSERT_NE(filename, nullptr); - - // Check that filename contains expected components - EXPECT_TRUE(strstr(filename, "192.168.1.1") != nullptr); - EXPECT_TRUE(strstr(filename, "192.168.1.2") != nullptr); - EXPECT_TRUE(strstr(filename, "80") != nullptr); - EXPECT_TRUE(strstr(filename, "8080") != nullptr); - EXPECT_TRUE(strstr(filename, "1234567890") != nullptr); - EXPECT_TRUE(strstr(filename, ".pcap") != nullptr); - - // Should not contain vlan suffix for non-VLAN traffic - EXPECT_FALSE(strstr(filename, "_vlan.pcap") != nullptr); - - free(filename); + auto tuple = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.2", 8080); + unsigned long timestamp = 1234567890; + + char *filename = new_file_name(tuple, timestamp); + ASSERT_NE(filename, nullptr); + + // Check that filename contains expected components + EXPECT_TRUE(strstr(filename, "192.168.1.1") != nullptr); + EXPECT_TRUE(strstr(filename, "192.168.1.2") != nullptr); + EXPECT_TRUE(strstr(filename, "80") != nullptr); + EXPECT_TRUE(strstr(filename, "8080") != nullptr); + EXPECT_TRUE(strstr(filename, "1234567890") != nullptr); + EXPECT_TRUE(strstr(filename, ".pcap") != nullptr); + + // Should not contain vlan suffix for non-VLAN traffic + EXPECT_FALSE(strstr(filename, "_vlan.pcap") != nullptr); + + free(filename); } TEST_F(UtilitiesTest, NewFileNameIPv4WithVlan) { - auto tuple = create_test_tuple_ipv4("10.0.0.1", 443, "10.0.0.2", 9090, IPPROTO_TCP, 1); - unsigned long timestamp = 9876543210; - - char *filename = new_file_name(tuple, timestamp); - ASSERT_NE(filename, nullptr); - - // Check that filename contains expected components including VLAN - EXPECT_TRUE(strstr(filename, "10.0.0.1") != nullptr); - EXPECT_TRUE(strstr(filename, "10.0.0.2") != nullptr); - EXPECT_TRUE(strstr(filename, "443") != nullptr); - EXPECT_TRUE(strstr(filename, "9090") != nullptr); - EXPECT_TRUE(strstr(filename, "9876543210") != nullptr); - EXPECT_TRUE(strstr(filename, "_vlan.pcap") != nullptr); - - free(filename); + auto tuple = + create_test_tuple_ipv4("10.0.0.1", 443, "10.0.0.2", 9090, IPPROTO_TCP, 1); + unsigned long timestamp = 9876543210; + + char *filename = new_file_name(tuple, timestamp); + ASSERT_NE(filename, nullptr); + + // Check that filename contains expected components including VLAN + EXPECT_TRUE(strstr(filename, "10.0.0.1") != nullptr); + EXPECT_TRUE(strstr(filename, "10.0.0.2") != nullptr); + EXPECT_TRUE(strstr(filename, "443") != nullptr); + EXPECT_TRUE(strstr(filename, "9090") != nullptr); + EXPECT_TRUE(strstr(filename, "9876543210") != nullptr); + EXPECT_TRUE(strstr(filename, "_vlan.pcap") != nullptr); + + free(filename); } TEST_F(UtilitiesTest, NewFileNameIPv6) { - auto tuple = create_test_tuple_ipv6("2001:db8::1", 80, "2001:db8::2", 8080); - unsigned long timestamp = 1111111111; - - char *filename = new_file_name(tuple, timestamp); - ASSERT_NE(filename, nullptr); - - // Check that filename contains expected components - EXPECT_TRUE(strstr(filename, "2001:db8::1") != nullptr); - EXPECT_TRUE(strstr(filename, "2001:db8::2") != nullptr); - EXPECT_TRUE(strstr(filename, "80") != nullptr); - EXPECT_TRUE(strstr(filename, "8080") != nullptr); - EXPECT_TRUE(strstr(filename, "1111111111") != nullptr); - EXPECT_TRUE(strstr(filename, ".pcap") != nullptr); - - free(filename); + auto tuple = create_test_tuple_ipv6("2001:db8::1", 80, "2001:db8::2", 8080); + unsigned long timestamp = 1111111111; + + char *filename = new_file_name(tuple, timestamp); + ASSERT_NE(filename, nullptr); + + // Check that filename contains expected components + EXPECT_TRUE(strstr(filename, "2001:db8::1") != nullptr); + EXPECT_TRUE(strstr(filename, "2001:db8::2") != nullptr); + EXPECT_TRUE(strstr(filename, "80") != nullptr); + EXPECT_TRUE(strstr(filename, "8080") != nullptr); + EXPECT_TRUE(strstr(filename, "1111111111") != nullptr); + EXPECT_TRUE(strstr(filename, ".pcap") != nullptr); + + free(filename); } TEST_F(UtilitiesTest, NewFileNameIPv6WithVlan) { - auto tuple = create_test_tuple_ipv6("fe80::1", 22, "fe80::2", 2222, IPPROTO_TCP, 1); - unsigned long timestamp = 5555555555; - - char *filename = new_file_name(tuple, timestamp); - ASSERT_NE(filename, nullptr); - - // Check that filename contains expected components including VLAN - EXPECT_TRUE(strstr(filename, "fe80::1") != nullptr); - EXPECT_TRUE(strstr(filename, "fe80::2") != nullptr); - EXPECT_TRUE(strstr(filename, "22") != nullptr); - EXPECT_TRUE(strstr(filename, "2222") != nullptr); - EXPECT_TRUE(strstr(filename, "5555555555") != nullptr); - EXPECT_TRUE(strstr(filename, "_vlan.pcap") != nullptr); - - free(filename); + auto tuple = + create_test_tuple_ipv6("fe80::1", 22, "fe80::2", 2222, IPPROTO_TCP, 1); + unsigned long timestamp = 5555555555; + + char *filename = new_file_name(tuple, timestamp); + ASSERT_NE(filename, nullptr); + + // Check that filename contains expected components including VLAN + EXPECT_TRUE(strstr(filename, "fe80::1") != nullptr); + EXPECT_TRUE(strstr(filename, "fe80::2") != nullptr); + EXPECT_TRUE(strstr(filename, "22") != nullptr); + EXPECT_TRUE(strstr(filename, "2222") != nullptr); + EXPECT_TRUE(strstr(filename, "5555555555") != nullptr); + EXPECT_TRUE(strstr(filename, "_vlan.pcap") != nullptr); + + free(filename); } TEST_F(UtilitiesTest, NewFileNameZeroPorts) { - auto tuple = create_test_tuple_ipv4("172.16.0.1", 0, "172.16.0.2", 0, IPPROTO_UDP); - unsigned long timestamp = 7777777777; - - char *filename = new_file_name(tuple, timestamp); - ASSERT_NE(filename, nullptr); - - // Should handle zero ports gracefully - EXPECT_TRUE(strstr(filename, "172.16.0.1") != nullptr); - EXPECT_TRUE(strstr(filename, "172.16.0.2") != nullptr); - EXPECT_TRUE(strstr(filename, "7777777777") != nullptr); - EXPECT_TRUE(strstr(filename, ".pcap") != nullptr); - - free(filename); + auto tuple = + create_test_tuple_ipv4("172.16.0.1", 0, "172.16.0.2", 0, IPPROTO_UDP); + unsigned long timestamp = 7777777777; + + char *filename = new_file_name(tuple, timestamp); + ASSERT_NE(filename, nullptr); + + // Should handle zero ports gracefully + EXPECT_TRUE(strstr(filename, "172.16.0.1") != nullptr); + EXPECT_TRUE(strstr(filename, "172.16.0.2") != nullptr); + EXPECT_TRUE(strstr(filename, "7777777777") != nullptr); + EXPECT_TRUE(strstr(filename, ".pcap") != nullptr); + + free(filename); } TEST_F(UtilitiesTest, FileNameUniqueness) { - auto tuple1 = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.2", 8080); - auto tuple2 = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.3", 8080); - unsigned long timestamp = 1234567890; - - char *filename1 = new_file_name(tuple1, timestamp); - char *filename2 = new_file_name(tuple2, timestamp); - - ASSERT_NE(filename1, nullptr); - ASSERT_NE(filename2, nullptr); - - // Different tuples should generate different filenames - EXPECT_STRNE(filename1, filename2); - - free(filename1); - free(filename2); + auto tuple1 = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.2", 8080); + auto tuple2 = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.3", 8080); + unsigned long timestamp = 1234567890; + + char *filename1 = new_file_name(tuple1, timestamp); + char *filename2 = new_file_name(tuple2, timestamp); + + ASSERT_NE(filename1, nullptr); + ASSERT_NE(filename2, nullptr); + + // Different tuples should generate different filenames + EXPECT_STRNE(filename1, filename2); + + free(filename1); + free(filename2); } TEST_F(UtilitiesTest, FileNameTimestampDifference) { - auto tuple = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.2", 8080); - unsigned long timestamp1 = 1234567890; - unsigned long timestamp2 = 1234567891; - - char *filename1 = new_file_name(tuple, timestamp1); - char *filename2 = new_file_name(tuple, timestamp2); - - ASSERT_NE(filename1, nullptr); - ASSERT_NE(filename2, nullptr); - - // Different timestamps should generate different filenames - EXPECT_STRNE(filename1, filename2); - - free(filename1); - free(filename2); + auto tuple = create_test_tuple_ipv4("192.168.1.1", 80, "192.168.1.2", 8080); + unsigned long timestamp1 = 1234567890; + unsigned long timestamp2 = 1234567891; + + char *filename1 = new_file_name(tuple, timestamp1); + char *filename2 = new_file_name(tuple, timestamp2); + + ASSERT_NE(filename1, nullptr); + ASSERT_NE(filename2, nullptr); + + // Different timestamps should generate different filenames + EXPECT_STRNE(filename1, filename2); + + free(filename1); + free(filename2); } \ No newline at end of file From 497ca6b7df3952f7afcadb55478ce97bed012cf3 Mon Sep 17 00:00:00 2001 From: "xiaming.cxm" Date: Thu, 10 Jul 2025 10:55:42 +0800 Subject: [PATCH 2/3] fix cppcheck --- .github/workflows/ci.yml | 4 +++- src/pkt2flow.cpp | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b9063e..3cb0db2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,8 +47,10 @@ jobs: env: PKG_CONFIG_PATH: ${{ matrix.os == 'macos-latest' && '/opt/homebrew/opt/libpcap/lib/pkgconfig' || '' }} run: | - cppcheck --enable=all --error-exitcode=1 --suppress=missingIncludeSystem \ + cppcheck --enable=all --error-exitcode=1 \ + --suppress=missingIncludeSystem \ --suppress=unusedFunction \ + --suppress=unusedStructMember \ --inline-suppr \ src/*.h src/*.cpp diff --git a/src/pkt2flow.cpp b/src/pkt2flow.cpp index 904815f..7b1006d 100644 --- a/src/pkt2flow.cpp +++ b/src/pkt2flow.cpp @@ -344,7 +344,6 @@ static int pcap_handle_ethernet(struct af_6tuple *af_6tuple, static void process_trace(void) { struct pcap_pkthdr hdr; - int syn_detected; struct ip_pair *pair = NULL; pcap_dumper_t *dumper = NULL; const u_char *pkt = NULL; @@ -352,7 +351,7 @@ static void process_trace(void) { struct af_6tuple af_6tuple; while ((pkt = pcap_next(inputp, &hdr)) != NULL) { - syn_detected = pcap_handle_ethernet(&af_6tuple, &hdr, pkt); + int syn_detected = pcap_handle_ethernet(&af_6tuple, &hdr, pkt); if (syn_detected < 0) continue; From afaa1deb93543d0c95f04481438a5361647fe52a Mon Sep 17 00:00:00 2001 From: "xiaming.cxm" Date: Thu, 10 Jul 2025 10:59:00 +0800 Subject: [PATCH 3/3] fix cppcheck --- .github/workflows/ci.yml | 13 ------------- cppcheck.sh | 7 +++++++ 2 files changed, 7 insertions(+), 13 deletions(-) create mode 100755 cppcheck.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3cb0db2..07000d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,6 @@ jobs: libgtest-dev \ cmake \ build-essential \ - cppcheck \ clang-format - name: Install dependencies (macOS) @@ -40,20 +39,8 @@ jobs: glog \ googletest \ cmake \ - cppcheck \ clang-format - - name: Run cppcheck - env: - PKG_CONFIG_PATH: ${{ matrix.os == 'macos-latest' && '/opt/homebrew/opt/libpcap/lib/pkgconfig' || '' }} - run: | - cppcheck --enable=all --error-exitcode=1 \ - --suppress=missingIncludeSystem \ - --suppress=unusedFunction \ - --suppress=unusedStructMember \ - --inline-suppr \ - src/*.h src/*.cpp - - name: Check code formatting env: PKG_CONFIG_PATH: ${{ matrix.os == 'macos-latest' && '/opt/homebrew/opt/libpcap/lib/pkgconfig' || '' }} diff --git a/cppcheck.sh b/cppcheck.sh new file mode 100755 index 0000000..f0f7df5 --- /dev/null +++ b/cppcheck.sh @@ -0,0 +1,7 @@ +#!/bin/bash +cppcheck --enable=all --error-exitcode=1 \ + --suppress=missingIncludeSystem \ + --suppress=unusedFunction \ + --suppress=unusedStructMember \ + --inline-suppr \ + src/*.h src/*.cpp \ No newline at end of file