[
  {
    "path": ".clang-format",
    "content": "---\nLanguage:        Cpp\n# BasedOnStyle:  LLVM\nAccessModifierOffset: 0\nAlignAfterOpenBracket: Align\nAlignConsecutiveMacros: true\nAlignConsecutiveAssignments: true\nAlignConsecutiveDeclarations: false\nAlignEscapedNewlines: Left\nAlignOperands:   true\nAlignTrailingComments: true\nAllowAllArgumentsOnNextLine: true\nAllowAllConstructorInitializersOnNextLine: true\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowShortBlocksOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: All\nAllowShortLambdasOnASingleLine: All\nAllowShortIfStatementsOnASingleLine: Never\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterDefinitionReturnType: TopLevel\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: false\nAlwaysBreakTemplateDeclarations: MultiLine\nBinPackArguments: true\nBinPackParameters: true\nBraceWrapping:\n  AfterCaseLabel:  false\n  AfterClass:      false\n  AfterControlStatement: false\n  AfterEnum:       false\n  AfterFunction:   true\n  AfterNamespace:  false\n  AfterObjCDeclaration: false\n  AfterStruct:     false\n  AfterUnion:      false\n  AfterExternBlock: false\n  BeforeCatch:     false\n  BeforeElse:      false\n  IndentBraces:    false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\nBreakBeforeBinaryOperators: None\nBreakBeforeBraces: Custom\nBreakBeforeInheritanceComma: false\nBreakInheritanceList: BeforeColon\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializersBeforeComma: false\nBreakConstructorInitializers: BeforeColon\nBreakAfterJavaFieldAnnotations: false\nBreakStringLiterals: true\nColumnLimit:     100\nCommentPragmas:  '^ IWYU pragma:'\nCompactNamespaces: false\nConstructorInitializerAllOnOneLineOrOnePerLine: false\nConstructorInitializerIndentWidth: 4\nContinuationIndentWidth: 4\nCpp11BracedListStyle: true\nDerivePointerAlignment: false\nDisableFormat:   false\nExperimentalAutoDetectBinPacking: false\nFixNamespaceComments: true\nForEachMacros:\n  - foreach\n  - Q_FOREACH\n  - BOOST_FOREACH\n  - STAILQ_FOREACH\n  - TAILQ_FOREACH_SAFE\n  - PKTDEV_FOREACH\n  - TAILQ_FOREACH\n  - json_array_foreach\n  - json_object_object_foreach\n  - json_object_object_foreachC\n  - CIRCLEQ_FOREACH\n  - jcfg_application_foreach\n  - jcfg_defaults_foreach\n  - jcfg_umem_foreach\n  - jcfg_lport_foreach\n  - jcfg_lgroup_foreach\n  - jcfg_options_foreach\n  - jcfg_thread_foreach\nIncludeBlocks:   Preserve\nIncludeCategories:\n  - Regex:           '^\"(llvm|llvm-c|clang|clang-c)/'\n    Priority:        2\n  - Regex:           '^(<|\"(gtest|gmock|isl|json)/)'\n    Priority:        3\n  - Regex:           '.*'\n    Priority:        1\nIncludeIsMainRegex: '(Test)?$'\nIndentCaseLabels: false\nIndentPPDirectives: None\nIndentWidth:     4\nIndentWrappedFunctionNames: false\nJavaScriptQuotes: Leave\nJavaScriptWrapImports: true\nKeepEmptyLinesAtTheStartOfBlocks: true\nMacroBlockBegin: ''\nMacroBlockEnd:   ''\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBinPackProtocolList: Auto\nObjCBlockIndentWidth: 4\nObjCSpaceAfterProperty: false\nObjCSpaceBeforeProtocolList: true\nPenaltyBreakAssignment: 4\nPenaltyBreakBeforeFirstCallParameter: 19\nPenaltyBreakComment: 300\nPenaltyBreakFirstLessLess: 100\nPenaltyBreakString: 1000\nPenaltyBreakTemplateDeclaration: 10\nPenaltyExcessCharacter: 1000000\nPenaltyReturnTypeOnItsOwnLine: 60\nPointerAlignment: Right\nReflowComments:  true\nSortIncludes:    false\nSortUsingDeclarations: true\nSpaceAfterCStyleCast: false\nSpaceAfterLogicalNot: false\nSpaceAfterTemplateKeyword: true\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCpp11BracedList: false\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeParens: ControlStatements\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 8\nSpacesInAngles:  false\nSpacesInContainerLiterals: true\nSpacesInCStyleCastParentheses: false\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nStandard:        Cpp11\nStatementMacros:\n  - Q_UNUSED\n  - QT_REQUIRE_VERSION\nTabWidth:        4\nUseTab:          Never\n...\n"
  },
  {
    "path": ".claude/settings.local.json",
    "content": "{\n  \"permissions\": {\n    \"allow\": [\n      \"Bash(ninja:*)\",\n      \"Bash(wc:*)\",\n      \"Bash(ls:*)\",\n      \"Read(//work/projects/intel/pktgen-dpdk/**)\",\n      \"Bash(find:*)\",\n      \"Bash(clang-format:*)\",\n      \"Bash(git -C /work/projects/intel/pktgen-dpdk diff --stat lib/)\",\n      \"Bash(grep -rn \\\"pktgen_stats_clear\\\\\\\\|\\\\\\\\.qstats\\\\\\\\|\\\\\\\\.curr\\\\\\\\|\\\\\\\\.prev\\\\\\\\|\\\\\\\\.rate\\\\\\\\|rte_eth_stats_get\\\" /work/projects/intel/pktgen-dpdk/app/*.c)\"\n    ]\n  }\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright 2019 6WIND S.A.\n# See https://editorconfig.org/ for syntax reference.\n\nroot = true\n\n[**]\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\ncharset = utf-8\nindent_style = space\nindent_size = 4\ntab_width = 4\nmax_line_length = 100\n\n[*.py]\nindent_style = space\nindent_size = 4\n\n[*.rst]\nindent_style = space\nindent_size = 3\n\n[*.build]\nindent_style = space\nindent_size = 4\n\n[*.yml]\nindent_size = 2\n\n[COMMIT_EDITMSG]\nmax_line_length = 72\n"
  },
  {
    "path": ".githooks/pre-commit",
    "content": "#!/usr/bin/env bash\n# Pre-commit hook for Markdown linting and optional auto-fix.\n# Usage: ln -sf ../../.githooks/pre-commit .git/hooks/pre-commit\n# Requires Node.js (npx). Installs markdownlint-cli2 on demand (no permanent dependency).\n\nset -euo pipefail\n\nREPO_ROOT=\"$(git rev-parse --show-toplevel)\"\ncd \"$REPO_ROOT\"\n\nSTAGED_MD_FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep -E '\\.(md|MD)$' || true)\n[ -z \"$STAGED_MD_FILES\" ] && exit 0\n\nif ! command -v npx >/dev/null 2>&1; then\n  echo \"[pre-commit] npx (Node.js) not found. Install Node.js to lint Markdown.\" >&2\n  exit 1\nfi\n\necho \"[pre-commit] Linting Markdown files:\"\necho \"$STAGED_MD_FILES\" | sed 's/^/  - /'\n\nif ! npx --yes -- markdownlint-cli2 --version >/dev/null 2>&1; then\n  echo \"[pre-commit] Installing markdownlint-cli2 (ephemeral).\" >&2\n  npm install --no-save markdownlint-cli2 >/dev/null 2>&1 || {\n    echo \"[pre-commit] Failed to install markdownlint-cli2.\" >&2\n    exit 1\n  }\nfi\n\nCONFIG_FILE=\".markdownlint.yml\"\n[ -f \"$CONFIG_FILE\" ] || CONFIG_FILE=\".markdownlint.yaml\"\n\nLINT_OK=1\nif ! npx --yes -- markdownlint-cli2 $STAGED_MD_FILES --config \"$CONFIG_FILE\"; then\n  LINT_OK=0\nfi\n\nif [ $LINT_OK -eq 0 ]; then\n  echo \"[pre-commit] Markdown issues detected. Attempting optional auto-fix (markdownlint legacy).\" >&2\n  if npx --yes -- markdownlint --version >/dev/null 2>&1; then\n    npx --yes -- markdownlint --fix $STAGED_MD_FILES || true\n    git add $STAGED_MD_FILES || true\n  fi\n  echo \"[pre-commit] Commit aborted. Review fixes and re-stage.\" >&2\n  exit 1\nfi\n\necho \"[pre-commit] Markdown lint passed.\"\nexit 0\n#! /bin/bash\n\nfor filename in $(git diff --cached --name-only | grep '.*\\.[c|h]$'); do\n  if [ -f $filename ]; then\n     clang-format -style=file -i $filename; git add $filename;\n  fi\ndone\n\n"
  },
  {
    "path": ".github/workflows/clang-format.yml",
    "content": "name: clang-format Check\non:\n  push:\n    branches: [main]\n  pull_request:\n\njobs:\n  formatting-check:\n    name: Formatting Check\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          # Fetch enough history to diff against the base branch on PRs,\n          # or against the previous commit on direct pushes.\n          fetch-depth: 0\n\n      - name: Install clang-format-21\n        run: |\n          wget -qO- https://apt.llvm.org/llvm.sh | sudo bash -s -- 21\n          sudo apt-get install -y clang-format-21\n          sudo update-alternatives --install /usr/bin/clang-format clang-format \\\n            /usr/bin/clang-format-21 100\n          clang-format --version\n\n      - name: Resolve changed C/C++ files\n        id: changed\n        run: |\n          if [ \"${{ github.event_name }}\" = \"pull_request\" ]; then\n            BASE=\"${{ github.event.pull_request.base.sha }}\"\n          else\n            BASE=\"${{ github.event.before }}\"\n          fi\n\n          # Gracefully handle the first push to a branch (no previous commit).\n          if git cat-file -t \"$BASE\" 2>/dev/null | grep -q commit; then\n            FILES=$(git diff --name-only --diff-filter=ACMR \"$BASE\" HEAD \\\n              | grep -E '\\.(c|h|cpp|hpp|cc|hh)$' || true)\n          else\n            FILES=$(git ls-files '*.c' '*.h' '*.cpp' '*.hpp' '*.cc' '*.hh')\n          fi\n\n          echo \"files<<EOF\" >> \"$GITHUB_OUTPUT\"\n          echo \"$FILES\"     >> \"$GITHUB_OUTPUT\"\n          echo \"EOF\"        >> \"$GITHUB_OUTPUT\"\n\n          if [ -z \"$FILES\" ]; then\n            echo \"No C/C++ files changed – skipping format check.\"\n            echo \"skip=true\" >> \"$GITHUB_OUTPUT\"\n          else\n            echo \"Checking $(echo \"$FILES\" | wc -l) file(s):\"\n            echo \"$FILES\"\n            echo \"skip=false\" >> \"$GITHUB_OUTPUT\"\n          fi\n\n      - name: Check formatting\n        if: steps.changed.outputs.skip == 'false'\n        run: |\n          echo \"${{ steps.changed.outputs.files }}\" \\\n            | xargs -r clang-format --dry-run --Werror\n"
  },
  {
    "path": ".github/workflows/doc.yml",
    "content": "name: Doc Deploy\n\non:\n  push:\n    branches: [main]\n\n# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages\npermissions:\n  contents: read\n  pages: write\n  id-token: write\n\n# Allow one concurrent deployment\nconcurrency:\n  group: \"pages\"\n  cancel-in-progress: true\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v3\n      - name: Install dependencies\n        run: |\n          sudo apt install python3-sphinx meson ninja-build\n      - name: Build docs\n        run: |\n          meson -Donly_docs=true builddir\n          make docs\n      - name: Setup Pages\n        uses: actions/configure-pages@v4\n      - name: Upload artifact\n        uses: actions/upload-artifact@v4\n        with:\n          path: \"./builddir/docs/source/html\"\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4\n"
  },
  {
    "path": ".github/workflows/markdownlint.yml",
    "content": "name: Markdown Lint\n\non:\n  pull_request:\n    paths:\n      - '**/*.md'\n      - '.markdownlint.yml'\n  push:\n    branches: [ main ]\n    paths:\n      - '**/*.md'\n      - '.markdownlint.yml'\n\njobs:\n  markdownlint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v4\n\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: '20'\n          cache: 'npm'\n\n      - name: Install markdownlint tools\n        run: |\n          npm install --no-save markdownlint-cli2\n\n      - name: Run markdownlint\n        run: |\n          npx markdownlint-cli2 \"**/*.md\" \"!usr/**\" \"!builddir/**\"\n\n      - name: Upload markdownlint report (always)\n        if: always()\n        run: |\n          # Create simple summary artifact (non-blocking)\n          echo \"Markdown lint completed\" > markdownlint-summary.txt\n        \n      - name: Store summary artifact\n        if: always()\n        uses: actions/upload-artifact@v4\n        with:\n          name: markdownlint-summary\n          path: markdownlint-summary.txt\n"
  },
  {
    "path": ".gitignore",
    "content": "*.o\n*.a\n*.d\n*~\n_install\n_postinstall\n_preinstall\n_postbuild\n_postclean\nlib/common/common/\ndocs/build/\n.project\n.pydevproject\nnode_modules\n.vscode\ngulpfile.js\nx86_64-*\nlib/lua/build\nbuilddir/\nusr/local\ntx_perf\n"
  },
  {
    "path": ".markdownlint.yml",
    "content": "# Markdownlint configuration\n# Adjusted for Pktgen project documentation style.\n# Rule references: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md\n\n# Disable long line rule; technical lines (URLs, tables, commands) often exceed 80 chars.\nMD013: false\n\n# Allow inline HTML only if absolutely needed (currently we removed it).\nMD033: true  # Disallow inline HTML (set to false to allow)\n\n# Require fenced code block style consistency (use backticks).\nMD048:\n  style: backtick\n\n# Enforce heading style ATX (# ...)\nMD003:\n  style: atx\n\n# No multiple consecutive blank lines\nMD012: true\n\n# Trailing spaces not allowed (except for deliberate hard-breaks)\nMD009:\n  strict: true\n\n# Lists: allow different markers but keep consistent indentation\nMD004: true\n\n# Ordered list numbering can be all '1.' for simplicity\nMD029:\n  style: one\n\n# Code blocks and lists separated by blank lines\nMD031: true\n\n# First line should be a top-level heading\nMD041: true\n\n# Horizontal rules style (enforce ---)\nMD035:\n  style: ---\n\n# Emphasis markers style (prefer *)\nMD049:\n  style: asterisk\n\n# Strong emphasis style (prefer **)\nMD050:\n  style: asterisk\n\n# Heading levels should only increment by one level at a time\nMD001: true\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "# Pre-commit configuration for FastIPC\n# See https://pre-commit.com for more information\n# See https://pre-commit.com/hooks.html for more hooks\n\n# Global exclusions - ignore build directories and specifications\nexclude: '^(builddir/.*|specs/.*|.github/.*|.githooks/.*)'\n\nrepos:\n  # General code quality hooks\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v6.0.0\n    hooks:\n      - id: trailing-whitespace\n        args: [--markdown-linebreak-ext=md]\n      - id: end-of-file-fixer\n      - id: check-yaml\n      - id: check-added-large-files\n        args: ['--maxkb=1000']\n      - id: check-case-conflict\n      - id: check-merge-conflict\n      - id: check-symlinks\n      - id: debug-statements\n      - id: detect-private-key\n      - id: mixed-line-ending\n        args: ['--fix=lf']\n\n  # C/C++ specific formatting and linting\n  - repo: https://github.com/pre-commit/mirrors-clang-format\n    rev: v22.1.0\n    hooks:\n      - id: clang-format\n        args: ['-i']\n        types_or: [c, c++]\n\n  # Additional C/C++ static analysis (manual use)\n  # Uncomment to enable clang-tidy in pre-commit (requires compilation database)\n  # - repo: local\n  #   hooks:\n  #     - id: clang-tidy\n  #       name: clang-tidy\n  #       entry: clang-tidy\n  #       language: system\n  #       files: \\.(c|h)$\n  #       args: [--format-style=file]\n\n  # Meson build file validation (manual)\n  # Run manually with: meson setup --dry-run build-test\n\n  # Documentation and markdown\n  - repo: https://github.com/igorshubovych/markdownlint-cli\n    rev: v0.47.0\n    hooks:\n      - id: markdownlint\n        args: ['--fix', '--disable', 'MD013', 'MD033']\n\n\n\n  # Shell script linting\n  # - repo: https://github.com/shellcheck-py/shellcheck-py\n  #   rev: v0.10.0.1\n  #   hooks:\n  #     - id: shellcheck\n\n  # Security scanning\n  #- repo: https://github.com/Yelp/detect-secrets\n  #  rev: v1.5.0\n  #  hooks:\n  #    - id: detect-secrets\n  #      args: ['--baseline', '.secrets.baseline']\n\n#ci:\n#  autofix_commit_msg: |\n#    [pre-commit.ci] auto fixes from pre-commit.com hooks\n#\n#    for more information, see https://pre-commit.ci\n#  autofix_prs: true\n#  autoupdate_branch: ''\n#  autoupdate_commit_msg: '[pre-commit.ci] pre-commit autoupdate'\n#  autoupdate_schedule: weekly\n#  skip: []\n#  submodules: false\n"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "# .readthedocs.yaml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details\n\n# Required\nversion: 2\n\n# Set the version of Python and other tools you might need\nbuild:\n  os: ubuntu-22.04\n  tools:\n    python: \"3.11\"\n\n# Build documentation in the docs/ directory with Sphinx\nsphinx:\n  configuration: docs/source/conf.py\n\n# We recommend specifying your dependencies to enable reproducible builds:\n# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html\n# python:\n#   install:\n#   - requirements: docs/requirements.txt\n"
  },
  {
    "path": "2-ports",
    "content": "#\n# Pktgen 25.08.2\n# Copyright(c) <2010-2025>, Intel Corporation. All rights reserved., Powered by DPDK 26.03.0-rc0\n\n# Command line arguments: (DPDK args are defaults)\n# ./usr/local/bin/pktgen -c fe -n 3 -m 512 --proc-type primary -- -v -T -P -G -m [2-3:4].0 -m [5-6:7].1 -f themes/black-yellow.theme\n\n#######################################################################\n# Pktgen Configuration script information:\n#   Flags 00008814\n#   Number of ports: 2\n#   Number ports per page: 4\n#   Number descriptors: RX 1024 TX: 1024\n#   Promiscuous mode is Enabled\n\n\n# Global configuration:\n#   geometry 132x56\ndisable mac_from_arp\n\n######################### Port  0 ##################################\n#\n# Port:  0, Burst (Rx/Tx): 64/ 32, Rate:100%, Flags:            1000, TX Count:Forever\n#           Sequence count:0, Prime:1 VLAN ID:0001, Link: <UP-100000-FD>\n#\n# Set up the primary port information:\nset 0 count 0\nset 0 size 64\nset 0 rate 100\nset 0 rxburst 64\nset 0 txburst 32\nset 0 sport 1234\nset 0 dport 5678\nset 0 prime 1\nset 0 type ipv4\nset 0 proto tcp\nset 0 dst ip 192.168.1.1\nset 0 src ip 192.168.0.1/24\nset 0 tcp flags ack\nset 0 tcp seq 74616\nset 0 tcp ack 74640\nset 0 dst mac 6c:fe:54:a0:84:f0\nset 0 src mac 6c:fe:54:a0:86:80\nset 0 vlan 1\n\nset 0 pattern abc\n\nset 0 jitter 50\ndisable 0 mpls\nrange 0 mpls entry 0x0\ndisable 0 qinq\nset 0 qinqids 0 0\ndisable 0 gre\ndisable 0 gre_eth\ndisable 0 vxlan\nset 0 vxlan 0x0 0 0\n#\n# Port flag values:\ndisable 0 icmp\ndisable 0 pcap\ndisable 0 range\ndisable 0 latency\ndisable 0 process\ndisable 0 capture\ndisable 0 vlan\n#\n# Range packet information:\nrange 0 src mac start 6c:fe:54:a0:86:80\nrange 0 src mac min 00:00:00:00:00:00\nrange 0 src mac max 00:00:00:00:00:00\nrange 0 src mac inc 00:00:00:00:00:00\n\nrange 0 dst mac start 6c:fe:54:a0:86:81\nrange 0 dst mac min 00:00:00:00:00:00\nrange 0 dst mac max 00:00:00:00:00:00\nrange 0 dst mac inc 00:00:00:00:00:00\n\nrange 0 src ip start 192.168.0.1\nrange 0 src ip min 192.168.0.1\nrange 0 src ip max 192.168.0.254\nrange 0 src ip inc 0.0.0.0\n\nrange 0 dst ip start 192.168.1.1\nrange 0 dst ip min 192.168.1.1\nrange 0 dst ip max 192.168.1.254\nrange 0 dst ip inc 0.0.0.1\n\nrange 0 proto tcp\n\nrange 0 src port start 1234\nrange 0 src port min 0\nrange 0 src port max 65535\nrange 0 src port inc 1\n\nrange 0 dst port start 5678\nrange 0 dst port min 0\nrange 0 dst port max 65535\nrange 0 dst port inc 1\n\nrange 0 tcp flags ack\n\nrange 0 tcp seq start 74616\nrange 0 tcp seq min 0\nrange 0 tcp seq max 536870911\nrange 0 tcp seq inc 0\n\nrange 0 tcp ack start 74640\nrange 0 tcp ack min 0\nrange 0 tcp ack max 536870911\nrange 0 tcp ack inc 0\n\nrange 0 ttl start 64\nrange 0 ttl min 0\nrange 0 ttl max 255\nrange 0 ttl inc 0\n\nrange 0 vlan start 1\nrange 0 vlan min 1\nrange 0 vlan max 4095\nrange 0 vlan inc 0\n\nrange 0 cos start 0\nrange 0 cos min 0\nrange 0 cos max 7\nrange 0 cos inc 0\n\nrange 0 tos start 0\nrange 0 tos min 0\nrange 0 tos max 255\nrange 0 tos inc 0\nrange 0 gre key 0\n\nrange 0 size start 64\nrange 0 size min 64\nrange 0 size max 1518\nrange 0 size inc 0\n\n#\n# Set up the sequence data for the port.\nset 0 seq_cnt 0\n\n######################### Port  1 ##################################\n#\n# Port:  1, Burst (Rx/Tx): 64/ 32, Rate:100%, Flags:            1000, TX Count:Forever\n#           Sequence count:0, Prime:1 VLAN ID:0001, Link: <UP-100000-FD>\n#\n# Set up the primary port information:\nset 1 count 0\nset 1 size 64\nset 1 rate 100\nset 1 rxburst 64\nset 1 txburst 32\nset 1 sport 1234\nset 1 dport 5678\nset 1 prime 1\nset 1 type ipv4\nset 1 proto tcp\nset 1 dst ip 192.168.0.1\nset 1 src ip 192.168.1.1/24\nset 1 tcp flags ack\nset 1 tcp seq 74616\nset 1 tcp ack 74640\nset 1 dst mac 6c:fe:54:a0:84:f1\nset 1 src mac 6c:fe:54:a0:86:81\nset 1 vlan 1\n\nset 1 pattern abc\n\nset 1 jitter 50\ndisable 1 mpls\nrange 1 mpls entry 0x0\ndisable 1 qinq\nset 1 qinqids 0 0\ndisable 1 gre\ndisable 1 gre_eth\ndisable 1 vxlan\nset 1 vxlan 0x0 0 0\n#\n# Port flag values:\ndisable 1 icmp\ndisable 1 pcap\ndisable 1 range\ndisable 1 latency\ndisable 1 process\ndisable 1 capture\ndisable 1 vlan\n#\n# Range packet information:\nrange 1 src mac start 6c:fe:54:a0:86:81\nrange 1 src mac min 00:00:00:00:00:00\nrange 1 src mac max 00:00:00:00:00:00\nrange 1 src mac inc 00:00:00:00:00:00\n\nrange 1 dst mac start 6c:fe:54:a0:86:80\nrange 1 dst mac min 00:00:00:00:00:00\nrange 1 dst mac max 00:00:00:00:00:00\nrange 1 dst mac inc 00:00:00:00:00:00\n\nrange 1 src ip start 192.168.1.1\nrange 1 src ip min 192.168.1.1\nrange 1 src ip max 192.168.1.254\nrange 1 src ip inc 0.0.0.0\n\nrange 1 dst ip start 192.168.2.1\nrange 1 dst ip min 192.168.2.1\nrange 1 dst ip max 192.168.2.254\nrange 1 dst ip inc 0.0.0.1\n\nrange 1 proto tcp\n\nrange 1 src port start 1234\nrange 1 src port min 0\nrange 1 src port max 65535\nrange 1 src port inc 1\n\nrange 1 dst port start 5678\nrange 1 dst port min 0\nrange 1 dst port max 65535\nrange 1 dst port inc 1\n\nrange 1 tcp flags ack\n\nrange 1 tcp seq start 74616\nrange 1 tcp seq min 0\nrange 1 tcp seq max 536870911\nrange 1 tcp seq inc 0\n\nrange 1 tcp ack start 74640\nrange 1 tcp ack min 0\nrange 1 tcp ack max 536870911\nrange 1 tcp ack inc 0\n\nrange 1 ttl start 64\nrange 1 ttl min 0\nrange 1 ttl max 255\nrange 1 ttl inc 0\n\nrange 1 vlan start 1\nrange 1 vlan min 1\nrange 1 vlan max 4095\nrange 1 vlan inc 0\n\nrange 1 cos start 0\nrange 1 cos min 0\nrange 1 cos max 7\nrange 1 cos inc 0\n\nrange 1 tos start 0\nrange 1 tos min 0\nrange 1 tos max 255\nrange 1 tos inc 0\nrange 1 gre key 0\n\nrange 1 size start 64\nrange 1 size min 64\nrange 1 size max 1518\nrange 1 size inc 0\n\n#\n# Set up the sequence data for the port.\nset 1 seq_cnt 0\n\n################################ Done #################################\n"
  },
  {
    "path": "2-vf-ports",
    "content": "#\n# Pktgen 25.07.1\n# Copyright(c) <2010-2025>, Intel Corporation. All rights reserved., Powered by DPDK 25.07.0\n\n# Command line arguments: (DPDK args are defaults)\n# ./usr/local/bin/pktgen -c 7c000 -n 3 -m 512 --proc-type primary -- -v -T -P -G -m [15:16].0 -m [17:18].1 -f themes/black-yellow.theme -f 2-ports\n\n#######################################################################\n# Pktgen Configuration script information:\n#   Flags 00008814\n#   Number of ports: 2\n#   Number ports per page: 4\n#   Number descriptors: RX 1024 TX: 1024\n#   Promiscuous mode is Enabled\n\n\n# Global configuration:\n#   geometry 132x56\ndisable mac_from_arp\n\n######################### Port  0 ##################################\n#\n# Port:  0, Burst (Rx/Tx): 64/ 32, Rate:100%, Flags:            1000, TX Count:Forever\n#           Sequence count:0, Prime:1 VLAN ID:0001, Link: <UP-100000-FD>\n#\n# Set up the primary port information:\nset 0 count 0\nset 0 size 64\nset 0 rate 100\nset 0 rxburst 64\nset 0 txburst 32\nset 0 sport 1234\nset 0 dport 5678\nset 0 prime 1\nset 0 type ipv4\nset 0 proto tcp\nset 0 dst ip 192.168.66.161\nset 0 src ip 192.168.66.101/24\nset 0 tcp flags ack\nset 0 tcp seq 74616\nset 0 tcp ack 74640\nset 0 dst mac 52:54:00:00:16:01\nset 0 src mac 06:bd:a3:87:a7:96\nset 0 vlan 1\n\nset 0 pattern abc\n\nset 0 jitter 50\ndisable 0 mpls\nrange 0 mpls entry 0x0\ndisable 0 qinq\nset 0 qinqids 0 0\ndisable 0 gre\ndisable 0 gre_eth\ndisable 0 vxlan\nset 0 vxlan 0x0 0 0\n#\n# Port flag values:\ndisable 0 icmp\ndisable 0 pcap\ndisable 0 range\ndisable 0 latency\ndisable 0 process\ndisable 0 capture\ndisable 0 vlan\n#\n# Range packet information:\nrange 0 src mac start 06:bd:a3:87:a7:96\nrange 0 src mac min 00:00:00:00:00:00\nrange 0 src mac max 00:00:00:00:00:00\nrange 0 src mac inc 00:00:00:00:00:00\n\nrange 0 dst mac start 2e:f9:d1:05:fb:e7\nrange 0 dst mac min 00:00:00:00:00:00\nrange 0 dst mac max 00:00:00:00:00:00\nrange 0 dst mac inc 00:00:00:00:00:00\n\nrange 0 src ip start 192.168.0.1\nrange 0 src ip min 192.168.0.1\nrange 0 src ip max 192.168.0.254\nrange 0 src ip inc 0.0.0.0\n\nrange 0 dst ip start 192.168.1.1\nrange 0 dst ip min 192.168.1.1\nrange 0 dst ip max 192.168.1.254\nrange 0 dst ip inc 0.0.0.1\n\nrange 0 proto tcp\n\nrange 0 src port start 1234\nrange 0 src port min 0\nrange 0 src port max 65535\nrange 0 src port inc 1\n\nrange 0 dst port start 5678\nrange 0 dst port min 0\nrange 0 dst port max 65535\nrange 0 dst port inc 1\n\nrange 0 tcp flags ack\n\nrange 0 tcp seq start 74616\nrange 0 tcp seq min 0\nrange 0 tcp seq max 536870911\nrange 0 tcp seq inc 0\n\nrange 0 tcp ack start 74640\nrange 0 tcp ack min 0\nrange 0 tcp ack max 536870911\nrange 0 tcp ack inc 0\n\nrange 0 ttl start 64\nrange 0 ttl min 0\nrange 0 ttl max 255\nrange 0 ttl inc 0\n\nrange 0 vlan start 1\nrange 0 vlan min 1\nrange 0 vlan max 4095\nrange 0 vlan inc 0\n\nrange 0 cos start 0\nrange 0 cos min 0\nrange 0 cos max 7\nrange 0 cos inc 0\n\nrange 0 tos start 0\nrange 0 tos min 0\nrange 0 tos max 255\nrange 0 tos inc 0\nrange 0 gre key 0\n\nrange 0 size start 64\nrange 0 size min 64\nrange 0 size max 1518\nrange 0 size inc 0\n\n#\n# Set up the sequence data for the port.\nset 0 seq_cnt 0\n\n######################### Port  1 ##################################\n#\n# Port:  1, Burst (Rx/Tx): 64/ 32, Rate:100%, Flags:            1000, TX Count:Forever\n#           Sequence count:0, Prime:1 VLAN ID:0001, Link: <UP-100000-FD>\n#\n# Set up the primary port information:\nset 1 count 0\nset 1 size 64\nset 1 rate 100\nset 1 rxburst 64\nset 1 txburst 32\nset 1 sport 1234\nset 1 dport 5678\nset 1 prime 1\nset 1 type ipv4\nset 1 proto tcp\nset 1 dst ip 192.168.66.162\nset 1 src ip 192.168.66.102/24\nset 1 tcp flags ack\nset 1 tcp seq 74616\nset 1 tcp ack 74640\nset 1 dst mac 52:54:00:00:16:02\nset 1 src mac 2e:f9:d1:05:fb:e7\nset 1 vlan 1\n\nset 1 pattern abc\n\nset 1 jitter 50\ndisable 1 mpls\nrange 1 mpls entry 0x0\ndisable 1 qinq\nset 1 qinqids 0 0\ndisable 1 gre\ndisable 1 gre_eth\ndisable 1 vxlan\nset 1 vxlan 0x0 0 0\n#\n# Port flag values:\ndisable 1 icmp\ndisable 1 pcap\ndisable 1 range\ndisable 1 latency\ndisable 1 process\ndisable 1 capture\ndisable 1 vlan\n#\n# Range packet information:\nrange 1 src mac start 2e:f9:d1:05:fb:e7\nrange 1 src mac min 00:00:00:00:00:00\nrange 1 src mac max 00:00:00:00:00:00\nrange 1 src mac inc 00:00:00:00:00:00\n\nrange 1 dst mac start 06:bd:a3:87:a7:96\nrange 1 dst mac min 00:00:00:00:00:00\nrange 1 dst mac max 00:00:00:00:00:00\nrange 1 dst mac inc 00:00:00:00:00:00\n\nrange 1 src ip start 192.168.1.1\nrange 1 src ip min 192.168.1.1\nrange 1 src ip max 192.168.1.254\nrange 1 src ip inc 0.0.0.0\n\nrange 1 dst ip start 192.168.2.1\nrange 1 dst ip min 192.168.2.1\nrange 1 dst ip max 192.168.2.254\nrange 1 dst ip inc 0.0.0.1\n\nrange 1 proto tcp\n\nrange 1 src port start 1234\nrange 1 src port min 0\nrange 1 src port max 65535\nrange 1 src port inc 1\n\nrange 1 dst port start 5678\nrange 1 dst port min 0\nrange 1 dst port max 65535\nrange 1 dst port inc 1\n\nrange 1 tcp flags ack\n\nrange 1 tcp seq start 74616\nrange 1 tcp seq min 0\nrange 1 tcp seq max 536870911\nrange 1 tcp seq inc 0\n\nrange 1 tcp ack start 74640\nrange 1 tcp ack min 0\nrange 1 tcp ack max 536870911\nrange 1 tcp ack inc 0\n\nrange 1 ttl start 64\nrange 1 ttl min 0\nrange 1 ttl max 255\nrange 1 ttl inc 0\n\nrange 1 vlan start 1\nrange 1 vlan min 1\nrange 1 vlan max 4095\nrange 1 vlan inc 0\n\nrange 1 cos start 0\nrange 1 cos min 0\nrange 1 cos max 7\nrange 1 cos inc 0\n\nrange 1 tos start 0\nrange 1 tos min 0\nrange 1 tos max 255\nrange 1 tos inc 0\nrange 1 gre key 0\n\nrange 1 size start 64\nrange 1 size min 64\nrange 1 size max 1518\nrange 1 size inc 0\n\n#\n# Set up the sequence data for the port.\nset 1 seq_cnt 0\n\n################################ Done #################################\n"
  },
  {
    "path": "CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n---\n\n## Build Commands\n\nThe project uses **Meson/Ninja**. A `builddir/` is typically already initialised.\n\n```bash\n# Incremental build (most common)\nninja -C builddir\n\n# Full configure + build from scratch\nmeson setup builddir\nmeson compile -C builddir\n\n# Makefile wrapper (delegates to tools/pktgen-build.sh)\nmake build          # release build\nmake rebuild        # clean + release build\nmake debug          # debug build (no optimisation)\nmake debugopt       # debug with -O2\nmake buildlua       # release + Lua scripting enabled\nmake clean\n\n# Build options (pass to meson setup)\nmeson setup builddir -Denable_lua=true\nmeson setup builddir -Denable_docs=true\nmeson setup builddir -Donly_docs=true      # docs only, skip app/lib\n\n# Code formatting\nninja -C builddir clang-format-check       # check only (CI does this)\nninja -C builddir clang-format             # auto-fix in place\n\n# Documentation\nmake docs                                  # Sphinx + Doxygen\n\n# Tests\nninja -C builddir test                     # meson test runner\n# Integration tests are Lua scripts in test/ that connect to a running\n# pktgen instance over its TCP socket (port 22022).\n```\n\nBuild is strict: **warning_level=3** and **werror=true**. Any new warning is a build failure.\n\n---\n\n## Pre-commit Hook Setup\n\n```bash\nln -sf ../../.githooks/pre-commit .git/hooks/pre-commit\nchmod +x .githooks/pre-commit\n```\n\nThis runs `clang-format -i` on staged C files and `markdownlint` on staged Markdown.\n\n---\n\n## Commit Message Convention\n\nFrom `CONTRIBUTING.md`:\n\n```\n<short summary in imperative mood>\n\n<context / motivation>\n<what changed>\n<any impact / migration notes>\n```\n\nExample prefix pattern observed in recent commits: `refactor:`, `fix:`, `stats:`, etc.\n\n---\n\n## High-Level Architecture\n\nPktgen is a DPDK-based wire-rate packet generator. The binary is `builddir/app/pktgen`.\n\n### Module layout\n\n```\nlib/\n  hmap/      Hash-map (type-safe, 64-bit unified storage)\n  common/    Shared utilities: checksums, IP parsing, strings, CPU info, port config\n  utils/     Portlist parsing, heap, inet_pton, JSON (parson)\n  vec/       Dynamic-array (vector) container\n  plugin/    dlopen-based plugin loader (up to 32 plugins, start/stop lifecycle)\n  cli/       Full interactive shell: node tree, gap-buffer editor, TAB completion,\n             history, help, env vars, map-driven argument parsing\n  lua/       Lua 5.3/5.4 scripting bindings (conditional on -Denable_lua=true)\n\napp/\n  pktgen-main.c      Entry point: arg parsing, EAL init, port setup, CLI loop start\n  pktgen.c           Core TX loop logic, packet rate calculation, timer callbacks\n  pktgen.h           Global pktgen_t struct, page/flag enums, per-port iteration macros\n  pktgen-port-cfg.h  port_info_t (per-port state), SEND_* flag bits, pkt_seq_t layout\n  pktgen-cmds.c      ~112 KB command implementation dispatcher\n  cli-functions.c    ~74 KB CLI map tables + command handler glue (registers maps)\n  l2p.c / l2p.h      Lcore-to-Port mapping matrix, mempool ownership\n  pktgen-{ipv4,ipv6,tcp,udp,arp,vlan,gre,gtpu,ether}.c   Protocol header builders\n  pktgen-range.c     Incrementing-field packet generation\n  pktgen-seq.c       16-slot packet sequence\n  pktgen-random.c    Per-field random bitfield masks\n  pktgen-pcap.c      PCAP file load/replay\n  pktgen-capture.c   Packet capture to memory\n  pktgen-latency.c   Latency sampling & histograms\n  pktgen-stats.c     Per-port / per-queue stats and rate calculation\n  pktgen-display.c   Terminal UI page rendering (10 display pages)\n  pktgen-log.c       Ring-buffer message log\n```\n\n### Build dependency order (lib subdirectories)\n\n```\nhmap → common → utils → vec → plugin → cli → lua\n```\n\nAll lib modules compile to static libraries and are linked into the single `pktgen` executable.\n\n### Data-flow summary\n\n1. **Initialisation** (`pktgen-main.c`): parse `-m` matrix string → populate `l2p_t` (which lcores own which ports/queues) → allocate per-queue mempools (`rx_mp`, `tx_mp`, `sp_mp`) → build default packet templates via `pktgen_packet_ctor()` → launch one worker per lcore.\n\n2. **TX loop** (`pktgen.c:pktgen_launch_one_lcore`): each worker polls its assigned port/queue. The active sending mode is determined by the port's `port_flags` atomic (e.g. `SEND_SINGLE_PKTS`, `SEND_RANGE_PKTS`, `SEND_SEQ_PKTS`, `SEND_PCAP_PKTS`). Rate limiting uses cycle-based inter-burst timing (`tx_cycles`).\n\n3. **RX path**: received packets are optionally processed (`PROCESS_INPUT_PKTS` — handles ARP/ICMP echo), captured (`CAPTURE_PKTS`), or used for latency measurement (`SAMPLING_LATENCIES`).\n\n4. **CLI / command path**: the interactive shell runs on the main lcore. Commands are dispatched via `cli_map` pattern matching in `cli-functions.c` → actual logic in `pktgen-cmds.c`. Changing a port setting typically sets a flag (`SETUP_TRANSMIT_PKTS`) so the worker rebuilds its packet template on the next iteration.\n\n5. **Display refresh**: a periodic timer (`UPDATE_DISPLAY_TICK_RATE`) triggers `pktgen_page_display()` which re-renders the current screen page using stats collected via `rte_eth_stats_get` / extended stats.\n\n### Key global state\n\n- `pktgen_t pktgen` — single global (defined in `pktgen.c`, declared `extern` in `pktgen.h`). Holds port count, display state, CPU info, capture buffers, Lua handles.\n- `l2p_t` — lcore↔port mapping. Accessed via `l2p_get_port_pinfo(pid)` and the per-lcore `l2p_get_lport(lid)`.\n- `port_info_t` — per-port struct (embedded inside `l2p_port_t.pinfo`). Contains packet templates (`seq_pkt[NUM_TOTAL_PKTS]`), range config, stats, latency, random-field masks, and the 64-bit `port_flags` atomic.\n- `this_cli` — `RTE_PER_LCORE` pointer to `struct cli`. The CLI tree, gap buffer, history, and the `cmd_maps` registry all live here.\n\n### Port-flag model\n\n`port_flags` is a 64-bit atomic on each `port_info_t`. Bits are grouped:\n\n| Group | Bits | Semantics |\n|---|---|---|\n| Non-exclusive RX/misc | 0–9 | ARP, ICMP echo, capture, latency sampling |\n| Exclusive TX mode | 12–15 | Exactly one of SINGLE/PCAP/RANGE/SEQ must be set |\n| Exclusive pkt-type | 16–23 | VLAN, MPLS, GRE, VxLAN, random, latency |\n| Control | 28–31 | SETUP_TRANSMIT_PKTS, SENDING_PACKETS, SEND_FOREVER |\n\nThe macros `EXCLUSIVE_MODES` and `EXCLUSIVE_PKT_MODES` define the mutual-exclusion masks.\n\n### CLI auto-completion architecture\n\nTAB completion has two layers:\n\n1. **Tree completion** — walks `cli_node` children of the current directory, matches prefixes of command/dir/file names.\n2. **Map completion** — once the first token (command name) is typed, `cli_get_cmd_map(argv[0])` returns the registered `cli_map` table. The completer scans all map entries whose fixed tokens match what has been typed so far, then offers candidates or placeholder hints (e.g. `<portlist>`, `<ipv4-addr>`) for the current argument position.\n\nMap registration happens automatically: `cli_help_add(group, map, help)` calls `cli_register_cmd_maps(map)` which extracts every unique first-token and stores `{token → map}` in the growable `cmd_maps` array on `this_cli`. Do not call `cli_register_cmd_map()` manually after `cli_help_add()` — it would be a redundant no-op.\n\nMap format tokens: `%d` (32-bit), `%D` (64-bit), `%u`/`%U` (unsigned), `%h`/`%H` (hex), `%b` (8-bit), `%n` (number), `%s` (string), `%c` (comma-list), `%m` (MAC), `%4` (IPv4), `%6` (IPv6), `%P` (portlist), `%C` (corelist), `%k` (kvargs), `%l` (list), `%|opt1|opt2|…` (choice token).\n\n### Packet-slot layout (`pkt_seq_t` array)\n\nEach port has `NUM_TOTAL_PKTS = 20` slots:\n\n| Index | Role |\n|---|---|\n| 0–15 | Sequence packets (cycled in `SEND_SEQ_PKTS` mode) |\n| 16 (`SINGLE_PKT`) | Default single-packet template |\n| 17 (`SPECIAL_PKT`) | Temporary / scratch packet |\n| 18 (`RANGE_PKT`) | Template rebuilt each burst in range mode |\n| 19 (`LATENCY_PKT`) | Latency probe packet |\n\n---\n\n## Coding Conventions\n\n- **Formatting**: LLVM-based `.clang-format`, 100-column limit, 4-space indent, right-aligned pointers. Run `clang-format -i` on any file you touch.\n- **New files**: must carry the SPDX header (`SPDX-License-Identifier: BSD-3-Clause`) and a copyright line.\n- **Port iteration**: use the `forall_ports(_action)` or `foreach_port(_portlist, _action)` macros from `pktgen.h` rather than hand-rolling port loops.\n- **TAILQ / CIRCLEQ**: the CLI and help systems use POSIX tail-queues extensively. New linked structures should follow the same pattern.\n- **Atomic port flags**: always use `rte_atomic64_t` operations when reading or writing `port_info_t.port_flags` from worker lcores.\n- **cli_map entries**: every entry in a `cli_map` array must have a corresponding `case` in the command handler's switch on `m->index`. Orphan entries cause phantom command registrations and broken auto-complete.\n\n---\n\n## CI Checks (GitHub Actions)\n\nThree workflows run on every PR:\n\n| Workflow | What it checks |\n|---|---|\n| `clang-format.yml` | All C/C++ files pass `clang-format --dry-run` (clang-format 21) |\n| `markdownlint.yml` | All `.md` files pass `markdownlint-cli2` (config in `.markdownlint.yml`) |\n| `doc.yml` | Sphinx docs build cleanly (runs on push to `main` only) |\n\nMarkdown rules of note: bare URLs must be wrapped in `< >`, numbered lists should use `1.` style, fenced code blocks must have a language hint.\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\nWe are committed to fostering a welcoming, respectful, and productive environment for all contributors and users of Pktgen-DPDK.\n\nThis project adopts (by reference) the [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) (Version 2.1) as the foundation of expected behavior.\n\n## 1. Scope\n\nApplies to all project spaces: GitHub issues, pull requests, discussions, documentation, community chats, and any project-related events.\n\n## 2. Summary of Expectations\n\n- Be respectful, inclusive, and patient.\n- Provide constructive feedback; focus on ideas, not individuals.\n- Use welcoming and professional language; avoid harassment or discrimination.\n- Assume good intent while clarifying misunderstandings.\n- Respect differing viewpoints and experiences.\n\n## 3. Unacceptable Behavior (Examples)\n\n- Harassment, intimidation, or discrimination of any form.\n- Personal or political attacks.\n- Trolling, insults, or derogatory comments.\n- Public or private harassment.\n- Publishing others’ private information without explicit permission.\n- Conduct inconsistent with professional standards.\n\n## 4. Reporting\n\nIf you experience or witness unacceptable behavior:\n\n- Preferred: Open a confidential issue labeled `coc-report` (omit sensitive personal details; maintainers will follow up privately), or\n- Email the maintainers (if listed in `README.md`) with details: date/time, participants, context, and any supporting material.\n\nReports will be handled promptly, respectfully, and—when requested—confidentially.\n\n## 5. Enforcement\n\nMaintainers are responsible for clarifying and enforcing this Code. Actions may include (progressively, as appropriate):\n\n1. Informal warning\n1. Formal written warning\n1. Temporary exclusion from discussions or repository interactions\n1. Permanent ban from project spaces\n\nSevere violations (e.g. threats, doxxing, hate speech) may result in immediate permanent removal without prior warning.\n\n## 6. Appeals\n\nIf you believe an enforcement decision was made in error, you may request a review by contacting the maintainers with a concise rationale.\n\n## 7. Attribution\n\nThis document is adapted from the Contributor Covenant, Version 2.1.\n\nSee: <https://www.contributor-covenant.org/version/2/1/code_of_conduct/>\n\n---\nQuestions or feedback regarding this Code of Conduct are welcome—open an issue to discuss improvements.\n"
  },
  {
    "path": "CONTRIBUTING.TXT",
    "content": "Contributors Guide\n==================\n\nThis document outlines how to contribute code to the PKTGEN project.\n\nGetting the code\n----------------\n\nThe PKTGEN code can be cloned from the repository on Github as follows:\n\n    git clone https://github.com/pktgen/Pktgen-DPDK.git\n\nThe code can be browsed at https://github.com/pktgen/Pktgen-DPDK\n\nSubmitting Patches\n------------------\n\nContributions to PKTGEN should be submitted as merge requests on Github.\n\nThe commit message must end with a \"Signed-off-by:\" line which is added using:\n\n    git commit --signoff # or -s\n\nThe purpose of the signoff is explained in the Linux kernel guidelines\nDeveloper's Certificate of Origin:\nhttps://www.kernel.org/doc/html/latest/process/submitting-patches.html\n\nNote:\n    All developers must ensure that they have read, understood and complied\n    with the Developer's Certificate of Origin section of the documentation\n    prior to applying the signoff and submitting a patch.\n\nThe DPDK Code Contributors guidelines contain information that is\nuseful for submitting patches to PKTGEN:\nhttp://dpdk.org/doc/guides/contributing/index.html\n\nCoding Guidelines\n-----------------\n\n* C code should follow the DPDK coding standards.\n* Lua code should follow existing code.\n\nMaintainer\n----------\n\nThe PKTGEN maintainer is: Keith Wiles <keith.wiles@intel.com>\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Pktgen-DPDK\n\nThank you for your interest in improving Pktgen-DPDK. This short guide complements the main `README.md` and highlights the key expectations for contributions.\n\n## 1. Before You Start\n\n- Skim the project overview and feature list in the README.\n- Make sure you can build and run the tool (see \"Quick Start\" and \"Clone and Build\" sections).\n- Search existing issues and pull requests to avoid duplication.\n\n## 2. Development Environment\n\n- Build system: Meson/Ninja (optional simple `Makefile` wrapper provided).\n- Required: A recent DPDK build with a valid `libdpdk.pc` on your `PKG_CONFIG_PATH`.\n- Recommended: Enable the pre-commit hook for markdown linting:\n  - `ln -sf ../../.githooks/pre-commit .git/hooks/pre-commit && chmod +x .githooks/pre-commit`\n\n## 3. Coding & Style\n\n- Follow existing C coding patterns; prefer readability and minimal global state changes.\n- Keep commits focused; one logical change per commit when possible.\n- Document non-obvious code with concise comments.\n- Avoid introducing new external dependencies without discussion.\n\n## 4. Documentation Standards\n\n- All Markdown must pass `markdownlint` (CI will enforce).\n- Use fenced code blocks with language hints.\n- Wrap bare URLs in `< >`.\n- Numbered lists may all use `1.` style.\n- If adding new user-facing features, update the relevant README section or create a doc under `docs/`.\n- Refer to the extended style guidance in [`docs/STYLE.md`](./docs/STYLE.md) for formatting patterns and examples.\n\n## 5. Commit Messages\n\nUse the following structure:\n\n```text\n<short summary imperative>\n\n<context / motivation>\n<what changed>\n<any impact / migration notes>\n```\n\nExamples:\n\n```text\nstats: fix link status refresh on state change\n\nPreviously link duplex/speed changes were ignored unless carrier toggled. Update\nlogic to trigger display refresh on any field difference.\n```\n\n## 6. Submitting Pull Requests\n\n- Fork, branch from `main`.\n- Rebase (not merge) to update your branch before final push.\n- Ensure `meson compile` succeeds and (if applicable) tests or sample scripts still run.\n- CI must be green (markdownlint, build, etc.).\n- Reference related issues with `Fixes #NNN` when appropriate.\n\n## 7. Reporting Issues\n\nInclude (when relevant):\n\n- Pktgen version (`cat VERSION`)\n- DPDK version and configuration\n- NIC model(s) and driver versions\n- Reproduction steps and minimal config (`cfg/` file if relevant)\n- Expected vs. actual behavior\n\n## 8. Performance / Feature Discussions\n\nOpen an issue first for large changes (subsystems, new protocol modules, architectural refactors). Describe:\n\n- Problem statement / use case\n- Proposed design sketch\n- Anticipated impacts (performance, config format, user CLI)\n\n## 9. Security\n\nDo not publicly disclose potential security-impacting bugs without prior coordination—open a private issue if possible or contact maintainers directly.\n\nSee also the community standards in the [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md).\n\n## 10. Licensing\n\nBy contributing you agree your changes are under the existing project license (BSD-3-Clause). Ensure any new files include the appropriate SPDX identifier header where applicable.\n\n## 11. Quick Reference\n\n| Area | Where to Look |\n|------|---------------|\n| Build & Run | README Quick Start / Build sections |\n| Config examples | `cfg/` directory |\n| Lua automation | README Automation section / `test/*.lua` |\n| Themes | `themes/` directory |\n| Plugins | `lib/plugin/` |\n\n---\nQuestions? Open an issue or start a discussion. Happy hacking!\n"
  },
  {
    "path": "INSTALL.md",
    "content": "# Pktgen - Traffic Generator powered by DPDK\n\n---\nPktgen is a traffic generator powered by DPDK at wire rate traffic with 64 byte frames.**\n\n## (Pktgen) Sounds like 'Packet-Gen'\n\n---\n**Copyright &copy; \\<2010-2026\\>, Intel Corporation. All rights reserved.**\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n- Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n\n- Redistributions in binary form must reproduce the above copyright\nnotice, this list of conditions and the following disclaimer in\nthe documentation and/or other materials provided with the\ndistribution.\n\n- Neither the name of Intel Corporation nor the names of its\ncontributors may be used to endorse or promote products derived\nfrom this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\nFOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\nCOPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\nHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\nSTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\nOF THE POSSIBILITY OF SUCH DAMAGE.\n\nSPDX-License-Identifier: BSD-3-Clause\n\nPktgen: Created 2010-2024 by Keith Wiles @ Intel.com\n\n---\n\n## Installation\n\nINSTALL for setting up Pktgen with DPDK on Ubuntu 22.04 to 23.10 desktop it should work on most Linux systems as long as the kernel has hugeTLB page support and builds DPDK.\n\n## Please Note\n\n> Pktgen-DPDK main repo is located @ <https://github.com/pktgen/Pktgen-DPDK>\n> Documentation can be found @ <https://pktgen.github.io/Pktgen-DPDK/>\n\n### Submitting bug fixes or enhancements to Pktgen-DPDK\n\n> Please fork Pktgen-DPDK from GitHub and submit a pull-request as I do not accept patches sent to DPDK mailing list. If you have any questions or issues please create an issue on Github.\n> Tested with Ubuntu 23.10 kernel version 6.5.0-28-generic (Mantic), and other earlier versions should work.\n> Please use the latest DPDK and latest Pktgen versions other combinations may work, but with limited resources and time the latest versions are the only ones tested.\n>\n> Please install the latest version of DPDK from dpdk.org and follow the build instructions using meson/ninja. Then install DPDK on your system.\n\n## Building Pktgen\n\nPktgen has been converted to use meson/ninja for configuration and building. **Makefiles have been removed.** This may require DPDK to be built\nusing meson/ninja. At least a libdpdk.pc file must be present in the system for Pktgen to locate the headers and libraries.\n\n>Please read the DPDK.org documentation to understand more on building DPDK. The following is the minimum set of\ninstructions to build DPDK. You may need to install meson and ninja, if not already installed.\n\nMay need to install the BSD headers to build Pktgen code base for Ubuntu do the following:\n\n```console\nsudo apt-get install libbsd-dev\n```\n\n```console\ngit clone git://dpdk.org/dpdk\n\nOr if git is blocked\n\ngit clone http://dpdk.org/git/dpdk\n\ncd dpdk\nmeson setup build\nninja -C build\nsudo ninja -C build install\nsudo ldconfig  # make sure ld.so is pointing new DPDK libraries\n```\n\nDPDK places the libdpdk.pc (pkg-config file) in a non-standard location and you need to set enviroment variable PKG_CONFIG_PATH to the location of the file. On Ubuntu 20.04 build of DPDK it places the file here /usr/local/lib/x86_64-linux-gnu/pkgconfig/libdpdk.pc\n\n```console\nexport PKG_CONFIG_PATH=/usr/local/lib/x86_64-linux-gnu/pkgconfig\n```\n\nBuilding Pktgen after you have built and installed DPDK. The new build system uses meson/ninja, but Pktgen has a build script\ncalled 'tools/pktgen-build.sh' and uses a very simple Makefile to help build Pktgen without having to fully understand meson/ninja command line.\n\nIf you prefer you can still use meson/ninja directly.\n\n```console\ngit clone https://github.com/pktgen/Pktgen-DPDK\n\ncd pktgen-dpdk # or Pktgen-DPDK or whatever name you gave the cloned directory\n\nmake\nor\nmake build    # Same as 'make'\nor\nmake rebuild  # Rebuild Pktgen, which removes the 'builddir' then builds it again via meson/ninja\nor\nmake rebuildlua # to enable Lua builds\n\n# Use 'make help' to read the help message for building.\n\n# DPDK does not add a /etc/ld.so.conf.d/<dpdk-libs> like file. This means you may need to\n# edit /etc/ld.so.conf.d/x86_64-linux-gnu.conf file and add /usr/local/lib/x86_64-linux-gnu\n# Then do 'sudo ldconfig' to locate the DPDK libraries.\n\n# If you want to use vfio-pci then edit /etc/default/grub and add 'intel_iommu=on' to the LINUX default line\n# Then use 'update-grub' command then reboot the system.\n```\n\nEditing the meson_options.txt can be done, but normally you should use a meson command line options to enable/disable options.\n\n## Pktgen Information\n\nThe pktgen output display needs 132 columns and about 42 lines to display\ncorrectly. I am using an xterm of 132x42, but you can have a larger display\nand maybe a bit smaller. If you are displaying more then 4-6 ports then you\nwill need a wider display. Pktgen allows you to view a set of ports if they\ndo not all fit on the screen at one time via the 'page' command.\n\nType 'help' at the 'Pktgen>' prompt to see the complete Pktgen command line\ncommands. Pktgen uses VT100 control codes or escape codes to display the screens,\nwhich means your terminal must support VT100.\n\n### Note\n\n`Hyperterminal in Windows is not going to work for Pktgen as it has a few problems with VT100 codes.`\n\n## Quick comment on Pktgen modes\n\nPktgen has a number of modes to send packets single, range, random, sequence and\nPCAP modes. Each mode has its own set of packet buffers and you must configure\neach mode to work correctly. The single packet mode is the information displayed\nat startup screen or when using the 'page main or page 0' command. The other\nscreens can be accessed using 'page seq|range|rnd|pcap|stats' command.\n\nThe pktgen program as built can send up to 16 packets per port in a sequence\nand you can configure a port using the 'seq' pktgen command. A script file\ncan be loaded from the shell command line via the -f option and you can 'load'\na script file from within pktgen as well.\n\n## Setup Pktgen-DPDK\n\nAt the pktgen-DPDK level directory we have the 'tools/setup.sh' script,\nwhich needs to be run as root once per boot. The script contains a commands to setup\nthe environment.\n\nBefore you run the script you will need to run:\n\n```bash\nexport RTE_SDK=<DPDKinstallDir>\nexport RTE_TARGET=x86_64-native-linux-gcc\n```\n\nMake sure you run the setup script as root via `./tools/setup.sh`. The setup\nscript is a bash script and tries to setup the system correctly, but you may have to\nchange the script to match your number of huge pages you configured above and ports.\n\nThe `modprobe uio` command, in the setup script, loads the UIO support module into the\nkernel plus it loads the igb-uio.ko module into the kernel. The two echo commands,\nin the setup script, finish setting up the huge pages one for each socket. If you\nonly have a single socket system then comment out the second echo command. The last\ncommand is to display the huge TLB setup.\n\nEdit your .bashrc or .profile or .cshrc to add the environment variables.\nI am using bash: `# vi ~/.bashrc`\n\nAdd the following lines: Change the $RTE_SDK to the location of the DPDK version\ndirectory. Your SDK directory maybe named differently, but should point to the DPDK\nSDK directory.\n\n```bash\nexport RTE_SDK=<DPDKinstallDir>\nexport RTE_TARGET=x86_64-native-linux-gcc\n```\n\nor use clang if you have it installed\n\n```bash\nexport RTE_TARGET=x86_64-native-linux-clang\n```\n\nCreate the DPDK build tree if you haven't already:\n\n```bash\ncd $RTE_SDK\nmake install T=x86_64-native-linux-gcc -j\n```\n\nThe above command will create the x86_64-native-linux-gcc directory in the\ntop level of the current-dkdp directory. The above command will build the basic\nDPDK libraries and build tree.\n\nNext we build pktgen:\n\n```bash\ncd <PktgenInstallDir>\nmake -j\n```\n\nFor CentOS and pcap support you may need to try (for libpcap-devel):\n\n```bash\nyum install dnf-plugins-core\nyum config-manager --set-enabled PowerTools\nyum repolist\n```\n\nYou should now have pktgen built.\n\nTo get started, see README.md.\n"
  },
  {
    "path": "LICENSE",
    "content": "#   BSD LICENSE\n#\n#   Copyright(c) <2010-2025> Intel Corporation. All rights reserved.\n#\n#   Redistribution and use in source and binary forms, with or without\n#   modification, are permitted provided that the following conditions\n#   are met:\n#\n#     * Redistributions of source code must retain the above copyright\n#       notice, this list of conditions and the following disclaimer.\n#     * Redistributions in binary form must reproduce the above copyright\n#       notice, this list of conditions and the following disclaimer in\n#       the documentation and/or other materials provided with the\n#       distribution.\n#     * Neither the name of Intel Corporation nor the names of its\n#       contributors may be used to endorse or promote products derived\n#       from this software without specific prior written permission.\n#\n#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n#   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n#   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n#   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n#   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n#   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n#   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n#   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n#\n# Created 2010 by Keith Wiles @ windriver.com\n#\n# SPDX-License-Identifier: BSD-3-Clause\n#\n"
  },
  {
    "path": "Makefile",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2019-2025> Intel Corporation\n\n#\n# Head Makefile for compiling Pktgen-DPDK, but just a wrapper around\n# meson and ninja using the tools/pktgen-build.sh script.\n#\n# Use 'make' or 'make build' to build Pktgen-DPDK. If the build directory does\n# not exist it will be created with these two build types.\n#\n\nBuild=./tools/pktgen-build.sh\n\nbuild: FORCE\n\t${Build} build\n\nrebuild: FORCE\n\t${Build} clean build\n\nbuildlua: FORCE\n\t${Build} clean buildlua\n\ndebuglua: FORCE\n\t${Build} clean debuglua\n\ndebug: FORCE\n\t${Build} clean debug\n\ndebugopt: FORCE\n\t${Build} clean debugopt\n\nclean: FORCE\n\t${Build} clean\n\ninstall: FORCE\n\t${Build} install\n\nuninstall: FORCE\n\t${Build} uninstall\n\ndocs: FORCE\n\t${Build} docs\n\nhelp: FORCE\n\t${Build} help\n\nFORCE:\n\t@echo \">>> Use 'make help' for more commands\\n\"\n"
  },
  {
    "path": "Pktgen.lua",
    "content": "-- SPDX-License-Identifier: BSD-3-Clause\n\n-- Create some short cuts to the real functions.\ngsub\t    = string.gsub\ngmatch      = string.gmatch\nstrrep\t    = string.rep\nstrsub      = string.sub\nstrfmt      = string.format\nstrmatch    = string.match\nstrrep      = string.rep\nstrfind     = string.find\nstrlen      = string.len\ntolower     = string.lower\nunlink      = os.remove\nsystem      = os.execute\ntinsert     = table.insert\ntgetn       = table.getn\ntconcat     = table.concat\n\n-- ===========================================================================\n-- getPwd - Get the current working directory path.\n-- Works for both dos and cygwin shell, which may emit an error if one failes.\n--\nfunction getPwd( func )\n\tlocal s = syscmd(\"pwd\", func);\n\tif ( s == nil ) then\n\t\ts = syscmd(\"sh pwd\", func);\n\tend\n\treturn s;\nend\n\n-- ===========================================================================\n-- d2u - convert dos backslashes to unix forward slashes.\n--\nfunction d2u( str )\n    return gsub(str or \"\", \"\\\\\", \"/\");\nend\n\n-- ===========================================================================\n-- u2d - convert unix forward slashes to dos backslashes.\n--\nfunction u2d( str )\n    return gsub(str or \"\", \"/\", \"\\\\\");\nend\n\n-- ===========================================================================\n-- str2tbl - convert a string to a table.\n--\nfunction str2tbl( str )\n    local t = {};\n    local s;\n\n    for s in gmatch(str or \"\", \"(.-)\\n\") do\n        tinsert(t, s);\n    end\n    return t\nend\n\n-- ===========================================================================\n-- trims the string of beginning and trailing spaces and tabs.\n--\nfunction trim(txt) return gsub(txt, \"%s*(.-)%s*$\", \"%1\", 1); end\n\n-- ===========================================================================\n-- A formatted output routine just like the real printf.\n--\nfunction printf(...) io.write(strfmt(...)); io.flush(); end\n\n-- ===========================================================================\n-- returns the table size or number of items in table.\n--\nfunction getn( t )\n\n    local   i = 0;\n\n    if ( (t ~= nil) and (type(t) == \"table\") ) then\n        i = tgetn(t);\n        if ( i > 0 ) then\n            return i;\n        end\n        for k in pairs(t) do\n            i = i + 1;\n        end\n    end\n    return i;\nend\n\n-- ===========================================================================\n-- returns the 'basename' and the 'basedir'\n--\nfunction basename(filename)\n    local   fn, dn;\n\n    -- Convert the '\\' to '/' in the path name.\n    filename = d2u(filename);\n\n    fn = gsub(filename, \"(.*/)(.*)\", \"%2\") or filename;\n    dn = gsub(filename, \"(.*/)(.*)\", \"%1\")\n\n    dn = strsub(dn, 1, -2);\n\n    return fn, dn;\nend\n\n-- ===========================================================================\n-- Default routine to read data from syscmd function.\n--\nlocal function __doRead(fn)\n    local   data, f;\n\n    -- Open and read all of the data.\n    f = assert(io.open(fn));\n    data = f:read(\"*all\");\n    f:close();\n\n    unlink(fn);\n\n    return data;\nend\n\n-- ===========================================================================\n-- Execute the system command return the command output if needed.\n--\nfunction syscmd( cmd, funcPtr )\n    local tmpFile = \"syscmd_tmp\";\n\n    system( cmd .. \" > \" .. tmpFile );\n\n    funcPtr = funcPtr or __doRead;\n\n    return funcPtr(tmpFile);    -- tmpFile is removed by the function.\nend\n\n-- ===========================================================================\n-- Execute the string and return true/false.\n--\nfunction isTrue(f)\n    local   s;\n\n    if ( f == nil ) then\n        return 0;\n    end\n\n    s = \"if ( \"..f..\" ) then return 1 else return 0 end\";\n    return assert(loadstring(s))();\nend\n\n-- ===========================================================================\n-- Output a message and return.\n--\nfunction msg(m, ...)\n\n    if ( m ~= nil ) then io.write(\"++ \"..strfmt(m, ...)); io.flush(); end\nend\n-- ===========================================================================\n-- Display an error message and exit.\n--\nfunction errmsg(m, ...)\n\n    if ( m ~= nil ) then printf(\"** %s\", strfmt(m, ...)); end\n\n    os.exit(1);\nend\n\n-- ===========================================================================\n-- Output a 'C' like block comment.\n--\nfunction cPrintf(m, ...)\n\n    printf(\"/* \");\n    io.write(strfmt(m, ...));\n    printf(\" */\\n\");\nend\n-- ===========================================================================\n-- Output a 'C' like comment.\n--\nfunction comment(msg)\n\n    printf(\"/* %s */\\n\", msg or \"ooops\");\nend\n\n-- Standard set of functions for normal operation.\n--\n\n-----------------------------------------------------------------------------\n-- serializeIt - Convert a variable to text or its type of variable.\n--\nlocal function serializeIt(v)\n    local   s;\n    local   t = type(v);\n\n    if (t == \"number\") then\n        s = tostring(v);\n    elseif (t == \"table\") then\n        s = tostring(v);\n    elseif (t == \"string\") then\n        s = strfmt(\"%q\", v);\n    elseif (t == \"boolean\") then\n        s = tostring(v);\n    elseif (t == \"function\") then\n        s = strfmt(\"()\");\n    elseif (t == \"userdata\") then\n        s = tostring(v);\n    elseif (t == \"nil\") then\n        s = \"nil\";\n    else\n        s = strfmt(\"<%s>\", tostring(v));\n    end\n\n    return s;\nend\n\n-----------------------------------------------------------------------------\n-- Serialize a value\n--    k - is the variable name string.\n--    o - is the orignal variable name for tables.\n--    v - the value of the variable.\n--    saved - is the saved table to detect loops.\n--    tab - is the current tab depth.\n--\nlocal function doSerialize(k, o, v, saved, tab)\n\tlocal s, t;\n    local space = function(t) return strrep(\" \", t); end;\n\n    tab     = tab or 0;\n    t       = type(v);\n    saved   = saved or {};\n\n\tif (t == \"table\") then\n        if ( saved[v] ~= nil ) then\n            return strfmt(\"%s[%s] = %s,\\n\", space(tab), serializeIt(o), saved[v]);\n        else\n            local   kk, vv, mt;\n\n            saved[v] = k;\n\n            if ( tab == 0 ) then\n                s = strfmt(\"%s%s = {\\n\", space(tab), tostring(k));\n            else\n                s = strfmt(\"%s[%s] = {\\n\", space(tab), serializeIt(o));\n            end\n            for kk,vv in pairs(v) do\n                local fn = strfmt(\"%s[%s]\", tostring(k), serializeIt(kk));\n\n                s = s .. doSerialize(fn, kk, vv, saved, tab+2);\n            end\n\n\t\t\tif ( tab == 0 ) then\n            \treturn s .. strfmt(\"%s}\\n\", space(tab));\n\t\t\telse\n            \treturn s .. strfmt(\"%s},\\n\", space(tab));\n\t\t\tend\n        end\n\telse\n        return strfmt(\"%s[%s] = %s,\\n\", space(tab), serializeIt(o), serializeIt(v));\n\tend\nend\n\n-----------------------------------------------------------------------------\n-- serialize - For a given key serialize the global variable.\n--    k is a string for display and t is the table to display.\n--    e.g. printf(serialize(\"foo\", { [\"bar\"] = \"foobar\" } ));\n--\nfunction serialize(k, t)\n\n    if ( k == nil ) then\n\t    k = \"Globals\";\n        t = _G;\t\t\t-- Dump out globals\n    end\n\tif ( t == nil ) then\n\t\tt = _G;\n\tend\n\n   \treturn doSerialize(k, k, t, {}, 0);\nend\n\nfunction prints(k, t) io.write(serialize(k, t)); io.flush(); end\nfunction sleep(t) pktgen.delay(t * 1000); end\n"
  },
  {
    "path": "README.md",
    "content": "# Pktgen — DPDK Traffic Generator\n\nHigh‑performance, scriptable packet generator capable of wire‑rate transmission with 64‑byte frames.\n\nPronounced: “packet‑gen”\n\n[Documentation](https://pktgen.github.io/Pktgen-DPDK/) · [Releases](https://github.com/pktgen/Pktgen-DPDK/releases)\n\n---\n\n## Table of Contents\n\n1. [Overview](#1-overview)\n1. [Features](#2-features-partial-list)\n1. [Quick Start](#3-quick-start)\n1. [Building](#4-building-details)\n1. [Configuration Files](#5-configuration-files-cfg)\n1. [Configuration Key Reference](#6-configuration-key-reference)\n1. [Runtime Modes & Pages](#7-runtime-modes--pages)\n1. [Automation & Remote Control](#8-automation--remote-control)\n1. [Advanced Topics](#9-advanced-topics)\n1. [Contributing](#10-contributing)\n1. [License](#11-license)\n1. [Related Links](#12-related-links)\n1. [Acknowledgments](#13-acknowledgments)\n\n---\n\n## 1. Overview\n\nPktgen is a multi‑port, multi‑core traffic generator built on top of [DPDK]. It targets realistic, repeatable performance and functional packet tests while remaining fully controllable via an interactive console, Lua scripts, or a remote TCP socket.\n\n> Primary repository: <https://github.com/pktgen/Pktgen-DPDK>\n\n## 2. Features (Partial List)\n\n- Wire‑rate 64B packet generation (hardware and core count permitting)\n- Multi‑port / multi‑queue scaling\n- IPv4 / IPv6, TCP / UDP, VLAN, GRE, GTP-U support\n- Packet sequence, range, random, pcap replay and latency modes\n- Latency & jitter measurement, per‑queue and extended stats pages\n- Lua scripting (local or remote) + TCP control socket (default port 22022)\n- Configurable theming and multiple display pages (main, seq, range, rnd, pcap, stats, xstats)\n- Plugin architecture (`lib/plugin`), capture and pcap dumping\n- Dynamic rate control and pacing recalculated on size/speed changes\n\n## 3. Quick Start\n\nPrerequisites (typical Ubuntu 22.04+):\n\n- Latest DPDK (build + install using Meson/Ninja)\n- libbsd (`sudo apt install libbsd-dev`)\n- Hugepages configured (e.g. 1G or 2M pages) and NICs bound to `vfio-pci` or `igb_uio`\n- Python 3.x for helper scripts\n\nClone and build:\n\n```bash\ngit clone https://github.com/pktgen/Pktgen-DPDK.git\ncd Pktgen-DPDK\nmeson setup builddir\nmeson compile -C builddir\n```\n\nInitial device setup (only once per boot) then run a config:\n\n```bash\nsudo ./tools/run.py -s default   # bind devices & prepare environment\nsudo ./tools/run.py default      # launch using cfg/default.cfg\n```\n\nMinimal manual run (no helper script) example (adjust cores/ports):\n\n```bash\nsudo ./builddir/app/pktgen -l 0-3 -n 4 -- -P -m \"[1:2].0\" -T\n```\n\n## 4. Building (Details)\n\nPktgen uses Meson/Ninja. A convenience `Makefile` and `tools/pktgen-build.sh` wrap standard steps.\n\n1. Build and install DPDK so `libdpdk.pc` is installed (often under `/usr/local/lib/x86_64-linux-gnu/pkgconfig`).\n1. Export or append to `PKG_CONFIG_PATH` if that path is non‑standard:\n\n```bash\nexport PKG_CONFIG_PATH=/usr/local/lib/x86_64-linux-gnu/pkgconfig:$PKG_CONFIG_PATH\n```\n\n1. Build Pktgen:\n\n```bash\nmeson setup builddir\nmeson compile -C builddir\n```\n\nOr use:\n\n```bash\nmake          # uses Meson/Ninja under the hood\nmake rebuild  # clean reconfigure & build\nmake rebuildlua\n```\n\n1. (Optional) Install Pktgen artifacts via Meson if desired.\n\nTroubleshooting hints:\n\n- If runtime fails to find DPDK libs: run `sudo ldconfig` or add the library path to `/etc/ld.so.conf.d/`.\n- Enable IOMMU for vfio: edit GRUB adding `intel_iommu=on` (or `amd_iommu=on`) then `update-grub` and reboot.\n\n## 5. Configuration Files (`cfg/`)\n\nConfiguration files are Python fragments consumed by `tools/run.py`. They define two dictionaries: `setup` (device binding, privilege wrapper) and `run` (execution parameters).\n\nTrimmed example:\n\n```python\ndescription = 'Simple default configuration'\n\nsetup = {\n    'devices': (\n        '81:00.0','81:00.1'\n    ),\n    'uio': 'vfio-pci'\n}\n\nrun = {\n    'app_name': 'pktgen',\n    'cores': '14,15-16',      # control lcore, then worker/core ranges\n    'map': ('[15:16].0',),     # tx:rx core pair -> port\n    'opts': ('-T','-P'),       # -T: color theme, -P: promiscuous\n    'theme': 'themes/black-yellow.theme'\n}\n```\n\nCommon keys (not exhaustive):\n\n- `devices`: PCI BDFs to bind.\n- `blocklist`: exclude listed devices.\n- `cores`: control and worker core list/ranges.\n- `map`: mapping of core pairs to ports `[tx:rx].port`.\n- `opts`: extra runtime flags passed after `--`.\n- `nrank`, `proc`, `log`, `prefix`: process / logging / multi‑process tuning.\n\nSee existing examples in `cfg/` (e.g. `default.cfg`, `two-ports.cfg`, `pktgen-1.cfg`, `pktgen-2.cfg`).\n\n## 6. Configuration Key Reference\n\n| Key | Location | Type | Example | Description |\n|-----|----------|------|---------|-------------|\n| devices | setup | tuple/list | `('81:00.0','81:00.1')` | PCI BDFs to bind to DPDK |\n| uio | setup | string | `vfio-pci` | Kernel driver to bind (vfio-pci / igb_uio / uio_pci_generic) |\n| exec | setup/run | tuple | `('sudo')` | Wrapper for privileged execution |\n| app_name | run | string | `pktgen` | Binary name; resolved via `app_path` list |\n| app_path | run | tuple/list | `('./app/%(target)s/%(app_name)s', ...)` | Candidate paths to locate binary |\n| cores | run | string | `14,15-16` | Control + worker cores; ranges and commas allowed |\n| map | run | tuple/list | `('[15:16].0',)` | TX:RX core pair mapped to port id |\n| opts | run | tuple/list | `('-T','-P')` | Extra runtime flags passed after `--` |\n| theme | run | string | `themes/black-yellow.theme` | Color/theme selection |\n| blocklist | run | tuple/list | `('81:00.2',)` | Exclude listed PCI devices |\n| nrank | run | string/int | `4` | Multi-process ranking parameter (advanced) |\n| proc | run | string | `auto` | Process type / role selection |\n| log | run | string/int | `7` | Log verbosity level |\n| prefix | run | string | `pg` | DPDK shared resource (memzone) prefix |\n\n> Not all keys are required; unused advanced keys can be omitted. Refer to examples in `cfg/` for patterns.\n\n## 7. Runtime Modes & Pages\n\nModes: single (default), sequence, range, random, pcap replay, latency.\n\nDisplay pages correspond to configuration areas: `page main|seq|range|rnd|pcap|stats|xstats`.\nEach mode maintains separate packet template buffers—configure the active mode explicitly.\n\n## 8. Automation & Remote Control\n\nPktgen exposes a TCP socket (default port `22022`) offering a Lua REPL‑like interface (no prompt). Examples:\n\nInteractive with socat:\n\n```bash\nsocat -d -d READLINE TCP4:localhost:22022\n```\n\nRun a Lua script remotely:\n\n```bash\nsocat - TCP4:localhost:22022 < test/hello-world.lua\n```\n\nSingle command:\n\n```bash\necho \"f,e=loadfile('test/hello-world.lua'); f();\" | socat - TCP4:localhost:22022\n```\n\nExample script (`test/hello-world.lua`):\n\n```lua\npackage.path = package.path .. \";?.lua;test/?.lua;app/?.lua;\"\nprintf(\"Lua Version: %s\\n\", pktgen.info.Lua_Version)\nprintf(\"Pktgen Version: %s\\n\", pktgen.info.Pktgen_Version)\nprintf(\"Pktgen Copyright: %s\\n\", pktgen.info.Pktgen_Copyright)\nprintf(\"Pktgen Authors: %s\\n\", pktgen.info.Pktgen_Authors)\nprintf(\"\\nHello World!!!!\\n\")\n```\n\n## 9. Advanced Topics\n\n- Multiple instances: see `pktgen-1.cfg` / `pktgen-2.cfg` for running concurrently (ensure isolated devices/cores).\n- Themes: located under `themes/`, selected via `-T` or config `theme` key.\n- Latency: latency packets can be injected in any mode; view stats on `page stats` / latency display.\n- Capture & PCAP: capture to pcap files or replay existing pcaps (`pcap/` directory contains samples).\n- Performance tuning: pin isolated cores, match NUMA locality (ports & mempools), ensure sufficient mbufs, verify TSC stability.\n- Plugins: extend via modules under `lib/plugin`.\n\n## 10. Contributing\n\nPlease fork and submit pull requests via GitHub. Patches sent to the DPDK mailing list are not accepted for this repo.\n\nFor detailed guidelines (coding style, commit message format, documentation rules) see [`CONTRIBUTING.md`](./CONTRIBUTING.md).\nExtended Markdown formatting conventions are documented in [`docs/STYLE.md`](./docs/STYLE.md).\nCommunity expectations and reporting process: see [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md).\n\nReport issues / feature requests: <https://github.com/pktgen/Pktgen-DPDK/issues>\n\nWhen filing issues include:\n\n- Pktgen version (`cat VERSION` → current: 25.08.0)\n- DPDK version & build config\n- NIC model(s) & driver\n- Reproduction steps + minimal config\n\n### 10.1 Markdown Linting (Pre-Commit Hook)\n\nDocumentation style is enforced via markdownlint.\n\nEnable the hook:\n\n```bash\nln -sf ../../.githooks/pre-commit .git/hooks/pre-commit\nchmod +x .githooks/pre-commit\n```\n\nRequirements:\n\n```bash\nnode --version   # ensure Node.js installed\nnpm install --no-save markdownlint-cli2  # optional; hook auto-installs if missing\n```\n\nOn commit, staged `*.md` files are linted. If violations are found the commit is aborted; some fixable rules may be auto-corrected—re-add and recommit.\n\nStaged C source/header files (`*.c`, `*.h`) are auto-formatted with `clang-format` (if present) before commit.\n\n## 11. License\n\nSPDX-License-Identifier: BSD-3-Clause\nCopyright © 2010-2026 Intel Corporation\n\nFull license text is available in [`LICENSE`](./LICENSE).\n\n## 12. Related Links\n\n- Documentation: <https://pktgen.github.io/Pktgen-DPDK/>\n- Install guide (legacy / extended details): [`INSTALL.md`](./INSTALL.md)\n- Example pcaps: `pcap/`\n\n## 13. Acknowledgments\n\nCreated and maintained by Keith Wiles @ Intel Corporation with contributions from the community.\n\n---\n\nIf this tool helps your testing, consider starring the project or contributing improvements.\n\n[DPDK]: https://www.dpdk.org/\n"
  },
  {
    "path": "VERSION",
    "content": "26.03.0\n"
  },
  {
    "path": "app/.gitignore",
    "content": "pktgen\nt/libtap/libtap.a\nt/libtap/tap.o\nbuild/\nt/*.t\n!t/*.pl.t\n"
  },
  {
    "path": "app/cli-functions.c",
    "content": "/*-\n * Copyright(c) <2020-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#include \"cli-functions.h\"\n\n#include <stdio.h>\n#include <termios.h>\n#include <string.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <signal.h>\n\n#include <rte_version.h>\n#include <rte_atomic.h>\n#include <rte_devargs.h>\n#include <rte_ether.h>\n#include <rte_string_fns.h>\n#include <pg_strings.h>\n#include <rte_hexdump.h>\n#include <rte_cycles.h>\n#include <rte_malloc.h>\n\n#include <lua_config.h>\n\n#include \"pktgen.h\"\n\n#include <cli.h>\n#include <cli_map.h>\n#include <plugin.h>\n\n#include \"copyright_info.h\"\n#include \"pktgen-cmds.h\"\n#include \"pktgen-main.h\"\n#include \"lpktgenlib.h\"\n#include \"pktgen-display.h\"\n#include \"pktgen-random.h\"\n#include \"pktgen-log.h\"\n#include \"pg_ether.h\"\n#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_NET_BOND)\n#include <rte_eth_bond.h>\n#include <rte_eth_bond_8023ad.h>\n#endif\n#include <hmap.h>\n\nstatic inline uint16_t\nvalid_gtpu_teid(port_info_t *info __rte_unused, char *val)\n{\n    uint16_t gtpu_teid;\n\n    gtpu_teid = atoi(val);\n\n    return gtpu_teid;\n}\n\n/**********************************************************/\nstatic const char *title_help[] = {\n    \"   *** Pktgen Help information ***\",\n    \"\",\n    NULL,\n};\n\nstatic const char *status_help[] = {\n    \"\",\n    \"       Flags: P------------------------- - Promiscuous mode enabled\",\n    \"               E                         - ICMP Echo enabled\",\n    \"                B                        - Bonding enabled LACP 802.3ad\",\n    \"                 I                       - Process packets on input enabled\",\n    \"                  L                      - Sends latency packets\",\n    \"                   i                     - Randomizing the source IP address\",\n    \"                    p                    - Randomizing the source port\",\n    \"                     R                   - Perform bit randomization (`rnd` page)\",\n    \"                      C                  - Capture received packets\",\n    \"                       <.......>         - Modes: Single, pcap, sequence, latency, \"\n    \"Rate\",\n    \"                                <......> - Modes: VLAN, VxLAN, MPLS, QnQ, GRE IPv4, GRE ETH\",\n    \"Notes: <state>       - Use enable|disable or on|off to set the state.\",\n    \"       <portlist>    - a list of ports (no spaces) as 2,4,6-9,12 or 3-5,8 or 5 or the word \"\n    \"'all'\",\n    \"       Colors best seen on a black background for now\",\n    CLI_HELP_PAUSE,\n    NULL};\n\n#define SMMI \"%|start|minimum|maximum|increment|min|max|inc\"\n// clang-format off\nstatic struct cli_map range_map[] = {\n    {20, \"range %P dst mac \" SMMI \" %m\"},\n    {21, \"range %P src mac \" SMMI \" %m\"},\n    {22, \"range %P dst mac %m %m %m %m\"},\n    {23, \"range %P src mac %m %m %m %m\"},\n    {30, \"range %P dst ip \" SMMI \" %4\"},\n    {31, \"range %P src ip \" SMMI \" %4\"},\n    {32, \"range %P dst ip %4 %4 %4 %4\"},\n    {33, \"range %P src ip %4 %4 %4 %4\"},\n    {34, \"range %P dst ip \" SMMI \" %6\"},\n    {35, \"range %P src ip \" SMMI \" %6\"},\n    {36, \"range %P dst ip %6 %6 %6 %6\"},\n    {37, \"range %P src ip %6 %6 %6 %6\"},\n    {40, \"range %P proto %|tcp|udp\"},\n    {41, \"range %P type %|ipv4|ipv6\"},\n    {42, \"range %P tcp %|flag|flags %c\"},\n    {44, \"range %P tcp seq %d %d %d %d\"},\n    {45, \"range %P tcp ack %d %d %d %d\"},\n    {46, \"range %P tcp seq \" SMMI \" %d\"},\n    {47, \"range %P tcp ack \" SMMI \" %d\"},\n    {50, \"range %P dst port \" SMMI \" %d\"},\n    {51, \"range %P src port \" SMMI \" %d\"},\n    {52, \"range %P dst port %d %d %d %d\"},\n    {53, \"range %P src port %d %d %d %d\"},\n    {55, \"range %P ttl \" SMMI \" %b\"},\n    {56, \"range %P ttl %b %b %b %b\"},\n    {60, \"range %P vlan \" SMMI \" %d\"},\n    {61, \"range %P vlan %d %d %d %d\"},\n    {70, \"range %P size \" SMMI \" %d\"},\n    {71, \"range %P size %d %d %d %d\"},\n    {80, \"range %P mpls entry %h\"},\n    {85, \"range %P qinq index %d %d\"},\n    {90, \"range %P gre key %d\"},\n    {91, \"range %P gre_key %d\"},\n    {100, \"range %P gtpu \" SMMI \" %d\"},\n    {101, \"range %P gtpu %d %d %d %d\"},\n    {160, \"range %P cos \" SMMI \" %d\"},\n    {161, \"range %P cos %d %d %d %d\"},\n    {170, \"range %P tos \" SMMI \" %d\"},\n    {171, \"range %P tos %d %d %d %d\"},\n    {172, \"range %P hop_limits \" SMMI \" %b\"},\n    {173, \"range %P hop_limits %d %d %d %d\"},\n    {174, \"range %P traffic_class \" SMMI \" %d\"},\n    {175, \"range %P traffic_class %d %d %d %d\"},\n    {-1, NULL}\n};\n// clang-format on\n\nstatic const char *range_help[] = {\n    \"\",\n    \"  -- Setup the packet range values --\",\n    \"     note: SMMI = start|min|max|inc (start, minimum, maximum, increment)\",\n    \"\",\n    \"range <portlist> src|dst mac <SMMI> <etheraddr> - Set destination/source MAC address\",\n    \"      e.g: range 0 src mac start 00:00:00:00:00:00\",\n    \"           range 0 dst mac max 00:12:34:56:78:90\",\n    \"      or  range 0 src mac 00:00:00:00:00:00 00:00:00:00:00:00 00:12:34:56:78:90 \"\n    \"00:00:00:01:01:01\",\n    \"range <portlist> src|dst ip <SMMI> <ipaddr>   - Set source IP start address\",\n    \"      e.g: range 0 dst ip start 0.0.0.0\",\n    \"           range 0 dst ip min 0.0.0.0\",\n    \"           range 0 dst ip max 1.2.3.4\",\n    \"           range 0 dst ip inc 0.0.1.0\",\n    \"       or  range 0 dst ip 0.0.0.0 0.0.0.0 1.2.3.4 0.0.1.0\",\n    \"range <portlist> type ipv4|ipv6               - Set the range packet type to IPv4 or IPv6\",\n    \"range <portlist> proto tcp|udp                - Set the IP protocol type\",\n    \"range <portlist> tcp flags <string>           - Set comma delimited TCP flags: \"\n    \"cwr,ece,urg,ack,psh,rst,syn,fin,clr\",\n    \"range <portlist> tcp seq <SMMI> <value>       - Set the TCP sequence number\",\n    \"       or  range <portlist> tcp seq <start> <min> <max> <inc>\",\n    \"range <portlist> tcp ack <SMMI> <value>       - Set the TCP acknowledge number\",\n    \"       or  range <portlist> tcp ack <start> <min> <max> <inc>\",\n    \"range <portlist> src|dst port <SMMI> <value>  - Set UDP/TCP source/dest port number\",\n    \"       or  range <portlist> src|dst port <start> <min> <max> <inc>\",\n    \"range <portlist> vlan <SMMI> <value>          - Set vlan id start address\",\n    \"      or  range <portlist> vlan <start> <min> <max> <inc>\",\n    \"range <portlist> size <SMMI> <value>          - Set pkt size start address\",\n    \"      or  range <portlist> size <start> <min> <max> <inc>\",\n    \"range <portlist> teid <SMMI> <value>          - Set TEID value\",\n    \"      or  range <portlist> teid <start> <min> <max> <inc>\",\n    \"range <portlist> mpls entry <hex-value>       - Set MPLS entry value\",\n    \"range <portlist> qinq index <val1> <val2>     - Set QinQ index values\",\n    \"range <portlist> gre key <value>              - Set GRE key value\",\n    \"range <portlist> cos <SMMI> <value>           - Set cos value\",\n    \"range <portlist> tos <SMMI> <value>           - Set tos value\",\n    \"range <portlist> ttl <SMMI> <value>           - Set TTL\",\n    \"range <portlist> hop_limits <SMMI> <value>    - Set Hop Limits\",\n    \"range <portlist> traffic_class <SMMI> <value> - Set Traffic Class value\",\n\n    CLI_HELP_PAUSE,\n    NULL};\n\nstatic int\nrange_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n    portlist_t portlist;\n    struct pg_ipaddr ip;\n    struct rte_ether_addr mac[4];\n    char *what, *p;\n    const char *val;\n\n    m = cli_mapping(range_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"Range command error\", \"Range\", argc, argv);\n\n    portlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\n    what = argv[4];\n    val  = (const char *)argv[5];\n    switch (m->index) {\n    case 20:\n        if (pg_ether_aton(val, &mac[0]) == NULL) {\n            cli_printf(\"Failed to parse MAC address from (%s)\\n\", val);\n            break;\n        }\n        foreach_port(portlist, range_set_dest_mac(pinfo, what, &mac[0]));\n        break;\n    case 21:\n        if (pg_ether_aton(val, &mac[0]) == NULL) {\n            cli_printf(\"Failed to parse MAC address from (%s)\\n\", val);\n            break;\n        }\n        foreach_port(portlist, range_set_src_mac(pinfo, what, &mac[0]));\n        break;\n    case 22:\n        if (pg_ether_aton(argv[4], &mac[0]) == NULL) {\n            cli_printf(\"Failed to parse MAC address from (%s)\\n\", argv[4]);\n            break;\n        }\n        if (pg_ether_aton(argv[5], &mac[1]) == NULL) {\n            cli_printf(\"Failed to parse MAC address from (%s)\\n\", argv[5]);\n            break;\n        }\n        if (pg_ether_aton(argv[6], &mac[2]) == NULL) {\n            cli_printf(\"Failed to parse MAC address from (%s)\\n\", argv[6]);\n            break;\n        }\n        if (pg_ether_aton(argv[7], &mac[3]) == NULL) {\n            cli_printf(\"Failed to parse MAC address from (%s)\\n\", argv[7]);\n            break;\n        }\n        foreach_port(portlist, range_set_dest_mac(pinfo, \"start\", &mac[0]);\n                     range_set_dest_mac(pinfo, \"min\", &mac[1]);\n                     range_set_dest_mac(pinfo, \"max\", &mac[2]);\n                     range_set_dest_mac(pinfo, \"inc\", &mac[3]));\n        break;\n    case 23:\n        if (pg_ether_aton(argv[4], &mac[0]) == NULL) {\n            cli_printf(\"Failed to parse MAC address from (%s)\\n\", argv[4]);\n            break;\n        }\n        if (pg_ether_aton(argv[5], &mac[1]) == NULL) {\n            cli_printf(\"Failed to parse MAC address from (%s)\\n\", argv[5]);\n            break;\n        }\n        if (pg_ether_aton(argv[6], &mac[2]) == NULL) {\n            cli_printf(\"Failed to parse MAC address from (%s)\\n\", argv[6]);\n            break;\n        }\n        if (pg_ether_aton(argv[7], &mac[3]) == NULL) {\n            cli_printf(\"Failed to parse MAC address from (%s)\\n\", argv[7]);\n            break;\n        }\n        foreach_port(portlist, range_set_src_mac(pinfo, \"start\", &mac[0]);\n                     range_set_src_mac(pinfo, \"min\", &mac[1]);\n                     range_set_src_mac(pinfo, \"max\", &mac[2]);\n                     range_set_src_mac(pinfo, \"inc\", &mac[3]));\n        break;\n    case 30:\n        /* Remove the /XX mask value is supplied */\n        p = strchr(argv[4], '/');\n        if (p)\n            *p = '\\0';\n        _atoip(val, 0, &ip, sizeof(ip));\n        foreach_port(portlist, range_set_dst_ip(pinfo, what, &ip));\n        break;\n    case 31:\n        /* Remove the /XX mask value is supplied */\n        p = strchr(argv[4], '/');\n        if (p)\n            *p = '\\0';\n        _atoip(argv[5], 0, &ip, sizeof(ip));\n        foreach_port(portlist, range_set_src_ip(pinfo, what, &ip));\n        break;\n    case 32:\n        // clang-format off\n        foreach_port(portlist, _atoip(argv[4], PG_IPADDR_V4, &ip, sizeof(ip));\n                     range_set_dst_ip(pinfo, (char *)(uintptr_t)\"start\", &ip);\n                     _atoip(argv[5], PG_IPADDR_V4, &ip, sizeof(ip));\n                     range_set_dst_ip(pinfo, (char *)(uintptr_t)\"min\", &ip);\n                     _atoip(argv[6], PG_IPADDR_V4, &ip, sizeof(ip));\n                     range_set_dst_ip(pinfo, (char *)(uintptr_t)\"max\", &ip);\n                     _atoip(argv[7], PG_IPADDR_V4, &ip, sizeof(ip));\n                     range_set_dst_ip(pinfo, (char *)(uintptr_t)\"inc\", &ip));\n        // clang-format on\n        break;\n    case 33:\n        // clang-format off\n        foreach_port(portlist, _atoip(argv[4], PG_IPADDR_V4, &ip, sizeof(ip));\n                     range_set_src_ip(pinfo, (char *)(uintptr_t)\"start\", &ip);\n                     _atoip(argv[5], PG_IPADDR_V4, &ip, sizeof(ip));\n                     range_set_src_ip(pinfo, (char *)(uintptr_t)\"min\", &ip);\n                     _atoip(argv[6], PG_IPADDR_V4, &ip, sizeof(ip));\n                     range_set_src_ip(pinfo, (char *)(uintptr_t)\"max\", &ip);\n                     _atoip(argv[7], PG_IPADDR_V4, &ip, sizeof(ip));\n                     range_set_src_ip(pinfo, (char *)(uintptr_t)\"inc\", &ip));\n        // clang-format on\n        break;\n    case 34:\n        /* Remove the /XX mask value is supplied */\n        p = strchr(argv[4], '/');\n        if (p)\n            *p = '\\0';\n        _atoip(val, 0, &ip, sizeof(ip));\n        foreach_port(portlist, range_set_dst_ip(pinfo, what, &ip));\n        break;\n    case 35:\n        /* Remove the /XX mask value is supplied */\n        p = strchr(argv[4], '/');\n        if (p)\n            *p = '\\0';\n        _atoip(argv[5], 0, &ip, sizeof(ip));\n        foreach_port(portlist, range_set_src_ip(pinfo, what, &ip));\n        break;\n    case 36:\n        // clang-format off\n        foreach_port(portlist, _atoip(argv[4], PG_IPADDR_V6, &ip, sizeof(ip));\n                     range_set_dst_ip(pinfo, (char *)(uintptr_t)\"start\", &ip);\n                     _atoip(argv[5], PG_IPADDR_V6, &ip, sizeof(ip));\n                     range_set_dst_ip(pinfo, (char *)(uintptr_t)\"min\", &ip);\n                     _atoip(argv[6], PG_IPADDR_V6, &ip, sizeof(ip));\n                     range_set_dst_ip(pinfo, (char *)(uintptr_t)\"max\", &ip);\n                     _atoip(argv[7], PG_IPADDR_V6, &ip, sizeof(ip));\n                     range_set_dst_ip(pinfo, (char *)(uintptr_t)\"inc\", &ip));\n        // clang-format on\n        break;\n    case 37:\n        // clang-format off\n        foreach_port(portlist, _atoip(argv[4], PG_IPADDR_V6, &ip, sizeof(ip));\n                     range_set_src_ip(pinfo, (char *)(uintptr_t)\"start\", &ip);\n                     _atoip(argv[5], PG_IPADDR_V6, &ip, sizeof(ip));\n                     range_set_src_ip(pinfo, (char *)(uintptr_t)\"min\", &ip);\n                     _atoip(argv[6], PG_IPADDR_V6, &ip, sizeof(ip));\n                     range_set_src_ip(pinfo, (char *)(uintptr_t)\"max\", &ip);\n                     _atoip(argv[7], PG_IPADDR_V6, &ip, sizeof(ip));\n                     range_set_src_ip(pinfo, (char *)(uintptr_t)\"inc\", &ip));\n        // clang-format on\n        break;\n    case 40:\n        foreach_port(portlist, range_set_proto(pinfo, argv[3]));\n        break;\n    case 41:\n        foreach_port(portlist, range_set_pkt_type(pinfo, argv[3]));\n        break;\n    case 42:\n        foreach_port(portlist, range_set_tcp_flags(pinfo, argv[4]));\n        break;\n    case 44:\n        // clang-format off\n        foreach_port(portlist, range_set_tcp_seq(pinfo, (char *)(uintptr_t)\"start\", atoi(argv[4]));\n                     range_set_tcp_seq(pinfo, (char *)(uintptr_t)\"min\", atoi(argv[5]));\n                     range_set_tcp_seq(pinfo, (char *)(uintptr_t)\"max\", atoi(argv[6]));\n                     range_set_tcp_seq(pinfo, (char *)(uintptr_t)\"inc\", atoi(argv[7])));\n        // clang-format on\n        break;\n    case 45:\n        // clang-format off\n        foreach_port(portlist, range_set_tcp_ack(pinfo, (char *)(uintptr_t)\"start\", atoi(argv[4]));\n                     range_set_tcp_ack(pinfo, (char *)(uintptr_t)\"min\", atoi(argv[5]));\n                     range_set_tcp_ack(pinfo, (char *)(uintptr_t)\"max\", atoi(argv[6]));\n                     range_set_tcp_ack(pinfo, (char *)(uintptr_t)\"inc\", atoi(argv[7])));\n        // clang-format on\n        break;\n    case 46:\n        foreach_port(portlist, range_set_tcp_seq(pinfo, what, atoi(val)));\n        break;\n    case 47:\n        foreach_port(portlist, range_set_tcp_ack(pinfo, what, atoi(val)));\n        break;\n    case 50:\n        foreach_port(portlist, range_set_dst_port(pinfo, what, atoi(val)));\n        break;\n    case 51:\n        foreach_port(portlist, range_set_src_port(pinfo, what, atoi(val)));\n        break;\n    case 52:\n        // clang-format off\n        foreach_port(portlist, range_set_dst_port(pinfo, (char *)(uintptr_t)\"start\", atoi(argv[4]));\n                     range_set_dst_port(pinfo, (char *)(uintptr_t)\"min\", atoi(argv[5]));\n                     range_set_dst_port(pinfo, (char *)(uintptr_t)\"max\", atoi(argv[6]));\n                     range_set_dst_port(pinfo, (char *)(uintptr_t)\"inc\", atoi(argv[7])));\n        // clang-format on\n        break;\n    case 53:\n        // clang-format off\n        foreach_port(portlist, range_set_src_port(pinfo, (char *)(uintptr_t)\"start\", atoi(argv[4]));\n                     range_set_src_port(pinfo, (char *)(uintptr_t)\"min\", atoi(argv[5]));\n                     range_set_src_port(pinfo, (char *)(uintptr_t)\"max\", atoi(argv[6]));\n                     range_set_src_port(pinfo, (char *)(uintptr_t)\"inc\", atoi(argv[7])));\n        // clang-format on\n        break;\n    case 55:\n        foreach_port(portlist, range_set_ttl(pinfo, argv[3], atoi(argv[4])));\n        break;\n    case 56:\n        // clang-format off\n        foreach_port(portlist, range_set_ttl(pinfo, (char *)(uintptr_t)\"start\", atoi(argv[3]));\n                     range_set_ttl(pinfo, (char *)(uintptr_t)\"min\", atoi(argv[4]));\n                     range_set_ttl(pinfo, (char *)(uintptr_t)\"max\", atoi(argv[5]));\n                     range_set_ttl(pinfo, (char *)(uintptr_t)\"inc\", atoi(argv[6])));\n        // clang-format on\n        break;\n    case 60:\n        foreach_port(portlist, range_set_vlan_id(pinfo, argv[3], atoi(what)));\n        break;\n    case 61:\n        // clang-format off\n        foreach_port(portlist, range_set_vlan_id(pinfo, (char *)(uintptr_t)\"start\", atoi(argv[3]));\n                     range_set_vlan_id(pinfo, (char *)(uintptr_t)\"min\", atoi(argv[4]));\n                     range_set_vlan_id(pinfo, (char *)(uintptr_t)\"max\", atoi(argv[5]));\n                     range_set_vlan_id(pinfo, (char *)(uintptr_t)\"inc\", atoi(argv[6])));\n        // clang-format on\n        break;\n    case 70:\n        foreach_port(portlist,\n                     range_set_pkt_size(pinfo, argv[3],\n                                        strcmp(\"inc\", argv[3]) ? atoi(argv[4]) : atoi(argv[3])));\n        break;\n    case 71:\n        // clang-format off\n        foreach_port(portlist, range_set_pkt_size(pinfo, (char *)(uintptr_t)\"start\", atoi(argv[3]));\n                     range_set_pkt_size(pinfo, (char *)(uintptr_t)\"min\", atoi(argv[4]));\n                     range_set_pkt_size(pinfo, (char *)(uintptr_t)\"max\", atoi(argv[5]));\n                     range_set_pkt_size(pinfo, (char *)(uintptr_t)\"inc\", atoi(argv[6])););\n        // clang-format on\n        break;\n    case 80:\n        foreach_port(portlist, range_set_mpls_entry(pinfo, strtoul(what, NULL, 16)));\n        break;\n    case 85:\n        foreach_port(portlist, range_set_qinqids(pinfo, atoi(what), atoi(val)));\n        break;\n    case 90:\n    case 91:\n        foreach_port(portlist, range_set_gre_key(pinfo, strtoul(what, NULL, 10)));\n        break;\n    case 100:\n        foreach_port(portlist,\n                     range_set_gtpu_teid(pinfo, argv[3],\n                                         strcmp(\"inc\", argv[3]) ? valid_gtpu_teid(pinfo, what)\n                                                                : atoi(what)));\n        break;\n    case 101:\n        // clang-format off\n        foreach_port(\n            portlist,\n            range_set_gtpu_teid(pinfo, (char *)(uintptr_t)\"start\", valid_gtpu_teid(pinfo, argv[3]));\n            range_set_gtpu_teid(pinfo, (char *)(uintptr_t)\"min\", valid_gtpu_teid(pinfo, argv[4]));\n            range_set_gtpu_teid(pinfo, (char *)(uintptr_t)\"max\", valid_gtpu_teid(pinfo, argv[5]));\n            range_set_gtpu_teid(pinfo, (char *)(uintptr_t)\"inc\", atoi(argv[6])););\n        // clang-format on\n        break;\n    case 160:\n        foreach_port(portlist, range_set_cos_id(pinfo, argv[3], atoi(what)));\n        break;\n    case 161:\n        // clang-format off\n        foreach_port(portlist, range_set_cos_id(pinfo, (char *)(uintptr_t)\"start\", atoi(argv[3]));\n                     range_set_cos_id(pinfo, (char *)(uintptr_t)\"min\", atoi(argv[4]));\n                     range_set_cos_id(pinfo, (char *)(uintptr_t)\"max\", atoi(argv[5]));\n                     range_set_cos_id(pinfo, (char *)(uintptr_t)\"inc\", atoi(argv[6])););\n        // clang-format on\n        break;\n    case 170:\n        foreach_port(portlist, range_set_tos_id(pinfo, argv[3], atoi(what)));\n        break;\n    case 171:\n        // clang-format off\n        foreach_port(portlist, range_set_tos_id(pinfo, (char *)(uintptr_t)\"start\", atoi(argv[3]));\n                     range_set_tos_id(pinfo, (char *)(uintptr_t)\"min\", atoi(argv[4]));\n                     range_set_tos_id(pinfo, (char *)(uintptr_t)\"max\", atoi(argv[5]));\n                     range_set_tos_id(pinfo, (char *)(uintptr_t)\"inc\", atoi(argv[6])));\n        // clang-format on\n        break;\n    case 172:\n        foreach_port(portlist, range_set_hop_limits(pinfo, argv[3], atoi(what)));\n        break;\n    case 173:\n        // clang-format off\n        foreach_port(portlist,\n                     range_set_hop_limits(pinfo, (char *)(uintptr_t)\"start\", atoi(argv[3]));\n                     range_set_hop_limits(pinfo, (char *)(uintptr_t)\"min\", atoi(argv[4]));\n                     range_set_hop_limits(pinfo, (char *)(uintptr_t)\"max\", atoi(argv[5]));\n                     range_set_hop_limits(pinfo, (char *)(uintptr_t)\"inc\", atoi(argv[6])));\n        // clang-format on\n        break;\n    case 174:\n        foreach_port(portlist, range_set_traffic_class(pinfo, argv[3], atoi(what)));\n        break;\n    case 175:\n        // clang-format off\n        foreach_port(portlist,\n                     range_set_traffic_class(pinfo, (char *)(uintptr_t)\"start\", atoi(argv[3]));\n                     range_set_traffic_class(pinfo, (char *)(uintptr_t)\"min\", atoi(argv[4]));\n                     range_set_traffic_class(pinfo, (char *)(uintptr_t)\"max\", atoi(argv[5]));\n                     range_set_traffic_class(pinfo, (char *)(uintptr_t)\"inc\", atoi(argv[6])));\n        // clang-format on\n        break;\n\n    default:\n        return cli_cmd_error(\"Range command error\", \"Range\", argc, argv);\n    }\n    pktgen_update_display();\n    return 0;\n}\n\n#define set_types         \\\n    \"count|\"     /*  0 */ \\\n    \"size|\"      /*  1 */ \\\n    \"rate|\"      /*  2 */ \\\n    \"burst|\"     /*  3 */ \\\n    \"tx_cycles|\" /*  4 */ \\\n    \"sport|\"     /*  5 */ \\\n    \"dport|\"     /*  6 */ \\\n    \"prime|\"     /*  7 */ \\\n    \"dump|\"      /*  8 */ \\\n    \"vlan|\"      /*  9 */ \\\n    \"vlanid|\"    /* 10 */ \\\n    \"seq_cnt|\"   /* 11 */ \\\n    \"seqCnt|\"    /* 12 */ \\\n    \"seqcnt|\"    /* 13 */ \\\n    \"ttl|\"       /* 14 */ \\\n    \"txburst|\"   /* 15 */ \\\n    \"rxburst\"    /* 16 */\n\n// clang-format off\nstatic struct cli_map set_map[] = {\n    {10, \"set %P %|\" set_types \" %d\"},\n    {11, \"set %P jitter %D\"},\n    {20, \"set %P type %|arp|ipv4|ipv6|ip4|ip6|vlan\"},\n    {21, \"set %P proto %|udp|tcp|icmp\"},\n    {22, \"set %P src mac %m\"},\n    {23, \"set %P dst mac %m\"},\n    {24, \"set %P pattern %|abc|none|user|zero\"},\n    {25, \"set %P user pattern %s\"},\n    {30, \"set %P src ip %4\"},\n    {31, \"set %P dst ip %4\"},\n    {32, \"set %P src ip %6\"},\n    {33, \"set %P dst ip %6\"},\n    {34, \"set %P tcp %|flag|flags %c\"},\n    {36, \"set %P tcp seq %d\"},\n    {37, \"set %P tcp ack %d\"},\n    {40, \"set ports_per_page %d\"},\n    {50, \"set %P qinqids %d %d\"},\n    {60, \"set %P rnd %d %d %s\"},\n    {70, \"set %P cos %d\"},\n    {80, \"set %P tos %d\"},\n    {90, \"set %P vxlan %h %d %d\"},\n    {100, \"set %P latsampler %|simple|poisson %d %d %s\"},\n    {-1, NULL}\n};\n// clang format on\n\nstatic const char *set_help[] = {\n    \"\",\n    \"    note: <portlist>               - a list of ports (no spaces) e.g. 2,4,6-9,12 or the word \"\n    \"'all'\",\n    \"set <portlist> count <value>       - number of packets to transmit\",\n    \"set <portlist> size <value>        - size of the packet to transmit\",\n    \"set <portlist> rate <percent>      - Packet rate in percentage\",\n    \"set <portlist> txburst|burst <value> - number of packets in a TX burst\",\n    \"set <portlist> rxburst <value>     - number of packets in a RX burst\",\n    \"set <portlist> tx_cycles <value>   - DEBUG to set the number of cycles per TX burst\",\n    \"set <portlist> sport <value>       - Source port number for UDP/TCP\",\n    \"set <portlist> dport <value>       - Destination port number for UDP/TCP\",\n    \"set <portlist> ttl <value>         - Set the TTL value for the single port more\",\n    \"set <portlist> seq_cnt|seqcnt|seqCnt <value>\",\n    \"                                   - Set the number of packet in the sequence to send [0-16]\",\n    \"set <portlist> prime <value>       - Set the number of packets to send on prime command\",\n    \"set <portlist> dump <value>        - Dump the next 1-32 received packets to the screen\",\n    \"                                     Dumped packets are in the log, use 'page log' to view\",\n    \"set <portlist> vlan|vlanid <value> - Set the VLAN ID value for the portlist\",\n    \"set <portlist> jitter <value>      - Set the jitter threshold in micro-seconds\",\n    \"set <portlist> src|dst mac <addr>  - Set MAC addresses 00:11:22:33:44:55 or 0011:2233:4455 \"\n    \"format\",\n    \"set <portlist> type ipv4|ipv6|vlan|arp - Set the packet type to IPv4 or IPv6 or VLAN\",\n    \"set <portlist> proto udp|tcp|icmp  - Set the packet protocol to UDP or TCP or ICMP per port\",\n    \"set <portlist> pattern <type>      - Set the fill pattern type\",\n    \"                 type - abc        - Default pattern of abc string\",\n    \"                        none       - No fill pattern, maybe random data\",\n    \"                        zero       - Fill of zero bytes\",\n    \"                        user       - User supplied string of max 16 bytes\",\n    \"set <portlist> user pattern <string> - A 16 byte string, must set 'pattern user' command\",\n    \"set <portlist> [src|dst] ip ipaddr - Set IP addresses, Source must include network mask e.g. \"\n    \"10.1.2.3/24\",\n    \"set <portlist> tcp flags <string>  - Set comma delimited TCP flags: cwr,ece,urg,ack,psh,rst,syn,fin,clr\",\n    \"set <portlist> tcp seq <sequence>  - Set the TCP sequence number\",\n    \"set <portlist> tcp ack <acknowledge> - Set the TCP acknowledge number\",\n    \"set <portlist> qinqids <id1> <id2> - Set the Q-in-Q ID's for the portlist\",\n    \"set <portlist> rnd <idx> <off> <mask> - Set random mask for all transmitted packets from \"\n    \"portlist\",\n    \"    idx: random mask index slot\",\n    \"    off: offset in bytes to apply mask value\",\n    \"    mask: up to 32 bit long mask specification (empty to disable):\",\n    \"          0: bit will be 0\",\n    \"          1: bit will be 1\",\n    \"          .: bit will be ignored (original value is retained)\",\n    \"          X: bit will get random value\",\n    \"set <portlist> cos <value>         - Set the CoS value for the portlist\",\n    \"set <portlist> tos <value>         - Set the ToS value for the portlist\",\n    \"set <portlist> vxlan <flags> <group id> <vxlan_id> - Set the vxlan values\",\n    \"set <portlist> latsampler [simple|poisson] <num-samples> <rate> <outfile>\t- Set latency \"\n    \"sampler parameters\",\n    \"\t\tnum-samples: number of samples.\",\n    \"\t\trate: sampling rate i.e., samples per second.\",\n    \"\t\toutfile: path to output file to dump all sampled latencies\",\n    \"set ports_per_page <value>         - Set ports per page value 1 - 6\",\n    CLI_HELP_PAUSE,\n    NULL};\n\nstatic int\nset_cmd(int argc, char **argv)\n{\n    portlist_t portlist;\n    struct rte_ether_addr mac;\n    char *what, *p;\n    int value, n;\n    struct cli_map *m;\n    struct pg_ipaddr ip;\n    uint16_t id1, id2;\n    uint32_t u1, u2;\n    int ip_ver;\n\n    m = cli_mapping(set_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"Set command is invalid\", \"Set\", argc, argv);\n\n    portlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\n    what  = argv[2];\n    value = atoi(argv[3]);\n\n    switch (m->index) {\n    case 10:\n        n = cli_map_list_search(m->fmt, argv[2], 2);\n        foreach_port(portlist, _do(switch (n) {\n            case 0:\n                single_set_tx_count(pinfo, value);\n                break;\n            case 1:\n                single_set_pkt_size(pinfo, atoi(argv[3]));\n                break;\n            case 2:\n                single_set_tx_rate(pinfo, argv[3]);\n                break;\n            case 3:\n                single_set_tx_burst(pinfo, value);\n                break;\n            case 4:\n                debug_set_tx_cycles(pinfo, value);\n                break;\n            case 5:\n                single_set_port_value(pinfo, what[0], value);\n                break;\n            case 6:\n                single_set_port_value(pinfo, what[0], value);\n                break;\n            case 7:\n                pktgen_set_port_prime(pinfo, value);\n                break;\n            case 8:\n                debug_set_port_dump(pinfo, value);\n                break;\n            case 9: /* vlanid and vlan are valid */\n            case 10:\n                single_set_vlan_id(pinfo, value);\n                break;\n            case 11:\n                /* FALLTHRU */\n            case 12:\n                /* FALLTHRU */\n            case 13:\n                pktgen_set_port_seqCnt(pinfo, value);\n                break;\n            case 14:\n                single_set_ttl_value(pinfo, value);\n                break;\n            case 15:\n                single_set_tx_burst(pinfo, value);\n                break;\n            case 16:\n                single_set_rx_burst(pinfo, value);\n                break;\n            default:\n                return cli_cmd_error(\"Set command is invalid\", \"Set\", argc, argv);\n        }));\n        break;\n    case 11:\n        foreach_port(portlist, single_set_jitter(pinfo, strtoull(argv[3], NULL, 0)));\n        break;\n    case 20:\n        foreach_port(portlist, single_set_pkt_type(pinfo, argv[3]));\n        break;\n    case 21:\n        foreach_port(portlist, single_set_proto(pinfo, argv[3]));\n        break;\n    case 22:\n        if (pg_ether_aton(argv[4], &mac) == NULL) {\n            cli_printf(\"Failed to parse MAC address from (%s)\\n\", argv[4]);\n            break;\n        }\n        foreach_port(portlist, single_set_src_mac(pinfo, &mac));\n        break;\n    case 23:\n        if (pg_ether_aton(argv[4], &mac) == NULL) {\n            cli_printf(\"Failed to parse MAC address from (%s)\\n\", argv[4]);\n            break;\n        }\n        foreach_port(portlist, single_set_dst_mac(pinfo, &mac));\n        break;\n    case 24:\n        foreach_port(portlist, pattern_set_type(pinfo, argv[3]));\n        break;\n    case 25:\n        foreach_port(portlist, pattern_set_user_pattern(pinfo, argv[4]));\n        break;\n    case 30:\n        p = strchr(argv[4], '/');\n        if (!p)\n            cli_printf(\"src IP address should contain subnet value, default /32 for IPv4\\n\");\n        ip_ver = _atoip(argv[4], PG_IPADDR_V4 | PG_IPADDR_NETWORK, &ip, sizeof(ip));\n        foreach_port(portlist, single_set_ipaddr(pinfo, 's', &ip, ip_ver));\n        break;\n    case 31:\n        /* Remove the /XX mask value if supplied */\n        p = strchr(argv[4], '/');\n        if (p) {\n            cli_printf(\"Subnet mask not required, removing subnet mask value\\n\");\n            *p = '\\0';\n        }\n        ip_ver = _atoip(argv[4], PG_IPADDR_V4, &ip, sizeof(ip));\n        foreach_port(portlist, single_set_ipaddr(pinfo, 'd', &ip, ip_ver));\n        break;\n    case 32:\n        p = strchr(argv[4], '/');\n        if (!p)\n            cli_printf(\"src IP address should contain subnet value, default /128 for IPv6\\n\");\n\n        ip_ver = _atoip(argv[4], PG_IPADDR_V6 | PG_IPADDR_NETWORK, &ip, sizeof(ip));\n        foreach_port(portlist, single_set_ipaddr(pinfo, 's', &ip, ip_ver));\n        break;\n    case 33:\n        /* Remove the /XX mask value if supplied */\n        p = strchr(argv[4], '/');\n        if (p) {\n            cli_printf(\"Subnet mask not required, removing subnet mask value\\n\");\n            *p = '\\0';\n        }\n        ip_ver = _atoip(argv[4], PG_IPADDR_V6, &ip, sizeof(ip));\n        foreach_port(portlist, single_set_ipaddr(pinfo, 'd', &ip, ip_ver));\n        break;\n    case 34:\n        foreach_port(portlist, single_set_tcp_flags(pinfo, argv[4]));\n        break;\n    case 36:\n        foreach_port(portlist, single_set_tcp_seq(pinfo, atoi(argv[4])));\n        break;\n    case 37:\n        foreach_port(portlist, single_set_tcp_ack(pinfo, atoi(argv[4])));\n        break;\n    case 40:\n        pktgen_set_page_size(atoi(argv[2]));\n        break;\n    case 50:\n        id1 = strtol(argv[3], NULL, 0);\n        id2 = strtol(argv[4], NULL, 0);\n        foreach_port(portlist, single_set_qinqids(pinfo, id1, id2));\n        break;\n    case 60: {\n        char mask[34] = {0}, *m;\n        char cb;\n\n        id1 = strtol(argv[3], NULL, 0);\n        id2 = strtol(argv[4], NULL, 0);\n        m   = argv[5];\n        if (strcmp(m, \"off\")) {\n            int idx;\n            /* Filter invalid characters from provided mask. This way the user can\n             * more easily enter long bitmasks, using for example '_' as a separator\n             * every 8 bits. */\n            for (n = 0, idx = 0; (idx < 32) && ((cb = m[n]) != '\\0'); n++)\n                if ((cb == '0') || (cb == '1') || (cb == '.') || (cb == 'X') || (cb == 'x'))\n                    mask[idx++] = cb;\n        }\n        foreach_port(portlist, enable_random(pinfo, pktgen_set_random_bitfield(pinfo->rnd_bitfields,\n                                                                              id1, id2, mask)\n                                                       ? ENABLE_STATE\n                                                       : DISABLE_STATE));\n    } break;\n    case 70:\n        id1 = strtol(argv[3], NULL, 0);\n        foreach_port(portlist, single_set_cos(pinfo, id1));\n        break;\n    case 80:\n        id1 = strtol(argv[3], NULL, 0);\n        foreach_port(portlist, single_set_tos(pinfo, id1));\n        break;\n    case 90:\n        id1 = strtol(argv[3], NULL, 0);\n        id2 = strtol(argv[4], NULL, 0);\n        u1  = strtol(argv[5], NULL, 0);\n        foreach_port(portlist, single_set_vxlan(pinfo, id1, id2, u1));\n        break;\n    case 100:\n        u1 = strtol(argv[4], NULL, 0);\n        u2 = strtol(argv[5], NULL, 0);\n        foreach_port(portlist, single_set_latsampler_params(pinfo, argv[3], u1, u2, argv[6]));\n        break;\n    default:\n        return cli_cmd_error(\"Command invalid\", \"Set\", argc, argv);\n    }\n\n    pktgen_update_display();\n    return 0;\n}\n\n// clang-format off\nstatic struct cli_map pcap_map[] = {\n    {10, \"pcap %D\"},\n    {20, \"pcap show\"},\n    {21, \"pcap show all\"},\n    {30, \"pcap filter %P %s\"},\n    {-1, NULL}\n};\n// clang-format on\n\nstatic const char *pcap_help[] = {\n    \"\",\n    \"pcap show                          - Show PCAP information\",\n    \"pcap <index>                       - Move the PCAP file index to the given packet number,\\n   \"\n    \"    0 - rewind, -1 - end of file\",\n    \"pcap filter <portlist> <string>    - PCAP filter string to filter packets on receive\",\n    CLI_HELP_PAUSE,\n    NULL};\n\nstatic int\npcap_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n    pcap_info_t *pcap;\n    uint32_t max_cnt;\n    uint32_t value;\n    portlist_t portlist;\n    port_info_t *pinfo;\n\n    m = cli_mapping(pcap_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"PCAP command invalid\", \"PCAP\", argc, argv);\n\n    pinfo = l2p_get_port_pinfo(pktgen.curr_port);\n    switch (m->index) {\n    case 10:\n        pcap  = l2p_get_pcap(pinfo->pid);\n        value = strtoul(argv[1], NULL, 10);\n\n        if (pcap) {\n            max_cnt = pcap->pkt_count;\n            if (value >= max_cnt)\n                pcap->pkt_index = max_cnt - RTE_MIN(PCAP_PAGE_SIZE, (int)max_cnt);\n            else\n                pcap->pkt_index = value;\n            pktgen.flags |= PRINT_LABELS_FLAG;\n        } else\n            pktgen_log_error(\" ** PCAP file is not loaded on port %d\", pktgen.curr_port);\n        break;\n    case 20:\n        pcap = l2p_get_pcap(pinfo->pid);\n        if (pcap)\n            pktgen_pcap_info(pcap, pktgen.curr_port, 1);\n        else\n            pktgen_log_error(\" ** PCAP file is not loaded on port %d\", pktgen.curr_port);\n        break;\n    case 21:\n        for (int pid = 0; pid < pktgen.nb_ports; pid++) {\n            pcap = l2p_get_pcap(pid);\n            if (pcap)\n                pktgen_pcap_info(pcap, pid, 1);\n        }\n        break;\n    case 30:\n        portlist_parse(argv[2], pktgen.nb_ports, &portlist);\n        foreach_port(portlist, pcap_filter(pinfo, argv[3]));\n        break;\n    default:\n        return cli_cmd_error(\"PCAP command invalid\", \"PCAP\", argc, argv);\n    }\n    pktgen_update_display();\n    return 0;\n}\n\n// clang-format off\nstatic struct cli_map start_map[] = {\n    {10, \"start %P\"},\n    {20, \"stop %P\"},\n    {40, \"start %P prime\"},\n    {50, \"start %P arp %|request|gratuitous|req|grat\"},\n    {60, \"start %P latsampler\"},\n    {70, \"stop %P latsampler\"},\n    {-1, NULL}\n};\n// clang-format on\n\nstatic const char *start_help[] = {\n    \"\",\n    \"start <portlist>                   - Start transmitting packets\",\n    \"stop <portlist>                    - Stop transmitting packets\",\n    \"stp                                - Stop all ports from transmitting\",\n    \"str                                - Start all ports transmitting\",\n    \"start <portlist> prime             - Transmit packets on each port listed. See set prime \"\n    \"command above\",\n    \"start <portlist> arp <type>        - Send a ARP type packet\",\n    \"    type - request | gratuitous | req | grat\",\n    \"start <portlist> latsampler        - Start latency sampler, make sure to set sampling \"\n    \"parameters before starting\",\n    \"stop <portlist> latsampler        \t- Stop latency sampler, dumps to file if specified\",\n    CLI_HELP_PAUSE,\n    NULL};\n\nstatic int\nstart_stop_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n    portlist_t portlist;\n\n    m = cli_mapping(start_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"Start/Stop command invalid\", \"Start\", argc, argv);\n\n    portlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\n    switch (m->index) {\n    case 10:\n        foreach_port(portlist, pktgen_start_transmitting(pinfo));\n        break;\n    case 20:\n        foreach_port(portlist, pktgen_stop_transmitting(pinfo));\n        break;\n    case 40:\n        foreach_port(portlist, pktgen_prime_ports(pinfo));\n        break;\n    case 50:\n        if (argv[3][0] == 'g')\n            foreach_port(portlist, pktgen_send_arp_requests(pinfo, GRATUITOUS_ARP));\n        else\n            foreach_port(portlist, pktgen_send_arp_requests(pinfo, 0));\n        break;\n    case 60:\n        foreach_port(portlist, pktgen_start_latency_sampler(pinfo));\n        break;\n    case 70:\n        foreach_port(portlist, pktgen_stop_latency_sampler(pinfo));\n        break;\n    default:\n        return cli_cmd_error(\"Start/Stop command invalid\", \"Start\", argc, argv);\n    }\n    pktgen_update_display();\n    return 0;\n}\n\n// clang-format off\nstatic struct cli_map theme_map[] = {\n    {0, \"theme\"},\n    {10, \"theme %|on|off\"},\n    {20, \"theme %s %s %s %s\"},\n    {30, \"theme save %s\"},\n    {-1, NULL}\n};\n// clang-format on\n\nstatic const char *theme_help[] = {\n    \"\",\n    \"theme <item> <fg> <bg> <attr>      - Set color for item with fg/bg color and attribute value\",\n    \"theme show                         - List the item strings, colors and attributes to the \"\n    \"items\",\n    \"theme save <filename>              - Save the current color theme to a file\",\n    CLI_HELP_PAUSE,\n    NULL};\n\nstatic int\ntheme_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n\n    m = cli_mapping(theme_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"Theme command invalid\", \"Theme\", argc, argv);\n\n    switch (m->index) {\n    case 0:\n        pktgen_theme_show();\n        break;\n    case 10:\n        pktgen_theme_state(argv[1]);\n        pktgen_clear_display();\n        break;\n    case 20:\n        pktgen_set_theme_item(argv[1], argv[2], argv[3], argv[4]);\n        break;\n    case 30:\n        pktgen_theme_save(argv[2]);\n        break;\n    default:\n        return cli_help_show_group(\"Theme\");\n    }\n    return 0;\n}\n\n#define ed_type          \\\n    \"process|\"  /*  0 */ \\\n    \"mpls|\"     /*  1 */ \\\n    \"qinq|\"     /*  2 */ \\\n    \"gre|\"      /*  3 */ \\\n    \"gre_eth|\"  /*  4 */ \\\n    \"vlan|\"     /*  5 */ \\\n    \"random|\"   /*  6 */ \\\n    \"latency|\"  /*  7 */ \\\n    \"pcap|\"     /*  8 */ \\\n    \"blink|\"    /*  9 */ \\\n    \"icmp|\"     /* 10 */ \\\n    \"range|\"    /* 11 */ \\\n    \"capture|\"  /* 12 */ \\\n    \"bonding|\"  /* 13 */ \\\n    \"vxlan|\"    /* 14 */ \\\n    \"rate|\"     /* 15 */ \\\n    \"rnd_s_ip|\" /* 16 */ \\\n    \"rnd_s_pt|\" /* 17 */ \\\n    \"lat\"       /* 18 */\n\n// clang-format off\nstatic struct cli_map enable_map[] = {\n    {10, \"enable %P %|\" ed_type},\n    {20, \"disable %P %|\" ed_type},\n    {30, \"enable %|screen|mac_from_arp\"},\n    {31, \"disable %|screen|mac_from_arp\"},\n    {40, \"enable clock_gettime\"},\n    {41, \"disable clock_gettime\"},\n    {-1, NULL}\n};\n// clang-format off\n\nstatic const char *enable_help[] = {\n    \"\",\n    \"enable|disable <portlist> process  - Enable or Disable processing of ARP/ICMP/IPv4/IPv6 \"\n    \"packets\",\n    \"enable|disable <portlist> mpls     - Enable/disable sending MPLS entry in packets\",\n    \"enable|disable <portlist> qinq     - Enable/disable sending Q-in-Q header in packets\",\n    \"enable|disable <portlist> gre      - Enable/disable GRE support\",\n    \"enable|disable <portlist> gre_eth  - Enable/disable GRE with Ethernet frame payload\",\n    \"enable|disable <portlist> vlan     - Enable/disable VLAN tagging\",\n    \"enable|disable <portlist> rnd_s_ip - Enable/disable randomizing the source IP address on \"\n    \"every packet\",\n    \"enable|disable <portlist> rnd_s_pt - Enable/disable randomizing the source port on every packet\",\n    \"enable|disable <portlist> random   - Enable/disable Random packet support through the `rnd` page\",\n    \"enable|disable <portlist> latency  - Enable/disable latency testing\",\n    \"enable|disable <portlist> pcap     - Enable or Disable sending pcap packets on a portlist\",\n    \"enable|disable <portlist> blink    - Blink LED on port(s)\",\n    \"enable|disable <portlist> icmp     - Enable/Disable sending ICMP packets\",\n    \"enable|disable <portlist> range    - Enable or Disable the given portlist for sending a range \"\n    \"of packets\",\n    \"enable|disable <portlist> capture  - Enable/disable packet capturing on a portlist, will \"\n    \"capture RX packets\",\n    \"                                     Disable capture on a port to save the data into the \"\n    \"current working directory.\",\n    \"enable|disable <portlist> bonding  - Enable call TX with zero packets for bonding driver\",\n    \"enable|disable <portlist> vxlan    - Send VxLAN packets\",\n    \"enable|disable mac_from_arp        - Enable/disable MAC address from ARP packet\",\n    \"enable|disable clock_gettime       - Enable/disable use of new clock_gettime() instead of rdtsc()\",\n    \"enable|disable screen              - Enable/disable updating the screen and unlock/lock window\",\n    \"    off                            - screen disable shortcut\",\n    \"    on                             - screen enable shortcut\",\n    CLI_HELP_PAUSE,\n    NULL};\n\nstatic int\nen_dis_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n    portlist_t portlist;\n    int n, state;\n\n    m = cli_mapping(enable_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"Enable/Disable invalid command\", \"Enable\", argc, argv);\n\n    portlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\n    switch (m->index) {\n    case 10:\n    case 20:\n        n = cli_map_list_search(m->fmt, argv[2], 2);\n\n        state = estate(argv[0]);\n\n        switch (n) {\n        case 0: // process\n            foreach_port(portlist, enable_process(pinfo, state));\n            break;\n        case 1: // mpls\n            foreach_port(portlist, enable_mpls(pinfo, state));\n            break;\n        case 2: // qinq\n            foreach_port(portlist, enable_qinq(pinfo, state));\n            break;\n        case 3: // gre\n            foreach_port(portlist, enable_gre(pinfo, state));\n            break;\n        case 4: // gre_eth\n            foreach_port(portlist, enable_gre_eth(pinfo, state));\n            break;\n        case 5: // vlan\n            foreach_port(portlist, enable_vlan(pinfo, state));\n            break;\n        case 6: // random\n            foreach_port(portlist, enable_random(pinfo, state));\n            break;\n        case 7: // latency\n            foreach_port(portlist, enable_latency(pinfo, state));\n            break;\n        case 8: // pcap\n            foreach_port(portlist, enable_pcap(pinfo, state));\n            break;\n        case 9: // blink\n            foreach_port(portlist, debug_blink(pinfo, state));\n\n            if (pktgen.blinklist)\n                pktgen.flags |= BLINK_PORTS_FLAG;\n            else\n                pktgen.flags &= ~BLINK_PORTS_FLAG;\n            break;\n        case 10: // icmp\n            foreach_port(portlist, enable_icmp_echo(pinfo, state));\n            break;\n        case 11: // range\n            foreach_port(portlist, enable_range(pinfo, state));\n            break;\n        case 12: // capture\n            foreach_port(portlist, pktgen_set_capture(pinfo, state));\n            break;\n        case 13: // bonding\n#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_NET_BOND)\n            foreach_port(portlist, enable_bonding(pinfo, state));\n#endif\n            break;\n        case 14: // vxlan\n            foreach_port(portlist, enable_vxlan(pinfo, state));\n            break;\n        case 16: // rnd_s_ip\n            foreach_port(portlist, enable_rnd_s_ip(pinfo, state));\n            break;\n        case 17: // rnd_s_pt\n            foreach_port(portlist, enable_rnd_s_pt(pinfo, state));\n            break;\n        case 18: // lat type alias latency\n            foreach_port(portlist, enable_latency(pinfo, state));\n            break;\n        default:\n            return cli_cmd_error(\"Enable/Disable invalid command\", \"Enable\", argc, argv);\n        }\n        break;\n\n    case 30:\n    case 31:\n        state = estate(argv[0]);\n\n        if (argv[1][0] == 'm')\n            enable_mac_from_arp(state);\n        else\n            pktgen_screen(state);\n        break;\n    case 40:\n    case 41:\n        enable_clock_gettime(estate(argv[0]));\n        break;\n    default:\n        return cli_cmd_error(\"Enable/Disable invalid command\", \"Enable\", argc, argv);\n    }\n    pktgen_update_display();\n    return 0;\n}\n\n// clang-format off\nstatic struct cli_map dbg_map[] = {\n    {20, \"dbg %|tx_dbg|dbg\"},\n    {21, \"dbg tx_rate %P\"},\n    {30, \"dbg %|mempool|dump %P %s\"},\n    {50, \"dbg memzone\"},\n    {51, \"dbg memseg\"},\n    {60, \"dbg hexdump %H %d\"},\n    {61, \"dbg hexdump %H\"},\n    {80, \"dbg break\"},\n    {90, \"dbg memcpy\"},\n    {91, \"dbg memcpy %d %d\"},\n#ifdef TX_DEBUG\n    {100, \"dbg %|enable|disable|on|off tx pcap %P\"},\n#endif\n    {-1, NULL}\n};\n// clang-format on\n\nstatic const char *dbg_help[] = {\n    \"\",\n    \"dbg tx_dbg|dbg                        - Enable tx debug output\",\n    \"dbg tx_rate <portlist>                - Show packet rate for all ports\",\n    \"dbg mempool|dump <portlist> <type>    - Dump out the mempool info for a given type\",\n    \"                                        types - rx|tx\",\n    \"dbg memzone                           - List all of the current memzones\",\n    \"dbg memseg                            - List all of the current memsegs\",\n    \"dbg hexdump <addr> <len>              - hex dump memory at given address\",\n    \"dbg break                             - break into the debugger\",\n    \"dbg memcpy [loop-cnt KBytes]          - run a memcpy test\",\n#ifdef TX_DEBUG\n    \"dbg enable|disable tx pcap <portlist> - Enable/disable writing pcap file for transmitted \"\n    \"packets\",\n#endif\n    CLI_HELP_PAUSE,\n    NULL};\n\nstatic void\nrte_memcpy_perf(unsigned int cnt, unsigned int kb, int flag)\n{\n    char *buf[2], *src, *dst;\n    uint64_t start_time, total_time;\n    uint64_t total_bits, bits_per_tick;\n    unsigned int i;\n\n    kb *= 1024;\n\n    buf[0] = malloc(kb + RTE_CACHE_LINE_SIZE);\n    buf[1] = malloc(kb + RTE_CACHE_LINE_SIZE);\n\n    src = RTE_PTR_ALIGN(buf[0], RTE_CACHE_LINE_SIZE);\n    dst = RTE_PTR_ALIGN(buf[1], RTE_CACHE_LINE_SIZE);\n\n    start_time = rte_get_tsc_cycles();\n    for (i = 0; i < cnt; i++) {\n        if (flag)\n            rte_memcpy(dst, src, kb);\n        else\n            memcpy(dst, src, kb);\n    }\n    total_time = rte_get_tsc_cycles() - start_time;\n\n    total_bits = ((uint64_t)cnt * (uint64_t)kb) * 8L;\n\n    bits_per_tick = total_bits / total_time;\n\n    free(buf[0]);\n    free(buf[1]);\n\n#define MEGA (uint64_t)(1024 * 1024)\n    printf(\"%3d Kbytes for %8d loops, \", (kb / 1024), cnt);\n    printf(\"%3ld bits/tick, \", bits_per_tick);\n    printf(\"%6ld Mbits/sec with %s\\n\", (bits_per_tick * pktgen_get_timer_hz()) / MEGA,\n           (flag) ? \"rte_memcpy\" : \"memcpy\");\n}\n\nstatic int\ndbg_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n    portlist_t portlist;\n    unsigned int len, cnt;\n    const void *addr;\n\n    m = cli_mapping(dbg_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"Debug invalid command\", \"Debug\", argc, argv);\n\n    len = 32;\n    cnt = 100000;\n    switch (m->index) {\n    case 20:\n        if ((pktgen.flags & TX_DEBUG_FLAG) == 0)\n            pktgen.flags |= TX_DEBUG_FLAG;\n        else\n            pktgen.flags &= ~TX_DEBUG_FLAG;\n        pktgen_clear_display();\n        break;\n    case 21:\n        portlist_parse(argv[2], pktgen.nb_ports, &portlist);\n        foreach_port(portlist, debug_tx_rate(pinfo));\n        break;\n    case 30:\n        portlist_parse(argv[2], pktgen.nb_ports, &portlist);\n        foreach_port(portlist, debug_mempool_dump(pinfo, argv[3]));\n        break;\n    case 50:\n        rte_memzone_dump(stdout);\n        break;\n    case 51:\n        rte_dump_physmem_layout(stdout);\n        break;\n    case 60:\n    case 61:\n        addr = (void *)(uintptr_t)strtoull(argv[2], NULL, 0);\n        if (argc == 3)\n            len = 64;\n        else\n            len = strtoul(argv[3], NULL, 0);\n        rte_hexdump(stdout, \"\", addr, len);\n        break;\n    case 80:\n        kill(0, SIGINT);\n        break;\n    case 91:\n        cnt = atoi(argv[2]);\n        len = atoi(argv[3]);\n        /*FALLTHRU*/\n    case 90:\n        rte_memcpy_perf(cnt, len, 0);\n        rte_memcpy_perf(cnt, len, 1);\n        break;\n#ifdef TX_DEBUG\n    case 100: /* enable/disable TX packets written to PCAP file */\n        portlist_parse(argv[4], pktgen.nb_ports, &portlist);\n        foreach_port(portlist, pktgen_pcap_handler(pinfo, estate(argv[1])));\n        break;\n#endif\n    default:\n        return cli_cmd_error(\"Debug invalid command\", \"Debug\", argc, argv);\n    }\n    return 0;\n}\n\n/**\n *\n * Set a sequence config for given port and slot.\n *\n * DESCRIPTION\n * Set up the sequence packets for a given port and slot.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n * \t\"%|seq|sequence %d %P %m %m %4 %4 %d %d %|ipv4|ipv6 %|udp|tcp|icmp %d %d %d\"\n */\n\nstatic int\nseq_1_set_cmd(int argc __rte_unused, char **argv)\n{\n    char *proto = argv[10], *p;\n    char *eth   = argv[9];\n    int seqnum  = atoi(argv[1]);\n    portlist_t portlist;\n    struct pg_ipaddr dst, src;\n    struct rte_ether_addr dmac, smac;\n    uint32_t teid;\n\n    if ((proto[0] == 'i') && (eth[3] == '6')) {\n        cli_printf(\"Must use IPv4 with ICMP type packets\\n\");\n        return -1;\n    }\n\n    if (seqnum >= NUM_SEQ_PKTS) {\n        cli_printf(\"sequence number too large\\n\");\n        return -1;\n    }\n\n    teid = (argc == 14) ? strtoul(argv[13], NULL, 10) : 0;\n    p    = strchr(argv[5], '/'); /* remove subnet if found */\n    if (p)\n        *p = '\\0';\n    _atoip(argv[5], 0, &dst, sizeof(dst));\n    p = strchr(argv[6], '/');\n    if (!p) {\n        cli_printf(\n            \"src IP address should contain subnet value, default /32 for IPv4, /128 for IPv6\\n\");\n    }\n    _atoip(argv[6], PG_IPADDR_NETWORK, &src, sizeof(src));\n    portlist_parse(argv[2], pktgen.nb_ports, &portlist);\n    if (pg_ether_aton(argv[3], &dmac) == NULL) {\n        cli_printf(\"invalid MAC string (%s)\\n\", argv[3]);\n        return -1;\n    }\n    if (pg_ether_aton(argv[4], &smac) == NULL) {\n        cli_printf(\"invalid MAC string (%s)\\n\", argv[4]);\n        return -1;\n    }\n    foreach_port(portlist, pktgen_set_seq(pinfo, seqnum, &dmac, &smac, &dst, &src, atoi(argv[7]),\n                                          atoi(argv[8]), eth[3], proto[0], atoi(argv[11]),\n                                          atoi(argv[12]), teid));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * Set a sequence config for given port and slot.\n *\n * DESCRIPTION\n * Set up the sequence packets for a given port and slot.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n * \t\"%|seq|sequence %d %P dst %m src %m dst %4 src %4 sport %d dport %d %|ipv4|ipv6 %|udp|tcp|icmp\n * vlan %d size %d teid %d\"\n */\n\nstatic int\nseq_2_set_cmd(int argc __rte_unused, char **argv)\n{\n    char *proto = argv[16], *p;\n    char *eth   = argv[15];\n    int seqnum  = atoi(argv[1]);\n    portlist_t portlist;\n    struct pg_ipaddr dst, src;\n    struct rte_ether_addr dmac, smac;\n    uint32_t teid;\n\n    if ((proto[0] == 'i') && (eth[3] == '6')) {\n        cli_printf(\"Must use IPv4 with ICMP type packets\\n\");\n        return -1;\n    }\n\n    if (seqnum >= NUM_SEQ_PKTS) {\n        cli_printf(\"Sequence number too large\\n\");\n        return -1;\n    }\n\n    teid = (argc == 23) ? strtoul(argv[22], NULL, 10) : 0;\n    p    = strchr(argv[8], '/'); /* remove subnet if found */\n    if (p)\n        *p = '\\0';\n    _atoip(argv[8], 0, &dst, sizeof(dst));\n    p = strchr(argv[10], '/');\n    if (p == NULL) {\n        cli_printf(\n            \"src IP address should contain subnet value, default /32 for IPv4, /128 for IPv6\\n\");\n    }\n    _atoip(argv[10], PG_IPADDR_NETWORK, &src, sizeof(src));\n    portlist_parse(argv[2], pktgen.nb_ports, &portlist);\n    if (pg_ether_aton(argv[4], &dmac) == NULL) {\n        cli_printf(\"invalid MAC string (%s)\\n\", argv[4]);\n        return -1;\n    }\n    if (pg_ether_aton(argv[6], &smac) == NULL) {\n        cli_printf(\"invalid MAC string (%s)\\n\", argv[6]);\n        return -1;\n    }\n    foreach_port(portlist, pktgen_set_seq(pinfo, seqnum, &dmac, &smac, &dst, &src, atoi(argv[12]),\n                                          atoi(argv[14]), eth[3], proto[0], atoi(argv[18]),\n                                          atoi(argv[20]), teid));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * Set a sequence config for given port and slot.\n *\n * DESCRIPTION\n * Set up the sequence packets for a given port and slot.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n * \t\"%|seq|sequence %d %P cos %d tos %d\"\n */\n\nstatic int\nseq_3_set_cmd(int argc __rte_unused, char **argv)\n{\n    int seqnum = atoi(argv[1]);\n    portlist_t portlist;\n    uint32_t cos, tos;\n\n    if (seqnum >= NUM_SEQ_PKTS) {\n        cli_printf(\"Sequence number too large\\n\");\n        return -1;\n    }\n\n    cos = strtoul(argv[4], NULL, 10);\n    tos = strtoul(argv[6], NULL, 10);\n\n    portlist_parse(argv[2], pktgen.nb_ports, &portlist);\n\n    foreach_port(portlist, pktgen_set_cos_tos_seq(pinfo, seqnum, cos, tos));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * Set a sequence config for given port and slot.\n *\n * DESCRIPTION\n * Set up the sequence packets for a given port and slot.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n * \t\"%|seq|sequence %d %P vxlan %d gid %d vid %d\"\n */\n\nstatic int\nseq_4_set_cmd(int argc __rte_unused, char **argv)\n{\n    int seqnum = atoi(argv[1]);\n    portlist_t portlist;\n    uint32_t flag, gid, vid;\n\n    if (seqnum >= NUM_SEQ_PKTS) {\n        cli_printf(\"Sequence number too large\\n\");\n        return -1;\n    }\n\n    flag = strtoul(argv[4], NULL, 0);\n    gid  = strtoul(argv[6], NULL, 10);\n    vid  = strtoul(argv[8], NULL, 10);\n\n    portlist_parse(argv[2], pktgen.nb_ports, &portlist);\n\n    foreach_port(portlist, pktgen_set_vxlan_seq(pinfo, seqnum, flag, gid, vid));\n\n    pktgen_update_display();\n    return 0;\n}\n\nstatic int\nseq_5_set_cmd(int argc __rte_unused, char **argv)\n{\n    int seqnum = atoi(argv[1]);\n    portlist_t portlist;\n\n    if (seqnum >= NUM_SEQ_PKTS) {\n        cli_printf(\"Sequence number too large\\n\");\n        return -1;\n    }\n\n    portlist_parse(argv[2], pktgen.nb_ports, &portlist);\n\n    foreach_port(portlist, seq_set_tcp_flags(pinfo, seqnum, argv[5]));\n\n    pktgen_update_display();\n    return 0;\n}\n\n// clang-format off\nstatic struct cli_map seq_map[] = {\n    {10, \"%|seq|sequence %d %P %m %m %4 %4 %d %d %|ipv4|ipv6 %|udp|tcp|icmp %d %d\"},\n    {11, \"%|seq|sequence %d %P %m %m %4 %4 %d %d %|ipv4|ipv6 %|udp|tcp|icmp %d %d %d\"},\n\n    {12, \"%|seq|sequence %d %P dst %m src %m dst %4 src %4 sport %d dport %d %|ipv4|ipv6 \"\n         \"%|udp|tcp|icmp vlan %d size %d\"},\n    {13, \"%|seq|sequence %d %P dst %m src %m dst %4 src %4 sport %d dport %d %|ipv4|ipv6 \"\n         \"%|udp|tcp|icmp vlan %d size %d teid %d\"},\n\n    {15, \"%|seq|sequence %d %P cos %d tos %d\"},\n    {16, \"%|seq|sequence %d %P vxlan %d gid %d vid %d\"},\n    {17, \"%|seq|sequence %d %P vxlan %h gid %d vid %d\"},\n\n    {18, \"%|seq|sequence %d %P tcp %|flag|flags %c\"},\n    {-1, NULL}\n};\n// clang-format on\n\nstatic const char *seq_help[] = {\n    \"\",\n    \"sequence <seq#> <portlist> dst <Mac> src <Mac> dst <IP> src <IP> sport <val> dport <val> \"\n    \"ipv4|ipv6 udp|tcp|icmp vlan <val> size <val> [teid <val>]\",\n    \"sequence <seq#> <portlist> <dst-Mac> <src-Mac> <dst-IP> <src-IP> <sport> <dport> ipv4|ipv6 \"\n    \"udp|tcp|icmp <vlanid> <pktsize> [<teid>]\",\n    \"sequence <seq#> <portlist> cos <cos> tos <tos>\",\n    \"sequence <seq#> <portlist> vxlan <flags> gid <group_id> vid <vxlan_id>\",\n    \"                                   - Set the sequence packet information, make sure the \"\n    \"src-IP\",\n    \"                                     has the netmask value eg 1.2.3.4/24\",\n    \"sequence <seq#> <portlist> tcp %|flag|flags <flags> - Set comma delimited TCP flags: \"\n    \"cwr,ece,urg,ack,psh,rst,syn,fin,clr\",\n    CLI_HELP_PAUSE,\n    NULL};\n\nstatic int\nseq_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n\n    m = cli_mapping(seq_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"Sequence invalid command\", \"Sequence\", argc, argv);\n\n    switch (m->index) {\n    case 10:\n    case 11:\n        seq_1_set_cmd(argc, argv);\n        break;\n    case 12:\n    case 13:\n        seq_2_set_cmd(argc, argv);\n        break;\n    case 15:\n        seq_3_set_cmd(argc, argv);\n        break;\n    case 16:\n    case 17:\n        seq_4_set_cmd(argc, argv);\n        break;\n    case 18:\n        seq_5_set_cmd(argc, argv);\n        break;\n    default:\n        return cli_cmd_error(\"Sequence invalid command\", \"Sequence\", argc, argv);\n    }\n    return 0;\n}\n\n#ifdef LUA_ENABLED\n/**\n *\n * script_cmd - Command to execute a script.\n *\n * DESCRIPTION\n * Load the script file and execute the commands.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nscript_cmd(int argc __rte_unused, char **argv)\n{\n    lua_State *L = pktgen.ld->L;\n\n    if (L == NULL) {\n        pktgen_log_error(\"Lua is not initialized!\");\n        return -1;\n    }\n\n    if (is_help(argc, argv)) {\n        cli_printf(\"\\nUsage: %s <script-string>\\n\", argv[0]);\n        return 0;\n    }\n\n    if (luaL_dofile(L, argv[1]) != 0)\n        pktgen_log_error(\"%s\", lua_tostring(L, -1));\n    return 0;\n}\n\n/**\n *\n * cmd_exec_lua_parsed - Command to execute lua code on command line.\n *\n * DESCRIPTION\n * Execute a string of lua code\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nexec_lua_cmd(int argc __rte_unused, char **argv __rte_unused)\n{\n    lua_State *L = pktgen.ld->L;\n    char buff[1024], *p;\n    int i;\n    size_t n, sz;\n\n    if (L == NULL) {\n        pktgen_log_error(\"Lua is not initialized!\");\n        return -1;\n    }\n\n    if (is_help(argc, argv)) {\n        cli_printf(\"\\nUsage: %s <script-string>\\n\", argv[0]);\n        return 0;\n    }\n\n    sz = sizeof(buff);\n    memset(buff, 0, sz);\n    sz--; /* Make sure a NULL is at the end of the string */\n    n = 0;\n    for (i = 1, p = buff; i < argc; i++) {\n        if ((strlen(argv[i]) + 1) > (sz - n)) {\n            cli_printf(\"Input line too long > %ld bytes\\n\", sizeof(buff));\n            return -1;\n        }\n        n += snprintf(&p[n], sz - n, \"%s \", argv[i]);\n    }\n\n    if (luaL_dostring(L, buff) != 0)\n        pktgen_log_error(\"%s\", lua_tostring(L, -1));\n    return 0;\n}\n#endif\n\n// clang-format off\nstatic struct cli_map misc_map[] = {\n    {10, \"clear %P stats\"},\n    {20, \"geometry\"},\n    {30, \"load %s\"},\n\n#ifdef LUA_ENABLED\n    {40, \"script %l\"},\n    {50, \"lua %l\"},\n#endif\n    {60, \"save %s\"},\n    {70, \"redisplay\"},\n    {100, \"reset %P\"},\n    {110, \"restart %P\"},\n    {130, \"port %d\"},\n    {140, \"ping4 %P\"},\n#ifdef INCLUDE_PING6\n    {141, \"ping6 %P\"},\n#endif\n    {-1, NULL}\n};\n// clang-format on\nstatic const char *misc_help[] = {\n    \"\",\n    \"save <path-to-file>                - Save a configuration file using the filename\",\n    \"load <path-to-file>                - Load a command/script file from the given path\",\n\n#ifdef LUA_ENABLED\n    \"script <filename>                  - Execute the Lua script code in file (www.lua.org).\",\n    \"lua 'lua string'                   - Execute the Lua code in the string needs quotes\",\n#endif\n    \"geometry                           - Show the display geometry Columns by Rows (ColxRow)\",\n    \"clear <portlist> stats             - Clear the statistics\",\n    \"clr                                - Clear all Statistics\",\n    \"reset <portlist>                   - Reset the configuration the ports to the default\",\n    \"rst                                - Reset the configuration for all ports\",\n    \"ports per page [1-6]               - Set the number of ports displayed per page\",\n    \"port <number>                      - Sets the sequence packets to display for a given port\",\n    \"restart <portlist>                 - Restart or stop a ethernet port and restart\",\n    \"ping4 <portlist>                   - Send a IPv4 ICMP echo request on the given portlist\",\n#ifdef INCLUDE_PING6\n    \"ping6 <portlist>                   - Send a IPv6 ICMP echo request on the given portlist\",\n#endif\n    CLI_HELP_PAUSE,\n    NULL};\n\nstatic int\nmisc_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n    portlist_t portlist;\n    uint16_t rows, cols;\n\n    m = cli_mapping(misc_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"Misc invalid command\", \"Misc\", argc, argv);\n\n    switch (m->index) {\n    case 10:\n        portlist_parse(argv[1], pktgen.nb_ports, &portlist);\n        foreach_port(portlist, pktgen_clear_stats(pinfo));\n        pktgen_clear_display();\n        break;\n    case 20:\n        pktgen_display_get_geometry(&rows, &cols);\n        break;\n    case 30:\n        if (cli_execute_cmdfile(argv[1]))\n            cli_printf(\"load command failed for %s\\n\", argv[1]);\n        break;\n#ifdef LUA_ENABLED\n    case 40:\n        script_cmd(argc, argv);\n        break;\n    case 50:\n        exec_lua_cmd(argc, argv);\n        break;\n#endif\n    case 60:\n        pktgen_save(argv[1]);\n        break;\n    case 70:\n        pktgen_clear_display();\n        break;\n    case 100:\n        portlist_parse(argv[1], pktgen.nb_ports, &portlist);\n        foreach_port(portlist, pktgen_reset(pinfo));\n        break;\n    case 110:\n        portlist_parse(argv[1], pktgen.nb_ports, &portlist);\n        foreach_port(portlist, pktgen_port_restart(pinfo));\n        break;\n    case 130:\n        pktgen_set_port_number((uint16_t)atoi(argv[1]));\n        break;\n    case 140:\n        portlist_parse(argv[1], pktgen.nb_ports, &portlist);\n        foreach_port(portlist, pktgen_ping4(pinfo));\n        pktgen_force_update();\n        break;\n#ifdef INCLUDE_PING6\n    case 141:\n        portlist_parse(argv[1], pktgen.nb_ports, &portlist);\n        foreach_port(portlist, pktgen_ping6(info));\n        pktgen_update_display();\n        break;\n#endif\n    default:\n        return cli_cmd_error(\"Misc invalid command\", \"Misc\", argc, argv);\n    }\n    return 0;\n}\n\n// clang-format off\nstatic struct cli_map page_map[] = {\n    {10, \"page %d\"},\n    {11, \"page %|main|range|cpu|system|sys|next|sequence|seq|rnd|\"\n         \"log|latency|lat|stats|qstats|xstats\"},\n    {-1, NULL}\n};\n\nstatic const char *page_help[] = {\n    \"\",\n    \"page [0-7]                         - Show the port pages or configuration or sequence page\",\n    \"page main                          - Display page zero\",\n    \"page range                         - Display the range packet page\",\n    \"page cpu                           - Display the CPU page\",\n    \"page system | sys                  - Display some information about the CPU system\",\n    \"page sequence | seq                - sequence will display a set of packets for a given port\",\n    \"                                     Note: use the 'port <number>' to display a new port \"\n    \"sequence\",\n    \"page rnd                           - Display the random bitfields to packets for a given port\",\n    \"                                     Note: use the 'port <number>' to display a new port \"\n    \"sequence\",\n    \"page log                           - Display the log messages page\",\n    \"page latency | lat                 - Display the latency page\",\n    \"page qstats | stats                - Display per port queue stats\",\n    \"page xstats                        - Display port XSTATS values\",\n    CLI_HELP_PAUSE,\n    NULL\n};\n// clang-format on\n\nstatic int\npage_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n\n    m = cli_mapping(page_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"Page invalid command\", \"Page\", argc, argv);\n\n    switch (m->index) {\n    case 10:\n    case 11:\n        pktgen_set_page(argv[1]);\n        break;\n    default:\n        return cli_cmd_error(\"Page invalid command\", \"Page\", argc, argv);\n    }\n    return 0;\n}\n\n// clang-format off\nstatic struct cli_map plugin_map[] = {\n    {10, \"plugin\"},\n    {20, \"plugin load %s\"},\n    {21, \"plugin load %s %s\"},\n    {30, \"plugin %|rm|del|delete %s\"},\n    {-1, NULL}\n};\n\nstatic const char *plugin_help[] = {\n    \"\",\n    \"plugin                             - Show the plugins currently installed\",\n    \"plugin load <filename>             - Load a plugin file\",\n    \"plugin load <filename> <path>      - Load a plugin file at path\",\n    \"plugin rm|delete <plugin>          - Remove or delete a plugin\",\n    CLI_HELP_PAUSE,\n    NULL\n};\n// clang-format on\n\nstatic int\nplugin_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n    int inst;\n\n    m = cli_mapping(plugin_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"Plugin invalid command\", \"Plugin\", argc, argv);\n\n    switch (m->index) {\n    case 10:\n        plugin_dump(stdout);\n        break;\n    case 20:\n    case 21:\n        if ((inst = plugin_create(argv[2], argv[3])) < 0) {\n            printf(\"Plugin not loaded %s, %s\\n\", argv[2], argv[3]);\n            return -1;\n        }\n\n        if (plugin_start(inst, NULL), 0) {\n            plugin_destroy(inst);\n            return -1;\n        }\n        break;\n    case 30:\n        inst = plugin_find_by_name(argv[2]);\n        if (inst < 0)\n            return -1;\n        plugin_stop(inst);\n        plugin_destroy(inst);\n        break;\n    default:\n        return cli_cmd_error(\"Plugin invalid command\", \"Plugin\", argc, argv);\n    }\n    return 0;\n}\n\n#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_NET_BOND)\n// clang-format off\nstatic struct cli_map bonding_map[] = {\n    {10, \"bonding %P show\"},\n    {20, \"bonding show\"},\n    {-1, NULL}\n};\n// clang-format on\n\nstatic const char *bonding_help[] = {\n    \"\", \"bonding <portlist> show          - Show the bonding configuration for <portlist>\",\n    \"bonding show                     - Show all bonding configurations\", CLI_HELP_PAUSE, NULL};\n\nstatic void\nbonding_dump(port_info_t *pinfo)\n{\n    uint16_t pid;\n\n    RTE_ETH_FOREACH_DEV(pid)\n    {\n        struct rte_eth_bond_8023ad_conf conf;\n\n        if (pinfo->pid != pid)\n            continue;\n\n        if (rte_eth_bond_8023ad_conf_get(pid, &conf) < 0)\n            continue;\n\n        printf(\"Port %d information:\\n\", pid);\n        show_bonding_mode(pinfo);\n        printf(\"\\n\");\n    }\n}\n\nstatic int\nbonding_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n    portlist_t portlist;\n\n    m = cli_mapping(bonding_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"bonding invalid command\", \"Bonding\", argc, argv);\n\n    switch (m->index) {\n    case 10:\n        portlist_parse(argv[1], pktgen.nb_ports, &portlist);\n        foreach_port(portlist, bonding_dump(pinfo));\n        break;\n    case 20:\n        portlist = 0xFFFFFFFF;\n        foreach_port(portlist, bonding_dump(pinfo));\n        break;\n    default:\n        return cli_cmd_error(\"Bonding invalid command\", \"Bonding\", argc, argv);\n    }\n    return 0;\n}\n#endif\n\n// clang-format off\nstatic struct cli_map latency_map[] = {\n    {10, \"latency %P rate %u\"},\n    {20, \"latency %P entropy %u\"},\n    {-1, NULL}\n};\n\nstatic const char *latency_help[] = {\n    \"\",\n    \"latency <portlist> rate <value>      - Rate in milli-seconds to send a latency packet\",\n    \"latency <portlist> entropy <value>   - Entropy value to adjust the src-port by (SPORT + (i % N)) (default: 1)\",\n    \"                                       e.eg. latency 0 entropy 16\",\n    CLI_HELP_PAUSE,\n    NULL\n};\n// clang-format on\n\nstatic int\nlatency_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n    portlist_t portlist;\n    int value;\n\n    m = cli_mapping(latency_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"Latency invalid command\", \"Latency\", argc, argv);\n\n    portlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\n    value = atoi(argv[3]);\n\n    switch (m->index) {\n    case 10:\n        foreach_port(portlist, latency_set_rate(pinfo, value));\n        break;\n    case 20:\n        foreach_port(portlist, latency_set_entropy(pinfo, (uint16_t)value));\n        break;\n    default:\n        return cli_cmd_error(\"Latency invalid command\", \"Latency\", argc, argv);\n    }\n\n    pktgen_update_display();\n    return 0;\n}\n\n// clang-format off\nstatic struct cli_map hmap_map[] = {\n    {10, \"hmap list\"},\n    {20, \"hmap dump all\"},\n    {30, \"hmap dump %s\"},\n    {-1, NULL}\n};\n\nstatic const char *hmap_help[] = {\n    \"\",\n    \"hmap list               - Show the hmap names currently active\",\n    \"hmap dump all           - Dump all hmap information\",\n    \"hmap dump <hmap-name>   - Dump the hmap information for <hmap-name>\",\n    CLI_HELP_PAUSE,\n    NULL\n};\n// clang-format on\nstatic int\nhmap_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n\n    m = cli_mapping(hmap_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"hmap invalid command\", \"Hashmap\", argc, argv);\n\n    switch (m->index) {\n    case 10:\n        hmap_list_names(NULL);\n        break;\n    case 20:\n        hmap_list_all(NULL, true);\n        break;\n    case 30:\n        hmap_list_by_name(NULL, argv[2], true);\n        break;\n    default:\n        return cli_cmd_error(\"Hashmap invalid command\", \"Hashmap\", argc, argv);\n    }\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**********************************************************/\n/**********************************************************/\n/****** CONTEXT (list of instruction) */\n\nstatic int help_cmd(int argc, char **argv);\n\n// clang-format off\nstatic struct cli_tree default_tree[] = {\n    c_dir(\"/pktgen/bin\"),\n    c_cmd(\"help\", help_cmd, \"help command\"),\n\n    c_cmd(\"clear\", misc_cmd, \"clear stats, ...\"),\n    c_alias(\"clr\", \"clear all stats\", \"clear all port stats\"),\n    c_cmd(\"geometry\", misc_cmd, \"show the screen geometry\"),\n    c_alias(\"geom\", \"geometry\", \"show the screen geometry\"),\n    c_cmd(\"load\", misc_cmd, \"load command file\"),\n#ifdef LUA_ENABLED\n    c_cmd(\"script\", misc_cmd, \"run a Lua script\"),\n    c_cmd(\"lua\", misc_cmd, \"execute a Lua string\"),\n#endif\n    c_cmd(\"save\", misc_cmd, \"save the current state\"),\n    c_cmd(\"redisplay\", misc_cmd, \"redisplay the screen\"),\n    c_alias(\"cls\", \"redisplay\", \"redraw screen\"),\n    c_cmd(\"reset\", misc_cmd, \"reset pktgen configuration\"),\n    c_alias(\"rst\", \"reset all\", \"reset all ports\"),\n    c_cmd(\"restart\", misc_cmd, \"restart port\"),\n    c_cmd(\"port\", misc_cmd, \"Switch between ports\"),\n    c_cmd(\"ping4\", misc_cmd, \"Send a ping packet for IPv4\"),\n#ifdef INCLUDE_PING6\n    c_cmd(\"ping6\", misc_cmd, \"Send a ping packet for IPv6\"),\n#endif\n\n    c_cmd(\"sequence\", seq_cmd, \"sequence command\"),\n    c_alias(\"seq\", \"sequence\", \"sequence command\"),\n\n    c_cmd(\"page\", page_cmd, \"change page displays\"),\n    c_cmd(\"theme\", theme_cmd, \"Set, save, show the theme\"),\n    c_cmd(\"range\", range_cmd, \"Range commands\"),\n    c_cmd(\"enable\", en_dis_cmd, \"enable features\"),\n    c_cmd(\"disable\", en_dis_cmd, \"disable features\"),\n    c_cmd(\"start\", start_stop_cmd, \"start features\"),\n    c_cmd(\"stop\", start_stop_cmd, \"stop features\"),\n    c_alias(\"str\", \"start all\", \"start all ports sending packets\"),\n    c_alias(\"stp\", \"stop all\", \"stop all ports sending packets\"),\n    c_cmd(\"pcap\", pcap_cmd, \"pcap commands\"),\n    c_cmd(\"set\", set_cmd, \"set a number of options\"),\n    c_cmd(\"dbg\", dbg_cmd, \"debug commands\"),\n    c_cmd(\"plugin\", plugin_cmd, \"Plugin a shared object file\"),\n#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_NET_BOND)\n    c_cmd(\"bonding\", bonding_cmd, \"Bonding commands\"),\n#endif\n    c_cmd(\"latency\", latency_cmd, \"Latency setup commands\"),\n    c_cmd(\"hmap\", hmap_cmd, \"hashmap commands\"),\n\n    c_alias(\"on\", \"enable screen\", \"Enable screen updates\"),\n    c_alias(\"off\", \"disable screen\", \"Disable screen updates\"),\n\n    c_end()\n};\n// clang-format on\nstatic int\ninit_tree(void)\n{\n    /* Add the system default commands in /sbin directory */\n    if (cli_default_tree_init())\n        return -1;\n\n    /* Add the Pktgen directory tree */\n    if (cli_add_tree(cli_root_node(), default_tree))\n        return -1;\n\n    cli_help_add(\"Title\", NULL, title_help);\n    cli_help_add(\"Page\", page_map, page_help);\n    cli_help_add(\"Enable\", enable_map, enable_help);\n    cli_help_add(\"Set\", set_map, set_help);\n    cli_help_add(\"Range\", range_map, range_help);\n    cli_help_add(\"Sequence\", seq_map, seq_help);\n    cli_help_add(\"PCAP\", pcap_map, pcap_help);\n    cli_help_add(\"Start\", start_map, start_help);\n    cli_help_add(\"Debug\", dbg_map, dbg_help);\n    cli_help_add(\"Misc\", misc_map, misc_help);\n    cli_help_add(\"Theme\", theme_map, theme_help);\n    cli_help_add(\"Plugin\", plugin_map, plugin_help);\n    cli_help_add(\"Latency\", latency_map, latency_help);\n    cli_help_add(\"Hashmap\", hmap_map, hmap_help);\n#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_NET_BOND)\n    cli_help_add(\"Bonding\", bonding_map, bonding_help);\n#endif\n    cli_help_add(\"Status\", NULL, status_help);\n\n    /* Make sure the pktgen commands are executable in search path */\n    if (cli_add_bin_path(\"/pktgen/bin\"))\n        return -1;\n\n    return 0;\n}\n\nstatic int\nmy_prompt(int cont __rte_unused)\n{\n    int nb;\n\n    pktgen_display_set_color(\"pktgen.prompt\");\n    nb = cli_printf(\"Pktgen:%s> \", cli_path_string(NULL, NULL));\n    pktgen_display_set_color(\"stats.stat.values\");\n\n    return nb;\n}\n\nint\npktgen_cli_create(void)\n{\n    int ret = -1;\n\n    if (!cli_create()) {\n        if (!cli_setup_with_tree(init_tree)) {\n            cli_set_prompt(my_prompt);\n            ret = 0;\n        }\n    }\n    return ret;\n}\n\n/**\n *\n * Display the help screen and pause if needed.\n *\n * DESCRIPTION\n * Display the help and use pause to show screen full of messages.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nhelp_cmd(int argc __rte_unused, char **argv __rte_unused)\n{\n    int paused;\n\n    paused = scrn_is_paused();\n\n    if (!paused)\n        scrn_pause();\n\n    scrn_setw(1);\n    scrn_cls();\n    scrn_pos(1, 1);\n\n    cli_help_show_all(\"** Pktgen Help Information **\");\n\n    if (!paused) {\n        scrn_setw(pktgen.last_row + 1);\n        scrn_resume();\n        pktgen_clear_display();\n    }\n    return 0;\n}\n"
  },
  {
    "path": "app/cli-functions.h",
    "content": "/*-\n * Copyright(c) <2020-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#ifndef _CLI_COMMANDS_H_\n#define _CLI_COMMANDS_H_\n\n/**\n * @file\n *\n * Pktgen interactive CLI command tree creation.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Build and register the full Pktgen CLI command tree.\n *\n * Registers all command maps, directory nodes, and help strings with the\n * CLI engine.  Must be called once during initialisation before entering\n * the interactive shell loop.\n *\n * @return\n *   0 on success, -1 on failure.\n */\nint pktgen_cli_create(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CLI_COMMANDS_H_ */\n"
  },
  {
    "path": "app/l2p.c",
    "content": "/*-\n * Copyright(c) <2012-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2012 by Keith Wiles @ intel.com */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <string.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <alloca.h>\n#include <pthread.h>\n\n#include <rte_version.h>\n#include <rte_config.h>\n#include <rte_lcore.h>\n#include <rte_atomic.h>\n#include <rte_cycles.h>\n#include <rte_pci.h>\n#include <rte_debug.h>\n#include <rte_memory.h>\n\n#include <pktgen.h>\n#include <pg_strings.h>\n#include \"l2p.h\"\n#include \"utils.h\"\n\nstatic l2p_t l2p_info, *l2p = &l2p_info;\n\nl2p_t *\nl2p_get(void)\n{\n    return l2p;\n}\n\n/**\n * Create and initialize the lcore to port object\n *\n * RETURNS: Pointer to l2p_t or NULL on error.\n */\nvoid\nl2p_create(void)\n{\n    for (int i = 0; i < RTE_MAX_ETHPORTS; i++)\n        l2p->ports[i].pid = RTE_MAX_ETHPORTS + 1;\n}\n\nstatic struct rte_mempool *\nl2p_pktmbuf_create(const char *type, l2p_port_t *port, uint16_t qid, int nb_mbufs, int cache_size)\n{\n    struct rte_mempool *mp;\n    char name[RTE_MEMZONE_NAMESIZE];\n    uint64_t sz;\n    int sid;\n\n    sid = pg_eth_dev_socket_id(port->pid);\n\n    snprintf(name, sizeof(name) - 1, \"%s-P%u/Q%u/S%u\", type, port->pid, qid, sid);\n\n    const int bufSize = pktgen.mbuf_buf_size;\n\n    sz = nb_mbufs * bufSize;\n    sz = RTE_ALIGN_CEIL(sz + sizeof(struct rte_mempool), 1024);\n\n    pktgen.mem_used += sz;\n    pktgen.total_mem_used += sz;\n\n    /* create the mbuf pool */\n    mp = rte_pktmbuf_pool_create(name, nb_mbufs, cache_size, DEFAULT_PRIV_SIZE, bufSize, sid);\n    if (mp == NULL)\n        rte_exit(EXIT_FAILURE,\n                 \"Cannot create mbuf pool (%s) port %d, queue %u, nb_mbufs %d, NUMA %d: %s\\n\", name,\n                 port->pid, qid, nb_mbufs, sid, rte_strerror(rte_errno));\n\n    pktgen_log_info(\"  Create: '%-*s' - Memory used (MBUFs %'6u x size %'6u) = %'8lu KB @ %p\\n\", 24,\n                    name, nb_mbufs, bufSize, sz / 1024, mp);\n\n    return mp;\n}\n\nstatic int\nparse_cores(uint16_t pid, const char *cores, int mode)\n{\n    l2p_t *l2p       = l2p_get();\n    l2p_port_t *port = &l2p->ports[pid];\n    char *core_map   = NULL;\n    int num_cores    = 0, l, h, num_fields;\n    char *fields[3]  = {0}, *f0, *f1;\n    int mbuf_count   = MAX_MBUFS_PER_PORT(DEFAULT_RX_DESC, DEFAULT_TX_DESC);\n    char name[64];\n\n    core_map = alloca(MAX_ALLOCA_SIZE);\n    if (!core_map)\n        rte_exit(EXIT_FAILURE, \"out of memory for core string\\n\");\n\n    snprintf(core_map, MAX_ALLOCA_SIZE - 1, \"%s\", cores);\n\n    num_fields = rte_strsplit(core_map, strlen(core_map), fields, RTE_DIM(fields), '-');\n    if (num_fields <= 0 || num_fields > 2)\n        rte_exit(EXIT_FAILURE, \"invalid core mapping '%s'\\n\", cores);\n    f0 = fields[0];\n    f1 = (fields[1] == NULL) ? f0 : fields[1];\n    f0 = pg_strtrimset(f0, \"[]\");\n    f0 = pg_strtrimset(f0, \"{}\");\n    f1 = pg_strtrimset(f1, \"[]\");\n    f1 = pg_strtrimset(f1, \"{}\");\n\n    l = strtol(f0, NULL, 10);\n    h = strtol(f1, NULL, 10);\n\n    do {\n        l2p_lport_t *lport;\n        int32_t sid = pg_eth_dev_socket_id(port->pid);\n\n        lport = l2p->lports[l];\n        if (lport == NULL) {\n            snprintf(name, sizeof(name) - 1, \"lport-%u:%u\", l, port->pid);\n            lport = rte_zmalloc_socket(name, sizeof(l2p_lport_t), RTE_CACHE_LINE_SIZE, sid);\n            if (!lport)\n                rte_exit(EXIT_FAILURE, \"Failed to allocate memory for lport info\\n\");\n            lport->lid = l;\n\n            l2p->lports[l] = lport;\n        } else\n            printf(\"Err: lcore %u already in use\\n\", l);\n\n        num_cores++;\n        lport->port = port;\n        lport->mode = mode;\n        switch (mode) {\n        case LCORE_MODE_RX:\n            if (port->num_rx_qids >= MAX_QUEUES_PER_PORT)\n                rte_exit(EXIT_FAILURE, \"Too many RX queues for port %u (max %u)\\n\", port->pid,\n                         (unsigned)MAX_QUEUES_PER_PORT);\n            lport->rx_qid = port->num_rx_qids++;\n            break;\n        case LCORE_MODE_TX:\n            if (port->num_tx_qids >= MAX_QUEUES_PER_PORT)\n                rte_exit(EXIT_FAILURE, \"Too many TX queues for port %u (max %u)\\n\", port->pid,\n                         (unsigned)MAX_QUEUES_PER_PORT);\n            lport->tx_qid = port->num_tx_qids++;\n            break;\n        case LCORE_MODE_BOTH:\n            if (port->num_rx_qids >= MAX_QUEUES_PER_PORT)\n                rte_exit(EXIT_FAILURE, \"Too many RX queues for port %u (max %u)\\n\", port->pid,\n                         (unsigned)MAX_QUEUES_PER_PORT);\n            if (port->num_tx_qids >= MAX_QUEUES_PER_PORT)\n                rte_exit(EXIT_FAILURE, \"Too many TX queues for port %u (max %u)\\n\", port->pid,\n                         (unsigned)MAX_QUEUES_PER_PORT);\n            lport->rx_qid = port->num_rx_qids++;\n            lport->tx_qid = port->num_tx_qids++;\n            break;\n        default:\n            rte_exit(EXIT_FAILURE, \"invalid port mode\\n\");\n            break;\n        }\n\n        if (mode == LCORE_MODE_RX || mode == LCORE_MODE_BOTH) {\n            const uint16_t qid = lport->rx_qid;\n            if (port->rx_mp[qid] == NULL) {\n                /* Create the Rx mbuf pool one per port/queue */\n                port->rx_mp[qid] =\n                    l2p_pktmbuf_create(\"RX\", port, qid, mbuf_count, MEMPOOL_CACHE_SIZE);\n                if (port->rx_mp[qid] == NULL)\n                    rte_exit(EXIT_FAILURE, \"Cannot init port %d qid %u for Default RX mbufs\",\n                             port->pid, qid);\n            }\n        }\n\n        if (mode == LCORE_MODE_TX || mode == LCORE_MODE_BOTH) {\n            const uint16_t qid = lport->tx_qid;\n            if (port->tx_mp[qid] == NULL) {\n                port->tx_mp[qid] =\n                    l2p_pktmbuf_create(\"TX\", port, qid, mbuf_count, MEMPOOL_CACHE_SIZE);\n                if (port->tx_mp[qid] == NULL)\n                    rte_exit(EXIT_FAILURE, \"Cannot init port %d qid %u for Default TX mbufs\",\n                             port->pid, qid);\n            }\n            if (port->sp_mp[qid] == NULL) {\n                /* Used for sending special packets like ARP requests */\n                port->sp_mp[qid] = l2p_pktmbuf_create(\"SP\", port, qid, MAX_SPECIAL_MBUFS, 0);\n                if (port->sp_mp[qid] == NULL)\n                    rte_exit(EXIT_FAILURE, \"Cannot init port %d qid %u for Special TX mbufs\", pid,\n                             qid);\n            }\n        }\n    } while (l++ < h);\n\n    return num_cores;\n}\n\nstatic int\nparse_mapping(const char *map)\n{\n    l2p_t *l2p      = l2p_get();\n    char *fields[3] = {0}, *f0, *f1, *lcores[3] = {0}, *c0, *c1;\n    char *mapping = NULL;\n    int num_fields, num_cores, num_lcores;\n    uint16_t pid;\n\n    if (!map || strlen(map) == 0) {\n        printf(\"no mapping specified or string empty\\n\");\n        goto leave;\n    }\n\n    mapping = alloca(MAX_ALLOCA_SIZE);\n    if (!mapping) {\n        printf(\"unable to allocate map string\\n\");\n        goto leave;\n    }\n    snprintf(mapping, MAX_ALLOCA_SIZE - 1, \"%s\", map);\n\n    /* parse map into a lcore list and port number */\n    num_fields = rte_strsplit(mapping, strlen(mapping), fields, RTE_DIM(fields), '.');\n    if (num_fields != 2) {\n        printf(\"Invalid mapping format '%s'\\n\", map);\n        goto leave;\n    }\n    f0 = fields[0];\n    f1 = (fields[1] == NULL) ? f0 : fields[1];\n    f0 = pg_strtrimset(f0, \"[]\");\n    f0 = pg_strtrimset(f0, \"{}\");\n    f1 = pg_strtrimset(f1, \"[]\");\n    f1 = pg_strtrimset(f1, \"{}\");\n\n    pid = strtol(f1, NULL, 10);\n    if (pid >= RTE_MAX_ETHPORTS) {\n        printf(\"Invalid port number '%s'\\n\", f1);\n        goto leave;\n    }\n\n    l2p->ports[pid].pid = pid;\n    l2p->num_ports++;\n\n    num_lcores = rte_strsplit(f0, strlen(f0), lcores, RTE_DIM(lcores), ':');\n    if (num_lcores <= 0 || num_lcores > 2) {\n        printf(\"Invalid mapping format '%s'\\n\", fields[0]);\n        goto leave;\n    }\n    c0 = lcores[0];\n    c1 = (lcores[1] == NULL) ? c0 : lcores[1];\n    c0 = pg_strtrimset(c0, \"[]\");\n    c0 = pg_strtrimset(c0, \"{}\");\n    c1 = pg_strtrimset(c1, \"[]\");\n    c1 = pg_strtrimset(c1, \"{}\");\n\n    if (num_lcores == 1) {\n        num_cores = parse_cores(pid, c0, LCORE_MODE_BOTH);\n        if (num_cores <= 0) {\n            printf(\"Invalid mapping format '%s'\\n\", c0);\n            goto leave;\n        }\n    } else {\n        num_cores = parse_cores(pid, c0, LCORE_MODE_RX);\n        if (num_cores <= 0) {\n            printf(\"Invalid mapping format '%s'\\n\", c0);\n            goto leave;\n        }\n\n        num_cores = parse_cores(pid, c1, LCORE_MODE_TX);\n        if (num_cores <= 0) {\n            printf(\"Invalid mapping format '%s'\\n\", c1);\n            goto leave;\n        }\n    }\n    return 0;\nleave:\n    return -1;\n}\n\n/**\n *\n * pg_parse_matrix - Parse the command line argument for port configuration\n *\n * DESCRIPTION\n * Parse the command line argument for port configuration.\n *\n * BNF: (or kind of BNF)\n *      <matrix-string> := \"\"\" <lcore-port> { \",\" <lcore-port>} \"\"\"\n *\t\t<lcore-port>\t:= <lcore-list> \".\" <port>\n *\t\t<lcore-list>\t:= \"[\" <rx-list> \":\" <tx-list> \"]\"\n *\t\t<port>      \t:= <num>\n *\t\t<rx-list>\t\t:= <num> { \"/\" (<num> | <list>) }\n *\t\t<tx-list>\t\t:= <num> { \"/\" (<num> | <list>) }\n *\t\t<list>\t\t\t:= <num>           { \"/\" (<range> | <list>) }\n *\t\t<range>\t\t\t:= <num> \"-\" <num> { \"/\" <range> }\n *\t\t<num>\t\t\t:= <digit>+\n *\t\t<digit>\t\t\t:= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9\n *\n * BTW: A single lcore can only handle a single port/queue, which means\n *      you can not have a single core processing more then one network device or port.\n *\n *\t1.0, 2.1, 3.2                   - core 1 handles port 0 rx/tx,\n *\t\t\t\t\t  core 2 handles port 1 rx/tx\n *\t[0-1].0, [2/4-5].1, ...\t\t- cores 0-1 handle port 0 rx/tx,\n *\t\t\t\t\t  cores 2,4,5 handle port 1 rx/tx\n *\t[1:2].0, [4:6].1, ...\t\t- core 1 handles port 0 rx,\n *\t\t\t\t\t  core 2 handles port 0 tx,\n *\t[1:2-3].0, [4:5-6].1, ...\t- core 1 handles port 1 rx, cores 2,3 handle port 0 tx\n *\t\t\t\t\t  core 4 handles port 1 rx & core 5,6 handles port 1 tx\n *\t[1-2:3].0, [4-5:6].1, ...\t- core 1,2 handles port 0 rx, core 3 handles port 0 tx\n *\t\t\t\t\t  core 4,5 handles port 1 rx & core 6 handles port 1 tx\n *\t[1-2:3-5].0, [4-5:6/8].1, ...\t- core 1,2 handles port 0 rx, core 3,4,5 handles port 0 tx\n *\t\t\t\t\t  core 4,5 handles port 1 rx & core 6,8 handles port 1 tx\n *\tBTW: you can use \"{}\" instead of \"[]\" as it does not matter to the syntax.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nint\nl2p_parse_mappings(void)\n{\n    l2p_t *l2p = l2p_get();\n\n    for (int i = 0; i < l2p->num_mappings; i++)\n        if (parse_mapping(l2p->mappings[i]))\n            return -1;\n    pktgen_log_info(\"%*sTotal memory used = %'8\" PRIu64 \" KB\", 54, \" \",\n                    (pktgen.total_mem_used + 1023) / 1024);\n    return 0;\n}\n\nint\nl2p_parse_mapping_add(const char *str)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (!str || l2p->num_mappings > RTE_MAX_ETHPORTS)\n        return -1;\n    l2p->mappings[l2p->num_mappings++] = strdup(str);\n    return 0;\n}\n"
  },
  {
    "path": "app/l2p.h",
    "content": "/*-\n * Copyright(c) <2012-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2014 by Keith Wiles @ intel.com */\n\n#ifndef __L2P_H\n#define __L2P_H\n\n/**\n * @file\n *\n * Lcore-to-Port (L2P) mapping for Pktgen.\n *\n * Manages the assignment of logical cores to Ethernet port/queue pairs,\n * the per-port mempool ownership, and provides accessors for obtaining\n * port_info_t pointers, queue counts, and PCAP state from any lcore or\n * port index.\n */\n\n#include <string.h>\n#include <pthread.h>\n\n#include <rte_memory.h>\n#include <rte_atomic.h>\n\n#include <pktgen-pcap.h>\n#include <pktgen-log.h>\n#include <pktgen-stats.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MAX_MATRIX_ENTRIES 128 /**< Maximum number of lcore/port mapping entries */\n#define MAX_STRING         256 /**< Maximum length of a mapping string */\n\n#define MAX_MAP_PORTS  (RTE_MAX_ETHPORTS + 1) /**< Sentinel-inclusive port array size */\n#define MAX_MAP_LCORES (RTE_MAX_LCORE + 1)    /**< Sentinel-inclusive lcore array size */\n\n/** Lcore operating mode: unknown, RX-only, TX-only, or both. */\nenum { LCORE_MODE_UNKNOWN = 0, LCORE_MODE_RX = 1, LCORE_MODE_TX = 2, LCORE_MODE_BOTH = 3 };\n\n#define MAX_MAPPINGS       32   /**< Maximum number of port/queue mappings */\n#define MAX_ALLOCA_SIZE    1024 /**< Maximum size of a stack allocation */\n#define MEMPOOL_CACHE_SIZE (RTE_MEMPOOL_CACHE_MAX_SIZE / 2) /**< Per-lcore mempool cache size */\n\nstruct port_info_s;\n\n/**\n * Per-port L2P state: mempools, queue counts, and PCAP context.\n */\ntypedef struct l2p_port_s {\n    struct port_info_s *pinfo;                      /**< Port information pointer */\n    uint16_t pid;                                   /**< Port ID attached to lcore */\n    uint16_t num_rx_qids;                           /**< Number of Rx queues */\n    uint16_t num_tx_qids;                           /**< Number of Tx queues */\n    struct rte_mempool *rx_mp[MAX_QUEUES_PER_PORT]; /**< Rx pktmbuf pool per queue */\n    struct rte_mempool *tx_mp[MAX_QUEUES_PER_PORT]; /**< Tx pktmbuf pool per queue */\n    struct rte_mempool *sp_mp[MAX_QUEUES_PER_PORT]; /**< Special TX pktmbuf pool per queue */\n    pcap_info_t *pcap_info;                         /**< PCAP packet structure */\n} l2p_port_t;\n\n/**\n * Per-lcore L2P state: mode, queue IDs, and a pointer to the owning port.\n */\ntypedef struct l2p_lport_s {\n    uint16_t mode;    /**< LCORE_MODE_RX, LCORE_MODE_TX, or LCORE_MODE_BOTH */\n    uint16_t lid;     /**< Lcore ID */\n    uint16_t rx_qid;  /**< RX queue ID assigned to this lcore */\n    uint16_t tx_qid;  /**< TX queue ID assigned to this lcore */\n    l2p_port_t *port; /**< Pointer to the owning port structure */\n} l2p_lport_t;\n\n/**\n * Top-level L2P mapping table.\n */\ntypedef struct {\n    l2p_lport_t *lports[RTE_MAX_LCORE]; /**< Per-lcore port/queue assignment */\n    l2p_port_t ports[RTE_MAX_ETHPORTS]; /**< Per-port L2P state */\n    char *mappings[RTE_MAX_ETHPORTS];   /**< Raw mapping strings from the command line */\n    uint16_t num_mappings;              /**< Number of mapping strings */\n    uint16_t num_ports;                 /**< Number of active ports */\n} l2p_t;\n\n/** Log an error message with the enclosing function name as prefix. */\n#define L2P_ERR(fmt, args...) printf(\"%s: \" fmt \"\\n\", __func__, ##args);\n\n/** Log an error message and return -1. */\n#define L2P_ERR_RET(fmt, args...) \\\n    do {                          \\\n        L2P_ERR(fmt, ##args);     \\\n        return -1;                \\\n    } while (0)\n\n/** Log an error message and return NULL. */\n#define L2P_NULL_RET(fmt, args...) \\\n    do {                           \\\n        L2P_ERR(fmt, ##args);      \\\n        return NULL;               \\\n    } while (0)\n\n/**\n * Return the global l2p_t singleton.\n *\n * @return\n *   Pointer to the global l2p_t mapping table.\n */\nl2p_t *l2p_get(void);\n\n/**\n * Return the l2p_port_t for a given port ID.\n *\n * @param pid\n *   Port ID to look up.\n * @return\n *   Pointer to the l2p_port_t, or NULL if @p pid is invalid.\n */\nstatic __inline__ l2p_port_t *\nl2p_get_port(uint16_t pid)\n{\n    l2p_t *l2p = l2p_get();\n\n    return ((pid < RTE_MAX_ETHPORTS) && (l2p->ports[pid].pid < RTE_MAX_ETHPORTS)) ? &l2p->ports[pid]\n                                                                                  : NULL;\n}\n\n/**\n * Return the number of active ports in the L2P mapping.\n *\n * @return\n *   Number of configured ports.\n */\nstatic __inline__ uint16_t\nl2p_get_port_cnt(void)\n{\n    l2p_t *l2p = l2p_get();\n\n    return l2p->num_ports;\n}\n\n/**\n * Return the lcore ID associated with a port.\n *\n * Scans the lport table and returns the first lcore mapped to @p pid.\n *\n * @param pid\n *   Port ID to search for.\n * @return\n *   Lcore ID, or RTE_MAX_LCORE if none found.\n */\nstatic __inline__ uint16_t\nl2p_get_lcore_by_pid(uint16_t pid)\n{\n    l2p_t *l2p   = l2p_get();\n    uint16_t lid = RTE_MAX_LCORE;\n\n    for (uint16_t i = 0; i < RTE_MAX_LCORE; i++) {\n        if (l2p->lports[i] && l2p->lports[i]->port->pid == pid) {\n            lid = l2p->lports[i]->lid;\n            break;\n        }\n    }\n    return lid;\n}\n\n/**\n * Return the port ID assigned to a given lcore.\n *\n * @param lid\n *   Lcore ID to look up.\n * @return\n *   Port ID, or RTE_MAX_ETHPORTS if @p lid has no port assignment.\n */\nstatic __inline__ uint16_t\nl2p_get_pid_by_lcore(uint16_t lid)\n{\n    l2p_t *l2p   = l2p_get();\n    uint16_t pid = RTE_MAX_ETHPORTS;\n\n    if (!l2p->lports[lid] || !l2p->lports[lid]->port)\n        return pid;\n    if (l2p->lports[lid]->port->pid >= RTE_MAX_ETHPORTS)\n        return pid;\n\n    return l2p->lports[lid]->port->pid;\n}\n\n/**\n * Return the RX mempool for a given port and queue.\n *\n * @param pid\n *   Port ID.\n * @param qid\n *   RX queue ID.\n * @return\n *   Pointer to the RX mempool, or NULL on error.\n */\nstatic __inline__ struct rte_mempool *\nl2p_get_rx_mp(uint16_t pid, uint16_t qid)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (pid >= RTE_MAX_ETHPORTS)\n        L2P_NULL_RET(\"Invalid port ID %u\", pid);\n\n    if (qid >= MAX_QUEUES_PER_PORT)\n        L2P_NULL_RET(\"Invalid queue ID %u\", qid);\n\n    return l2p->ports[pid].rx_mp[qid];\n}\n\n/**\n * Return the TX mempool for a given port and queue.\n *\n * @param pid\n *   Port ID.\n * @param qid\n *   TX queue ID.\n * @return\n *   Pointer to the TX mempool, or NULL on error.\n */\nstatic __inline__ struct rte_mempool *\nl2p_get_tx_mp(uint16_t pid, uint16_t qid)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (pid >= RTE_MAX_ETHPORTS)\n        L2P_NULL_RET(\"Invalid port ID %u\", pid);\n\n    if (qid >= MAX_QUEUES_PER_PORT)\n        L2P_NULL_RET(\"Invalid queue ID %u\", qid);\n\n    return l2p->ports[pid].tx_mp[qid];\n}\n\n/**\n * Return the special TX mempool for a given port and queue.\n *\n * @param pid\n *   Port ID.\n * @param qid\n *   TX queue ID.\n * @return\n *   Pointer to the special TX mempool, or NULL on error.\n */\nstatic __inline__ struct rte_mempool *\nl2p_get_sp_mp(uint16_t pid, uint16_t qid)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (pid >= RTE_MAX_ETHPORTS)\n        L2P_NULL_RET(\"Invalid port ID %u\", pid);\n\n    if (qid >= MAX_QUEUES_PER_PORT)\n        L2P_NULL_RET(\"Invalid queue ID %u\", qid);\n\n    return l2p->ports[pid].sp_mp[qid];\n}\n\n/**\n * Return the lcore operating mode (RX, TX, or both).\n *\n * @param lid\n *   Lcore ID to query.\n * @return\n *   LCORE_MODE_* constant, or -1 on error.\n */\nstatic __inline__ int\nl2p_get_type(uint16_t lid)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (lid >= RTE_MAX_LCORE)\n        L2P_ERR_RET(\"Invalid lcore ID %u\", lid);\n\n    return l2p->lports[lid]->mode;\n}\n\n/**\n * Return the port_info_t for a given port ID.\n *\n * @param pid\n *   Port ID to look up.\n * @return\n *   Pointer to port_info_t, or NULL if @p pid is invalid.\n */\nstatic __inline__ struct port_info_s *\nl2p_get_port_pinfo(uint16_t pid)\n{\n    l2p_port_t *port = l2p_get_port(pid);\n\n    return (port == NULL) ? NULL : port->pinfo;\n}\n\n/**\n * Set the port_info_t pointer for a given port ID.\n *\n * @param pid\n *   Port ID to update.\n * @param pinfo\n *   Pointer to the port_info_t to associate.\n * @return\n *   0 on success, -1 on invalid @p pid.\n */\nstatic __inline__ int\nl2p_set_port_pinfo(uint16_t pid, struct port_info_s *pinfo)\n{\n    l2p_port_t *port = l2p_get_port(pid);\n\n    if (port == NULL)\n        L2P_ERR_RET(\"Invalid port ID %u\", pid);\n\n    port->pinfo = pinfo;\n    return 0;\n}\n\n/**\n * Return the port_info_t for the port assigned to a given lcore.\n *\n * @param lid\n *   Lcore ID to look up.\n * @return\n *   Pointer to port_info_t, or NULL if @p lid is invalid.\n */\nstatic __inline__ struct port_info_s *\nl2p_get_pinfo_by_lcore(uint16_t lid)\n{\n    l2p_t *l2p = l2p_get();\n    l2p_port_t *port;\n\n    if (lid >= RTE_MAX_LCORE || l2p->lports[lid]->lid >= RTE_MAX_LCORE ||\n        (port = l2p->lports[lid]->port) == NULL)\n        L2P_NULL_RET(\"Invalid lcore ID %u\", lid);\n\n    return port->pinfo;\n}\n\n/**\n * Set the port_info_t pointer for the port assigned to a given lcore.\n *\n * @param lid\n *   Lcore ID to update.\n * @param pinfo\n *   Pointer to the port_info_t to associate.\n * @return\n *   0 on success, -1 on invalid @p lid.\n */\nstatic __inline__ int\nl2p_set_pinfo_by_lcore(uint16_t lid, struct port_info_s *pinfo)\n{\n    l2p_t *l2p = l2p_get();\n    l2p_port_t *port;\n\n    if (lid >= RTE_MAX_LCORE || l2p->lports[lid]->lid >= RTE_MAX_LCORE ||\n        (port = l2p->lports[lid]->port) == NULL)\n        L2P_ERR_RET(\"Invalid lcore ID %u\", lid);\n\n    port->pinfo = pinfo;\n\n    return 0;\n}\n\n/**\n * Return the RX and TX queue counts for a port.\n *\n * @param pid\n *   Port ID to query.\n * @param rx_qid\n *   Output: number of RX queues (may be NULL).\n * @param tx_qid\n *   Output: number of TX queues (may be NULL).\n * @return\n *   0 on success, -1 on invalid @p pid.\n */\nstatic __inline__ int\nl2p_get_qcnt(uint16_t pid, uint16_t *rx_qid, uint16_t *tx_qid)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (pid < RTE_MAX_ETHPORTS && l2p->ports[pid].pid < RTE_MAX_ETHPORTS) {\n        if (rx_qid)\n            *rx_qid = l2p->ports[pid].num_rx_qids;\n        if (tx_qid)\n            *tx_qid = l2p->ports[pid].num_tx_qids;\n        return 0;\n    }\n    return -1;\n}\n\n/**\n * Return the number of RX queues for a port.\n *\n * @param pid\n *   Port ID to query.\n * @return\n *   RX queue count, or 0 on invalid @p pid.\n */\nstatic __inline__ int\nl2p_get_rxcnt(uint16_t pid)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (pid < RTE_MAX_ETHPORTS && l2p->ports[pid].pid < RTE_MAX_ETHPORTS)\n        return l2p->ports[pid].num_rx_qids;\n    return 0;\n}\n\n/**\n * Return the number of TX queues for a port.\n *\n * @param pid\n *   Port ID to query.\n * @return\n *   TX queue count, or 0 on invalid @p pid.\n */\nstatic __inline__ int\nl2p_get_txcnt(uint16_t pid)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (pid < RTE_MAX_ETHPORTS && l2p->ports[pid].pid < RTE_MAX_ETHPORTS)\n        return l2p->ports[pid].num_tx_qids;\n    return 0;\n}\n\n/**\n * Return the RX queue ID assigned to a given lcore.\n *\n * @param lid\n *   Lcore ID to query.\n * @return\n *   RX queue ID, or 0 on invalid @p lid.\n */\nstatic __inline__ int\nl2p_get_rxqid(uint16_t lid)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (lid < RTE_MAX_LCORE && l2p->lports[lid] != NULL && l2p->lports[lid]->port != NULL)\n        return l2p->lports[lid]->rx_qid;\n    return 0;\n}\n\n/**\n * Return the TX queue ID assigned to a given lcore.\n *\n * @param lid\n *   Lcore ID to query.\n * @return\n *   TX queue ID, or 0 on invalid @p lid.\n */\nstatic __inline__ int\nl2p_get_txqid(uint16_t lid)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (lid < RTE_MAX_LCORE && l2p->lports[lid] != NULL && l2p->lports[lid]->port != NULL)\n        return l2p->lports[lid]->tx_qid;\n    return 0;\n}\n\n/**\n * Return the RX and TX queue IDs for a port (via the first mapped lcore).\n *\n * @param pid\n *   Port ID to query.\n * @param rx_qid\n *   Output: RX queue ID (may be NULL).\n * @param tx_qid\n *   Output: TX queue ID (may be NULL).\n * @return\n *   0 on success, -1 on invalid @p pid.\n */\nstatic __inline__ int\nl2p_get_qids(uint16_t pid, uint16_t *rx_qid, uint16_t *tx_qid)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (pid < RTE_MAX_ETHPORTS && l2p->ports[pid].pid < RTE_MAX_ETHPORTS) {\n        if (rx_qid)\n            *rx_qid = l2p_get_rxqid(pid);\n        if (tx_qid)\n            *tx_qid = l2p_get_txqid(pid);\n        return 0;\n    }\n    return -1;\n}\n\n/**\n * Return the PCAP info structure for a port.\n *\n * @param pid\n *   Port ID to look up.\n * @return\n *   Pointer to pcap_info_t, or NULL on invalid @p pid.\n */\nstatic __inline__ pcap_info_t *\nl2p_get_pcap(uint16_t pid)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (pid < RTE_MAX_ETHPORTS && l2p->ports[pid].pid < RTE_MAX_ETHPORTS)\n        return l2p->ports[pid].pcap_info;\n    L2P_NULL_RET(\"Invalid port ID %u\", pid);\n}\n\n/**\n * Set the PCAP info pointer for a port.\n *\n * @param pid\n *   Port ID to update.\n * @param pcap_info\n *   PCAP info structure to associate with the port.\n * @return\n *   0 on success, -1 on invalid @p pid.\n */\nstatic __inline__ int\nl2p_set_pcap_info(uint16_t pid, pcap_info_t *pcap_info)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (pid >= RTE_MAX_ETHPORTS || l2p->ports[pid].pid >= RTE_MAX_ETHPORTS)\n        L2P_ERR_RET(\"Invalid port ID %u\", pid);\n    l2p->ports[pid].pcap_info = pcap_info;\n    return 0;\n}\n\n/**\n * Return the PCAP mempool for a port.\n *\n * @param pid\n *   Port ID to look up.\n * @return\n *   Pointer to the PCAP mempool, or NULL on error.\n */\nstatic __inline__ struct rte_mempool *\nl2p_get_pcap_mp(uint16_t pid)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (pid >= RTE_MAX_ETHPORTS || l2p->ports[pid].pid >= RTE_MAX_ETHPORTS ||\n        l2p->ports[pid].pcap_info == NULL)\n        L2P_NULL_RET(\"Invalid port ID %u\", pid);\n\n    return l2p->ports[pid].pcap_info->mp;\n}\n\n/**\n * Set the PCAP mempool for a port.\n *\n * @param pid\n *   Port ID to update.\n * @param mp\n *   Mempool to associate with the port's PCAP state.\n * @return\n *   0 on success, -1 on invalid @p pid.\n */\nstatic __inline__ int\nl2p_set_pcap_mp(uint16_t pid, struct rte_mempool *mp)\n{\n    l2p_t *l2p = l2p_get();\n\n    if (pid >= RTE_MAX_ETHPORTS || l2p->ports[pid].pid >= RTE_MAX_ETHPORTS)\n        L2P_ERR_RET(\"Invalid port ID %u\", pid);\n\n    l2p->ports[pid].pcap_info->mp = mp;\n    return 0;\n}\n\n/**\n * Allocate and initialise the global l2p_t mapping table.\n */\nvoid l2p_create(void);\n\n/**\n * Parse all registered mapping strings and populate the l2p_t table.\n *\n * @return\n *   0 on success, negative on parse error.\n */\nint l2p_parse_mappings(void);\n\n/**\n * Register a mapping string for later parsing.\n *\n * @param str\n *   Mapping string in the form expected by the -m argument parser.\n * @return\n *   0 on success, negative on error.\n */\nint l2p_parse_mapping_add(const char *str);\n\n/**\n * Parse a hex portmask string into a bitmask of port indices.\n *\n * @param portmask\n *   Null-terminated hex string (e.g. \"0x3\" selects ports 0 and 1).\n * @return\n *   Bitmask of selected port IDs.\n */\nuint32_t l2p_parse_portmask(const char *portmask);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __L2P_H */\n"
  },
  {
    "path": "app/lpktgenlib.c",
    "content": "/*-\n * Copyright(c) <2011-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2011 by Keith Wiles @ intel.com */\n\n#define lpktgenlib_c\n#define LUA_LIB\n#define lua_c\n\n#include <pg_ether.h>\n#include <pg_inet.h>\n#include \"lpktgenlib.h\"\n\n#include <stdint.h>\n#include <netinet/in.h>\n\n#include <lua_config.h>\n#include <lua_stdio.h>\n#include <lua_utils.h>\n\n#include \"pktgen-cmds.h\"\n#include <cli.h>\n#include <copyright_info.h>\n#include <luaconf.h>\n#include <lualib.h>\n\n#include <rte_net.h>\n#include <rte_ether.h>\n#include <lua_config.h>\n#include <lua_stdio.h>\n#include <pg_delay.h>\n#include <pg_strings.h>\n\n#include <rte_bus_pci.h>\n#include <rte_bus.h>\n\n#include <cli_help.h>\n\n#ifndef __INTEL_COMPILER\n#pragma GCC diagnostic ignored \"-Wcast-qual\"\n#endif\n\nextern pktgen_t pktgen;\n\nvoid pktgen_quit(void);\n\nstatic int\npktgen_exit(lua_State *L __rte_unused)\n{\n    pktgen_quit();\n    return 0;\n}\n\nstatic inline double\ncycles_to_us(uint64_t cycles)\n{\n    return (cycles == 0) ? 0.0 : ((1.0 / pktgen.hz) * (double)cycles) * Million;\n}\n\nstatic inline portlist_t\npktgen_get_portlist(lua_State *L, int index)\n{\n    portlist_t portlist;\n\n    if (lua_isstring(L, index)) {\n        if (portlist_parse(luaL_checkstring(L, index), pktgen.nb_ports, &portlist) < 0)\n            portlist = INVALID_PORTLIST;\n    } else if (lua_isnumber(L, index))\n        portlist = (uint64_t)lua_tonumber(L, index);\n    else if (lua_isinteger(L, index))\n        portlist = (uint64_t)lua_tointeger(L, index);\n    else {\n        printf(\"%s: unknown Lua variable type not a string, number or int\\n\", __func__);\n        portlist = INVALID_PORTLIST;\n    }\n\n    return portlist;\n}\n\n/**\n *\n * setf_integer - Helper routine to set Lua variables.\n *\n * DESCRIPTION\n * Helper routine to a set Lua variables.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic __inline__ void\nsetf_integer(lua_State *L, const char *name, lua_Integer value)\n{\n    lua_pushinteger(L, value);\n    lua_setfield(L, -2, name);\n}\n\n/**\n *\n * setf_number - Helper routine to set Lua variables.\n *\n * DESCRIPTION\n * Helper routine to a set Lua variables.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic __inline__ void\nsetf_number(lua_State *L, const char *name, lua_Number value)\n{\n    lua_pushnumber(L, value);\n    lua_setfield(L, -2, name);\n}\n\n/**\n *\n * setf_string - Helper routine to set Lua variables.\n *\n * DESCRIPTION\n * Helper routine to a set Lua variables.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic __inline__ void\nsetf_string(lua_State *L, const char *name, const char *value)\n{\n    lua_pushstring(L, value);\n    lua_setfield(L, -2, name);\n}\n\nstatic __inline__ void\nsetf_eth_stats(lua_State *L, const struct rte_eth_stats *stats)\n{\n    setf_integer(L, \"ipackets\", stats->ipackets);\n    setf_integer(L, \"opackets\", stats->opackets);\n    setf_integer(L, \"ibytes\", stats->ibytes);\n    setf_integer(L, \"obytes\", stats->obytes);\n    setf_integer(L, \"ierrors\", stats->ierrors);\n    setf_integer(L, \"oerrors\", stats->oerrors);\n    setf_integer(L, \"rx_nombuf\", stats->rx_nombuf);\n    setf_integer(L, \"imissed\", stats->imissed);\n}\n\nstatic __inline__ void\nsetf_qstats(lua_State *L, const qstats_t *qs)\n{\n    setf_integer(L, \"ipackets\", qs->q_ipackets);\n    setf_integer(L, \"opackets\", qs->q_opackets);\n    setf_integer(L, \"errors\", qs->q_errors);\n}\n\nstatic void\npush_port_stats_t(lua_State *L, const port_stats_t *ps, uint16_t rxq_cnt)\n{\n    /* Assumes a Lua table is on the top of the stack. */\n    lua_newtable(L); /* curr */\n    setf_eth_stats(L, &ps->curr);\n    lua_setfield(L, -2, \"curr\");\n\n    lua_newtable(L); /* sizes */\n    setf_integer(L, \"_64\", ps->sizes._64);\n    setf_integer(L, \"_65_127\", ps->sizes._65_127);\n    setf_integer(L, \"_128_255\", ps->sizes._128_255);\n    setf_integer(L, \"_256_511\", ps->sizes._256_511);\n    setf_integer(L, \"_512_1023\", ps->sizes._512_1023);\n    setf_integer(L, \"_1024_1522\", ps->sizes._1024_1522);\n    setf_integer(L, \"broadcast\", ps->sizes.broadcast);\n    setf_integer(L, \"multicast\", ps->sizes.multicast);\n    setf_integer(L, \"jumbo\", ps->sizes.jumbo);\n    setf_integer(L, \"runt\", ps->sizes.runt);\n    setf_integer(L, \"unknown\", ps->sizes.unknown);\n    lua_setfield(L, -2, \"sizes\");\n\n    lua_newtable(L); /* qstats */\n    if (rxq_cnt > MAX_QUEUES_PER_PORT)\n        rxq_cnt = MAX_QUEUES_PER_PORT;\n    for (uint16_t qid = 0; qid < rxq_cnt; qid++) {\n        lua_pushinteger(L, qid);\n        lua_newtable(L);\n        setf_qstats(L, &ps->qstats[qid]);\n        lua_rawset(L, -3);\n    }\n    lua_setfield(L, -2, \"qstats\");\n}\n\nstatic __inline__ void\ngetf_etheraddr(lua_State *L, const char *field, struct rte_ether_addr *value)\n{\n    lua_getfield(L, 3, field);\n    if (lua_isstring(L, -1))\n        if (pg_ether_aton(luaL_checkstring(L, -1), value) == NULL)\n            lua_putstring(\"failed to convert MAC string %s\\n\", luaL_checkstring(L, -1));\n    lua_pop(L, 1);\n}\n\nstatic __inline__ void\ngetf_ipaddr(lua_State *L, const char *field, void *value, uint32_t flags)\n{\n    lua_getfield(L, 3, field);\n    if (lua_isstring(L, -1)) {\n        _atoip((char *)(uintptr_t)luaL_checkstring(L, -1), flags, value, sizeof(struct pg_ipaddr));\n    }\n    lua_pop(L, 1);\n}\n\nstatic __inline__ uint32_t\ngetf_integer(lua_State *L, const char *field)\n{\n    uint32_t value = 0;\n\n    lua_getfield(L, 3, field);\n    if (lua_isinteger(L, -1))\n        value = luaL_checkinteger(L, -1);\n    lua_pop(L, 1);\n\n    return value;\n}\n\nstatic __inline__ char *\ngetf_string(lua_State *L, const char *field)\n{\n    char *value = NULL;\n\n    lua_getfield(L, 3, field);\n    if (lua_isstring(L, -1))\n        value = (char *)luaL_checkstring(L, -1);\n    lua_pop(L, 1);\n\n    return value;\n}\n\n/**\n *\n * pktgen_set - Set a number of Pktgen values.\n *\n * DESCRIPTION\n * Set a number of Pktgen values for a given port list.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_set(lua_State *L)\n{\n    uint32_t value;\n    portlist_t portlist;\n    char *what;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"set, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    what  = (char *)luaL_checkstring(L, 2);\n    value = luaL_checknumber(L, 3);\n\n    foreach_port(\n        portlist,\n        _do(if (!strcasecmp(what, \"count\")) single_set_tx_count(pinfo, value);\n            else if (!strcasecmp(what, \"size\")) single_set_pkt_size(pinfo, value);\n            else if (!strcasecmp(what, \"rate\")) single_set_tx_rate(pinfo, luaL_checkstring(L, 3));\n            else if (!strcasecmp(what, \"burst\")) single_set_tx_burst(pinfo, value);\n            else if (!strcasecmp(what, \"txburst\")) single_set_tx_burst(pinfo, value);\n            else if (!strcasecmp(what, \"rxburst\")) single_set_rx_burst(pinfo, value);\n            else if (!strcasecmp(what, \"cycles\")) debug_set_tx_cycles(pinfo, value);\n            else if (!strcasecmp(what, \"sport\")) single_set_port_value(pinfo, what[0], value);\n            else if (!strcasecmp(what, \"dport\")) single_set_port_value(pinfo, what[0], value);\n            else if (!strcasecmp(what, \"ttl\")) single_set_ttl_value(pinfo, value);\n            else if (!strcasecmp(what, \"seq_cnt\")) pktgen_set_port_seqCnt(pinfo, value);\n            else if (!strcasecmp(what, \"seqCnt\")) pktgen_set_port_seqCnt(pinfo, value);\n            else if (!strcasecmp(what, \"prime\")) pktgen_set_port_prime(pinfo, value);\n            else if (!strcasecmp(what, \"dump\")) debug_set_port_dump(pinfo, value);\n            else return luaL_error(L, \"set does not support %s\", what);));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * set_seq - Set the sequence data for a given port.\n *\n * DESCRIPTION\n * Set the sequence data for a given port and sequence number.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nset_seq(lua_State *L, uint32_t seqnum)\n{\n    portlist_t portlist;\n    uint32_t pktsize, sport, dport, gtpu_teid;\n    uint16_t vlanid;\n    uint8_t cos, tos;\n    struct rte_ether_addr daddr;\n    struct rte_ether_addr saddr;\n    struct pg_ipaddr ip_daddr;\n    struct pg_ipaddr ip_saddr;\n    char *proto, *ip;\n\n    portlist = pktgen_get_portlist(L, 2);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    if (pg_ether_aton(luaL_checkstring(L, 3), &daddr) == NULL) {\n        lua_putstring(\"invalid destination MAC string %s\\n\", luaL_checkstring(L, 3));\n        return -1;\n    }\n    if (pg_ether_aton(luaL_checkstring(L, 4), &saddr) == NULL) {\n        lua_putstring(\"invalid source MAC string %s\\n\", luaL_checkstring(L, 4));\n        return -1;\n    }\n\n    sport = luaL_checkinteger(L, 7);\n    dport = luaL_checkinteger(L, 8);\n\n    /* Determine if we are IPv4 or IPv6 packets */\n    ip = (char *)luaL_checkstring(L, 9);\n    _atoip(luaL_checkstring(L, 5), 0, &ip_daddr, sizeof(struct pg_ipaddr));\n    _atoip(luaL_checkstring(L, 6), PG_IPADDR_NETWORK, &ip_saddr, sizeof(struct pg_ipaddr));\n    proto   = (char *)luaL_checkstring(L, 10);\n    vlanid  = luaL_checkinteger(L, 11);\n    pktsize = luaL_checkinteger(L, 12);\n    if (lua_gettop(L) == 13)\n        gtpu_teid = luaL_checkinteger(L, 13);\n    else\n        gtpu_teid = 0;\n\n    cos = 0;\n    tos = 0;\n    if (lua_gettop(L) > 13) {\n        cos = luaL_checkinteger(L, 14);\n        tos = luaL_checkinteger(L, 15);\n    }\n\n    if ((proto[0] == 'i') && (ip[3] == '6')) {\n        lua_putstring(\"Must use IPv4 with ICMP type packets\\n\");\n        return -1;\n    }\n\n    foreach_port(portlist,\n                 pktgen_set_seq(pinfo, seqnum, &daddr, &saddr, &ip_daddr, &ip_saddr, sport, dport,\n                                ip[3], proto[0], vlanid, pktsize, gtpu_teid);\n                 pktgen_set_cos_tos_seq(pinfo, seqnum, cos, tos));\n\n    pktgen_update_display();\n\n    return 0;\n}\n\n/**\n *\n * pktgen_seq - Set the sequence data for a given port.\n *\n * DESCRIPTION\n * Set the sequence data for a given port and sequence number.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_seq(lua_State *L)\n{\n    uint32_t seqnum;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"seq, wrong number of arguments\");\n    case 12:\n    case 13:\n        break;\n    }\n    seqnum = luaL_checkinteger(L, 1);\n    if (seqnum >= NUM_SEQ_PKTS)\n        return -1;\n\n    return set_seq(L, seqnum);\n}\n\n/**\n *\n * set_seqTable - Set the sequence data for a given port.\n *\n * DESCRIPTION\n * Set the sequence data for a given port and sequence number.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nset_seqTable(lua_State *L, uint32_t seqnum)\n{\n    portlist_t portlist;\n    uint32_t pktSize, sport, dport, gtpu_teid;\n    uint16_t vlanid;\n    uint8_t cos, tos;\n    struct rte_ether_addr daddr;\n    struct rte_ether_addr saddr;\n    struct pg_ipaddr ip_daddr;\n    struct pg_ipaddr ip_saddr;\n    char *ipProto, *ethType, *tcp_flags;\n\n    portlist = pktgen_get_portlist(L, 2);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    getf_etheraddr(L, \"eth_dst_addr\", &daddr);\n    getf_etheraddr(L, \"eth_src_addr\", &saddr);\n    getf_ipaddr(L, \"ip_dst_addr\", &ip_daddr, PG_IPADDR_V4);\n    getf_ipaddr(L, \"ip_src_addr\", &ip_saddr, PG_IPADDR_NETWORK | PG_IPADDR_V4);\n\n    sport     = getf_integer(L, \"sport\");\n    dport     = getf_integer(L, \"dport\");\n    ipProto   = getf_string(L, \"ipProto\");\n    ethType   = getf_string(L, \"ethType\");\n    vlanid    = getf_integer(L, \"vlanid\");\n    pktSize   = getf_integer(L, \"pktSize\");\n    cos       = getf_integer(L, \"cos\");\n    tos       = getf_integer(L, \"tos\");\n    tcp_flags = getf_string(L, \"tcp_flags\");\n\n    gtpu_teid = getf_integer(L, \"gtpu_teid\");\n\n    if ((ipProto[0] == 'i') && (ethType[3] == '6')) {\n        lua_putstring(\"Must use IPv4 with ICMP type packets\\n\");\n        return -1;\n    }\n\n    foreach_port(portlist,\n                 pktgen_set_seq(pinfo, seqnum, &daddr, &saddr, &ip_daddr, &ip_saddr, sport, dport,\n                                ethType[3], ipProto[0], vlanid, pktSize, gtpu_teid);\n                 pktgen_set_cos_tos_seq(pinfo, seqnum, cos, tos);\n                 seq_set_tcp_flags(pinfo, seqnum, tcp_flags));\n\n    pktgen_update_display();\n\n    return 0;\n}\n\n/**\n *\n * pktgen_seqTable - Set the sequence data for a given port.\n *\n * DESCRIPTION\n * Set the sequence data for a given port and sequence number.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_seqTable(lua_State *L)\n{\n    uint32_t seqnum;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"seqTable, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    seqnum = luaL_checkinteger(L, 1);\n    if (seqnum >= NUM_SEQ_PKTS)\n        return -1;\n\n    return set_seqTable(L, seqnum);\n}\n\n/**\n *\n * pktgen_seq_tcp_flag - Set the TCP Flags for a given port and sequence number.\n *\n * DESCRIPTION\n * Set the sequence data for a given port and sequence number.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_seq_tcp_flags(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t seqnum;\n    const char *flag_str;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"seq_tcp_flags, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    seqnum = luaL_checkinteger(L, 1);\n    if (seqnum >= NUM_SEQ_PKTS)\n        return -1;\n\n    portlist = pktgen_get_portlist(L, 2);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    flag_str = luaL_checkstring(L, 3);\n\n    foreach_port(portlist, seq_set_tcp_flags(pinfo, seqnum, flag_str));\n\n    return 0;\n}\n\n/**\n *\n * pktgen_ports_per_page - Set the number of ports per page.\n *\n * DESCRIPTION\n * Set the number of ports per page.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_ports_per_page(lua_State *L)\n{\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"ports_per_page, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    pktgen_set_page_size(luaL_checkinteger(L, 1));\n    return 0;\n}\n\n/**\n *\n * pktgen_icmp - Enable or Disable ICMP echo processing.\n *\n * DESCRIPTION\n * Enable or disable ICMP echo process for a given port list.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_icmp(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"icmp, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    foreach_port(portlist, enable_icmp_echo(pinfo, estate((char *)luaL_checkstring(L, 2))));\n    return 0;\n}\n\n/**\n *\n * pktgen_sendARP - Send ARP type packets from a given port list.\n *\n * DESCRIPTION\n * Send APR request and gratuitous packets for a given port list.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_sendARP(lua_State *L)\n{\n    portlist_t portlist;\n    char *what;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"sendARP, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    what     = (char *)luaL_checkstring(L, 2);\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    foreach_port(portlist, pktgen_send_arp_requests(pinfo, (what[0] == 'g') ? GRATUITOUS_ARP : 0));\n    return 0;\n}\n\n/**\n *\n * pktgen_set_mac - Set the MAC address for a set of ports.\n *\n * DESCRIPTION\n * Set the MAC address for a set of ports.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_set_mac(lua_State *L)\n{\n    portlist_t portlist;\n    struct rte_ether_addr mac;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"set_mac, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    if (pg_ether_aton(luaL_checkstring(L, 3), &mac) == NULL) {\n        lua_putstring(\"invalid MAC string (%s)\\n \", luaL_checkstring(L, 3));\n        return luaL_error(L, \"invalid MAC string\");\n    }\n\n    foreach_port(portlist, single_set_mac(pinfo, luaL_checkstring(L, 2), &mac));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_macFromArp - Enable or Disable getting MAC address from ARP packets.\n *\n * DESCRIPTION\n * Enable or disable getting MAC address from an ARP request.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_macFromArp(lua_State *L)\n{\n    char *state;\n    uint32_t onOff;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"mac_from_arp, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    state = (char *)luaL_checkstring(L, 1);\n\n    onOff = estate(state);\n\n    enable_mac_from_arp(onOff);\n\n    return 0;\n}\n\n/**\n *\n * pktgen_prototype - Set the packet protocol type.\n *\n * DESCRIPTION\n * Set the packet protocol type.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_prototype(lua_State *L)\n{\n    char *type;\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"set_proto, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    type = (char *)luaL_checkstring(L, 2);\n\n    foreach_port(portlist, single_set_proto(pinfo, type));\n\n    return 0;\n}\n\n/**\n *\n * pktgen_set_ip_addr - Set the ip address value for src and dst.\n *\n * DESCRIPTION\n * Set the IP address for src and dst.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_set_ip_addr(lua_State *L)\n{\n    portlist_t portlist;\n    struct pg_ipaddr ipaddr;\n    int flags;\n    char *type;\n    int ip_ver;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"set_ipaddr, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    type     = (char *)luaL_checkstring(L, 2);\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    if (type[0] == 's')\n        flags = PG_IPADDR_NETWORK;\n    ip_ver = _atoip(luaL_checkstring(L, 3), flags, &ipaddr, sizeof(struct pg_ipaddr));\n\n    foreach_port(portlist, single_set_ipaddr(pinfo, type[0], &ipaddr, ip_ver));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_set_type - Set the type of packet IPv4/v6\n *\n * DESCRIPTION\n * Set the port packet types to IPv4 or v6.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_set_type(lua_State *L)\n{\n    char *type;\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"set_type, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    type     = (char *)luaL_checkstring(L, 2);\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, single_set_pkt_type(pinfo, type));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_set_tcp_flags - Set the TCP flags\n *\n * DESCRIPTION\n * Set the port packet types to IPv4 or v6.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_set_tcp_flags(lua_State *L)\n{\n    char *type;\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"set_type, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    type = (char *)luaL_checkstring(L, 2);\n    foreach_port(portlist, single_set_tcp_flags(pinfo, type));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_send_ping4 - Send ping packets for IPv4\n *\n * DESCRIPTION\n * Send a ping packet for IPv4.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_send_ping4(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"ping4, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, pktgen_ping4(pinfo));\n\n    return 0;\n}\n\n#ifdef INCLUDE_PING6\n/**\n *\n * pktgen_send_ping6 - Send IPv6 ICMP echo requests.\n *\n * DESCRIPTION\n * Send IPv6 ICMP echo requests.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_send_ping6(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"ping6, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, pktgen_ping6(pinfo));\n\n    return 0;\n}\n\n#endif\n\n/**\n *\n * pktgen_pcap - Enable or disable PCAP support sending.\n *\n * DESCRIPTION\n * Enable or disable PCAP sending.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_pcap(lua_State *L)\n{\n    portlist_t portlist;\n    char *what;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"pcap, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    what = (char *)luaL_checkstring(L, 2);\n\n    foreach_port(portlist, enable_pcap(pinfo, estate(what)));\n\n    return 0;\n}\n\n/**\n *\n * pktgen_start - Start ports sending packets.\n *\n * DESCRIPTION\n * Start ports sending packets.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_start(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"start, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, pktgen_start_transmitting(pinfo));\n\n    return 0;\n}\n\n/**\n *\n * pktgen_stop - Stop ports from sending packets\n *\n * DESCRIPTION\n * Stop port from sending packets.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_stop(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"stop, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, pktgen_stop_transmitting(pinfo));\n    return 0;\n}\n\n/**\n *\n * pktgen_scrn - Enable or Disable the screen updates.\n *\n * DESCRIPTION\n * Enable or disable screen updates.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_scrn(lua_State *L)\n{\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"screen, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    pktgen_screen(estate((const char *)luaL_checkstring(L, 1)));\n    return 0;\n}\n\n/**\n *\n * pktgen_prime - Send a set of packet to prime the forwarding tables.\n *\n * DESCRIPTION\n * Send a small set of packet to prime the forwarding table on a port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_prime(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"prime, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, pktgen_prime_ports(pinfo));\n    return 0;\n}\n\nstatic __inline__ void\n__delay(int32_t t)\n{\n    int32_t n;\n\n    while (t > 0) {\n        n = (t > 10) ? 10 : t;\n        rte_delay_us_sleep(n * 1000);\n        t -= n;\n    }\n}\n\n/**\n *\n * pktgen_delay - Delay for a given number of milliseconds.\n *\n * DESCRIPTION\n * Delay a script for a given number of milliseconds.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_delay(lua_State *L)\n{\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"delay, wrong number of arguments\");\n    case 1:\n        break;\n    }\n\n    __delay(luaL_checkinteger(L, 1));\n\n    return 0;\n}\n\n/**\n *\n * pktgen_pause - Delay for a given number of milliseconds and display a message\n *\n * DESCRIPTION\n * Delay a script for a given number of milliseconds and display a message\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_pause(lua_State *L)\n{\n    char *str;\n    int v;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"pause, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    str = (char *)luaL_checkstring(L, 1);\n    if (strlen(str) > 0)\n        lua_putstring(str);\n\n    v = luaL_checkinteger(L, 2);\n    __delay(v);\n\n    return 0;\n}\n\n/**\n *\n * pktgen_continue - Display a message and wait for a single keyboard input.\n *\n * DESCRIPTION\n * Display a message and wait for a keyboard input.\n *\n * RETURNS: the single keyboard character typed as a string.\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_continue(lua_State *L)\n{\n    char buf[4], *str;\n    int n;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"continue, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    str = (char *)luaL_checkstring(L, 1);\n\n    if (strlen(str) > 0)\n        lua_putstring(str);\n\n    buf[0] = '\\0';\n    n      = fread(buf, 1, 1, (FILE *)lua_get_stdin(pktgen.ld));\n    if (n > 0)\n        buf[n] = '\\0';\n\n    lua_pushstring(L, buf);\n    return 1;\n}\n\n/**\n *\n * pktgen_input - Display a message and wait for keyboard input.\n *\n * DESCRIPTION\n * Display a message and wait for a keyboard input.\n *\n * RETURNS: keyboard string typed at display\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_input(lua_State *L)\n{\n    char buf[256], c, *str;\n    uint32_t n, idx;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"input, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    str = (char *)luaL_checkstring(L, 1);\n\n    if (strlen(str) > 0)\n        lua_putstring(str);\n\n    idx      = 0;\n    buf[idx] = '\\0';\n    while (idx < (sizeof(buf) - 2)) {\n        n = fread(&c, 1, 1, (FILE *)lua_get_stdin(pktgen.ld));\n        if ((n <= 0) || (c == '\\r') || (c == '\\n'))\n            break;\n        buf[idx++] = c;\n    }\n    buf[idx] = '\\0';\n\n    lua_pushstring(L, buf);\n    return 1;\n}\n\n/**\n *\n * pktgen_sleep - Sleep for a given number of seconds.\n *\n * DESCRIPTION\n * Delay a script for a given number of seconds.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_sleep(lua_State *L)\n{\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"sleep, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    __delay(luaL_checkinteger(L, 1) * 1000);\n    return 0;\n}\n\n/**\n *\n * pktgen_load - Load and execute a script.\n *\n * DESCRIPTION\n * Load and execute a script\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_load(lua_State *L)\n{\n    char *path;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"load, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    path = (char *)luaL_checkstring(L, 1);\n\n    if (cli_execute_cmdfile(path))\n        return luaL_error(L, \"load command failed for %s\\n\", path);\n    return 0;\n}\n\n/**\n *\n * pktgen_config_save - Save to a configuration file.\n *\n * DESCRIPTION\n * Save configuration to a file.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_config_save(lua_State *L)\n{\n    char *path;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"save, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    path = (char *)luaL_checkstring(L, 1);\n\n    if (pktgen_save(path))\n        return luaL_error(L, \"save command failed for %s\\n\", path);\n    return 0;\n}\n\n/**\n *\n * pktgen_clear - Clear all port statistics\n *\n * DESCRIPTION\n * Clear all port statistics to zero for a given port\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_clear(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"clear, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, pktgen_clear_stats(pinfo));\n    pktgen_update_display();\n\n    return 0;\n}\n\n/**\n *\n * pktgen_clear_all - Clear all port statistics\n *\n * DESCRIPTION\n * Clear all port statistics to zero for a given port\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_clear_all(lua_State *L __rte_unused)\n{\n    forall_ports(pktgen_clear_stats(pinfo));\n    pktgen_update_display();\n\n    return 0;\n}\n\n/**\n *\n * pktgen_cls_screen - Clear and redraw the screen\n *\n * DESCRIPTION\n * Clear and redraw the screen\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_cls_screen(lua_State *L __rte_unused)\n{\n    pktgen_clear_display();\n\n    return 0;\n}\n\n/**\n *\n * pktgen_update - Update the screen information\n *\n * DESCRIPTION\n * Update the screen information\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_update_screen(lua_State *L __rte_unused)\n{\n    pktgen_update_display();\n\n    return 0;\n}\n\n/**\n *\n * pktgen_reset_config - Reset pktgen to all default values.\n *\n * DESCRIPTION\n * Reset pktgen to all default values.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_reset_config(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"reset, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, pktgen_reset(pinfo));\n\n    return 0;\n}\n\n/**\n *\n * pktgen_restart - Reset ports\n *\n * DESCRIPTION\n * Reset ports\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_restart(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"reset, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, pktgen_port_restart(pinfo));\n\n    return 0;\n}\n\n/**\n *\n * range_dst_mac - Set a destination MAC address\n *\n * DESCRIPTION\n * Set a destination MAC address.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_dst_mac(lua_State *L)\n{\n    portlist_t portlist;\n    struct rte_ether_addr mac;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"dst_mac, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    if (pg_ether_aton(luaL_checkstring(L, 3), &mac) == NULL) {\n        lua_putstring(\"invalid destination MAC string %s\\n\", luaL_checkstring(L, 3));\n        return -1;\n    }\n\n    foreach_port(portlist, range_set_dest_mac(pinfo, luaL_checkstring(L, 2), &mac));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_src_mac - Set the source MAC address in the range data.\n *\n * DESCRIPTION\n * Set the source MAC address for a given set of ports in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_src_mac(lua_State *L)\n{\n    portlist_t portlist;\n    struct rte_ether_addr mac;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"src_mac, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    if (pg_ether_aton(luaL_checkstring(L, 3), &mac) == NULL) {\n        lua_putstring(\"invalid source MAC string %s\\n\", luaL_checkstring(L, 3));\n        return -1;\n    }\n\n    foreach_port(portlist, range_set_src_mac(pinfo, luaL_checkstring(L, 2), &mac));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_set_type - Set the type of range packet IPv4/v6\n *\n * DESCRIPTION\n * Set the range packet types to IPv4 or v6.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_set_type(lua_State *L)\n{\n    char *type;\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"pkt_type, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    type     = (char *)luaL_checkstring(L, 2);\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, range_set_pkt_type(pinfo, type));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_dst_ip - Set the IP address in the range data.\n *\n * DESCRIPTION\n * Set the IP address in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_dst_ip(lua_State *L)\n{\n    portlist_t portlist;\n    struct pg_ipaddr ipaddr;\n    char *type;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"dst_ip, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    _atoip(luaL_checkstring(L, 3), 0, &ipaddr, sizeof(struct pg_ipaddr));\n\n    type = (char *)luaL_checkstring(L, 2);\n    foreach_port(portlist, range_set_dst_ip(pinfo, type, &ipaddr));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_src_ip - Set the source IP address in the range data.\n *\n * DESCRIPTION\n * Set the source IP address in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_src_ip(lua_State *L)\n{\n    portlist_t portlist;\n    struct pg_ipaddr ipaddr;\n    char *type;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"src_ip, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    _atoip(luaL_checkstring(L, 3), 0, &ipaddr, sizeof(ipaddr));\n\n    type = (char *)luaL_checkstring(L, 2);\n    foreach_port(portlist, range_set_src_ip(pinfo, type, &ipaddr));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_dst_port - Set the port type in the range data.\n *\n * DESCRIPTION\n * Set the port type in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_dst_port(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"dst_port, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, range_set_dst_port(pinfo, (char *)luaL_checkstring(L, 2),\n                                              luaL_checkinteger(L, 3)));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_ip_proto - Set the ip proto value in the range data.\n *\n * DESCRIPTION\n * Set the ip proto value in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_ip_proto(lua_State *L)\n{\n    portlist_t portlist;\n    const char *ip;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"ip_proto, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    ip = luaL_checkstring(L, 2);\n    foreach_port(portlist, range_set_proto(pinfo, ip));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_src_port - Set the source port value in the range data.\n *\n * DESCRIPTION\n * Set the source port value in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_src_port(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"src_port, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, range_set_src_port(pinfo, (char *)luaL_checkstring(L, 2),\n                                              luaL_checkinteger(L, 3)));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_ttl - Set the ttl value in the range data.\n *\n * DESCRIPTION\n * Set the ttl value in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_ttl(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"set ttl, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist,\n                 range_set_ttl(pinfo, (char *)luaL_checkstring(L, 2), luaL_checkinteger(L, 3)));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_hop_limits - Set the hop_limits value in the range data.\n *\n * DESCRIPTION\n * Set the Hop Limits value in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_hop_limits(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"set hop_limits, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, range_set_hop_limits(pinfo, (char *)luaL_checkstring(L, 2),\n                                                luaL_checkinteger(L, 3)));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_gtpu_teid - Set the GTPU-TEID value in the range data.\n *\n * DESCRIPTION\n * Set the source port value in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_gtpu_teid(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"GTP-U TEID, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, range_set_gtpu_teid(pinfo, (char *)luaL_checkstring(L, 2),\n                                               luaL_checkinteger(L, 3)));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_vlan_id - Set the VLAN id in the range data.\n *\n * DESCRIPTION\n * Set the VLAN id in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_vlan_id(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t vlan_id;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"vlan_id, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    vlan_id = luaL_checkinteger(L, 3);\n\n    foreach_port(portlist, range_set_vlan_id(pinfo, (char *)luaL_checkstring(L, 2), vlan_id));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_cos - Set the CoS in the range data.\n *\n * DESCRIPTION\n * Set the CoS in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_cos(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t cos;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"cos, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    cos = luaL_checkinteger(L, 3);\n\n    foreach_port(portlist, range_set_cos_id(pinfo, (char *)luaL_checkstring(L, 2), cos));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_tos - Set the ToS in the range data.\n *\n * DESCRIPTION\n * Set the ToS in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_tos(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t tos;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"tos, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    tos = luaL_checkinteger(L, 3);\n\n    foreach_port(portlist, range_set_tos_id(pinfo, (char *)luaL_checkstring(L, 2), tos));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_traffic_class - Set the traffic class in the range data.\n *\n * DESCRIPTION\n * Set the Traffic Class in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_traffic_class(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t traffic_class;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"traffic_class, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    traffic_class = luaL_checkinteger(L, 3);\n\n    foreach_port(portlist,\n                 range_set_traffic_class(pinfo, (char *)luaL_checkstring(L, 2), traffic_class));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * single_vlan_id - Set the VLAN id for a single port\n *\n * DESCRIPTION\n * Set the VLAN id for a single port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nsingle_vlan_id(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t vlanid;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"vlanid, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    vlanid = luaL_checkinteger(L, 2);\n    if ((vlanid < MIN_VLAN_ID) || (vlanid > MAX_VLAN_ID))\n        vlanid = 1;\n\n    foreach_port(portlist, single_set_vlan_id(pinfo, vlanid));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * single_cos - Set the 802.1p prio for a single port\n *\n * DESCRIPTION\n * Set the 802.1p cos for a single port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nsingle_cos(lua_State *L)\n{\n    portlist_t portlist;\n    uint8_t cos;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"cos, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    cos = luaL_checkinteger(L, 3);\n    if (cos > MAX_COS)\n        cos = 0;\n\n    foreach_port(portlist, single_set_cos(pinfo, cos));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * single_tos - Set the TOS for a single port\n *\n * DESCRIPTION\n * Set the TOS for a single port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nsingle_tos(lua_State *L)\n{\n    portlist_t portlist;\n    uint8_t tos;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"tos, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    tos = luaL_checkinteger(L, 2);\n\n    foreach_port(portlist, single_set_tos(pinfo, tos));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * single_vxlan_id - Set the VxLAN for a single port\n *\n * DESCRIPTION\n * Set the VxLAN for a single port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nsingle_vxlan_id(lua_State *L)\n{\n    portlist_t portlist;\n    uint8_t flags, group_id;\n    uint32_t vxlan_id;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"tos, wrong number of arguments\");\n    case 4:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    flags    = luaL_checkinteger(L, 2);\n    group_id = luaL_checkinteger(L, 3);\n    vxlan_id = luaL_checkinteger(L, 4);\n\n    foreach_port(portlist, single_set_vxlan(pinfo, flags, group_id, vxlan_id));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * single_vlan - Enable or Disable vlan header\n *\n * DESCRIPTION\n * Enable or disable insertion of VLAN header.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nsingle_vlan(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"process, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, enable_vlan(pinfo, estate(luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * single_vxlan - Enable or Disable vxlan header\n *\n * DESCRIPTION\n * Enable or disable insertion of VxLAN header.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nsingle_vxlan(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"process, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, enable_vxlan(pinfo, estate(luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_mpls_entry - Set the MPLS entry in the range data.\n *\n * DESCRIPTION\n * Set the VLAN id in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_mpls_entry(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t mpls_entry;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"mpls_entry, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    mpls_entry = strtoul(luaL_checkstring(L, 2), NULL, 16);\n\n    foreach_port(portlist, range_set_mpls_entry(pinfo, mpls_entry));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_mpls - Enable or Disable MPLS header\n *\n * DESCRIPTION\n * Enable or disable insertion of MPLS header.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_mpls(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"mpls, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, enable_mpls(pinfo, estate(luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_qinqids - Set the Q-in-Q ID's in the range data.\n *\n * DESCRIPTION\n * Set the Q-in-Q ID's in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_qinqids(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t qinq_id1, qinq_id2;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"qinqids, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    qinq_id1 = luaL_checkinteger(L, 2);\n    if ((qinq_id1 < MIN_VLAN_ID) || (qinq_id1 > MAX_VLAN_ID))\n        qinq_id1 = 1;\n\n    qinq_id2 = luaL_checkinteger(L, 3);\n    if ((qinq_id2 < MIN_VLAN_ID) || (qinq_id2 > MAX_VLAN_ID))\n        qinq_id2 = 1;\n\n    foreach_port(portlist, single_set_qinqids(pinfo, qinq_id1, qinq_id2));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_qinq - Enable or Disable Q-in-Q header\n *\n * DESCRIPTION\n * Enable or disable insertion of Q-in-Q header.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_qinq(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"qinq, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, enable_qinq(pinfo, estate(luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_gre_key - Set the GRE key in the range data.\n *\n * DESCRIPTION\n * Set the GRE key in the range data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_gre_key(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t gre_key;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"gre_key, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    gre_key = luaL_checkinteger(L, 2);\n\n    foreach_port(portlist, range_set_gre_key(pinfo, gre_key));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_gre - Enable or Disable GRE with IPv4 payload\n *\n * DESCRIPTION\n * Enable or disable GRE with IPv4 payload.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_gre(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"gre, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, enable_gre(pinfo, estate(luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_gre_eth - Enable or Disable GRE with Ethernet payload\n *\n * DESCRIPTION\n * Enable or disable GRE with Ethernet payload\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_gre_eth(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"gre_eth, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, enable_gre_eth(pinfo, estate(luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_rnd_s_ip - Enable or disable randomizing the source IP address\n *\n * DESCRIPTION\n * Enable or disable randomizing the source IP address\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_rnd_s_ip(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"rnd_s_ip, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, enable_rnd_s_ip(pinfo, estate(luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_rnd_s_pt - Enable or disable randomizing the source port\n *\n * DESCRIPTION\n * Enable or disable randomizing the source port\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_rnd_s_pt(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"rnd_s_pt, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, enable_rnd_s_pt(pinfo, estate(luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range_pkt_size - Set the port range size.\n *\n * DESCRIPTION\n * Set the port range size.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange_pkt_size(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t size;\n    char *type;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"pkt_size, wrong number of arguments\");\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n    type = (char *)luaL_checkstring(L, 2);\n    size = luaL_checkinteger(L, 3);\n\n    foreach_port(portlist, range_set_pkt_size(pinfo, type, size));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * range - Enable or disable the range data sending.\n *\n * DESCRIPTION\n * Enable or disable the range data sending.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\nrange(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"range, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, enable_range(pinfo, estate((const char *)luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_latency - Enable or disable the latency testing.\n *\n * DESCRIPTION\n * Enable or disable the latency testing.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_latency(lua_State *L)\n{\n    portlist_t portlist;\n    char *what = NULL;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"latency, wrong number of arguments\");\n    case 2:\n    case 3:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    what = (char *)luaL_checkstring(L, 2);\n    if (!what)\n        return luaL_error(L, \"latency, missing argument or not a string\");\n\n    if (lua_gettop(L) == 3) {\n        if (strncasecmp(lua_tostring(L, 2), \"rate\", 4) == 0)\n            foreach_port(portlist, latency_set_rate(pinfo, (uint32_t)luaL_checkinteger(L, 3)));\n        else if (strncasecmp(lua_tostring(L, 2), \"entropy\", 7) == 0)\n            foreach_port(portlist, latency_set_entropy(pinfo, (uint16_t)luaL_checkinteger(L, 3)));\n        else\n            return luaL_error(L, \"latency, invalid arguments must be rate or entropy\");\n    } else if (lua_gettop(L) == 2)\n        foreach_port(portlist, enable_latency(pinfo, estate((const char *)luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_jitter - Set Jitter threshold\n *\n * DESCRIPTION\n * Set Jitter threshold in micro-seconds\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_jitter(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"jitter, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, single_set_jitter(pinfo, luaL_checkinteger(L, 2)));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_pattern - Set the pattern type.\n *\n * DESCRIPTION\n * Set the pattern type.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_pattern(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"pattern, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, pattern_set_type(pinfo, (char *)luaL_checkstring(L, 2)));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_pattern - Set the pattern type.\n *\n * DESCRIPTION\n * Set the pattern type.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_user_pattern(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"user.pattern, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, pattern_set_user_pattern(pinfo, (char *)luaL_checkstring(L, 2)));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_page - Set the page type to be displayed.\n *\n * DESCRIPTION\n * Set the page type to be displayed.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_page(lua_State *L)\n{\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"page, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    pktgen_set_page((char *)luaL_checkstring(L, 1));\n    return 0;\n}\n\n/**\n *\n * pktgen_port - Set the port type number\n *\n * DESCRIPTION\n * Set the port type IPv4 or IPv6\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_port(lua_State *L)\n{\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"port, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    pktgen_set_port_number((uint16_t)luaL_checkinteger(L, 1));\n    return 0;\n}\n\n/**\n *\n * pktgen_process - Enable or Disable input packet processing.\n *\n * DESCRIPTION\n * Enable or disable input packet processing.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_process(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"process, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, enable_process(pinfo, estate((const char *)luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_capture - Enable or Disable capture packet processing.\n *\n * DESCRIPTION\n * Enable or disable capture packet processing.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_capture(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"capture, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, enable_capture(pinfo, estate((const char *)luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_NET_BOND)\n/**\n *\n * pktgen_bonding - Enable or Disable bonding to send zero packets\n *\n * DESCRIPTION\n * Enable or disable bonding packet processing.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_bonding(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"bonding, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, enable_bonding(pinfo, estate((const char *)luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n#endif\n\n/**\n *\n * pktgen_latsampler_params - Set latency sampler params.\n *\n * DESCRIPTION\n * Set latency sampler params.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_latsampler_params(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"latsampler_params, wrong number of arguments\");\n    case 5:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, single_set_latsampler_params(pinfo, (char *)luaL_checkstring(L, 2),\n                                                        (uint32_t)luaL_checkinteger(L, 3),\n                                                        (uint32_t)luaL_checkinteger(L, 4),\n                                                        (char *)luaL_checkstring(L, 5)));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_latsampler - Enable or Disable latency sampler.\n *\n * DESCRIPTION\n * Enable or disable latency sampler.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_latsampler(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"latsampler, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist,\n                 pktgen_start_stop_latency_sampler(pinfo, estate((char *)luaL_checkstring(L, 2))));\n\n    pktgen_update_display();\n    return 0;\n}\n\n/**\n *\n * pktgen_blink - Enable or disable port Led blinking.\n *\n * DESCRIPTION\n * Enable or disable port led blinking.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_blink(lua_State *L)\n{\n    portlist_t portlist;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"blink, wrong number of arguments\");\n    case 2:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    foreach_port(portlist, debug_blink(pinfo, estate((const char *)luaL_checkstring(L, 2))));\n\n    if (pktgen.blinklist)\n        pktgen.flags |= BLINK_PORTS_FLAG;\n    else\n        pktgen.flags &= ~BLINK_PORTS_FLAG;\n    pktgen_update_display();\n\n    return 0;\n}\n\n/**\n *\n * isSending - Get the current state of the transmitter on a port.\n *\n * DESCRIPTION\n * Get the current state of the transmitter on a port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic void\nisSending(lua_State *L, port_info_t *pinfo)\n{\n    lua_pushinteger(L, pinfo->pid); /* Push the table index */\n    lua_pushstring(L, pktgen_port_transmitting(pinfo->pid) ? \"y\" : \"n\");\n\n    /* Now set the table as an array with pid as the index. */\n    lua_rawset(L, -3);\n}\n\n/**\n *\n * pktgen_isSending - Get the current state of the transmitter on a port.\n *\n * DESCRIPTION\n * Get the current state of the transmitter on a port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_isSending(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t n;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"isSending, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    lua_newtable(L);\n\n    n = 0;\n    foreach_port(portlist, _do(isSending(L, pinfo); n++));\n\n    setf_integer(L, \"n\", n);\n\n    return 1;\n}\n\n/**\n *\n * link_state - Get the current link state of a port.\n *\n * DESCRIPTION\n * Get the current link state of a port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic void\nlink_state(lua_State *L, port_info_t *pinfo)\n{\n    char buff[32];\n\n    lua_pushinteger(L, pinfo->pid); /* Push the table index */\n    lua_pushstring(L, pktgen_link_state(pinfo->pid, buff, sizeof(buff)));\n\n    /* Now set the table as an array with pid as the index. */\n    lua_rawset(L, -3);\n}\n\n/**\n *\n * pktgen_linkState - Get the current link state of a port.\n *\n * DESCRIPTION\n * Get the current link state of a port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_linkState(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t n;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"linkState, wrong number of arguments\");\n    case 1:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    lua_newtable(L);\n\n    n = 0;\n    foreach_port(portlist, _do(link_state(L, pinfo); n++));\n\n    setf_integer(L, \"n\", n);\n\n    return 1;\n}\n\n/**\n *\n * port_stats - Return the other port stats for a given port.\n *\n * DESCRIPTION\n * Return the packet stats for a given port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic void\nport_stats(lua_State *L, port_info_t *pinfo)\n{\n    port_stats_t ps = {0};\n    uint16_t rxq_cnt;\n\n    pktgen_port_stats(pinfo->pid, &ps);\n\n    lua_pushinteger(L, pinfo->pid); /* Push the table index */\n    lua_newtable(L);\n\n    /* Full port_stats_t structure as nested table. */\n    rxq_cnt = l2p_get_rxcnt(pinfo->pid);\n    push_port_stats_t(L, &ps, rxq_cnt);\n\n    /* Now set the table as an array with pid as the index. */\n    lua_rawset(L, -3);\n}\n\n/**\n *\n * pktgen_portStats - Return the other port stats for a given ports.\n *\n * DESCRIPTION\n * Return the other port stats for a given ports.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_portStats(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t n;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"portStats, wrong number of arguments\");\n    case 1:\n        break;\n    }\n\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    lua_newtable(L);\n\n    n = 0;\n    foreach_port(portlist, _do(port_stats(L, pinfo); n++));\n\n    setf_integer(L, \"n\", n);\n\n    return 1;\n}\n\n/**\n *\n * port_info - Return the other port information for a given port.\n *\n * DESCRIPTION\n * Return the other port information for a given port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic void\nport_info(lua_State *L, port_info_t *pinfo)\n{\n    struct rte_eth_dev_info dev = {0};\n    pkt_seq_t *pkt;\n    char buff[32] = {0};\n\n    pkt = &pinfo->seq_pkt[SINGLE_PKT];\n\n    /*------------------------------------*/\n    lua_pushinteger(L, pinfo->pid); /* Push the table index */\n    lua_newtable(L);                /* Create the structure table for a packet */\n\n    /*------------------------------------*/\n    lua_pushstring(L, \"total_stats\");\n    lua_newtable(L);\n    setf_integer(L, \"max_ipackets\", pktgen.max_total_ipackets);\n    setf_integer(L, \"max_opackets\", pktgen.max_total_opackets);\n\n    setf_integer(L, \"cumm_rate_ipackets\", pktgen.cumm_rate_totals.ipackets);\n    setf_integer(L, \"cumm_rate_opackets\", pktgen.cumm_rate_totals.opackets);\n\n    setf_integer(L, \"cumm_rate_itotals\", iBitsTotal(pktgen.cumm_rate_totals));\n    setf_integer(L, \"cumm_rate_ototals\", oBitsTotal(pktgen.cumm_rate_totals));\n    lua_rawset(L, -3);\n\n    /*------------------------------------*/\n    lua_pushstring(L, \"info\");\n    lua_newtable(L);\n    setf_string(L, \"pattern_type\",\n                (pinfo->fill_pattern_type == ABC_FILL_PATTERN)    ? \"abcd...\"\n                : (pinfo->fill_pattern_type == NO_FILL_PATTERN)   ? \"None\"\n                : (pinfo->fill_pattern_type == ZERO_FILL_PATTERN) ? \"Zero\"\n                                                                  : pinfo->user_pattern);\n\n    if (rte_atomic64_read(&pinfo->transmit_count) == 0)\n        setf_string(L, \"tx_count\", \"Forever\");\n    else\n        setf_integer(L, \"tx_count\", rte_atomic64_read(&pinfo->transmit_count));\n    setf_integer(L, \"tx_rate\", pinfo->tx_rate);\n\n    setf_integer(L, \"pkt_size\", pkt->pkt_size + RTE_ETHER_CRC_LEN);\n    setf_integer(L, \"tx_burst\", pinfo->tx_burst);\n    setf_integer(L, \"rx_burst\", pinfo->rx_burst);\n\n    setf_string(L, \"eth_type\",\n                (pkt->ethType == RTE_ETHER_TYPE_IPV4)   ? \"IPv4\"\n                : (pkt->ethType == RTE_ETHER_TYPE_IPV6) ? \"IPv6\"\n                : (pkt->ethType == RTE_ETHER_TYPE_ARP)  ? \"ARP\"\n                                                        : \"Other\");\n    setf_string(L, \"proto_type\",\n                (pkt->ipProto == PG_IPPROTO_TCP)                               ? \"TCP\"\n                : (pkt->ipProto == PG_IPPROTO_ICMP)                            ? \"ICMP\"\n                : (rte_atomic64_read(&pinfo->port_flags) & SEND_VXLAN_PACKETS) ? \"VXLAN\"\n                                                                               : \"UDP\");\n    setf_integer(L, \"vlanid\", pkt->vlanid);\n\n    /*------------------------------------*/\n    lua_pushstring(L, \"l2_l3_info\");\n    lua_newtable(L);\n    setf_integer(L, \"src_port\", pkt->sport);\n    setf_integer(L, \"dst_port\", pkt->dport);\n    setf_integer(L, \"ttl\", pkt->ttl);\n\n    inet_ntop4(buff, sizeof(buff), htonl(pkt->ip_dst_addr.addr.ipv4.s_addr), 0xFFFFFFFF);\n    setf_string(L, \"dst_ip\", buff);\n\n    inet_ntop4(buff, sizeof(buff), htonl(pkt->ip_src_addr.addr.ipv4.s_addr), pkt->ip_mask);\n    setf_string(L, \"src_ip\", buff);\n\n    inet_mtoa(buff, sizeof(buff), &pkt->eth_dst_addr);\n    setf_string(L, \"dst_mac\", buff);\n    inet_mtoa(buff, sizeof(buff), &pkt->eth_src_addr);\n    setf_string(L, \"src_mac\", buff);\n    lua_rawset(L, -3);\n\n    if (rte_eth_dev_info_get(pinfo->pid, &dev) < 0)\n        rte_exit(EXIT_FAILURE, \"Cannot get device info for port %u\\n\", pinfo->pid);\n\n    const struct rte_bus *bus = NULL;\n    if (dev.device)\n        bus = rte_bus_find_by_device(dev.device);\n    if (bus && !strcmp(rte_bus_name(bus), \"pci\")) {\n        char name[RTE_ETH_NAME_MAX_LEN];\n        char vend[8], device[8];\n\n        vend[0] = device[0] = '\\0';\n        sscanf(rte_dev_bus_info(dev.device), \"vendor_id=%4s, device_id=%4s\", vend, device);\n\n        rte_eth_dev_get_name_by_port(pinfo->pid, name);\n        snprintf(buff, sizeof(buff), \"%d/%s:%s/%s\", rte_dev_numa_node(dev.device), vend, device,\n                 rte_dev_name(dev.device));\n    } else\n        snprintf(buff, sizeof(buff), \"%04x:%04x/%02x:%02d.%d\", 0, 0, 0, 0, 0);\n    setf_string(L, \"pci_vendor\", buff);\n\n    /*------------------------------------*/\n    lua_pushstring(L, \"tx_debug\");\n    lua_newtable(L);\n    setf_integer(L, \"tx_pps\", pinfo->tx_pps);\n    setf_integer(L, \"tx_cycles\", pinfo->tx_cycles);\n    lua_rawset(L, -3);\n\n    /*------------------------------------*/\n    lua_pushstring(L, \"802.1p\");\n    lua_newtable(L);\n    setf_integer(L, \"cos\", pkt->cos);\n    setf_integer(L, \"dscp\", pkt->tos >> 2);\n    setf_integer(L, \"ipp\", pkt->tos >> 5);\n    lua_rawset(L, -3);\n\n    /*------------------------------------*/\n    lua_pushstring(L, \"VxLAN\");\n    lua_newtable(L);\n    setf_integer(L, \"vni_flags\", pkt->vni_flags);\n    setf_integer(L, \"group_id\", pkt->group_id);\n    setf_integer(L, \"vlan_id\", pkt->vlanid);\n\n    pktgen_link_state(pinfo->pid, buff, sizeof(buff));\n    setf_string(L, \"link_state\", buff);\n    lua_rawset(L, -3);\n    lua_rawset(L, -3);\n\n    /* Now set the table as an array with pid as the index. */\n    lua_rawset(L, -3);\n}\n\n/**\n *\n * pktgen_portInfo - Return the other port Info for a given ports.\n *\n * DESCRIPTION\n * Return the other port Info for a given ports.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_portInfo(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t n;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"portInfo, wrong number of arguments\");\n    case 1:\n        break;\n    }\n\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    lua_newtable(L);\n\n    n = 0;\n    foreach_port(portlist, _do(port_info(L, pinfo); n++));\n\n    setf_integer(L, \"n\", n);\n\n    return 1;\n}\n\nstatic void\n_pktgen_push_line(void *arg, const char **h)\n{\n    lua_State *L = arg;\n    int j;\n\n    for (j = 0; h[j] != NULL; j++) {\n        if (strcmp(h[j], CLI_HELP_PAUSE)) {\n            lua_pushstring(L, h[j]);\n            lua_concat(L, 2);\n        }\n    }\n}\n\n/**\n *\n * pktgen_help - Display the current help information.\n *\n * DESCRIPTION\n * Display the current help information.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_help(lua_State *L)\n{\n    lua_concat(L, 0);\n\n    cli_help_foreach(_pktgen_push_line, L);\n\n    return 1;\n}\n\n/**\n *\n * pktgen_portCount - Return number of ports used\n *\n * DESCRIPTION\n * Return the number of ports used\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_portCount(lua_State *L)\n{\n    lua_pushinteger(L, pktgen.nb_ports);\n\n    return 1;\n}\n\n/**\n *\n * pktgen_totalPorts - Return the total number of ports\n *\n * DESCRIPTION\n * Return the total number of ports seen by DPDK\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_totalPorts(lua_State *L)\n{\n    lua_pushinteger(L, pktgen.nb_ports);\n\n    return 1;\n}\n\n/**\n *\n * pktgen_rnd - Setup random bit patterns\n *\n * DESCRIPTION\n * Setup the random bit pattern support.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_rnd(lua_State *L)\n{\n    portlist_t portlist;\n    char mask[33] = {0};\n    const char *msk;\n    int i, mask_idx = 0;\n    char curr_bit;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"rnd, wrong number of arguments\");\n    case 4:\n        break;\n    }\n    portlist = pktgen_get_portlist(L, 1);\n    if (portlist == INVALID_PORTLIST)\n        return luaL_error(L, \"invalid portlist\");\n\n    msk = luaL_checkstring(L, 4);\n    if (strcmp(msk, \"off\"))\n        /* Filter invalid characters from provided mask. This way the user can\n         * more easily enter long bitmasks, using for example '_' as a separator\n         * every 8 bits. */\n        for (i = 0; (mask_idx < 32) && ((curr_bit = msk[i]) != '\\0'); i++)\n            if ((curr_bit == '0') || (curr_bit == '1') || (curr_bit == '.') || (curr_bit == 'X') ||\n                (curr_bit == 'x'))\n                mask[mask_idx++] = curr_bit;\n\n    foreach_port(portlist, enable_random(pinfo, pktgen_set_random_bitfield(\n                                                    pinfo->rnd_bitfields, luaL_checkinteger(L, 2),\n                                                    luaL_checkinteger(L, 3), mask)\n                                                    ? ENABLE_STATE\n                                                    : DISABLE_STATE));\n\n    return 0;\n}\n\nstatic void\nadd_rnd_pattern(lua_State *L, port_info_t *pinfo)\n{\n    uint32_t i, curr_bit, idx;\n    char mask[36]; /* 4*8 bits, 3 delimiter spaces, \\0 */\n    bf_spec_t *curr_spec;\n    rnd_bits_t *rnd_bits = pinfo->rnd_bitfields;\n\n    lua_pushinteger(L, pinfo->pid); /* Push the port number as the table index */\n    lua_newtable(L);                /* Create the structure table for a packet */\n\n    for (idx = 0; idx < MAX_RND_BITFIELDS; idx++) {\n        curr_spec = &rnd_bits->specs[idx];\n\n        memset(mask, 0, sizeof(mask));\n        memset(mask, ' ', sizeof(mask) - 1);\n        /* Compose human readable bitmask representation */\n        for (i = 0; i < MAX_BITFIELD_SIZE; ++i) {\n            curr_bit = (uint32_t)1 << (MAX_BITFIELD_SIZE - i - 1);\n\n            /* + i >> 3 for space delimiter after every 8 bits.\n             * Need to check rndMask before andMask: for random bits, the\n             * andMask is also 0. */\n            mask[i + (i >> 3)] = ((ntohl(curr_spec->rndMask) & curr_bit) != 0)   ? 'X'\n                                 : ((ntohl(curr_spec->andMask) & curr_bit) == 0) ? '0'\n                                 : ((ntohl(curr_spec->orMask) & curr_bit) != 0)  ? '1'\n                                                                                 : '.';\n        }\n\n        lua_pushinteger(L, idx); /* Push the RND bit index */\n        lua_newtable(L);         /* Create the structure table for a packet */\n        setf_integer(L, \"offset\", curr_spec->offset);\n        setf_string(L, \"mask\", mask);\n        setf_string(L, \"active\", (rnd_bits->active_specs & (1 << idx)) ? \"Yes\" : \"No\");\n        lua_rawset(L, -3);\n    }\n\n    lua_rawset(L, -3);\n}\n\n/**\n *\n * pktgen_rnd_list - Return the random bit patterns in a table\n *\n * DESCRIPTION\n * Return a table of the random bit patterns.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_rnd_list(lua_State *L)\n{\n    portlist_t portlist;\n    int n;\n\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"rnd_list, wrong number of arguments\");\n    case 1:\n    case 0:\n        break;\n    }\n    if (lua_gettop(L) == 1) {\n        portlist = pktgen_get_portlist(L, 1);\n        if (portlist == INVALID_PORTLIST)\n            return luaL_error(L, \"invalid portlist\");\n    } else\n        portlist = -1;\n\n    lua_newtable(L);\n\n    n = 0;\n    foreach_port(portlist, _do(add_rnd_pattern(L, pinfo); n++));\n\n    setf_integer(L, \"n\", n);\n\n    return 1;\n}\n\n/**\n *\n * pktgen_run - Run a Lua or command script on the local disk or in a string.\n *\n * DESCRIPTION\n * Run a script file on the local disk, can be Lua or command lines.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_run(lua_State *L)\n{\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"run( ['cmd'|'lua'], <string_or_path>), arguments wrong.\");\n    case 3:\n        break;\n    }\n\n    /* A cmd executes a file on the disk and can be a lua Script of command line file. */\n    if (strcasecmp(\"cmd\", luaL_checkstring(L, 1)) == 0)\n        cli_execute_cmdfile(luaL_checkstring(L, 2));\n    else if (strcasecmp(\"lua\", luaL_checkstring(L, 1)) == 0) /* Only a Lua script in memory. */\n        lua_execute_string(pktgen.ld, (char *)luaL_checkstring(L, 2));\n    else\n        return luaL_error(L, \"run( ['cmd'|'lua'], <string>), arguments wrong.\");\n\n    return 0;\n}\n\n/**\n *\n * pktgen_clock_gettime - Enable or Disable clock_gettime support.\n *\n * DESCRIPTION\n * Enable or disable clock_gettime support.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_clock_gettime(lua_State *L)\n{\n    switch (lua_gettop(L)) {\n    default:\n        return luaL_error(L, \"clock_gettime, wrong number of arguments\");\n    case 1:\n        break;\n    }\n\n    enable_clock_gettime(estate((char *)luaL_checkstring(L, 1)));\n\n    pktgen_update_display();\n    return 0;\n}\n\nstatic const char *lua_help_info[] = {\n    \"Pktgen Lua functions and values using pktgen.XXX to access\\n\",\n    \"set            - Set a number of options\\n\",\n    \"set_mac        - Set the MAC address for a port\\n\",\n    \"set_ipaddr     - Set the src and dst IP addresses\\n\",\n    \"mac_from_arp   - Configure MAC from ARP packet\\n\",\n    \"set_proto      - Set the prototype value\\n\",\n    \"set_type       - Set the type value\\n\",\n    \"\\n\",\n    \"seq            - Set the sequence data for a port\\n\",\n    \"seqTable       - Set the sequence data for a port using tables\\n\",\n    \"ports_per_page - Set the number of ports per page\\n\",\n    \"icmp_echo      - Enable/disable ICMP echo support\\n\",\n    \"send_arp       - Send a ARP request or GRATUITOUS_ARP\\n\",\n    \"pcap           - Load a PCAP file\\n\",\n    \"ping4          - Send a Ping IPv4 packet (ICMP echo)\\n\",\n#ifdef INCLUDE_PING6\n    \"ping6          - Send a Ping IPv6 packet (ICMP echo)\\n\",\n#endif\n    \"start          - Start a set of ports sending packets\\n\",\n    \"stop           - Stop a set of ports sending packets\\n\",\n    \"screen         - Turn off and on the screen updates\\n\",\n    \"prime          - Send a small number of packets to prime the routes\\n\",\n    \"delay          - Delay a given number of milliseconds\\n\",\n    \"pause          - Delay for a given number of milliseconds and display message\\n\",\n    \"sleep          - Delay a given number of seconds\\n\",\n    \"load           - load and run a command file or Lua file.\\n\",\n    \"save           - Save the configuration of Pktgen to a file.\\n\",\n    \"clear          - Clear stats for the given ports\\n\",\n    \"clr            - Clear all stats on all ports\\n\",\n    \"cls            - Redraw the screen\\n\",\n    \"clock_gettime  - Enable or Disable using clock_gettime() data\\n\",\n    \"\\n\",\n    \"update         - Update the screen information\\n\",\n    \"reset          - Reset the configuration to all ports\\n\",\n    \"vlan           - Enable or disable VLAN header\\n\",\n    \"mpls           - Enable or disable MPLS header\\n\",\n    \"qinq           - Enable or disable Q-in-Q header\\n\",\n    \"gre            - Enable or disable GRE with IPv4 payload\\n\",\n    \"gre_eth        - Enable or disable GRE with Ethernet payload\\n\",\n    \"rnd_s_ip       - Enable or disable randomizing the source IP address\\n\",\n    \"rnd_s_pt       - Enable or disable randomizing the source port\\n\",\n    \"rnd            - Enable or disable random bit patterns for a given portlist\\n\",\n    \"rnd_list       - List of current random bit patterns\\n\",\n    \"\\n\",\n    \"Range commands\\n\",\n    \"dst_mac        - Set the destination MAC address for a port\\n\",\n    \"src_mac        - Set the src MAC address for a port\\n\",\n    \"src_ip         - Set the source IP address and netmask value\\n\",\n    \"dst_ip         - Set the destination IP address\\n\",\n    \"src_port       - Set the IP source port number\\n\",\n    \"dst_port       - Set the IP destination port number\\n\",\n    \"ttl            - Set the Time to Live value\\n\",\n    \"hop_limits     - Set the Hop Limit value\\n\",\n    \"vlan_id        - Set the vlan id value\\n\",\n    \"mpls_entry     - Set the MPLS entry\\n\",\n    \"qinqids        - Set the Q-in-Q ID's\\n\",\n    \"gre_key        - Set the range GRE key\\n\",\n    \"pkt_size       - the packet size for a range port\\n\",\n    \"range          - Enable or disable sending range data on a port.\\n\",\n    \"rxtap          - Enable or disable RX Tap packet processing on a port\\n\",\n    \"txtap          - Enable or disable TX Tap packet processing on a port\\n\",\n    \"latsampler_params - set latency sampler params\\n\",\n    \"latsampler     - enable or disable latency sampler\\n\",\n    \"pattern        - Set pattern type\\n\",\n    \"userPattern    - Set the user pattern string\\n\",\n    \"jitter         - Set the jitter threshold\\n\",\n    \"gtpu_teid      - Set GTP-U TEID\\n\",\n    \"\\n\",\n    \"page           - Select a page to display, seq, range, pcap and a number from 0-N\\n\",\n    \"port           - select a different port number used for sequence and range pages.\\n\",\n    \"process        - Enable or disable input packet processing on a port\\n\",\n    \"capture        - Enable or disable capture packet processing on a port\\n\",\n    \"bonding        - Enable or disable bonding support for sending zero packets\\n\",\n    \"blink          - Blink an led on a port\\n\",\n    \"help           - Return the help text\\n\",\n    \"Lua.help       - Lua command help text\\n\",\n    \"\\n\",\n    \"isSending      - Test to see if a port is sending packets\\n\",\n    \"linkState      - Return the current link state of a port\\n\",\n    \"\\n\",\n    \"portStats      - Return the current port stats (port_stats_t snapshot)\\n\",\n    \"portInfo       - Return the current port configuration/info (no stats)\\n\",\n    \"portCount      - Number of port being used\\n\",\n    \"totalPorts     - Total number of ports seen by DPDK\\n\",\n    \"\\n\",\n    \"run            - Load a Lua string or command file and execute it.\\n\",\n    \"continue       - Display a message and wait for keyboard key and return\\n\",\n    \"input          - Wait for a keyboard input line and return line.\\n\",\n    \"\\n\",\n    \"Pktgen.info.XXXX values below\\n\",\n    \"\\n\",\n    \"Lua_Version    - Lua version string\\n\",\n    \"Lua_Release    - Lua release string\\n\",\n    \"Lua_Copyright  - Lua Copyright string\\n\",\n    \"Lua_Authors    - Lua Authors string\\n\",\n    \"\\n\",\n    \"Pktgen_Version - Pktgen version string\\n\",\n    \"Pktgen_Copyright - Pktgen copyright string\\n\",\n    \"Pktgen_Authors - Pktgen Authors string\\n\",\n    \"DPDK_Version   - DPDK version string\\n\",\n    \"DPDK_Copyright - DPDK copyright string\",\n    \"\\n\",\n    \"startSeqIdx    - Start of Sequence index value\\n\",\n    \"singlePktIdx   - Single packet index value\\n\",\n    \"rangePktIdx    - Start of the Range packet index\\n\",\n    \"pingPktIdx     - Ping packet index value\\n\",\n    \"startExtraTxIdx- Start of Extra TX index value\",\n    \"\\n\",\n    \"numSeqPkts     - Max number of sequence packets\\n\",\n    \"numExtraTxPkts - Number of Extra TX packet buffers\\n\",\n    \"numTotalPkts   - Number of total packet buffers\\n\",\n    \"\\n\",\n    \"minPktSize     - Min packet size plus FCS\\n\",\n    \"maxPktSize     - Max packet size plus FCS\\n\",\n    \"minVlanID      - Min VLAN ID value\\n\",\n    \"maxVlanID      - Max VLAN ID value\\n\",\n    \"vlanTagSize    - VLAN Tag size\\n\",\n    \"minCos       - Min 802.1p value\\n\",\n    \"maxCos       - Max 802.1p value\\n\",\n    \"minTOS       - Min TOS value\\n\",\n    \"maxTOS       - Max TOS value\\n\",\n    \"mbufCacheSize  - mbuf cache size value]n\",\n    \"\\n\",\n    \"defaultPktBurst- Default Tx burst packet count\\n\",\n    \"defaultPktTxBurst- Default Tx burst packet count\\n\",\n    \"defaultPktRxBurst- Default Rx burst packet count\\n\",\n    \"defaultBuffSize- Default buffer size value\\n\",\n    \"maxMbufsPerPort- Max mbufs per port value\\n\",\n    \"maxPrimeCount  - Max prime count\\n\",\n    \"portCount      - Number of ports used\\n\",\n    \"totalPorts     - Total ports found\\n\",\n\n    NULL};\n\n/**\n *\n * pktgen_lua_help - Display the current Lua help information.\n *\n * DESCRIPTION\n * Display the current Lua help information.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_lua_help(lua_State *L)\n{\n    int i;\n\n    lua_concat(L, 0);\n    for (i = 1; lua_help_info[i] != NULL; i++) {\n        lua_pushstring(L, lua_help_info[i]);\n        lua_concat(L, 2);\n    }\n\n    return 1;\n}\n\nstatic const luaL_Reg pktgenlib_range[] = {\n    /* Range commands */\n    {\"dst_mac\", range_dst_mac},             /* Set the destination MAC address for a port */\n    {\"src_mac\", range_src_mac},             /* Set the src MAC address for a port */\n    {\"set_type\", range_set_type},           /* Set the packet type to IPv4 or IPv6 */\n    {\"src_ip\", range_src_ip},               /* Set the source IP address and netmask value */\n    {\"dst_ip\", range_dst_ip},               /* Set the destination IP address */\n    {\"ip_proto\", range_ip_proto},           /* Set the IP Protocol type */\n    {\"src_port\", range_src_port},           /* Set the IP source port number */\n    {\"dst_port\", range_dst_port},           /* Set the IP destination port number */\n    {\"ttl\", range_ttl},                     /* Set the IP TTL value */\n    {\"hop_limits\", range_hop_limits},       /* Set the IPv6 Hop Limits value */\n    {\"vlan_id\", range_vlan_id},             /* Set the vlan id value */\n    {\"mpls_entry\", range_mpls_entry},       /* Set the MPLS entry value */\n    {\"qinqids\", range_qinqids},             /* Set the Q-in-Q ID values */\n    {\"gre_key\", range_gre_key},             /* Set the GRE key */\n    {\"pkt_size\", range_pkt_size},           /* the packet size for a range port */\n    {\"cos\", range_cos},                     /* Set the COS value */\n    {\"tos\", range_tos},                     /* Set the COS value */\n    {\"traffic_class\", range_traffic_class}, /* Set the IPv6 Traffic Class */\n    {NULL, NULL}};\n\nstatic const luaL_Reg pktgenlib[] = {\n    {\"quit\", pktgen_exit},\n    {\"set\", pktgen_set}, /* Set a number of options */\n\n    {\"start\", pktgen_start}, /* Start a set of ports sending packets */\n    {\"stop\", pktgen_stop},   /* Stop a set of ports sending packets */\n\n    /* Set the single packet value on main screen */\n    {\"set_mac\", pktgen_set_mac},             /* Set the MAC address for a port */\n    {\"set_ipaddr\", pktgen_set_ip_addr},      /* Set the src and dst IP addresses */\n    {\"mac_from_arp\", pktgen_macFromArp},     /* Configure MAC from ARP packet */\n    {\"set_proto\", pktgen_prototype},         /* Set the prototype value */\n    {\"set_type\", pktgen_set_type},           /* Set the type value */\n    {\"set_tcp_flags\", pktgen_set_tcp_flags}, /* Set TCP flags for a given port */\n\n    {\"ping4\", pktgen_send_ping4}, /* Send a Ping IPv4 packet (ICMP echo) */\n#ifdef INCLUDE_PING6\n    {\"ping6\", pktgen_send_ping6}, /* Send a Ping IPv6 packet (ICMP echo) */\n#endif\n\n    {\"pcap\", pktgen_pcap},        /* Load a PCAP file */\n    {\"icmp_echo\", pktgen_icmp},   /* Enable/disable ICMP echo support */\n    {\"send_arp\", pktgen_sendARP}, /* Send a ARP request or GRATUITOUS_ARP */\n\n    /* Setup sequence packets */\n    {\"seq\", pktgen_seq},                     /* Set the sequence data for a port */\n    {\"seqTable\", pktgen_seqTable},           /* Set the sequence data for a port using tables */\n    {\"seq_tcp_flags\", pktgen_seq_tcp_flags}, /* Set tcp flags */\n\n    {\"screen\", pktgen_scrn},      /* Turn off and on the screen updates */\n    {\"prime\", pktgen_prime},      /* Send a small number of packets to prime the routes */\n    {\"delay\", pktgen_delay},      /* Delay a given number of milliseconds */\n    {\"pause\", pktgen_pause},      /* Delay for a given number of milliseconds and display message */\n    {\"sleep\", pktgen_sleep},      /* Delay a given number of seconds */\n    {\"load\", pktgen_load},        /* load and run a command file or Lua file. */\n    {\"save\", pktgen_config_save}, /* Save the configuration of Pktgen to a file. */\n    {\"clear\", pktgen_clear},      /* Clear stats for the given ports */\n    {\"clr\", pktgen_clear_all},    /* Clear all stats on all ports */\n    {\"cls\", pktgen_cls_screen},   /* Redraw the screen */\n    {\"update\", pktgen_update_screen},  /* Update the screen information */\n    {\"reset\", pktgen_reset_config},    /* Reset the configuration to all ports */\n    {\"port_restart\", pktgen_restart},  /* Reset ports */\n    {\"portCount\", pktgen_portCount},   /* Used port count value */\n    {\"totalPorts\", pktgen_totalPorts}, /* Total ports seen by DPDK */\n\n    {\"vlan\", single_vlan},      /* Enable or disable VLAN header */\n    {\"vlanid\", single_vlan_id}, /* Set the vlan ID for a given portlist */\n\n    {\"cos\", single_cos},           /* Set the prio for a given portlist */\n    {\"tos\", single_tos},           /* Set the tos for a given portlist */\n    {\"vxlan\", single_vxlan},       /* Enable or disable VxLAN */\n    {\"vxlan_id\", single_vxlan_id}, /* Set the VxLAN values */\n\n    {\"mpls\", pktgen_mpls},         /* Enable or disable MPLS header */\n    {\"qinq\", pktgen_qinq},         /* Enable or disable Q-in-Q header */\n    {\"gre\", pktgen_gre},           /* Enable or disable GRE with IPv4 payload */\n    {\"gre_eth\", pktgen_gre_eth},   /* Enable or disable GRE with Ethernet payload */\n    {\"rnd_s_ip\", pktgen_rnd_s_ip}, /* Enable or disable randomizing the source IP address */\n    {\"rnd_s_pt\", pktgen_rnd_s_pt}, /* Enable or disable randomizing the source port */\n\n    {\"set_range\", range}, /* Enable or disable sending range data on a port. */\n\n    {\"ports_per_page\", pktgen_ports_per_page}, /* Set the number of ports per page */\n    {\"page\", pktgen_page}, /* Select a page to display, seq, range, pcap and a number from 0-N */\n    {\"port\", pktgen_port}, /* select a different port number used for sequence and range pages. */\n    {\"process\", pktgen_process}, /* Enable or disable input packet processing on a port */\n    {\"capture\", pktgen_capture}, /* Enable or disable capture on a port */\n#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_NET_BOND)\n    {\"bonding\", pktgen_bonding}, /* Enable or disable bonding on a port */\n#endif\n    {\"blink\", pktgen_blink},       /* Blink an led on a port */\n    {\"help\", pktgen_help},         /* Return the help text */\n    {\"Lua.help\", pktgen_lua_help}, /* Lua command help text */\n\n    {\"isSending\", pktgen_isSending}, /* Test to see if a port is sending packets */\n    {\"linkState\", pktgen_linkState}, /* Return the current link state of a port */\n\n    {\"portStats\", pktgen_portStats}, /* return the current port stats */\n    {\"portInfo\", pktgen_portInfo},   /* return the current port info */\n\n    {\"run\", pktgen_run},           /* Load a Lua string or command file and execute it. */\n    {\"continue\", pktgen_continue}, /* Display a message and wait for keyboard key and return */\n    {\"input\", pktgen_input},       /* Wait for a keyboard input line and return line. */\n\n    {\"pattern\", pktgen_pattern},          /* Set pattern type */\n    {\"userPattern\", pktgen_user_pattern}, /* Set the user pattern string */\n    {\"latency\", pktgen_latency},          /* Enable or disable latency testing */\n    {\"jitter\", pktgen_jitter},            /* Set the jitter threshold */\n    {\"gtpu_teid\", range_gtpu_teid},       /* set GTP-U TEID. */\n\n    {\"rnd\", pktgen_rnd},           /* Set up the rnd function on a portlist */\n    {\"rnd_list\", pktgen_rnd_list}, /* Return a table of rnd bit patterns per port */\n\n    {\"latsampler_params\", pktgen_latsampler_params}, /* set latency sampler params */\n    {\"latsampler\", pktgen_latsampler},               /* enable or disable latency sampler */\n\n    {\"clock_gettime\", pktgen_clock_gettime}, /* Enable/disable clock_gettime support */\n\n    {NULL, NULL}};\n\n/* }====================================================== */\n\n/**\n *\n * luaopen_pktgen - Initialize the Lua support for pktgen.\n *\n * DESCRIPTION\n * Initialize the Lua library for Pktgen.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nLUALIB_API int\nluaopen_pktgen(lua_State *L)\n{\n    luaL_newlib(L, pktgenlib);\n\n    lua_pushstring(L, \"info\"); /* Push the table index name */\n    lua_newtable(L);           /* Create the structure table for information */\n\n    setf_string(L, \"Lua_Version\", (char *)LUA_VERSION);\n    setf_string(L, \"Lua_Release\", (char *)LUA_RELEASE);\n    setf_string(L, \"Lua_Copyright\", (char *)LUA_COPYRIGHT);\n    setf_string(L, \"Lua_Authors\", (char *)LUA_AUTHORS);\n\n    setf_string(L, \"Pktgen_Version\", (char *)PKTGEN_VERSION);\n    setf_string(L, \"Pktgen_Copyright\", (char *)copyright_msg());\n    setf_string(L, \"Pktgen_Authors\", (char *)\"Keith Wiles @ Intel Corp\");\n    setf_string(L, \"DPDK_Version\", (char *)rte_version());\n\n    /* Now set the table for the info values. */\n    lua_rawset(L, -3);\n\n    lua_pushstring(L, \"range\"); /* Push the table index name */\n    lua_newtable(L);            /* Create the structure table for information */\n\n    luaL_setfuncs(L, pktgenlib_range, 0);\n\n    lua_rawset(L, -3);\n\n    return 1;\n}\n\n/**\n *\n * pktgen_lua_openlib - Open the Pktgen Lua library.\n *\n * DESCRIPTION\n * Open and initialize the Pktgen Lua Library.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_lua_openlib(lua_State *L)\n{\n    lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */\n\n    luaL_requiref(L, LUA_PKTGENLIBNAME, luaopen_pktgen, 1);\n    lua_pop(L, 1);\n\n    lua_gc(L, LUA_GCRESTART, 0);\n}\n"
  },
  {
    "path": "app/lpktgenlib.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2011 by Keith Wiles @ intel.com */\n\n#ifndef LPKTGENLIB_H_\n#define LPKTGENLIB_H_\n\n/**\n * @file\n *\n * Lua bindings for the Pktgen application API.\n *\n * Registers the \"pktgen\" Lua library so that Lua scripts can control\n * port configuration, traffic generation, and statistics collection.\n * Declarations are conditionally compiled under LUA_ENABLED.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifdef LUA_ENABLED\n#define lua_c\n#include <lua.h>\n#include <lauxlib.h>\n\n#define LUA_PKTGENLIBNAME \"pktgen\" /**< Lua library name for the pktgen bindings */\n#define PKTGEN_SHORTCUTS  \"Pktgen\" /**< Lua shortcut table name */\n\n/**\n * Open the Pktgen Lua library and register all pktgen API functions.\n *\n * Called automatically by the Lua runtime when the library is required.\n *\n * @param L\n *   Lua state to register the library into.\n * @return\n *   Number of values pushed onto the Lua stack (1 — the library table).\n */\nLUALIB_API int luaopen_pktgen(lua_State *L);\n\n/**\n * Register all Pktgen Lua libraries (including shortcut aliases) into @p L.\n *\n * @param L\n *   Lua state to register the libraries into.\n */\nvoid pktgen_lua_openlib(lua_State *L);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* LPKTGENLIB_H_ */\n"
  },
  {
    "path": "app/meson.build",
    "content": "sources = files(\n\t'cli-functions.c',\n    'l2p.c',\n\t'pktgen-arp.c',\n\t'pktgen-capture.c',\n\t'pktgen-cmds.c',\n\t'pktgen-cpu.c',\n\t'pktgen-display.c',\n\t'pktgen-dump.c',\n\t'pktgen-ether.c',\n\t'pktgen-gre.c',\n\t'pktgen-gtpu.c',\n\t'pktgen-ipv4.c',\n\t'pktgen-ipv6.c',\n\t'pktgen-latency.c',\n\t'pktgen-log.c',\n\t'pktgen-main.c',\n\t'pktgen-pcap.c',\n\t'pktgen-port-cfg.c',\n\t'pktgen-random.c',\n\t'pktgen-range.c',\n\t'pktgen-seq.c',\n\t'pktgen-stats.c',\n\t'pktgen-sys.c',\n\t'pktgen-tcp.c',\n\t'pktgen-udp.c',\n\t'pktgen-vlan.c',\n\t'pktgen.c',\n\t'xorshift64star.c',\n)\n\nif get_option('enable_lua')\n    sources += files('lpktgenlib.c')\nendif\n\ncflags = ['-D__PROJECT_VERSION=\"' + meson.project_version() + '\"']\n\ndeps = [dpdk, common, utils, vec, plugin, cli, lua, hmap]\n\nif fgen_dep.found()\n    deps += [fgen_dep]\nendif\n\ndeps += [cc.find_library('rte_net_i40e', dirs: [dpdk_libs_path], required: false)]\ndeps += [cc.find_library('rte_net_ixgbe', dirs: [dpdk_libs_path], required: false)]\ndeps += [cc.find_library('rte_net_ice', dirs: [dpdk_libs_path], required: false)]\ndeps += [cc.find_library('rte_bus_vdev', dirs: [dpdk_libs_path], required: false)]\n\ndeps += [dependency('threads')]\ndeps += [dependency('numa', required: true)]\ndeps += [dependency('pcap', required: true)]\ndeps += [cc.find_library('dl', required: false)]\ndeps += [cc.find_library('m', required: false)]\ndeps += [cc.find_library('bsd', required: true)]\n\npktgen = executable('pktgen',\n\t\tsources,\n\t\tc_args: cflags,\n\t\tinstall: true,\n\t\tdependencies: [deps, lua_dep, dpdk_bond])\n"
  },
  {
    "path": "app/pktgen-arp.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <cli_scrn.h>\n#include <lua_config.h>\n#include <rte_arp.h>\n\n#include \"pktgen-arp.h\"\n\n#include \"pktgen.h\"\n#include \"pktgen-cmds.h\"\n#include \"pktgen-log.h\"\n#include \"pktgen-txbuff.h\"\n\nvoid\narp_pkt_dump(struct rte_mbuf *m)\n{\n    struct rte_ether_hdr *eth;\n    struct rte_arp_hdr *arp;\n    char dst[64], src[64];\n    char sip[64], tip[64];\n\n    eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);\n    arp = rte_pktmbuf_mtod_offset(m, struct rte_arp_hdr *, sizeof(struct rte_ether_hdr));\n\n    printf(\"\\nARP Packet Dump\\n\");\n\n    rte_ether_format_addr(dst, sizeof(dst), &eth->dst_addr);\n    rte_ether_format_addr(src, sizeof(src), &eth->src_addr);\n    printf(\"  Ethernet Header DST: %s, SRC: %s, Type: %04x\\n\", dst, src, ntohs(eth->ether_type));\n\n    printf(\"  ARP Header Type: %04x, Proto: %04x, hlen: %d, plen: %d, opcode: %d\\n\",\n           ntohs(arp->arp_hardware), ntohs(arp->arp_protocol), arp->arp_hlen, arp->arp_plen,\n           ntohs(arp->arp_opcode));\n\n    rte_ether_format_addr(dst, sizeof(dst), &arp->arp_data.arp_sha);\n    rte_ether_format_addr(src, sizeof(src), &arp->arp_data.arp_tha);\n    inet_ntop(AF_INET, &arp->arp_data.arp_sip, sip, sizeof(sip));\n    inet_ntop(AF_INET, &arp->arp_data.arp_tip, tip, sizeof(tip));\n    printf(\"  ARP Data Sender: %s-%s, Target: %s-%s\\n\", dst, sip, src, tip);\n}\n\n/**\n *\n * pktgen_send_arp - Send an ARP request packet.\n *\n * DESCRIPTION\n * Create and send an ARP request packet.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_send_arp(uint32_t pid, uint32_t type, uint8_t seq_idx)\n{\n    port_info_t *pinfo = l2p_get_port_pinfo(pid);\n    l2p_port_t *port;\n    pkt_seq_t *pkt;\n    struct rte_mbuf *m;\n    struct rte_ether_hdr *eth;\n    struct rte_arp_hdr *arp;\n    uint32_t addr;\n\n    pkt  = &pinfo->seq_pkt[seq_idx];\n    port = l2p_get_port(pid);\n    {\n        const uint16_t tx_qid = l2p_get_txqid(rte_lcore_id());\n        if (rte_mempool_get(port->sp_mp[tx_qid], (void **)&m)) {\n            pktgen_log_warning(\"No packet buffers found\");\n            return;\n        }\n    }\n    eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);\n    arp = (struct rte_arp_hdr *)&eth[1];\n\n    /* src and dest addr */\n    memset(&eth->dst_addr, 0xFF, 6);\n    rte_ether_addr_copy(&pkt->eth_src_addr, &eth->src_addr);\n    eth->ether_type = htons(RTE_ETHER_TYPE_ARP);\n\n    memset(arp, 0, sizeof(struct rte_arp_hdr));\n\n    rte_memcpy(&arp->arp_data.arp_sha, &pkt->eth_src_addr, 6);\n    addr = htonl(pkt->ip_src_addr.addr.ipv4.s_addr);\n    inetAddrCopy(&arp->arp_data.arp_sip, &addr);\n\n    if (likely(type == GRATUITOUS_ARP)) {\n        rte_memcpy(&arp->arp_data.arp_tha, &pkt->eth_src_addr, 6);\n        addr = htonl(pkt->ip_src_addr.addr.ipv4.s_addr);\n        inetAddrCopy(&arp->arp_data.arp_tip, &addr);\n    } else {\n        memset(&arp->arp_data.arp_tha, 0, 6);\n        addr = htonl(pkt->ip_dst_addr.addr.ipv4.s_addr);\n        inetAddrCopy(&arp->arp_data.arp_tip, &addr);\n    }\n\n    /* Fill in the rest of the ARP packet header */\n    arp->arp_hardware = htons(ETH_HW_TYPE);\n    arp->arp_protocol = htons(RTE_ETHER_TYPE_IPV4);\n    arp->arp_hlen     = 6;\n    arp->arp_plen     = 4;\n    arp->arp_opcode   = htons(ARP_REQUEST);\n\n    m->pkt_len  = 60;\n    m->data_len = 60;\n\n    tx_send_packets(pinfo, l2p_get_txqid(rte_lcore_id()), &m, 1);\n}\n\n/**\n *\n * pktgen_process_arp - Handle a ARP request input packet and send a response.\n *\n * DESCRIPTION\n * Handle a ARP request input packet and send a response if required.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_process_arp(struct rte_mbuf *m, uint32_t pid, uint32_t qid, uint32_t vlan)\n{\n    port_info_t *pinfo        = l2p_get_port_pinfo(pid);\n    pkt_seq_t *pkt            = NULL;\n    struct rte_ether_hdr *eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);\n    struct rte_arp_hdr *arp   = (struct rte_arp_hdr *)&eth[1];\n\n    /* Adjust for a vlan header if present */\n    if (vlan)\n        arp = (struct rte_arp_hdr *)((char *)arp + sizeof(struct rte_vlan_hdr));\n\n    /* Process all ARP requests if they are for us. */\n    if (arp->arp_opcode == htons(ARP_REQUEST)) {\n        int idx;\n\n        if (arp->arp_data.arp_tip == arp->arp_data.arp_sip) { /* GARP Packet */\n            idx = pktgen_find_matching_ipdst(pinfo, arp->arp_data.arp_sip);\n\n            /* Found a matching packet, replace the dst address */\n            if (idx >= 0) {\n                rte_ether_addr_copy(&arp->arp_data.arp_sha, &pkt->eth_dst_addr);\n                pktgen_clear_display();\n            }\n            return;\n        }\n\n        idx = pktgen_find_matching_ipsrc(pinfo, arp->arp_data.arp_tip);\n\n        /* ARP request not for this interface. */\n        if (likely(idx >= 0)) {\n            struct rte_mbuf *m1;\n            l2p_port_t *port;\n\n            pkt  = &pinfo->seq_pkt[idx];\n            port = l2p_get_port(pid);\n            m1   = rte_pktmbuf_copy(m, port->sp_mp[qid], 0, UINT32_MAX);\n            if (unlikely(m1 == NULL))\n                return;\n            eth = rte_pktmbuf_mtod(m1, struct rte_ether_hdr *);\n            arp = (struct rte_arp_hdr *)&eth[1];\n\n            /* Grab the source MAC address as the destination address for the port. */\n            if (unlikely(pktgen.flags & MAC_FROM_ARP_FLAG)) {\n                rte_ether_addr_copy(&arp->arp_data.arp_sha, &pkt->eth_dst_addr);\n                for (uint32_t i = 0; i < pinfo->seqCnt; i++)\n                    pktgen_packet_ctor(pinfo, i, -1);\n            }\n\n            /* Swap the two MAC addresses */\n            ethAddrSwap(&arp->arp_data.arp_sha, &arp->arp_data.arp_tha);\n\n            /* Swap the two IP addresses */\n            inetAddrSwap(&arp->arp_data.arp_tip, &arp->arp_data.arp_sip);\n\n            /* Set the packet to ARP reply */\n            arp->arp_opcode = htons(ARP_REPLY);\n\n            /* Swap the MAC addresses */\n            ethAddrSwap(&eth->dst_addr, &eth->src_addr);\n\n            /* Copy in the MAC address for the reply. */\n            rte_ether_addr_copy(&pkt->eth_src_addr, &arp->arp_data.arp_sha);\n            rte_ether_addr_copy(&pkt->eth_src_addr, &eth->src_addr);\n\n            m1->ol_flags = 0;\n\n            tx_send_packets(pinfo, qid, &m1, 1);\n            return;\n        }\n    } else if (arp->arp_opcode == htons(ARP_REPLY)) {\n        int idx;\n\n        idx = pktgen_find_matching_ipsrc(pinfo, arp->arp_data.arp_tip);\n\n        /* ARP request not for this interface. */\n        if (likely(idx >= 0)) {\n            pkt = &pinfo->seq_pkt[idx];\n\n            /* Grab the real destination MAC address */\n            if (pkt->ip_dst_addr.addr.ipv4.s_addr == ntohl(arp->arp_data.arp_sip))\n                rte_ether_addr_copy(&arp->arp_data.arp_sha, &pkt->eth_dst_addr);\n\n            pktgen.flags |= PRINT_LABELS_FLAG;\n        }\n    }\n}\n"
  },
  {
    "path": "app/pktgen-arp.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_ARP_H_\n#define _PKTGEN_ARP_H_\n\n/**\n * @file\n *\n * ARP packet transmission, processing, and debug dump for Pktgen.\n */\n\n#include <stdint.h>\n\n#include <rte_mbuf.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Transmit an ARP request or reply on port @p pid.\n *\n * @param pid\n *   Port index to send the ARP packet on.\n * @param type\n *   ARP operation type (e.g. RTE_ARP_OP_REQUEST or RTE_ARP_OP_REPLY).\n * @param seq_idx\n *   Packet sequence slot index used as the ARP template.\n */\nvoid pktgen_send_arp(uint32_t pid, uint32_t type, uint8_t seq_idx);\n\n/**\n * Process a received ARP packet and send a reply if warranted.\n *\n * @param m\n *   Received mbuf containing the ARP packet.\n * @param pid\n *   Port index on which the packet arrived.\n * @param qid\n *   Queue index on which the packet arrived.\n * @param vlan\n *   VLAN ID extracted from the outer header (0 if untagged).\n */\nvoid pktgen_process_arp(struct rte_mbuf *m, uint32_t pid, uint32_t qid, uint32_t vlan);\n\n/**\n * Hex-dump the ARP fields of mbuf @p m to stdout for debugging.\n *\n * @param m\n *   Mbuf containing the ARP packet to dump.\n */\nvoid arp_pkt_dump(struct rte_mbuf *m);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_ARP_H_ */\n"
  },
  {
    "path": "app/pktgen-capture.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include \"pktgen-capture.h\"\n#include <time.h>\n#include <sys/stat.h>\n\n#include <rte_memcpy.h>\n#include <rte_memzone.h>\n#include <rte_string_fns.h>\n\n#ifdef LUA_ENABLED\n#include <lua_config.h>\n#endif\n\n#include \"pktgen-cmds.h\"\n#include \"pktgen-log.h\"\n#include \"pktgen-display.h\"\n\n#define CAPTURE_BUFF_SIZE (4 * (1024 * 1024))\n\n/**\n *\n * pktgen_packet_capture_init - Initialize memory and data structures for packet\n * capture.\n *\n * DESCRIPTION\n * Initialization of memory zones and data structures for packet capture\n * storage.\n *\n * PARAMETERS:\n * capture: capture_t struct that will keep a pointer to the allocated memzone\n * socket_id: Socket where the memzone will be reserved\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_packet_capture_init(uint16_t sid)\n{\n    char memzone_name[RTE_MEMZONE_NAMESIZE];\n    capture_t *cap = &pktgen.capture[sid];\n\n    snprintf(memzone_name, sizeof(memzone_name), \"Capture_MZ_%d\", sid);\n    cap->mz = rte_memzone_reserve(memzone_name, CAPTURE_BUFF_SIZE, sid,\n                                  RTE_MEMZONE_2MB | RTE_MEMZONE_SIZE_HINT_ONLY);\n    if (cap->mz == NULL)\n        printf(\"*** Unable to create capture memzone for socket ID %d\\n\", sid);\n    else {\n        cap->port    = RTE_MAX_ETHPORTS;\n        cap->used    = 0;\n        cap->nb_pkts = 0;\n\n        cap->tail = (cap_hdr_t *)cap->mz->addr;\n        cap->end  = (cap_hdr_t *)((char *)cap->mz->addr + (cap->mz->len - sizeof(cap_hdr_t)));\n        memset(cap->tail, 0, sizeof(cap_hdr_t));\n        memset(cap->end, 0, sizeof(cap_hdr_t));\n    }\n}\n\n/**\n *\n * pktgen_set_capture - Enable or disable packet capturing\n *\n * DESCRIPTION\n * Set up packet capturing for the given ports and make sure only 1 port per\n * socket is in capture mode.\n *\n * PARAMETERS:\n * info: port to capture from\n * onOff: enable or disable capturing?\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_set_capture(port_info_t *pinfo, uint32_t onOff)\n{\n    capture_t *cap;\n\n    if (onOff == ENABLE_STATE) {\n        /* Enabling an already enabled port is a no-op */\n        if (rte_atomic64_read(&pinfo->port_flags) & CAPTURE_PKTS)\n            return;\n\n        /* Find an lcore that can capture packets for the requested port */\n        uint16_t lid;\n        if ((lid = l2p_get_lcore_by_pid(pinfo->pid)) >= RTE_MAX_LCORE) {\n            pktgen_log_warning(\"Port %d has no rx lcore: capture is not possible\", pinfo->pid);\n            return;\n        }\n\n        /* Get socket of the selected lcore and check if capturing is possible */\n        uint16_t sid = coreinfo_get(lid)->socket_id;\n\n        cap = &pktgen.capture[sid];\n\n        if (cap->mz == NULL) {\n            pktgen_log_warning(\n                \"No memory allocated for capturing on socket %d for port %d, are hugepages\"\n                \" allocated on this socket?\",\n                sid, pinfo->pid);\n            return;\n        }\n\n        /* Everything checks out: enable packet capture */\n        cap->used    = 0;\n        cap->port    = pinfo->pid;\n        cap->nb_pkts = 0;\n\n        cap->tail = cap->mz->addr;\n\n        /* Write end-of-data sentinel to start of capture memory. This */\n        /* effectively clears previously captured data. */\n        memset(cap->tail, 0, sizeof(cap_hdr_t));\n        memset(cap->end, 0, sizeof(cap_hdr_t));\n\n        pktgen_set_port_flags(pinfo, CAPTURE_PKTS);\n\n        pktgen_log_info(\"Capturing on port %d, lcore %d, socket %d; buffer size: %.2f MB \",\n                        pinfo->pid, lid, sid, (double)cap->mz->len / (1024 * 1024));\n        pktgen_log_info(\"  (~%.2f seconds for 64 byte packets at line rate)\",\n                        (double)cap->mz->len /\n                            (66 /* 64 bytes payload + 2 bytes for payload size */\n                             * ((double)pinfo->link.link_speed * Million / 8) /* Xbit -> Xbyte */\n                             / 84) /* 64 bytes payload + 20 byte etherrnet\n                                    * frame overhead: 84 bytes per packet */\n        );\n    } else {\n        if (!(rte_atomic64_read(&pinfo->port_flags) & CAPTURE_PKTS))\n            return;\n\n        int sid;\n        for (sid = 0; sid < RTE_MAX_NUMA_NODES; ++sid) {\n            cap = &pktgen.capture[sid];\n            if (cap->mz && (cap->port == pinfo->pid))\n                break;\n        }\n\n        /* This should never happen. */\n        if (sid == RTE_MAX_NUMA_NODES) {\n            pktgen_log_error(\"Could not find socket for port %d\", pinfo->pid);\n            return;\n        }\n\n        /* If there is previously captured data in the buffer, write it to disk. */\n        if (cap->nb_pkts > 0) {\n            pcap_t *pcap;\n            pcap_dumper_t *pcap_dumper;\n            struct pcap_pkthdr pcap_hdr;\n            cap_hdr_t *hdr;\n            time_t t;\n            char filename[64];\n            char str_time[64];\n            size_t mem_dumped = 0;\n            unsigned int pct  = 0;\n\n            char status[1024];\n            printf(\"Used %'lu, count %'u\\n\", cap->used, cap->nb_pkts);\n            sprintf(status, \"\\r    Dumping ~%.2fMB of captured data to disk: 0%%\",\n                    (double)cap->used / (1024 * 1024));\n            scrn_printf(0, 0, \"\\n%s\", status);\n\n            pcap = pcap_open_dead(DLT_EN10MB, 65535);\n\n            t = time(NULL);\n            strftime(str_time, sizeof(str_time), \"%Y%m%d-%H%M%S\", localtime(&t));\n            snprintf(filename, sizeof(filename), \"pktgen-%s-%d.pcap\", str_time, cap->port);\n            pcap_dumper = pcap_dump_open(pcap, filename);\n\n            hdr = cap->mz->addr;\n\n            for (uint32_t i = 0; i < cap->nb_pkts; i++) {\n                if (hdr->pkt_len == 0) {\n                    printf(\"\\n>>> Hit packet length zero at %'u of %'u\\n\", i, cap->nb_pkts);\n                    break;\n                }\n                struct timespec ts;\n                clock_gettime(CLOCK_REALTIME, &ts);\n                pcap_hdr.len        = hdr->pkt_len;\n                pcap_hdr.caplen     = hdr->data_len;\n                pcap_hdr.ts.tv_sec  = ts.tv_sec;\n                pcap_hdr.ts.tv_usec = ts.tv_nsec / 1000;\n\n                pcap_dump((u_char *)pcap_dumper, &pcap_hdr, (const u_char *)hdr->pkt);\n\n                hdr = (cap_hdr_t *)(hdr->pkt + hdr->data_len);\n\n                mem_dumped = hdr->pkt - (unsigned char *)cap->mz->addr;\n\n                /* The amount of data to dump to disk, is potentially very large */\n                /* (a few gigabytes), so print a percentage counter. */\n                if (pct < ((mem_dumped * 100) / cap->used)) {\n                    pct = (mem_dumped * 100) / cap->used;\n\n                    if (pct % 10 == 0)\n                        strncatf(status, \"%d%%\", pct);\n                    else if (pct % 2 == 0)\n                        strncatf(status, \".\");\n\n                    scrn_printf(0, 0, \"%s\", status);\n                }\n            }\n            scrn_printf(0, 0, \"\\r\");\n            scrn_printf(0, 0, \"\\n\"); /* Clean of the screen a bit */\n\n            pcap_dump_close(pcap_dumper);\n            pcap_close(pcap);\n\n            chmod(filename, 0666);\n        }\n\n        cap->used = 0;\n        cap->tail = (cap_hdr_t *)cap->mz->addr;\n        cap->port = RTE_MAX_ETHPORTS;\n\n        pktgen_clr_port_flags(pinfo, CAPTURE_PKTS);\n    }\n}\n\n/**\n *\n * pktgen_packet_capture_bulk - Capture packets to memory.\n *\n * DESCRIPTION\n * Capture packet contents to memory, so they can be written to disk later.\n *\n * A captured packet is stored as follows:\n * - uint16_t: non-truncated packet length\n * - uint16_t: size of actual packet contents that are stored\n * - unsigned char[]: packet contents (number of bytes stored equals previous\n *       uint16_t)\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_packet_capture_bulk(struct rte_mbuf **pkts, uint32_t nb_dump, capture_t *cap)\n{\n    uint32_t dlen, plen, i;\n    struct rte_mbuf *pkt;\n\n    /* Don't capture if buffer is full */\n    if (cap->tail >= cap->end)\n        return;\n\n    for (i = 0; i < nb_dump; i++) {\n        pkt = pkts[i];\n\n        /* If the packet is segmented by DPDK, only the contents of the first\n         * segment are captured. Capturing all segments uses too much CPU\n         * cycles, which causes packets to be dropped.\n         * Hence, data_len is used instead of pkt_len. */\n        dlen = pkt->data_len;\n        plen = pkt->pkt_len;\n        if (plen == 0)\n            plen = 128;\n        if (dlen == 0)\n            dlen = 128;\n\n        /* If packet to capture is larger than available buffer size, stop capturing.\n         * The packet data is prepended by the untruncated packet length and\n         * the amount of captured data (which can be less than the packet size\n         * if DPDK has stored the packet contents in segmented mbufs).\n         */\n        if ((cap_hdr_t *)(cap->tail->pkt + dlen) > cap->end)\n            break;\n\n        /* Write untruncated data length and size of the actually captured data. */\n        cap->tail->pkt_len  = plen;\n        cap->tail->data_len = dlen;\n        cap->tail->tstamp   = pktgen_get_time();\n\n        rte_memcpy(cap->tail->pkt, rte_pktmbuf_mtod(pkt, uint8_t *), dlen);\n\n        cap->tail = (cap_hdr_t *)(cap->tail->pkt + dlen);\n\n        cap->nb_pkts++;\n    }\n\n    /* Write end-of-data sentinel */\n    cap->tail->pkt_len = 0;\n    cap->used          = (unsigned char *)cap->tail - (unsigned char *)cap->mz->addr;\n}\n"
  },
  {
    "path": "app/pktgen-capture.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_CAPTURE_H_\n#define _PKTGEN_CAPTURE_H_\n\n/**\n * @file\n *\n * In-memory packet capture for Pktgen.\n *\n * Provides structures and functions for capturing received packets into a\n * DPDK memzone with per-packet timestamps and length metadata.\n */\n\n#include <stddef.h>\n#include <inttypes.h>\n\n#include <rte_memzone.h>\n#include <rte_mbuf.h>\n\n#include \"pktgen-port-cfg.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Per-packet capture record header, immediately followed by packet data. */\ntypedef struct cap_hdr_s {\n    uint64_t tstamp;   /**< Capture timestamp in TSC cycles */\n    uint16_t pkt_len;  /**< Original packet length in bytes */\n    uint16_t data_len; /**< Number of packet bytes stored after this header */\n    uint8_t pkt[0];    /**< Inline packet data (flexible array) */\n} cap_hdr_t;\n\n/** Capture buffer state for one NUMA socket. */\ntypedef struct capture_s {\n    const struct rte_memzone *mz; /**< Memory region to store packets */\n    cap_hdr_t *tail;              /**< Current tail pointer in the pkt buffer */\n    cap_hdr_t *end;               /**< Points to just before the end[-1] of the buffer */\n    size_t used;                  /**< Memory used by captured packets */\n    uint32_t nb_pkts;             /**< Number of packets in capture pool */\n    uint16_t port;                /**< port for this memzone */\n} capture_t;\n\n/**\n * Allocate and initialise the capture memzone for a NUMA socket.\n *\n * @param socket_id\n *   NUMA socket ID on which to allocate the capture buffer.\n */\nvoid pktgen_packet_capture_init(uint16_t socket_id);\n\n/**\n * Enable or disable packet capture on a port.\n *\n * @param pinfo  Per-port state.\n * @param onOff  ENABLE_STATE to start capturing, DISABLE_STATE to stop.\n */\nvoid pktgen_set_capture(port_info_t *pinfo, uint32_t onOff);\n\n/**\n * Capture a burst of received packets into the per-socket capture buffer.\n *\n * @param pkts     Array of mbufs to capture.\n * @param nb_dump  Number of mbufs in @p pkts.\n * @param capture  Capture state for the NUMA socket.\n */\nvoid pktgen_packet_capture_bulk(struct rte_mbuf **pkts, uint32_t nb_dump, capture_t *capture);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_CAPTURE_H_ */\n"
  },
  {
    "path": "app/pktgen-cmds.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <string.h>\n#include <sys/stat.h>\n\n#include <lua_config.h>\n\n#include \"pktgen.h\"\n\n#include \"pktgen-cmds.h\"\n\n#include \"pktgen-display.h\"\n\n#include <pg_delay.h>\n\n#include <rte_ether.h>\n#include <rte_net.h>\n#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_NET_BOND)\n#include <rte_eth_bond.h>\n#include <rte_eth_bond_8023ad.h>\n#endif\n\nstatic char hash_line[] = \"#######################################################################\";\n#define _cp(s) (strcmp(str, s) == 0)\n\nstatic char *\nconvert_bitfield(bf_spec_t *bf)\n{\n    uint32_t mask;\n    char *p;\n    uint32_t i;\n    static char rnd_bitmask[33];\n\n    memset(rnd_bitmask, 0, sizeof(rnd_bitmask));\n    memset(rnd_bitmask, '.', sizeof(rnd_bitmask) - 1);\n\n    p = rnd_bitmask;\n    for (i = 0; i < MAX_BITFIELD_SIZE; i++) {\n        mask = (uint32_t)(1 << (MAX_BITFIELD_SIZE - i - 1));\n\n        /* Need to check rndMask before andMask: for random bits, the\n         * andMask is also 0. */\n        p[i] = ((ntohl(bf->rndMask) & mask) != 0)   ? 'X'\n               : ((ntohl(bf->andMask) & mask) == 0) ? '0'\n               : ((ntohl(bf->orMask) & mask) != 0)  ? '1'\n                                                    : '.';\n    }\n\n    return rnd_bitmask;\n}\n\n/**\n * pktgen_script_save - Save a configuration as a startup script\n *\n * DESCRIPTION\n * Save a configuration as a startup script\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic int\npktgen_script_save(char *path)\n{\n    port_info_t *pinfo;\n    pkt_seq_t *pkt;\n    range_info_t *range;\n    latency_t *lat;\n    uint32_t flags;\n    char buff[64];\n    FILE *fd;\n    int i, j;\n    uint64_t lcore;\n    struct rte_ether_addr eaddr;\n    char buf[128];\n\n    fd = fopen(path, \"w\");\n    if (fd == NULL)\n        return -1;\n\n    for (i = 0, lcore = 0; i < RTE_MAX_LCORE; i++)\n        if (rte_lcore_is_enabled(i))\n            lcore |= (1 << i);\n\n    fprintf(fd, \"#\\n# %s\\n\", pktgen_version());\n    fprintf(fd, \"# %s, %s %s\\n\\n\", copyright_msg(), powered_by(), rte_version());\n\n    fprintf(fd, \"# Command line arguments: (DPDK args are defaults)\\n\");\n    fprintf(fd, \"# %s -c %\" PRIx64 \" -n 3 -m 512 --proc-type %s -- \", pktgen.argv[0], lcore,\n            (rte_eal_process_type() == RTE_PROC_PRIMARY) ? \"primary\" : \"secondary\");\n    for (i = 1; i < pktgen.argc; i++)\n        fprintf(fd, \"%s \", pktgen.argv[i]);\n    fprintf(fd, \"\\n\\n%s\\n\", hash_line);\n\n    fprintf(fd, \"# Pktgen Configuration script information:\\n\");\n    fprintf(fd, \"#   Flags %08x\\n\", pktgen.flags);\n    fprintf(fd, \"#   Number of ports: %d\\n\", pktgen.nb_ports);\n    fprintf(fd, \"#   Number ports per page: %d\\n\", pktgen.nb_ports_per_page);\n    fprintf(fd, \"#   Number descriptors: RX %d TX: %d\\n\", pktgen.nb_rxd, pktgen.nb_txd);\n    fprintf(fd, \"#   Promiscuous mode is %s\\n\\n\",\n            (pktgen.flags & PROMISCUOUS_ON_FLAG) ? \"Enabled\" : \"Disabled\");\n\n    fprintf(fd, \"\\n# Global configuration:\\n\");\n    uint16_t rows, cols;\n    pktgen_display_get_geometry(&rows, &cols);\n    fprintf(fd, \"#   geometry %dx%d\\n\", cols, rows);\n    fprintf(fd, \"%s mac_from_arp\\n\\n\", (pktgen.flags & MAC_FROM_ARP_FLAG) ? \"enable\" : \"disable\");\n\n    for (i = 0; i < pktgen.nb_ports; i++) {\n        pinfo = l2p_get_port_pinfo(i);\n        pkt   = &pinfo->seq_pkt[SINGLE_PKT];\n        range = &pinfo->range;\n\n        if (pinfo->tx_burst == 0)\n            continue;\n\n        lat = &pinfo->latency;\n\n        fprintf(fd, \"######################### Port %2d ##################################\\n\", i);\n        if (rte_atomic64_read(&pinfo->transmit_count) == 0)\n            strcpy(buff, \"Forever\");\n        else\n            snprintf(buff, sizeof(buff), \"%\" PRIu64, rte_atomic64_read(&pinfo->transmit_count));\n        fprintf(fd, \"#\\n\");\n        flags = rte_atomic64_read(&pinfo->port_flags);\n        fprintf(fd, \"# Port: %2d, Burst (Rx/Tx):%3d/%3d, Rate:%g%%, Flags:%16x, TX Count:%s\\n\",\n                pinfo->pid, pinfo->rx_burst, pinfo->tx_burst, pinfo->tx_rate, flags, buff);\n        fprintf(fd, \"#           Sequence count:%d, Prime:%d VLAN ID:%04x, \", pinfo->seqCnt,\n                pinfo->prime_cnt, pinfo->vlanid);\n        pktgen_link_state(pinfo->pid, buff, sizeof(buff));\n        fprintf(fd, \"Link: %s\\n\", buff);\n\n        fprintf(fd, \"#\\n# Set up the primary port information:\\n\");\n        fprintf(fd, \"set %d count %\" PRIu64 \"\\n\", pinfo->pid,\n                rte_atomic64_read(&pinfo->transmit_count));\n        fprintf(fd, \"set %d size %d\\n\", pinfo->pid, pkt->pkt_size + RTE_ETHER_CRC_LEN);\n        fprintf(fd, \"set %d rate %g\\n\", pinfo->pid, pinfo->tx_rate);\n        fprintf(fd, \"set %d rxburst %d\\n\", pinfo->pid, pinfo->rx_burst);\n        fprintf(fd, \"set %d txburst %d\\n\", pinfo->pid, pinfo->tx_burst);\n        fprintf(fd, \"set %d sport %d\\n\", pinfo->pid, pkt->sport);\n        fprintf(fd, \"set %d dport %d\\n\", pinfo->pid, pkt->dport);\n        fprintf(fd, \"set %d prime %d\\n\", pinfo->pid, pinfo->prime_cnt);\n        fprintf(fd, \"set %d type %s\\n\", i,\n                (pkt->ethType == RTE_ETHER_TYPE_IPV4)   ? \"ipv4\"\n                : (pkt->ethType == RTE_ETHER_TYPE_IPV6) ? \"ipv6\"\n                : (pkt->ethType == RTE_ETHER_TYPE_VLAN) ? \"vlan\"\n                : (pkt->ethType == RTE_ETHER_TYPE_ARP)  ? \"arp\"\n                                                        : \"unknown\");\n        fprintf(fd, \"set %d proto %s\\n\", i,\n                (pkt->ipProto == PG_IPPROTO_TCP)    ? \"tcp\"\n                : (pkt->ipProto == PG_IPPROTO_ICMP) ? \"icmp\"\n                                                    : \"udp\");\n        fprintf(fd, \"set %d dst ip %s\\n\", i,\n                (pkt->ethType == RTE_ETHER_TYPE_IPV6)\n                    ? inet_ntop6(buff, sizeof(buff), pkt->ip_dst_addr.addr.ipv6.a, PG_PREFIXMAX)\n                    : inet_ntop4(buff, sizeof(buff), ntohl(pkt->ip_dst_addr.addr.ipv4.s_addr),\n                                 0xFFFFFFFF));\n        fprintf(fd, \"set %d src ip %s\\n\", i,\n                (pkt->ethType == RTE_ETHER_TYPE_IPV6)\n                    ? inet_ntop6(buff, sizeof(buff), pkt->ip_src_addr.addr.ipv6.a,\n                                 pkt->ip_src_addr.prefixlen)\n                    : inet_ntop4(buff, sizeof(buff), ntohl(pkt->ip_src_addr.addr.ipv4.s_addr),\n                                 pkt->ip_mask));\n\n        tcp_str_from_flags(pkt->tcp_flags, buf, sizeof(buf));\n        fprintf(fd, \"set %d tcp flags %s\\n\", i, buf);\n\n        fprintf(fd, \"set %d tcp seq %u\\n\", i, pkt->tcp_seq);\n        fprintf(fd, \"set %d tcp ack %u\\n\", i, pkt->tcp_ack);\n\n        fprintf(fd, \"set %d dst mac %s\\n\", pinfo->pid,\n                inet_mtoa(buff, sizeof(buff), &pkt->eth_dst_addr));\n        fprintf(fd, \"set %d src mac %s\\n\", pinfo->pid,\n                inet_mtoa(buff, sizeof(buff), &pkt->eth_src_addr));\n        fprintf(fd, \"set %d vlan %d\\n\\n\", i, pkt->vlanid);\n\n        fprintf(fd, \"set %d pattern %s\\n\", i,\n                (pinfo->fill_pattern_type == ABC_FILL_PATTERN)    ? \"abc\"\n                : (pinfo->fill_pattern_type == NO_FILL_PATTERN)   ? \"none\"\n                : (pinfo->fill_pattern_type == ZERO_FILL_PATTERN) ? \"zero\"\n                                                                  : \"user\");\n        if ((pinfo->fill_pattern_type == USER_FILL_PATTERN) && strlen(pinfo->user_pattern)) {\n            char buff[64];\n            memset(buff, 0, sizeof(buff));\n            snprintf(buff, sizeof(buff), \"%s\", pinfo->user_pattern);\n            fprintf(fd, \"set %d user pattern %s\\n\", i, buff);\n        }\n        fprintf(fd, \"\\n\");\n\n        fprintf(fd, \"set %d jitter %\" PRIu64 \"\\n\", i, lat->jitter_threshold_us);\n        fprintf(fd, \"%sable %d mpls\\n\", (flags & SEND_MPLS_LABEL) ? \"en\" : \"dis\", i);\n        sprintf(buff, \"0x%x\", pkt->mpls_entry);\n        fprintf(fd, \"range %d mpls entry %s\\n\", i, buff);\n\n        fprintf(fd, \"%sable %d qinq\\n\", (flags & SEND_Q_IN_Q_IDS) ? \"en\" : \"dis\", i);\n        fprintf(fd, \"set %d qinqids %d %d\\n\", i, pkt->qinq_outerid, pkt->qinq_innerid);\n\n        fprintf(fd, \"%sable %d gre\\n\", (flags & SEND_GRE_IPv4_HEADER) ? \"en\" : \"dis\", i);\n        fprintf(fd, \"%sable %d gre_eth\\n\", (flags & SEND_GRE_ETHER_HEADER) ? \"en\" : \"dis\", i);\n\n        fprintf(fd, \"%sable %d vxlan\\n\", (flags & SEND_VXLAN_PACKETS) ? \"en\" : \"dis\", i);\n        fprintf(fd, \"set %d vxlan 0x%x %d %d\\n\", i, pkt->vni_flags, pkt->group_id, pkt->vxlan_id);\n\n        fprintf(fd, \"#\\n# Port flag values:\\n\");\n        fprintf(fd, \"%sable %d icmp\\n\", (flags & ICMP_ECHO_ENABLE_FLAG) ? \"en\" : \"dis\", i);\n        fprintf(fd, \"%sable %d pcap\\n\", (flags & SEND_PCAP_PKTS) ? \"en\" : \"dis\", i);\n        fprintf(fd, \"%sable %d range\\n\", (flags & SEND_RANGE_PKTS) ? \"en\" : \"dis\", i);\n        fprintf(fd, \"%sable %d latency\\n\", (flags & SEND_LATENCY_PKTS) ? \"en\" : \"dis\", i);\n        fprintf(fd, \"%sable %d process\\n\", (flags & PROCESS_INPUT_PKTS) ? \"en\" : \"dis\", i);\n        fprintf(fd, \"%sable %d capture\\n\", (flags & CAPTURE_PKTS) ? \"en\" : \"dis\", i);\n        fprintf(fd, \"%sable %d vlan\\n\", (flags & SEND_VLAN_ID) ? \"en\" : \"dis\", i);\n\n        fprintf(fd, \"#\\n# Range packet information:\\n\");\n        fprintf(fd, \"range %d src mac start %s\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->src_mac, &eaddr)));\n        fprintf(fd, \"range %d src mac min %s\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->src_mac_min, &eaddr)));\n        fprintf(fd, \"range %d src mac max %s\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->src_mac_max, &eaddr)));\n        fprintf(fd, \"range %d src mac inc %s\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->src_mac_inc, &eaddr)));\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d dst mac start %s\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->dst_mac, &eaddr)));\n        fprintf(fd, \"range %d dst mac min %s\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->dst_mac_min, &eaddr)));\n        fprintf(fd, \"range %d dst mac max %s\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->dst_mac_max, &eaddr)));\n        fprintf(fd, \"range %d dst mac inc %s\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->dst_mac_inc, &eaddr)));\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d src ip start %s\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->src_ip), 0xFFFFFFFF));\n        fprintf(fd, \"range %d src ip min %s\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->src_ip_min), 0xFFFFFFFF));\n        fprintf(fd, \"range %d src ip max %s\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->src_ip_max), 0xFFFFFFFF));\n        fprintf(fd, \"range %d src ip inc %s\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->src_ip_inc), 0xFFFFFFFF));\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d dst ip start %s\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->dst_ip), 0xFFFFFFFF));\n        fprintf(fd, \"range %d dst ip min %s\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->dst_ip_min), 0xFFFFFFFF));\n        fprintf(fd, \"range %d dst ip max %s\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->dst_ip_max), 0xFFFFFFFF));\n        fprintf(fd, \"range %d dst ip inc %s\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->dst_ip_inc), 0xFFFFFFFF));\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d proto %s\\n\", i,\n                (range->ip_proto == PG_IPPROTO_UDP)    ? \"udp\"\n                : (range->ip_proto == PG_IPPROTO_ICMP) ? \"icmp\"\n                                                       : \"tcp\");\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d src port start %d\\n\", i, range->src_port);\n        fprintf(fd, \"range %d src port min %d\\n\", i, range->src_port_min);\n        fprintf(fd, \"range %d src port max %d\\n\", i, range->src_port_max);\n        fprintf(fd, \"range %d src port inc %d\\n\", i, range->src_port_inc);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d dst port start %d\\n\", i, range->dst_port);\n        fprintf(fd, \"range %d dst port min %d\\n\", i, range->dst_port_min);\n        fprintf(fd, \"range %d dst port max %d\\n\", i, range->dst_port_max);\n        fprintf(fd, \"range %d dst port inc %d\\n\", i, range->dst_port_inc);\n\n        fprintf(fd, \"\\n\");\n        tcp_str_from_flags(range->tcp_flags, buf, sizeof(buf));\n        fprintf(fd, \"range %d tcp flags %s\\n\", i, buf);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d tcp seq start %u\\n\", i, range->tcp_seq);\n        fprintf(fd, \"range %d tcp seq min %u\\n\", i, range->tcp_seq_min);\n        fprintf(fd, \"range %d tcp seq max %u\\n\", i, range->tcp_seq_max);\n        fprintf(fd, \"range %d tcp seq inc %u\\n\", i, range->tcp_seq_inc);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d tcp ack start %u\\n\", i, range->tcp_ack);\n        fprintf(fd, \"range %d tcp ack min %u\\n\", i, range->tcp_ack_min);\n        fprintf(fd, \"range %d tcp ack max %u\\n\", i, range->tcp_ack_max);\n        fprintf(fd, \"range %d tcp ack inc %u\\n\", i, range->tcp_ack_inc);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d ttl start %d\\n\", i, range->ttl);\n        fprintf(fd, \"range %d ttl min %d\\n\", i, range->ttl_min);\n        fprintf(fd, \"range %d ttl max %d\\n\", i, range->ttl_max);\n        fprintf(fd, \"range %d ttl inc %d\\n\", i, range->ttl_inc);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d vlan start %d\\n\", i, range->vlan_id);\n        fprintf(fd, \"range %d vlan min %d\\n\", i, range->vlan_id_min);\n        fprintf(fd, \"range %d vlan max %d\\n\", i, range->vlan_id_max);\n        fprintf(fd, \"range %d vlan inc %d\\n\", i, range->vlan_id_inc);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d cos start %d\\n\", i, range->cos);\n        fprintf(fd, \"range %d cos min %d\\n\", i, range->cos_min);\n        fprintf(fd, \"range %d cos max %d\\n\", i, range->cos_max);\n        fprintf(fd, \"range %d cos inc %d\\n\", i, range->cos_inc);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d tos start %d\\n\", i, range->tos);\n        fprintf(fd, \"range %d tos min %d\\n\", i, range->tos_min);\n        fprintf(fd, \"range %d tos max %d\\n\", i, range->tos_max);\n        fprintf(fd, \"range %d tos inc %d\\n\", i, range->tos_inc);\n        fprintf(fd, \"range %d gre key %d\\n\", i, pkt->gre_key);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"range %d size start %d\\n\", i, range->pkt_size + RTE_ETHER_CRC_LEN);\n        fprintf(fd, \"range %d size min %d\\n\", i, range->pkt_size_min + RTE_ETHER_CRC_LEN);\n        fprintf(fd, \"range %d size max %d\\n\", i, range->pkt_size_max + RTE_ETHER_CRC_LEN);\n        fprintf(fd, \"range %d size inc %d\\n\\n\", i, range->pkt_size_inc);\n\n        fprintf(fd, \"#\\n# Set up the sequence data for the port.\\n\");\n        fprintf(fd, \"set %d seq_cnt %d\\n\", pinfo->pid, pinfo->seqCnt);\n        for (j = 0; j < pinfo->seqCnt; j++) {\n            pkt = &pinfo->seq_pkt[j];\n            fprintf(fd, \"seq %d %d %s \", j, i, inet_mtoa(buff, sizeof(buff), &pkt->eth_dst_addr));\n            fprintf(fd, \"%s \", inet_mtoa(buff, sizeof(buff), &pkt->eth_src_addr));\n            fprintf(fd, \"%s \",\n                    (pkt->ethType == RTE_ETHER_TYPE_IPV6)\n                        ? inet_ntop6(buff, sizeof(buff), pkt->ip_dst_addr.addr.ipv6.a, PG_PREFIXMAX)\n                        : inet_ntop4(buff, sizeof(buff), htonl(pkt->ip_dst_addr.addr.ipv4.s_addr),\n                                     0xFFFFFFFF));\n            fprintf(fd, \"%s \",\n                    (pkt->ethType == RTE_ETHER_TYPE_IPV6)\n                        ? inet_ntop6(buff, sizeof(buff), pkt->ip_src_addr.addr.ipv6.a,\n                                     pkt->ip_src_addr.prefixlen)\n                        : inet_ntop4(buff, sizeof(buff), htonl(pkt->ip_src_addr.addr.ipv4.s_addr),\n                                     pkt->ip_mask));\n            fprintf(fd, \"%d %d %s %s %d %d %d\\n\", pkt->sport, pkt->dport,\n                    (pkt->ethType == RTE_ETHER_TYPE_IPV4)   ? \"ipv4\"\n                    : (pkt->ethType == RTE_ETHER_TYPE_IPV6) ? \"ipv6\"\n                    : (pkt->ethType == RTE_ETHER_TYPE_VLAN) ? \"vlan\"\n                                                            : \"Other\",\n                    (pkt->ipProto == PG_IPPROTO_TCP)    ? \"tcp\"\n                    : (pkt->ipProto == PG_IPPROTO_ICMP) ? \"icmp\"\n                                                        : \"udp\",\n                    pkt->vlanid, pkt->pkt_size + RTE_ETHER_CRC_LEN, pkt->gtpu_teid);\n        }\n\n        pcap_info_t *pcap = l2p_get_pcap(pinfo->pid);\n        if (pcap) {\n            fprintf(fd, \"#\\n# PCAP port %d\\n\", i);\n            fprintf(fd, \"#    Packet count: %d, max size: %d\\n\", pcap->pkt_count,\n                    pcap->max_pkt_size);\n            fprintf(fd, \"#    Filename    : %s\\n\", pcap->filename);\n        }\n        fprintf(fd, \"\\n\");\n\n        if (pinfo->rnd_bitfields && pinfo->rnd_bitfields->active_specs) {\n            uint32_t active = pinfo->rnd_bitfields->active_specs;\n            bf_spec_t *bf;\n            fprintf(fd, \"\\n-- Rnd bitfields\\n\");\n            for (j = 0; j < MAX_RND_BITFIELDS; j++) {\n                if ((active & (1 << j)) == 0)\n                    continue;\n                bf = &pinfo->rnd_bitfields->specs[j];\n                fprintf(fd, \"set %d rnd %d %d %s\\n\", i, j, bf->offset, convert_bitfield(bf));\n            }\n            fprintf(fd, \"\\n\");\n        }\n    }\n    fprintf(fd, \"################################ Done #################################\\n\");\n\n    fchmod(fileno(fd), 0666);\n    fclose(fd);\n    return 0;\n}\n\n/**\n *\n * pktgen_lua_save - Save a configuration as a Lua script\n *\n * DESCRIPTION\n * Save a configuration as a Lua script\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic int\npktgen_lua_save(char *path)\n{\n    port_info_t *pinfo;\n    pkt_seq_t *pkt;\n    range_info_t *range;\n    latency_t *lat;\n    uint32_t flags;\n    char buff[64];\n    FILE *fd;\n    int i, j;\n    uint64_t lcore;\n    struct rte_ether_addr eaddr;\n\n    fd = fopen(path, \"w\");\n    if (fd == NULL)\n        return -1;\n\n    for (i = 0, lcore = 0; i < RTE_MAX_LCORE; i++)\n        if (rte_lcore_is_enabled(i))\n            lcore |= (1 << i);\n\n    fprintf(fd, \"--\\n-- %s\\n\", pktgen_version());\n    fprintf(fd, \"-- %s, %s %s\\n\\n\", copyright_msg(), powered_by(), rte_version());\n\n    fprintf(fd, \"package.path = package.path ..\\\";?.lua;test/?.lua;app/?.lua;\\\"\\n\");\n    fprintf(fd, \"require \\\"Pktgen\\\"\\n\\n\");\n\n    fprintf(fd, \"-- Command line arguments: (DPDK args are defaults)\\n\");\n    fprintf(fd, \"-- %s -c %\" PRIx64 \" -n 3 -m 512 --proc-type %s -- \", pktgen.argv[0], lcore,\n            (rte_eal_process_type() == RTE_PROC_PRIMARY) ? \"primary\" : \"secondary\");\n    for (i = 1; i < pktgen.argc; i++)\n        fprintf(fd, \"%s \", pktgen.argv[i]);\n    fprintf(fd, \"\\n\\n-- %s\\n\", hash_line);\n\n    fprintf(fd, \"-- Pktgen Configuration script information:\\n\");\n    fprintf(fd, \"--   Flags %08x\\n\", pktgen.flags);\n    fprintf(fd, \"--   Number of ports: %d\\n\", pktgen.nb_ports);\n    fprintf(fd, \"--   Number ports per page: %d\\n\", pktgen.nb_ports_per_page);\n    fprintf(fd, \"--   Number descriptors: RX %d TX: %d\\n\", pktgen.nb_rxd, pktgen.nb_txd);\n    fprintf(fd, \"--   Promiscuous mode is %s\\n\\n\",\n            (pktgen.flags & PROMISCUOUS_ON_FLAG) ? \"Enabled\" : \"Disabled\");\n\n    fprintf(fd, \"\\n--%s\\n\", hash_line);\n\n    fprintf(fd, \"-- Global configuration:\\n\");\n    uint16_t rows, cols;\n    pktgen_display_get_geometry(&rows, &cols);\n    fprintf(fd, \"--   geometry %dx%d\\n\", cols, rows);\n    fprintf(fd, \"pktgen.mac_from_arp(\\\"%s\\\");\\n\\n\",\n            (pktgen.flags & MAC_FROM_ARP_FLAG) ? \"enable\" : \"disable\");\n\n    for (i = 0; i < pktgen.nb_ports; i++) {\n        pinfo = l2p_get_port_pinfo(i);\n        pkt   = &pinfo->seq_pkt[SINGLE_PKT];\n        range = &pinfo->range;\n\n        if (pinfo->tx_burst == 0)\n            continue;\n\n        lat = &pinfo->latency;\n\n        fprintf(fd, \"-- ######################### Port %2d ##################################\\n\",\n                i);\n        if (rte_atomic64_read(&pinfo->transmit_count) == 0)\n            strcpy(buff, \"Forever\");\n        else\n            snprintf(buff, sizeof(buff), \"%\" PRIu64, rte_atomic64_read(&pinfo->transmit_count));\n        fprintf(fd, \"-- \\n\");\n        flags = rte_atomic64_read(&pinfo->port_flags);\n        fprintf(fd, \"-- Port: %2d, Burst (Rx/Tx):%3d/%3d, Rate:%g%%, Flags:%16x, TX Count:%s\\n\",\n                pinfo->pid, pinfo->rx_burst, pinfo->tx_burst, pinfo->tx_rate, flags, buff);\n        fprintf(fd, \"--           Sequence Count:%d, Prime:%d VLAN ID:%04x, \", pinfo->seqCnt,\n                pinfo->prime_cnt, pinfo->vlanid);\n        pktgen_link_state(pinfo->pid, buff, sizeof(buff));\n        fprintf(fd, \"Link: %s\\n\", buff);\n\n        fprintf(fd, \"--\\n-- Set up the primary port information:\\n\");\n        fprintf(fd, \"pktgen.set('%d', 'count', %\" PRIu64 \");\\n\", pinfo->pid,\n                rte_atomic64_read(&pinfo->transmit_count));\n        fprintf(fd, \"pktgen.set('%d', 'size', %d);\\n\", pinfo->pid,\n                pkt->pkt_size + RTE_ETHER_CRC_LEN);\n        fprintf(fd, \"pktgen.set('%d', 'rate', %g);\\n\", pinfo->pid, pinfo->tx_rate);\n        fprintf(fd, \"pktgen.set('%d', 'txburst', %d);\\n\", pinfo->pid, pinfo->tx_burst);\n        fprintf(fd, \"pktgen.set('%d', 'rxburst', %d);\\n\", pinfo->pid, pinfo->rx_burst);\n        fprintf(fd, \"pktgen.set('%d', 'sport', %d);\\n\", pinfo->pid, pkt->sport);\n        fprintf(fd, \"pktgen.set('%d', 'dport', %d);\\n\", pinfo->pid, pkt->dport);\n        fprintf(fd, \"pktgen.set('%d', 'prime', %d);\\n\", pinfo->pid, pinfo->prime_cnt);\n        fprintf(fd, \"pktgen.set_type('%d', '%s');\\n\", i,\n                (pkt->ethType == RTE_ETHER_TYPE_IPV4)   ? \"ipv4\"\n                : (pkt->ethType == RTE_ETHER_TYPE_IPV6) ? \"ipv6\"\n                : (pkt->ethType == RTE_ETHER_TYPE_VLAN) ? \"vlan\"\n                : (pkt->ethType == RTE_ETHER_TYPE_ARP)  ? \"arp\"\n                                                        : \"unknown\");\n        fprintf(fd, \"pktgen.set_proto('%d', '%s');\\n\", i,\n                (pkt->ipProto == PG_IPPROTO_TCP)    ? \"tcp\"\n                : (pkt->ipProto == PG_IPPROTO_ICMP) ? \"icmp\"\n                                                    : \"udp\");\n        fprintf(fd, \"pktgen.set_ipaddr('%d', 'dst', '%s');\\n\", i,\n                (pkt->ethType == RTE_ETHER_TYPE_IPV6)\n                    ? inet_ntop6(buff, sizeof(buff), pkt->ip_dst_addr.addr.ipv6.a, PG_PREFIXMAX)\n                    : inet_ntop4(buff, sizeof(buff), ntohl(pkt->ip_dst_addr.addr.ipv4.s_addr),\n                                 0xFFFFFFFF));\n        fprintf(fd, \"pktgen.set_ipaddr('%d', 'src','%s');\\n\", i,\n                (pkt->ethType == RTE_ETHER_TYPE_IPV6)\n                    ? inet_ntop6(buff, sizeof(buff), pkt->ip_src_addr.addr.ipv6.a,\n                                 pkt->ip_src_addr.prefixlen)\n                    : inet_ntop4(buff, sizeof(buff), ntohl(pkt->ip_src_addr.addr.ipv4.s_addr),\n                                 pkt->ip_mask));\n        fprintf(fd, \"pktgen.set_mac('%d', 'dst', '%s');\\n\", pinfo->pid,\n                inet_mtoa(buff, sizeof(buff), &pkt->eth_dst_addr));\n        fprintf(fd, \"pktgen.set_mac('%d', 'src', '%s');\\n\", pinfo->pid,\n                inet_mtoa(buff, sizeof(buff), &pkt->eth_src_addr));\n        fprintf(fd, \"pktgen.vlanid('%d', %d);\\n\\n\", i, pkt->vlanid);\n\n        fprintf(fd, \"pktgen.pattern('%d', '%s');\\n\", i,\n                (pinfo->fill_pattern_type == ABC_FILL_PATTERN)    ? \"abc\"\n                : (pinfo->fill_pattern_type == NO_FILL_PATTERN)   ? \"none\"\n                : (pinfo->fill_pattern_type == ZERO_FILL_PATTERN) ? \"zero\"\n                                                                  : \"user\");\n        if ((pinfo->fill_pattern_type == USER_FILL_PATTERN) && strlen(pinfo->user_pattern)) {\n            char buff[64];\n            memset(buff, 0, sizeof(buff));\n            snprintf(buff, sizeof(buff), \"%s\", pinfo->user_pattern);\n            fprintf(fd, \"pktgen.userPattern('%d', '%s');\\n\", i, buff);\n        }\n        fprintf(fd, \"\\n\");\n\n        fflush(fd);\n        fprintf(fd, \"pktgen.jitter('%d', %lu);\\n\", i, lat->jitter_threshold_us);\n        fprintf(fd, \"pktgen.mpls('%d', '%sable');\\n\", i, (flags & SEND_MPLS_LABEL) ? \"en\" : \"dis\");\n        sprintf(buff, \"0x%x\", pkt->mpls_entry);\n        fprintf(fd, \"pktgen.range.mpls_entry('%d', '%s');\\n\", i, buff);\n\n        fprintf(fd, \"pktgen.qinq('%d', '%sable');\\n\", i, (flags & SEND_Q_IN_Q_IDS) ? \"en\" : \"dis\");\n        fprintf(fd, \"pktgen.range.qinqids('%d', %d, %d);\\n\", i, pkt->qinq_outerid,\n                pkt->qinq_innerid);\n\n        fprintf(fd, \"pktgen.gre('%d', '%sable');\\n\", i,\n                (flags & SEND_GRE_IPv4_HEADER) ? \"en\" : \"dis\");\n        fprintf(fd, \"pktgen.gre_eth('%d', '%sable');\\n\", i,\n                (flags & SEND_GRE_ETHER_HEADER) ? \"en\" : \"dis\");\n        fprintf(fd, \"pktgen.range.gre_key('%d', %d);\\n\", i, pkt->gre_key);\n\n        fprintf(fd, \"pktgen.vxlan('%d', '%sable');\\n\", i,\n                (flags & SEND_VXLAN_PACKETS) ? \"en\" : \"dis\");\n        fprintf(fd, \"pktgen.vxlan_id('%d', '0x%x', '%d', '%d');\\n\", i, pkt->vni_flags,\n                pkt->group_id, pkt->vxlan_id);\n\n        fprintf(fd, \"--\\n-- Port flag values:\\n\");\n        fprintf(fd, \"pktgen.icmp_echo('%d', '%sable');\\n\", i,\n                (flags & ICMP_ECHO_ENABLE_FLAG) ? \"en\" : \"dis\");\n        fprintf(fd, \"pktgen.pcap('%d', '%sable');\\n\", i, (flags & SEND_PCAP_PKTS) ? \"en\" : \"dis\");\n        fprintf(fd, \"pktgen.set_range('%d', '%sable');\\n\", i,\n                (flags & SEND_RANGE_PKTS) ? \"en\" : \"dis\");\n        fprintf(fd, \"pktgen.latency('%d', '%sable');\\n\", i,\n                (flags & SEND_LATENCY_PKTS) ? \"en\" : \"dis\");\n        fprintf(fd, \"pktgen.process('%d', '%sable');\\n\", i,\n                (flags & PROCESS_INPUT_PKTS) ? \"en\" : \"dis\");\n        fprintf(fd, \"pktgen.capture('%d', '%sable');\\n\", i, (flags & CAPTURE_PKTS) ? \"en\" : \"dis\");\n        fprintf(fd, \"pktgen.vlan('%d', '%sable');\\n\\n\", i, (flags & SEND_VLAN_ID) ? \"en\" : \"dis\");\n        fflush(fd);\n        fprintf(fd, \"--\\n-- Range packet information:\\n\");\n        fprintf(fd, \"pktgen.range.src_mac('%d', 'start', '%s');\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->src_mac, &eaddr)));\n        fprintf(fd, \"pktgen.range.src_mac('%d', 'min', '%s');\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->src_mac_min, &eaddr)));\n        fprintf(fd, \"pktgen.range.src_mac('%d', 'max', '%s');\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->src_mac_max, &eaddr)));\n        fprintf(fd, \"pktgen.range.src_mac('%d', 'inc', '%s');\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->src_mac_inc, &eaddr)));\n\n        fprintf(fd, \"pktgen.range.dst_mac('%d', 'start', '%s');\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->dst_mac, &eaddr)));\n        fprintf(fd, \"pktgen.range.dst_mac('%d', 'min', '%s');\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->dst_mac_min, &eaddr)));\n        fprintf(fd, \"pktgen.range.dst_mac('%d', 'max', '%s');\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->dst_mac_max, &eaddr)));\n        fprintf(fd, \"pktgen.range.dst_mac('%d', 'inc', '%s');\\n\", i,\n                inet_mtoa(buff, sizeof(buff), inet_h64tom(range->dst_mac_inc, &eaddr)));\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"pktgen.range.src_ip('%d', 'start', '%s');\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->src_ip), 0xFFFFFFFF));\n        fprintf(fd, \"pktgen.range.src_ip('%d', 'min', '%s');\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->src_ip_min), 0xFFFFFFFF));\n        fprintf(fd, \"pktgen.range.src_ip('%d', 'max', '%s');\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->src_ip_max), 0xFFFFFFFF));\n        fprintf(fd, \"pktgen.range.src_ip('%d', 'inc', '%s');\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->src_ip_inc), 0xFFFFFFFF));\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"pktgen.range.dst_ip('%d', 'start', '%s');\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->dst_ip), 0xFFFFFFFF));\n        fprintf(fd, \"pktgen.range.dst_ip('%d', 'min', '%s');\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->dst_ip_min), 0xFFFFFFFF));\n        fprintf(fd, \"pktgen.range.dst_ip('%d', 'max', '%s');\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->dst_ip_max), 0xFFFFFFFF));\n        fprintf(fd, \"pktgen.range.dst_ip('%d', 'inc', '%s');\\n\", i,\n                inet_ntop4(buff, sizeof(buff), ntohl(range->dst_ip_inc), 0xFFFFFFFF));\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"pktgen.range.ip_proto('%d', '%s');\\n\", i,\n                (range->ip_proto == PG_IPPROTO_UDP)    ? \"udp\"\n                : (range->ip_proto == PG_IPPROTO_ICMP) ? \"icmp\"\n                                                       : \"tcp\");\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"pktgen.range.src_port('%d', 'start', %d);\\n\", i, range->src_port);\n        fprintf(fd, \"pktgen.range.src_port('%d', 'min', %d);\\n\", i, range->src_port_min);\n        fprintf(fd, \"pktgen.range.src_port('%d', 'max', %d);\\n\", i, range->src_port_max);\n        fprintf(fd, \"pktgen.range.src_port('%d', 'inc', %d);\\n\", i, range->src_port_inc);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"pktgen.range.dst_port('%d', 'start', %d);\\n\", i, range->dst_port);\n        fprintf(fd, \"pktgen.range.dst_port('%d', 'min', %d);\\n\", i, range->dst_port_min);\n        fprintf(fd, \"pktgen.range.dst_port('%d', 'max', %d);\\n\", i, range->dst_port_max);\n        fprintf(fd, \"pktgen.range.dst_port('%d', 'inc', %d);\\n\", i, range->dst_port_inc);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"pktgen.range.ttl('%d', 'start', %d);\\n\", i, range->ttl);\n        fprintf(fd, \"pktgen.range.ttl('%d', 'min', %d);\\n\", i, range->ttl_min);\n        fprintf(fd, \"pktgen.range.ttl('%d', 'max', %d);\\n\", i, range->ttl_max);\n        fprintf(fd, \"pktgen.range.ttl('%d', 'inc', %d);\\n\", i, range->ttl_inc);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"pktgen.range.vlan_id('%d', 'start', %d);\\n\", i, range->vlan_id);\n        fprintf(fd, \"pktgen.range.vlan_id('%d', 'min', %d);\\n\", i, range->vlan_id_min);\n        fprintf(fd, \"pktgen.range.vlan_id('%d', 'max', %d);\\n\", i, range->vlan_id_max);\n        fprintf(fd, \"pktgen.range.vlan_id('%d', 'inc', %d);\\n\", i, range->vlan_id_inc);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"pktgen.range.cos('%d', 'start', %d);\\n\", i, range->cos);\n        fprintf(fd, \"pktgen.range.cos('%d', 'min', %d);\\n\", i, range->cos_min);\n        fprintf(fd, \"pktgen.range.cos('%d', 'max', %d);\\n\", i, range->cos_max);\n        fprintf(fd, \"pktgen.range.cos('%d', 'inc', %d);\\n\", i, range->cos_inc);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"pktgen.range.tos('%d', 'start', %d);\\n\", i, range->tos);\n        fprintf(fd, \"pktgen.range.tos('%d', 'min', %d);\\n\", i, range->tos_min);\n        fprintf(fd, \"pktgen.range.tos('%d', 'max', %d);\\n\", i, range->tos_max);\n        fprintf(fd, \"pktgen.range.tos('%d', 'inc', %d);\\n\", i, range->tos_inc);\n\n        fprintf(fd, \"\\n\");\n        fprintf(fd, \"pktgen.range.pkt_size('%d', 'start', %d);\\n\", i,\n                range->pkt_size + RTE_ETHER_CRC_LEN);\n        fprintf(fd, \"pktgen.range.pkt_size('%d', 'min', %d);\\n\", i,\n                range->pkt_size_min + RTE_ETHER_CRC_LEN);\n        fprintf(fd, \"pktgen.range.pkt_size('%d', 'max', %d);\\n\", i,\n                range->pkt_size_max + RTE_ETHER_CRC_LEN);\n        fprintf(fd, \"pktgen.range.pkt_size('%d', 'inc', %d);\\n\\n\", i, range->pkt_size_inc);\n\n        fprintf(fd, \"--\\n-- Set up the sequence data for the port.\\n\");\n        fprintf(fd, \"pktgen.set('%d', 'seq_cnt', %d);\\n\\n\", pinfo->pid, pinfo->seqCnt);\n        fflush(fd);\n        if (pinfo->seqCnt) {\n            fprintf(fd, \"-- (seqnum, port, dst_mac, src_mac, ip_dst, ip_src, sport, dport, \"\n                        \"ethType, proto, vlanid, pkt_size, gtpu_teid)\\n\");\n            for (j = 0; j < pinfo->seqCnt; j++) {\n                pkt = &pinfo->seq_pkt[j];\n                fprintf(fd, \"-- pktgen.seq(%d, '%d', '%s' \", j, i,\n                        inet_mtoa(buff, sizeof(buff), &pkt->eth_dst_addr));\n                fprintf(fd, \"'%s', \", inet_mtoa(buff, sizeof(buff), &pkt->eth_src_addr));\n                fprintf(\n                    fd, \"'%s', \",\n                    (pkt->ethType == RTE_ETHER_TYPE_IPV6)\n                        ? inet_ntop6(buff, sizeof(buff), pkt->ip_dst_addr.addr.ipv6.a, PG_PREFIXMAX)\n                        : inet_ntop4(buff, sizeof(buff), htonl(pkt->ip_dst_addr.addr.ipv4.s_addr),\n                                     0xFFFFFFFF));\n                fprintf(fd, \"'%s', \",\n                        (pkt->ethType == RTE_ETHER_TYPE_IPV6)\n                            ? inet_ntop6(buff, sizeof(buff), pkt->ip_src_addr.addr.ipv6.a,\n                                         pkt->ip_src_addr.prefixlen)\n                            : inet_ntop4(buff, sizeof(buff),\n                                         htonl(pkt->ip_src_addr.addr.ipv4.s_addr), pkt->ip_mask));\n                fprintf(fd, \"%d, %d, '%s', '%s', %d, %d, %d);\\n\", pkt->sport, pkt->dport,\n                        (pkt->ethType == RTE_ETHER_TYPE_IPV4)   ? \"ipv4\"\n                        : (pkt->ethType == RTE_ETHER_TYPE_IPV6) ? \"ipv6\"\n                        : (pkt->ethType == RTE_ETHER_TYPE_VLAN) ? \"vlan\"\n                                                                : \"Other\",\n                        (pkt->ipProto == PG_IPPROTO_TCP)    ? \"tcp\"\n                        : (pkt->ipProto == PG_IPPROTO_ICMP) ? \"icmp\"\n                                                            : \"udp\",\n                        pkt->vlanid, pkt->pkt_size + RTE_ETHER_CRC_LEN, pkt->gtpu_teid);\n            }\n            fflush(fd);\n            fprintf(fd, \"local seq_table = {}\\n\");\n            for (j = 0; j < pinfo->seqCnt; j++) {\n                pkt = &pinfo->seq_pkt[j];\n                fprintf(fd, \"seq_table[%d] = {\\n\", j);\n                fprintf(fd, \"  ['eth_dst_addr'] = '%s',\\n\",\n                        inet_mtoa(buff, sizeof(buff), &pkt->eth_dst_addr));\n                fprintf(fd, \"  ['eth_src_addr'] = '%s',\\n\",\n                        inet_mtoa(buff, sizeof(buff), &pkt->eth_src_addr));\n                fprintf(\n                    fd, \"  ['ip_dst_addr'] = '%s',\\n\",\n                    (pkt->ethType == RTE_ETHER_TYPE_IPV6)\n                        ? inet_ntop6(buff, sizeof(buff), pkt->ip_dst_addr.addr.ipv6.a, PG_PREFIXMAX)\n                        : inet_ntop4(buff, sizeof(buff), htonl(pkt->ip_dst_addr.addr.ipv4.s_addr),\n                                     0xFFFFFFFF));\n                fprintf(fd, \"  ['ip_src_addr'] = '%s',\\n\",\n                        (pkt->ethType == RTE_ETHER_TYPE_IPV6)\n                            ? inet_ntop6(buff, sizeof(buff), pkt->ip_src_addr.addr.ipv6.a,\n                                         pkt->ip_src_addr.prefixlen)\n                            : inet_ntop4(buff, sizeof(buff),\n                                         htonl(pkt->ip_src_addr.addr.ipv4.s_addr), 0xFFFFFFFF));\n                fprintf(fd, \"  ['sport'] = %d,\\n\", pkt->sport);\n                fprintf(fd, \"  ['dport'] = %d,\\n\", pkt->dport);\n                fprintf(fd, \"  ['ethType'] = '%s',\\n\",\n                        (pkt->ethType == RTE_ETHER_TYPE_IPV4)   ? \"ipv4\"\n                        : (pkt->ethType == RTE_ETHER_TYPE_IPV6) ? \"ipv6\"\n                        : (pkt->ethType == RTE_ETHER_TYPE_VLAN) ? \"vlan\"\n                                                                : \"Other\");\n                fprintf(fd, \"  ['ipProto'] = '%s',\\n\",\n                        (pkt->ipProto == PG_IPPROTO_TCP)    ? \"tcp\"\n                        : (pkt->ipProto == PG_IPPROTO_ICMP) ? \"icmp\"\n                                                            : \"udp\");\n                fprintf(fd, \"  ['vlanid'] = %d,\\n\", pkt->vlanid);\n                fprintf(fd, \"  ['pktSize'] = %d,\\n\", pkt->pkt_size);\n                fprintf(fd, \"  ['gtpu_teid'] = %d\\n\", pkt->gtpu_teid);\n                fprintf(fd, \"}\\n\");\n            }\n            fflush(fd);\n            for (j = 0; j < pinfo->seqCnt; j++)\n                fprintf(fd, \"pktgen.seqTable(%d, '%d', seq_table[%d]);\\n\", j, i, j);\n        }\n        fflush(fd);\n        pcap_info_t *pcap = l2p_get_pcap(pinfo->pid);\n        if (pcap) {\n            fprintf(fd, \"--\\n-- PCAP port %d\\n\", i);\n            fprintf(fd, \"--    Packet count: %d, max size: %d\\n\", pcap->pkt_count,\n                    pcap->max_pkt_size);\n            fprintf(fd, \"--    Filename    : %s\\n\", pcap->filename);\n        }\n        fprintf(fd, \"\\n\");\n        fflush(fd);\n        if (pinfo->rnd_bitfields && pinfo->rnd_bitfields->active_specs) {\n            uint32_t active = pinfo->rnd_bitfields->active_specs;\n            bf_spec_t *bf;\n            fprintf(fd, \"\\n-- Rnd bitfields\\n\");\n            fflush(fd);\n            for (j = 0; j < MAX_RND_BITFIELDS; j++) {\n                if ((active & (1 << j)) == 0)\n                    continue;\n                bf = &pinfo->rnd_bitfields->specs[j];\n                fprintf(fd, \"pktgen.rnd('%d', %d, %d, '%s');\\n\", i, j, bf->offset,\n                        convert_bitfield(bf));\n            }\n            fprintf(fd, \"\\n\");\n        }\n    }\n    fprintf(fd, \"pktgen.screen('on');\\n\");\n    fprintf(fd, \"pktgen.cls();\\n\\n\");\n    fprintf(fd, \"-- ################################ Done #################################\\n\");\n    fflush(fd);\n\n    fchmod(fileno(fd), 0666);\n    fclose(fd);\n    return 0;\n}\n\n/**\n *\n * pktgen_save - Save a configuration as a startup script\n *\n * DESCRIPTION\n * Save a configuration as a startup script\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nint\npktgen_save(char *path)\n{\n    if (strcasestr(path, \".lua\") != NULL)\n        return pktgen_lua_save(path);\n    else\n        return pktgen_script_save(path);\n}\n\n/**\n *\n * pktgen_port_transmitting - Is the port transmitting packets?\n *\n * DESCRIPTION\n * Is the port transmitting packets.\n *\n * RETURNS: 1 for yes and 0 for no.\n *\n * SEE ALSO:\n */\nint\npktgen_port_transmitting(int port)\n{\n    return pktgen_tst_port_flags(l2p_get_port_pinfo(port), SENDING_PACKETS);\n}\n\n/**\n *\n * pktgen_link_state - Get the ASCII string for the port state.\n *\n * DESCRIPTION\n * Return the port state string for a given port.\n *\n * RETURNS: String pointer to link state\n *\n * SEE ALSO:\n */\n\nchar *\npktgen_link_state(int port, char *buff, int len)\n{\n    port_info_t *pinfo = l2p_get_port_pinfo(port);\n\n    if (pinfo->link.link_status == RTE_ETH_LINK_UP)\n        snprintf(buff, len, \"<UP-%u-%s>\", (uint32_t)pinfo->link.link_speed,\n                 (pinfo->link.link_duplex == RTE_ETH_LINK_FULL_DUPLEX) ? (\"FD\") : (\"HD\"));\n    else\n        snprintf(buff, len, \"<--Down-->\");\n\n    return buff;\n}\n\n/**\n *\n * pktgen_transmit_count_rate - Get a string for the current transmit count and rate\n *\n * DESCRIPTION\n * Current value of the transmit count/%rate as a string.\n *\n * RETURNS: String pointer to transmit count/%rate.\n *\n * SEE ALSO:\n */\n\nchar *\npktgen_transmit_count_rate(int port, char *buff, int len)\n{\n    port_info_t *pinfo = l2p_get_port_pinfo(port);\n\n    if (rte_atomic64_read(&pinfo->transmit_count) == 0)\n        snprintf(buff, len, \"Forever /%g%%\", pinfo->tx_rate);\n    else\n        snprintf(buff, len, \"%\" PRIu64 \" /%g%%\", rte_atomic64_read(&pinfo->transmit_count),\n                 pinfo->tx_rate);\n\n    return buff;\n}\n\n/**\n *\n * pktgen_port_stats - Get the port stats structure.\n *\n * DESCRIPTION\n * Return the packet statistics values.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nint\npktgen_port_stats(int port, port_stats_t *ps)\n{\n    port_info_t *pinfo = l2p_get_port_pinfo(port);\n\n    if (!ps)\n        return -1;\n\n    *ps = pinfo->stats;\n    return 0;\n}\n\n/**\n *\n * pktgen_flags_string - Return the flags string for display\n *\n * DESCRIPTION\n * Return the current flags string for display for a port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nchar *\npktgen_flags_string(port_info_t *pinfo)\n{\n    static char buff[64];\n    uint64_t flags = rte_atomic64_read(&pinfo->port_flags);\n\n    buff[0] = '\\0';\n    // clang-format off\n    snprintf(buff, sizeof(buff), \"%c%c%c%c%c%c%c%c%c%-6s%6s\",\n             (pktgen.flags & PROMISCUOUS_ON_FLAG) ? 'P' : '-',\n             (flags & ICMP_ECHO_ENABLE_FLAG)      ? 'E' : '-',\n             (flags & BONDING_TX_PACKETS)         ? 'B' : '-',\n             (flags & PROCESS_INPUT_PKTS)         ? 'I' : '-',\n             (flags & SEND_LATENCY_PKTS)          ? 'L' : '-',\n             (flags & RANDOMIZE_SRC_IP)           ? 'i' : '-',\n             (flags & RANDOMIZE_SRC_PT)           ? 'p' : '-',\n             (flags & SEND_RANDOM_PKTS)           ? 'R' : '-',\n             (flags & CAPTURE_PKTS)               ? 'c' : '-',\n\n             (flags & SEND_VLAN_ID)               ? \"VLAN\"\n             : (flags & SEND_VXLAN_PACKETS)       ? \"VxLan\"\n             : (flags & SEND_MPLS_LABEL)          ? \"MPLS\"\n             : (flags & SEND_Q_IN_Q_IDS)          ? \"QnQ\"\n             : (flags & SEND_GRE_IPv4_HEADER)     ? \"GREip\"\n             : (flags & SEND_GRE_ETHER_HEADER)    ? \"GREet\"\n                                                  : \"\",\n\n             (flags & SEND_PCAP_PKTS)             ? \"PCAP\"\n             : (flags & SEND_SEQ_PKTS)            ? \"Seq\"\n             : (flags & SEND_RANGE_PKTS)          ? \"Range\"\n             : (flags & SEND_SINGLE_PKTS)         ? \"Single\"\n                                                  : \"Unkn\");\n    // clang-format on\n\n    return buff;\n}\n\n/**\n *\n * pktgen_update_display - Update the display data and static data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_update_display(void)\n{\n    pktgen.flags |= PRINT_LABELS_FLAG;\n    pktgen.flags |= UPDATE_DISPLAY_FLAG;\n}\n\n/**\n *\n * pktgen_clear_display - clear the screen.\n *\n * DESCRIPTION\n * clear the screen and redisplay data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_clear_display(void)\n{\n    if (!scrn_is_paused()) {\n        scrn_pause();\n\n        scrn_cls();\n        scrn_pos(this_scrn->nrows + 1, 1);\n\n        pktgen_update_display();\n\n        scrn_resume();\n\n        pktgen_page_display();\n    }\n}\n\n/**\n *\n * pktgen_force_update - Force the screen to update data and static data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_force_update(void)\n{\n    pktgen.flags |= UPDATE_DISPLAY_FLAG;\n\n    if (!scrn_is_paused())\n        pktgen_page_display();\n}\n\n/**\n *\n * pktgen_set_page_size - Set the number of ports per page.\n *\n * DESCRIPTION\n * Set the max number of ports per page.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_set_page_size(uint32_t page_size)\n{\n    if ((page_size > 0) && (page_size <= pktgen.nb_ports) && (page_size <= 6)) {\n        pktgen.nb_ports_per_page = page_size;\n        pktgen.ending_port       = pktgen.starting_port + page_size;\n        if (pktgen.ending_port >= (pktgen.starting_port + pktgen.nb_ports))\n            pktgen.ending_port = (pktgen.starting_port + pktgen.nb_ports);\n        pktgen_clear_display();\n    }\n}\n\n/**\n *\n * pktgen_screen - Enable or Disable screen updates.\n *\n * DESCRIPTION\n * Enable or disable screen updates.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_screen(int state)\n{\n    uint16_t rows;\n\n    pktgen_display_get_geometry(&rows, NULL);\n\n    if (state == DISABLE_STATE) {\n        if (!scrn_is_paused()) {\n            scrn_pause();\n            scrn_cls();\n            scrn_setw(1);\n            scrn_pos(rows + 1, 1);\n        }\n    } else {\n        scrn_cls();\n        scrn_setw(pktgen.last_row + 1);\n        scrn_resume();\n        scrn_pos(rows + 1, 1);\n        pktgen_force_update();\n    }\n}\n\n/**\n *\n * pktgen_set_port_number - Set the current port number for sequence and range pages\n *\n * DESCRIPTION\n * Set the current port number for sequence and range pages.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_set_port_number(uint16_t port_number)\n{\n    if (port_number < pktgen.nb_ports) {\n        pktgen.curr_port = port_number;\n        pktgen_clear_display();\n    }\n}\n\n/**\n *\n * pktgen_set_icmp_echo - Set the ICMP echo response flag on a port\n *\n * DESCRIPTION\n * Enable or disable the ICMP echo response flags for the given ports.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_icmp_echo(port_info_t *pinfo, uint32_t onOff)\n{\n    if (onOff == ENABLE_STATE)\n        pktgen_set_port_flags(pinfo, ICMP_ECHO_ENABLE_FLAG);\n    else\n        pktgen_clr_port_flags(pinfo, ICMP_ECHO_ENABLE_FLAG);\n}\n\n/**\n *\n * enable_mac_from_arp - Enable or disable getting MAC from ARP requests.\n *\n * DESCRIPTION\n * Enable or disable getting the MAC address from the ARP request packets.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_mac_from_arp(uint32_t onOff)\n{\n    if (onOff == ENABLE_STATE)\n        pktgen.flags |= MAC_FROM_ARP_FLAG;\n    else\n        pktgen.flags &= ~MAC_FROM_ARP_FLAG;\n}\n\n/**\n *\n * enable_rnd_s_ip - Enable/disable randomizing the source IP address\n *\n * DESCRIPTION\n * Enable/disable randomizing the source IP address.\n *\n * Naively randomizes the addresses, as validating each IP could be a performance\n * bottleneck (given that only ~14% are invalid) and some people might want to\n * test invalid IPs too.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_rnd_s_ip(port_info_t *pinfo, uint32_t onOff)\n{\n    if (onOff == ENABLE_STATE)\n        pktgen_set_port_flags(pinfo, RANDOMIZE_SRC_IP);\n    else\n        pktgen_clr_port_flags(pinfo, RANDOMIZE_SRC_IP);\n}\n\n/**\n *\n * enable_rnd_s_pt - Enable/disable randomizing the source port\n *\n * DESCRIPTION\n * Enable/disable randomizing the source port.\n *\n * Naively randomizes the port, as despite it probably being weird for some ports to\n * receive traffic, all of them are technically valid (except for port 0).\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_rnd_s_pt(port_info_t *pinfo, uint32_t onOff)\n{\n    if (onOff == ENABLE_STATE)\n        pktgen_set_port_flags(pinfo, RANDOMIZE_SRC_PT);\n    else\n        pktgen_clr_port_flags(pinfo, RANDOMIZE_SRC_PT);\n}\n\n/**\n *\n * enable_random - Enable/disable random bitfield mode\n *\n * DESCRIPTION\n * Enable/disable random bitfield mode\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_random(port_info_t *pinfo, uint32_t onOff)\n{\n    if (onOff == ENABLE_STATE) {\n        pktgen_clr_port_flags(pinfo, EXCLUSIVE_MODES);\n        pktgen_set_port_flags(pinfo, SEND_RANDOM_PKTS | SEND_SINGLE_PKTS);\n    } else\n        pktgen_clr_port_flags(pinfo, SEND_RANDOM_PKTS);\n}\n\nvoid\nenable_clock_gettime(uint32_t onOff)\n{\n    if (onOff == ENABLE_STATE)\n        pktgen.flags |= CLOCK_GETTIME_FLAG;\n    else\n        pktgen.flags &= ~CLOCK_GETTIME_FLAG;\n\n    pktgen.hz            = pktgen_get_timer_hz();\n    pktgen.page_timeout  = UPDATE_DISPLAY_TICK_RATE;\n    pktgen.stats_timeout = pktgen.hz;\n}\n\nvoid\ndebug_tx_rate(port_info_t *pinfo)\n{\n    printf(\"  %d: rate %.2f, tx_cycles %'ld, tx_pps %'ld, link %s-%d-%s, hz %'ld\\n\", pinfo->pid,\n           pinfo->tx_rate, pinfo->tx_cycles, pinfo->tx_pps,\n           (pinfo->link.link_status) ? \"UP\" : \"Down\", pinfo->link.link_speed,\n           (pinfo->link.link_duplex == RTE_ETH_LINK_FULL_DUPLEX) ? \"FD\" : \"HD\", pktgen.hz);\n}\n\n/*\n * Local wrapper function to test mp is NULL and return or continue\n * to call rte_mempool_dump() routine.\n */\nstatic void\n__mempool_dump(FILE *f, struct rte_mempool *mp)\n{\n    if (mp == NULL)\n        return;\n    rte_mempool_dump(f, mp);\n}\n\n/**\n *\n * debug_mempool_dump - Display the mempool information\n *\n * DESCRIPTION\n * Dump out the mempool information.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\ndebug_mempool_dump(port_info_t *pinfo __rte_unused, char *name __rte_unused)\n{\n    int all;\n\n    all = !strcmp(name, \"all\");\n\n    if (all || !strcmp(name, \"rx\")) {\n        for (uint16_t q = 0; q < l2p_get_rxcnt(pinfo->pid); q++) {\n            struct rte_mempool *mp = l2p_get_rx_mp(pinfo->pid, q);\n            if (mp)\n                fprintf(stdout, \"\\nPort %u RX qid %u: %s @ %p\\n\", pinfo->pid, q, mp->name, mp);\n            __mempool_dump(stdout, mp);\n        }\n    }\n    if (all || !strcmp(name, \"tx\")) {\n        for (uint16_t q = 0; q < l2p_get_txcnt(pinfo->pid); q++) {\n            struct rte_mempool *mp = l2p_get_tx_mp(pinfo->pid, q);\n            if (mp)\n                fprintf(stdout, \"\\nPort %u TX qid %u: %s @ %p\\n\", pinfo->pid, q, mp->name, mp);\n            __mempool_dump(stdout, mp);\n        }\n    }\n    if (all || !strcmp(name, \"arp\")) {\n        for (uint16_t q = 0; q < l2p_get_txcnt(pinfo->pid); q++) {\n            struct rte_mempool *mp = l2p_get_sp_mp(pinfo->pid, q);\n            if (mp)\n                fprintf(stdout, \"\\nPort %u SP qid %u: %s @ %p\\n\", pinfo->pid, q, mp->name, mp);\n            __mempool_dump(stdout, mp);\n        }\n    }\n}\n\n/**\n *\n * pktgen_start_transmitting - Start a port transmitting packets.\n *\n * DESCRIPTION\n * Start the given ports sending packets.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_start_transmitting(port_info_t *pinfo)\n{\n    if (!pktgen_tst_port_flags(pinfo, SENDING_PACKETS)) {\n        rte_atomic64_set(&pinfo->current_tx_count, rte_atomic64_read(&pinfo->transmit_count));\n\n        if (rte_atomic64_read(&pinfo->current_tx_count) == 0)\n            pktgen_set_port_flags(pinfo, SEND_FOREVER);\n\n        pktgen_set_port_flags(pinfo, SETUP_TRANSMIT_PKTS);\n        pktgen_setup_packets(pinfo->pid);        // will clear the SETUP_TRANSMIT_PKTS flag\n        pktgen_set_port_flags(pinfo, SENDING_PACKETS);\n    }\n}\n\n/**\n *\n * pktgen_stop_transmitting - Stop port transmitting packets.\n *\n * DESCRIPTION\n * Stop the given ports from sending traffic.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_stop_transmitting(port_info_t *pinfo)\n{\n    if (pktgen_tst_port_flags(pinfo, SENDING_PACKETS))\n        pktgen_clr_port_flags(pinfo, (SENDING_PACKETS | SEND_FOREVER));\n}\n\nstatic void\npktgen_set_receive_state(port_info_t *pinfo, int state)\n{\n    if (state)\n        pktgen_set_port_flags(pinfo, STOP_RECEIVING_PACKETS);\n    else\n        pktgen_clr_port_flags(pinfo, STOP_RECEIVING_PACKETS);\n}\n\n/**\n *\n * pktgen_start_stop_latency_sampler - Starts or stops latency sampler.\n *\n * DESCRIPTION\n * Starts or stops latency sampler.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_start_stop_latency_sampler(port_info_t *pinfo, uint32_t state)\n{\n    if (state == ENABLE_STATE)\n        pktgen_start_latency_sampler(pinfo);\n    else if (state == DISABLE_STATE)\n        pktgen_stop_latency_sampler(pinfo);\n}\n\n/**\n *\n * start_latency_sampler - Starts latency sampler.\n *\n * DESCRIPTION\n * Starts latency sampler.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_start_latency_sampler(port_info_t *pinfo)\n{\n    uint16_t q, rxq;\n\n    /* Start sampler */\n    if (pktgen_tst_port_flags(pinfo, SAMPLING_LATENCIES)) {\n        pktgen_log_info(\"Latency sampler is already running, stop it first!\");\n        return;\n    }\n\n    if (pinfo->latsamp_rate == 0 || pinfo->latsamp_type == LATSAMPLER_UNSPEC ||\n        pinfo->latsamp_num_samples == 0) {\n        pktgen_log_error(\"Set proper sampling type, number, rate and outfile!\");\n        return;\n    }\n\n    rxq = l2p_get_rxcnt(pinfo->pid);\n    if (rxq == 0 || rxq > MAX_QUEUES_PER_PORT) {\n        pktgen_log_error(\"no rx queues or rx queues over limit (%d) to sample on this port!\",\n                         MAX_QUEUES_PER_PORT);\n        return;\n    }\n\n    for (q = 0; q < rxq; q++) {\n        pinfo->latsamp_stats[q].next        = 0;\n        pinfo->latsamp_stats[q].idx         = 0;\n        pinfo->latsamp_stats[q].num_samples = pinfo->latsamp_num_samples / rxq;\n        pktgen_log_info(\"Assigning %d sample latencies to queue %d\",\n                        pinfo->latsamp_num_samples / rxq, q);\n    }\n\n    if (pinfo->seq_pkt[LATENCY_PKT].pkt_size <\n        (RTE_ETHER_MIN_LEN - RTE_ETHER_CRC_LEN) + sizeof(tstamp_t))\n        pinfo->seq_pkt[LATENCY_PKT].pkt_size += sizeof(tstamp_t);\n\n    pinfo->seq_pkt[LATENCY_PKT].ipProto = PG_IPPROTO_UDP;\n    pktgen_packet_ctor(pinfo, LATENCY_PKT, -1);\n\n    /* Start sampling */\n    pktgen_set_port_flags(pinfo, SAMPLING_LATENCIES);\n}\n\n/**\n *\n * stop_latency_sampler - Stops latency sampler\n *\n * DESCRIPTION\n * Stops latency sampler\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_stop_latency_sampler(port_info_t *pinfo)\n{\n    FILE *outfile;\n    uint32_t i, count;\n    uint16_t q, rxq = l2p_get_rxcnt(pinfo->pid);\n\n    if (!pktgen_tst_port_flags(pinfo, SAMPLING_LATENCIES)) {\n        pktgen_log_info(\"Latency sampler is not running, nothing to do!\");\n        return;\n    }\n\n    /* Stop sampling */\n    pktgen_clr_port_flags(pinfo, SAMPLING_LATENCIES);\n\n    /* Dump stats to file */\n    outfile = fopen(pinfo->latsamp_outfile, \"w\");\n    if (outfile == NULL)\n        pktgen_log_error(\"Cannot open the latency outfile!\");\n    else {\n        pktgen_log_info(\"Writing to file %s\", pinfo->latsamp_outfile);\n        fprintf(outfile, \"Latency\\n\");\n        for (q = 0, count = 0; q < rxq; q++) {\n            pktgen_log_info(\"Writing sample latencies of queue %d\", q);\n            for (i = 0; i < pinfo->latsamp_stats[q].idx; i++) {\n                fprintf(outfile, \"%\" PRIu64 \"\\n\", pinfo->latsamp_stats[q].data[i]);\n                count++;\n            }\n        }\n        fclose(outfile);\n        pktgen_log_warning(\"Wrote %d sample latencies to file %s\", count, pinfo->latsamp_outfile);\n    }\n\n    /* Reset stats data */\n    for (q = 0; q < rxq; q++) {\n        pinfo->latsamp_stats[q].next        = 0;\n        pinfo->latsamp_stats[q].idx         = 0;\n        pinfo->latsamp_stats[q].num_samples = 0;\n    }\n\n    pinfo->seq_pkt[LATENCY_PKT].ipProto = PG_IPPROTO_UDP;\n    pktgen_packet_ctor(pinfo, LATENCY_PKT, -1);\n}\n\n/**\n *\n * pktgen_prime_ports - Send a small number of packets to setup forwarding tables\n *\n * DESCRIPTION\n * Send a small number of packets from a port to setup the forwarding tables in\n * the device under test.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_prime_ports(port_info_t *pinfo)\n{\n    rte_atomic64_set(&pinfo->current_tx_count, pinfo->prime_cnt);\n    pktgen_set_port_flags(pinfo, SENDING_PACKETS);\n    rte_delay_us_sleep(300 * 1000);\n}\n\n/**\n *\n * single_set_proto - Set up the protocol type for a port/packet.\n *\n * DESCRIPTION\n * Setup all single packets with a protocol types with the port list.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_proto(port_info_t *pinfo, char *type)\n{\n    pinfo->seq_pkt[SINGLE_PKT].ipProto = (type[0] == 'u')   ? PG_IPPROTO_UDP\n                                         : (type[0] == 'i') ? PG_IPPROTO_ICMP\n                                         : (type[0] == 't') ? PG_IPPROTO_TCP\n                                                            : PG_IPPROTO_TCP;\n\n    /* ICMP only works on IPv4 packets. */\n    if (type[0] == 'i')\n        pinfo->seq_pkt[SINGLE_PKT].ethType = RTE_ETHER_TYPE_IPV4;\n\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * range_set_proto - Set up the protocol type for a port/packet.\n *\n * DESCRIPTION\n * Setup all range packets with a protocol types with the port list.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_proto(port_info_t *pinfo, const char *type)\n{\n    pinfo->seq_pkt[RANGE_PKT].ipProto = (type[0] == 'u')   ? PG_IPPROTO_UDP\n                                        : (type[0] == 'i') ? PG_IPPROTO_ICMP\n                                        : (type[0] == 't') ? PG_IPPROTO_TCP\n                                                           : PG_IPPROTO_TCP;\n    pinfo->range.ip_proto             = pinfo->seq_pkt[RANGE_PKT].ipProto;\n\n    /* ICMP only works on IPv4 packets. */\n    if (type[0] == 'i')\n        pinfo->seq_pkt[RANGE_PKT].ethType = RTE_ETHER_TYPE_IPV4;\n}\n\n/**\n *\n * enable_pcap - Enable or disable PCAP sending of packets.\n *\n * DESCRIPTION\n * Enable or disable PCAP packet sending.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_pcap(port_info_t *pinfo, uint32_t state)\n{\n    pcap_info_t *pcap = l2p_get_pcap(pinfo->pid);\n\n    if ((pcap != NULL) && (pcap->pkt_count != 0)) {\n        if (state == ENABLE_STATE) {\n            pktgen_clr_port_flags(pinfo, EXCLUSIVE_MODES);\n            pktgen_set_port_flags(pinfo, SEND_PCAP_PKTS);\n        } else {\n            pktgen_clr_port_flags(pinfo, EXCLUSIVE_MODES);\n            pktgen_set_port_flags(pinfo, SEND_SINGLE_PKTS);\n        }\n        pinfo->tx_cycles = 0;\n    }\n}\n\n/**\n *\n * pcap_filter - Compile a PCAP filter for a portlist\n *\n * DESCRIPTION\n * Compile a pcap filter for a portlist\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npcap_filter(port_info_t *pinfo, char *str)\n{\n    pcap_t *pc        = pcap_open_dead(DLT_EN10MB, 65535);\n    pcap_info_t *pcap = l2p_get_pcap(pinfo->pid);\n\n    pcap->pcap_result = pcap_compile(pc, &pcap->pcap_program, str, 1, PCAP_NETMASK_UNKNOWN);\n\n    pcap_close(pc);\n}\n\n/**\n *\n * debug_blink - Enable or disable a port from blinking.\n *\n * DESCRIPTION\n * Enable or disable the given ports from blinking.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\ndebug_blink(port_info_t *pinfo, uint32_t state)\n{\n    if (state == ENABLE_STATE)\n        pktgen.blinklist |= (1 << pinfo->pid);\n    else {\n        pktgen.blinklist &= ~(1 << pinfo->pid);\n        rte_eth_led_on(pinfo->pid);\n    }\n}\n\n/**\n *\n * enable_process - Enable or disable input packet processing.\n *\n * DESCRIPTION\n * Enable or disable input packet processing of ICMP, ARP, ...\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_process(port_info_t *pinfo, int state)\n{\n    if (state == ENABLE_STATE)\n        pktgen_set_port_flags(pinfo, PROCESS_INPUT_PKTS);\n    else\n        pktgen_clr_port_flags(pinfo, PROCESS_INPUT_PKTS);\n}\n\n/**\n *\n * enable_capture - Enable or disable capture packet processing.\n *\n * DESCRIPTION\n * Enable or disable capture packet processing of ICMP, ARP, ...\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_capture(port_info_t *pinfo, uint32_t state)\n{\n    pktgen_set_capture(pinfo, state);\n}\n\n#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_NET_BOND)\n/**\n *\n * enable_bonding - Enable or disable bonding TX zero packet processing.\n *\n * DESCRIPTION\n * Enable or disable calling TX with zero packets for bonding PMD\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_bonding(port_info_t *pinfo, uint32_t state)\n{\n    struct rte_eth_bond_8023ad_conf conf;\n    uint16_t workers[RTE_MAX_ETHPORTS];\n    uint16_t active_workers[RTE_MAX_ETHPORTS];\n    int i, num_workers, num_active_workers;\n\n    if (rte_eth_bond_8023ad_conf_get(pinfo->pid, &conf) < 0) {\n        printf(\"Port %d is not a bonding port\\n\", pinfo->pid);\n        return;\n    }\n\n    num_workers = rte_eth_bond_members_get(pinfo->pid, workers, RTE_MAX_ETHPORTS);\n    if (num_workers < 0) {\n        printf(\"Failed to get worker list for port = %d\\n\", pinfo->pid);\n        return;\n    }\n\n    num_active_workers =\n        rte_eth_bond_active_members_get(pinfo->pid, active_workers, RTE_MAX_ETHPORTS);\n    if (num_active_workers < 0) {\n        printf(\"Failed to get active worker list for port = %d\\n\", pinfo->pid);\n        return;\n    }\n\n    printf(\"Port %d:\\n\", pinfo->pid);\n    for (i = 0; i < num_workers; i++) {\n        if (state == ENABLE_STATE) {\n            pktgen_set_port_flags(pinfo, BONDING_TX_PACKETS);\n            rte_eth_bond_8023ad_ext_distrib(pinfo->pid, workers[i], 1);\n            printf(\"   Enable worker %u 802.3ad distributing\\n\", workers[i]);\n            rte_eth_bond_8023ad_ext_collect(pinfo->pid, workers[i], 1);\n            printf(\"   Enable worker %u 802.3ad collecting\\n\", workers[i]);\n        } else {\n            pktgen_clr_port_flags(pinfo, BONDING_TX_PACKETS);\n            rte_eth_bond_8023ad_ext_distrib(pinfo->pid, workers[i], 0);\n            printf(\"   Disable worker %u 802.3ad distributing\\n\", workers[i]);\n            rte_eth_bond_8023ad_ext_collect(pinfo->pid, workers[i], 1);\n            printf(\"   Enable worker %u 802.3ad collecting\\n\", workers[i]);\n        }\n    }\n}\n\nstatic void\nshow_states(uint8_t state)\n{\n    const char *states[] = {\"LACP_Active\", \"LACP_Short_timeout\", \"Aggregation\", \"Synchronization\",\n                            \"Collecting\",  \"Distributing\",       \"Defaulted\",   \"Expired\",\n                            NULL};\n    int j;\n\n    for (j = 0; states[j]; j++) {\n        if (state & (1 << j))\n            printf(\"%s \", states[j]);\n    }\n}\n\nvoid\nshow_bonding_mode(port_info_t *pinfo)\n{\n    int bonding_mode, agg_mode;\n    uint16_t workers[RTE_MAX_ETHPORTS];\n    int num_workers, num_active_workers;\n    int primary_id;\n    int i;\n    uint16_t port_id = pinfo->pid;\n\n    /* Display the bonding mode.*/\n    bonding_mode = rte_eth_bond_mode_get(port_id);\n    if (bonding_mode < 0) {\n        printf(\"Failed to get bonding mode for port = %d\\n\", port_id);\n        return;\n    } else\n        printf(\"\\tBonding mode: %d, \", bonding_mode);\n\n    if (bonding_mode == BONDING_MODE_BALANCE) {\n        int balance_xmit_policy;\n\n        balance_xmit_policy = rte_eth_bond_xmit_policy_get(port_id);\n        if (balance_xmit_policy < 0) {\n            printf(\"\\nFailed to get balance xmit policy for port = %d\\n\", port_id);\n            return;\n        } else {\n            printf(\"Balance Xmit Policy: \");\n\n            switch (balance_xmit_policy) {\n            case BALANCE_XMIT_POLICY_LAYER2:\n                printf(\"BALANCE_XMIT_POLICY_LAYER2\");\n                break;\n            case BALANCE_XMIT_POLICY_LAYER23:\n                printf(\"BALANCE_XMIT_POLICY_LAYER23\");\n                break;\n            case BALANCE_XMIT_POLICY_LAYER34:\n                printf(\"BALANCE_XMIT_POLICY_LAYER34\");\n                break;\n            }\n            printf(\", \");\n        }\n    }\n\n    if (bonding_mode == BONDING_MODE_8023AD) {\n        agg_mode = rte_eth_bond_8023ad_agg_selection_get(port_id);\n        printf(\"IEEE802.3AD Aggregator Mode: \");\n        switch (agg_mode) {\n        case AGG_BANDWIDTH:\n            printf(\"bandwidth\");\n            break;\n        case AGG_STABLE:\n            printf(\"stable\");\n            break;\n        case AGG_COUNT:\n            printf(\"count\");\n            break;\n        }\n        printf(\"\\n\");\n    }\n\n    num_workers = rte_eth_bond_members_get(port_id, workers, RTE_MAX_ETHPORTS);\n\n    if (num_workers < 0) {\n        printf(\"\\tFailed to get worker list for port = %d\\n\", port_id);\n        return;\n    }\n    if (num_workers > 0) {\n        printf(\"\\tSlaves (%d): [\", num_workers);\n        for (i = 0; i < num_workers - 1; i++)\n            printf(\"%d \", workers[i]);\n\n        printf(\"%d]\\n\", workers[num_workers - 1]);\n    } else {\n        printf(\"\\tSlaves: []\\n\");\n    }\n\n    num_active_workers = rte_eth_bond_active_members_get(port_id, workers, RTE_MAX_ETHPORTS);\n\n    if (num_active_workers < 0) {\n        printf(\"\\tFailed to get active worker list for port = %d\\n\", port_id);\n        return;\n    }\n    if (num_active_workers > 0) {\n        printf(\"\\tActive Slaves (%d): [\", num_active_workers);\n        for (i = 0; i < num_active_workers - 1; i++)\n            printf(\"%d \", workers[i]);\n\n        printf(\"%d]\\n\", workers[num_active_workers - 1]);\n\n    } else {\n        printf(\"\\tActive Slaves: []\\n\");\n    }\n\n    for (i = 0; i < num_active_workers; i++) {\n        struct rte_eth_bond_8023ad_member_info conf;\n\n        printf(\"\\t\\tSlave %u\\n\", workers[i]);\n        rte_eth_bond_8023ad_member_info(pinfo->pid, workers[i], &conf);\n        printf(\"\\t\\t  %sSelected\\n\\t\\t  Actor States  ( \", conf.selected ? \"\" : \"Not \");\n        show_states(conf.actor_state);\n        printf(\")\\n\\t\\t  Partner States( \");\n        show_states(conf.partner_state);\n        printf(\")\\n\\t\\t  AGG Port %u\\n\", conf.agg_port_id);\n    }\n\n    primary_id = rte_eth_bond_primary_get(port_id);\n    if (primary_id < 0) {\n        printf(\"\\tFailed to get primary worker for port = %d\\n\", port_id);\n        return;\n    } else\n        printf(\"\\tPrimary: [%d]\\n\", primary_id);\n}\n#endif\n\n/**\n *\n * range_set_pkt_type - Set the packet type value for range packets.\n *\n * DESCRIPTION\n * Set the packet type value for the given port list.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_pkt_type(port_info_t *pinfo, const char *type)\n{\n    pinfo->seq_pkt[RANGE_PKT].ethType = (type[0] == 'a')   ? RTE_ETHER_TYPE_ARP\n                                        : (type[3] == '4') ? RTE_ETHER_TYPE_IPV4\n                                        : (type[3] == '6') ? RTE_ETHER_TYPE_IPV6\n                                                           : RTE_ETHER_TYPE_IPV4;\n    if (pinfo->seq_pkt[RANGE_PKT].ethType == RTE_ETHER_TYPE_IPV6) {\n        if (pinfo->range.pkt_size < MIN_v6_PKT_SIZE)\n            pinfo->range.pkt_size = MIN_v6_PKT_SIZE;\n        if (pinfo->range.pkt_size_min < MIN_v6_PKT_SIZE)\n            pinfo->range.pkt_size_min = MIN_v6_PKT_SIZE;\n        if (pinfo->range.pkt_size_max < MIN_v6_PKT_SIZE)\n            pinfo->range.pkt_size_max = MIN_v6_PKT_SIZE;\n    }\n}\n\n/**\n *\n * single_set_pkt_type - Set the packet type value.\n *\n * DESCRIPTION\n * Set the packet type value for the given port list.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_pkt_type(port_info_t *pinfo, const char *type)\n{\n    pkt_seq_t *pkt   = &pinfo->seq_pkt[SINGLE_PKT];\n    uint16_t ethtype = pkt->ethType;\n\n    /* handle ['arp' | 'a'] or ['ipv4' | 'ipv6'] or ['ip4' | 'ip6'] */\n    pkt->ethType = (type[0] == 'a')   ? RTE_ETHER_TYPE_ARP\n                   : (type[3] == '4') ? RTE_ETHER_TYPE_IPV4\n                   : (type[3] == '6') ? RTE_ETHER_TYPE_IPV6\n                   : (type[2] == '4') ? RTE_ETHER_TYPE_IPV4\n                   : (type[2] == '6') ? RTE_ETHER_TYPE_IPV6\n                                      : RTE_ETHER_TYPE_IPV4;\n\n    if ((ethtype == RTE_ETHER_TYPE_IPV6) && (pkt->ethType == RTE_ETHER_TYPE_IPV4)) {\n        if (pkt->pkt_size >= (MIN_v6_PKT_SIZE - RTE_ETHER_CRC_LEN))\n            pkt->pkt_size =\n                RTE_ETHER_MIN_LEN + (pkt->pkt_size - (MIN_v6_PKT_SIZE - RTE_ETHER_CRC_LEN));\n    }\n    if ((ethtype == RTE_ETHER_TYPE_IPV4) && (pkt->ethType == RTE_ETHER_TYPE_IPV6)) {\n        if (pkt->pkt_size < (MIN_v6_PKT_SIZE - RTE_ETHER_CRC_LEN))\n            pkt->pkt_size =\n                MIN_v6_PKT_SIZE + (pkt->pkt_size - (RTE_ETHER_MIN_LEN - RTE_ETHER_CRC_LEN));\n    }\n\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * enable_vxlan - Set the port to send a VxLAN ID\n *\n * DESCRIPTION\n * Set the given port list to send VxLAN ID packets.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_vxlan(port_info_t *pinfo, uint32_t onOff)\n{\n    if (onOff == ENABLE_STATE) {\n        pktgen_clr_port_flags(pinfo, EXCLUSIVE_PKT_MODES);\n        pktgen_set_port_flags(pinfo, SEND_VXLAN_PACKETS);\n    } else\n        pktgen_clr_port_flags(pinfo, SEND_VXLAN_PACKETS);\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * enable_vlan - Set the port to send a VLAN ID\n *\n * DESCRIPTION\n * Set the given port list to send VLAN ID packets.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_vlan(port_info_t *pinfo, uint32_t onOff)\n{\n    if (onOff == ENABLE_STATE) {\n        pktgen_clr_port_flags(pinfo, EXCLUSIVE_PKT_MODES);\n        pktgen_set_port_flags(pinfo, SEND_VLAN_ID | SEND_SINGLE_PKTS);\n    } else\n        pktgen_clr_port_flags(pinfo, SEND_VLAN_ID);\n}\n\n/**\n *\n * single_set_vlan_id - Set the port VLAN ID value\n *\n * DESCRIPTION\n * Set the given port list with the given VLAN ID.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_vlan_id(port_info_t *pinfo, uint16_t vlanid)\n{\n    pinfo->vlanid                     = vlanid;\n    pinfo->seq_pkt[SINGLE_PKT].vlanid = pinfo->vlanid;\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * single_set_prio - Set the port 802.1p cos value\n *\n * DESCRIPTION\n * Set the given port list with the given 802.1p cos\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_cos(port_info_t *pinfo, uint8_t cos)\n{\n    pinfo->cos                     = cos;\n    pinfo->seq_pkt[SINGLE_PKT].cos = pinfo->cos;\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * single_set_tos - Set the port tos value\n *\n * DESCRIPTION\n * Set the given port list with the given tos\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_tos(port_info_t *pinfo, uint8_t tos)\n{\n    pinfo->tos                     = tos;\n    pinfo->seq_pkt[SINGLE_PKT].tos = pinfo->tos;\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * single_set_vxlan - Set the port vxlan value\n *\n * DESCRIPTION\n * Set the given port list with the given vxlan\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_vxlan(port_info_t *pinfo, uint16_t flags, uint16_t group_id, uint32_t vxlan_id)\n{\n    pinfo->vni_flags                     = flags;\n    pinfo->group_id                      = group_id;\n    pinfo->vxlan_id                      = vxlan_id;\n    pinfo->seq_pkt[SINGLE_PKT].vni_flags = pinfo->vni_flags;\n    pinfo->seq_pkt[SINGLE_PKT].group_id  = pinfo->group_id;\n    pinfo->seq_pkt[SINGLE_PKT].vxlan_id  = pinfo->vxlan_id;\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * single_set_latsamp_params - Set the port latency sampler parameters\n *\n * DESCRIPTION\n * Set the given port list with the given latency sampler parameters\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_latsampler_params(port_info_t *pinfo, char *type, uint32_t num_samples,\n                             uint32_t sampling_rate, char outfile[])\n{\n    FILE *fp = NULL;\n    uint32_t sampler_type;\n\n    /* Stop if latency sampler is running */\n    if (pktgen_tst_port_flags(pinfo, SAMPLING_LATENCIES)) {\n        pktgen_log_warning(\"Latency sampler is already running, stop it first!\");\n        return;\n    }\n    /* Validate sampler type*/\n    if (!strcasecmp(type, \"simple\"))\n        sampler_type = LATSAMPLER_SIMPLE;\n    else if (!strcasecmp(type, \"poisson\"))\n        sampler_type = LATSAMPLER_POISSON;\n    else {\n        pktgen_log_error(\"Unknown latsampler type %s! Valid values: simple, poisson\", type);\n        return;\n    }\n\n    /* Validate file path */\n    fp = fopen(outfile, \"w+\");\n    if (fp == NULL) {\n        pktgen_log_error(\"Cannot write to file path %s!\", outfile);\n        return;\n    }\n    fclose(fp);\n\n    if (num_samples > MAX_LATENCY_ENTRIES) {\n        pktgen_log_error(\"Too many samples requested. Max %d!\", MAX_LATENCY_ENTRIES);\n        return;\n    }\n\n    pinfo->latsamp_type        = sampler_type;\n    pinfo->latsamp_rate        = sampling_rate;\n    pinfo->latsamp_num_samples = num_samples;\n    strcpy(pinfo->latsamp_outfile, outfile);\n\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * enable_mpls - Set the port to send a mpls ID\n *\n * DESCRIPTION\n * Set the given port list to send mpls ID packets.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_mpls(port_info_t *pinfo, uint32_t onOff)\n{\n    if (onOff == ENABLE_STATE) {\n        pktgen_clr_port_flags(pinfo, EXCLUSIVE_PKT_MODES);\n        pktgen_set_port_flags(pinfo, SEND_MPLS_LABEL | SEND_SINGLE_PKTS);\n    } else\n        pktgen_clr_port_flags(pinfo, SEND_MPLS_LABEL);\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * range_set_mpls_entry - Set the port MPLS entry value\n *\n * DESCRIPTION\n * Set the given port list with the given MPLS entry.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_mpls_entry(port_info_t *pinfo, uint32_t mpls_entry)\n{\n    pinfo->mpls_entry                     = mpls_entry;\n    pinfo->seq_pkt[SINGLE_PKT].mpls_entry = pinfo->mpls_entry;\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * enable_qinq - Set the port to send a Q-in-Q header\n *\n * DESCRIPTION\n * Set the given port list to send Q-in-Q ID packets.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_qinq(port_info_t *pinfo, uint32_t onOff)\n{\n    if (onOff == ENABLE_STATE) {\n        pktgen_clr_port_flags(pinfo, EXCLUSIVE_MODES);\n        pktgen_clr_port_flags(pinfo, EXCLUSIVE_PKT_MODES);\n        pktgen_set_port_flags(pinfo, SEND_Q_IN_Q_IDS | SEND_SINGLE_PKTS);\n    } else\n        pktgen_clr_port_flags(pinfo, SEND_Q_IN_Q_IDS);\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * single_set_qinqids - Set the port Q-in-Q ID values\n *\n * DESCRIPTION\n * Set the given port list with the given Q-in-Q ID's.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_qinqids(port_info_t *pinfo, uint16_t outerid, uint16_t innerid)\n{\n    pinfo->seq_pkt[SINGLE_PKT].qinq_outerid = outerid;\n    pinfo->seq_pkt[SINGLE_PKT].qinq_innerid = innerid;\n\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * range_set_qinqids - Set the port Q-in-Q ID values\n *\n * DESCRIPTION\n * Set the given port list with the given Q-in-Q ID's.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_qinqids(port_info_t *pinfo, uint16_t outerid, uint16_t innerid)\n{\n    pinfo->seq_pkt[RANGE_PKT].qinq_outerid = outerid;\n    pinfo->seq_pkt[RANGE_PKT].qinq_innerid = innerid;\n\n    pktgen_packet_ctor(pinfo, RANGE_PKT, -1);\n}\n\n/**\n *\n * enable_gre - Set the port to send GRE with IPv4 payload\n *\n * DESCRIPTION\n * Set the given port list to send GRE with IPv4 payload\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_gre(port_info_t *pinfo, uint32_t onOff)\n{\n    if (onOff == ENABLE_STATE) {\n        pktgen_clr_port_flags(pinfo, EXCLUSIVE_PKT_MODES);\n        pktgen_set_port_flags(pinfo, SEND_GRE_IPv4_HEADER | SEND_SINGLE_PKTS);\n    } else\n        pktgen_clr_port_flags(pinfo, SEND_GRE_IPv4_HEADER);\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * pktgen_set_gre_eth - Set the port to send GRE with Ethernet payload\n *\n * DESCRIPTION\n * Set the given port list to send GRE with Ethernet payload\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_gre_eth(port_info_t *pinfo, uint32_t onOff)\n{\n    if (onOff == ENABLE_STATE) {\n        pktgen_clr_port_flags(pinfo, EXCLUSIVE_PKT_MODES);\n        pktgen_set_port_flags(pinfo, SEND_GRE_ETHER_HEADER | SEND_SINGLE_PKTS);\n    } else\n        pktgen_clr_port_flags(pinfo, SEND_GRE_ETHER_HEADER);\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * range_set_gre_key - Set the port GRE key\n *\n * DESCRIPTION\n * Set the given port list with the given GRE key.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_gre_key(port_info_t *pinfo, uint32_t gre_key)\n{\n    pinfo->gre_key                     = gre_key;\n    pinfo->seq_pkt[SINGLE_PKT].gre_key = pinfo->gre_key;\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * pktgen_clear_stats - Clear a given port list of stats.\n *\n * DESCRIPTION\n * Clear the given port list of all statistics.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_clear_stats(port_info_t *pinfo)\n{\n    /* curr_stats are reset each time the stats are read */\n    memset(&pinfo->stats, 0, sizeof(port_stats_t));\n\n    struct rte_eth_stats *base = &pinfo->stats.base;\n\n    /* Normalize the stats to a zero base line */\n    rte_eth_stats_get(pinfo->pid, base);\n\n    pktgen.max_total_ipackets = 0;\n    pktgen.max_total_opackets = 0;\n\n    latency_t *lat = &pinfo->latency;\n\n    memset(lat->stats, 0, (lat->end_stats - lat->stats) * sizeof(uint64_t));\n\n    memset(&pktgen.cumm_rate_totals, 0, sizeof(struct rte_eth_stats));\n}\n\n/**\n *\n * pktgen_port_defaults - Set all ports back to the default values.\n *\n * DESCRIPTION\n * Reset the ports back to the defaults.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_port_defaults(uint16_t pid)\n{\n    port_info_t *pinfo = l2p_get_port_pinfo(pid);\n\n    rte_atomic64_set(&pinfo->transmit_count, DEFAULT_TX_COUNT);\n    rte_atomic64_init(&pinfo->current_tx_count);\n\n    pinfo->tx_rate   = DEFAULT_TX_RATE;\n    pinfo->tx_burst  = DEFAULT_PKT_TX_BURST;\n    pinfo->rx_burst  = DEFAULT_PKT_RX_BURST;\n    pinfo->vlanid    = DEFAULT_VLAN_ID;\n    pinfo->cos       = DEFAULT_COS;\n    pinfo->tos       = DEFAULT_TOS;\n    pinfo->seqCnt    = 0;\n    pinfo->seqIdx    = 0;\n    pinfo->prime_cnt = DEFAULT_PRIME_COUNT;\n\n    if (rte_eth_macaddr_get(pid, &pinfo->src_mac) < 0)\n        pktgen_log_panic(\"Can't get MAC address: port=%u\", pid);\n\n    pktgen.flags |= PRINT_LABELS_FLAG;\n}\n\n/**\n *\n * pktgen_seq_defaults - Set all ports back to the default values.\n *\n * DESCRIPTION\n * Reset the ports back to the defaults.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_seq_defaults(uint16_t pid)\n{\n    port_info_t *pi, *pinfo = l2p_get_port_pinfo(pid);\n    pkt_seq_t *pkt = NULL;\n\n    pktgen_log_info(\"   Setting sequence defaults for port %u\", pid);\n\n    /* Setup the port and packet defaults */\n    for (uint8_t s = 0; s < NUM_TOTAL_PKTS; s++) {\n        pkt = &pinfo->seq_pkt[s];\n\n        pkt->pkt_size  = (RTE_ETHER_MIN_LEN - RTE_ETHER_CRC_LEN);\n        pkt->sport     = DEFAULT_SRC_PORT;\n        pkt->dport     = DEFAULT_DST_PORT;\n        pkt->ttl       = DEFAULT_TTL;\n        pkt->ipProto   = PG_IPPROTO_TCP;\n        pkt->ethType   = RTE_ETHER_TYPE_IPV4;\n        pkt->vlanid    = DEFAULT_VLAN_ID;\n        pkt->cos       = DEFAULT_COS;\n        pkt->tos       = DEFAULT_TOS;\n        pkt->tcp_flags = DEFAULT_TCP_FLAGS;\n\n        pkt->ip_mask = DEFAULT_NETMASK;\n        if (pktgen.nb_ports > 1) {\n            if ((pid & 1) == 0) {\n                pkt->ip_src_addr.addr.ipv4.s_addr = DEFAULT_IP_ADDR | (pid << 8) | 1;\n                pkt->ip_dst_addr.addr.ipv4.s_addr = DEFAULT_IP_ADDR | ((pid + 1) << 8) | 1;\n            } else {\n                pkt->ip_src_addr.addr.ipv4.s_addr = DEFAULT_IP_ADDR | (pid << 8) | 1;\n                pkt->ip_dst_addr.addr.ipv4.s_addr = DEFAULT_IP_ADDR | ((pid - 1) << 8) | 1;\n            }\n        } else {\n            pkt->ip_src_addr.addr.ipv4.s_addr = DEFAULT_IP_ADDR | (pid << 8) | 1;\n            pkt->ip_dst_addr.addr.ipv4.s_addr = DEFAULT_IP_ADDR | ((pid + 1) << 8) | 1;\n        }\n        rte_ether_addr_copy(&pinfo->src_mac, &pkt->eth_src_addr);\n\n        if (pid < (pktgen.nb_ports - 1) && (pid & 1) == 0) {\n            pi = l2p_get_port_pinfo(pid + 1);\n            rte_ether_addr_copy(&pi->src_mac, &pkt->eth_dst_addr);\n        } else if (pid > 0 && (pid & 1) == 1) {\n            pi = l2p_get_port_pinfo(pid - 1);\n            rte_ether_addr_copy(&pi->src_mac, &pkt->eth_dst_addr);\n        }\n        pktgen_packet_ctor(pinfo, s, -1);\n    }\n}\n\n/**\n *\n * pktgen_ping4 - Send a IPv4 ICMP echo request.\n *\n * DESCRIPTION\n * Send a IPv4 ICMP echo request packet.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_ping4(port_info_t *pinfo)\n{\n    memcpy(&pinfo->seq_pkt[SPECIAL_PKT], &pinfo->seq_pkt[SINGLE_PKT], sizeof(pkt_seq_t));\n    pinfo->seq_pkt[SPECIAL_PKT].ipProto = PG_IPPROTO_ICMP;\n    pktgen_packet_ctor(pinfo, SPECIAL_PKT, ICMP4_ECHO);\n    pktgen_set_port_flags(pinfo, SEND_PING4_REQUEST);\n}\n\n#ifdef INCLUDE_PING6\n/**\n *\n * pktgen_ping6 - Send a IPv6 ICMP echo request packet.\n *\n * DESCRIPTION\n * Send a IPv6 ICMP echo request packet for the given ports.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_ping6(port_info_t *pinfo)\n{\n    memcpy(&pinfo->pkt[PING_PKT], &pinfo->pkt[SINGLE_PKT], sizeof(pkt_seq_t));\n    pinfo->pkt[PING_PKT].ipProto = PG_IPPROTO_ICMP;\n    pktgen_packet_ctor(pinfo, PING_PKT, ICMP6_ECHO);\n    pktgen_set_port_flags(pinfo, SEND_PING6_REQUEST);\n}\n#endif\n\n/**\n *\n * pktgen_reset - Reset all ports to the default state\n *\n * DESCRIPTION\n * Reset all ports to the default state.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_reset(port_info_t *pinfo)\n{\n    char off[8];\n\n    if (pinfo == NULL)\n        rte_exit(EXIT_FAILURE, \"No port_info_t pointer specified\\n\");\n\n    pktgen_log_info(\"Reset port %u configuration to default\", pinfo->pid);\n\n    strcpy(off, \"off\");\n    pktgen_stop_transmitting(pinfo);\n\n    pktgen.flags &= ~MAC_FROM_ARP_FLAG;\n\n    /* Make sure the port is active and enabled. */\n    if (pinfo->seq_pkt) {\n        pktgen_port_defaults(pinfo->pid);\n        pktgen_seq_defaults(pinfo->pid);\n\n        pktgen_range_setup(pinfo);\n        pktgen_clear_stats(pinfo);\n\n        enable_range(pinfo, estate(off));\n        enable_latency(pinfo, estate(off));\n        memset(pinfo->rnd_bitfields, 0, sizeof(struct rnd_bits_s));\n        pktgen_rnd_bits_init(&pinfo->rnd_bitfields);\n        pktgen_set_port_seqCnt(pinfo, 0);\n    } else\n        pktgen_log_info(\"No sequence packets allocated for port %u\", pinfo->pid);\n\n    pktgen_update_display();\n}\n\n/**\n *\n * pktgen_port_restart - Attempt to cleanup port state.\n *\n * DESCRIPTION\n * Reset all ports\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_port_restart(port_info_t *pinfo)\n{\n    if (pinfo == NULL)\n        pinfo = l2p_get_port_pinfo(0);\n\n    printf(\"Port %d attempt to stop/start PMD\\n\", pinfo->pid);\n\n    pktgen_set_receive_state(pinfo, 1);\n\n    pktgen_stop_transmitting(pinfo);\n\n    rte_delay_us_sleep(10 * 1000);\n\n    /* Stop and start the device to flush TX and RX buffers from the device rings. */\n    if (rte_eth_dev_stop(pinfo->pid) < 0)\n        printf(\"Unable to stop device %d\\n\", pinfo->pid);\n\n    rte_delay_us_sleep(250);\n\n    if (rte_eth_dev_start(pinfo->pid) < 0)\n        printf(\"Unable to start device %d\\n\", pinfo->pid);\n\n    pktgen_set_receive_state(pinfo, 0);\n\n    pktgen_update_display();\n}\n\n/**\n *\n * single_set_tx_count - Set the number of packets to transmit on a port.\n *\n * DESCRIPTION\n * Set the transmit count for all ports in the list.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_tx_count(port_info_t *pinfo, uint32_t cnt)\n{\n    rte_atomic64_set(&pinfo->transmit_count, cnt);\n}\n\n/**\n *\n * pktgen_set_port_seqCnt - Set the sequence count for a port\n *\n * DESCRIPTION\n * Set a sequence count of packets for all ports in the list.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_set_port_seqCnt(port_info_t *pinfo, uint32_t cnt)\n{\n    if (cnt > NUM_SEQ_PKTS)\n        cnt = NUM_SEQ_PKTS;\n\n    pinfo->seqCnt = cnt;\n    if (cnt) {\n        pktgen_clr_port_flags(pinfo, EXCLUSIVE_MODES);\n        pktgen_set_port_flags(pinfo, SEND_SEQ_PKTS);\n    } else {\n        pktgen_clr_port_flags(pinfo, EXCLUSIVE_MODES);\n        pktgen_set_port_flags(pinfo, SEND_SINGLE_PKTS);\n    }\n}\n\n/**\n *\n * pktgen_set_port_prime - Set the number of packets to send on a prime command\n *\n * DESCRIPTION\n * Set the number packets to send on the prime command for all ports in list.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_set_port_prime(port_info_t *pinfo, uint32_t cnt)\n{\n    if (cnt > MAX_PRIME_COUNT)\n        cnt = MAX_PRIME_COUNT;\n    else if (cnt == 0)\n        cnt = DEFAULT_PRIME_COUNT;\n\n    pinfo->prime_cnt = cnt;\n}\n\n/**\n *\n * pktgen_set_port_dump - Set the number of received packets to dump to screen.\n *\n * DESCRIPTION\n * Set the number of received packets to dump to screen.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\ndebug_set_port_dump(port_info_t *pinfo, uint32_t cnt)\n{\n    int i;\n\n    if (cnt > MAX_DUMP_PACKETS)\n        cnt = MAX_DUMP_PACKETS;\n\n    /* Prevent concurrency issues by setting the fields in this specific order */\n    pinfo->dump_count = 0;\n    pinfo->dump_tail  = 0;\n    pinfo->dump_head  = 0;\n\n    for (i = 0; i < MAX_DUMP_PACKETS; ++i)\n        if (pinfo->dump_list->data != NULL) {\n            rte_free(pinfo->dump_list->data);\n            pinfo->dump_list->data = NULL;\n        }\n\n    pinfo->dump_count = cnt;\n}\n\n/**\n *\n * single_set_tx_burst - Set the transmit burst count.\n *\n * DESCRIPTION\n * Set the transmit burst count for all packets.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_tx_burst(port_info_t *pinfo, uint32_t burst)\n{\n    if (burst == 0)\n        burst = 1;\n    else if (burst > MAX_PKT_TX_BURST)\n        burst = MAX_PKT_TX_BURST;\n    pinfo->tx_burst  = burst;\n    pinfo->tx_cycles = 0;\n\n    pktgen_packet_rate(pinfo);\n}\n\n/**\n *\n * single_set_rx_burst - Set the receive burst count.\n *\n * DESCRIPTION\n * Set the receive burst count for all packets.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_rx_burst(port_info_t *pinfo, uint32_t burst)\n{\n    if (burst == 0)\n        burst = 1;\n    else if (burst > MAX_PKT_RX_BURST)\n        burst = MAX_PKT_RX_BURST;\n    pinfo->rx_burst = burst;\n\n    pktgen_packet_rate(pinfo);\n}\n\n/**\n *\n * debug_set_tx_cycles - Set the number of Transmit cycles to use.\n *\n * DESCRIPTION\n * Set the number of transmit cycles for the given port list.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\ndebug_set_tx_cycles(port_info_t *pinfo, uint32_t cycles)\n{\n    pinfo->tx_cycles = cycles;\n}\n\n/**\n *\n * single_set_pkt_size - Set the size of the packets to send.\n *\n * DESCRIPTION\n * Set the pkt size for the single packet transmit.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_pkt_size(port_info_t *pinfo, uint16_t size)\n{\n    pkt_seq_t *pkt = &pinfo->seq_pkt[SINGLE_PKT];\n\n    if (size < RTE_ETHER_MIN_LEN)\n        size = RTE_ETHER_MIN_LEN;\n\n    if ((pktgen.flags & JUMBO_PKTS_FLAG) && size > RTE_ETHER_MAX_JUMBO_FRAME_LEN)\n        size = RTE_ETHER_MAX_JUMBO_FRAME_LEN;\n    else if (!(pktgen.flags & JUMBO_PKTS_FLAG) && size > RTE_ETHER_MAX_LEN)\n        size = RTE_ETHER_MAX_LEN;\n\n    if ((pkt->ethType == RTE_ETHER_TYPE_IPV6) && (size < MIN_v6_PKT_SIZE))\n        size = MIN_v6_PKT_SIZE;\n\n    pkt->pkt_size = (size - RTE_ETHER_CRC_LEN);\n\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n    pktgen_packet_rate(pinfo);\n}\n\n/**\n *\n * single_set_port_value - Set the port value for single or sequence packets.\n *\n * DESCRIPTION\n * Set the port value for single or sequence packets for the ports listed.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_port_value(port_info_t *pinfo, char type, uint32_t portValue)\n{\n    if (type == 'd')\n        pinfo->seq_pkt[SINGLE_PKT].dport = (uint16_t)portValue;\n    else\n        pinfo->seq_pkt[SINGLE_PKT].sport = (uint16_t)portValue;\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * single_set_tx_rate - Set the transmit rate as a percent value.\n *\n * DESCRIPTION\n * Set the transmit rate as a percent value for all ports listed.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_tx_rate(port_info_t *pinfo, const char *r)\n{\n    double rate = strtod(r, NULL);\n\n    if (rate == 0)\n        rate = 0.01;\n    else if (rate > 100.00)\n        rate = 100.00;\n    pinfo->tx_rate   = rate;\n    pinfo->tx_cycles = 0;\n\n    pktgen_packet_rate(pinfo);\n}\n\n/**\n *\n * single_set_ipaddr - Set the IP address for all ports listed\n *\n * DESCRIPTION\n * Set an IP address for all ports listed in the call.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_ipaddr(port_info_t *pinfo, char type, struct pg_ipaddr *ip, int ip_ver)\n{\n    pkt_seq_t *pkt = &pinfo->seq_pkt[SINGLE_PKT];\n\n    if (ip_ver == 4) {\n        if (type == 's') {\n            pkt->ip_mask                      = size_to_mask(ip->prefixlen);\n            pkt->ip_src_addr.addr.ipv4.s_addr = ntohl(ip->ipv4.s_addr);\n        } else if (type == 'd')\n            pkt->ip_dst_addr.addr.ipv4.s_addr = ntohl(ip->ipv4.s_addr);\n        else\n            return;\n\n        if (pkt->ethType != RTE_ETHER_TYPE_IPV4 && pkt->ethType != RTE_ETHER_TYPE_ARP)\n            single_set_pkt_type(pinfo, \"ipv4\");\n\n    } else if (ip_ver == 6) {\n        if (type == 's') {\n            pkt->ip_src_addr.prefixlen = ip->prefixlen;\n            rte_memcpy(&pkt->ip_src_addr.addr.ipv6, &ip->ipv6, sizeof(struct rte_ipv6_addr));\n        } else if (type == 'd')\n            rte_memcpy(&pkt->ip_dst_addr.addr.ipv6, &ip->ipv6, sizeof(struct rte_ipv6_addr));\n        else\n            return;\n\n        if (pkt->ethType != RTE_ETHER_TYPE_IPV6)\n            single_set_pkt_type(pinfo, \"ipv6\");\n    }\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\nuint16_t\ntcp_flags_from_str(const char *str)\n{\n    tcp_flags_t flag_list[] = TCP_FLAGS_LIST;\n    uint16_t flags          = 0;\n    char flags_str[128];\n    char *fields[16];\n    int num_fields;\n\n    memset(flags_str, 0, sizeof(flags_str));\n    strncpy(flags_str, str, sizeof(flags_str) - 1);\n\n    num_fields = rte_strsplit(flags_str, strlen(flags_str), fields, RTE_DIM(fields), ',');\n    for (int i = 0; i < num_fields; i++) {\n        if (!strcmp(fields[i], \"clr\")) {\n            flags = 0;\n            continue;\n        }\n        for (tcp_flags_t *flag = flag_list; flag->name; flag++) {\n            if (!strcmp(fields[i], flag->name)) {\n                flags |= flag->bit;\n                break;\n            }\n        }\n    }\n    return flags;\n}\n\nint\ntcp_str_from_flags(uint16_t flags, char *buf, size_t len)\n{\n    tcp_flags_t flag_list[] = TCP_FLAGS_LIST;\n    char *str               = NULL;\n    int n                   = 0;\n\n    if (buf == NULL || len < 4)\n        return -1;\n\n    memset(buf, 0, len);\n    if ((flags & TCP_FLAGS_MASK) == 0) {\n        strcpy(buf, \"clr\");\n        return 0;\n    }\n\n    for (tcp_flags_t *flag = flag_list; flag->name; flag++) {\n        if (flags & flag->bit) {\n            if (str) {\n                if (n + strlen(flag->name) + 1 >= len)\n                    return -1;\n                n = snprintf(str, len, \",%s\", flag->name);\n            } else {\n                str = buf;\n                if (n + strlen(flag->name) + 1 >= len)\n                    return -1;\n                n = snprintf(str, len, \"%s\", flag->name);\n            }\n            str += n;\n            len -= n;\n        }\n    }\n\n    return 0;\n}\n\n/**\n *\n * single_set_tcp_flags - Set a TCP flag\n *\n * DESCRIPTION\n * Set a TCP flag for the single packet.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_tcp_flags(port_info_t *pinfo, const char *flags)\n{\n    pinfo->seq_pkt[SINGLE_PKT].tcp_flags = tcp_flags_from_str(flags);\n}\n\n/**\n *\n * range_set_tcp_flags - Set a TCP flag\n *\n * DESCRIPTION\n * Set a TCP flag for the range packet.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_tcp_flags(port_info_t *pinfo, const char *flags)\n{\n    pinfo->range.tcp_flags              = tcp_flags_from_str(flags);\n    pinfo->seq_pkt[RANGE_PKT].tcp_flags = pinfo->range.tcp_flags;\n}\n\n/**\n *\n * seq_set_tcp_flags - Set a TCP flag\n *\n * DESCRIPTION\n * Set a TCP flag for the single packet.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nseq_set_tcp_flags(port_info_t *pinfo, uint32_t seqnum, const char *flags)\n{\n    pinfo->seq_pkt[seqnum].tcp_flags = tcp_flags_from_str(flags);\n}\n\n/**\n *\n * single_set_tcp_seq - Set TCP sequence number\n *\n * DESCRIPTION\n * Set TCP sequence number\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_tcp_seq(port_info_t *pinfo, uint32_t seq)\n{\n    pinfo->seq_pkt[SINGLE_PKT].tcp_seq = seq;\n}\n\n/**\n *\n * single_set_tcp_ack - Set TCP acknowledge number\n *\n * DESCRIPTION\n * Set TCP acknowledge number\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_tcp_ack(port_info_t *pinfo, uint32_t ack)\n{\n    pinfo->seq_pkt[SINGLE_PKT].tcp_ack = ack;\n}\n\n/**\n *\n * range_set_tcp_seq - Set TCP sequence numbers\n *\n * DESCRIPTION\n * Set TCP sequence numbers\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_tcp_seq(port_info_t *pinfo, char *what, uint32_t seq)\n{\n    char *str = what;\n    if (_cp(\"inc\") || _cp(\"increment\"))\n        pinfo->range.tcp_seq_inc = seq;\n    else if (_cp(\"min\") || _cp(\"minimum\"))\n        pinfo->range.tcp_seq_min = seq;\n    else if (_cp(\"max\") || _cp(\"maximum\"))\n        pinfo->range.tcp_seq_max = seq;\n    else if (_cp(\"start\"))\n        pinfo->range.tcp_seq = seq;\n}\n\n/**\n *\n * range_set_tcp_ack - Set TCP acknowledge numbers\n *\n * DESCRIPTION\n * Set TCP acknowledge numbers\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_tcp_ack(port_info_t *pinfo, char *what, uint32_t ack)\n{\n    char *str = what;\n    if (_cp(\"inc\") || _cp(\"increment\"))\n        pinfo->range.tcp_ack_inc = ack;\n    else if (_cp(\"min\") || _cp(\"minimum\"))\n        pinfo->range.tcp_ack_min = ack;\n    else if (_cp(\"max\") || _cp(\"maximum\"))\n        pinfo->range.tcp_ack_max = ack;\n    else if (_cp(\"start\"))\n        pinfo->range.tcp_ack = ack;\n}\n\n/**\n *\n * single_set_mac - Setup the MAC address\n *\n * DESCRIPTION\n * Set the MAC address for all ports given.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_mac(port_info_t *pinfo, const char *which, struct rte_ether_addr *mac)\n{\n    if (!strcmp(which, \"dst\")) {\n        rte_ether_addr_copy(mac, &pinfo->seq_pkt[SINGLE_PKT].eth_dst_addr);\n        pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n    } else if (!strcmp(which, \"src\")) {\n        rte_ether_addr_copy(mac, &pinfo->seq_pkt[SINGLE_PKT].eth_src_addr);\n        pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n    }\n}\n\n/**\n *\n * single_set_dst_mac - Setup the destination MAC address\n *\n * DESCRIPTION\n * Set the destination MAC address for all ports given.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_dst_mac(port_info_t *pinfo, struct rte_ether_addr *mac)\n{\n    rte_ether_addr_copy(mac, &pinfo->seq_pkt[SINGLE_PKT].eth_dst_addr);\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * single_set_src_mac - Setup the source MAC address\n *\n * DESCRIPTION\n * Set the source MAC address for all ports given.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_src_mac(port_info_t *pinfo, struct rte_ether_addr *mac)\n{\n    rte_ether_addr_copy(mac, &pinfo->seq_pkt[SINGLE_PKT].eth_src_addr);\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * single_set_ttl_ttl - Setup the Time to Live\n *\n * DESCRIPTION\n * Set the TTL  for all ports given.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_ttl_value(port_info_t *pinfo, uint8_t ttl)\n{\n    pinfo->seq_pkt[SINGLE_PKT].ttl = ttl;\n    pktgen_packet_ctor(pinfo, SINGLE_PKT, -1);\n}\n\n/**\n *\n * enable_range - Enable or disable range packet sending.\n *\n * DESCRIPTION\n * Enable or disable range packet sending.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_range(port_info_t *pinfo, uint32_t state)\n{\n    if (state == ENABLE_STATE) {\n        if (pktgen_tst_port_flags(pinfo, SENDING_PACKETS)) {\n            pktgen_log_warning(\"Cannot enable the range settings while sending packets!\");\n            return;\n        }\n        pktgen_clr_port_flags(pinfo, EXCLUSIVE_MODES);\n        pktgen_set_port_flags(pinfo, SEND_RANGE_PKTS);\n    } else {\n        pktgen_clr_port_flags(pinfo, EXCLUSIVE_MODES);\n        pktgen_set_port_flags(pinfo, SEND_SINGLE_PKTS);\n    }\n\n    pktgen_packet_rate(pinfo);\n}\n\n/**\n *\n * enable_latency - Enable or disable latency testing.\n *\n * DESCRIPTION\n * Enable or disable latency testing.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nenable_latency(port_info_t *pinfo, uint32_t state)\n{\n    if (state == ENABLE_STATE) {\n        pktgen_latency_setup(pinfo);\n\n        pktgen_set_port_flags(pinfo, SEND_LATENCY_PKTS);\n    } else\n        pktgen_clr_port_flags(pinfo, SEND_LATENCY_PKTS);\n}\n\n/**\n *\n * single_set_jitter - Set the jitter threshold.\n *\n * DESCRIPTION\n * Set the jitter threshold.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nsingle_set_jitter(port_info_t *pinfo, uint64_t threshold)\n{\n    latency_t *lat = &pinfo->latency;\n    uint64_t us_per_tick;\n\n    lat->jitter_threshold_us     = threshold;\n    lat->jitter_count            = 0;\n    us_per_tick                  = pktgen_get_timer_hz() / 1000000;\n    lat->jitter_threshold_cycles = lat->jitter_threshold_us * us_per_tick;\n}\n\n/**\n *\n * pattern_set_type - Set the pattern type per port.\n *\n * DESCRIPTION\n * Set the given pattern type.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npattern_set_type(port_info_t *pinfo, char *str)\n{\n    if (strncmp(str, \"abc\", 3) == 0)\n        pinfo->fill_pattern_type = ABC_FILL_PATTERN;\n    else if (strncmp(str, \"none\", 4) == 0)\n        pinfo->fill_pattern_type = NO_FILL_PATTERN;\n    else if (strncmp(str, \"user\", 4) == 0)\n        pinfo->fill_pattern_type = USER_FILL_PATTERN;\n    else if (strncmp(str, \"zero\", 4) == 0)\n        pinfo->fill_pattern_type = ZERO_FILL_PATTERN;\n}\n\n/**\n *\n * pattern_set_user_pattern - Set the user pattern string.\n *\n * DESCRIPTION\n * Set the given user pattern string.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npattern_set_user_pattern(port_info_t *pinfo, char *str)\n{\n    char copy[USER_PATTERN_SIZE + 1], *cp;\n\n    memset(copy, 0, sizeof(copy));\n    strcpy(copy, str);\n    cp = &copy[0];\n    if ((cp[0] == '\"') || (cp[0] == '\\'')) {\n        cp[strlen(cp) - 1] = 0;\n        cp++;\n    }\n    memset(pinfo->user_pattern, 0, USER_PATTERN_SIZE);\n    snprintf(pinfo->user_pattern, USER_PATTERN_SIZE, \"%s\", cp);\n    pinfo->fill_pattern_type = USER_FILL_PATTERN;\n}\n\n/**\n *\n * range_set_dest_mac - Set the destination MAC address\n *\n * DESCRIPTION\n * Set the destination MAC address for all ports given.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_dest_mac(port_info_t *pinfo, const char *what, struct rte_ether_addr *mac)\n{\n    if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n        inet_mtoh64(mac, &pinfo->range.dst_mac_min);\n    else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n        inet_mtoh64(mac, &pinfo->range.dst_mac_max);\n    else if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\"))\n        inet_mtoh64(mac, &pinfo->range.dst_mac_inc);\n    else if (!strcmp(what, \"start\")) {\n        inet_mtoh64(mac, &pinfo->range.dst_mac);\n        /* Changes add below to reflect MAC value in range */\n        rte_ether_addr_copy(mac, &pinfo->seq_pkt[RANGE_PKT].eth_dst_addr);\n    }\n}\n\n/**\n *\n * range_set_src_mac - Set the source MAC address for the ports.\n *\n * DESCRIPTION\n * Set the source MAC address for the ports given in the list.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_src_mac(port_info_t *pinfo, const char *what, struct rte_ether_addr *mac)\n{\n    if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n        inet_mtoh64(mac, &pinfo->range.src_mac_min);\n    else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n        inet_mtoh64(mac, &pinfo->range.src_mac_max);\n    else if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\"))\n        inet_mtoh64(mac, &pinfo->range.src_mac_inc);\n    else if (!strcmp(what, \"start\")) {\n        inet_mtoh64(mac, &pinfo->range.src_mac);\n        /* Changes add below to reflect MAC value in range */\n        rte_ether_addr_copy(mac, &pinfo->seq_pkt[RANGE_PKT].eth_src_addr);\n    }\n}\n\n/**\n *\n * range_set_src_ip - Set the source IP address value.\n *\n * DESCRIPTION\n * Set the source IP address for all of the ports listed.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_src_ip(port_info_t *pinfo, char *what, struct pg_ipaddr *ip)\n{\n    if (pinfo->seq_pkt[RANGE_PKT].ethType == RTE_ETHER_TYPE_IPV6) {\n        if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n            rte_memcpy(pinfo->range.src_ipv6_min, &ip->ipv6, sizeof(struct rte_ipv6_addr));\n        else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n            rte_memcpy(pinfo->range.src_ipv6_max, &ip->ipv6, sizeof(struct rte_ipv6_addr));\n        else if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\"))\n            rte_memcpy(pinfo->range.src_ipv6_inc, &ip->ipv6, sizeof(struct rte_ipv6_addr));\n        else if (!strcmp(what, \"start\"))\n            rte_memcpy(pinfo->range.src_ipv6, &ip->ipv6, sizeof(struct rte_ipv6_addr));\n    } else {\n        if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n            pinfo->range.src_ip_min = ntohl(ip->ipv4.s_addr);\n        else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n            pinfo->range.src_ip_max = ntohl(ip->ipv4.s_addr);\n        else if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\"))\n            pinfo->range.src_ip_inc = ntohl(ip->ipv4.s_addr);\n        else if (!strcmp(what, \"start\"))\n            pinfo->range.src_ip = ntohl(ip->ipv4.s_addr);\n    }\n}\n\n/**\n *\n * range_set_dst_ip - Set the destination IP address values\n *\n * DESCRIPTION\n * Set the destination IP address values.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_dst_ip(port_info_t *pinfo, char *what, struct pg_ipaddr *ip)\n{\n    if (pinfo->seq_pkt[RANGE_PKT].ethType == RTE_ETHER_TYPE_IPV6) {\n        if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n            rte_memcpy(pinfo->range.dst_ipv6_min, &ip->ipv6, sizeof(struct rte_ipv6_addr));\n        else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n            rte_memcpy(pinfo->range.dst_ipv6_max, &ip->ipv6, sizeof(struct rte_ipv6_addr));\n        else if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\"))\n            rte_memcpy(pinfo->range.dst_ipv6_inc, &ip->ipv6, sizeof(struct rte_ipv6_addr));\n        else if (!strcmp(what, \"start\"))\n            rte_memcpy(pinfo->range.dst_ipv6, &ip->ipv6, sizeof(struct rte_ipv6_addr));\n    } else {\n        if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n            pinfo->range.dst_ip_min = ntohl(ip->ipv4.s_addr);\n        else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n            pinfo->range.dst_ip_max = ntohl(ip->ipv4.s_addr);\n        else if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\"))\n            pinfo->range.dst_ip_inc = ntohl(ip->ipv4.s_addr);\n        else if (!strcmp(what, \"start\"))\n            pinfo->range.dst_ip = ntohl(ip->ipv4.s_addr);\n    }\n}\n\n/**\n *\n * range_set_src_port - Set the source IP port number for the ports\n *\n * DESCRIPTION\n * Set the source IP port number for the ports listed.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_src_port(port_info_t *pinfo, char *what, uint16_t port)\n{\n    if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\")) {\n        if (port > 64)\n            port = 64;\n        pinfo->range.src_port_inc = port;\n    } else {\n        if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n            pinfo->range.src_port_min = port;\n        else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n            pinfo->range.src_port_max = port;\n        else if (!strcmp(what, \"start\"))\n            pinfo->range.src_port = port;\n    }\n}\n\n/**\n *\n * range_set_gtpu_teid - Set the TEID for GTPU header\n *\n * DESCRIPTION\n * Set the GTP-U TEID for the ports listed.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_gtpu_teid(port_info_t *pinfo, char *what, uint32_t teid)\n{\n    if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\")) {\n        if (teid != 0)\n            pinfo->range.gtpu_teid_inc = teid;\n    } else {\n        if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n            pinfo->range.gtpu_teid_min = teid;\n        else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n            pinfo->range.gtpu_teid_max = teid;\n        else if (!strcmp(what, \"start\")) {\n            pinfo->range.gtpu_teid              = teid;\n            pinfo->seq_pkt[RANGE_PKT].gtpu_teid = teid;\n        }\n    }\n}\n\n/**\n *\n * range_set_dst_port - Set the destination port value\n *\n * DESCRIPTION\n * Set the destination port values.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_dst_port(port_info_t *pinfo, char *what, uint16_t port)\n{\n    if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\")) {\n        if (port > 64)\n            port = 64;\n        pinfo->range.dst_port_inc = port;\n    } else {\n        if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n            pinfo->range.dst_port_min = port;\n        else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n            pinfo->range.dst_port_max = port;\n        else if (!strcmp(what, \"start\"))\n            pinfo->range.dst_port = port;\n    }\n}\n\n/**\n *\n * range_set_ttl - Set the ttl value\n *\n * DESCRIPTION\n * Set the Time to Live values\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_ttl(port_info_t *pinfo, char *what, uint8_t ttl)\n{\n    if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\"))\n        pinfo->range.ttl_inc = ttl;\n    else if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n        pinfo->range.ttl_min = ttl;\n    else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n        pinfo->range.ttl_max = ttl;\n    else if (!strcmp(what, \"start\"))\n        pinfo->range.ttl = ttl;\n}\n\n/**\n *\n * range_set_hop_limits - Set the hop_limits value\n *\n * DESCRIPTION\n * Set the Hop Limits values\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_hop_limits(port_info_t *pinfo, char *what, uint8_t hop_limits)\n{\n    if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\"))\n        pinfo->range.hop_limits_inc = hop_limits;\n    else if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n        pinfo->range.hop_limits_min = hop_limits;\n    else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n        pinfo->range.hop_limits_max = hop_limits;\n    else if (!strcmp(what, \"start\"))\n        pinfo->range.hop_limits = hop_limits;\n}\n\n/**\n *\n * range_set_vlan_id - Set the VLAN id value\n *\n * DESCRIPTION\n * Set the VLAN id values.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_vlan_id(port_info_t *pinfo, char *what, uint16_t id)\n{\n    if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\")) {\n        if (id > 64)\n            id = 64;\n        pinfo->range.vlan_id_inc = id;\n    } else {\n        if ((id < MIN_VLAN_ID) || (id > MAX_VLAN_ID))\n            id = MIN_VLAN_ID;\n\n        if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n            pinfo->range.vlan_id_min = id;\n        else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n            pinfo->range.vlan_id_max = id;\n        else if (!strcmp(what, \"start\"))\n            pinfo->range.vlan_id = id;\n    }\n}\n\n/**\n *\n * range_set_tos_id - Set the tos value\n *\n * DESCRIPTION\n * Set the tos values.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_tos_id(port_info_t *pinfo, char *what, uint8_t id)\n{\n\n    if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\")) {\n        pinfo->range.tos_inc = id;\n    } else {\n        if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n            pinfo->range.tos_min = id;\n        else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n            pinfo->range.tos_max = id;\n        else if (!strcmp(what, \"start\"))\n            pinfo->range.tos = id;\n    }\n}\n\n/**\n *\n * range_set_traffic_class - Set the traffic class value\n *\n * DESCRIPTION\n * Set the traffic class value.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_traffic_class(port_info_t *pinfo, char *what, uint8_t traffic_class)\n{\n\n    if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\")) {\n        pinfo->range.traffic_class_inc = traffic_class;\n    } else {\n        if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n            pinfo->range.traffic_class_min = traffic_class;\n        else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n            pinfo->range.traffic_class_max = traffic_class;\n        else if (!strcmp(what, \"start\"))\n            pinfo->range.traffic_class = traffic_class;\n    }\n}\n\n/**\n *\n * range_set_cos_id - Set the prio (cos) value\n *\n * DESCRIPTION\n * Set the prio (cos) values.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nrange_set_cos_id(port_info_t *pinfo, char *what, uint8_t id)\n{\n    if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\")) {\n        if (id > 7)\n            id = 7;\n        pinfo->range.cos_inc = id;\n    } else {\n        if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n            pinfo->range.cos_min = id;\n        else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n            pinfo->range.cos_max = id;\n        else if (!strcmp(what, \"start\"))\n            pinfo->range.cos = id;\n    }\n}\n\n/**\n *\n * range_set_pkt_size - Set the Packet size value\n *\n * DESCRIPTION\n * Set the packet size values.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\nrange_set_pkt_size(port_info_t *pinfo, char *what, uint16_t size)\n{\n    if (!strcmp(what, \"inc\") || !strcmp(what, \"increment\")) {\n        if ((pktgen.flags & JUMBO_PKTS_FLAG) && (size > RTE_ETHER_MAX_JUMBO_FRAME_LEN))\n            size = RTE_ETHER_MAX_JUMBO_FRAME_LEN;\n        pinfo->range.pkt_size_inc = size;\n    } else {\n        if (size < RTE_ETHER_MIN_LEN)\n            size = RTE_ETHER_MIN_LEN;\n\n        const uint32_t max_size = (pktgen.flags & JUMBO_PKTS_FLAG) ? RTE_ETHER_MAX_JUMBO_FRAME_LEN\n                                                                   : RTE_ETHER_MAX_LEN;\n        if (size > max_size)\n            size = max_size;\n\n        if (pinfo->seq_pkt[RANGE_PKT].ethType == RTE_ETHER_TYPE_IPV6 && size < MIN_v6_PKT_SIZE)\n            size = MIN_v6_PKT_SIZE;\n\n        if (!strcmp(what, \"start\"))\n            pinfo->range.pkt_size = (size - RTE_ETHER_CRC_LEN);\n        else if (!strcmp(what, \"min\") || !strcmp(what, \"minimum\"))\n            pinfo->range.pkt_size_min = (size - RTE_ETHER_CRC_LEN);\n        else if (!strcmp(what, \"max\") || !strcmp(what, \"maximum\"))\n            pinfo->range.pkt_size_max = (size - RTE_ETHER_CRC_LEN);\n    }\n}\n\n/**\n *\n * pktgen_send_arp_requests - Send an ARP request for a given port.\n *\n * DESCRIPTION\n * Using the port list do an ARp send for all ports.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_send_arp_requests(port_info_t *pinfo, uint32_t type)\n{\n    if (type == GRATUITOUS_ARP)\n        pktgen_set_port_flags(pinfo, SEND_GRATUITOUS_ARP);\n    else\n        pktgen_set_port_flags(pinfo, SEND_ARP_REQUEST);\n}\n\n/**\n *\n * pktgen_set_page - Set the page type to display\n *\n * DESCRIPTION\n * Set the page type to display\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_set_page(char *str)\n{\n    uint16_t page      = 0;\n    port_info_t *pinfo = NULL;\n\n    if (str == NULL)\n        return;\n\n    if ((str[0] >= '0') && (str[0] <= '9')) {\n        page = atoi(str);\n        if (page > pktgen.nb_ports)\n            return;\n    }\n    pinfo             = l2p_get_port_pinfo(pktgen.curr_port);\n    pcap_info_t *pcap = l2p_get_pcap(pinfo->pid);\n\n    /* Switch to the correct page */\n    if (_cp(\"next\")) {\n\n        if (pcap) {\n            if ((pcap->pkt_index + PCAP_PAGE_SIZE) < pcap->pkt_count)\n                pcap->pkt_index += PCAP_PAGE_SIZE;\n            else\n                pcap->pkt_index = 0;\n        }\n        pktgen.flags |= PRINT_LABELS_FLAG;\n    } else if (_cp(\"system\") || _cp(\"sys\")) {\n        pktgen.flags &= ~PAGE_MASK_BITS;\n        pktgen.flags |= SYSTEM_PAGE_FLAG;\n    } else if (_cp(\"range\")) {\n        pktgen.flags &= ~PAGE_MASK_BITS;\n        pktgen.flags |= RANGE_PAGE_FLAG;\n    } else if (_cp(\"cpu\")) {\n        pktgen.flags &= ~PAGE_MASK_BITS;\n        pktgen.flags |= CPU_PAGE_FLAG;\n    } else if (_cp(\"stats\") || _cp(\"qstats\")) {\n        pktgen.flags &= ~PAGE_MASK_BITS;\n        pktgen.flags |= QSTATS_PAGE_FLAG;\n    } else if (_cp(\"xstats\")) {\n        pktgen.flags &= ~PAGE_MASK_BITS;\n        pktgen.flags |= XSTATS_PAGE_FLAG;\n    } else if (_cp(\"sequence\") || _cp(\"seq\")) {\n        pktgen.flags &= ~PAGE_MASK_BITS;\n        pktgen.flags |= SEQUENCE_PAGE_FLAG;\n    } else if (_cp(\"random\") || _cp(\"rnd\")) {\n        pktgen.flags &= ~PAGE_MASK_BITS;\n        pktgen.flags |= RND_BITFIELD_PAGE_FLAG;\n    } else if (_cp(\"log\")) {\n        pktgen.flags &= ~PAGE_MASK_BITS;\n        pktgen.flags |= LOG_PAGE_FLAG;\n    } else if (_cp(\"latency\") || _cp(\"lat\")) {\n        pktgen.flags &= ~PAGE_MASK_BITS;\n        pktgen.flags |= LATENCY_PAGE_FLAG;\n    } else {\n        uint16_t start_port;\n\n        if (_cp(\"main\"))\n            page = 0;\n        start_port = (page * pktgen.nb_ports_per_page);\n        if ((pktgen.starting_port != start_port) && (start_port < pktgen.nb_ports)) {\n            pktgen.starting_port = start_port;\n            pktgen.ending_port   = start_port + pktgen.nb_ports_per_page;\n            if (pktgen.ending_port > (pktgen.starting_port + pktgen.nb_ports))\n                pktgen.ending_port = (pktgen.starting_port + pktgen.nb_ports);\n        }\n        if (pktgen.flags & PAGE_MASK_BITS) {\n            pktgen.flags &= ~PAGE_MASK_BITS;\n            pktgen.flags |= (MAIN_PAGE_FLAG | PRINT_LABELS_FLAG);\n        }\n    }\n    pktgen_clear_display();\n}\n\n/**\n *\n * pktgen_set_seq - Set a sequence packet for given port\n *\n * DESCRIPTION\n * Set the sequence packet information for all ports listed.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_set_seq(port_info_t *pinfo, uint32_t seqnum, struct rte_ether_addr *daddr,\n               struct rte_ether_addr *saddr, struct pg_ipaddr *ip_daddr, struct pg_ipaddr *ip_saddr,\n               uint32_t sport, uint32_t dport, char type, char proto, uint16_t vlanid,\n               uint32_t pktsize, uint32_t gtpu_teid)\n{\n    pkt_seq_t *pkt;\n\n    pkt = &pinfo->seq_pkt[seqnum];\n    rte_ether_addr_copy(daddr, &pkt->eth_dst_addr);\n    rte_ether_addr_copy(saddr, &pkt->eth_src_addr);\n    pkt->ip_mask = size_to_mask(ip_saddr->prefixlen);\n    if (type == '4') {\n        pkt->ip_src_addr.addr.ipv4.s_addr = htonl(ip_saddr->ipv4.s_addr);\n        pkt->ip_dst_addr.addr.ipv4.s_addr = htonl(ip_daddr->ipv4.s_addr);\n    } else {\n        memcpy(&pkt->ip_src_addr.addr.ipv6, &ip_saddr->ipv6, sizeof(struct rte_ipv6_addr));\n        memcpy(&pkt->ip_dst_addr.addr.ipv6, &ip_daddr->ipv6, sizeof(struct rte_ipv6_addr));\n    }\n    pkt->dport    = dport;\n    pkt->sport    = sport;\n    pkt->pkt_size = pktsize - RTE_ETHER_CRC_LEN;\n    pkt->ipProto  = (proto == 'u')   ? PG_IPPROTO_UDP\n                    : (proto == 'i') ? PG_IPPROTO_ICMP\n                                     : PG_IPPROTO_TCP;\n    /* Force the IP protocol to IPv4 if this is a ICMP packet. */\n    if (proto == 'i')\n        type = '4';\n    pkt->ethType   = (type == '6') ? RTE_ETHER_TYPE_IPV6 : RTE_ETHER_TYPE_IPV4;\n    pkt->vlanid    = vlanid;\n    pkt->gtpu_teid = gtpu_teid;\n    pktgen_packet_ctor(pinfo, seqnum, -1);\n}\n\nvoid\npktgen_set_cos_tos_seq(port_info_t *pinfo, uint32_t seqnum, uint32_t cos, uint32_t tos)\n{\n    pkt_seq_t *pkt;\n\n    pkt      = &pinfo->seq_pkt[seqnum];\n    pkt->cos = cos;\n    pkt->tos = tos;\n    pktgen_packet_ctor(pinfo, seqnum, -1);\n}\n\nvoid\npktgen_set_vxlan_seq(port_info_t *pinfo, uint32_t seqnum, uint32_t flag, uint32_t gid, uint32_t vid)\n{\n    pkt_seq_t *pkt;\n\n    pkt            = &pinfo->seq_pkt[seqnum];\n    pkt->vni_flags = flag;\n    pkt->group_id  = gid;\n    pkt->vxlan_id  = vid;\n    pktgen_packet_ctor(pinfo, seqnum, -1);\n}\n\n/**\n *\n * pktgen_quit - Exit pktgen.\n *\n * DESCRIPTION\n * Close and exit Pktgen.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_quit(void)\n{\n    cli_quit();\n}\n\nstatic void\n_pcap_file_close(port_info_t *port)\n{\n    pktgen_close_pcap_file(port->pcap_file);\n    port->pcap_file = NULL;\n}\n\nstatic void\n_pcap_file_open(port_info_t *port, char *filename)\n{\n    _pcap_file_close(port);\n\n    port->pcap_file = pktgen_create_pcap_file(filename);\n}\n\nvoid\npktgen_pcap_handler(port_info_t *pinfo, uint32_t state)\n{\n    if (state == ENABLE_STATE) {\n        char filename[64];\n\n        snprintf(filename, sizeof(filename), \"tx-%d.pcap\", pinfo->pid);\n        _pcap_file_open(pinfo, filename);\n    } else if (state == DISABLE_STATE) {\n        _pcap_file_close(pinfo);\n    }\n}\n"
  },
  {
    "path": "app/pktgen-cmds.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_CMDS_H_\n#define _PKTGEN_CMDS_H_\n\n/**\n * @file\n *\n * Pktgen command API: all functions invoked by the CLI and Lua scripting layer.\n *\n * Covers internal display helpers, global port control, single/range/sequence\n * packet configuration, debug utilities, enable/disable toggles, PCAP handling,\n * and TCP flag parsing helpers.\n */\n\n#include <inttypes.h>\n#include <rte_version.h>\n\n#include <rte_net.h>\n\n#include \"pktgen.h\"\n#include <rte_string_fns.h>\n#include <portlist.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Internal APIs */\n\n/** Format the active port flags as a human-readable string. */\nchar *pktgen_flags_string(port_info_t *pinfo);\n\n/** Format the TX count and rate for a port into @p buff. */\nchar *pktgen_transmit_count_rate(int port, char *buff, int len);\n\n/** Schedule a display refresh on the next timer tick. */\nvoid pktgen_update_display(void);\n\n/** Refresh port statistics and redraw the active display page. */\nvoid pktgen_update(void);\n\n/** Format the link state for a port into @p buff. */\nchar *pktgen_link_state(int port, char *buff, int len);\n\n/** Format the TX packet count for a port into @p buff. */\nchar *pktgen_transmit_count(int port, char *buff, int len);\n\n/** Format the TX rate percentage for a port into @p buff. */\nchar *pktgen_transmit_rate(int port, char *buff, int len);\n\n/**\n * Copy the current statistics for a port into a caller-supplied struct.\n *\n * @return\n *   0 on success, negative on invalid port.\n */\nint pktgen_port_stats(int port, port_stats_t *pstats);\n\n/* Global commands */\n\n/**\n * Send an ARP request of the given type on a port.\n *\n * @param pinfo\n *   Per-port state.\n * @param type\n *   ARP type flag (SEND_ARP_REQUEST or SEND_GRATUITOUS_ARP).\n */\nvoid pktgen_send_arp_requests(port_info_t *pinfo, uint32_t type);\n\n/** Start packet transmission on a port. */\nvoid pktgen_start_transmitting(port_info_t *pinfo);\n\n/** Stop packet transmission on a port. */\nvoid pktgen_stop_transmitting(port_info_t *pinfo);\n\n/** Return non-zero if a port is currently transmitting. */\nint pktgen_port_transmitting(int port);\n\n/** Switch the active display page (e.g. \"main\", \"range\", \"seq\"). */\nvoid pktgen_set_page(char *str);\n\n/** Enable or disable the terminal screen. */\nvoid pktgen_screen(int state);\n\n/** Force an immediate display update regardless of the timer tick. */\nvoid pktgen_force_update(void);\n\nvoid pktgen_update_display(void);\n\n/** Clear and redraw the display screen. */\nvoid pktgen_clear_display(void);\n\n/** Start or stop the latency sampler on a port based on @p state. */\nvoid pktgen_start_stop_latency_sampler(port_info_t *pinfo, uint32_t state);\n\n/** Start the latency sampler on a port. */\nvoid pktgen_start_latency_sampler(port_info_t *pinfo);\n\n/** Stop the latency sampler on a port. */\nvoid pktgen_stop_latency_sampler(port_info_t *pinfo);\n\n/**\n * Save the current pktgen configuration to a file.\n *\n * @param path\n *   Destination file path.\n * @return\n *   0 on success, negative on error.\n */\nint pktgen_save(char *path);\n\n/** Clear the terminal screen. */\nvoid pktgen_cls(void);\n\n/** Send an IPv4 ping (ICMP Echo Request) on a port. */\nvoid pktgen_ping4(port_info_t *pinfo);\n\n#ifdef INCLUDE_PING6\n/** Send an IPv6 ping (ICMPv6 Echo Request) on a port. */\nvoid pktgen_ping6(port_info_t *pinfo);\n#endif\n\n/** Clear accumulated statistics for a port. */\nvoid pktgen_clear_stats(port_info_t *pinfo);\n\n/** Reset a port's configuration to factory defaults. */\nvoid pktgen_reset(port_info_t *pinfo);\n\n/** Stop and restart the Ethernet device for a port. */\nvoid pktgen_port_restart(port_info_t *pinfo);\n\n/** Enable or disable learning the source MAC address from received ARP replies. */\nvoid pktgen_mac_from_arp(int state);\n\n/** Send a burst of priming packets to pre-fill downstream buffers. */\nvoid pktgen_prime_ports(port_info_t *pinfo);\n\n/** Terminate pktgen cleanly. */\nvoid pktgen_quit(void);\n\n/** Set the number of ports displayed per screen page. */\nvoid pktgen_set_page_size(uint32_t page_size);\n\n/** Set the currently displayed port number. */\nvoid pktgen_set_port_number(uint16_t port_number);\n\n/** Set the prime-burst packet count for a port. */\nvoid pktgen_set_port_prime(port_info_t *pinfo, uint32_t cnt);\n\n/** Reset per-port settings for a port to their defaults. */\nvoid pktgen_port_defaults(uint16_t pid);\n\n/** Reset all sequence-packet slots for a port to their defaults. */\nvoid pktgen_seq_defaults(uint16_t pid);\n\nstruct pg_ipaddr;\n\n/* Single */\n\n/**\n * Set the source or destination IP address for single-packet mode.\n *\n * @param pinfo   Per-port state.\n * @param type    's' for source, 'd' for destination.\n * @param ip      Parsed IP address.\n * @param ip_ver  4 for IPv4, 6 for IPv6.\n */\nvoid single_set_ipaddr(port_info_t *pinfo, char type, struct pg_ipaddr *ip, int ip_ver);\n\n/** Set the transport protocol (\"tcp\", \"udp\", or \"icmp\"). */\nvoid single_set_proto(port_info_t *pinfo, char *type);\n\n/** Set the TCP initial sequence number. */\nvoid single_set_tcp_seq(port_info_t *pinfo, uint32_t seq);\n\n/** Set the TCP initial acknowledgement number. */\nvoid single_set_tcp_ack(port_info_t *pinfo, uint32_t ack);\n\n/** Set TCP flags from a string (e.g. \"syn\", \"ack\", \"fin\"). */\nvoid single_set_tcp_flags(port_info_t *pinfo, const char *flags);\n\n/** Set the VLAN ID for single-packet mode. */\nvoid single_set_vlan_id(port_info_t *pinfo, uint16_t vlanid);\n\n/** Set the CoS / 802.1p priority value. */\nvoid single_set_cos(port_info_t *pinfo, uint8_t cos);\n\n/** Set the ToS (DSCP) byte value. */\nvoid single_set_tos(port_info_t *pinfo, uint8_t tos);\n\n/**\n * Set a MAC address for single-packet mode.\n *\n * @param which  \"src\" or \"dst\".\n */\nvoid single_set_mac(port_info_t *pinfo, const char *which, struct rte_ether_addr *mac);\n\n/** Set the destination MAC address. */\nvoid single_set_dst_mac(port_info_t *pinfo, struct rte_ether_addr *mac);\n\n/** Set the source MAC address. */\nvoid single_set_src_mac(port_info_t *pinfo, struct rte_ether_addr *mac);\n\n/** Set the packet type (\"ipv4\", \"ipv6\", \"vlan\", etc.). */\nvoid single_set_pkt_type(port_info_t *pinfo, const char *type);\n\n/** Set the TX packet count (0 means send forever). */\nvoid single_set_tx_count(port_info_t *pinfo, uint32_t cnt);\n\n/** Set the TX burst size. */\nvoid single_set_tx_burst(port_info_t *pinfo, uint32_t burst);\n\n/** Set the RX burst size. */\nvoid single_set_rx_burst(port_info_t *pinfo, uint32_t burst);\n\n/** Set the packet size in bytes (excluding FCS). */\nvoid single_set_pkt_size(port_info_t *pinfo, uint16_t size);\n\n/** Set the TX rate as a percentage string (e.g. \"100\" or \"50.5\"). */\nvoid single_set_tx_rate(port_info_t *pinfo, const char *rate);\n\n/** Set the jitter threshold in microseconds. */\nvoid single_set_jitter(port_info_t *pinfo, uint64_t threshold);\n\n/** Set the IP TTL / hop-limit value. */\nvoid single_set_ttl_value(port_info_t *pinfo, uint8_t ttl);\n\n/**\n * Set the source or destination L4 port number.\n *\n * @param type       's' for source, 'd' for destination.\n * @param portValue  Port number to set.\n */\nvoid single_set_port_value(port_info_t *pinfo, char type, uint32_t portValue);\n\n/** Set the outer and inner VLAN IDs for Q-in-Q mode. */\nvoid single_set_qinqids(port_info_t *pinfo, uint16_t outerid, uint16_t innerid);\n\n/** Set VxLAN tunnel flags, group ID, and VNI. */\nvoid single_set_vxlan(port_info_t *pinfo, uint16_t flags, uint16_t group_id, uint32_t vxlan_id);\n\n/**\n * Configure the latency sampler for a port.\n *\n * @param type           Sampler type string (\"simple\" or \"poisson\").\n * @param num_samples    Number of samples to collect.\n * @param sampling_rate  Sampling rate in packets per second.\n * @param outfile        Path to write the sample data, or empty for none.\n */\nvoid single_set_latsampler_params(port_info_t *pinfo, char *type, uint32_t num_samples,\n                                  uint32_t sampling_rate, char outfile[]);\n\n/* Debug */\n\n/** Dump port debug information to the log. */\nvoid debug_dump(port_info_t *pinfo, char *str);\n\n/** Enable or disable port LED blinking. */\nvoid debug_blink(port_info_t *pinfo, uint32_t state);\n\n/** Override the TX inter-burst cycle count for debugging. */\nvoid debug_set_tx_cycles(port_info_t *pinfo, uint32_t cycles);\n\n/** Override the RX poll cycle count for debugging. */\nvoid debug_set_rx_cycles(port_info_t *pinfo, uint32_t cycles);\n\n/** Dump the full L2P lcore-to-port matrix to the log. */\nvoid debug_matrix_dump(void);\n\n/** Dump mempool statistics for a named pool on a port. */\nvoid debug_mempool_dump(port_info_t *pinfo, char *name);\n\n/** Enable raw packet dump for the next @p cnt received packets. */\nvoid debug_set_port_dump(port_info_t *pinfo, uint32_t cnt);\n\n/** Print TX rate debug counters for a port. */\nvoid debug_tx_rate(port_info_t *pinfo);\n\n/** Enable or disable PCAP file replay mode on a port. */\nvoid pktgen_pcap_handler(port_info_t *pinfo, uint32_t state);\n\n#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_NET_BOND)\n/** Display the bonding driver mode for a port. */\nvoid show_bonding_mode(port_info_t *pinfo);\n#endif\n\n/* Enable or toggle types */\n\n/** Enable or disable the RX TAP interface. */\nvoid enable_rx_tap(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable the TX TAP interface. */\nvoid enable_tx_tap(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable VLAN tagging. */\nvoid enable_vlan(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable VxLAN encapsulation. */\nvoid enable_vxlan(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable Q-in-Q double VLAN tagging. */\nvoid enable_qinq(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable MPLS label insertion. */\nvoid enable_mpls(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable GRE IPv4 encapsulation. */\nvoid enable_gre(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable GRE Ethernet frame encapsulation. */\nvoid enable_gre_eth(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable ICMP Echo reply processing. */\nvoid enable_icmp_echo(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable random source IP address generation. */\nvoid enable_rnd_s_ip(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable random source port generation. */\nvoid enable_rnd_s_pt(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable random bitfield packet mode. */\nvoid enable_random(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable latency measurement packet injection. */\nvoid enable_latency(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable global MAC-from-ARP learning. */\nvoid enable_mac_from_arp(uint32_t state);\n\n/** Enable or disable clock_gettime() as the time source (vs rdtsc). */\nvoid enable_clock_gettime(uint32_t state);\n\n/** Enable or disable input packet processing (ARP/ICMP handling). */\nvoid enable_process(port_info_t *pinfo, int state);\n\n/** Enable or disable packet capture to memory. */\nvoid enable_capture(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable range-mode packet generation. */\nvoid enable_range(port_info_t *pinfo, uint32_t state);\n\n/** Enable or disable PCAP file replay mode. */\nvoid enable_pcap(port_info_t *pinfo, uint32_t state);\n\n#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_NET_BOND)\n/** Enable or disable the bonding driver TX-zero-packets workaround. */\nvoid enable_bonding(port_info_t *pinfo, uint32_t state);\n#endif\n\n/* PCAP */\n\n/** Set a BPF filter string for PCAP packet capture. */\nvoid pcap_filter(port_info_t *pinfo, char *str);\n\n/* Range commands */\n\n/**\n * Set start, minimum, or maximum destination MAC in range mode.\n *\n * @param what  \"start\", \"min\", \"max\", or \"inc\".\n */\nvoid range_set_dest_mac(port_info_t *pinfo, const char *what, struct rte_ether_addr *mac);\n\n/** Set start, minimum, maximum, or increment for the source MAC in range mode. */\nvoid range_set_src_mac(port_info_t *pinfo, const char *what, struct rte_ether_addr *mac);\n\n/** Set start, minimum, maximum, or increment for the source IP in range mode. */\nvoid range_set_src_ip(port_info_t *pinfo, char *what, struct pg_ipaddr *ip);\n\n/** Set start, minimum, maximum, or increment for the destination IP in range mode. */\nvoid range_set_dst_ip(port_info_t *pinfo, char *what, struct pg_ipaddr *ip);\n\n/** Set start, minimum, maximum, or increment for the source L4 port in range mode. */\nvoid range_set_src_port(port_info_t *pinfo, char *what, uint16_t port);\n\n/** Set start, minimum, maximum, or increment for the destination L4 port in range mode. */\nvoid range_set_dst_port(port_info_t *pinfo, char *what, uint16_t port);\n\n/** Set the transport protocol for range mode (\"tcp\" or \"udp\"). */\nvoid range_set_proto(port_info_t *pinfo, const char *type);\n\n/** Set the packet type for range mode. */\nvoid range_set_pkt_type(port_info_t *pinfo, const char *type);\n\n/** Set TCP flags for range mode from a string. */\nvoid range_set_tcp_flags(port_info_t *pinfo, const char *flags);\n\n/** Set start, minimum, maximum, or increment for the TCP sequence number in range mode. */\nvoid range_set_tcp_seq(port_info_t *pinfo, char *what, uint32_t seq);\n\n/** Set start, minimum, maximum, or increment for the TCP acknowledgement number in range mode. */\nvoid range_set_tcp_ack(port_info_t *pinfo, char *what, uint32_t ack);\n\n/** Set start, minimum, maximum, or increment for the packet size in range mode. */\nvoid range_set_pkt_size(port_info_t *pinfo, char *what, uint16_t size);\n\n/** Set start, minimum, maximum, or increment for the GTP-U TEID in range mode. */\nvoid range_set_gtpu_teid(port_info_t *pinfo, char *what, uint32_t teid);\n\n/** Set start, minimum, maximum, or increment for the VLAN ID in range mode. */\nvoid range_set_vlan_id(port_info_t *pinfo, char *what, uint16_t id);\n\n/** Set start, minimum, maximum, or increment for the ToS byte in range mode. */\nvoid range_set_tos_id(port_info_t *pinfo, char *what, uint8_t id);\n\n/** Set start, minimum, maximum, or increment for the CoS value in range mode. */\nvoid range_set_cos_id(port_info_t *pinfo, char *what, uint8_t id);\n\n/** Set the MPLS label entry for range mode. */\nvoid range_set_mpls_entry(port_info_t *pinfo, uint32_t mpls_entry);\n\n/** Set the Q-in-Q outer and inner VLAN IDs for range mode. */\nvoid range_set_qinqids(port_info_t *pinfo, uint16_t outerid, uint16_t innerid);\n\n/** Set the GRE key for range mode. */\nvoid range_set_gre_key(port_info_t *pinfo, uint32_t gre_key);\n\n/** Set start, minimum, maximum, or increment for the TTL in range mode. */\nvoid range_set_ttl(port_info_t *pinfo, char *what, uint8_t ttl);\n\n/** Set start, minimum, maximum, or increment for the IPv6 hop limit in range mode. */\nvoid range_set_hop_limits(port_info_t *pinfo, char *what, uint8_t hop_limits);\n\n/** Set start, minimum, maximum, or increment for the IPv6 traffic class in range mode. */\nvoid range_set_traffic_class(port_info_t *pinfo, char *what, uint8_t traffic_class);\n\n/* Sequence */\n\n/** Set the number of active sequence packets for a port. */\nvoid pktgen_set_port_seqCnt(port_info_t *pinfo, uint32_t cnt);\n\n/**\n * Set all fields for one sequence-packet slot.\n *\n * @param seqnum     Slot index (0 .. NUM_SEQ_PKTS-1).\n * @param daddr      Destination MAC address.\n * @param saddr      Source MAC address.\n * @param ip_daddr   Destination IP address.\n * @param ip_saddr   Source IP address.\n * @param sport      Source L4 port.\n * @param dport      Destination L4 port.\n * @param ip         'v' for IPv4, '6' for IPv6.\n * @param proto      't' for TCP, 'u' for UDP, 'i' for ICMP.\n * @param vlanid     VLAN ID.\n * @param pktsize    Packet size in bytes.\n * @param gtpu_teid  GTP-U TEID (0 if unused).\n */\nvoid pktgen_set_seq(port_info_t *pinfo, uint32_t seqnum, struct rte_ether_addr *daddr,\n                    struct rte_ether_addr *saddr, struct pg_ipaddr *ip_daddr,\n                    struct pg_ipaddr *ip_saddr, uint32_t sport, uint32_t dport, char ip, char proto,\n                    uint16_t vlanid, uint32_t pktsize, uint32_t gtpu_teid);\n\n/** Set CoS and ToS values for a sequence-packet slot. */\nvoid pktgen_set_cos_tos_seq(port_info_t *pinfo, uint32_t seqnum, uint32_t cos, uint32_t tos);\n\n/** Set VxLAN parameters for a sequence-packet slot. */\nvoid pktgen_set_vxlan_seq(port_info_t *pinfo, uint32_t seqnum, uint32_t flag, uint32_t gid,\n                          uint32_t vid);\n\n/** Set TCP flags for a sequence-packet slot from a string. */\nvoid seq_set_tcp_flags(port_info_t *pinfo, uint32_t seqnum, const char *flags);\n\n/* Pattern */\n\n/** Set the payload fill pattern type (\"zero\", \"abc\", \"user\", or \"none\"). */\nvoid pattern_set_type(port_info_t *pinfo, char *str);\n\n/** Set the user-defined payload fill pattern string. */\nvoid pattern_set_user_pattern(port_info_t *pinfo, char *str);\n\n/**\n * Parse a TCP flags string into a bitmask.\n *\n * @return\n *   Bitmask of TCP flag bits.\n */\nuint16_t tcp_flags_from_str(const char *str);\n\n/**\n * Format a TCP flag bitmask as a human-readable string.\n *\n * @param flags  Bitmask of TCP flag bits.\n * @param buf    Output buffer.\n * @param len    Size of @p buf.\n * @return\n *   0 on success, negative if the buffer is too small.\n */\nint tcp_str_from_flags(uint16_t flags, char *buf, size_t len);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_CMDS_H_ */\n"
  },
  {
    "path": "app/pktgen-constants.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_CONSTANTS_H_\n#define _PKTGEN_CONSTANTS_H_\n\n/**\n * @file\n *\n * Memory and burst sizing constants for Pktgen.\n */\n\n#include <rte_mbuf.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum {\n    MAX_PKT_RX_BURST                  = 256,  /**< Maximum RX burst size (array dimension) */\n    MAX_PKT_TX_BURST                  = 256,  /**< Maximum TX burst size (array dimension) */\n    DEFAULT_PKT_RX_BURST              = 64,   /**< Default RX burst size */\n    DEFAULT_PKT_TX_BURST              = 32,   /**< Default TX burst size */\n    DEFAULT_RX_DESC                   = 1024, /**< Default RX descriptor ring size */\n    DEFAULT_TX_DESC                   = 1024, /**< Default TX descriptor ring size */\n    DEFAULT_MBUFS_PER_PORT_MULTIPLIER = 16,   /**< Multiplier for mbufs-per-port calculation */\n    MAX_SPECIAL_MBUFS                 = 1024, /**< Maximum special mbufs per port */\n    MBUF_CACHE_SIZE                   = 256,  /**< Per-lcore mempool cache size */\n    DEFAULT_PRIV_SIZE                 = 0,    /**< Default mbuf private data size */\n};\n\n/** Compute the total mbufs needed for a port given its descriptor ring sizes. */\n#define MAX_MBUFS_PER_PORT(rxd, txd) ((rxd + txd) * DEFAULT_MBUFS_PER_PORT_MULTIPLIER)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_CONSTANTS_H_ */\n"
  },
  {
    "path": "app/pktgen-cpu.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <lua_config.h>\n\n#include \"pktgen-display.h\"\n#include \"pktgen-cpu.h\"\n#include \"pktgen-log.h\"\n\n#include \"pktgen.h\"\n\nstatic char *uname_str;\n\nstatic int\nsave_uname(char *line, int i __rte_unused)\n{\n    uname_str = pg_strdupf(uname_str, line);\n    return 0;\n}\n\n/**\n *\n * pktgen_page_cpu - Display the CPU page.\n *\n * DESCRIPTION\n * Display the CPU page.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_page_cpu(void)\n{\n    uint32_t row, cnt, nb_sockets, nb_cores, nb_threads;\n    static int counter = 0;\n\n    display_topline(\"<CPU Page>\", 0, 0, 0);\n\n    cnt = coreinfo_lcore_cnt();\n    if ((cnt == 0) || (pktgen.lscpu == NULL))\n        pktgen_cpu_init();\n\n    nb_sockets = coreinfo_socket_cnt();\n    nb_cores   = coreinfo_core_cnt();\n    nb_threads = coreinfo_thread_cnt();\n\n    if ((counter++ & 3) != 0)\n        return;\n\n    pktgen_display_set_color(\"stats.stat.label\");\n    row = 3;\n    scrn_printf(row++, 1, \"Kernel: %s\", uname_str);\n    row++;\n    pktgen_display_set_color(\"stats.stat.values\");\n    scrn_printf(row++, 1, \"Model Name: %s\", pktgen.lscpu->model_name);\n    scrn_printf(row++, 1, \"Cache Size: %s\", pktgen.lscpu->cache_size);\n    row++;\n    pktgen_display_set_color(\"top.ports\");\n    scrn_printf(row++, 1, \"CPU Flags : %s\", pktgen.lscpu->cpu_flags);\n    row += 6;\n\n    pktgen_display_set_color(\"stats.total.data\");\n    scrn_printf(row++, 1, \"Number of sockets %d, cores/socket %d, threads/core %d, total %d\",\n                nb_sockets, nb_cores, nb_threads, cnt);\n\n    if (pktgen.flags & PRINT_LABELS_FLAG) {\n        pktgen.last_row = row + pktgen.nb_ports;\n        display_dashline(pktgen.last_row);\n\n        scrn_setw(pktgen.last_row);\n        scrn_printf(100, 1, \"\"); /* Put cursor on the last row. */\n    }\n    pktgen_display_set_color(NULL);\n    pktgen.flags &= ~PRINT_LABELS_FLAG;\n}\n\n/**\n *\n * pktgen_cfg_init - Init the CPU information\n *\n * DESCRIPTION\n * initialize the CPU information\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_cpu_init(void)\n{\n    do_command(\"uname -a\", save_uname);\n    pktgen.lscpu = lscpu_info(NULL, NULL);\n}\n"
  },
  {
    "path": "app/pktgen-cpu.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_CPU_H_\n#define _PKTGEN_CPU_H_\n\n/**\n * @file\n *\n * CPU topology initialisation and display page for Pktgen.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Initialise the CPU topology information for all detected lcores.\n *\n * Populates socket, core, and thread IDs for each lcore in the\n * global coreinfo table.\n */\nvoid pktgen_cpu_init(void);\n\n/**\n * Render the CPU topology display page to the console.\n */\nvoid pktgen_page_cpu(void);\n\n/**\n * Look up the lcore ID for a given socket/core/thread combination.\n *\n * @param s\n *   NUMA socket ID.\n * @param c\n *   Physical core ID within the socket.\n * @param t\n *   Hyper-thread (sibling thread) index within the core.\n * @return\n *   Logical core (lcore) ID on success, or 0 if not found.\n */\nstatic inline uint16_t\nsct(uint8_t s, uint8_t c, uint8_t t)\n{\n    coreinfo_t *ci;\n\n    for (uint16_t i = 0; i < coreinfo_lcore_cnt(); i++) {\n        ci = coreinfo_get(i);\n\n        if (ci->socket_id == s && ci->core_id == c && ci->thread_id == t)\n            return ci->lcore_id;\n    }\n    return 0;\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_CPU_H_ */\n"
  },
  {
    "path": "app/pktgen-display.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <sys/stat.h>\n#include \"lua_config.h\"\n\n#include \"pktgen-display.h\"\n#include \"pktgen-cmds.h\"\n\n#define MAX_COLOR_NAME_SIZE    64\n#define MAX_PROMPT_STRING_SIZE 64\n\nstatic char prompt_str[MAX_PROMPT_STRING_SIZE] = {0};\n\n/* String to color value mapping */\ntypedef struct string_color_map_s {\n    const char *name;   /**< Color name */\n    scrn_color_e color; /**< Color value for scrn_{fg,bg}color() */\n} string_color_map_t;\n\n// clang-format off\nstring_color_map_t string_color_map[] = {\n    {\"black\", SCRN_BLACK},\n    {\"white\", SCRN_DEFAULT_FG},\n    {\"red\", SCRN_RED},\n    {\"green\", SCRN_GREEN},\n    {\"yellow\", SCRN_YELLOW},\n    {\"blue\", SCRN_BLUE},\n    {\"magenta\", SCRN_MAGENTA},\n    {\"cyan\", SCRN_CYAN},\n    {\"white\", SCRN_WHITE},\n    {\"black\", SCRN_DEFAULT_BG},\n    {\"default\", SCRN_BLACK}, /* alias */\n    {\"none\", SCRN_NO_CHANGE},\n    {\"default_fg\", SCRN_WHITE},\n    {\"default_bg\", SCRN_BLACK},\n    {NULL, 0}\n};\n// clang-format on\n\n/* String to attribute mapping */\ntypedef struct string_attr_map_s {\n    const char *name; /**< Attribute name */\n    scrn_attr_e attr; /**< Attribute value for scrn_{fg,bg}color_attr() */\n} string_attr_map_t;\n\n// clang-format off\nstring_attr_map_t string_attr_map[] = {\n    {\"off\", SCRN_OFF},\n    {\"default\", SCRN_OFF}, /* alias */\n    {\"bold\", SCRN_BOLD},\n    {\"bright\", SCRN_BOLD}, /* alias */\n    {\"underscore\", SCRN_UNDERSCORE},\n    {\"underline\", SCRN_UNDERSCORE}, /* alias */\n    {\"blink\", SCRN_BLINK},\n    {\"reverse\", SCRN_REVERSE},\n    {\"concealed\", SCRN_CONCEALED},\n    {NULL, 0}\n};\n// clang-format on\n\n/* Element to color mapping */\ntypedef struct theme_color_map_s {\n    const char *name; /**< Display element name */\n    scrn_color_e fg_color;\n    scrn_color_e bg_color;\n    scrn_attr_e attr;\n} theme_color_map_t;\n\n// clang-format off\ntheme_color_map_t theme_color_map[] = {\n/*\t{ \"element name\",\t\tFG_COLOR,\tBG_COLOR,\tATTR\t} */\n    { \"default\",            SCRN_DEFAULT_FG, SCRN_DEFAULT_BG, SCRN_OFF     },\n\n    /*\n     * Top line of the screen\n     */\n    { \"top.spinner\",        SCRN_CYAN,       SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"top.ports\",          SCRN_GREEN,      SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"top.page\",           SCRN_WHITE,      SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"top.copyright\",      SCRN_YELLOW,     SCRN_NO_CHANGE,  SCRN_OFF     },\n    { \"top.poweredby\",      SCRN_BLUE,       SCRN_NO_CHANGE,  SCRN_BOLD    },\n\n    /*\n     * Separator between displayed values and command history\n     */\n    { \"sep.dash\",           SCRN_BLUE,       SCRN_NO_CHANGE,  SCRN_OFF     },\n    { \"sep.text\",           SCRN_WHITE,      SCRN_NO_CHANGE,  SCRN_OFF     },\n\n    /*\n     * Stats screen\n     */\n    /* Port related */\n    { \"stats.port.label\",   SCRN_BLUE,       SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"stats.port.flags\",   SCRN_BLUE,       SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"stats.port.data\",    SCRN_BLUE,       SCRN_NO_CHANGE,  SCRN_OFF     },\n\n    { \"stats.port.status\",  SCRN_GREEN,      SCRN_NO_CHANGE,  SCRN_OFF     },\n    { \"stats.port.linklbl\", SCRN_GREEN,      SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"stats.port.link\",    SCRN_GREEN,      SCRN_NO_CHANGE,  SCRN_OFF     },\n    { \"stats.port.ratelbl\", SCRN_YELLOW,     SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"stats.port.rate\",    SCRN_YELLOW,     SCRN_NO_CHANGE,  SCRN_OFF     },\n    { \"stats.port.sizelbl\", SCRN_CYAN,       SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"stats.port.sizes\",   SCRN_CYAN,       SCRN_NO_CHANGE,  SCRN_OFF     },\n    { \"stats.port.errlbl\",  SCRN_RED,        SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"stats.port.errors\",  SCRN_RED,        SCRN_NO_CHANGE,  SCRN_OFF     },\n    { \"stats.port.totlbl\",  SCRN_BLUE,       SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"stats.port.totals\",  SCRN_BLUE,       SCRN_NO_CHANGE,  SCRN_OFF     },\n\n    /* Dynamic elements (updated every second) */\n    { \"stats.dyn.label\",    SCRN_BLUE,       SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"stats.dyn.values\",   SCRN_GREEN,      SCRN_NO_CHANGE,  SCRN_OFF     },\n\n    /* Static elements (only update when explicitly set to different value) */\n    { \"stats.stat.label\",   SCRN_MAGENTA,    SCRN_NO_CHANGE,  SCRN_OFF     },\n    { \"stats.stat.values\",  SCRN_WHITE,      SCRN_NO_CHANGE,  SCRN_OFF    },\n\n    /* Total statistics */\n    { \"stats.total.label\",  SCRN_RED,        SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"stats.total.data\",   SCRN_BLUE,       SCRN_NO_CHANGE,  SCRN_BOLD    },\n\n    /* Colon separating labels and values */\n    { \"stats.colon\",        SCRN_BLUE,       SCRN_NO_CHANGE,  SCRN_BOLD    },\n\n    /* Highlight some static values */\n    { \"stats.rate.count\",   SCRN_BLUE,       SCRN_NO_CHANGE,  SCRN_BOLD    },\n    { \"stats.bdf\",          SCRN_BLUE,       SCRN_NO_CHANGE,  SCRN_OFF     },\n    { \"stats.mac\",          SCRN_GREEN,      SCRN_NO_CHANGE,  SCRN_OFF     },\n    { \"stats.ip\",           SCRN_CYAN,       SCRN_NO_CHANGE,  SCRN_OFF     },\n\n    /*\n     * Misc.\n     */\n    { \"pktgen.prompt\",      SCRN_GREEN,      SCRN_NO_CHANGE,  SCRN_OFF     },\n    { NULL, 0, 0, 0 }\n};\n// clang-format on\n\n/* Initialize screen data structures */\n/* Print out the top line on the screen */\nvoid\ndisplay_topline(const char *msg, int pstart, int pstop, int pcnt)\n{\n    if (this_scrn && this_scrn->type != SCRN_STDIN_TYPE) {\n        scrn_puts(\"\\n\");\n        return;\n    }\n\n    pktgen_display_set_color(\"top.page\");\n    scrn_printf(PAGE_TITLE_ROW, 3, \"%s \", msg);\n    if (pcnt > 0) {\n        pktgen_display_set_color(\"stats.port.totlbl\");\n        scrn_puts(\"Ports %d-%d of %d\", pstart, pstop, pcnt);\n    }\n    pktgen_display_set_color(\"top.copyright\");\n    scrn_puts(\"  %s\", copyright_msg_short());\n}\n\n/* Print out the dashed line on the screen. */\nvoid\ndisplay_dashline(int last_row)\n{\n    int i;\n\n    if (this_scrn && this_scrn->type != SCRN_STDIN_TYPE) {\n        scrn_puts(\"\\n\");\n        return;\n    }\n\n    scrn_setw(last_row);\n    last_row--;\n    scrn_pos(last_row, 1);\n    pktgen_display_set_color(\"sep.dash\");\n    for (i = 0; i < 79; i++)\n        scrn_fprintf(0, 0, stdout, \"-\");\n    pktgen_display_set_color(\"stats.port.linklbl\");\n    scrn_printf(last_row, 3, \" %s \", pktgen_version());\n    pktgen_display_set_color(\"top.poweredby\");\n    scrn_puts(\" %s %s\", powered_by(), rte_version());\n    pktgen_display_set_color(\"stats.port.rate\");\n    scrn_puts(\" (pid:%d) \", getpid());\n    pktgen_display_set_color(NULL);\n}\n\n/* Get the display geometry */\nvoid\npktgen_display_get_geometry(uint16_t *rows, uint16_t *cols)\n{\n    if (!this_scrn)\n        return;\n\n    if (rows != NULL)\n        *rows = this_scrn->nrows;\n\n    if (cols != NULL)\n        *cols = this_scrn->ncols;\n}\n\n/* Look up the named color in the colormap */\nstatic theme_color_map_t *\nlookup_item(const char *elem)\n{\n    theme_color_map_t *result;\n\n    if (elem == NULL)\n        elem = \"default\";\n\n    /* Look up colors and attributes for elem */\n    for (result = theme_color_map; result->name != NULL; ++result)\n        if (strncasecmp(result->name, elem, MAX_COLOR_NAME_SIZE) == 0)\n            break;\n\n    /* Report failure if element is not found */\n    if (result->name == NULL)\n        result = NULL;\n\n    return result;\n}\n\n/* Changes the color to the color of the specified element */\nvoid\npktgen_display_set_color(const char *elem)\n{\n    theme_color_map_t *theme_color;\n\n    if (!this_scrn || this_scrn->theme == SCRN_THEME_OFF)\n        return;\n\n    theme_color = lookup_item(elem);\n    if (theme_color == NULL) {\n        pktgen_log_error(\"Unknown color '%s'\", elem);\n        return;\n    }\n\n    scrn_color(theme_color->fg_color, theme_color->bg_color, theme_color->attr);\n}\n\n/* String to use as prompt, with proper ANSI color codes */\nvoid\n__set_prompt(void)\n{\n    theme_color_map_t *def, *prompt;\n\n    if (!this_scrn)\n        return;\n\n    /* Set default return value. */\n    snprintf(prompt_str, sizeof(prompt_str), \"%s> \", PKTGEN_VER_PREFIX);\n\n    if ((this_scrn->theme == SCRN_THEME_ON) && !scrn_is_paused()) {\n        /* Look up the default and prompt values */\n        def    = lookup_item(NULL);\n        prompt = lookup_item(\"pktgen.prompt\");\n\n        if ((def == NULL) || (prompt == NULL))\n            pktgen_log_error(\"Prompt and/or default color undefined\");\n\n        else\n            snprintf(prompt_str, sizeof(prompt_str), \"\\033[%d;%d;%dm%s>\\033[%d;%d;%dm \",\n                     prompt->attr, 30 + prompt->fg_color, 40 + prompt->bg_color, PKTGEN_VER_PREFIX,\n                     def->attr, 30 + def->fg_color, 40 + def->bg_color);\n    }\n}\n\nstatic const char *\nget_name_by_color(scrn_color_e color)\n{\n    int i;\n\n    for (i = 0; string_color_map[i].name; i++)\n        if (color == string_color_map[i].color)\n            return string_color_map[i].name;\n    return NULL;\n}\n\nstatic const char *\nget_name_by_attr(scrn_attr_e attr)\n{\n    int i;\n\n    for (i = 0; string_attr_map[i].name; i++)\n        if (attr == string_attr_map[i].attr)\n            return string_attr_map[i].name;\n    return NULL;\n}\n\nstatic scrn_color_e\nget_color_by_name(char *name)\n{\n    int i;\n\n    for (i = 0; string_color_map[i].name; i++)\n        if (strcmp(name, string_color_map[i].name) == 0)\n            return string_color_map[i].color;\n    return SCRN_UNKNOWN_COLOR;\n}\n\nstatic scrn_attr_e\nget_attr_by_name(char *name)\n{\n    int i;\n\n    for (i = 0; string_attr_map[i].name; i++)\n        if (strcmp(name, string_attr_map[i].name) == 0)\n            return string_attr_map[i].attr;\n    return SCRN_UNKNOWN_ATTR;\n}\n\nvoid\npktgen_theme_show(void)\n{\n    int i;\n\n    if (!this_scrn)\n        return;\n\n    printf(\"*** Theme Color Map Names (%s) ***\\n\", this_scrn->theme ? \"Enabled\" : \"Disabled\");\n    printf(\"   %-30s %-10s %-10s %s\\n\", \"name\", \"FG Color\", \"BG Color\", \"Attribute\");\n    for (i = 0; theme_color_map[i].name; i++) {\n        printf(\"   %-32s %-10s %-10s %-6s\", theme_color_map[i].name,\n               get_name_by_color(theme_color_map[i].fg_color),\n               get_name_by_color(theme_color_map[i].bg_color),\n               get_name_by_attr(theme_color_map[i].attr));\n        printf(\"     \");\n        pktgen_display_set_color(theme_color_map[i].name);\n        printf(\"%-s\", theme_color_map[i].name);\n        pktgen_display_set_color(NULL);\n        printf(\"\\n\");\n    }\n}\n\nvoid\npktgen_theme_state(const char *state)\n{\n    if (!this_scrn)\n        return;\n    if (estate(state) == DISABLE_STATE)\n        this_scrn->theme = SCRN_THEME_OFF;\n    else\n        this_scrn->theme = SCRN_THEME_ON;\n    __set_prompt();\n}\n\nvoid\npktgen_set_theme_item(char *item, char *fg_color, char *bg_color, char *attr)\n{\n    theme_color_map_t *elem;\n    scrn_color_e fg, bg;\n    scrn_attr_e at;\n\n    elem = lookup_item(item);\n\n    if (elem == NULL) {\n        pktgen_log_error(\"Unknown item name (%s)\\n\", item);\n        return;\n    }\n\n    fg = get_color_by_name(fg_color);\n    bg = get_color_by_name(bg_color);\n    at = get_attr_by_name(attr);\n\n    if ((fg == SCRN_UNKNOWN_COLOR) || (bg == SCRN_UNKNOWN_COLOR) || (at == SCRN_UNKNOWN_ATTR)) {\n        pktgen_log_error(\"Unknown color or attribute (%s, %s, %s)\", fg_color, bg_color, attr);\n        return;\n    }\n\n    elem->fg_color = fg;\n    elem->bg_color = bg;\n    elem->attr     = at;\n}\n\nvoid\npktgen_theme_save(char *filename)\n{\n    FILE *f;\n    int i;\n\n    f = fopen(filename, \"w+\");\n    if (f == NULL) {\n        pktgen_log_error(\"Unable to open file %s\", filename);\n        return;\n    }\n\n    for (i = 0; theme_color_map[i].name; i++)\n        fprintf(f, \"theme %s %s %s %s\\n\", theme_color_map[i].name,\n                get_name_by_color(theme_color_map[i].fg_color),\n                get_name_by_color(theme_color_map[i].bg_color),\n                get_name_by_attr(theme_color_map[i].attr));\n    fprintf(f, \"cls\\n\");\n\n    fchmod(fileno(f), 0666);\n    fclose(f);\n}\n\nvoid\npktgen_print_div(uint32_t row_first, uint32_t row_last, uint32_t col)\n{\n    uint32_t row;\n\n    if (this_scrn && this_scrn->type != SCRN_STDIN_TYPE)\n        return;\n\n    pktgen_display_set_color(\"stats.colon\");\n    for (row = row_first; row < row_last; row++)\n        scrn_printf(row, col, \":\");\n}\n"
  },
  {
    "path": "app/pktgen-display.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_DISPLAY_H_\n#define _PKTGEN_DISPLAY_H_\n\n/**\n * @file\n *\n * Terminal display helpers for Pktgen: screen initialisation, top/dash\n * line drawing, geometry queries, colour-theme management, and column\n * divider rendering.\n */\n\n/* TODO create pktgen_display_*() abstractions and remove this #include */\n#include <cli_scrn.h>\n\n#include <copyright_info.h>\n\n#include \"pktgen.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Initialise the terminal screen and colour theme.\n *\n * @param theme\n *   Non-zero to enable the colour theme; 0 for monochrome output.\n */\nvoid pktgen_init_screen(int theme);\n\n/**\n * Print the top banner line of the display with page title and port range.\n *\n * @param msg\n *   Page title string to centre in the top line.\n * @param pstart\n *   First port index included on this display page.\n * @param pstop\n *   Last port index included on this display page (inclusive).\n * @param pcnt\n *   Total number of ports shown on this page.\n */\nvoid display_topline(const char *msg, int pstart, int pstop, int pcnt);\n\n/**\n * Print a full-width dashed separator line at the given row.\n *\n * @param last_row\n *   Terminal row number at which to draw the dashed line.\n */\nvoid display_dashline(int last_row);\n\n/**\n * Query the current terminal geometry.\n *\n * @param rows\n *   Output: number of terminal rows.\n * @param cols\n *   Output: number of terminal columns.\n */\nvoid pktgen_display_get_geometry(uint16_t *rows, uint16_t *cols);\n\n/**\n * Apply the foreground/background colours associated with theme element @p elem.\n *\n * @param elem\n *   Theme element name string (e.g. \"top.page\", \"stats.total.label\").\n */\nvoid pktgen_display_set_color(const char *elem);\n\n/**\n * Update the CLI prompt string to reflect the current colour theme.\n *\n * When the colour theme is enabled the prompt includes ANSI escape codes;\n * when disabled it is plain text.\n */\nvoid pktgen_set_prompt(void);\n\n/**\n * Print all defined colour theme entries with their associated colours.\n */\nvoid pktgen_show_theme(void);\n\n/**\n * Set the colours and text attribute for a named theme item.\n *\n * @param item\n *   Theme item name to update.\n * @param fg_color\n *   Foreground colour name string (e.g. \"red\", \"default\").\n * @param bg_color\n *   Background colour name string.\n * @param attr\n *   Text attribute string (e.g. \"bold\", \"underline\", \"none\").\n */\nvoid pktgen_set_theme_item(char *item, char *fg_color, char *bg_color, char *attr);\n\n/**\n * Save the current colour theme as a sequence of Pktgen commands to @p filename.\n *\n * @param filename\n *   Path to the file in which the theme commands are written.\n */\nvoid pktgen_theme_save(char *filename);\n\n/**\n * Enable or disable the colour theme.\n *\n * @param state\n *   \"on\" to enable the colour theme, \"off\" to disable it.\n */\nvoid pktgen_theme_state(const char *state);\n\n/**\n * Print the current colour-theme enable/disable state to the console.\n */\nvoid pktgen_theme_show(void);\n\n/**\n * Draw a vertical column divider (colon characters) between two rows.\n *\n * @param row_first\n *   First terminal row at which to start drawing the divider.\n * @param row_last\n *   Last terminal row at which to stop drawing the divider (inclusive).\n * @param col\n *   Terminal column at which the divider is drawn.\n */\nvoid pktgen_print_div(uint32_t row_first, uint32_t row_last, uint32_t col);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_DISPLAY_H_ */\n"
  },
  {
    "path": "app/pktgen-dump.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <cli_scrn.h>\n#include <lua_config.h>\n\n#include \"pktgen.h\"\n#include \"pktgen-log.h\"\n\n/**\n *\n * pktgen_packet_dump - Dump the contents of a packet\n *\n * DESCRIPTION\n * Dump the contents of a packet.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_packet_dump(struct rte_mbuf *m, int pid)\n{\n    port_info_t *pinfo = l2p_get_port_pinfo(pid);\n    int plen           = (m->pkt_len + RTE_ETHER_CRC_LEN);\n    unsigned char *curr_data;\n    struct rte_mbuf *curr_mbuf;\n\n    /* Checking if pinfo->dump_tail will not overflow is done in the caller */\n    if (pinfo->dump_list[pinfo->dump_tail].data != NULL)\n        rte_free(pinfo->dump_list[pinfo->dump_tail].data);\n\n    pinfo->dump_list[pinfo->dump_tail].data =\n        rte_zmalloc_socket(\"Packet data\", plen, 0, pg_socket_id());\n    pinfo->dump_list[pinfo->dump_tail].len = plen;\n\n    for (curr_data = pinfo->dump_list[pinfo->dump_tail].data, curr_mbuf = m; curr_mbuf != NULL;\n         curr_data += curr_mbuf->data_len, curr_mbuf                    = curr_mbuf->next)\n        rte_memcpy(curr_data, (uint8_t *)curr_mbuf->buf_addr + m->data_off, curr_mbuf->data_len);\n\n    ++pinfo->dump_tail;\n}\n\n/**\n *\n * pktgen_packet_dump_bulk - Dump packet contents.\n *\n * DESCRIPTION\n * Dump packet contents for later inspection.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_packet_dump_bulk(struct rte_mbuf **pkts, int nb_dump, int pid)\n{\n    port_info_t *pinfo = l2p_get_port_pinfo(pid);\n    int i;\n\n    /* Don't dump more packets than the user asked */\n    if (nb_dump > pinfo->dump_count)\n        nb_dump = pinfo->dump_count;\n\n    /* Don't overflow packet array */\n    if (nb_dump > MAX_DUMP_PACKETS - pinfo->dump_tail)\n        nb_dump = MAX_DUMP_PACKETS - pinfo->dump_tail;\n\n    if (nb_dump == 0)\n        return;\n\n    for (i = 0; i < nb_dump; i++)\n        pktgen_packet_dump(pkts[i], pid);\n\n    pinfo->dump_count -= nb_dump;\n}\n\n/**\n *\n * pktgen_print_packet_dump - Print captured packets to the screen\n *\n * DESCRIPTION\n * When some packets are captured on user request, print the packet data to\n * the screen.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_print_packet_dump(void)\n{\n    port_info_t *pinfo;\n\n    unsigned int pid;\n    unsigned int i, j;\n    unsigned char *pdata;\n    uint32_t plen;\n    char buff[4096];\n\n    for (pid = 0; pid < RTE_MAX_ETHPORTS; pid++) {\n        pinfo = l2p_get_port_pinfo(pid);\n        if (pinfo == NULL)\n            continue;\n        for (; pinfo->dump_head < pinfo->dump_tail; ++pinfo->dump_head) {\n            pdata = (unsigned char *)pinfo->dump_list[pinfo->dump_head].data;\n            plen  = pinfo->dump_list[pinfo->dump_head].len;\n\n            snprintf(buff, sizeof(buff), \"Port %d, packet with length %d:\", pid, plen);\n\n            for (i = 0; i < plen; i += 16) {\n                strncatf(buff, \"\\n\\t\");\n\n                /* Byte counter */\n                strncatf(buff, \"%06x: \", i);\n\n                for (j = 0; j < 16; ++j) {\n                    /* Hex. value of character */\n                    if (i + j < plen)\n                        strncatf(buff, \"%02x \", pdata[i + j]);\n                    else\n                        strncatf(buff, \"   \");\n\n                    /* Extra padding after 8 hex values for readability */\n                    if ((j + 1) % 8 == 0)\n                        strncatf(buff, \" \");\n                }\n\n                /* Separate hex. values and raw characters */\n                strncatf(buff, \"\\t\");\n\n                for (j = 0; j < 16; ++j)\n                    if (i + j < plen)\n                        strncatf(buff, \"%c\", isprint(pdata[i + j]) ? pdata[i + j] : '.');\n            }\n            pktgen_log_info(\"%s\", buff);\n\n            rte_free(pinfo->dump_list[pinfo->dump_head].data);\n            pinfo->dump_list[pinfo->dump_head].data = NULL;\n        }\n    }\n}\n"
  },
  {
    "path": "app/pktgen-dump.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_DUMP_H_\n#define _PKTGEN_DUMP_H_\n\n/**\n * @file\n *\n * Packet hex-dump capture and printing utilities for Pktgen debugging.\n */\n\n#include <rte_mbuf.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Maximum number of packets that can be queued for dump output. */\n#define MAX_DUMP_PACKETS 32\n\n/**\n * Capture a single packet for later hex-dump output.\n *\n * @param m\n *   Mbuf containing the packet to capture.\n * @param pid\n *   Port index associated with the packet.\n */\nvoid pktgen_packet_dump(struct rte_mbuf *m, int pid);\n\n/**\n * Capture up to @p nb_dump packets from an mbuf array for later hex-dump output.\n *\n * @param pkts\n *   Array of mbufs to capture.\n * @param nb_dump\n *   Number of mbufs in @p pkts to capture (capped at MAX_DUMP_PACKETS).\n * @param pid\n *   Port index associated with the packets.\n */\nvoid pktgen_packet_dump_bulk(struct rte_mbuf **pkts, int nb_dump, int pid);\n\n/**\n * Print all captured packet hex-dumps to the console and clear the dump queue.\n */\nvoid pktgen_print_packet_dump(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_DUMP_H_ */\n"
  },
  {
    "path": "app/pktgen-ether.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <rte_hexdump.h>\n\n#include \"pktgen.h\"\n#include \"pktgen-ether.h\"\n#include \"pktgen-seq.h\"\n#include \"pktgen-port-cfg.h\"\n\n/**\n *\n * pktgen_ether_hdr_ctor - Ethernet header constructor routine.\n *\n * DESCRIPTION\n * Construct the ethernet header for a given packet buffer.\n *\n * RETURNS: Pointer to memory after the ethernet header.\n *\n * SEE ALSO:\n */\nchar *\npktgen_ether_hdr_ctor(port_info_t *pinfo, pkt_seq_t *pkt)\n{\n    struct rte_ether_hdr *eth;\n    uint16_t vlan_id;\n\n    eth = &pkt->hdr->eth;\n\n    /* src and dest addr */\n    rte_ether_addr_copy(&pkt->eth_src_addr, &eth->src_addr);\n    rte_ether_addr_copy(&pkt->eth_dst_addr, &eth->dst_addr);\n\n    if (pktgen_tst_port_flags(pinfo, SEND_VLAN_ID)) {\n        /* vlan ethernet header */\n        eth->ether_type = htons(RTE_ETHER_TYPE_VLAN);\n\n        /* only set the TCI field for now; don't bother with PCP/DEI */\n        struct rte_vlan_hdr *rte_vlan_hdr = (struct rte_vlan_hdr *)(eth + 1);\n        vlan_id                           = (pkt->vlanid | (pkt->cos << 13));\n        rte_vlan_hdr->vlan_tci            = htons(vlan_id);\n        rte_vlan_hdr->eth_proto           = htons(pkt->ethType);\n\n        /* adjust header size for VLAN tag */\n        pkt->ether_hdr_size = sizeof(struct rte_ether_hdr) + sizeof(struct rte_vlan_hdr);\n\n        return (char *)(rte_vlan_hdr + 1);\n    } else if (pktgen_tst_port_flags(pinfo, SEND_MPLS_LABEL)) {\n        /* MPLS unicast ethernet header */\n        eth->ether_type = htons(ETHER_TYPE_MPLS_UNICAST);\n\n        mplsHdr_t *mpls_hdr = (mplsHdr_t *)(eth + 1);\n\n        /* Only a single MPLS label is supported at the moment. Make sure the\n         * BoS flag is set. */\n        uint32_t mpls_label = pkt->mpls_entry;\n        MPLS_SET_BOS(mpls_label);\n\n        mpls_hdr->label = htonl(mpls_label);\n\n        /* Adjust header size for MPLS label */\n        pkt->ether_hdr_size = sizeof(struct rte_ether_hdr) + sizeof(mplsHdr_t);\n\n        return (char *)(mpls_hdr + 1);\n    } else if (pktgen_tst_port_flags(pinfo, SEND_Q_IN_Q_IDS)) {\n        /* Q-in-Q ethernet header */\n        eth->ether_type = htons(ETHER_TYPE_Q_IN_Q);\n\n        qinqHdr_t *qinq_hdr = (qinqHdr_t *)(eth + 1);\n\n        /* only set the TCI field for now; don't bother with PCP/DEI */\n        qinq_hdr->qinq_tci = htons(pkt->qinq_outerid);\n\n        qinq_hdr->vlan_tpid = htons(RTE_ETHER_TYPE_VLAN);\n        qinq_hdr->vlan_tci  = htons(pkt->qinq_innerid);\n\n        qinq_hdr->eth_proto = htons(pkt->ethType);\n\n        /* Adjust header size for Q-in-Q header */\n        pkt->ether_hdr_size = sizeof(struct rte_ether_hdr) + sizeof(qinqHdr_t);\n\n        return (char *)(qinq_hdr + 1);\n    } else {\n        /* normal ethernet header */\n        eth->ether_type     = htons(pkt->ethType);\n        pkt->ether_hdr_size = sizeof(struct rte_ether_hdr);\n    }\n\n#ifdef TX_DEBUG_PKT\n    if (eth->dst_addr.addr_bytes[0] & 1)\n        rte_hexdump(stdout, \"Ether\", eth, sizeof(struct rte_ether_hdr));\n#endif\n\n    return (char *)(eth + 1);\n}\n"
  },
  {
    "path": "app/pktgen-ether.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_ETHER_H_\n#define _PKTGEN_ETHER_H_\n\n/**\n * @file\n *\n * Ethernet header construction for Pktgen transmit packets.\n */\n\n#include <rte_ethdev.h>\n\n#include \"pktgen-seq.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct port_info_s;\n\n/**\n * Construct the Ethernet header for a packet template.\n *\n * Fills in the destination and source MAC addresses and EtherType based on\n * the port configuration and packet sequence entry @p pkt.\n *\n * @param info\n *   Port information providing source MAC and port-level configuration.\n * @param pkt\n *   Packet sequence entry whose Ethernet header fields are populated.\n * @return\n *   Pointer to the byte immediately following the Ethernet header within\n *   the packet template buffer.\n */\nchar *pktgen_ether_hdr_ctor(struct port_info_s *info, pkt_seq_t *pkt);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_ETHER_H_ */\n"
  },
  {
    "path": "app/pktgen-gre.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <cli_scrn.h>\n#include <lua_config.h>\n#include <rte_net.h>\n\n#include \"pktgen-gre.h\"\n#include \"pktgen.h\"\n\n/**\n *\n * pktgen_gre_hdr_ctor - IPv4/GRE header construction routine.\n *\n * DESCRIPTION\n * Construct an IPv4/GRE header in a packet buffer.\n *\n * RETURNS: Pointer to memory after the GRE header.\n *\n * SEE ALSO:\n */\n\nchar *\npktgen_gre_hdr_ctor(port_info_t *info __rte_unused, pkt_seq_t *pkt, greIp_t *gre)\n{\n    /* Zero out the header space */\n    memset((char *)gre, 0, sizeof(greIp_t));\n\n    /* Create the IP header */\n    gre->ip.version_ihl     = (IPv4_VERSION << 4) | (sizeof(struct rte_ipv4_hdr) / 4);\n    gre->ip.type_of_service = 0;\n    gre->ip.total_length    = htons(pkt->pkt_size - pkt->ether_hdr_size);\n\n    pktgen.ident += 27; /* bump by a prime number */\n    gre->ip.packet_id       = htons(pktgen.ident);\n    gre->ip.fragment_offset = 0;\n    gre->ip.time_to_live    = 64;\n    gre->ip.next_proto_id   = PG_IPPROTO_GRE;\n\n    /* FIXME don't hardcode */\n#define GRE_SRC_ADDR (10 << 24) | (10 << 16) | (1 << 8) | 1\n#define GRE_DST_ADDR (10 << 24) | (10 << 16) | (1 << 8) | 2\n    gre->ip.src_addr = htonl(GRE_SRC_ADDR);\n    gre->ip.dst_addr = htonl(GRE_DST_ADDR);\n#undef GRE_SRC_ADDR\n#undef GRE_DST_ADDR\n\n    gre->ip.hdr_checksum = rte_ipv4_cksum(&gre->ip);\n\n    /* Create the GRE header */\n    gre->gre.chk_present = 0;\n    gre->gre.unused      = 0;\n    gre->gre.key_present = 1;\n    gre->gre.seq_present = 0;\n\n    gre->gre.reserved0_0 = 0;\n    gre->gre.reserved0_1 = 0;\n\n    gre->gre.version = 0;\n    gre->gre.eth_type =\n        htons(RTE_ETHER_TYPE_IPV4); /* FIXME get EtherType of the actual encapsulated\n                                     * packet instead of defaulting to IPv4 */\n\n    int extra_count = 0;\n    if (gre->gre.chk_present)\n        /* The 16 MSBs of gre->gre.extra_fields[0] must be set to the IP (one's */\n        /* complement) checksum of the GRE header and the payload packet. */\n        /* Since the packet is still under construction at this moment, the */\n        /* checksum cannot be calculated. We just record the presence of this */\n        /* field, so the correct header length can be calculated. */\n        ++extra_count;\n\n    if (gre->gre.key_present) {\n        gre->gre.extra_fields[extra_count] = htonl(pkt->gre_key);\n        ++extra_count;\n    }\n\n    if (gre->gre.seq_present)\n        /* gre->gre.extra_fields[extra_count] = htonl(<SEQ_NR>); */\n        /* TODO implement GRE sequence numbers */\n        ++extra_count;\n\n    /* 4 * (3 - extra_count) is the amount of bytes that are not used by */\n    /* optional fields, but are included in sizeof(greIp_t). */\n    pkt->ether_hdr_size += sizeof(greIp_t) - 4 * (3 - extra_count);\n    return (char *)(gre + 1) - 4 * (3 - extra_count);\n}\n\n/**\n *\n * pktgen_gre_ether_hdr_ctor - GRE/Ethernet header construction routine.\n *\n * DESCRIPTION\n * Construct a GRE/Ethernet header in a packet buffer.\n *\n * RETURNS: Pointer to memory after the GRE header.\n *\n * SEE ALSO:\n */\n\nchar *\npktgen_gre_ether_hdr_ctor(port_info_t *info __rte_unused, pkt_seq_t *pkt, greEther_t *gre)\n{\n    /* Zero out the header space */\n    memset((char *)gre, 0, sizeof(greEther_t));\n\n    /* Create the IP header */\n    gre->ip.version_ihl     = (IPv4_VERSION << 4) | (sizeof(struct rte_ipv4_hdr) / 4);\n    gre->ip.type_of_service = 0;\n    gre->ip.total_length    = htons(pkt->pkt_size - pkt->ether_hdr_size);\n\n    pktgen.ident += 27; /* bump by a prime number */\n    gre->ip.packet_id       = htons(pktgen.ident);\n    gre->ip.fragment_offset = 0;\n    gre->ip.time_to_live    = 64;\n    gre->ip.next_proto_id   = PG_IPPROTO_GRE;\n\n    /* FIXME don't hardcode */\n#define GRE_SRC_ADDR (10 << 24) | (10 << 16) | (1 << 8) | 1\n#define GRE_DST_ADDR (10 << 24) | (10 << 16) | (1 << 8) | 2\n    gre->ip.src_addr = htonl(GRE_SRC_ADDR);\n    gre->ip.dst_addr = htonl(GRE_DST_ADDR);\n#undef GRE_SRC_ADDR\n#undef GRE_DST_ADDR\n\n    gre->ip.hdr_checksum = rte_ipv4_cksum(&gre->ip);\n\n    /* Create the GRE header */\n    gre->gre.chk_present = 0;\n    gre->gre.unused      = 0;\n    gre->gre.key_present = 1;\n    gre->gre.seq_present = 0;\n\n    gre->gre.reserved0_0 = 0;\n    gre->gre.reserved0_1 = 0;\n\n    gre->gre.version  = 0;\n    gre->gre.eth_type = htons(ETHER_TYPE_TRANSP_ETH_BR);\n\n    int extra_count = 0;\n    if (gre->gre.chk_present)\n        /* The 16 MSBs of gre->gre.extra_fields[0] must be set to the IP (one's */\n        /* complement) checksum of the GRE header and the payload packet. */\n        /* Since the packet is still under construction at this moment, the */\n        /* checksum cannot be calculated. We just record the presence of this */\n        /* field, so the correct header length can be calculated. */\n        ++extra_count;\n\n    if (gre->gre.key_present) {\n        gre->gre.extra_fields[extra_count] = htonl(pkt->gre_key);\n        ++extra_count;\n    }\n\n    if (gre->gre.seq_present)\n        /* gre->gre.extra_fields[extra_count] = htonl(<SEQ_NR>); */\n        /* TODO implement GRE sequence numbers */\n        ++extra_count;\n\n    /* Inner Ethernet header. Exact offset of start of ethernet header depends\n     * on the presence of optional fields in the GRE header. */\n    struct rte_ether_hdr *eth_hdr =\n        (struct rte_ether_hdr *)((char *)&gre->gre + 2 /* Flags and version */\n                                 + 2                   /* Protocol type */\n                                 + 4 * extra_count);   /* 4 bytes per optional field */\n    rte_ether_addr_copy(&pkt->eth_src_addr,\n                        &eth_hdr->src_addr); /* FIXME get inner Ethernet parameters from user */\n    rte_ether_addr_copy(&pkt->eth_dst_addr,\n                        &eth_hdr->dst_addr); /* FIXME get inner Ethernet parameters from user */\n    eth_hdr->ether_type =\n        htons(RTE_ETHER_TYPE_IPV4); /* FIXME get Ethernet type from actual\n                                     * encapsulated packet instead of hardcoding */\n\n    /* 4 * (3 - extra_count) is the amount of bytes that are not used by */\n    /* optional fields, but are included in sizeof(greIp_t). */\n    pkt->ether_hdr_size += sizeof(greEther_t) - 4 * (3 - extra_count);\n    return (char *)(gre + 1) - 4 * (3 - extra_count);\n}\n"
  },
  {
    "path": "app/pktgen-gre.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_GRE_H_\n#define _PKTGEN_GRE_H_\n\n/**\n * @file\n *\n * GRE tunnel header construction for Pktgen transmit packets.\n */\n\n#include <pg_inet.h>\n\n#include \"pktgen-port-cfg.h\"\n#include \"pktgen-seq.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Construct a GRE-over-IP header for a packet template.\n *\n * @param pinfo\n *   Port information providing source IP and port-level configuration.\n * @param pkt\n *   Packet sequence entry whose header fields are populated.\n * @param gre\n *   Pointer to the GRE-over-IP header region in the packet buffer.\n * @return\n *   Pointer to the byte immediately following the completed GRE header.\n */\nchar *pktgen_gre_hdr_ctor(port_info_t *pinfo, pkt_seq_t *pkt, greIp_t *gre);\n\n/**\n * Construct a GRE-over-Ethernet header for a packet template.\n *\n * @param pinfo\n *   Port information providing source MAC and port-level configuration.\n * @param pkt\n *   Packet sequence entry whose header fields are populated.\n * @param gre\n *   Pointer to the GRE-over-Ethernet header region in the packet buffer.\n * @return\n *   Pointer to the byte immediately following the completed GRE header.\n */\nchar *pktgen_gre_ether_hdr_ctor(port_info_t *pinfo, pkt_seq_t *pkt, greEther_t *gre);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_GRE_H_ */\n"
  },
  {
    "path": "app/pktgen-gtpu.c",
    "content": "/*-\n * Copyright(c) <2015-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2015 by abhinandan.gujjar@intel.com */\n\n#include <cli_scrn.h>\n#include \"lua_config.h\"\n\n#include \"pktgen.h\"\n\n#include \"pktgen-gtpu.h\"\n\n/**\n *\n * pktgen_gtpu_udp_hdr_ctor - GTP-U header constructor routine for TCP/UDP.\n *\n * DESCRIPTION\n * Construct the GTP-U header in a packer buffer.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_gtpu_hdr_ctor(pkt_seq_t *pkt, void *hdr, uint16_t ipProto, uint8_t flags, uint16_t seq_no,\n                     uint8_t npdu_no, uint8_t next_ext_hdr_type)\n{\n    unsigned int l4HdrSize;\n    unsigned int options = 0;\n    gtpuHdr_t *gtppHdr;\n    void *p;\n\n    if (ipProto == PG_IPPROTO_UDP)\n        l4HdrSize = sizeof(struct rte_udp_hdr);\n    else\n        l4HdrSize = sizeof(struct rte_tcp_hdr);\n\n    gtppHdr = (gtpuHdr_t *)RTE_PTR_ADD(hdr, sizeof(struct rte_ipv4_hdr) + l4HdrSize);\n\n    /* Zero out the header space */\n    memset((char *)gtppHdr, 0, sizeof(gtpuHdr_t));\n\n    /* Version: It is a 3-bit field. For GTPv1, this has a value of 1. */\n    gtppHdr->version_flags = flags;\n\n    /* Message Type: an 8-bit field that indicates the type of GTP message.\n     * Different types of messages are defined in 3GPP TS 29.060 section 7.1\n     */\n    gtppHdr->msg_type = 0xff;\n\n    /* Tunnel endpoint identifier (TEID)\n     * A 32-bit(4-octet) field used to multiplex different connections in the\n     * same GTP tunnel.\n     */\n    gtppHdr->teid = htonl(pkt->gtpu_teid);\n\n    p = (void *)gtppHdr;\n\n    if (gtppHdr->version_flags & GTPu_S_FLAG) {\n        /* Sequence number an (optional) 16-bit field.\n         * This field exists if any of the E, S, or PN bits are on. The field\n         * must be interpreted only if the S bit is on.\n         */\n        *(uint16_t *)p = seq_no;\n        p              = RTE_PTR_ADD(p, 2);\n        gtppHdr->tot_len += 2;\n        options += 2;\n    }\n\n    if (gtppHdr->version_flags & GTPu_PN_FLAG) {\n        /* N-PDU number an (optional) 8-bit field. This field exists if any of\n         * the E, S, or PN bits are on.\n         * The field must be interpreted only if the PN bit is on.\n         */\n        *(uint8_t *)p = npdu_no;\n        p             = RTE_PTR_ADD(p, 1);\n        gtppHdr->tot_len++;\n        options++;\n    }\n\n    if (gtppHdr->version_flags & GTPu_E_FLAG) {\n        /* Next extension header type an (optional) 8-bit field. This field\n         * exists if any of the E, S, or PN bits are on.\n         * The field must be interpreted only if the E bit is on.\n         */\n        *(uint8_t *)p = next_ext_hdr_type;\n        p             = RTE_PTR_ADD(p, 1);\n        gtppHdr->tot_len++;\n        options++;\n    }\n\n    /* Message Length - a 16-bit field that indicates the length of the payload\n     * in bytes (rest of the packet following the mandatory 8-byte GTP header).\n     * Includes the optional fields.\n     */\n    gtppHdr->tot_len = htons(pkt->pkt_size - (l4HdrSize + sizeof(gtpuHdr_t) + pkt->ether_hdr_size));\n}\n"
  },
  {
    "path": "app/pktgen-gtpu.h",
    "content": "/*-\n * Copyright(c) <2015-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2015 by abhinandan.gujjar@intel.com */\n\n#ifndef _PKTGEN_GTPU_H_\n#define _PKTGEN_GTPU_H_\n\n/**\n * @file\n *\n * GTP-U header construction for Pktgen transmit packets.\n */\n\n#include <pg_inet.h>\n\n#include \"pktgen-seq.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Construct a GTP-U header in the packet buffer.\n *\n * @param pkt\n *   Packet sequence entry providing TEID and payload length.\n * @param hdr\n *   Pointer to the start of the GTP-U header region in the packet buffer.\n * @param ipProto\n *   Inner IP protocol type carried by the GTP-U tunnel.\n * @param flags\n *   GTP-U flags byte (PT, E, S, PN bits).\n * @param seq_no\n *   GTP-U sequence number (used when the S flag is set).\n * @param npdu_no\n *   N-PDU number (used when the PN flag is set).\n * @param next_ext_hdr_type\n *   Next extension header type (used when the E flag is set).\n */\nvoid pktgen_gtpu_hdr_ctor(pkt_seq_t *pkt, void *hdr, uint16_t ipProto, uint8_t flags,\n                          uint16_t seq_no, uint8_t npdu_no, uint8_t next_ext_hdr_type);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_GTPU_H_ */\n"
  },
  {
    "path": "app/pktgen-ipv4.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <arpa/inet.h>\n\n#include <cli_scrn.h>\n#include <lua_config.h>\n\n#include \"pktgen.h\"\n#include \"pktgen-log.h\"\n#include \"pktgen-ipv4.h\"\n#include \"pktgen-txbuff.h\"\n#include \"l2p.h\"\n\n/**\n *\n * pktgen_ipv4_ctor - Construct the IPv4 header for a packet\n *\n * DESCRIPTION\n * Constructor for the IPv4 header for a given packet.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_ipv4_ctor(pkt_seq_t *pkt, void *hdr, bool cksum_offload)\n{\n    struct rte_ipv4_hdr *ip = hdr;\n    uint16_t tlen;\n\n    /* IPv4 Header constructor */\n    tlen = pkt->pkt_size - pkt->ether_hdr_size;\n\n    /* Zero out the header space */\n    memset((char *)ip, 0, sizeof(struct rte_ipv4_hdr));\n\n    ip->version_ihl = (IPv4_VERSION << 4) | (sizeof(struct rte_ipv4_hdr) / 4);\n\n    ip->total_length    = htons(tlen);\n    ip->time_to_live    = pkt->ttl;\n    ip->type_of_service = pkt->tos;\n\n    pktgen.ident += 27; /* bump by a prime number */\n    ip->packet_id       = htons(pktgen.ident);\n    ip->fragment_offset = 0;\n    ip->next_proto_id   = pkt->ipProto;\n    ip->src_addr        = htonl(pkt->ip_src_addr.addr.ipv4.s_addr);\n    ip->dst_addr        = htonl(pkt->ip_dst_addr.addr.ipv4.s_addr);\n    ip->hdr_checksum    = 0;\n    if (!cksum_offload)\n        ip->hdr_checksum = rte_ipv4_cksum((const struct rte_ipv4_hdr *)ip);\n}\n\n/**\n *\n * pktgen_send_ping4 - Create and send a Ping or ICMP echo packet.\n *\n * DESCRIPTION\n * Create a ICMP echo request packet and send the packet to a give port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_send_ping4(uint32_t pid, uint8_t seq_idx)\n{\n    port_info_t *pinfo = l2p_get_port_pinfo(pid);\n    pkt_seq_t *ppkt    = &pinfo->seq_pkt[SPECIAL_PKT];\n    pkt_seq_t *spkt    = &pinfo->seq_pkt[seq_idx];\n    struct rte_mbuf *m;\n    l2p_port_t *port;\n\n    port = l2p_get_port(pid);\n    {\n        const uint16_t tx_qid = l2p_get_txqid(rte_lcore_id());\n        if (rte_mempool_get(port->sp_mp[tx_qid], (void **)&m)) {\n            pktgen_log_warning(\"No packet buffers found\");\n            return;\n        }\n    }\n    *ppkt = *spkt; /* Copy the sequence setup to the ping setup. */\n    pktgen_packet_ctor(pinfo, SPECIAL_PKT, ICMP4_ECHO);\n    rte_memcpy(rte_pktmbuf_mtod(m, uint8_t *), (uint8_t *)ppkt->hdr, ppkt->pkt_size);\n\n    m->pkt_len  = ppkt->pkt_size;\n    m->data_len = ppkt->pkt_size;\n\n    tx_send_packets(pinfo, l2p_get_txqid(rte_lcore_id()), &m, 1);\n}\n\n/**\n *\n * pktgen_process_ping4 - Process a input ICMP echo packet for IPv4.\n *\n * DESCRIPTION\n * Process a input packet for IPv4 ICMP echo request and send response if needed.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_process_ping4(struct rte_mbuf *m, uint32_t pid, uint32_t qid, uint32_t vlan)\n{\n    port_info_t *pinfo        = l2p_get_port_pinfo(pid);\n    struct rte_ether_hdr *eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);\n    struct rte_ipv4_hdr *ip   = (struct rte_ipv4_hdr *)&eth[1];\n    char buff[24];\n\n    /* Adjust for a vlan header if present */\n    if (vlan)\n        ip = (struct rte_ipv4_hdr *)((char *)ip + sizeof(struct rte_vlan_hdr));\n\n    /* Look for a ICMP echo requests, but only if enabled. */\n    if ((rte_atomic64_read(&pinfo->port_flags) & ICMP_ECHO_ENABLE_FLAG) &&\n        (ip->next_proto_id == PG_IPPROTO_ICMP)) {\n        struct rte_icmp_hdr *icmp =\n            (struct rte_icmp_hdr *)((uintptr_t)ip + sizeof(struct rte_ipv4_hdr));\n        uint16_t cksum = ~rte_raw_cksum(\n            icmp, (m->data_len - sizeof(struct rte_ether_hdr) - sizeof(struct rte_ipv4_hdr)));\n        /* We do not handle IP options, which will effect the IP header size. */\n        if (unlikely(cksum != 0)) {\n            pktgen_log_error(\"ICMP checksum failed\");\n            return;\n        }\n\n        if (unlikely(icmp->icmp_type == ICMP4_ECHO)) {\n            int idx;\n\n            if (ntohl(ip->dst_addr) == INADDR_BROADCAST) {\n                pktgen_log_warning(\"IP address %s is a Broadcast\",\n                                   inet_ntop4(buff, sizeof(buff), ip->dst_addr, INADDR_BROADCAST));\n                return;\n            }\n\n            /* Toss all broadcast addresses and requests not for this port */\n            idx = pktgen_find_matching_ipsrc(pinfo, ip->dst_addr);\n\n            /* ARP request not for this interface. */\n            if (unlikely(idx == -1)) {\n                pktgen_log_warning(\"IP address %s not found\",\n                                   inet_ntop4(buff, sizeof(buff), ip->dst_addr, INADDR_BROADCAST));\n                return;\n            }\n\n            icmp->icmp_type = ICMP4_ECHO_REPLY;\n\n            /* Recompute the ICMP checksum */\n            icmp->icmp_cksum = 0;\n            icmp->icmp_cksum = rte_raw_cksum(\n                icmp, (m->data_len - sizeof(struct rte_ether_hdr) - sizeof(struct rte_ipv4_hdr)));\n\n            /* Swap the IP addresses. */\n            inetAddrSwap(&ip->src_addr, &ip->dst_addr);\n\n            /* Bump the ident value */\n            ip->packet_id = htons(ntohs(ip->packet_id) + m->data_len);\n\n            /* Recompute the IP checksum */\n            ip->hdr_checksum = 0;\n            ip->hdr_checksum = ~rte_raw_cksum(ip, sizeof(struct rte_ipv4_hdr));\n\n            /* Swap the MAC addresses */\n            ethAddrSwap(&eth->dst_addr, &eth->src_addr);\n\n            tx_send_packets(pinfo, qid, &m, 1);\n\n            /* No need to free mbuf as it was reused. */\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "app/pktgen-ipv4.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_IPV4_H_\n#define _PKTGEN_IPV4_H_\n\n/**\n * @file\n *\n * IPv4 header construction and ICMPv4 ping handling for Pktgen.\n */\n\n#include \"pktgen-seq.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Construct the IPv4 header for a packet template.\n *\n * @param pkt\n *   Packet sequence entry providing IP addresses, protocol, and length.\n * @param hdr\n *   Pointer to the start of the IPv4 header region in the packet buffer.\n * @param cksum_offload\n *   When true, set the checksum to 0 and rely on hardware offload;\n *   when false, compute the checksum in software.\n */\nvoid pktgen_ipv4_ctor(pkt_seq_t *pkt, void *hdr, bool cksum_offload);\n\n/**\n * Transmit an ICMPv4 echo-request (ping) on port @p pid.\n *\n * @param pid\n *   Port index to send the ping on.\n * @param seq_idx\n *   Packet sequence slot index to use as the ping template.\n */\nvoid pktgen_send_ping4(uint32_t pid, uint8_t seq_idx);\n\n/**\n * Process a received ICMPv4 echo-reply and update port statistics.\n *\n * @param m\n *   Received mbuf containing the ICMP reply.\n * @param pid\n *   Port index on which the packet arrived.\n * @param qid\n *   Queue index on which the packet arrived.\n * @param vlan\n *   VLAN ID extracted from the packet (0 if untagged).\n */\nvoid pktgen_process_ping4(struct rte_mbuf *m, uint32_t pid, uint32_t qid, uint32_t vlan);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /*  _PKTGEN_IPV4_H_ */\n"
  },
  {
    "path": "app/pktgen-ipv6.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <cli_scrn.h>\n#include <lua_config.h>\n\n#include \"pktgen.h\"\n\n#include \"pktgen-ipv6.h\"\n\n/**\n *\n * pktgen_ipv6_ctor - IPv6 packet header constructor routine.\n *\n * DESCRIPTION\n * Construct the IPv6 header constructor routine.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_ipv6_ctor(pkt_seq_t *pkt, void *hdr)\n{\n    struct rte_ipv6_hdr *ip = hdr;\n    uint16_t tlen;\n\n    /* IPv6 Header constructor */\n    memset(ip, 0, sizeof(struct rte_ipv6_hdr));\n\n    ip->vtc_flow = htonl(IPv6_VERSION << 28);\n    ip->vtc_flow |= htonl(pkt->traffic_class << RTE_IPV6_HDR_TC_SHIFT);\n    tlen = pkt->pkt_size - (pkt->ether_hdr_size + sizeof(struct rte_ipv6_hdr));\n\n    ip->payload_len = htons(tlen);\n    ip->hop_limits  = pkt->hop_limits;\n    ip->proto       = pkt->ipProto;\n\n    rte_memcpy(&ip->dst_addr, &pkt->ip_dst_addr.addr.ipv6, sizeof(struct rte_ipv6_addr));\n    rte_memcpy(&ip->src_addr, &pkt->ip_src_addr.addr.ipv6, sizeof(struct rte_ipv6_addr));\n}\n\n/**\n *\n * pktgen_process_ping6 - Process a IPv6 ICMP echo request packet.\n *\n * DESCRIPTION\n * Process a IPv6 ICMP echo request packet and send response if needed.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_process_ping6(struct rte_mbuf *m __rte_unused, uint32_t pid __rte_unused,\n                     uint32_t qid __rte_unused, uint32_t vlan __rte_unused)\n{\n#if 0 /* Broken needs to be updated to do IPv6 packets */\n\tport_info_t     *info = &pktgen.info[pid];\n\tstruct rte_ether_hdr *eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);\n\tstruct rte_ipv6_hdr       *ip = (struct rte_ipv6_hdr *)&eth[1];\n\n\t/* Adjust for a vlan header if present */\n\tif (vlan)\n\t\tip = (struct rte_ipv6_hdr *)((char *)ip + sizeof(struct rte_vlan_hdr));\n\n\t/* Look for a ICMP echo requests, but only if enabled. */\n\tif ( (rte_atomic32_read(&info->port_flags) & ICMP_ECHO_ENABLE_FLAG) &&\n\t     (ip->next_header == PG_IPPROTO_ICMPV6) ) {\n#if !defined(RTE_ARCH_X86_64)\n\t\tstruct rte_icmp_hdr *icmp =\n\t\t\t(struct rte_icmp_hdr *)((uint32_t)ip + sizeof(struct rte_ipv4_hdr));\n#else\n\t\tstruct rte_icmp_hdr *icmp =\n\t\t\t(struct rte_icmp_hdr *)((uint64_t)ip + sizeof(struct rte_ipv4_hdr));\n#endif\n\t\t/* We do not handle IP options, which will effect the IP header size. */\n\t\tif (rte_ipv6_cksum(icmp,\n\t\t\t  (m->pkt.data_len - sizeof(struct rte_ether_hdr) -\n\t\t\t   sizeof(struct rte_ipv4_hdr))) ) {\n\t\t\trte_printf_status(\"ICMP checksum failed\\n\");\n\t\t\tgoto leave :\n\t\t}\n\n\t\tif (icmp->type == ICMP4_ECHO) {\n\t\t\t/* Toss all broadcast addresses and requests not for this port */\n\t\t\tif ( (ip->dst == INADDR_BROADCAST) ||\n\t\t\t     (ip->dst != info->ip_src_addr) ) {\n\t\t\t\tchar buff[24];\n\t\t\t\trte_printf_status(\"IP address %s != \",\n\t\t\t\t\t\t  inet_ntop4(buff, sizeof(buff),\n\t\t\t\t\t\t\t     ip->dst,\n\t\t\t\t\t\t\t     INADDR_BROADCAST));\n\t\t\t\trte_printf_status(\"%s\\n\",\n\t\t\t\t\t\t  inet_ntop4(buff, sizeof(buff),\n\t\t\t\t\t\t\t     htonl(info->\n\t\t\t\t\t\t\t\t   ip_src_addr),\n\t\t\t\t\t\t\t     INADDR_BROADCAST));\n\t\t\t\tgoto leave;\n\t\t\t}\n\n\t\t\ticmp->type  = ICMP4_ECHO_REPLY;\n\n\t\t\t/* Recompute the ICMP checksum */\n\t\t\ticmp->cksum = 0;\n\t\t\ticmp->cksum =\n\t\t\t\trte_raw_cksum(icmp,\n\t\t\t\t      (m->pkt.data_len -\n\t\t\t\t       sizeof(struct rte_ether_hdr) -\n\t\t\t\t       sizeof(struct rte_ipv4_hdr)));\n\n\t\t\t/* Swap the IP addresses. */\n\t\t\tinetAddrSwap(&ip->src, &ip->dst);\n\n\t\t\t/* Bump the ident value */\n\t\t\tip->ident   = htons(ntohs(ip->ident) + m->pkt.data_len);\n\n\t\t\t/* Recompute the IP checksum */\n\t\t\tip->cksum   = 0;\n\t\t\tip->cksum   = rte_raw_cksum(ip, sizeof(struct rte_ipv4_hdr));\n\n\t\t\t/* Swap the MAC addresses */\n\t\t\tethAddrSwap(&eth->d_addr, &eth->s_addr);\n\n\t\t\trte_eth_tx_buffer(pid, 0, info->q[0].txbuff, m);\n\n\t\t\tpktgen_set_q_flags(info, 0, DO_TX_FLUSH);\n\n\t\t\t/* No need to free mbuf as it was reused */\n\t\t\treturn;\n\t\t}\n\t}\nleave:\n#else\n#endif\n}\n"
  },
  {
    "path": "app/pktgen-ipv6.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_IPV6_H_\n#define _PKTGEN_IPV6_H_\n\n/**\n * @file\n *\n * IPv6 header construction and ICMPv6 ping handling for Pktgen.\n */\n\n#include \"pktgen.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Construct an IPv6 header in the packet buffer.\n *\n * @param pkt\n *   Packet sequence entry providing source/destination IPv6 addresses,\n *   traffic class, flow label, and next-header type.\n * @param hdr\n *   Pointer to the start of the IPv6 header region in the packet buffer.\n */\nvoid pktgen_ipv6_ctor(pkt_seq_t *pkt, void *hdr);\n\n/**\n * Process a received ICMPv6 echo-reply (ping6 response).\n *\n * @param m\n *   Received mbuf containing the ICMPv6 reply.\n * @param pid\n *   Port index on which the packet arrived.\n * @param qid\n *   Queue index on which the packet arrived.\n * @param vlan\n *   VLAN ID extracted from the outer header (0 if untagged).\n */\nvoid pktgen_process_ping6(struct rte_mbuf *m, uint32_t pid, uint32_t qid, uint32_t vlan);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_IPV6_H_ */\n"
  },
  {
    "path": "app/pktgen-latency.c",
    "content": "/*-\n * Copyright(c) <2016-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2016 by Keith Wiles @ intel.com */\n\n#include <stdio.h>\n\n#include \"lua_config.h\"\n\n#include \"pktgen-cmds.h\"\n#include \"pktgen-display.h\"\n\n#include \"pktgen.h\"\n\n#include <rte_bus_pci.h>\n#include <rte_bus.h>\n\nvoid\nlatency_set_rate(port_info_t *pinfo, uint32_t value)\n{\n    latency_t *lat = &pinfo->latency;\n\n    if (value == 0)\n        value = DEFAULT_LATENCY_RATE;\n    if (value > MAX_LATENCY_RATE)\n        value = MAX_LATENCY_RATE;\n\n    lat->latency_rate_us     = value;\n    lat->latency_rate_cycles = pktgen_get_timer_hz() / (MAX_LATENCY_RATE / lat->latency_rate_us);\n}\n\nvoid\nlatency_set_entropy(port_info_t *pinfo, uint16_t value)\n{\n    latency_t *lat = &pinfo->latency;\n\n    lat->latency_entropy = value;\n}\n\n/**\n *\n * pktgen_print_static_data - Display the static data on the screen.\n *\n * DESCRIPTION\n * Display a set of port static data on the screen.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic void\npktgen_print_static_data(void)\n{\n    port_info_t *pinfo;\n    struct rte_eth_dev_info dev = {0};\n    uint32_t pid, col, row, sp, ip_row;\n    pkt_seq_t *pkt;\n    char buff[32];\n    int display_cnt;\n\n    pktgen_display_set_color(\"top.page\");\n    display_topline(\"<Latency Page>\", 0, 0, 0);\n\n    pktgen_display_set_color(\"top.ports\");\n    scrn_printf(1, 3, \"Ports %d-%d of %d\", pktgen.starting_port, (pktgen.ending_port - 1),\n                pktgen.nb_ports);\n\n    row = PORT_FLAGS_ROW;\n    pktgen_display_set_color(\"stats.port.label\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  Port:Flags\");\n\n    /* Labels for dynamic fields (update every second) */\n    pktgen_display_set_color(\"stats.dyn.label\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Link State\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Pkts/s Max/Rx\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"       Max/Tx\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"MBits/s Rx/Tx\");\n\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Latency:\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  Rate (us)\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  Entropy\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  Total RX Pkts\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  Total TX Pkts\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  Skipped Pkts\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  Cycles/Minimum(us)\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  Cycles/Average(us)\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  Cycles/Maximum(us)\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Percentiles:\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  90th Cycles / us\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  95th Cycles / us\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  99th Cycles / us\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Jitter:\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  Threshold (us)\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"  Count/Percent\");\n\n    /* Labels for static fields */\n    pktgen_display_set_color(\"stats.stat.label\");\n    ip_row = ++row;\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Pattern Type\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Tx Count/% Rate\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"PktSize/Rx:Tx Burst\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Src/Dest Port\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Type:VLAN ID:Flags\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Dst  IP Address\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Src  IP Address\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Dst MAC Address\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Src MAC Address\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"NUMA/Vend:ID/PCI\");\n\n    /* Get the last location to use for the window starting row. */\n    pktgen.last_row = ++row;\n    display_dashline(pktgen.last_row);\n\n    /* Display the colon after the row label. */\n    pktgen_print_div(PORT_FLAGS_ROW, pktgen.last_row - 1, COLUMN_WIDTH_0 - 1);\n\n    pktgen_display_set_color(\"stats.stat.values\");\n    sp          = pktgen.starting_port;\n    display_cnt = 0;\n    for (pid = 0; pid < pktgen.nb_ports_per_page; pid++) {\n        pinfo = l2p_get_port_pinfo(pid + sp);\n        if (pinfo == NULL)\n            continue;\n\n        pkt = &pinfo->seq_pkt[SINGLE_PKT];\n\n        pktgen_display_set_color(\"stats.stat.values\");\n        /* Display Port information Src/Dest IP addr, Netmask, Src/Dst MAC addr */\n        col = (COLUMN_WIDTH_1 * pid) + COLUMN_WIDTH_0;\n        row = ip_row;\n\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1,\n                    (pinfo->fill_pattern_type == ABC_FILL_PATTERN)    ? \"abcd...\"\n                    : (pinfo->fill_pattern_type == NO_FILL_PATTERN)   ? \"None\"\n                    : (pinfo->fill_pattern_type == ZERO_FILL_PATTERN) ? \"Zero\"\n                                                                      : pinfo->user_pattern);\n        pktgen_transmit_count_rate(pid, buff, sizeof(buff));\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        snprintf(buff, sizeof(buff), \"%d /%3d:%3d\", pkt->pkt_size + RTE_ETHER_CRC_LEN,\n                 pinfo->rx_burst, pinfo->tx_burst);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n        snprintf(buff, sizeof(buff), \"%d/%5d/%5d\", pkt->ttl, pkt->sport, pkt->dport);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n        snprintf(buff, sizeof(buff), \"%s / %s:%04x:%04x\",\n                 (pkt->ethType == RTE_ETHER_TYPE_IPV4)   ? \"IPv4\"\n                 : (pkt->ethType == RTE_ETHER_TYPE_IPV6) ? \"IPv6\"\n                 : (pkt->ethType == RTE_ETHER_TYPE_ARP)  ? \"ARP\"\n                                                         : \"Other\",\n                 (pkt->ipProto == PG_IPPROTO_TCP)    ? \"TCP\"\n                 : (pkt->ipProto == PG_IPPROTO_ICMP) ? \"ICMP\"\n                                                     : \"UDP\",\n                 pkt->vlanid, pkt->tcp_flags);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        scrn_printf(\n            row++, col, \"%*s\", COLUMN_WIDTH_1,\n            inet_ntop4(buff, sizeof(buff), htonl(pkt->ip_dst_addr.addr.ipv4.s_addr), 0xFFFFFFFF));\n        scrn_printf(\n            row++, col, \"%*s\", COLUMN_WIDTH_1,\n            inet_ntop4(buff, sizeof(buff), htonl(pkt->ip_src_addr.addr.ipv4.s_addr), pkt->ip_mask));\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1,\n                    inet_mtoa(buff, sizeof(buff), &pkt->eth_dst_addr));\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1,\n                    inet_mtoa(buff, sizeof(buff), &pkt->eth_src_addr));\n        if (rte_eth_dev_info_get(pid, &dev) < 0)\n            rte_exit(EXIT_FAILURE, \"Cannot get device info for port %u\", pid);\n        const struct rte_bus *bus = NULL;\n        if (dev.device)\n            bus = rte_bus_find_by_device(dev.device);\n        if (bus && !strcmp(rte_bus_name(bus), \"pci\")) {\n            char name[RTE_ETH_NAME_MAX_LEN];\n            char vend[8], device[8];\n\n            vend[0] = device[0] = '\\0';\n            sscanf(rte_dev_bus_info(dev.device), \"vendor_id=%4s, device_id=%4s\", vend, device);\n\n            rte_eth_dev_get_name_by_port(pid, name);\n            snprintf(buff, sizeof(buff), \"%d/%s:%s/%s\", rte_dev_numa_node(dev.device), vend, device,\n                     rte_dev_name(dev.device));\n        } else\n            snprintf(buff, sizeof(buff), \"-1/0000:0000/00:00.0\");\n        pktgen_display_set_color(\"stats.bdf\");\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n        display_cnt++;\n    }\n\n    /* Display the string for total pkts/s rate of all ports */\n    col = (COLUMN_WIDTH_1 * display_cnt) + COLUMN_WIDTH_0;\n    pktgen_display_set_color(\"stats.total.label\");\n    scrn_printf(LINK_STATE_ROW, col, \"%*s\", COLUMN_WIDTH_3, \"----TotalRate----\");\n    scrn_eol();\n    pktgen_display_set_color(NULL);\n\n    pktgen.flags &= ~PRINT_LABELS_FLAG;\n}\n/* comparator function for qsort ring */\nstatic int\ncmp_uint64_asc(const void *a, const void *b)\n{\n    uint64_t va = *(const uint64_t *)a;\n    uint64_t vb = *(const uint64_t *)b;\n    if (va < vb)\n        return -1;\n    if (va > vb)\n        return 1;\n    return 0;\n}\n/* returns quantile (0.9, 0.95, 0.99) */\nstatic uint64_t\nlatency_ring_percentile(const latency_ring_t *ring, double q)\n{\n    if (ring->count == 0)\n        return 0;\n\n    uint64_t tmp[RING_SIZE];\n    for (int i = 0; i < ring->count; i++) {\n        tmp[i] = ring->data[i];\n    }\n\n    qsort(tmp, ring->count, sizeof(uint64_t), cmp_uint64_asc);\n\n    // percentile position (es. 0.9 == 90%)\n    double pos  = q * (ring->count - 1);\n    int idx     = (int)pos;\n    double frac = pos - idx;\n\n    if (idx + 1 < ring->count)\n        return tmp[idx] + (uint64_t)((tmp[idx + 1] - tmp[idx]) * frac);\n    else\n        return tmp[idx];\n}\n\nstatic inline double\ncycles_to_us(uint64_t cycles, double per_cycle)\n{\n    return (cycles == 0) ? 0.0 : (per_cycle * (double)cycles) * Million;\n}\n\n/**\n *\n * pktgen_page_latency - Display the latency on the screen for all ports.\n *\n * DESCRIPTION\n * Display the port latency on the screen for all ports.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_page_latency(void)\n{\n    port_info_t *pinfo;\n    latency_t *lat;\n    unsigned int pid, col, row;\n    unsigned sp, nb_pkts;\n    char buff[32];\n    int display_cnt;\n    double latency, per_cycle;\n\n    if (pktgen.flags & PRINT_LABELS_FLAG)\n        pktgen_print_static_data();\n\n    memset(&pktgen.cumm_rate_totals, 0, sizeof(struct rte_eth_stats));\n\n    sp          = pktgen.starting_port;\n    display_cnt = 0;\n    per_cycle   = (1.0 / pktgen.hz);\n    for (pid = 0; pid < pktgen.nb_ports_per_page; pid++) {\n        pinfo = l2p_get_port_pinfo(pid + sp);\n        if (pinfo == NULL)\n            continue;\n\n        /* Display the disable string when port is not enabled. */\n        col = (COLUMN_WIDTH_1 * pid) + COLUMN_WIDTH_0;\n        row = PORT_FLAGS_ROW;\n\n        /* Display the port number for the column */\n        snprintf(buff, sizeof(buff), \"%d:%s\", pid + sp, pktgen_flags_string(pinfo));\n        pktgen_display_set_color(\"stats.port.flags\");\n        scrn_printf(row, col, \"%*s\", COLUMN_WIDTH_1, buff);\n        pktgen_display_set_color(NULL);\n\n        row = LINK_STATE_ROW;\n\n        /* Grab the link state of the port and display Duplex/Speed and UP/Down */\n        pktgen_get_link_status(pinfo);\n\n        pktgen_link_state(pid, buff, sizeof(buff));\n        pktgen_display_set_color(\"stats.port.status\");\n        scrn_printf(row, col, \"%*s\", COLUMN_WIDTH_1, buff);\n        pktgen_display_set_color(NULL);\n\n        pktgen_display_set_color(\"stats.stat.values\");\n        /* Rx/Tx pkts/s rate */\n        row = LINK_STATE_ROW + 1;\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \"/%'\" PRIu64,\n                 iBitsTotal(pinfo->stats.rate) / Million, oBitsTotal(pinfo->stats.rate) / Million);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        pktgen.cumm_rate_totals.ipackets += pinfo->stats.rate.ipackets;\n        pktgen.cumm_rate_totals.opackets += pinfo->stats.rate.opackets;\n        pktgen.cumm_rate_totals.ibytes += pinfo->stats.rate.ibytes;\n        pktgen.cumm_rate_totals.obytes += pinfo->stats.rate.obytes;\n        pktgen.cumm_rate_totals.ierrors += pinfo->stats.rate.ierrors;\n        pktgen.cumm_rate_totals.oerrors += pinfo->stats.rate.oerrors;\n        pktgen.cumm_rate_totals.imissed += pinfo->stats.rate.imissed;\n        pktgen.cumm_rate_totals.rx_nombuf += pinfo->stats.rate.rx_nombuf;\n\n        row++; /* Skip Latency header row */\n        lat = &pinfo->latency;\n        /* Ensure all RX worker stores to latency fields are visible before reading */\n        rte_smp_rmb();\n        nb_pkts = (lat->num_latency_pkts == 0) ? 1 : lat->num_latency_pkts;\n        uint64_t avg_cycles =\n            lat->running_cycles / nb_pkts; /* compute locally; do not write back */\n\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64, lat->latency_rate_us);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        snprintf(buff, sizeof(buff), \"%'\" PRIu16, lat->latency_entropy);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64, lat->num_latency_pkts);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64, lat->num_latency_tx_pkts);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64, lat->num_skipped);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        latency = cycles_to_us(lat->min_cycles, per_cycle);\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \"/%'8.2f\", lat->min_cycles, latency);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        latency = cycles_to_us(avg_cycles, per_cycle);\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \"/%'8.2f\", avg_cycles, latency);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        latency = cycles_to_us(lat->max_cycles, per_cycle);\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \"/%'8.2f\", lat->max_cycles, latency);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        row++; /* Skip Percentiles header */\n        double q90, q95, q99;\n        q90 = cycles_to_us(latency_ring_percentile(&lat->tail_latencies, 0.90), per_cycle);\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \"/%'8.2f\",\n                 latency_ring_percentile(&lat->tail_latencies, 0.90), q90);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        q95 = cycles_to_us(latency_ring_percentile(&lat->tail_latencies, 0.95), per_cycle);\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \"/%'8.2f\",\n                 latency_ring_percentile(&lat->tail_latencies, 0.95), q95);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        q99 = cycles_to_us(latency_ring_percentile(&lat->tail_latencies, 0.99), per_cycle);\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \"/%'8.2f\",\n                 latency_ring_percentile(&lat->tail_latencies, 0.99), q99);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        row++; /* Skip Jitter header */\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64, lat->jitter_threshold_us);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \"/%'6.2f\", lat->jitter_count,\n                 (double)(lat->jitter_count * 100) / nb_pkts);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        display_cnt++;\n    }\n\n    /* Display the total pkts/s for all ports */\n    col = (COLUMN_WIDTH_1 * display_cnt) + COLUMN_WIDTH_0;\n    row = LINK_STATE_ROW + 1;\n    snprintf(buff, sizeof(buff), \"%'lu/%'lu\", pktgen.max_total_ipackets,\n             pktgen.cumm_rate_totals.ipackets);\n    scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_3, buff);\n    scrn_eol();\n    snprintf(buff, sizeof(buff), \"%'lu/%'lu\", pktgen.max_total_opackets,\n             pktgen.cumm_rate_totals.opackets);\n    scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_3, buff);\n    scrn_eol();\n    snprintf(buff, sizeof(buff), \"%lu/%lu\", iBitsTotal(pktgen.cumm_rate_totals) / Million,\n             oBitsTotal(pktgen.cumm_rate_totals) / Million);\n    scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_3, buff);\n    scrn_eol();\n}\n\nvoid\npktgen_latency_setup(port_info_t *pinfo)\n{\n    pkt_seq_t *pkt = &pinfo->seq_pkt[LATENCY_PKT];\n\n    rte_memcpy(pkt, &pinfo->seq_pkt[SINGLE_PKT], sizeof(pkt_seq_t));\n\n    pkt->pkt_size = LATENCY_PKT_SIZE;\n    pkt->ipProto  = PG_IPPROTO_UDP;\n    pkt->ethType  = RTE_ETHER_TYPE_IPV4;\n    pkt->dport    = LATENCY_DPORT;\n}\n"
  },
  {
    "path": "app/pktgen-latency.h",
    "content": "/*-\n * Copyright(c) <2016-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2016 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_LATENCY_H_\n#define _PKTGEN_LATENCY_H_\n\n/**\n * @file\n *\n * One-way latency measurement support for Pktgen.\n *\n * Implements timestamped probe-packet injection, latency histogram\n * collection, jitter tracking, and the latency statistics display page.\n */\n\n#include <rte_timer.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define DEFAULT_JITTER_THRESHOLD (50)    /**< Default jitter threshold in microseconds */\n#define DEFAULT_LATENCY_RATE     (10000) /**< Default probe injection rate in microseconds */\n#define MAX_LATENCY_RATE (1000000)  /**< Maximum allowed probe injection rate in microseconds */\n#define DEFAULT_LATENCY_ENTROPY (0) /**< Default entropy seed for source port randomisation */\n#define LATENCY_PKT_SIZE        RTE_ETHER_MIN_LEN /**< Latency probe packet size (64 B + 4 B FCS) */\n#define LATENCY_DPORT           1028 /**< Destination UDP port used for latency probes */\n\n/**\n * Render the latency statistics display page to the console.\n */\nvoid pktgen_page_latency(void);\n\n/**\n * Initialise latency measurement state for port @p pinfo to default values.\n *\n * @param pinfo\n *   Port information structure to initialise.\n */\nvoid pktgen_latency_setup(port_info_t *pinfo);\n\n/**\n * Set the latency probe injection rate for port @p pinfo.\n *\n * @param pinfo\n *   Port information structure to update.\n * @param value\n *   Probe injection interval in microseconds (1–MAX_LATENCY_RATE).\n */\nvoid latency_set_rate(port_info_t *pinfo, uint32_t value);\n\n/**\n * Set the source-port entropy value used for latency probe identification.\n *\n * The entropy value controls how many distinct source ports are cycled\n * (SPORT + (i % entropy)); a value of 0 disables cycling.\n *\n * @param pinfo\n *   Port information structure to update.\n * @param value\n *   Entropy range in [0, 0xFFFF].\n */\nvoid latency_set_entropy(port_info_t *pinfo, uint16_t value);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_LATENCY_H_ */\n"
  },
  {
    "path": "app/pktgen-log.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include \"pktgen-log.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <time.h>\n#include <sys/time.h>\n#include <stdarg.h>\n#include <libgen.h>\n#include <sys/stat.h>\n\n#include \"lua_config.h\"\n\n#include \"pktgen-display.h\"\n#include <rte_rwlock.h>\n\n/* Log sizes and data structure */\n#define LOG_HISTORY  64   /* log \"scrollback buffer\" size */\n#define LOG_MAX_LINE 1024 /* max. length of a log line */\n\n/* Log message and metadata */\ntypedef struct log_msg_s {\n    struct timeval tv;      /**< Timestamp */\n    int level;              /**< Log level */\n    char *file;             /**< Source file of the caller */\n    long line;              /**< Line number of the caller */\n    char *func;             /**< Function name of the caller */\n    char msg[LOG_MAX_LINE]; /**< Log message */\n} log_msg_t;\n\n/* Log history */\ntypedef struct log_s {\n    log_msg_t msg[LOG_HISTORY]; /**< Log message buffer */\n    uint16_t head;              /**< index of most recent log msg */\n    uint16_t tail;              /**< index of oldest log msg */\n    uint8_t need_refresh;       /**< log page doesn't contain the latest messages */\n    rte_rwlock_t lock;          /**< multi-threaded list lock */\n} log_t;\n\nlog_t log_history;\n\nFILE *log_file       = NULL;\nint log_level_screen = LOG_LEVEL_INFO;\n\n/* Forward declarations of log entry formatting functions */\nstatic const char *pktgen_format_msg_page(const log_msg_t *log_msg);\nstatic const char *pktgen_format_msg_file(const log_msg_t *log_msg);\nstatic const char *pktgen_format_msg_stdout(const log_msg_t *log_msg);\n\n/* Initialize screen data structures */\nvoid\npktgen_init_log(void)\n{\n    memset(&log_history, 0, sizeof(log_history));\n    log_history.head         = 0;\n    log_history.tail         = 0;\n    log_history.need_refresh = 0;\n}\n\n/* Set minimum message level for printing to screen */\nvoid\npktgen_log_set_screen_level(int level)\n{\n    log_level_screen = level;\n}\n\n/* Log the provided message to the log screen and optionally a file. */\nvoid __attribute__((format(printf, 5, 6)))\npktgen_log(int level, const char *file, long line, const char *func, const char *fmt, ...)\n{\n    log_msg_t *curr_msg;\n    va_list args;\n\n    rte_rwlock_write_lock(&log_history.lock);\n\n    curr_msg = &log_history.msg[log_history.head];\n\n    /* log message metadata */\n    gettimeofday(&curr_msg->tv, NULL);\n\n    curr_msg->level = level;\n\n    if (curr_msg->file != NULL)\n        free(curr_msg->file);\n    curr_msg->file = strdup(file);\n\n    curr_msg->line = line;\n\n    if (curr_msg->func != NULL)\n        free(curr_msg->func);\n    curr_msg->func = strdup(func);\n\n    /* actual log message */\n    va_start(args, fmt);\n    vsnprintf(curr_msg->msg, LOG_MAX_LINE, fmt, args);\n    va_end(args);\n\n    if (curr_msg->msg[strlen(curr_msg->msg) - 1] == '\\n')\n        curr_msg->msg[strlen(curr_msg->msg) - 1] = '\\0';\n\n    /* Adjust head and tail indexes: head must point one beyond the last valid\n     * entry, tail must move one entry if head has caught up.\n     * The array acts as a circular buffer, so if either head or tail move\n     * beyond the last array element, they are wrapped around.\n     */\n    log_history.head = (log_history.head + 1) % LOG_HISTORY;\n\n    if (log_history.head == log_history.tail)\n        log_history.tail = (log_history.tail + 1) % LOG_HISTORY;\n\n    /* Log to file if enabled */\n    if (log_file != NULL)\n        fprintf(log_file, \"%s\\n\", pktgen_format_msg_file(curr_msg));\n\n    /* Print message to screen if its level is high enough. */\n    if (level >= log_level_screen)\n        fprintf(stdout, \"%s\\n\", pktgen_format_msg_stdout(curr_msg));\n\n    log_history.need_refresh = 1;\n\n    rte_rwlock_write_unlock(&log_history.lock);\n}\n\n/* Open file on disk for logging. */\nvoid\npktgen_log_set_file(const char *filename)\n{\n    FILE *fp;\n\n    /* Clean up if already logging to a file */\n    if (log_file != NULL) {\n        fchmod(fileno(log_file), 0666);\n        fclose(log_file);\n        log_file = NULL;\n    }\n\n    /* No filename given: disable logging to disk */\n    if (filename == NULL)\n        return;\n\n    fp = fopen(filename, \"w\");\n\n    if (fp == NULL)\n        pktgen_log_warning(\"Unable to open log file '%s' for writing\", filename);\n\n    /* Unbuffered output if file is successfully opened */\n    if (fp != NULL)\n        setbuf(fp, NULL);\n\n    log_file = fp;\n}\n\n/* Display log page on the screen */\nvoid\npktgen_page_log(uint32_t print_labels)\n{\n    /* Maximum number of log lines to display */\n#define MAX_PAGE_LINES 28\n\n    uint32_t row, curr_msg, output_lines, curr_char;\n    int curr_line;\n    char lines[MAX_PAGE_LINES][LOG_MAX_LINE];\n\n    if (!print_labels && !log_history.need_refresh)\n        return;\n\n    pktgen_display_set_color(\"top.page\");\n    display_topline(\"<Logged messages>\", 0, 0, 0);\n    row = PORT_FLAGS_ROW;\n\n    pktgen_display_set_color(\"stats.stat.label\");\n    /* Header line */\n    scrn_printf(row++, 1, \"%1s %8s %-32s %s\", \"L\", \"Time\", \"Function\", \"Message\");\n\n    pktgen_display_set_color(\"stats.stat.values\");\n    curr_line = output_lines = 0;\n    curr_msg                 = log_history.head;\n    while ((curr_msg != log_history.tail) && (output_lines < MAX_PAGE_LINES)) {\n        /* Go backwards and wrap around */\n        curr_msg = (curr_msg + LOG_HISTORY - 1) % LOG_HISTORY;\n\n        snprintf(lines[curr_line], LOG_MAX_LINE, \"%s\",\n                 pktgen_format_msg_page(&log_history.msg[curr_msg]));\n\n        /* Count number of lines occupied by current log entry. Line wrapping\n         * because of screen width is not counted, \\n's embedded in the log\n         * message are counted.\n         */\n        for (curr_char = 0; lines[curr_line][curr_char] != '\\0'; ++curr_char)\n            if (lines[curr_line][curr_char] == '\\n')\n                ++output_lines;\n\n        ++output_lines; /* First line before possible \\n's */\n\n        ++curr_line;\n    }\n\n    /* The lines[] array contains the messages to print on the screen, with\n     * the most recent message first.\n     * Iterating backwards gives the messages in chronological order.\n     * curr_line points 1 beyond the last entry in lines[].\n     */\n    for (--curr_line; curr_line >= 0; --curr_line) {\n        scrn_printf(row++, 1, \"%s\", lines[curr_line]);\n\n        /* Increase row for each embedded \\n */\n        for (curr_char = 0; lines[curr_line][curr_char] != '\\0'; ++curr_char)\n            if (lines[curr_line][curr_char] == '\\n')\n                ++row;\n    }\n\n    if (row < MAX_PAGE_LINES)\n        row = MAX_PAGE_LINES;\n\n    display_dashline(++row);\n    log_history.need_refresh = 0;\n    pktgen_display_set_color(NULL);\n\n#undef MAX_PAGE_LINES\n}\n\n/**\n *\n * pktgen_format_msg_page - formats the log entry for the log page\n *\n * DESCRIPTION\n * Generates a string representation of the log entry, suitable for the log page.\n * No effort is made to prettify multi-line messages: if indentation\n * of multiple lines is required, the log msg itself must contain appropriate\n * whitespace.\n *\n * RETURNS: Pointer to formatted string. The memory associated with the pointer\n *          is managed by this function and must not be free'd by the calling\n *          function.\n *          The memory pointed to may be altered on subsequent calls to this\n *          function. Copy the result if needed.\n *\n * SEE ALSO:\n */\nstatic const char *\npktgen_format_msg_page(const log_msg_t *log_msg)\n{\n    /* Example log line:\n     *   I 13:37:05 bar_func                         This is a message\n     */\n    static char msg[LOG_MAX_LINE] = {0};\n    char timestamp[9]             = {0};\n    char func[32];\n\n    strftime(timestamp, sizeof(timestamp), \"%H:%M:%S\", localtime(&log_msg->tv.tv_sec));\n\n    if (strlen(log_msg->func) > sizeof(func) - 1)\n        snprintf(func, sizeof(func), \"…%s\",\n                 &log_msg->func[strlen(log_msg->func) - sizeof(func) - 2]);\n    else\n        sprintf(func, \"%s\", log_msg->func);\n\n    snprintf(msg, sizeof(msg), \"%1s %8s %-*s %s\",\n             (log_msg->level == LOG_LEVEL_TRACE)     ? \"t\"\n             : (log_msg->level == LOG_LEVEL_DEBUG)   ? \"d\"\n             : (log_msg->level == LOG_LEVEL_INFO)    ? \"I\"\n             : (log_msg->level == LOG_LEVEL_WARNING) ? \"W\"\n             : (log_msg->level == LOG_LEVEL_ERROR)   ? \"E\"\n             : (log_msg->level == LOG_LEVEL_PANIC)   ? \"P\"\n                                                     : \"?\",\n             timestamp, (int)sizeof(func), func, log_msg->msg);\n\n    return msg;\n}\n\n/**\n *\n * pktgen_format_msg_file - formats the log entry for output to disk\n *\n * DESCRIPTION\n * Generates a string representation of the log entry, suitable for writing to\n * disk.\n * The output is more verbose than the output of the format log functions for\n * stdout and page.\n * No effort is made to prettify multi-line messages: if indentation\n * of multiple lines is required, the log msg itself must contain appropriate\n * whitespace.\n *\n * RETURNS: Pointer to formatted string. The memory associated with the pointer\n *          is managed by this function and must not be free'd by the calling\n *          function.\n *          The memory pointed to may be altered on subsequent calls to this\n *          function. Copy the result if needed.\n *\n * SEE ALSO:\n */\nstatic const char *\npktgen_format_msg_file(const log_msg_t *log_msg)\n{\n    /* Example log line:\n     *   II 2014-03-14 13:37:05.123 [foo.c:42(bar_func)] This is a message\n     */\n    static char msg[LOG_MAX_LINE] = {0};\n    char timestamp[32]            = {0};\n    char *file;\n\n    strftime(timestamp, sizeof(timestamp), \"%Y-%m-%d %H:%M:%S\", localtime(&log_msg->tv.tv_sec));\n\n    file = strdup(log_msg->file);\n\n    snprintf(msg, sizeof(msg), \"%s %s.%03ld [%s:%ld(%s)] %s\",\n             (log_msg->level == LOG_LEVEL_TRACE)     ? \"tt\"\n             : (log_msg->level == LOG_LEVEL_DEBUG)   ? \"dd\"\n             : (log_msg->level == LOG_LEVEL_INFO)    ? \"II\"\n             : (log_msg->level == LOG_LEVEL_WARNING) ? \"WW\"\n             : (log_msg->level == LOG_LEVEL_ERROR)   ? \"EE\"\n             : (log_msg->level == LOG_LEVEL_PANIC)   ? \"PP\"\n                                                     : \"??\",\n             timestamp, log_msg->tv.tv_usec / 1000, basename(file), log_msg->line, log_msg->func,\n             log_msg->msg);\n\n    free(file);\n\n    return msg;\n}\n\n/**\n *\n * pktgen_format_msg_stdout - formats the log entry for output to screen\n *\n * DESCRIPTION\n * Generates a string representation of the log entry, suitable for writing to\n * the screen.\n * For info mesaages, just the message is printed. Warnings and more severe\n * messages get an appropriate label.\n * No effort is made to prettify multi-line messages: if indentation\n * of multiple lines is required, the log msg itself must contain appropriate\n * whitespace.\n *\n * RETURNS: Pointer to formatted string. The memory associated with the pointer\n *          is managed by this function and must not be free'd by the calling\n *          function.\n *          The memory pointed to may be altered on subsequent calls to this\n *          function. Copy the result if needed.\n *\n * SEE ALSO:\n */\nstatic const char *\npktgen_format_msg_stdout(const log_msg_t *log_msg)\n{\n    /* Example log line:\n     *   This is a message\n     */\n    static char msg[LOG_MAX_LINE] = {0};\n\n    snprintf(msg, sizeof(msg), \"%s%s\",\n             (log_msg->level <= LOG_LEVEL_INFO)      ? \"\"\n             : (log_msg->level == LOG_LEVEL_WARNING) ? \"WARNING: \"\n             : (log_msg->level == LOG_LEVEL_ERROR)   ? \"!ERROR!: \"\n             : (log_msg->level == LOG_LEVEL_PANIC)   ? \"!PANIC!: \"\n                                                     : \"??? \",\n             log_msg->msg);\n\n    return msg;\n}\n"
  },
  {
    "path": "app/pktgen-log.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_LOG_H_\n#define _PKTGEN_LOG_H_\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Log levels. Each log level has an associated pktgen_log_<LEVEL>()\n * function. */\n#define LOG_LEVEL_ALL     0\n#define LOG_LEVEL_TRACE   1\n#define LOG_LEVEL_DEBUG   2\n#define LOG_LEVEL_INFO    3\n#define LOG_LEVEL_WARNING 4\n#define LOG_LEVEL_ERROR   5\n#define LOG_LEVEL_PANIC   6\n#define LOG_LEVEL_NONE    7\n\n/* Set default minimum message level to log if one isn't provided at compile\n * time. All pktgen_log_<LEVEL>() calls with a log level lower than the one\n * specified below won't even be compiled.\n * More detailed logs have a negative performance impact, which is undesirable\n * in a production build.\n */\n#ifndef LOG_LEVEL\n#define LOG_LEVEL LOG_LEVEL_INFO\n#endif\n\n/* Conditionally generate code for pktgen_log_<LEVEL>() functions, depending on\n * the minimum requested log level.\n */\n#if LOG_LEVEL <= LOG_LEVEL_TRACE\n#define pktgen_log_trace(fmt, ...) \\\n    pktgen_log(LOG_LEVEL_TRACE, __FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)\n#else\n#define pktgen_log_trace(fmt, ...) /* no-op */\n#endif\n\n#if LOG_LEVEL <= LOG_LEVEL_DEBUG\n#define pktgen_log_debug(fmt, ...) \\\n    pktgen_log(LOG_LEVEL_DEBUG, __FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)\n#else\n#define pktgen_log_debug(fmt, ...) /* no-op */\n#endif\n\n#if LOG_LEVEL <= LOG_LEVEL_INFO\n#define pktgen_log_info(fmt, ...) \\\n    pktgen_log(LOG_LEVEL_INFO, __FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)\n#else\n#define pktgen_log_info(fmt, ...) /* no-op */\n#endif\n\n#if LOG_LEVEL <= LOG_LEVEL_WARNING\n#define pktgen_log_warning(fmt, ...) \\\n    pktgen_log(LOG_LEVEL_WARNING, __FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)\n#else\n#define pktgen_log_warning(fmt, ...) /* no-op */\n#endif\n\n#if LOG_LEVEL <= LOG_LEVEL_ERROR\n#define pktgen_log_error(fmt, ...) \\\n    pktgen_log(LOG_LEVEL_ERROR, __FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)\n#else\n#define pktgen_log_error(fmt, ...) /* no-op */\n#endif\n\n#if LOG_LEVEL <= LOG_LEVEL_PANIC\n#define pktgen_log_panic(fmt, ...)                                                     \\\n    do {                                                                               \\\n        scrn_destroy();                                                                \\\n        pktgen_log(LOG_LEVEL_PANIC, __FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__); \\\n        rte_panic(fmt \"\\n\", ##__VA_ARGS__);                                            \\\n    } while (0)\n#else\n#define pktgen_log_panic(fmt, ...)     \\\n    do {                               \\\n        scrn_destroy();                \\\n        rte_panic(fmt, ##__VA_ARGS__); \\\n    } while (0)\n#endif\n\n/* Helper for building log strings.\n * The macro takes an existing string, a printf-like format string and optional\n * arguments. It formats the string and appends it to the existing string, while\n * avoiding possible buffer overruns.\n */\n#define strncatf(dest, fmt, ...)                               \\\n    do {                                                       \\\n        char _buff[1023];                                      \\\n        snprintf(_buff, sizeof(_buff), fmt, ##__VA_ARGS__);    \\\n        strncat(dest, _buff, sizeof(dest) - strlen(dest) - 1); \\\n    } while (0)\n\n/* Initialize log data structures */\nvoid pktgen_init_log(void);\n\n/**\n *\n * pktgen_log_set_screen_level - Set level of messages that are printed to the screen\n *\n * DESCRIPTION\n * Messages of the specified level or higher are printed to the screen\n * in addition to being logged to the log page and optionally to a file.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid pktgen_log_set_screen_level(int level);\n\n/**\n *\n * pktgen_log - printf-like function for logging\n *\n * DESCRIPTION\n * Log the provided message to the log page and to a file if enabled.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid pktgen_log(int level, const char *file, long line, const char *func, const char *fmt, ...);\n\n/**\n *\n * pktgen_log_set_file - Start logging to a file\n *\n * DESCRIPTION\n * Writes the log to the provided filename. If the file already exists, it is\n * truncated.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid pktgen_log_set_file(const char *filename);\n\n/**\n *\n * pktgen_page_log - Display the log page.\n *\n * DESCRIPTION\n * Display the log page on the screen.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid pktgen_page_log(uint32_t print_labels);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_LOG_H_ */\n"
  },
  {
    "path": "app/pktgen-main.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <execinfo.h>\n#include <signal.h>\n#include <locale.h>\n\n#include <lua_config.h>\n#ifdef LUA_ENABLED\n#include <lua_socket.h>\n#endif\n#include <pg_delay.h>\n\n#include \"pktgen-main.h\"\n\n#include \"pktgen.h\"\n#ifdef LUA_ENABLED\n#include \"lpktgenlib.h\"\n#include \"lauxlib.h\"\n#endif\n#include \"pktgen-cmds.h\"\n#include \"pktgen-cpu.h\"\n#include \"pktgen-display.h\"\n#include \"pktgen-log.h\"\n#include \"cli-functions.h\"\n\n#ifdef LUA_ENABLED\n/**\n *\n * pktgen_get_lua - Get Lua state pointer.\n *\n * DESCRIPTION\n * Get the Lua state pointer value.\n *\n * RETURNS: Lua pointer\n *\n * SEE ALSO:\n */\n\nvoid *\npktgen_get_lua(void)\n{\n    return pktgen.ld->L;\n}\n#endif\n\n/**\n *\n * pktgen_usage - Display the help for the command line.\n *\n * DESCRIPTION\n * Display the help message for the command line.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic void\npktgen_usage(const char *prgname)\n{\n    printf(\"Usage: %s [EAL options] -- [-h] [-v] [-P] [-G] [-g host:port] [-T] [-f cmd_file] [-l \"\n           \"log_file] [-s P:filepath] [-m <string>]\\n\"\n#ifdef LUA_ENABLED\n           \"  -f filename   Command file (.pkt) to execute or a Lua script (.lua) file\\n\"\n#else\n           \"  -f filename   Command file (.pkt) to execute\\n\"\n#endif\n           \"  -l filename   Write log to filename\\n\"\n           \"  -s P:filepath PCAP packet stream file, 'P' is the port number\\n\"\n           \"  -P            Enable PROMISCUOUS mode on all ports\\n\"\n           \"  -g address    Optional IP address and port number default is (localhost:0x5606)\\n\"\n           \"                If -g is used that enable socket support as a server application\\n\"\n           \"  -G            Enable socket support using default server values localhost:0x5606 \\n\"\n           \"  -N            Enable NUMA support\\n\"\n           \"  -T            Enable the color output\\n\"\n           \"  -v            Verbose output\\n\"\n           \"  -j            Enable jumbo frames of 9600 bytes\\n\"\n           \"  -c            Enable clock_gettime\\n\"\n           \"  --txd=N       set the number of descriptors in Tx rings to N \\n\"\n           \"  --rxd=N       set the number of descriptors in Rx rings to N \\n\"\n           \"  -m <string>   matrix for mapping ports to logical cores\\n\"\n           \"      BNF: (or kind of BNF)\\n\"\n           \"      <matrix-string>   := \\\"\\\"\\\" <lcore-port> { \\\",\\\" <lcore-port>} \\\"\\\"\\\"\\n\"\n           \"      <lcore-port>      := <lcore-list> \\\".\\\" <port>\\n\"\n           \"      <lcore-list>      := \\\"[\\\" <rx-list> \\\":\\\" <tx-list> \\\"]\\\"\\n\"\n           \"      <port>            := \\\"<num>\\\"\\n\"\n           \"      <rx-list>         := <num> { \\\"/\\\" (<num> | <list>) }\\n\"\n           \"      <tx-list>         := <num> { \\\"/\\\" (<num> | <list>) }\\n\"\n           \"      <list>            := <num> { \\\"/\\\" (<range> | <list>) }\\n\"\n           \"      <range>           := <num> \\\"-\\\" <num> { \\\"/\\\" <range> }\\n\"\n           \"      <num>             := <digit>+\\n\"\n           \"      <digit>           := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9\\n\"\n           \"      1.0, 2.1, 3.2                 - core 1 handles port 0 rx/tx,\\n\"\n           \"                                      core 2 handles port 1 rx/tx\\n\"\n           \"                                      core 3 handles port 2 rx/tx\\n\"\n           \"      1.[0-2], 2.3, ...             - core 1 handle ports 0,1,2 rx/tx,\\n\"\n           \"                                      core 2 handle port 3 rx/tx\\n\"\n           \"      [0-1].0, [2/4-5].1, ...       - cores 0-1 handle port 0 rx/tx,\\n\"\n           \"                                      cores 2,4,5 handle port 1 rx/tx\\n\"\n           \"      [1:2].0, [4:6].1, ...         - core 1 handles port 0 rx,\\n\"\n           \"                                      core 2 handles port 0 tx,\\n\"\n           \"      [1:2-3].0, [4:5-6].1, ...     - core 1 handles port 0 rx, cores 2,3 handle port \"\n           \"0 tx\\n\"\n           \"                                      core 4 handles port 1 rx & core 5,6 handles port \"\n           \"1 tx\\n\"\n           \"      [1-2:3].0, [4-5:6].1, ...     - core 1,2 handles port 0 rx, core 3 handles port \"\n           \"0 tx\\n\"\n           \"                                      core 4,5 handles port 1 rx & core 6 handles port \"\n           \"1 tx\\n\"\n           \"      [1-2:3-5].0, [4-5:6/8].1, ... - core 1,2 handles port 0 rx, core 3,4,5 handles \"\n           \"port 0 tx\\n\"\n           \"                                      core 4,5 handles port 1 rx & core 6,8 handles \"\n           \"port 1 tx\\n\"\n           \"      BTW: you can use \\\"{}\\\" instead of \\\"[]\\\" as it does not matter to the syntax.\\n\"\n           \"  -h           Display the help information\\n\",\n           prgname);\n}\n\n/**\n *\n * pktgen_parse_args - Main parsing routine for the command line.\n *\n * DESCRIPTION\n * Main parsing routine for the command line.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic int\npktgen_parse_args(int argc, char **argv)\n{\n    int opt, ret;\n    char **argvopt;\n    int option_index;\n    char *prgname   = argv[0], *p;\n    char buff[1024] = {0};\n    // clang-format off\n    static struct option lgopts[] = {\n        {\"txd\", required_argument, 0, 't'},\n        {\"rxd\", required_argument, 0, 'r'},\n        {NULL, 0, 0, 0}\n    };\n    // clang-format on\n    uint16_t pid;\n\n    argvopt = argv;\n\n    pktgen.hostname    = (char *)strdupf(pktgen.hostname, \"localhost\");\n    pktgen.socket_port = 0x5606;\n\n    pktgen.argc = argc;\n    for (opt = 0; opt < argc; opt++)\n        pktgen.argv[opt] = strdup(argv[opt]);\n\n    pktgen.mbuf_dataroom = RTE_MBUF_DEFAULT_DATAROOM;\n    pktgen.mbuf_headroom = RTE_PKTMBUF_HEADROOM;\n    pktgen.mbuf_buf_size = pktgen.mbuf_dataroom + pktgen.mbuf_headroom;\n\n    pktgen.verbose = 0;\n    while ((opt = getopt_long(argc, argvopt, \"p:m:f:l:s:g:hPNGTvjtrc\", lgopts, &option_index)) !=\n           EOF) {\n        switch (opt) {\n        case 't':\n            pktgen.nb_txd = atoi(optarg);\n            pktgen_log_info(\">>> Tx Descriptor set to %d\", pktgen.nb_txd);\n            break;\n\n        case 'r':\n            pktgen.nb_rxd = atoi(optarg);\n            pktgen_log_info(\">>> Rx Descriptor set to %d\", pktgen.nb_rxd);\n            break;\n\n        case 'j':\n            pktgen.flags |= JUMBO_PKTS_FLAG;\n            pktgen.mbuf_dataroom = PG_JUMBO_DATAROOM_SIZE;\n            pktgen.mbuf_buf_size = pktgen.mbuf_dataroom + pktgen.mbuf_headroom;\n\n            pktgen_log_info(\"**** Jumbo Frames of %'d enabled.\", RTE_ETHER_MAX_JUMBO_FRAME_LEN);\n            break;\n\n        case 'p':\n            /* Port mask not used anymore */\n            break;\n\n        case 'f': /* Command file or Lua script. */\n            cli_add_cmdfile(optarg);\n            break;\n\n        case 'l': /* Log file */\n            pktgen_log_set_file(optarg);\n            break;\n\n        case 'm': /* Matrix for port mapping. */\n            if (l2p_parse_mapping_add(optarg)) {\n                pktgen_log_error(\"too many mapping entries\");\n                pktgen_usage(prgname);\n                return -1;\n            }\n            break;\n\n        case 's': /* Read a PCAP packet capture file (stream) */\n            snprintf(buff, sizeof(buff), \"%s\", optarg);\n            p = strchr(buff, ':');\n            if (p == NULL)\n                goto pcap_err;\n            *p++ = '\\0';\n\n            pid = (uint16_t)strtol(buff, NULL, 10);\n\n            if (pktgen_pcap_add(p, pid) < 0)\n                goto pcap_err;\n            break;\n        case 'P': /* Enable promiscuous mode on the ports */\n            pktgen.flags |= PROMISCUOUS_ON_FLAG;\n            break;\n\n        case 'N': /* Enable NUMA support. */\n            pktgen.flags |= NUMA_SUPPORT_FLAG;\n            break;\n\n        case 'G':\n            pktgen.flags |= IS_SERVER_FLAG;\n            break;\n\n        case 'g': /* Define the port number and IP address used for the socket connection. */\n            pktgen.flags |= IS_SERVER_FLAG;\n\n            p = strchr(optarg, ':');\n            if (p == NULL) /* No : symbol means pktgen is a server application. */\n                pktgen.hostname = (char *)strdupf(pktgen.hostname, optarg);\n            else {\n                char c = *p;\n\n                *p = '\\0';\n                if (p != optarg)\n                    pktgen.hostname = (char *)strdupf(pktgen.hostname, optarg);\n\n                pktgen.socket_port = strtol(++p, NULL, 0);\n                pktgen_log_info(\">>> Socket support %s%c0x%x\", pktgen.hostname, c,\n                                pktgen.socket_port);\n            }\n            break;\n\n        case 'T':\n            pktgen.flags |= ENABLE_THEME_FLAG;\n            break;\n        case 'c':\n            enable_clock_gettime(ENABLE_STATE);\n            break;\n        case 'v':\n            pktgen.verbose = 1;\n            break;\n\n        case 'h': /* print out the help message */\n            pktgen_usage(prgname);\n            return -1;\n\n        default:\n            pktgen_usage(prgname);\n            return -1;\n        }\n    }\n\n    /* Setup the program name */\n    if (optind >= 0)\n        argv[optind - 1] = prgname;\n\n    ret    = optind - 1;\n    optind = 1; /* reset getopt lib */\n\n    if (l2p_parse_mappings() < 0)\n        pktgen_log_error(\"error or too many mapping entries\");\n    if (pktgen_pcap_open() < 0)\n        pktgen_log_error(\"error opening PCAP files\");\n\n    return ret;\n\npcap_err:\n    pktgen_log_error(\"Invalid PCAP filename (%s) must include port number as P:filename\", optarg);\n    pktgen_usage(prgname);\n    return -1;\n}\n\n#define MAX_BACKTRACE 32\n\nstatic void\nsig_handler(int v __rte_unused)\n{\n    void *array[MAX_BACKTRACE];\n    size_t size;\n    char **strings;\n    size_t i;\n\n    scrn_setw(1);              /* Reset the window size, from possible crash run. */\n    scrn_printf(100, 1, \"\\n\"); /* Move the cursor to the bottom of the screen again */\n\n    printf(\"\\n======\");\n\n    if (v == SIGSEGV)\n        printf(\" Pktgen got a Segment Fault\\n\");\n    else if (v == SIGHUP)\n        printf(\" Pktgen received a SIGHUP\\n\");\n    else if (v == SIGINT)\n        printf(\" Pktgen received a SIGINT\\n\");\n    else if (v == SIGPIPE) {\n        printf(\" Pktgen received a SIGPIPE\\n\");\n        return;\n    } else\n        printf(\" Pktgen received signal %d\\n\", v);\n\n    printf(\"\\n\");\n\n    size    = backtrace(array, MAX_BACKTRACE);\n    strings = backtrace_symbols(array, size);\n\n    printf(\"Obtained %zd stack frames.\\n\", size);\n\n    for (i = 0; i < size; i++)\n        printf(\"%s\\n\", strings[i]);\n\n    free(strings);\n\n    cli_destroy();\n    scrn_destroy();\n\n    exit(-1);\n}\n\n#ifdef LUA_ENABLED\nstatic int\npktgen_lua_dofile(void *ld, const char *filename)\n{\n    int ret;\n\n    ret = lua_dofile((luaData_t *)ld, filename);\n\n    return ret;\n}\n#endif\n\n/**\n *\n * main - Main routine to setup pktgen.\n *\n * DESCRIPTION\n * Main routine to setup pktgen.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nint\nmain(int argc, char **argv)\n{\n    int32_t ret;\n    struct sigaction sa;\n    sigset_t set;\n\n    setlocale(LC_ALL, \"\");\n\n    sa.sa_handler = sig_handler;\n    sigemptyset(&sa.sa_mask);\n    sa.sa_flags = 0;\n\n    sigaction(SIGSEGV, &sa, NULL);\n    sigaction(SIGHUP, &sa, NULL);\n    sigaction(SIGINT, &sa, NULL);\n    sigaction(SIGPIPE, &sa, NULL);\n\n    /* Block SIGWINCH for all threads,\n     * because we only want it to be\n     * handled by the main thread */\n    sigemptyset(&set);\n    sigaddset(&set, SIGWINCH);\n    pthread_sigmask(SIG_BLOCK, &set, NULL);\n\n    scrn_setw(1);     /* Reset the window size, from possible crash run. */\n    scrn_pos(100, 1); /* Move the cursor to the bottom of the screen again */\n\n    print_copyright(PKTGEN_VERSION, PKTGEN_VER_CREATED_BY);\n    fflush(stdout);\n\n    /* call before the rte_eal_init() */\n    (void)rte_set_application_usage_hook(pktgen_usage);\n\n    memset(&pktgen, 0, sizeof(pktgen));\n\n    pktgen.flags             = PRINT_LABELS_FLAG;\n    pktgen.ident             = 0x1234;\n    pktgen.nb_rxd            = DEFAULT_RX_DESC;\n    pktgen.nb_txd            = DEFAULT_TX_DESC;\n    pktgen.nb_ports_per_page = DEFAULT_PORTS_PER_PAGE;\n\n    l2p_create();\n\n    pktgen.portdesc_cnt = get_portdesc(pktgen.portdesc, RTE_MAX_ETHPORTS, 0);\n\n    /* Initialize the screen and logging */\n    pktgen_init_log();\n    pktgen_cpu_init();\n\n    /* initialize EAL */\n    ret = rte_eal_init(argc, argv);\n    if (ret < 0)\n        return -1;\n\n    argc -= ret;\n    argv += ret;\n\n    if (pktgen_cli_create()) {\n        cli_destroy();\n        scrn_destroy();\n        return -1;\n    }\n\n#ifdef LUA_ENABLED\n    lua_newlib_add(pktgen_lua_openlib, 0);\n\n    /* Open the Lua script handler. */\n    if ((pktgen.ld = lua_create_instance()) == NULL) {\n        pktgen_log_error(\"Failed to open Lua pktgen support library\");\n        return -1;\n    }\n    cli_set_lua_callback(pktgen_lua_dofile);\n    cli_set_user_state(pktgen.ld);\n#endif\n\n    /* parse application arguments (after the EAL ones) */\n    ret = pktgen_parse_args(argc, argv);\n    if (ret < 0) {\n        cli_destroy();\n        scrn_destroy();\n        return -1;\n    }\n\n    if (l2p_get_pid_by_lcore(rte_get_main_lcore()) < RTE_MAX_ETHPORTS) {\n        cli_printf(\"*** Error can not use initial lcore %d for port handling\\n\",\n                   rte_get_main_lcore());\n        cli_printf(\"    The initial lcore is %d\\n\", rte_get_main_lcore());\n        cli_destroy();\n        scrn_destroy();\n        exit(-1);\n    }\n\n    pktgen.hz = pktgen_get_timer_hz(); /* Get the starting HZ value. */\n\n    scrn_create_with_defaults(pktgen.flags & ENABLE_THEME_FLAG);\n\n    rte_delay_us_sleep(100 * 1000); /* Wait a bit for things to settle. */\n\n    if (pktgen.verbose)\n        pktgen_log_info(\n            \">>> Packet Max Burst %d/%d, RX Desc %d, TX Desc %d, mbufs/port %d, mbuf cache %d\",\n            MAX_PKT_RX_BURST, MAX_PKT_TX_BURST, pktgen.nb_rxd, pktgen.nb_txd,\n            MAX_MBUFS_PER_PORT(pktgen.nb_rxd, pktgen.nb_txd), MBUF_CACHE_SIZE);\n\n    /* Configure and initialize the ports */\n    pktgen_config_ports();\n\n    if (pktgen.verbose) {\n        pktgen_log_info(\"\");\n        pktgen_log_info(\"=== Display processing on lcore %d\", rte_lcore_id());\n    }\n\n    /* launch per-lcore init on every lcore except initial and initial + 1 lcores */\n    ret = rte_eal_mp_remote_launch(pktgen_launch_one_lcore, NULL, SKIP_MAIN);\n    if (ret != 0)\n        pktgen_log_error(\"Failed to start lcores, return %d\", ret);\n\n    for (uint16_t i = 0; i < 4; i++)\n        printf(\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\");\n    fflush(stdout);\n\n    /* Disable printing log messages of level info and below to screen, */\n    /* erase the screen and start updating the screen again. */\n    pktgen_log_set_screen_level(LOG_LEVEL_WARNING);\n    scrn_erase(this_scrn->nrows);\n\n    scrn_resume();\n\n    pktgen_clear_display();\n\n    pktgen_timer_setup();\n\n#ifdef LUA_ENABLED\n    if (pktgen.flags & IS_SERVER_FLAG) {\n        pktgen.ld_sock = lua_create_instance();\n        if (pktgen.ld_sock == NULL) {\n            pktgen_log_error(\"Failed to open Lua socket server support library\");\n            return -1;\n        }\n\n        if (lua_start_socket(pktgen.ld_sock, &pktgen.thread, pktgen.hostname, pktgen.socket_port) <\n            0) {\n            pktgen_log_error(\"Failed to start Lua socket server thread\");\n            return -1;\n        }\n    }\n#endif\n\n    /* Unblock SIGWINCH so main thread\n     * can handle screen resizes */\n    sigemptyset(&set);\n    sigaddset(&set, SIGWINCH);\n    pthread_sigmask(SIG_UNBLOCK, &set, NULL);\n\n    /* execute the command files if present */\n    scrn_pause();\n    cli_execute_cmdfiles();\n    scrn_resume();\n    pktgen_clear_display();\n\n    cli_start(NULL); /* Start accepting input from user */\n\n    scrn_pause();\n    scrn_setw(1); /* Reset the window size, from possible crash run. */\n    /* Move the cursor to the bottom of the screen again */\n    scrn_printf(this_scrn->nrows + 1, 1, \"\\n\");\n\n    pktgen_stop_running();\n\n    /* Wait for all of the cores to stop running and exit. */\n    rte_eal_mp_wait_lcore();\n\n    int i = 0;\n    RTE_ETH_FOREACH_DEV(i)\n    {\n        rte_eth_dev_stop(i);\n        rte_delay_us_sleep(100 * 1000);\n    }\n\n    cli_destroy();\n    scrn_destroy();\n    return 0;\n}\n\n/**\n *\n * pktgen_stop_running - Stop pktgen to exit in a clean way\n *\n * DESCRIPTION\n * Stop all of the logical core threads to stop pktgen cleanly.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_stop_running(void)\n{\n#ifdef LUA_ENABLED\n    lua_execute_close(pktgen.ld);\n#endif\n\n    pktgen.timer_running = 0;\n    pktgen.force_quit    = 1;\n}\n"
  },
  {
    "path": "app/pktgen-main.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_MAIN_H_\n#define _PKTGEN_MAIN_H_\n\n/**\n * @file\n *\n * Pktgen main loop and lifecycle control functions.\n */\n\n#include <stdint.h>\n#include <termios.h>\n#include <stdio.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Enter the interactive CLI loop and block until the user quits.\n *\n * Runs the CLI read-eval-print loop on the calling lcore, processing\n * user commands until pktgen_stop_running() signals termination.\n */\nvoid pktgen_interact(void);\n\n/**\n * Return the Lua state pointer for the active Lua instance.\n *\n * @return\n *   Pointer to the lua_State, or NULL if Lua is not enabled.\n */\nvoid *pktgen_get_lua(void);\n\n/**\n * Signal the main loop to stop and exit gracefully.\n */\nvoid pktgen_stop_running(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_MAIN_H_ */\n"
  },
  {
    "path": "app/pktgen-pcap.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <lua_config.h>\n\n#include \"pktgen-display.h\"\n#include \"pktgen-log.h\"\n\n#include \"pktgen.h\"\n\n#ifndef MBUF_INVALID_PORT\n#define MBUF_INVALID_PORT UINT16_MAX\n#endif\n\nstatic pcap_info_t *pcap_info_list[RTE_MAX_ETHPORTS];\n\nvoid\npktgen_pcap_info(pcap_info_t *pcap, uint16_t port, int flag)\n{\n    printf(\"PCAP file for port %d: %s\\n\", port, pcap->filename);\n    printf(\"  magic: %08x,\", pcap->info.magic_number);\n    printf(\" Version: %d.%d,\", pcap->info.version_major, pcap->info.version_minor);\n    printf(\" Zone: %d,\", pcap->info.thiszone);\n    printf(\" snaplen: %d,\", pcap->info.snaplen);\n    printf(\" sigfigs: %d,\", pcap->info.sigfigs);\n    printf(\" network: %d\", pcap->info.network);\n    printf(\" Convert Endian: %s\\n\", pcap->convert ? \"Yes\" : \"No\");\n    if (flag)\n        printf(\"  Packet count: %d, max size %d\\n\", pcap->pkt_count, pcap->max_pkt_size);\n    fflush(stdout);\n}\n\nstatic __inline__ void\npcap_convert(pcap_info_t *pcap, pcap_record_hdr_t *pHdr)\n{\n    if (pcap->convert) {\n        pHdr->incl_len = ntohl(pHdr->incl_len);\n        pHdr->orig_len = ntohl(pHdr->orig_len);\n        pHdr->ts_sec   = ntohl(pHdr->ts_sec);\n        pHdr->ts_usec  = ntohl(pHdr->ts_usec);\n    }\n}\n\nstatic void\npcap_rewind(pcap_info_t *pcap)\n{\n    /* Rewind to the beginning */\n    rewind(pcap->fp);\n\n    /* Seek past the pcap header */\n    (void)fseek(pcap->fp, sizeof(pcap_hdr_t), SEEK_SET);\n}\n\nstatic void\npcap_get_info(pcap_info_t *pcap)\n{\n    pcap_record_hdr_t hdr;\n\n    if (fread(&pcap->info, 1, sizeof(pcap_hdr_t), pcap->fp) != sizeof(pcap_hdr_t))\n        rte_exit(EXIT_FAILURE, \"%s: failed to read pcap header\\n\", __func__);\n\n    /* Make sure we have a valid PCAP file for Big or Little Endian formats. */\n    if (pcap->info.magic_number == PCAP_MAGIC_NUMBER)\n        pcap->convert = 0;\n    else if (pcap->info.magic_number == ntohl(PCAP_MAGIC_NUMBER))\n        pcap->convert = 1;\n    else\n        rte_exit(EXIT_FAILURE, \"%s: invalid magic number 0x%08x\\n\", __func__,\n                 pcap->info.magic_number);\n\n    if (pcap->convert) {\n        pcap->info.magic_number  = ntohl(pcap->info.magic_number);\n        pcap->info.version_major = ntohs(pcap->info.version_major);\n        pcap->info.version_minor = ntohs(pcap->info.version_minor);\n        pcap->info.thiszone      = ntohl(pcap->info.thiszone);\n        pcap->info.sigfigs       = ntohl(pcap->info.sigfigs);\n        pcap->info.snaplen       = ntohl(pcap->info.snaplen);\n        pcap->info.network       = ntohl(pcap->info.network);\n    }\n\n    pcap->max_pkt_size  = 0;\n    pcap->avg_pkt_size  = 0;\n    uint64_t total_size = 0;\n    /* count the number of packets and get the largest size packet */\n    for (;;) {\n        if (fread(&hdr, 1, sizeof(pcap_record_hdr_t), pcap->fp) != sizeof(hdr))\n            break;\n\n        /* Convert the packet header to the correct format if needed */\n        pcap_convert(pcap, &hdr);\n\n        if (fseek(pcap->fp, hdr.incl_len, SEEK_CUR) < 0)\n            break;\n\n        pcap->pkt_count++;\n        if (hdr.incl_len > pcap->max_pkt_size)\n            pcap->max_pkt_size = hdr.incl_len;\n\n        total_size += hdr.incl_len;\n    }\n    printf(\"PCAP: Max Packet Size: %d\\n\", pcap->max_pkt_size);\n\n    pcap->avg_pkt_size = total_size / pcap->pkt_count;\n\n    printf(\"PCAP: Avg Packet Size: %d\\n\", pcap->avg_pkt_size);\n\n    pcap_rewind(pcap);\n}\n\nstatic __inline__ void\nmbuf_iterate_cb(struct rte_mempool *mp, void *opaque, void *obj, unsigned obj_idx __rte_unused)\n{\n    pcap_info_t *pcap     = (pcap_info_t *)opaque;\n    struct rte_mbuf *m    = (struct rte_mbuf *)obj;\n    pcap_record_hdr_t hdr = {0};\n\n    if (fread(&hdr, 1, sizeof(pcap_record_hdr_t), pcap->fp) != sizeof(hdr)) {\n        pcap_rewind(pcap);\n        if (fread(&hdr, 1, sizeof(pcap_record_hdr_t), pcap->fp) != sizeof(hdr))\n            rte_exit(EXIT_FAILURE, \"%s: failed to read pcap header\\n\", __func__);\n    }\n\n    pcap_convert(pcap, &hdr); /* Convert the packet header to the correct format. */\n\n    if (fread(rte_pktmbuf_mtod(m, char *), 1, hdr.incl_len, pcap->fp) == 0)\n        rte_exit(EXIT_FAILURE, \"%s: failed to read packet data from PCAP file\\n\", __func__);\n\n    m->pool     = mp;\n    m->next     = NULL;\n    m->data_len = hdr.incl_len;\n    m->pkt_len  = hdr.incl_len;\n    m->port     = 0;\n    m->ol_flags = 0;\n}\n\nint\npktgen_pcap_add(char *filename, uint16_t pid)\n{\n    pcap_info_t *pcap = NULL;\n    char name[64]     = {0};\n    uint16_t sid;\n\n    if (filename == NULL)\n        rte_exit(EXIT_FAILURE, \"%s: PCAP filename is NULL\\n\", __func__);\n\n    sid = pg_eth_dev_socket_id(pid);\n\n    snprintf(name, sizeof(name), \"PCAP-Info-%d\", pid);\n    pcap = (pcap_info_t *)rte_zmalloc_socket(name, sizeof(pcap_info_t), RTE_CACHE_LINE_SIZE, sid);\n    if (pcap == NULL)\n        rte_exit(EXIT_FAILURE, \"%s: rte_zmalloc_socket() failed for pcap_info_t structure\\n\",\n                 __func__);\n\n    /* Default to little endian format. */\n    pcap->filename = strdup(filename);\n\n    pcap_info_list[pid] = pcap;\n\n    return 0;\n}\n\nint\npktgen_pcap_open(void)\n{\n    pcap_info_t *pcap = NULL;\n    struct rte_mempool *mp;\n    char name[64] = {0};\n    uint16_t sid;\n    uint32_t pkt_count;\n\n    for (int pid = 0; pid < RTE_MAX_ETHPORTS; pid++) {\n        if ((pcap = pcap_info_list[pid]) == NULL)\n            continue;\n\n        pcap = pcap_info_list[pid];\n\n        sid = pg_eth_dev_socket_id(pid);\n\n        /* Read the pcap file trailer. */\n        pcap->fp = fopen((const char *)pcap->filename, \"r\");\n        if (pcap->fp == NULL)\n            rte_exit(EXIT_FAILURE, \"%s: failed for (%s)\\n\", __func__, pcap->filename);\n\n        pcap_get_info(pcap);\n\n        pkt_count = pcap->pkt_count;\n        if (pkt_count == 0) {\n            fclose(pcap->fp);\n            rte_exit(EXIT_FAILURE, \"%s: PCAP file is empty: %s\\n\", __func__, pcap->filename);\n        }\n        if (pkt_count < (DEFAULT_TX_DESC * 4))\n            pkt_count = (DEFAULT_TX_DESC * 4);\n\n        snprintf(name, sizeof(name), \"pcap-%d\", pid);\n        uint32_t dataroom =\n            RTE_ALIGN_CEIL(pcap->max_pkt_size + RTE_PKTMBUF_HEADROOM, RTE_CACHE_LINE_SIZE);\n        mp = rte_pktmbuf_pool_create(name, pkt_count, 0, DEFAULT_PRIV_SIZE, dataroom, sid);\n        if (mp == NULL)\n            rte_exit(EXIT_FAILURE,\n                     \"Cannot create mbuf pool (%s) port %d, nb_mbufs %d, socket_id %d: %s\", name,\n                     pid, pcap->pkt_count, sid, rte_strerror(rte_errno));\n\n        pcap->mp = mp;\n\n        rte_mempool_obj_iter(mp, mbuf_iterate_cb, pcap);\n\n        if (l2p_set_pcap_info(pid, pcap) < 0)\n            pktgen_log_error(\"Error opening PCAP file: %s\", pcap->filename);\n    }\n    return 0;\n}\n\nvoid\npktgen_pcap_close(void)\n{\n    pcap_info_t *pcap = NULL;\n\n    for (int pid = 0; pid < RTE_MAX_ETHPORTS; pid++) {\n        pcap = pcap_info_list[pid];\n        if (pcap == NULL)\n            return;\n\n        if (pcap->filename)\n            free(pcap->filename);\n        if (pcap->fp)\n            fclose(pcap->fp);\n        if (pcap->mp)\n            rte_mempool_free(pcap->mp);\n        rte_free(pcap);\n    }\n}\n\nFILE *\npktgen_create_pcap_file(char *filename)\n{\n    struct pcap_file_header file_header;\n    file_header.magic         = 0xa1b2c3d4;\n    file_header.version_major = 2;\n    file_header.version_minor = 4;\n    file_header.thiszone      = 0;\n    file_header.sigfigs       = 0;\n    file_header.snaplen       = 65535;\n    file_header.linktype      = 1;        // LINKTYPE_ETHERNET\n\n    printf(\"Creating PCAP file: %s, %lu, %lu\\n\", filename, sizeof(struct pcap_file_header),\n           sizeof(struct pcap_pkthdr));\n\n    // Open the output file\n    FILE *file = fopen(filename, \"wb\");\n    if (!file)\n        return NULL;\n\n    // Write the file header\n    fwrite(&file_header, sizeof(uint8_t), sizeof(file_header), file);\n    fflush(file);\n\n    return file;\n}\n\nvoid\npktgen_close_pcap_file(FILE *fp)\n{\n    if (fp)\n        fclose(fp);\n}\n\nint\npktgen_write_mbuf_to_pcap_file(FILE *fp, struct rte_mbuf *mbuf)\n{\n    // Packet header\n    pcap_record_hdr_t packet_header;\n    size_t size;\n\n    if (fp == NULL)\n        return 0;\n\n    packet_header.ts_sec   = 0;\n    packet_header.ts_usec  = 0;\n    packet_header.incl_len = rte_pktmbuf_pkt_len(mbuf);\n    packet_header.orig_len = rte_pktmbuf_pkt_len(mbuf);\n\n    // Write the packet header\n    if ((size = fwrite(&packet_header, sizeof(uint8_t), sizeof(packet_header), fp)) != 16)\n        printf(\"Error writing packet header %ld\\n\", size);\n\n    // Write the packet data\n    fwrite(rte_pktmbuf_mtod(mbuf, char *), sizeof(uint8_t), rte_pktmbuf_pkt_len(mbuf), fp);\n    fflush(fp);\n\n    return 0;\n}\n"
  },
  {
    "path": "app/pktgen-pcap.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_PCAP_H_\n#define _PKTGEN_PCAP_H_\n\n/**\n * @file\n *\n * PCAP file read/write support for Pktgen.\n *\n * Provides structures mirroring the libpcap file format, plus functions for\n * opening, replaying, and writing PCAP files from mbufs.\n */\n\n#include <pcap/bpf.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define PCAP_MAGIC_NUMBER  0xa1b2c3d4 /**< PCAP global header magic (little-endian) */\n#define PCAP_MAJOR_VERSION 2          /**< PCAP file format major version */\n#define PCAP_MINOR_VERSION 4          /**< PCAP file format minor version */\n\n/** PCAP global file header. */\ntypedef struct pcap_hdr_s {\n    uint32_t magic_number;  /**< magic number */\n    uint16_t version_major; /**< major version number */\n    uint16_t version_minor; /**< minor version number */\n    int32_t thiszone;       /**< GMT to local correction */\n    uint32_t sigfigs;       /**< accuracy of timestamps */\n    uint32_t snaplen;       /**< max length of captured packets, in octets */\n    uint32_t network;       /**< data link type */\n} pcap_hdr_t;\n\n/** PCAP per-packet record header. */\ntypedef struct pcap_record_hdr_s {\n    uint32_t ts_sec;   /**< timestamp seconds */\n    uint32_t ts_usec;  /**< timestamp microseconds */\n    uint32_t incl_len; /**< number of octets of packet saved in file */\n    uint32_t orig_len; /**< actual length of packet */\n} pcap_record_hdr_t;\n\n/** Pktgen PCAP replay state for one port. */\ntypedef struct pcap_info_s {\n    char *filename;                  /**< allocated string for filename of pcap */\n    FILE *fp;                        /**< file pointer for pcap file */\n    struct rte_mempool *mp;          /**< Mempool for storing packets */\n    uint32_t convert;                /**< Endian flag value if 1 convert to host endian format */\n    uint32_t max_pkt_size;           /**< largest packet found in pcap file */\n    uint32_t avg_pkt_size;           /**< average packet size in pcap file */\n    uint32_t pkt_count;              /**< Number of packets in pcap file */\n    uint32_t pkt_index;              /**< Index of current packet in pcap file */\n    pcap_hdr_t info;                 /**< information on the PCAP file */\n    int32_t pcap_result;             /**< PCAP result of filter compile */\n    struct bpf_program pcap_program; /**< PCAP filter program structure */\n\n} pcap_info_t;\n\n/**\n * Register a PCAP file for replay on a port.\n *\n * @param filename  Path to the PCAP file.\n * @param port      Port ID to associate with this PCAP.\n * @return\n *   0 on success, negative on error.\n */\nint pktgen_pcap_add(char *filename, uint16_t port);\n\n/**\n * Open all registered PCAP files and load packets into mempools.\n *\n * @return\n *   0 on success, negative on error.\n */\nint pktgen_pcap_open(void);\n\n/** Close all open PCAP file handles. */\nvoid pktgen_pcap_close(void);\n\n/**\n * Print PCAP file information to the display.\n *\n * @param pcap  PCAP info structure to display.\n * @param port  Port ID.\n * @param flag  Display verbosity flag.\n */\nvoid pktgen_pcap_info(pcap_info_t *pcap, uint16_t port, int flag);\n\n/**\n * Create a new PCAP output file for packet capture.\n *\n * @param filename  Path to the output PCAP file.\n * @return\n *   Open FILE pointer, or NULL on error.\n */\nFILE *pktgen_create_pcap_file(char *filename);\n\n/**\n * Close and finalise a PCAP output file.\n *\n * @param fp  File pointer returned by pktgen_create_pcap_file().\n */\nvoid pktgen_close_pcap_file(FILE *fp);\n\n/**\n * Append one mbuf as a packet record to an open PCAP file.\n *\n * @param fp    Open PCAP file handle.\n * @param mbuf  mbuf containing the packet to write.\n * @return\n *   0 on success, negative on write error.\n */\nint pktgen_write_mbuf_to_pcap_file(FILE *fp, struct rte_mbuf *mbuf);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_PCAP_H_ */\n"
  },
  {
    "path": "app/pktgen-port-cfg.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include \"pg_compat.h\"\n#include \"pktgen-cmds.h\"\n#include \"pktgen-log.h\"\n#include \"l2p.h\"\n\n#include <rte_dev.h>\n\n#include <link.h>\n\n#if defined(RTE_LIBRTE_PMD_BOND) || defined(RTE_NET_BOND)\n#include <rte_eth_bond_8023ad.h>\n#endif\n#include <rte_bus_pci.h>\n#include <rte_bus.h>\n\nenum {\n    RX_PTHRESH = 8, /**< Default values of RX prefetch threshold reg. */\n    RX_HTHRESH = 8, /**< Default values of RX host threshold reg. */\n    RX_WTHRESH = 4, /**< Default values of RX write-back threshold reg. */\n\n    TX_PTHRESH     = 36, /**< Default values of TX prefetch threshold reg. */\n    TX_HTHRESH     = 0,  /**< Default values of TX host threshold reg. */\n    TX_WTHRESH     = 0,  /**< Default values of TX write-back threshold reg. */\n    TX_WTHRESH_1GB = 16, /**< Default value for 1GB ports */\n};\n\n/**\n * An array of drivers that require a pseudo-header calculation before the checksum calculation.\n * The names used are the ones used by DPDK.\n */\nstatic const char *DRIVERS_REQUIRING_PHDR[] = {\n    \"net_ixgbe\",\n    // TODO: Add the others\n};\n\nstatic struct rte_eth_conf default_port_conf = {\n    .rxmode =\n        {\n            .mq_mode          = RTE_ETH_MQ_RX_RSS,\n            .max_lro_pkt_size = RTE_ETHER_MAX_LEN,\n            .offloads         = RTE_ETH_RX_OFFLOAD_CHECKSUM,\n            .mtu              = RTE_ETHER_MTU,\n        },\n    .txmode =\n        {\n            .mq_mode = RTE_ETH_MQ_TX_NONE,\n        },\n    .rx_adv_conf =\n        {\n            .rss_conf =\n                {\n                    .rss_key = NULL,\n                    .rss_hf  = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP |\n                               RTE_ETH_RSS_SCTP | RTE_ETH_RSS_L2_PAYLOAD,\n                },\n        },\n    .intr_conf =\n        {\n            .lsc = 0,\n        },\n};\n\nstatic void\ndump_device_info(void)\n{\n    printf(\"\\n%-4s %-16s %-5s %-4s %-22s %-17s %s\\n\", \"Port\", \"DevName\", \"Index\", \"NUMA\",\n           \"PCI Information\", \"Src MAC\", \"Promiscuous\");\n\n    for (uint16_t i = 0; i < pktgen.nb_ports; i++) {\n        struct rte_eth_dev_info dev;\n        const struct rte_bus *bus = NULL;\n        port_info_t *pinfo;\n        pkt_seq_t *pkt;\n        char buff[128];\n\n        if (rte_eth_dev_info_get(i, &dev)) {\n            printf(buff, sizeof(buff), \"%6u No device information: %s\", i, rte_strerror(rte_errno));\n            continue;\n        }\n\n        buff[0] = 0;\n        if (dev.device)\n            bus = rte_bus_find_by_device(dev.device);\n        if (bus && !strcmp(rte_bus_name(bus), \"pci\")) {\n            char name[RTE_ETH_NAME_MAX_LEN];\n            char vend[8], device[8];\n\n            vend[0] = device[0] = '\\0';\n            sscanf(rte_dev_bus_info(dev.device), \"vendor_id=%4s, device_id=%4s\", vend, device);\n\n            rte_eth_dev_get_name_by_port(i, name);\n            snprintf(buff, sizeof(buff), \"%s:%s/%s\", vend, device, rte_dev_name(dev.device));\n        } else\n            snprintf(buff, sizeof(buff), \"non-PCI device\");\n        pinfo = l2p_get_port_pinfo(i);\n        pkt   = &pinfo->seq_pkt[SINGLE_PKT];\n        printf(\"%3u  %-16s %5d %4d %s %02x:%02x:%02x:%02x:%02x:%02x <%s>\\n\", i, dev.driver_name,\n               dev.if_index, rte_dev_numa_node(dev.device), buff, pkt->eth_src_addr.addr_bytes[0],\n               pkt->eth_src_addr.addr_bytes[1], pkt->eth_src_addr.addr_bytes[2],\n               pkt->eth_src_addr.addr_bytes[3], pkt->eth_src_addr.addr_bytes[4],\n               pkt->eth_src_addr.addr_bytes[5],\n               (pktgen.flags & PROMISCUOUS_ON_FLAG) ? \"Enabled\" : \"Disabled\");\n    }\n    printf(\"\\n\");\n}\n\n/**\n * Determines whether the pseudo-header is required when calculating the checksum.\n * Depends on the original NIC driver (e.g., ixgbe NICs expect the pseudo-header)\n * See Table 1.133: https://doc.dpdk.org/guides/nics/overview.html\n */\nbool\nis_cksum_phdr_required(const char *driver_name)\n{\n    size_t num_drivers = RTE_DIM(DRIVERS_REQUIRING_PHDR);\n\n    for (size_t i = 0; i < num_drivers; i++) {\n        if (DRIVERS_REQUIRING_PHDR[i] == NULL)\n            break;\n        if (strcmp(driver_name, DRIVERS_REQUIRING_PHDR[i]) == 0)\n            return true;\n    }\n\n    return false;\n}\n\nstatic uint32_t\neth_dev_get_overhead_len(uint32_t max_rx_pktlen, uint16_t max_mtu)\n{\n    uint32_t overhead_len;\n\n    if (max_mtu != UINT16_MAX && max_rx_pktlen > max_mtu)\n        overhead_len = max_rx_pktlen - max_mtu;\n    else\n        overhead_len = RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN;\n\n    return overhead_len;\n}\n\nstatic port_info_t *\nallocate_port_info(uint16_t pid)\n{\n    port_info_t *pinfo = l2p_get_port_pinfo(pid);\n    int32_t sid        = pg_eth_dev_socket_id(pid);\n\n    pktgen_log_info(\"Port info setup for port %u\", pid);\n\n    /* If port info is already set ignore */\n    if (pinfo) {\n        pktgen_log_error(\"Port info already setup for port %u\", pid);\n        goto leave;\n    }\n\n    /* Allocate each port_info_t structure on the correct NUMA node for the port */\n    if ((pinfo = rte_zmalloc_socket(NULL, sizeof(port_info_t), RTE_CACHE_LINE_SIZE, sid)) == NULL)\n        goto leave;\n\n    pinfo->pid     = pid;\n    pinfo->max_mtu = RTE_ETHER_MAX_LEN;\n    pinfo->conf    = default_port_conf;\n\n    if (rte_eth_dev_info_get(pid, &pinfo->dev_info) < 0) {\n        pktgen_log_error(\"Cannot get device info for port %u\", pid);\n        goto leave;\n    }\n\n    for (int qid = 0; qid < l2p_get_txcnt(pid); qid++) {\n        per_queue_t *pq = &pinfo->per_queue[qid];\n        char buff[64];\n\n        snprintf(buff, sizeof(buff), \"RxMbufs-%u-%d\", pid, qid);\n        pq->rx_pkts = rte_calloc_socket(buff, MAX_PKT_RX_BURST, sizeof(struct rte_mbuf *),\n                                        RTE_CACHE_LINE_SIZE, sid);\n        if (pq->rx_pkts == NULL) {\n            pktgen_log_error(\"Cannot allocate RX burst for port %u-%d\", pid, qid);\n            goto leave;\n        }\n    }\n\n    for (int qid = 0; qid < l2p_get_txcnt(pid); qid++) {\n        per_queue_t *pq = &pinfo->per_queue[qid];\n        char buff[64];\n\n        snprintf(buff, sizeof(buff), \"TxMbufs-%u-%d\", pid, qid);\n        pq->tx_pkts = rte_calloc_socket(buff, MAX_PKT_TX_BURST, sizeof(struct rte_mbuf *),\n                                        RTE_CACHE_LINE_SIZE, sid);\n        if (pq->tx_pkts == NULL) {\n            pktgen_log_error(\"Cannot allocate TX burst for port %u-%d\", pid, qid);\n            goto leave;\n        }\n    }\n\n    if (l2p_set_port_pinfo(pid, pinfo)) {\n        pktgen_log_error(\"Failed to set port info for port %u\", pid);\n        goto leave;\n    }\n\n    pktgen_log_info(\"   Allocate packet sequence array\");\n\n    size_t pktsz = RTE_ETHER_MAX_LEN;\n    if (pktgen.flags & JUMBO_PKTS_FLAG)\n        pktsz = RTE_ETHER_MAX_JUMBO_FRAME_LEN;\n\n    /* allocate the sequence packet array */\n    pinfo->seq_pkt =\n        rte_zmalloc_socket(NULL, (sizeof(pkt_seq_t) * NUM_TOTAL_PKTS), RTE_CACHE_LINE_SIZE, sid);\n    if (pinfo->seq_pkt == NULL) {\n        pktgen_log_error(\"Unable to allocate %'ld pkt_seq_t headers\", (long int)NUM_TOTAL_PKTS);\n        goto leave;\n    }\n\n    for (int i = 0; i < NUM_TOTAL_PKTS; i++) {\n        pinfo->seq_pkt[i].hdr = rte_zmalloc_socket(NULL, pktsz, RTE_CACHE_LINE_SIZE, sid);\n        if (pinfo->seq_pkt[i].hdr == NULL)\n            pktgen_log_panic(\"Unable to allocate %ld pkt_seq_t buffer space\", pktsz);\n\n        pinfo->seq_pkt[i].seq_enabled = 1;\n        pinfo->seq_pkt[i].tcp_flags   = DEFAULT_TCP_FLAGS;\n        pinfo->seq_pkt[i].tcp_seq     = DEFAULT_TCP_SEQ_NUMBER;\n        pinfo->seq_pkt[i].tcp_ack     = DEFAULT_TCP_ACK_NUMBER;\n    }\n\n    /* Determines if pseudo-header is needed, based on the driver type */\n    pinfo->cksum_requires_phdr = is_cksum_phdr_required(pinfo->dev_info.driver_name);\n    pktgen_log_info(\"   Checksum offload Pseudo-header required: %s\",\n                    pinfo->cksum_requires_phdr ? \"Yes\" : \"No\");\n\n    return pinfo;\nleave:\n    if (pinfo) {\n        for (int i = 0; i < MAX_QUEUES_PER_PORT; i++) {\n            per_queue_t *pq = &pinfo->per_queue[i];\n\n            rte_free(pq->rx_pkts);\n            rte_free(pq->tx_pkts);\n        }\n        rte_free(pinfo);\n        l2p_set_port_pinfo(pid, NULL);\n    }\n    return NULL;\n}\n\nstatic void\n_latency_defaults(port_info_t *pinfo)\n{\n    latency_t *lat = &pinfo->latency;\n\n    pktgen_log_info(\"   Setup latency defaults\");\n\n    lat->jitter_threshold_us = DEFAULT_JITTER_THRESHOLD;\n    lat->latency_rate_us     = DEFAULT_LATENCY_RATE;\n    lat->latency_entropy     = DEFAULT_LATENCY_ENTROPY;\n    lat->latency_rate_cycles =\n        pktgen_get_timer_hz() / ((uint64_t)MAX_LATENCY_RATE / lat->latency_rate_us);\n    uint64_t ticks               = pktgen_get_timer_hz() / (uint64_t)1000000;\n    lat->jitter_threshold_cycles = lat->jitter_threshold_us * ticks;\n}\n\nstatic void\n_fill_pattern_defaults(port_info_t *pinfo)\n{\n    pktgen_log_info(\"   Setup fill pattern defaults\");\n\n    pinfo->fill_pattern_type = ABC_FILL_PATTERN;\n    snprintf(pinfo->user_pattern, sizeof(pinfo->user_pattern), \"%s\", \"0123456789abcdef\");\n}\n\nstatic void\n_mtu_defaults(port_info_t *pinfo)\n{\n    struct rte_eth_dev_info *dinfo = &pinfo->dev_info;\n    struct rte_eth_conf *conf      = &pinfo->conf;\n\n    pktgen_log_info(\"   Setup MTU defaults and Jumbo Frames are %s\",\n                    (pktgen.flags & JUMBO_PKTS_FLAG) ? \"Enabled\" : \"Disabled\");\n\n    if (pinfo->max_mtu < dinfo->min_mtu)\n        pinfo->max_mtu = dinfo->min_mtu;\n    if (pinfo->max_mtu > dinfo->max_mtu)\n        pinfo->max_mtu = dinfo->max_mtu;\n    if (pktgen.flags & JUMBO_PKTS_FLAG) {\n        uint32_t eth_overhead_len;\n\n        conf->rxmode.max_lro_pkt_size = PG_JUMBO_ETHER_MTU;\n        eth_overhead_len = eth_dev_get_overhead_len(dinfo->max_rx_pktlen, dinfo->max_mtu);\n        pinfo->max_mtu   = dinfo->max_mtu - eth_overhead_len;\n\n        /* device may have higher theoretical MTU e.g. for infiniband */\n        if (pinfo->max_mtu > PG_JUMBO_ETHER_MTU)\n            pinfo->max_mtu = PG_JUMBO_ETHER_MTU;\n\n        pktgen_log_info(\n            \"     Jumbo Frames: Default Max Rx pktlen: %'u, MTU %'u, overhead len: %'u, \"\n            \"New MTU %'d\",\n            dinfo->max_rx_pktlen, dinfo->max_mtu, eth_overhead_len, pinfo->max_mtu);\n\n        pktgen_log_info(\"     Note: Using Scatter/Multi-segs offloads\");\n        pktgen_log_info(\"     Note: Performance may be degraded due to reduced Tx performance\");\n\n        // FIXME: Tx performance takes a big hit when enabled\n        if (dinfo->rx_offload_capa & RTE_ETH_RX_OFFLOAD_SCATTER)\n            conf->rxmode.offloads |= RTE_ETH_RX_OFFLOAD_SCATTER;\n        if (dinfo->tx_offload_capa & RTE_ETH_TX_OFFLOAD_MULTI_SEGS)\n            conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_MULTI_SEGS;\n    }\n    conf->rxmode.mtu = pinfo->max_mtu;\n}\n\nstatic void\n_rx_offload_defaults(port_info_t *pinfo)\n{\n    struct rte_eth_dev_info *dinfo = &pinfo->dev_info;\n    struct rte_eth_conf *conf      = &pinfo->conf;\n\n    conf->rx_adv_conf.rss_conf.rss_key = NULL;\n    conf->rx_adv_conf.rss_conf.rss_hf &= dinfo->flow_type_rss_offloads;\n    if (dinfo->max_rx_queues == 1)\n        conf->rxmode.mq_mode = RTE_ETH_MQ_RX_NONE;\n\n    if (dinfo->max_vfs) {\n        if (conf->rx_adv_conf.rss_conf.rss_hf != 0)\n            conf->rxmode.mq_mode = RTE_ETH_MQ_RX_VMDQ_RSS;\n    }\n    conf->rxmode.offloads &= dinfo->rx_offload_capa;\n}\n\nstatic void\n_tx_offload_defaults(port_info_t *pinfo)\n{\n    struct rte_eth_dev_info *dinfo = &pinfo->dev_info;\n    struct rte_eth_conf *conf      = &pinfo->conf;\n\n    pktgen_log_info(\"   Setup TX offload defaults\");\n\n    if (dinfo->tx_offload_capa & RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE)\n        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE;\n\n    if (dinfo->tx_offload_capa & RTE_ETH_TX_OFFLOAD_TCP_CKSUM) {\n        pktgen_log_info(\"   Enabling Tx TCP_CKSUM offload\");\n        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_TCP_CKSUM;\n    }\n\n    if (dinfo->tx_offload_capa & RTE_ETH_TX_OFFLOAD_UDP_CKSUM) {\n        pktgen_log_info(\"   Enabling Tx UDP_CKSUM offload\\r\\n\");\n        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_UDP_CKSUM;\n    }\n\n    if (dinfo->tx_offload_capa & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) {\n        pktgen_log_info(\"   Enabling Tx IPV4_CKSUM offload\\r\\n\");\n        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_IPV4_CKSUM;\n    }\n}\n\nstatic void\n_device_configuration(port_info_t *pinfo)\n{\n    uint16_t pid = pinfo->pid;\n    int ret;\n\n    pktgen_log_info(\"   Configure device: RxQueueCnt: %u, TxQueueCnt: %u\", l2p_get_rxcnt(pid),\n                    l2p_get_txcnt(pid));\n\n    ret = rte_eth_dev_configure(pid, l2p_get_rxcnt(pid), l2p_get_txcnt(pid), &pinfo->conf);\n    if (ret < 0)\n        pktgen_log_panic(\"Cannot configure device: port=%d, Num queues %d,%d\", pid,\n                         l2p_get_rxcnt(pid), l2p_get_txcnt(pid));\n}\n\nstatic void\n_rxtx_descriptors(port_info_t *pinfo)\n{\n    uint16_t pid = pinfo->pid;\n    int ret;\n\n    pktgen_log_info(\"   Setup number of descriptors RX: %u, TX: %u\", pktgen.nb_rxd, pktgen.nb_txd);\n\n    ret = rte_eth_dev_adjust_nb_rx_tx_desc(pid, &pktgen.nb_rxd, &pktgen.nb_txd);\n    if (ret < 0)\n        pktgen_log_panic(\"Can't adjust number of descriptors: port=%u:%s\", pid, rte_strerror(-ret));\n    pktgen_log_info(\"           Updated descriptors RX: %u, TX: %u\", pktgen.nb_rxd, pktgen.nb_txd);\n}\n\nstatic void\n_src_mac_address(port_info_t *pinfo)\n{\n    uint16_t pid = pinfo->pid;\n    char buff[64];\n\n    int ret = rte_eth_macaddr_get(pid, &pinfo->src_mac);\n    if (ret < 0)\n        pktgen_log_panic(\"Port %u, Failed to get source MAC address, (%d)%s\", pinfo->pid, -ret,\n                         rte_strerror(-ret));\n    else\n        pktgen_log_info(\"   Source MAC: %s\", inet_mtoa(buff, sizeof(buff), &pinfo->src_mac));\n}\n\nstatic void\n_device_ptypes(port_info_t *pinfo)\n{\n    pktgen_log_info(\"   Setup up device Ptypes\");\n\n    int ret = rte_eth_dev_set_ptypes(pinfo->pid, RTE_PTYPE_UNKNOWN, NULL, 0);\n    if (ret < 0)\n        pktgen_log_panic(\"Port %u, Failed to disable Ptype parsing\", pinfo->pid);\n}\n\nstatic void\n_device_mtu(port_info_t *pinfo)\n{\n    struct rte_eth_dev_info *dinfo = &pinfo->dev_info;\n    struct rte_eth_conf *conf      = &pinfo->conf;\n    uint32_t max_mtu               = RTE_ETHER_MAX_LEN;\n    int ret;\n\n    if (max_mtu < dinfo->min_mtu) {\n        pktgen_log_warning(\"Increasing MTU from %u to %u\", max_mtu, dinfo->min_mtu);\n        max_mtu = dinfo->min_mtu;\n    }\n    if (max_mtu > dinfo->max_mtu) {\n        pktgen_log_warning(\"Reducing MTU from %u to %u\", max_mtu, dinfo->max_mtu);\n        max_mtu = dinfo->max_mtu;\n    }\n    conf->rxmode.mtu = max_mtu;\n\n    if ((ret = rte_eth_dev_set_mtu(pinfo->pid, pinfo->max_mtu)) < 0)\n        pktgen_log_panic(\"Cannot set MTU on port %u, (%d)%s\", pinfo->pid, -ret, rte_strerror(-ret));\n}\n\nstatic void\n_rx_queues(port_info_t *pinfo)\n{\n    struct rte_eth_dev_info *dinfo = &pinfo->dev_info;\n    struct rte_eth_conf *conf      = &pinfo->conf;\n    uint16_t sid, pid = pinfo->pid;\n    int ret;\n\n    l2p_port_t *lport = l2p_get_port(pid);\n    if (lport == NULL)\n        pktgen_log_panic(\"Failed: l2p_port_t for port %u not found\", pid);\n\n    sid = pg_eth_dev_socket_id(pid);\n\n    pktgen_log_info(\"   Number of RX queues %u\", l2p_get_rxcnt(pid));\n    for (int q = 0; q < l2p_get_rxcnt(pid); q++) {\n        struct rte_eth_rxconf rxq_conf;\n\n        rxq_conf          = dinfo->default_rxconf;\n        rxq_conf.offloads = conf->rxmode.offloads;\n\n        pktgen_log_info(\"     RX queue %d enabled offloads: 0x%0lx, socket_id %u, mp %p\", q,\n                        rxq_conf.offloads, sid, lport->rx_mp[q]);\n\n        ret = rte_eth_rx_queue_setup(pid, q, pktgen.nb_rxd, sid, &rxq_conf, lport->rx_mp[q]);\n        if (ret < 0)\n            pktgen_log_panic(\"rte_eth_rx_queue_setup: err=%d, port=%d, %s\", ret, pid,\n                             rte_strerror(-ret));\n    }\n}\n\nstatic void\n_tx_queues(port_info_t *pinfo)\n{\n    struct rte_eth_dev_info *dinfo = &pinfo->dev_info;\n    struct rte_eth_conf *conf      = &pinfo->conf;\n    uint16_t sid, pid = pinfo->pid;\n    int ret;\n\n    l2p_port_t *lport = l2p_get_port(pid);\n    if (lport == NULL)\n        pktgen_log_panic(\"Failed: l2p_port_t for port %u not found\", pid);\n\n    sid = pg_eth_dev_socket_id(pid);\n\n    pktgen_log_info(\"   Number of TX queues %u\", l2p_get_txcnt(pid));\n    for (int q = 0; q < l2p_get_txcnt(pid); q++) {\n        struct rte_eth_txconf txq_conf;\n\n        txq_conf          = dinfo->default_txconf;\n        txq_conf.offloads = conf->txmode.offloads;\n\n        pktgen_log_info(\"     TX queue %d enabled offloads: 0x%0lx\", q, txq_conf.offloads);\n\n        ret = rte_eth_tx_queue_setup(pid, q, pktgen.nb_txd, sid, &txq_conf);\n        if (ret < 0)\n            pktgen_log_panic(\"rte_eth_tx_queue_setup: err=%d, port=%d, %s\", ret, pid,\n                             rte_strerror(-ret));\n    }\n}\n\nstatic void\n_promiscuous_mode(port_info_t *pinfo)\n{\n    /* If enabled, put device in promiscuous mode. */\n    if (pktgen.flags & PROMISCUOUS_ON_FLAG) {\n        pktgen_log_info(\"   Enabling promiscuous mode\");\n        if (rte_eth_promiscuous_enable(pinfo->pid))\n            pktgen_log_info(\"Enabling promiscuous failed: %s\", rte_strerror(-rte_errno));\n    }\n}\n\nstatic void\n_debug_output(port_info_t *pinfo)\n{\n    if (pktgen.verbose)\n        pktgen_log_info(\"%*sPort memory used = %6lu KB\", 57, \" \", (pktgen.mem_used + 1023) / 1024);\n\n    if (pktgen.verbose)\n        rte_eth_dev_info_dump(stderr, pinfo->pid);\n}\n\nstatic void\n_device_start(port_info_t *pinfo)\n{\n    int ret;\n\n    pktgen_log_info(\"   Start network device\");\n\n    /* Start device */\n    if ((ret = rte_eth_dev_start(pinfo->pid)) < 0)\n        pktgen_log_panic(\"rte_eth_dev_start: port=%d, %s\", pinfo->pid, rte_strerror(-ret));\n}\n\nstatic void\n_port_defaults(port_info_t *pinfo)\n{\n    pktgen_log_info(\"   Setup port defaults\");\n\n    pktgen_port_defaults(pinfo->pid);\n}\n\nstatic port_info_t *\ninitialize_port_info(uint16_t pid)\n{\n    port_info_t *pinfo = l2p_get_port_pinfo(pid);\n    // clang-format off\n    void (*setups[])(port_info_t *) = {\n        _latency_defaults,\n        _fill_pattern_defaults,\n        _mtu_defaults,\n        _rx_offload_defaults,\n        _tx_offload_defaults,\n        _device_configuration,\n        _rxtx_descriptors,\n        _src_mac_address,\n        _device_ptypes,\n        _device_mtu,\n        _rx_queues,\n        _tx_queues,\n        _debug_output,\n        _promiscuous_mode,\n        _port_defaults,\n        _device_start,\n    };\n    // clang-format on\n\n    if ((pinfo = allocate_port_info(pid)) == NULL)\n        pktgen_log_panic(\"Unable to allocate port_info_t for port %u\", pid);\n\n    for (uint16_t i = 0; i < RTE_DIM(setups); i++) {\n        if (setups[i])\n            setups[i](pinfo);\n    }\n\n    pktgen_set_port_flags(pinfo, SEND_SINGLE_PKTS);\n\n    return pinfo;\n}\n\n/**\n *\n * pktgen_config_ports - Configure the ports for RX and TX\n *\n * DESCRIPTION\n * Handle setting up the ports in DPDK.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_config_ports(void)\n{\n    uint16_t pid;\n    port_info_t *pinfo;\n\n    /* Find out the total number of ports in the system. */\n    /* We have already block list the ones we needed to in main routine. */\n    pktgen.nb_ports = rte_eth_dev_count_avail();\n    if (pktgen.nb_ports == 0)\n        pktgen_log_panic(\"*** Did not find any ports to use ***\");\n    if (pktgen.nb_ports > RTE_MAX_ETHPORTS)\n        pktgen_log_panic(\"*** Too many ports in the system %d ***\", pktgen.nb_ports);\n\n    /* Setup the number of ports to display at a time */\n    pktgen.ending_port =\n        ((pktgen.nb_ports > pktgen.nb_ports_per_page) ? pktgen.nb_ports_per_page : pktgen.nb_ports);\n\n    RTE_ETH_FOREACH_DEV(pid)\n    {\n        pktgen_log_info(\"Initialize Port %u ... \", pid);\n\n        pinfo = initialize_port_info(pid);\n        if (pinfo == NULL) {\n            pktgen_log_info(\"Failed: port_info_t for port %u initialize\\n\", pid);\n            return;\n        }\n    }\n\n    RTE_ETH_FOREACH_DEV(pid)\n    {\n        pktgen_log_info(\"Initialize Port %u sequence ... \", pid);\n        pinfo = l2p_get_port_pinfo(pid);\n\n        pktgen_log_info(\"   Setup sequence defaults\");\n        /* Setup the port and packet defaults */\n        pktgen_seq_defaults(pid);\n\n        pktgen_log_info(\"   Setup range defaults\");\n        pktgen_range_setup(pinfo);\n\n        pktgen_log_info(\"   Setup latency defaults\");\n        pktgen_latency_setup(pinfo);\n\n        pktgen_log_info(\"   Setup clear stats\");\n        pktgen_clear_stats(pinfo);\n\n        pktgen_log_info(\"   Setup random bits\");\n        pktgen_rnd_bits_init(&pinfo->rnd_bitfields);\n    }\n\n    /* Clear the log information by putting a blank line */\n    if (pktgen.verbose)\n        dump_device_info();\n\n    /* Setup the packet capture per port if needed. */\n    for (uint16_t sid = 0; sid < coreinfo_socket_cnt(); sid++)\n        pktgen_packet_capture_init(sid);\n}\n"
  },
  {
    "path": "app/pktgen-port-cfg.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_PORT_CFG_H_\n#define _PKTGEN_PORT_CFG_H_\n\n/**\n * @file\n *\n * Per-port configuration, port flags, and key data structures for Pktgen.\n *\n * Defines port_info_t (the central per-port state), the SEND_* flag bits\n * that control TX behaviour, the latency sampling structs, and a set of\n * diagnostic dump helpers for NIC capabilities.\n */\n\n#include <stdio.h>\n#include <string.h>\n#include <rte_version.h>\n#include <rte_atomic.h>\n#include <rte_pci.h>\n#ifdef TX_DEBUG_PKT_DUMP\n#include \"rte_hexdump.h\"\n#endif\n\n#undef BPF_MAJOR_VERSION\n#include <pcap/pcap.h>\n\n#include \"pktgen-seq.h\"\n#include \"pktgen-range.h\"\n#include \"pktgen-stats.h\"\n#include \"pktgen-pcap.h\"\n#include \"pktgen-dump.h\"\n#include \"pktgen-ether.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define USER_PATTERN_SIZE 16 /**< Maximum length of the user-defined fill pattern */\n#define MAX_LATENCY_ENTRIES \\\n    50108 /**< Max latency records (limited by latsamp_stats_t.data[] max size) */\n\n// clang-format off\nenum { /* Per port flag bits */\n       /* Supported packet modes non-exclusive */\n       SEND_ARP_REQUEST         = (1ULL << 0), /**< Send a ARP request */\n       SEND_GRATUITOUS_ARP      = (1ULL << 1), /**< Send a Gratuitous ARP */\n       ICMP_ECHO_ENABLE_FLAG    = (1ULL << 2), /**< Enable ICMP Echo support */\n       BONDING_TX_PACKETS       = (1ULL << 3), /**< Bonding driver send zero pkts */\n\n       /* Receive packet modes */\n       PROCESS_INPUT_PKTS       = (1ULL << 4), /**< Process input packets */\n       CAPTURE_PKTS             = (1ULL << 5), /**< Capture received packets */\n       SAMPLING_LATENCIES       = (1ULL << 6), /**< Sampling latency measurements */\n\n       SEND_PING4_REQUEST       = (1ULL << 8), /**< Send a IPv4 Ping request */\n       SEND_PING6_REQUEST       = (1ULL << 9), /**< Send a IPv6 Ping request */\n\n       /* Exclusive Packet sending modes */\n       SEND_SINGLE_PKTS         = (1ULL << 12), /**< Send single packets */\n       SEND_PCAP_PKTS           = (1ULL << 13), /**< Send a pcap file of packets */\n       SEND_RANGE_PKTS          = (1ULL << 14), /**< Send range of packets */\n       SEND_SEQ_PKTS            = (1ULL << 15), /**< Send sequence of packets */\n\n       /* Exclusive Packet type modes */\n       SEND_RANDOM_PKTS         = (1ULL << 16), /**< Send random bitfields in packets */\n       SEND_VLAN_ID             = (1ULL << 17), /**< Send packets with VLAN ID */\n       SEND_MPLS_LABEL          = (1ULL << 18), /**< Send MPLS label */\n       SEND_Q_IN_Q_IDS          = (1ULL << 19), /**< Send packets with Q-in-Q */\n\n       SEND_GRE_IPv4_HEADER     = (1ULL << 20), /**< Encapsulate IPv4 in GRE */\n       SEND_GRE_ETHER_HEADER    = (1ULL << 21), /**< Encapsulate Ethernet frame in GRE */\n       SEND_VXLAN_PACKETS       = (1ULL << 22), /**< Send VxLAN Packets */\n       SEND_LATENCY_PKTS        = (1ULL << 23), /**< Send latency packets in any mode */\n\n       /* Sending flags */\n       SETUP_TRANSMIT_PKTS      = (1ULL << 28), /**< Need to setup transmit packets */\n       STOP_RECEIVING_PACKETS   = (1ULL << 29), /**< Stop receiving packet */\n       SENDING_PACKETS          = (1ULL << 30), /**< sending packets on this port */\n       SEND_FOREVER             = (1ULL << 31), /**< Send packets forever */\n\n       SEND_ARP_PING_REQUESTS   =\n           (SEND_ARP_REQUEST | SEND_GRATUITOUS_ARP | SEND_PING4_REQUEST | SEND_PING6_REQUEST)\n};\n#define RANDOMIZE_SRC_IP (1ULL << 32) /**< Set the source IP address as random */\n#define RANDOMIZE_SRC_PT (1ULL << 33) /**< Set the source port as random */\n// clang-format on\n\n#define EXCLUSIVE_MODES (SEND_SINGLE_PKTS | SEND_PCAP_PKTS | SEND_RANGE_PKTS | SEND_SEQ_PKTS)\n\n#define EXCLUSIVE_PKT_MODES                                                                       \\\n    (SEND_RANDOM_PKTS | SEND_VLAN_ID | SEND_MPLS_LABEL | SEND_Q_IN_Q_IDS | SEND_GRE_IPv4_HEADER | \\\n     SEND_GRE_ETHER_HEADER | SEND_VXLAN_PACKETS | SEND_LATENCY_PKTS)\n\n#define RTE_PMD_PARAM_UNSET -1\n\n/** Payload fill pattern mode. */\ntypedef enum {\n    ZERO_FILL_PATTERN = 1, /**< Fill payload with zero bytes */\n    ABC_FILL_PATTERN,      /**< Fill payload with repeating 0x00..0xff pattern */\n    USER_FILL_PATTERN,     /**< Fill payload with the user-defined pattern */\n    NO_FILL_PATTERN,       /**< Leave payload bytes unchanged */\n} fill_t;\n\n/** Function pointer type for a port's TX handler. */\ntypedef void (*tx_func_t)(struct port_info_s *info, uint16_t qid);\n\n#define RING_SIZE 1024 /**< Number of entries in the tail-latency ring buffer */\n\n/** Ring buffer for recording tail-latency samples. */\ntypedef struct {\n    uint64_t data[RING_SIZE]; /**< Latency sample values (in TSC cycles) */\n    int head;                 /**< Index of next write position */\n    int count;                /**< Number of elements currently in the ring */\n} latency_ring_t;\n\n/** Per-queue latency sample collection buffer. */\ntypedef struct {\n    uint64_t data[MAX_LATENCY_ENTRIES]; /**< Recorded latency values in cycles */\n    uint32_t idx;                       /**< Index to the latencies array */\n    uint32_t num_samples;               /**< Number of latency samples */\n    uint64_t next;                      /**< Next latency sample time */\n    uint64_t reserved[2];               /**< Reserved for future use and align 64 bytes */\n} latsamp_stats_t __rte_cache_aligned;\n\n/** Per-port latency measurement state and running statistics. */\ntypedef struct {\n    uint64_t latency_rate_us;         /**< Number micro-seconds between injecting packets */\n    uint64_t jitter_threshold_us;     /**< Jitter threshold in micro-seconds */\n    uint64_t jitter_threshold_cycles; /**< Jitter threshold cycles */\n    uint64_t latency_rate_cycles;     /**< Number of cycles between injections */\n    uint64_t latency_timo_cycles;     /**< Number of cycles to next latency injection */\n    uint16_t latency_entropy;         /**< Entropy value to be used to increment sport */\n    MARKER stats;                     /**< Start marker for stats region (used to clear stats) */\n    uint64_t jitter_count;            /**< Number of jitter stats */\n    uint64_t num_latency_pkts;        /**< Total number of latency packets */\n    uint64_t num_latency_tx_pkts;     /**< Total number of TX latency packets */\n    uint64_t num_skipped;             /**< Number of skipped latency packets */\n    uint64_t running_cycles;          /**< Running, Number of cycles per latency packet */\n    uint64_t prev_cycles;             /**< previous cycles cyles time from last latency packet */\n    uint64_t min_cycles;              /**< minimum cycles per latency packet */\n    uint64_t avg_cycles;              /**< average cycles per latency packet */\n    uint64_t max_cycles;              /**< maximum cycles per latency packet */\n    uint32_t next_index;              /**< Next index to use for sending latency packets */\n    uint32_t expect_index;            /**< Expected index for received latency packets */\n    latency_ring_t tail_latencies;    /**< ring buffer for tail latencies */\n    MARKER end_stats;                 /**< End marker for stats region (used to clear stats) */\n} latency_t;\n\n/** Per-queue packet buffer arrays for RX and TX. */\ntypedef struct per_queue_s {\n    struct rte_mbuf **rx_pkts; /**< Array of pointers to packet buffers for RX */\n    struct rte_mbuf **tx_pkts; /**< Array of pointers to packet buffers for TX */\n} per_queue_t;\n\n/** Central per-port state for Pktgen. */\ntypedef struct port_info_s {\n    struct rte_eth_dev_info dev_info; /**< Device information */\n    struct rte_eth_conf conf;         /**< Configuration settings */\n    struct rte_eth_link link;         /**< Link Information speed and duplex */\n    struct rte_ether_addr src_mac;    /**< Source MAC address of the port */\n    rte_atomic64_t port_flags;        /**< Special send flags for ARP and other */\n    rte_atomic64_t transmit_count;    /**< Packets to transmit loaded into current_tx_count */\n    rte_atomic64_t current_tx_count;  /**< Current number of packets to send */\n    volatile uint64_t tx_cycles;      /**< Number cycles between TX bursts */\n    pkt_seq_t *seq_pkt;               /**< Packet sequence array */\n    range_info_t range;               /**< Range Information */\n    uint16_t pid;                     /**< Port ID value */\n    uint16_t rx_burst;                /**< Number of RX burst size */\n    uint16_t tx_burst;                /**< Number of TX burst packets */\n    uint16_t max_mtu;                 /**< Maximum MTU size */\n    uint64_t tx_pps;                  /**< Transmit packets per seconds */\n    double tx_rate;                   /**< Percentage rate for tx packets with fractions */\n    uint16_t seqIdx;                  /**< Current Packet sequence index 0 to NUM_SEQ_PKTS */\n    uint16_t seqCnt;                  /**< Current packet sequence max count */\n    uint16_t prime_cnt;               /**< Set the number of packets to send in a prime command */\n    uint16_t vlanid;                  /**< Set the port VLAN ID value */\n    uint8_t cos;                      /**< Set the port 802.1p cos value */\n    uint8_t tos;                      /**< Set the port tos value */\n    uint32_t mpls_entry;              /**< Set the port MPLS entry */\n    uint32_t gre_key;                 /**< GRE key if used */\n    per_queue_t per_queue[MAX_QUEUES_PER_PORT]; /**< Per queue info */\n    FILE *pcap_file;                            /**< PCAP file handle */\n\n    /** Whether the pseudo-header is required when calculating the checksum.\n     *  Depends on the original NIC driver (e.g., ixgbe NICs expect the pseudo-header)\n     *  See Table 1.133: https://doc.dpdk.org/guides/nics/overview.html */\n    bool cksum_requires_phdr;\n    struct rnd_bits_s *rnd_bitfields;     /**< Random bitfield settings */\n    char user_pattern[USER_PATTERN_SIZE]; /**< User set pattern values */\n    fill_t fill_pattern_type;             /**< Type of pattern to fill with */\n    union {\n        uint64_t vxlan; /**< VxLAN 64 bit word */\n        struct {\n            uint16_t vni_flags; /**< VxLAN Flags */\n            uint16_t group_id;  /**< VxLAN Group Policy ID */\n            uint32_t vxlan_id;  /**< VxLAN VNI */\n        };\n    };\n\n    port_stats_t stats; /**< Port statistics */\n\n    /* Packet dump related */\n    struct packet {\n        void *data;   /**< Packet data */\n        uint32_t len; /**< Length of data */\n    } dump_list[MAX_DUMP_PACKETS];\n    uint8_t dump_head;  /**< Index of last packet written to screen */\n    uint8_t dump_tail;  /**< Index of last valid packet in dump_list */\n    uint8_t dump_count; /**< Number of packets the user requested */\n\n    /* Latency sampling data */\n    /* Depending on MAX_LATENCY_ENTRIES, this could blow up static array memory usage\n     * over the limit allowed by x86_64 architecture */\n    latency_t latency;                                  /**< Latency information */\n    latsamp_stats_t latsamp_stats[MAX_QUEUES_PER_PORT]; /**< Per port stats */\n    uint32_t latsamp_type;                              /**< Type of lat sampler  */\n    uint32_t latsamp_rate;        /**< Sampling rate i.e., samples per second  */\n    uint32_t latsamp_num_samples; /**< Number of samples to collect  */\n    char latsamp_outfile[256];    /**< Path to file for dumping latency samples */\n} port_info_t;\n\n/** VxLAN tunnel header fields. */\nstruct vxlan {\n    uint16_t vni_flags; /**< VxLAN Flags */\n    uint16_t group_id;  /**< VxLAN Group Policy ID */\n    uint32_t vxlan_id;  /**< VxLAN VNI */\n};\n\n/**\n * Configure and initialise all Ethernet ports.\n *\n * Sets up RX/TX queues, applies offload settings, and populates each\n * port's port_info_t from the l2p_t mapping.\n */\nvoid pktgen_config_ports(void);\n\n/**\n * Transmit a burst of packets on a port queue.\n *\n * @param pinfo\n *   Per-port state, used to obtain flags and statistics.\n * @param qid\n *   Queue ID to transmit on.\n * @param pkts\n *   Array of mbufs to send.\n * @param nb_pkts\n *   Number of mbufs in @p pkts.\n */\nvoid tx_send_packets(port_info_t *pinfo, uint16_t qid, struct rte_mbuf **pkts, uint16_t nb_pkts);\n\n/**\n * Atomically subtract a 64-bit value from the tx counter.\n *\n * @param v\n *   A pointer to the atomic tx counter.\n * @param burst\n *   The value to be subtracted from the counter for tx burst size.\n * @return\n *   The number of packets to burst out\n */\nstatic inline uint64_t\npkt_atomic64_tx_count(rte_atomic64_t *v, int64_t burst)\n{\n    int success;\n    int64_t tmp2;\n\n    do {\n        int64_t tmp1 = v->cnt;\n\n        if (tmp1 == 0)\n            return 0;\n        tmp2    = likely(tmp1 > burst) ? burst : tmp1;\n        success = rte_atomic64_cmpset((volatile uint64_t *)&v->cnt, tmp1, tmp1 - tmp2);\n    } while (success == 0);\n\n    return tmp2;\n}\n\n/**\n * Print RX queue configuration to a file stream.\n *\n * @param f\n *   Output stream.\n * @param rx\n *   RX queue configuration to display.\n */\nstatic inline void\nrte_eth_rxconf_dump(FILE *f, struct rte_eth_rxconf *rx)\n{\n    fprintf(f, \"  RX Conf:\\n\");\n    fprintf(f,\n            \"     pthresh        :%5\" PRIu16 \" | hthresh          :%5\" PRIu16\n            \" | wthresh        :%5\" PRIu16 \"\\n\",\n            rx->rx_thresh.pthresh, rx->rx_thresh.hthresh, rx->rx_thresh.wthresh);\n    fprintf(f,\n            \"     Free Thresh    :%5\" PRIu16 \" | Drop Enable      :%5\" PRIu16\n            \" | Deferred Start :%5\" PRIu16 \"\\n\",\n            rx->rx_free_thresh, rx->rx_drop_en, rx->rx_deferred_start);\n    fprintf(f, \"     offloads       :%016\" PRIx64 \"\\n\", rx->offloads);\n}\n\n/**\n * Print TX queue configuration to a file stream.\n *\n * @param f\n *   Output stream.\n * @param tx\n *   TX queue configuration to display.\n */\nstatic inline void\nrte_eth_txconf_dump(FILE *f, struct rte_eth_txconf *tx)\n{\n    fprintf(f, \"  TX Conf:\\n\");\n    fprintf(f,\n            \"     pthresh        :%5\" PRIu16 \" | hthresh          :%5\" PRIu16\n            \" | wthresh        :%5\" PRIu16 \"\\n\",\n            tx->tx_thresh.pthresh, tx->tx_thresh.hthresh, tx->tx_thresh.wthresh);\n    fprintf(f,\n            \"     Free Thresh    :%5\" PRIu16 \" | RS Thresh        :%5\" PRIu16\n            \" | Deferred Start :%5\" PRIu16 \"\\n\",\n            tx->tx_free_thresh, tx->tx_rs_thresh, tx->tx_deferred_start);\n    fprintf(f, \"     offloads       :%016\" PRIx64 \"\\n\", tx->offloads);\n}\n\n/**\n * Print descriptor limits for an RX or TX queue.\n *\n * @param f\n *   Output stream.\n * @param lim\n *   Descriptor limit structure to display.\n * @param tx_flag\n *   Non-zero to label the output as TX, zero for RX.\n */\nstatic inline void\nrte_eth_desc_lim_dump(FILE *f, struct rte_eth_desc_lim *lim, int tx_flag)\n{\n    fprintf(f, \"  %s: descriptor Limits\\n\", tx_flag ? \"Tx\" : \"Rx\");\n\n    fprintf(f,\n            \"     nb_max         :%5\" PRIu16 \" | nb_min           :%5\" PRIu16\n            \" | nb_align       :%5\" PRIu16 \"\\n\",\n            lim->nb_max, lim->nb_min, lim->nb_align);\n    fprintf(f, \"     nb_seg_max     :%5\" PRIu16 \" | nb_mtu_seg_max   :%5\" PRIu16 \"\\n\",\n            lim->nb_seg_max, lim->nb_mtu_seg_max);\n}\n\n/**\n * Print the default RX or TX port configuration.\n *\n * @param f\n *   Output stream.\n * @param conf\n *   Default port configuration to display.\n * @param tx_flag\n *   Non-zero to label the output as TX, zero for RX.\n */\nstatic inline void\nrte_eth_dev_portconf_dump(FILE *f, struct rte_eth_dev_portconf *conf, int tx_flag)\n{\n    fprintf(f, \"  %s: Port Config (Default)\\n\", tx_flag ? \"Tx\" : \"Rx\");\n\n    fprintf(f,\n            \"     burst_size     :%5\" PRIu16 \" | ring_size        :%5\" PRIu16\n            \" | nb_queues      :%5\" PRIu16 \"\\n\",\n            conf->burst_size, conf->ring_size, conf->nb_queues);\n}\n\n/**\n * Print switch info for a device.\n *\n * @param f\n *   Output stream.\n * @param sw\n *   Switch info structure to display.\n */\nstatic inline void\nrte_eth_switch_info_dump(FILE *f, struct rte_eth_switch_info *sw)\n{\n    fprintf(f, \"  Switch Info: %s\\n\", sw->name);\n\n    fprintf(f, \"     domain_id      :%5\" PRIu16 \" | port_id          :%5\" PRIu16 \"\\n\",\n            sw->domain_id, sw->port_id);\n}\n\n/**\n * Format the RX offload capability flags as a space-separated string.\n *\n * @param rx_capa\n *   Bitmask of RTE_ETH_RX_OFFLOAD_* flags.\n * @param buf\n *   Output buffer to write the names into.\n * @param len\n *   Size of @p buf in bytes.\n * @return\n *   0 on success, -1 if @p len is zero or the buffer is too small.\n */\nstatic inline int\nrte_get_rx_capa_list(uint64_t rx_capa, char *buf, size_t len)\n{\n    uint32_t i;\n    int ret;\n#define _(x) #x\n    struct {\n        uint64_t flag;\n        const char *name;\n    } rx_flags[] = {{RTE_ETH_RX_OFFLOAD_VLAN_STRIP, _(VLAN_STRIP)},\n                    {RTE_ETH_RX_OFFLOAD_IPV4_CKSUM, _(IPV4_CKSUM)},\n                    {RTE_ETH_RX_OFFLOAD_UDP_CKSUM, _(UDP_CKSUM)},\n                    {RTE_ETH_RX_OFFLOAD_TCP_CKSUM, _(TCP_CKSUM)},\n                    {RTE_ETH_RX_OFFLOAD_TCP_LRO, _(TCP_LRO)},\n                    {RTE_ETH_RX_OFFLOAD_QINQ_STRIP, _(QINQ_STRIP)},\n                    {RTE_ETH_RX_OFFLOAD_OUTER_IPV4_CKSUM, _(OUTER_IPV4_CKSUM)},\n                    {RTE_ETH_RX_OFFLOAD_MACSEC_STRIP, _(MACSEC_STRIP)},\n                    {RTE_ETH_RX_OFFLOAD_VLAN_FILTER, _(VLAN_FILTER)},\n                    {RTE_ETH_RX_OFFLOAD_VLAN_EXTEND, _(VLAN_EXTEND)},\n                    {RTE_ETH_RX_OFFLOAD_SCATTER, _(SCATTER)},\n                    {RTE_ETH_RX_OFFLOAD_TIMESTAMP, _(TIMESTAMP)},\n                    {RTE_ETH_RX_OFFLOAD_SECURITY, _(SECURITY)},\n                    {RTE_ETH_RX_OFFLOAD_KEEP_CRC, _(KEEP_CRC)},\n                    {RTE_ETH_RX_OFFLOAD_SCTP_CKSUM, _(SCTP_CKSUM)},\n                    {RTE_ETH_RX_OFFLOAD_OUTER_UDP_CKSUM, _(OUTER_UDP_CKSUM)},\n                    {RTE_ETH_RX_OFFLOAD_RSS_HASH, _(RSS_HASH)},\n                    {RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT, _(BUFFER_SPLIT)}};\n#undef _\n\n    if (len == 0)\n        return -1;\n\n    buf[0] = '\\0';\n    for (i = 0; i < RTE_DIM(rx_flags); i++) {\n        if ((rx_capa & rx_flags[i].flag) != rx_flags[i].flag)\n            continue;\n\n        ret = snprintf(buf, len, \"%s \", rx_flags[i].name);\n        if (ret < 0)\n            return -1;\n        if ((size_t)ret >= len)\n            return -1;\n        buf += ret;\n        len -= ret;\n    }\n    return 0;\n}\n\n/**\n * Format the TX offload capability flags as a space-separated string.\n *\n * @param tx_capa\n *   Bitmask of RTE_ETH_TX_OFFLOAD_* flags.\n * @param buf\n *   Output buffer to write the names into.\n * @param len\n *   Size of @p buf in bytes.\n * @return\n *   0 on success, -1 if @p len is zero or the buffer is too small.\n */\nstatic inline int\nrte_get_tx_capa_list(uint64_t tx_capa, char *buf, size_t len)\n{\n    uint32_t i;\n    int ret;\n#define _(x) #x\n    struct {\n        uint64_t flag;\n        const char *name;\n    } tx_flags[] = {\n        {RTE_ETH_TX_OFFLOAD_VLAN_INSERT, _(VLAN_INSERT)},\n        {RTE_ETH_TX_OFFLOAD_IPV4_CKSUM, _(IPV4_CKSUM)},\n        {RTE_ETH_TX_OFFLOAD_UDP_CKSUM, _(UDP_CKSUM)},\n        {RTE_ETH_TX_OFFLOAD_TCP_CKSUM, _(TCP_CKSUM)},\n        {RTE_ETH_TX_OFFLOAD_SCTP_CKSUM, _(SCTP_CKSUM)},\n        {RTE_ETH_TX_OFFLOAD_TCP_TSO, _(TCP_TSO)},\n        {RTE_ETH_TX_OFFLOAD_UDP_TSO, _(UDP_TSO)},\n        {RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM, _(OUTER_IPV4_CKSUM)},\n        {RTE_ETH_TX_OFFLOAD_QINQ_INSERT, _(QINQ_INSERT)},\n        {RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO, _(VXLAN_TNL_TSO)},\n        {RTE_ETH_TX_OFFLOAD_GRE_TNL_TSO, _(GRE_TNL_TSO)},\n        {RTE_ETH_TX_OFFLOAD_IPIP_TNL_TSO, _(IPIP_TNL_TSO)},\n        {RTE_ETH_TX_OFFLOAD_GENEVE_TNL_TSO, _(GENEVE_TNL_TSO)},\n        {RTE_ETH_TX_OFFLOAD_MACSEC_INSERT, _(MACSEC_INSERT)},\n        {RTE_ETH_TX_OFFLOAD_MT_LOCKFREE, _(MT_LOCKFREE)},\n        {RTE_ETH_TX_OFFLOAD_MULTI_SEGS, _(MULTI_SEGS)},\n        {RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE, _(MBUF_FAST_FREE)},\n        {RTE_ETH_TX_OFFLOAD_SECURITY, _(SECURITY)},\n        {RTE_ETH_TX_OFFLOAD_UDP_TNL_TSO, _(UDP_TNL_TSO)},\n        {RTE_ETH_TX_OFFLOAD_IP_TNL_TSO, _(IP_TNL_TSO)},\n        {RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM, _(OUTER_UDP_CKSUM)},\n    };\n#undef _\n\n    if (len == 0)\n        return -1;\n\n    buf[0] = '\\0';\n    for (i = 0; i < RTE_DIM(tx_flags); i++) {\n        if ((tx_capa & tx_flags[i].flag) != tx_flags[i].flag)\n            continue;\n\n        ret = snprintf(buf, len, \"%s \", tx_flags[i].name);\n        if (ret < 0)\n            return -1;\n        if ((size_t)ret >= len)\n            return -1;\n        buf += ret;\n        len -= ret;\n    }\n    return 0;\n}\n\n/**\n * Print a comprehensive device information summary to a file stream.\n *\n * @param f\n *   Output stream (defaults to stderr if NULL).\n * @param pid\n *   Port ID whose device info is displayed.\n */\nstatic inline void\nrte_eth_dev_info_dump(FILE *f, uint16_t pid)\n{\n    struct rte_eth_dev_info dev_info, *di = &dev_info;\n    char buf[512];\n\n    if (rte_eth_dev_info_get(pid, &dev_info) < 0) {\n        fprintf(f, \"Failed to get eth dev info for port %u\\n\", pid);\n        return;\n    }\n\n    if (!f)\n        f = stderr;\n\n    char dev_name[64];\n\n    rte_eth_dev_get_name_by_port(pid, dev_name);\n    fprintf(f, \"\\n** Device Info (%s, if_index:%\" PRId32 \", flags %08\" PRIu32 \") **\\n\", dev_name,\n            di->if_index, *di->dev_flags);\n\n    fprintf(f,\n            \"   min_rx_bufsize :%5\" PRIu32 \"  max_rx_pktlen     :%5\" PRIu32\n            \"  hash_key_size :%5\" PRIu8 \"\\n\",\n            di->min_rx_bufsize, di->max_rx_pktlen, di->hash_key_size);\n    fprintf(f,\n            \"   max_rx_queues  :%5\" PRIu16 \"  max_tx_queues     :%5\" PRIu16\n            \"  max_vfs       :%5\" PRIu16 \"\\n\",\n            di->max_rx_queues, di->max_tx_queues, di->max_vfs);\n    fprintf(f,\n            \"   max_mac_addrs  :%5\" PRIu32 \"  max_hash_mac_addrs:%5\" PRIu32\n            \"  max_vmdq_pools:%5\" PRIu16 \"\\n\",\n            di->max_mac_addrs, di->max_hash_mac_addrs, di->max_vmdq_pools);\n    fprintf(f,\n            \"   vmdq_queue_base:%5\" PRIu16 \"  vmdq_queue_num    :%5\" PRIu16\n            \"  vmdq_pool_base:%5\" PRIu16 \"\\n\",\n            di->vmdq_queue_base, di->vmdq_queue_num, di->vmdq_pool_base);\n    fprintf(f,\n            \"   nb_rx_queues   :%5\" PRIu16 \"  nb_tx_queues      :%5\" PRIu16\n            \"  speed_capa    : %08\" PRIx32 \"\\n\",\n            di->nb_rx_queues, di->nb_tx_queues, di->speed_capa);\n    fprintf(f, \"\\n\");\n    fprintf(f, \"   flow_type_rss_offloads:%016\" PRIx64 \"  reta_size             :%5\" PRIu16 \"\\n\",\n            di->flow_type_rss_offloads, di->reta_size);\n    rte_get_rx_capa_list(di->rx_offload_capa, buf, sizeof(buf));\n    fprintf(f, \"   rx_offload_capa       :%s\\n\", buf);\n    rte_get_tx_capa_list(di->tx_offload_capa, buf, sizeof(buf));\n    fprintf(f, \"   tx_offload_capa       :%s\\n\", buf);\n    fprintf(f, \"   rx_queue_offload_capa :%016\" PRIx64, di->rx_queue_offload_capa);\n    fprintf(f, \"  tx_queue_offload_capa :%016\" PRIx64 \"\\n\", di->tx_queue_offload_capa);\n    fprintf(f, \"   dev_capa              :%016\" PRIx64 \"\\n\", di->dev_capa);\n    fprintf(f, \"\\n\");\n\n    rte_eth_rxconf_dump(f, &di->default_rxconf);\n    rte_eth_txconf_dump(f, &di->default_txconf);\n\n    rte_eth_desc_lim_dump(f, &di->rx_desc_lim, 0);\n    rte_eth_desc_lim_dump(f, &di->tx_desc_lim, 1);\n\n    rte_eth_dev_portconf_dump(f, &di->default_rxportconf, 0);\n    rte_eth_dev_portconf_dump(f, &di->default_txportconf, 1);\n\n    rte_eth_switch_info_dump(f, &di->switch_info);\n\n    fprintf(f, \"\\n\");\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_PORT_CFG_H_ */\n"
  },
  {
    "path": "app/pktgen-random.c",
    "content": "/*-\n * Copyright(c) <2016-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2016 by Keith Wiles @ intel.com */\n\n#include \"pktgen-random.h\"\n\n#include <stddef.h>\n#include <string.h>\n#include <stdio.h>\n\n#include <rte_malloc.h>\n\n#include \"lua_config.h\"\n\n#include \"pktgen-display.h\"\n#include \"pktgen-log.h\"\n\n/* Allow PRNG function to be changed at runtime for testing*/\n#ifdef TESTING\nstatic rnd_func_t _rnd_func = NULL;\n#endif\n\n/* Forward declaration */\nstatic void pktgen_init_default_rnd(void);\n\n/**\n *\n * pktgen_rnd_bits_init - Initialize random bitfield structures and PRNG\n *\n * DESCRIPTION\n * Initialize the random bitfield structures the PRNG context.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_rnd_bits_init(rnd_bits_t **rnd_bits)\n{\n    int i;\n\n    *rnd_bits = (rnd_bits_t *)rte_zmalloc_socket(\"Random bitfield structure\", sizeof(rnd_bits_t), 0,\n                                                 pg_socket_id());\n\n    pktgen_display_set_color(\"stats.stat.values\");\n\n    /* Initialize mask to all ignore */\n    for (i = 0; i < MAX_RND_BITFIELDS; ++i) {\n        pktgen_set_random_bitfield(*rnd_bits, i, 0,\n                                   \"????????????????????????????????\"); /* 32 ?'s */\n        pktgen_set_random_bitfield(*rnd_bits, i, 0, \"\");\n    }\n\n    pktgen_init_default_rnd();\n}\n\n/**\n *\n * pktgen_set_random_bitfield - Set random bit specification\n *\n * DESCRIPTION\n * Set random bit specification. This extracts the 0, 1 and random bitmasks from\n * the provided bitmask template.\n *\n * RETURNS: Active specifications\n *\n * SEE ALSO:\n */\n\nuint32_t\npktgen_set_random_bitfield(rnd_bits_t *rnd_bits, uint8_t idx, uint8_t offset, const char *mask)\n{\n    if (idx >= MAX_RND_BITFIELDS)\n        goto leave;\n\n    size_t mask_len = strlen(mask);\n    if (mask_len > MAX_BITFIELD_SIZE)\n        goto leave;\n\n    /* Disable spec number idx when no mask is specified */\n    if (mask_len == 0) {\n        rnd_bits->active_specs &= ~((uint32_t)1 << idx);\n        goto leave;\n    }\n\n    /* Iterate over specified mask from left to right. The mask components are\n     * shifted left and filled at the right side.\n     * When the complete mask has been processed, for each position exactly 1\n     * mask component has a 1 bit set.\n     */\n    BITFIELD_T mask0 = 0, mask1 = 0, maskRnd = 0;\n\n    uint32_t i;\n    for (i = 0; i < mask_len; ++i) {\n        mask0 <<= 1;\n        mask1 <<= 1;\n        maskRnd <<= 1;\n\n        switch (mask[i]) {\n        case '0':\n            mask0 += 1;\n            break;\n        case '1':\n            mask1 += 1;\n            break;\n        case '.': /* ignore bit */\n            break;\n        case 'x':\n        case 'X':\n            maskRnd += 1;\n            break;\n        default: /* print error: \"Unknown char in bitfield spec\" */\n            goto leave;\n        }\n    }\n\n    /* Shift bitmasks to MSB position, so the bitmask starts at the provided\n     * offset.\n     * This way the mask can be used as a full-width BITFIELD_T, even when the\n     * provided mask is shorter than the number of bits a BITFIELD_T can store.\n     */\n    int pad_len = MAX_BITFIELD_SIZE - mask_len;\n    mask0 <<= pad_len;\n    mask1 <<= pad_len;\n    maskRnd <<= pad_len;\n\n    rnd_bits->active_specs |= (1 << idx);\n\n    rnd_bits->specs[idx].offset = offset;\n\n    /* andMask is used to clear certain bits. Bits corresponding to a 1 bit in\n     * the mask will retain their original value, bits corresponding to a 0 bit\n     * in the mask will be cleared.\n     *\n     * - mask0: all set bits in this mask must be 0 in the result\n     * - maskRnd: all set bits in this mask will be zero'd. This enables merging\n     *       of a random value by doing a bitwise OR with the random value.\n     */\n    rnd_bits->specs[idx].andMask = htonl(~(mask0 | maskRnd));\n\n    /* orMask is used to set certain bits.\n     */\n    rnd_bits->specs[idx].orMask = htonl(mask1);\n\n    /* rndMask is used to filter a generated random value, so all bits that\n     * should not be random are 0.\n     * The result of this filtering operation can then be bitwise OR-ed with\n     * the original value to merge the random value in.\n     * In the original value, the bits that must be random need to be 0. This is\n     * taken care of by taking the rndMask into account for the andMask.\n     */\n    rnd_bits->specs[idx].rndMask = htonl(maskRnd);\n\nleave:\n    return rnd_bits->active_specs;\n}\n\n/**\n *\n * pktgen_rnd_bits_apply - Set random bitfields in packet.\n *\n * DESCRIPTION\n * Set bitfields in packet specified packet to random values according to the\n * requested bitfield specification.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_rnd_bits_apply(port_info_t *pinfo, struct rte_mbuf **pkts, size_t cnt, rnd_bits_t *rbits)\n{\n    rnd_bits_t *rnd_bits;\n    size_t mbuf_cnt;\n    uint32_t active_specs;\n    uint32_t *pkt_data;\n    BITFIELD_T rnd_value;\n    bf_spec_t *bf_spec;\n\n    /* the info pointer could be null. */\n    rnd_bits = (rbits) ? rbits : pinfo->rnd_bitfields;\n    if ((active_specs = rnd_bits->active_specs) == 0)\n        return;\n\n    for (mbuf_cnt = 0; mbuf_cnt < cnt; ++mbuf_cnt) {\n        bf_spec = rnd_bits->specs;\n\n        while (active_specs > 0) {\n            if (likely(active_specs & 1)) {\n                /* Get pointer to byte <offset> in mbuf data as uint32_t*, so */\n                /* the masks can be applied. */\n                pkt_data =\n                    (uint32_t *)(&rte_pktmbuf_mtod(pkts[mbuf_cnt], uint8_t *)[bf_spec->offset]);\n\n                *pkt_data &= bf_spec->andMask;\n                *pkt_data |= bf_spec->orMask;\n\n                if (bf_spec->rndMask) {\n#ifdef TESTING\n                    /* Allow PRNG to be set when testing */\n                    rnd_value = _rnd_func ? _rnd_func() : pktgen_default_rnd_func();\n#else\n                    /* ... but allow inlining for production build */\n                    rnd_value = pktgen_default_rnd_func();\n#endif\n                    rnd_value &= bf_spec->rndMask;\n                    *pkt_data |= rnd_value;\n                }\n            }\n\n            ++bf_spec;          /* prepare to use next bitfield spec */\n            active_specs >>= 1; /* use next bit in active spec bitfield */\n        }\n\n        active_specs = rnd_bits->active_specs;\n    }\n}\n\n/**\n *\n * pktgen_page_random_bitfields - Display the random bitfields data page.\n *\n * DESCRIPTION\n * Display the random bitfields data page on the screen.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_page_random_bitfields(uint32_t print_labels, uint16_t pid, rnd_bits_t *rnd_bits)\n{\n    uint32_t row, bitmask_idx, i, curr_bit;\n    char mask[36]; /* 4*8 bits, 3 delimiter spaces, \\0 */\n    bf_spec_t *curr_spec;\n\n    if (!print_labels)\n        return;\n\n    mask[35] = '\\0';\n    mask[8] = mask[17] = mask[26] = ' ';\n\n    display_topline(\"<Random bitfield Page>\", 0, 0, 0);\n\n    row = PORT_FLAGS_ROW;\n\n    pktgen_display_set_color(\"top.ports\");\n    scrn_printf(row++, 3, \"Port %d\", pid);\n\n    if (rnd_bits == NULL) {\n        scrn_cprintf(10, this_scrn->ncols, \"** Port is not active - no random bitfields set **\");\n        row = 28;\n        goto leave;\n    }\n    pktgen_display_set_color(\"stats.stat.label\");\n    /* Header line */\n    scrn_printf(row++, 1, \"%8s %8s %8s  %s\", \"Index\", \"Offset\", \"Act?\",\n                \"Mask [0 = 0 bit, 1 = 1 bit, X = random bit, . = ignore]\");\n\n    pktgen_display_set_color(\"stats.stat.values\");\n    for (bitmask_idx = 0; bitmask_idx < MAX_RND_BITFIELDS; ++bitmask_idx) {\n        curr_spec = &rnd_bits->specs[bitmask_idx];\n\n        memset(mask, 0, sizeof(mask));\n        memset(mask, ' ', sizeof(mask) - 1);\n        /* Compose human readable bitmask representation */\n        for (i = 0; i < MAX_BITFIELD_SIZE; ++i) {\n            curr_bit = (uint32_t)1 << (MAX_BITFIELD_SIZE - i - 1);\n\n            /* + i >> 3 for space delimiter after every 8 bits.\n             * Need to check rndMask before andMask: for random bits, the\n             * andMask is also 0. */\n            mask[i + (i >> 3)] = ((ntohl(curr_spec->rndMask) & curr_bit) != 0)   ? 'X'\n                                 : ((ntohl(curr_spec->andMask) & curr_bit) == 0) ? '0'\n                                 : ((ntohl(curr_spec->orMask) & curr_bit) != 0)  ? '1'\n                                                                                 : '.';\n        }\n\n        scrn_printf(row++, 1, \"%8d %8d %7s   %s\", bitmask_idx, curr_spec->offset,\n                    (rnd_bits->active_specs & (1 << bitmask_idx)) ? \"Yes\" : \"No\", mask);\n    }\n\nleave:\n    display_dashline(++row);\n    pktgen_display_set_color(NULL);\n}\n\nstatic void\npktgen_init_default_rnd(void)\n{\n    FILE *dev_random;\n    int ret;\n\n    if ((dev_random = fopen(\"/dev/urandom\", \"r\")) == NULL) {\n        pktgen_log_error(\"Could not open /dev/urandom for reading\");\n        return;\n    }\n\n    /* Use contents of /dev/urandom as seed for ISAAC */\n    ret = fread(xor_state, 1, sizeof(xor_state[0]), dev_random);\n    if (ret != sizeof(xor_state[0]))\n        pktgen_log_warning(\"Could not read enough random data for PRNG seed (%d)\", ret);\n\n    fclose(dev_random);\n}\n\n#ifdef TESTING\n/**\n *\n * pktgen_set_rnd_func - Set function to use as random number generator\n *\n * DESCRIPTION\n * Set function to use as random number generator.\n *\n * RETURNS: Previous random number function (or NULL if the default function was\n *          used).\n *\n * SEE ALSO:\n */\n\nrnd_func_t\npktgen_set_rnd_func(rnd_func_t rnd_func)\n{\n    rnd_func_t prev_rnd_func = _rnd_func;\n\n    _rnd_func = rnd_func;\n\n    return prev_rnd_func;\n}\n\n#endif\n"
  },
  {
    "path": "app/pktgen-random.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_RANDOM_H_\n#define _PKTGEN_RANDOM_H_\n\n/**\n * @file\n *\n * Per-port random bitfield mask engine for Pktgen.\n *\n * Allows independent random bits to be applied to arbitrary byte offsets\n * within each transmitted packet, enabling fuzz-style traffic generation\n * without rebuilding the full packet template each burst.\n */\n\n#include <stdint.h>\n\n#include <rte_mbuf.h>\n\n#include \"pktgen-seq.h\"\n\n#include \"xorshift64star.h\" /* PRNG function */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MAX_RND_BITFIELDS 32 /**< Maximum simultaneous random bitfield specs per port */\n\n#define BITFIELD_T        uint32_t /**< Underlying integer type for bitfield masks */\n#define MAX_BITFIELD_SIZE (sizeof(BITFIELD_T) << 3) /**< Size of BITFIELD_T in bits */\n\nstruct port_info_s;\n\n/**\n * Random bitfield specification — describes one randomised bit range in a packet.\n */\ntypedef struct bf_spec_s {\n    uint8_t offset; /**< Offset (in bytes) of where to apply the bitmask */\n\n    BITFIELD_T andMask; /**< Mask to bitwise AND value with */\n    BITFIELD_T orMask;  /**< Mask to bitwise OR value with */\n    BITFIELD_T rndMask; /**< Which bits will get a random value */\n} bf_spec_t;\n\n/**\n * Collection of all active random bitfield specs for one port.\n */\ntypedef struct rnd_bits_s {\n    uint32_t active_specs; /**< Bitmask identifying which spec slots are active */\n\n    bf_spec_t specs[MAX_RND_BITFIELDS]; /**< Per-slot bitfield specifications */\n} rnd_bits_t;\n\n/**\n * Allocate and zero-initialise a rnd_bits_t for a port.\n *\n * @param rnd_bits\n *   Address of the pointer to fill; on success *rnd_bits points to the\n *   newly allocated structure.\n */\nvoid pktgen_rnd_bits_init(struct rnd_bits_s **rnd_bits);\n\n/**\n * Configure one random bitfield slot.\n *\n * @param rnd_bits\n *   Pointer to the rnd_bits_t to modify.\n * @param idx\n *   Slot index (0 .. MAX_RND_BITFIELDS-1).\n * @param offset\n *   Byte offset within the packet where the mask is applied.\n * @param mask\n *   String of '0', '1', and 'X' characters (MSB first) describing\n *   fixed-0, fixed-1, and randomised bit positions respectively.\n * @return\n *   0 on success, non-zero on error.\n */\nuint32_t pktgen_set_random_bitfield(struct rnd_bits_s *rnd_bits, uint8_t idx, uint8_t offset,\n                                    const char *mask);\n\n/**\n * Apply all active random bitfield specs to a burst of packets.\n *\n * @param info\n *   Per-port state (used for port-specific context).\n * @param pkt\n *   Array of mbuf pointers to modify in place.\n * @param cnt\n *   Number of mbufs in @p pkt.\n * @param rbits\n *   Random bitfield configuration to apply.\n */\nvoid pktgen_rnd_bits_apply(struct port_info_s *info, struct rte_mbuf **pkt, size_t cnt,\n                           struct rnd_bits_s *rbits);\n\n/**\n * Render the random-bitfields display page to the terminal.\n *\n * @param print_labels\n *   Non-zero to (re)print column headers and labels.\n * @param pid\n *   Port index whose bitfield settings are displayed.\n * @param rnd_bits\n *   Pointer to the rnd_bits_t to display.\n */\nvoid pktgen_page_random_bitfields(uint32_t print_labels, uint16_t pid, struct rnd_bits_s *rnd_bits);\n\n/**\n * Generate a 32-bit random value using the built-in xorshift64* PRNG.\n *\n * @return\n *   32-bit pseudo-random number.\n */\nstatic __inline__ uint32_t\npktgen_default_rnd_func(void)\n{\n    return (uint32_t)xorshift64star();\n}\n\n#ifdef TESTING\n/** Function pointer type for a pluggable random-number generator. */\ntypedef BITFIELD_T (*rnd_func_t)(void);\n\n/**\n * Replace the active PRNG function (test/validation use only).\n *\n * @param rnd_func\n *   New PRNG function to install.\n * @return\n *   Previously installed PRNG function.\n */\nrnd_func_t pktgen_set_rnd_func(rnd_func_t rnd_func);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_RANDOM_H_ */\n"
  },
  {
    "path": "app/pktgen-range.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <lua_config.h>\n\n#include \"pktgen-display.h\"\n#include \"pktgen-log.h\"\n#include \"pktgen.h\"\n\n/**\n *\n * pktgen_range_ctor - Construct a range packet in buffer provided.\n *\n * DESCRIPTION\n * Build the special range packet in the buffer provided.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_range_ctor(range_info_t *range, pkt_seq_t *pkt)\n{\n    if (pkt->ipProto == PG_IPPROTO_TCP) {\n        if (unlikely(range->tcp_seq_inc != 0)) {\n            uint32_t seq = pkt->tcp_seq;\n\n            seq += range->tcp_seq_inc;\n            if (seq < range->tcp_seq_min)\n                seq = range->tcp_seq_max;\n            if (seq > range->tcp_seq_max)\n                seq = range->tcp_seq_min;\n            pkt->tcp_seq = seq;\n        } else\n            pkt->tcp_seq = range->tcp_seq;\n        if (unlikely(range->tcp_ack_inc != 0)) {\n            uint32_t ack = pkt->tcp_ack;\n\n            ack += range->tcp_ack_inc;\n            if (ack < range->tcp_ack_min)\n                ack = range->tcp_ack_max;\n            if (ack > range->tcp_ack_max)\n                ack = range->tcp_ack_min;\n            pkt->tcp_ack = ack;\n        } else\n            pkt->tcp_ack = range->tcp_ack;\n    }\n\n    switch (pkt->ethType) {\n    case RTE_ETHER_TYPE_IPV4:\n        switch (pkt->ipProto) {\n        case PG_IPPROTO_TCP:\n        case PG_IPPROTO_UDP:\n\n            if (pkt->dport == PG_IPPROTO_L4_GTPU_PORT) {\n                if (unlikely(range->gtpu_teid_inc != 0)) {\n                    uint32_t teid = pkt->gtpu_teid;\n\n                    teid += range->gtpu_teid_inc;\n                    if (teid < range->gtpu_teid_min)\n                        teid = range->gtpu_teid_max;\n                    if (teid > range->gtpu_teid_max)\n                        teid = range->gtpu_teid_min;\n                    pkt->gtpu_teid = teid;\n                } else\n                    pkt->gtpu_teid = range->gtpu_teid;\n            }\n\n            if (unlikely(range->src_port_inc != 0)) {\n                uint32_t sport = pkt->sport;\n\n                sport += range->src_port_inc;\n                if (sport < range->src_port_min)\n                    sport = range->src_port_max;\n                if (sport > range->src_port_max)\n                    sport = range->src_port_min;\n                pkt->sport = (uint16_t)sport;\n            } else\n                pkt->sport = range->src_port;\n\n            if (unlikely(range->dst_port_inc != 0)) {\n                uint32_t dport = pkt->dport;\n\n                dport += range->dst_port_inc;\n                if (dport < range->dst_port_min)\n                    dport = range->dst_port_max;\n                if (dport > range->dst_port_max)\n                    dport = range->dst_port_min;\n                pkt->dport = (uint16_t)dport;\n            } else\n                pkt->dport = range->dst_port;\n\n            if (unlikely(range->ttl_inc != 0)) {\n                uint16_t ttl = pkt->ttl;\n                ttl += range->ttl_inc;\n                if (ttl < range->ttl_min)\n                    ttl = range->ttl_max;\n                if (ttl > range->ttl_max)\n                    ttl = range->ttl_min;\n                pkt->ttl = (uint8_t)ttl;\n            } else\n                pkt->ttl = range->ttl;\n\n            if (unlikely(range->src_ip_inc != 0)) {\n                uint32_t p = pkt->ip_src_addr.addr.ipv4.s_addr;\n\n                p += range->src_ip_inc;\n                if (p < range->src_ip_min)\n                    p = range->src_ip_max;\n                else if (p > range->src_ip_max)\n                    p = range->src_ip_min;\n                pkt->ip_src_addr.addr.ipv4.s_addr = p;\n            } else\n                pkt->ip_src_addr.addr.ipv4.s_addr = range->src_ip;\n\n            if (unlikely(range->dst_ip_inc != 0)) {\n                uint32_t p = pkt->ip_dst_addr.addr.ipv4.s_addr;\n\n                p += range->dst_ip_inc;\n                if (p < range->dst_ip_min)\n                    p = range->dst_ip_max;\n                else if (p > range->dst_ip_max)\n                    p = range->dst_ip_min;\n                pkt->ip_dst_addr.addr.ipv4.s_addr = p;\n            } else\n                pkt->ip_dst_addr.addr.ipv4.s_addr = range->dst_ip;\n\n            if (unlikely(range->vlan_id_inc != 0)) {\n                /* Since VLAN is set to MIN_VLAN_ID, check this and skip first increment\n                 * to maintain the range sequence in sync with other range fields */\n                uint32_t p;\n                static uint8_t min_vlan_set = 0;\n\n                if ((pkt->vlanid == MIN_VLAN_ID) && !min_vlan_set) {\n                    p            = 0;\n                    min_vlan_set = 1;\n                } else\n                    p = pkt->vlanid;\n                p += range->vlan_id_inc;\n                if (p < range->vlan_id_min)\n                    p = range->vlan_id_max;\n                else if (p > range->vlan_id_max)\n                    p = range->vlan_id_min;\n                pkt->vlanid = p;\n            } else\n                pkt->vlanid = range->vlan_id;\n\n            if (unlikely(range->cos_inc != 0)) {\n                uint32_t p;\n                static uint8_t min_cos_set = 0;\n\n                if ((pkt->cos == MIN_COS) && !min_cos_set) {\n                    p           = 0;\n                    min_cos_set = 1;\n                } else\n                    p = pkt->cos;\n                p += range->cos_inc;\n                if (p < range->cos_min)\n                    p = range->cos_max;\n                else if (p > range->cos_max)\n                    p = range->cos_min;\n                pkt->cos = p;\n            } else\n                pkt->cos = range->cos;\n\n            if (unlikely(range->tos_inc != 0)) {\n                uint32_t p;\n                static uint8_t min_tos_set = 0;\n\n                if ((pkt->tos == MIN_TOS) && !min_tos_set) {\n                    p           = 0;\n                    min_tos_set = 1;\n                } else\n                    p = pkt->tos;\n                p += range->tos_inc;\n                if (p < range->tos_min)\n                    p = range->tos_max;\n                else if (p > range->tos_max)\n                    p = range->tos_min;\n                pkt->tos = p;\n            } else\n                pkt->tos = range->tos;\n\n            if (unlikely(range->pkt_size_inc != 0)) {\n                uint32_t p = pkt->pkt_size;\n\n                p += range->pkt_size_inc;\n                if (p < range->pkt_size_min)\n                    p = range->pkt_size_max;\n                else if (p > range->pkt_size_max)\n                    p = range->pkt_size_min;\n                pkt->pkt_size = p;\n            } else\n                pkt->pkt_size = range->pkt_size;\n\n            if (unlikely(range->src_mac_inc != 0)) {\n                uint64_t p;\n\n                inet_mtoh64(&pkt->eth_src_addr, &p);\n\n                p += range->src_mac_inc;\n                if (p < range->src_mac_min)\n                    p = range->src_mac_max;\n                else if (p > range->src_mac_max)\n                    p = range->src_mac_min;\n\n                inet_h64tom(p, &pkt->eth_src_addr);\n            } else\n                inet_h64tom(range->src_mac, &pkt->eth_src_addr);\n\n            if (unlikely(range->dst_mac_inc != 0)) {\n                uint64_t p;\n\n                inet_mtoh64(&pkt->eth_dst_addr, &p);\n\n                p += range->dst_mac_inc;\n                if (p < range->dst_mac_min)\n                    p = range->dst_mac_max;\n                else if (p > range->dst_mac_max)\n                    p = range->dst_mac_min;\n\n                inet_h64tom(p, &pkt->eth_dst_addr);\n            } else\n                inet_h64tom(range->dst_mac, &pkt->eth_dst_addr);\n\n            if (unlikely(range->vxlan_gid_inc != 0)) {\n                uint32_t gid = pkt->group_id;\n\n                gid += range->vxlan_gid_inc;\n                if (gid < range->vxlan_gid_min)\n                    gid = range->vxlan_gid_max;\n                else if (gid > range->vxlan_gid_max)\n                    gid = range->vxlan_gid_min;\n\n                pkt->group_id = gid;\n            } else\n                pkt->group_id = range->vxlan_gid;\n\n            if (unlikely(range->vxlan_vid_inc != 0)) {\n                uint32_t vid = pkt->vxlan_id;\n\n                vid += range->vxlan_gid_inc;\n                if (vid < range->vxlan_vid_min)\n                    vid = range->vxlan_vid_max;\n                else if (vid > range->vxlan_vid_max)\n                    vid = range->vxlan_vid_min;\n\n                pkt->group_id = vid;\n            } else\n                pkt->vxlan_id = range->vxlan_vid;\n\n            break;\n        default:\n            pktgen_log_info(\"IPv4 ipProto %02x\", pkt->ipProto);\n            break;\n        }\n        break;\n    case RTE_ETHER_TYPE_IPV6:\n        switch (pkt->ipProto) {\n        case PG_IPPROTO_UDP:\n        case PG_IPPROTO_TCP:\n\n            if (unlikely(range->src_port_inc != 0)) {\n                uint16_t sport = pkt->sport;\n\n                sport += range->src_port_inc;\n                if (sport < range->src_port_min)\n                    sport = range->src_port_max;\n                if (sport > range->src_port_max)\n                    sport = range->src_port_min;\n                pkt->sport = sport;\n            } else\n                pkt->sport = range->src_port;\n\n            if (unlikely(range->dst_port_inc != 0)) {\n                uint16_t dport = pkt->dport;\n\n                dport += range->dst_port_inc;\n                if (dport < range->dst_port_min)\n                    dport = range->dst_port_max;\n                if (dport > range->dst_port_max)\n                    dport = range->dst_port_min;\n                pkt->dport = dport;\n            } else\n                pkt->dport = range->dst_port;\n\n            if (unlikely(range->hop_limits_inc != 0)) {\n                uint8_t hop_limits = pkt->hop_limits;\n\n                hop_limits += range->hop_limits_inc;\n                if (hop_limits < range->hop_limits_min)\n                    hop_limits = range->hop_limits_max;\n                if (hop_limits > range->hop_limits_max)\n                    hop_limits = range->hop_limits_min;\n                pkt->hop_limits = hop_limits;\n            } else\n                pkt->hop_limits = range->hop_limits;\n\n            if (unlikely(!inet6AddrIsUnspecified(range->src_ipv6_inc))) {\n                uint8_t p[PG_IN6ADDRSZ];\n\n                rte_memcpy(p, &pkt->ip_src_addr.addr.ipv6, sizeof(struct rte_ipv6_addr));\n                inet6AddrAdd(p, range->src_ipv6_inc, p);\n                if (memcmp(p, range->src_ipv6_min, sizeof(struct rte_ipv6_addr)) < 0)\n                    rte_memcpy(p, range->src_ipv6_min, sizeof(struct rte_ipv6_addr));\n                else if (memcmp(p, range->src_ipv6_max, sizeof(struct rte_ipv6_addr)) > 0)\n                    rte_memcpy(p, range->src_ipv6_min, sizeof(struct rte_ipv6_addr));\n                rte_memcpy(&pkt->ip_src_addr.addr.ipv6, p, sizeof(struct rte_ipv6_addr));\n            } else\n                rte_memcpy(&pkt->ip_src_addr.addr.ipv6, range->src_ipv6,\n                           sizeof(struct rte_ipv6_addr));\n\n            if (unlikely(!inet6AddrIsUnspecified(range->dst_ipv6_inc))) {\n                uint8_t p[PG_IN6ADDRSZ];\n\n                rte_memcpy(p, &pkt->ip_dst_addr.addr.ipv6, sizeof(struct rte_ipv6_addr));\n                inet6AddrAdd(p, range->dst_ipv6_inc, p);\n                if (memcmp(p, range->dst_ipv6_min, sizeof(struct rte_ipv6_addr)) < 0)\n                    rte_memcpy(p, range->dst_ipv6_min, sizeof(struct rte_ipv6_addr));\n                else if (memcmp(p, range->dst_ipv6_max, sizeof(struct rte_ipv6_addr)) > 0)\n                    rte_memcpy(p, range->dst_ipv6_min, sizeof(struct rte_ipv6_addr));\n                rte_memcpy(&pkt->ip_dst_addr.addr.ipv6, p, sizeof(struct rte_ipv6_addr));\n            } else\n                rte_memcpy(&pkt->ip_dst_addr.addr.ipv6, range->dst_ipv6,\n                           sizeof(struct rte_ipv6_addr));\n\n            if (unlikely(range->vlan_id_inc != 0)) {\n                /* Since VLAN is set to MIN_VLAN_ID, check this and skip first increment\n                 * to maintain the range sequence in sync with other range fields */\n                uint32_t p;\n                static uint8_t min_vlan_set = 0;\n\n                if ((pkt->vlanid == MIN_VLAN_ID) && !min_vlan_set) {\n                    p            = 0;\n                    min_vlan_set = 1;\n                } else\n                    p = pkt->vlanid;\n                p += range->vlan_id_inc;\n                if (p < range->vlan_id_min)\n                    p = range->vlan_id_max;\n                else if (p > range->vlan_id_max)\n                    p = range->vlan_id_min;\n                pkt->vlanid = p;\n            } else\n                pkt->vlanid = range->vlan_id;\n\n            if (unlikely(range->cos_inc != 0)) {\n                uint32_t p;\n                static uint8_t min_cos_set = 0;\n\n                if ((pkt->cos == MIN_COS) && !min_cos_set) {\n                    p           = 0;\n                    min_cos_set = 1;\n                } else\n                    p = pkt->cos;\n                p += range->cos_inc;\n                if (p < range->cos_min)\n                    p = range->cos_max;\n                else if (p > range->cos_max)\n                    p = range->cos_min;\n                pkt->cos = p;\n            } else\n                pkt->cos = range->cos;\n\n            if (unlikely(range->traffic_class_inc != 0)) {\n                uint32_t p;\n                static uint8_t min_traffic_class_set = 0;\n\n                if ((pkt->traffic_class == MIN_TOS) && !min_traffic_class_set) {\n                    p                     = 0;\n                    min_traffic_class_set = 1;\n                } else\n                    p = pkt->traffic_class;\n                p += range->traffic_class_inc;\n                if (p < range->traffic_class_min)\n                    p = range->traffic_class_max;\n                else if (p > range->traffic_class_max)\n                    p = range->traffic_class_min;\n                pkt->traffic_class = p;\n            } else\n                pkt->traffic_class = range->traffic_class;\n\n            if (unlikely(range->pkt_size_inc != 0)) {\n                uint32_t p = pkt->pkt_size;\n\n                p += range->pkt_size_inc;\n                if (p < range->pkt_size_min)\n                    p = range->pkt_size_max;\n                else if (p > range->pkt_size_max)\n                    p = range->pkt_size_min;\n                pkt->pkt_size = p;\n            } else\n                pkt->pkt_size = range->pkt_size;\n\n            if (unlikely(range->src_mac_inc != 0)) {\n                uint64_t p;\n\n                inet_mtoh64(&pkt->eth_src_addr, &p);\n\n                p += range->src_mac_inc;\n                if (p < range->src_mac_min)\n                    p = range->src_mac_max;\n                else if (p > range->src_mac_max)\n                    p = range->src_mac_min;\n\n                inet_h64tom(p, &pkt->eth_src_addr);\n            } else\n                inet_h64tom(range->src_mac, &pkt->eth_src_addr);\n\n            if (unlikely(range->dst_mac_inc != 0)) {\n                uint64_t p;\n\n                inet_mtoh64(&pkt->eth_dst_addr, &p);\n\n                p += range->dst_mac_inc;\n                if (p < range->dst_mac_min)\n                    p = range->dst_mac_max;\n                else if (p > range->dst_mac_max)\n                    p = range->dst_mac_min;\n\n                inet_h64tom(p, &pkt->eth_dst_addr);\n            } else\n                inet_h64tom(range->dst_mac, &pkt->eth_dst_addr);\n\n            break;\n        default:\n            pktgen_log_info(\"IPv6 ipProto %04x\", pkt->ipProto);\n            break;\n        }\n        break;\n    default:\n        pktgen_log_info(\"ethType %04x\", pkt->ethType);\n        break;\n    }\n}\n\n/**\n *\n * pktgen_print_range - Display the range data page.\n *\n * DESCRIPTION\n * Display the range data page on the screen.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nstatic void\npktgen_print_range(void)\n{\n    port_info_t *pinfo;\n    range_info_t *range;\n    uint32_t pid, col, sp;\n    char buff[32];\n    int32_t row;\n    int32_t col_0 = COLUMN_WIDTH_0 + 1, col_1 = COLUMN_WIDTH_1 + 4;\n    struct rte_ether_addr eaddr;\n    char str[64];\n\n    display_topline(\"<Range Page>\", pktgen.starting_port, (pktgen.ending_port - 1),\n                    pktgen.nb_ports);\n\n    pktgen_display_set_color(\"stats.stat.label\");\n    row = PORT_FLAGS_ROW;\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"Port #\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"IP.proto\");\n\n    row++;\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"dst.ip\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"    min\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"    max\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"    inc\");\n\n    row++;\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"src.ip\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"    min\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"    max\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"    inc\");\n\n    row++;\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"dst.port :min/max/inc\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"src.port :min/max/inc\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"ttl      :min/max/inc\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"vlan.id  :min/max/inc\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"cos      :min/max/inc\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"tos      :min/max/inc\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"pkt.size :min/max/inc\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"gtpu.teid:min/max/inc\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"vxlan.gid:min/max/inc\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"      vid:min/max/inc\");\n\n    row++;\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"tcp.flags\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"tcp.seq   :min/max/inc\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"tcp.ack   :min/max/inc\");\n\n    row++;\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"dst.mac\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"    min\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"    max\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"    inc\");\n\n    row++;\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"src.mac\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"    min\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"    max\");\n    scrn_printf(row++, 1, \"%-*s\", col_0, \"    inc\");\n\n    /* Get the last location to use for the window starting row. */\n    pktgen.last_row = ++row;\n    display_dashline(pktgen.last_row);\n\n    /* Display the colon after the row label. */\n    pktgen_print_div(3, pktgen.last_row - 1, col_0 - 1);\n\n    sp = pktgen.starting_port;\n    for (pid = 0; pid < pktgen.nb_ports_per_page; pid++) {\n        pinfo = l2p_get_port_pinfo(pid + sp);\n        if (pinfo == NULL)\n            continue;\n\n        /* Display Port information Src/Dest IP addr, Netmask, Src/Dst MAC addr */\n        col = (col_1 * pid) + col_0;\n        row = PORT_FLAGS_ROW;\n\n        pktgen_display_set_color(\"stats.stat.label\");\n        /* Display the port number for the column */\n        snprintf(buff, sizeof(buff), \"Port-%d\", pid + sp);\n        scrn_printf(row++, col, \"%*s\", col_1, buff);\n\n        pktgen_display_set_color(\"stats.stat.values\");\n        range = &pinfo->range;\n\n        scrn_printf(row++, col, \"%*s\", col_1, (range->ip_proto == PG_IPPROTO_TCP) ? \"TCP\" : \"UDP\");\n\n        if (pinfo->seq_pkt[RANGE_PKT].ethType == RTE_ETHER_TYPE_IPV6) {\n            row++;\n            scrn_printf(\n                row++, col, \"%*s\", col_1,\n                inet_ntop6(buff, sizeof(buff), range->dst_ipv6, PG_PREFIXMAX | ((col_1 - 1) << 8)));\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop6(buff, sizeof(buff), range->dst_ipv6_min,\n                                   PG_PREFIXMAX | ((col_1 - 1) << 8)));\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop6(buff, sizeof(buff), range->dst_ipv6_max,\n                                   PG_PREFIXMAX | ((col_1 - 1) << 8)));\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop6(buff, sizeof(buff), range->dst_ipv6_inc,\n                                   PG_PREFIXMAX | ((col_1 - 1) << 8)));\n\n            row++;\n            scrn_printf(\n                row++, col, \"%*s\", col_1,\n                inet_ntop6(buff, sizeof(buff), range->src_ipv6, PG_PREFIXMAX | ((col_1 - 1) << 8)));\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop6(buff, sizeof(buff), range->src_ipv6_min,\n                                   PG_PREFIXMAX | ((col_1 - 1) << 8)));\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop6(buff, sizeof(buff), range->src_ipv6_max,\n                                   PG_PREFIXMAX | ((col_1 - 1) << 8)));\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop6(buff, sizeof(buff), range->src_ipv6_inc,\n                                   PG_PREFIXMAX | ((col_1 - 1) << 8)));\n        } else {\n            row++;\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop4(buff, sizeof(buff), htonl(range->dst_ip), 0xFFFFFFFF));\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop4(buff, sizeof(buff), htonl(range->dst_ip_min), 0xFFFFFFFF));\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop4(buff, sizeof(buff), htonl(range->dst_ip_max), 0xFFFFFFFF));\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop4(buff, sizeof(buff), htonl(range->dst_ip_inc), 0xFFFFFFFF));\n\n            row++;\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop4(buff, sizeof(buff), htonl(range->src_ip), 0xFFFFFFFF));\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop4(buff, sizeof(buff), htonl(range->src_ip_min), 0xFFFFFFFF));\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop4(buff, sizeof(buff), htonl(range->src_ip_max), 0xFFFFFFFF));\n            scrn_printf(row++, col, \"%*s\", col_1,\n                        inet_ntop4(buff, sizeof(buff), htonl(range->src_ip_inc), 0xFFFFFFFF));\n        }\n\n        row++;\n        snprintf(str, sizeof(str), \"%5d:%5d/%5d/%5d\", range->dst_port, range->dst_port_min,\n                 range->dst_port_max, range->dst_port_inc);\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        snprintf(str, sizeof(str), \"%5d:%5d/%5d/%5d\", range->src_port, range->src_port_min,\n                 range->src_port_max, range->src_port_inc);\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        pinfo->seq_pkt[RANGE_PKT].ethType == RTE_ETHER_TYPE_IPV6\n            ? snprintf(str, sizeof(str), \"%5d:%5d/%5d/%5d\", range->hop_limits,\n                       range->hop_limits_min, range->hop_limits_max, range->hop_limits_inc)\n            : snprintf(str, sizeof(str), \"%5d:%5d/%5d/%5d\", range->ttl, range->ttl_min,\n                       range->ttl_max, range->ttl_inc);\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        snprintf(str, sizeof(str), \"%5d:%5d/%5d/%5d\", range->vlan_id, range->vlan_id_min,\n                 range->vlan_id_max, range->vlan_id_inc);\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        snprintf(str, sizeof(str), \"%5d:%5d/%5d/%5d\", range->cos, range->cos_min, range->cos_max,\n                 range->cos_inc);\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        pinfo->seq_pkt[RANGE_PKT].ethType == RTE_ETHER_TYPE_IPV6\n            ? snprintf(str, sizeof(str), \"%5d:%5d/%5d/%5d\", range->traffic_class,\n                       range->traffic_class_min, range->traffic_class_max, range->traffic_class_inc)\n            : snprintf(str, sizeof(str), \"%5d:%5d/%5d/%5d\", range->tos, range->tos_min,\n                       range->tos_max, range->tos_inc);\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        snprintf(str, sizeof(str), \"%5d:%5d/%5d/%5d\", range->pkt_size + RTE_ETHER_CRC_LEN,\n                 range->pkt_size_min + RTE_ETHER_CRC_LEN, range->pkt_size_max + RTE_ETHER_CRC_LEN,\n                 range->pkt_size_inc);\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        snprintf(str, sizeof(str), \"%5d:%5d/%5d/%5d\", range->gtpu_teid, range->gtpu_teid_min,\n                 range->gtpu_teid_max, range->gtpu_teid_inc);\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        snprintf(str, sizeof(str), \"%5d:%5d/%5d/%5d\", range->vxlan_gid, range->vxlan_gid_min,\n                 range->vxlan_gid_max, range->vxlan_gid_inc);\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        snprintf(str, sizeof(str), \"%5d:%5d/%5d/%5d\", range->vxlan_vid, range->vxlan_vid_min,\n                 range->vxlan_vid_max, range->vxlan_vid_inc);\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        row++;\n        snprintf(str, sizeof(str), \"%s%s%s%s%s%s\", range->tcp_flags & URG_FLAG ? \"U\" : \".\",\n                 range->tcp_flags & ACK_FLAG ? \"A\" : \".\", range->tcp_flags & PSH_FLAG ? \"P\" : \".\",\n                 range->tcp_flags & RST_FLAG ? \"R\" : \".\", range->tcp_flags & SYN_FLAG ? \"S\" : \".\",\n                 range->tcp_flags & FIN_FLAG ? \"F\" : \".\");\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        snprintf(str, sizeof(str), \"%x:%x/%x/%x\", range->tcp_seq, range->tcp_seq_min,\n                 range->tcp_seq_max, range->tcp_seq_inc);\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        snprintf(str, sizeof(str), \"%x:%x/%x/%x\", range->tcp_ack, range->tcp_ack_min,\n                 range->tcp_ack_max, range->tcp_ack_inc);\n        scrn_printf(row++, col, \"%*s\", col_1, str);\n\n        row++;\n        scrn_printf(row++, col, \"%*s\", col_1,\n                    inet_mtoa(buff, sizeof(buff), inet_h64tom(range->dst_mac, &eaddr)));\n        scrn_printf(row++, col, \"%*s\", col_1,\n                    inet_mtoa(buff, sizeof(buff), inet_h64tom(range->dst_mac_min, &eaddr)));\n        scrn_printf(row++, col, \"%*s\", col_1,\n                    inet_mtoa(buff, sizeof(buff), inet_h64tom(range->dst_mac_max, &eaddr)));\n        scrn_printf(row++, col, \"%*s\", col_1,\n                    inet_mtoa(buff, sizeof(buff), inet_h64tom(range->dst_mac_inc, &eaddr)));\n\n        row++;\n        scrn_printf(row++, col, \"%*s\", col_1,\n                    inet_mtoa(buff, sizeof(buff), inet_h64tom(range->src_mac, &eaddr)));\n        scrn_printf(row++, col, \"%*s\", col_1,\n                    inet_mtoa(buff, sizeof(buff), inet_h64tom(range->src_mac_min, &eaddr)));\n        scrn_printf(row++, col, \"%*s\", col_1,\n                    inet_mtoa(buff, sizeof(buff), inet_h64tom(range->src_mac_max, &eaddr)));\n        scrn_printf(row++, col, \"%*s\", col_1,\n                    inet_mtoa(buff, sizeof(buff), inet_h64tom(range->src_mac_inc, &eaddr)));\n    }\n\n    pktgen_display_set_color(NULL);\n\n    pktgen.flags &= ~PRINT_LABELS_FLAG;\n}\n\n/**\n *\n * pktgen_page_range - Display the range data page.\n *\n * DESCRIPTION\n * Display the range data page for a given port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_page_range(void)\n{\n    if (pktgen.flags & PRINT_LABELS_FLAG)\n        pktgen_print_range();\n}\n\n/**\n *\n * pktgen_range_setup - Setup the default values for a range port.\n *\n * DESCRIPTION\n * Setup the default range data for a given port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_range_setup(port_info_t *pinfo)\n{\n    range_info_t *range = &pinfo->range;\n\n    range->ip_proto = pinfo->seq_pkt[SINGLE_PKT].ipProto;\n\n    range->dst_ip     = IPv4(192, 168, pinfo->pid + 1, 1);\n    range->dst_ip_min = IPv4(192, 168, pinfo->pid + 1, 1);\n    range->dst_ip_max = IPv4(192, 168, pinfo->pid + 1, 254);\n    range->dst_ip_inc = 0x00000001;\n\n    range->src_ip     = IPv4(192, 168, pinfo->pid, 1);\n    range->src_ip_min = IPv4(192, 168, pinfo->pid, 1);\n    range->src_ip_max = IPv4(192, 168, pinfo->pid, 254);\n    range->src_ip_inc = 0x00000000;\n\n    range->dst_port     = pinfo->seq_pkt[SINGLE_PKT].dport;\n    range->dst_port_inc = 0x0001;\n    range->dst_port_min = 0;\n    range->dst_port_max = 65535;\n\n    range->src_port     = pinfo->seq_pkt[SINGLE_PKT].sport;\n    range->src_port_inc = 0x0001;\n    range->src_port_min = 0;\n    range->src_port_max = 65535;\n\n    range->ttl     = pinfo->seq_pkt[SINGLE_PKT].ttl;\n    range->ttl_inc = 0;\n    range->ttl_min = 0;\n    range->ttl_max = 255;\n\n    range->vlan_id     = pinfo->seq_pkt[SINGLE_PKT].vlanid;\n    range->vlan_id_inc = 0;\n    range->vlan_id_min = MIN_VLAN_ID;\n    range->vlan_id_max = MAX_VLAN_ID;\n\n    range->cos     = pinfo->seq_pkt[SINGLE_PKT].cos;\n    range->cos_inc = 0;\n    range->cos_min = MIN_COS;\n    range->cos_max = MAX_COS;\n\n    range->tos     = pinfo->seq_pkt[SINGLE_PKT].tos;\n    range->tos_inc = 0;\n    range->tos_min = MIN_TOS;\n    range->tos_max = MAX_TOS;\n\n    range->pkt_size     = (RTE_ETHER_MIN_LEN - RTE_ETHER_CRC_LEN);\n    range->pkt_size_inc = 0;\n    range->pkt_size_min = (RTE_ETHER_MIN_LEN - RTE_ETHER_CRC_LEN);\n    range->pkt_size_max = (RTE_ETHER_MAX_LEN - RTE_ETHER_CRC_LEN);\n    range->pkt_size_max = (pktgen.flags & JUMBO_PKTS_FLAG) ? RTE_ETHER_MAX_JUMBO_FRAME_LEN\n                                                           : RTE_ETHER_MAX_LEN;\n    range->pkt_size_max -= RTE_ETHER_CRC_LEN;\n\n    range->vxlan_gid     = pinfo->seq_pkt[SINGLE_PKT].group_id;\n    range->vxlan_gid_inc = 0;\n    range->vxlan_gid_min = 0;\n    range->vxlan_gid_max = 65535;\n\n    range->vxlan_vid     = pinfo->seq_pkt[SINGLE_PKT].vxlan_id;\n    range->vxlan_vid_inc = 0;\n    range->vxlan_vid_min = 0;\n    range->vxlan_vid_max = 65535; /* Should be 24 bits */\n\n    range->vni_flags = pinfo->vni_flags;\n\n    range->tcp_flags = pinfo->seq_pkt[SINGLE_PKT].tcp_flags;\n\n    range->tcp_seq     = pinfo->seq_pkt[SINGLE_PKT].tcp_seq;\n    range->tcp_seq_inc = 0;\n    range->tcp_seq_min = 0;\n    range->tcp_seq_max = MAX_TCP_SEQ_NUMBER;\n\n    range->tcp_ack     = pinfo->seq_pkt[SINGLE_PKT].tcp_ack;\n    range->tcp_ack_inc = 0;\n    range->tcp_ack_min = 0;\n    range->tcp_ack_max = MAX_TCP_ACK_NUMBER;\n\n    pinfo->seq_pkt[RANGE_PKT].pkt_size = range->pkt_size;\n\n    inet_mtoh64(&pinfo->seq_pkt[SINGLE_PKT].eth_dst_addr, &range->dst_mac);\n    memset(&range->dst_mac_inc, 0, sizeof(range->dst_mac_inc));\n    memset(&range->dst_mac_min, 0, sizeof(range->dst_mac_min));\n    memset(&range->dst_mac_max, 0, sizeof(range->dst_mac_max));\n\n    inet_mtoh64(&pinfo->seq_pkt[SINGLE_PKT].eth_src_addr, &range->src_mac);\n    memset(&range->src_mac_inc, 0, sizeof(range->src_mac_inc));\n    memset(&range->src_mac_min, 0, sizeof(range->src_mac_min));\n    memset(&range->src_mac_max, 0, sizeof(range->src_mac_max));\n}\n"
  },
  {
    "path": "app/pktgen-range.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_RANGE_H_\n#define _PKTGEN_RANGE_H_\n\n/**\n * @file\n *\n * Range-mode packet field cycling for Pktgen.\n *\n * Defines the range_info_t structure that stores per-port start, min, max,\n * and increment values for every field that can sweep across a configurable\n * range during SEND_RANGE_PKTS traffic generation.\n */\n\n#include <stdint.h>\n\n#include \"pktgen-seq.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct range_info_s {\n    /** Source IP increment (per-burst step; IPv4 or IPv6 depending on mode) */\n    union {\n        uint32_t src_ip_inc;\n        uint8_t src_ipv6_inc[PG_IN6ADDRSZ];\n    };\n    /** Destination IP increment (per-burst step; IPv4 or IPv6 depending on mode) */\n    union {\n        uint32_t dst_ip_inc;\n        uint8_t dst_ipv6_inc[PG_IN6ADDRSZ];\n    };\n    uint16_t src_port_inc; /**< Source port increment */\n    uint16_t dst_port_inc; /**< Destination port increment */\n    uint16_t vlan_id_inc;  /**< VLAN id increment */\n    uint16_t tcp_seq_inc;  /**< TCP sequence number increment */\n    uint16_t tcp_ack_inc;  /**< TCP acknowledgement number increment */\n    /** ToS / traffic-class increment (per-burst step) */\n    union {\n        uint16_t tos_inc;\n        uint16_t traffic_class_inc;\n    };\n    uint16_t cos_inc;      /**< CoS / priority value increment */\n    uint16_t pkt_size_inc; /**< Packet size increment */\n    uint64_t src_mac_inc;  /**< Source MAC increment */\n    uint64_t dst_mac_inc;  /**< Destination MAC increment */\n    /** TTL / hop-limits increment (per-burst step) */\n    union {\n        uint8_t ttl_inc;\n        uint8_t hop_limits_inc;\n    };\n\n    uint8_t pad0[3];\n\n    /** Source starting IP address (IPv4 or IPv6 depending on mode) */\n    union {\n        uint32_t src_ip;\n        uint8_t src_ipv6[PG_IN6ADDRSZ];\n    };\n    /** Source IP address minimum */\n    union {\n        uint32_t src_ip_min;\n        uint8_t src_ipv6_min[PG_IN6ADDRSZ];\n    };\n    /** Source IP address maximum */\n    union {\n        uint32_t src_ip_max;\n        uint8_t src_ipv6_max[PG_IN6ADDRSZ];\n    };\n    /** Destination starting IP address (IPv4 or IPv6 depending on mode) */\n    union {\n        uint32_t dst_ip;\n        uint8_t dst_ipv6[PG_IN6ADDRSZ];\n    };\n    /** Destination IP address minimum */\n    union {\n        uint32_t dst_ip_min;\n        uint8_t dst_ipv6_min[PG_IN6ADDRSZ];\n    };\n    /** Destination IP address maximum */\n    union {\n        uint32_t dst_ip_max;\n        uint8_t dst_ipv6_max[PG_IN6ADDRSZ];\n    };\n\n    uint16_t ip_proto; /**< IP Protocol type TCP or UDP */\n\n    uint16_t src_port;     /**< Source port starting value */\n    uint16_t src_port_min; /**< Source port minimum */\n    uint16_t src_port_max; /**< Source port maximum */\n\n    uint16_t dst_port;     /**< Destination port starting value */\n    uint16_t dst_port_min; /**< Destination port minimum */\n    uint16_t dst_port_max; /**< Destination port maximum */\n\n    uint8_t tcp_flags; /**< TCP flags value */\n\n    uint32_t tcp_seq;     /**< TCP sequence number starting value */\n    uint32_t tcp_seq_min; /**< TCP sequence number minimum */\n    uint32_t tcp_seq_max; /**< TCP sequence number maximum */\n\n    uint32_t tcp_ack;     /**< TCP acknowledgement number starting value */\n    uint32_t tcp_ack_min; /**< TCP acknowledgement number minimum */\n    uint32_t tcp_ack_max; /**< TCP acknowledgement number maximum */\n\n    uint16_t vlan_id;     /**< VLAN id starting value */\n    uint16_t vlan_id_min; /**< VLAN id minimum */\n    uint16_t vlan_id_max; /**< VLAN id maximum */\n\n    uint8_t cos;     /**< CoS / priority value starting */\n    uint8_t cos_min; /**< CoS / priority value minimum */\n    uint8_t cos_max; /**< CoS / priority value maximum */\n\n    /** ToS / traffic-class starting value */\n    union {\n        uint8_t tos;\n        uint8_t traffic_class;\n    };\n    /** ToS / traffic-class minimum */\n    union {\n        uint8_t tos_min;\n        uint8_t traffic_class_min;\n    };\n    /** ToS / traffic-class maximum */\n    union {\n        uint8_t tos_max;\n        uint8_t traffic_class_max;\n    };\n\n    uint16_t pkt_size;     /**< Packet size starting value (bytes) */\n    uint16_t pkt_size_min; /**< Packet size minimum (bytes) */\n    uint16_t pkt_size_max; /**< Packet size maximum (bytes) */\n\n    uint64_t dst_mac;     /**< Destination starting MAC address */\n    uint64_t dst_mac_min; /**< Destination minimum MAC address */\n    uint64_t dst_mac_max; /**< Destination maximum MAC address */\n\n    uint64_t src_mac;     /**< Source starting MAC address */\n    uint64_t src_mac_min; /**< Source minimum MAC address */\n    uint64_t src_mac_max; /**< Source maximum MAC address */\n\n    /** TTL / hop-limits starting value */\n    union {\n        uint8_t ttl;\n        uint8_t hop_limits;\n    };\n    /** TTL / hop-limits minimum */\n    union {\n        uint8_t ttl_min;\n        uint8_t hop_limits_min;\n    };\n    /** TTL / hop-limits maximum */\n    union {\n        uint8_t ttl_max;\n        uint8_t hop_limits_max;\n    };\n\n    uint32_t gtpu_teid;     /**< GTP-U TEID starting value */\n    uint32_t gtpu_teid_inc; /**< GTP-U TEID increment */\n    uint32_t gtpu_teid_min; /**< GTP-U TEID minimum */\n    uint32_t gtpu_teid_max; /**< GTP-U TEID maximum */\n\n    uint32_t vxlan_gid;     /**< VxLAN Group ID starting value */\n    uint32_t vxlan_gid_inc; /**< VxLAN Group ID increment */\n    uint32_t vxlan_gid_min; /**< VxLAN Group ID minimum */\n    uint32_t vxlan_gid_max; /**< VxLAN Group ID maximum */\n\n    uint32_t vxlan_vid;     /**< VxLAN VLAN ID starting value */\n    uint32_t vxlan_vid_inc; /**< VxLAN VLAN ID increment */\n    uint32_t vxlan_vid_min; /**< VxLAN VLAN ID minimum */\n    uint32_t vxlan_vid_max; /**< VxLAN VLAN ID maximum */\n\n    uint32_t vni_flags; /**< VxLAN VNI flags */\n} range_info_t;\n\nstruct port_info_s;\n\n/**\n * Populate a packet sequence template from a range_info_t configuration.\n *\n * @param range\n *   Pointer to the range configuration to apply.\n * @param pkt\n *   Packet sequence entry to update with current range field values.\n */\nvoid pktgen_range_ctor(range_info_t *range, pkt_seq_t *pkt);\n\n/**\n * Initialise range-mode state for a port.\n *\n * Copies the default single-packet template into the range packet slot and\n * sets up the initial range field values from the port's range_info_t.\n *\n * @param info\n *   Per-port state structure to initialise.\n */\nvoid pktgen_range_setup(struct port_info_s *info);\n\n/**\n * Render the range-mode display page to the terminal.\n */\nvoid pktgen_page_range(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_RANGE_H_ */\n"
  },
  {
    "path": "app/pktgen-seq.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <lua_config.h>\n\n#include \"pktgen-display.h\"\n#include \"pktgen.h\"\n\nvoid\npktgen_send_seq_pkt(port_info_t *pinfo, uint32_t seq_idx)\n{\n    (void)pinfo;\n    (void)seq_idx;\n}\n\n/**\n *\n * pktgen_page_seq - Display the sequence port data on the screen.\n *\n * DESCRIPTION\n * For a given port display the sequence packet data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_page_seq(uint32_t pid)\n{\n    uint32_t i, row, col, sav;\n    port_info_t *pinfo;\n    pkt_seq_t *pkt;\n    char buff[128];\n\n    display_topline(\"<Sequence Page>\", 0, 0, 0);\n\n    pinfo = l2p_get_port_pinfo(pid);\n    if (pinfo == NULL)\n        return;\n\n    pktgen_display_set_color(\"top.ports\");\n    row = PORT_FLAGS_ROW;\n    col = 1;\n    scrn_printf(row, col, \"Port: %2d, Sequence Count: %2d of %2d  \", pid, pinfo->seqCnt,\n                NUM_SEQ_PKTS);\n    pktgen_display_set_color(\"stats.stat.label\");\n    scrn_printf(row++, col + 102, \"GTP-u\");\n    scrn_printf(row++, col, \"%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\", 3, \"Seq\", 15, \"Dst MAC\", 15,\n                \"Src MAC\", 16, \"Dst IP\", 18, \"Src IP\", 13, \"TTL/Port S/D\", 20,\n                \"Protocol:VLAN:Flags\", 8, \"CoS/ToS\", 5, \"TEID\", 17, \"Flag/Group/vxlan\", 6, \"Size\");\n\n    pktgen_display_set_color(\"stats.stat.values\");\n    scrn_fgcolor(SCRN_DEFAULT_FG, SCRN_NO_ATTR);\n    sav = row;\n    for (i = 0; i <= NUM_SEQ_PKTS; i++) {\n        if (i >= pinfo->seqCnt)\n            scrn_eol_pos(row++, col);\n    }\n    row = sav;\n    for (i = 0; i < pinfo->seqCnt; i++) {\n        pkt = &pinfo->seq_pkt[i];\n\n        col = 1;\n        scrn_printf(row, col, \"%2d:\", i);\n\n        col += 3;\n        snprintf(buff, sizeof(buff), \"%02x%02x:%02x%02x:%02x%02x\", pkt->eth_dst_addr.addr_bytes[0],\n                 pkt->eth_dst_addr.addr_bytes[1], pkt->eth_dst_addr.addr_bytes[2],\n                 pkt->eth_dst_addr.addr_bytes[3], pkt->eth_dst_addr.addr_bytes[4],\n                 pkt->eth_dst_addr.addr_bytes[5]);\n        scrn_printf(row, col, \"%*s\", 15, buff);\n\n        col += 15;\n        snprintf(buff, sizeof(buff), \"%02x%02x:%02x%02x:%02x%02x\", pkt->eth_src_addr.addr_bytes[0],\n                 pkt->eth_src_addr.addr_bytes[1], pkt->eth_src_addr.addr_bytes[2],\n                 pkt->eth_src_addr.addr_bytes[3], pkt->eth_src_addr.addr_bytes[4],\n                 pkt->eth_src_addr.addr_bytes[5]);\n        scrn_printf(row, col, \"%*s\", 15, buff);\n\n        col += 15;\n        scrn_printf(\n            row, col, \"%*s\", 16,\n            inet_ntop4(buff, sizeof(buff), htonl(pkt->ip_dst_addr.addr.ipv4.s_addr), 0xFFFFFFFF));\n\n        col += 16;\n        scrn_printf(\n            row, col, \"%*s\", 18,\n            inet_ntop4(buff, sizeof(buff), htonl(pkt->ip_src_addr.addr.ipv4.s_addr), pkt->ip_mask));\n\n        col += 18;\n        snprintf(buff, sizeof(buff), \"%d/%d/%d\", pkt->ttl, pkt->sport, pkt->dport);\n        scrn_printf(row, col, \"%*s\", 13, buff);\n\n        col += 13;\n        snprintf(buff, sizeof(buff), \"%s/%s:%04x:%04x\",\n                 (pkt->ethType == RTE_ETHER_TYPE_IPV4)   ? \"IPv4\"\n                 : (pkt->ethType == RTE_ETHER_TYPE_IPV6) ? \"IPv6\"\n                                                         : \"....\",\n                 (pkt->ipProto == PG_IPPROTO_TCP)    ? \"TCP\"\n                 : (pkt->ipProto == PG_IPPROTO_ICMP) ? \"ICMP\"\n                                                     : \"UDP\",\n                 pkt->vlanid, pkt->tcp_flags);\n        scrn_printf(row, col, \"%*s\", 20, buff);\n\n        col += 20;\n        snprintf(buff, sizeof(buff), \"%3d/%3d\", pkt->cos, pkt->tos);\n        scrn_printf(row, col, \"%*s\", 8, buff);\n\n        col += 8;\n        scrn_printf(row, col, \"%5d\", pkt->gtpu_teid);\n\n        col += 6;\n        snprintf(buff, sizeof(buff), \"%04x/%5d/%5d\", pkt->vni_flags, pkt->group_id, pkt->vxlan_id);\n        scrn_printf(row, col, \"%*s\", 16, buff);\n\n        col += 16;\n        scrn_printf(row, col, \"%6d\", pkt->pkt_size + RTE_ETHER_CRC_LEN);\n\n        row++;\n    }\n\n    display_dashline(row + 2);\n    pktgen_display_set_color(NULL);\n}\n"
  },
  {
    "path": "app/pktgen-seq.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_SEQ_H_\n#define _PKTGEN_SEQ_H_\n\n/**\n * @file\n *\n * Packet sequence template for Pktgen.\n *\n * Defines pkt_seq_t, the per-slot packet template that stores all layer-2\n * through layer-7 parameters needed to build a transmit packet.\n * Each port has an array of NUM_TOTAL_PKTS such templates.\n */\n\n#include <rte_ether.h>\n#include <cmdline_parse.h>\n#include <cmdline_parse_ipaddr.h>\n#include <pg_inet.h>\n\n#include \"pktgen-constants.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n__extension__ typedef void *MARKER[0]; /**< generic marker for a point in a structure */\n\n/** Per-slot packet template containing all fields needed to build a packet. */\ntypedef struct pkt_seq_s {\n    /* Packet type and information */\n    struct rte_ether_addr eth_dst_addr; /**< Destination Ethernet address */\n    struct rte_ether_addr eth_src_addr; /**< Source Ethernet address */\n\n    struct cmdline_ipaddr ip_src_addr; /**< Source IPv4 address also used for IPv6 */\n    struct cmdline_ipaddr ip_dst_addr; /**< Destination IPv4 address */\n    uint32_t ip_mask;                  /**< IPv4 Netmask value */\n\n    uint16_t sport;   /**< Source port value */\n    uint16_t dport;   /**< Destination port value */\n    uint16_t ethType; /**< IPv4 or IPv6 */\n    uint16_t ipProto; /**< TCP or UDP or ICMP */\n    uint16_t vlanid;  /**< VLAN ID value if used */\n    uint8_t cos;      /**< 802.1p cos value if used */\n    union {\n        uint8_t tos;           /**< tos value if used */\n        uint8_t traffic_class; /**< traffic class for IPv6 headers*/\n    };\n    uint16_t ether_hdr_size; /**< Size of Ethernet header in packet for VLAN ID */\n\n    uint32_t tcp_seq;   /**< TCP sequence number */\n    uint32_t tcp_ack;   /**< TCP acknowledge number*/\n    uint16_t tcp_flags; /**< TCP flags value */\n\n    uint32_t mpls_entry;   /**< MPLS entry if used */\n    uint16_t qinq_outerid; /**< Outer VLAN ID if Q-in-Q */\n    uint16_t qinq_innerid; /**< Inner VLAN ID if Q-in-Q */\n    uint32_t gre_key;      /**< GRE key if used */\n\n    uint16_t pkt_size;   /**< Size of packet in bytes not counting FCS */\n    uint8_t seq_enabled; /**< Enable or disable this sequence */\n    union {\n        uint8_t ttl;        /**< TTL value for IPv4 headers */\n        uint8_t hop_limits; /**< Hop limits for IPv6 headers */\n    };\n    uint32_t gtpu_teid; /**< GTP-U TEID, if UDP dport=2152 */\n\n    union {\n        uint64_t vxlan; /**< VxLAN 64 bit word */\n        struct {\n            uint16_t vni_flags; /**< VxLAN Flags */\n            uint16_t group_id;  /**< VxLAN Group Policy ID */\n            uint32_t vxlan_id;  /**< VxLAN VNI */\n        };\n    };\n    uint64_t ol_flags; /**< offload flags */\n    pkt_hdr_t *hdr;    /**< Packet header data */\n} pkt_seq_t __rte_cache_aligned;\n\nstruct port_info_s;\n\n/**\n * Build and transmit the sequence packet at a given slot index.\n *\n * @param info     Per-port state.\n * @param seq_idx  Sequence slot index (0 .. NUM_SEQ_PKTS-1).\n */\nvoid pktgen_send_seq_pkt(struct port_info_s *info, uint32_t seq_idx);\n\n/**\n * Render the sequence-mode display page to the terminal.\n *\n * @param pid  Port ID whose sequence slots are displayed.\n */\nvoid pktgen_page_seq(uint32_t pid);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_SEQ_H_ */\n"
  },
  {
    "path": "app/pktgen-stats.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <stdio.h>\n#include <string.h>\n\n#include <pg_delay.h>\n#include <lua_config.h>\n\n#include \"pktgen-cmds.h\"\n#include \"pktgen-display.h\"\n\n#include \"pktgen.h\"\n\n#include <rte_bus_pci.h>\n#include <rte_bus.h>\n\n/**\n *\n * pktgen_print_static_data - Display the static data on the screen.\n *\n * DESCRIPTION\n * Display a set of port static data on the screen.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic void\npktgen_print_static_data(void)\n{\n    port_info_t *pinfo;\n    struct rte_eth_dev_info dev = {0};\n    uint32_t pid, col, row, sp, ip_row;\n    pkt_seq_t *pkt;\n    char buff[INET6_ADDRSTRLEN * 2];\n    int display_cnt;\n\n    display_topline(\"<Main Page>\", pktgen.starting_port, (pktgen.ending_port - 1), pktgen.nb_ports);\n\n    row = PORT_FLAGS_ROW;\n    pktgen_display_set_color(\"stats.port.label\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Port:Flags\");\n\n    row = LINK_STATE_ROW;\n    /* Labels for dynamic fields (update every second) */\n    pktgen_display_set_color(\"stats.port.linklbl\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Link State\");\n\n    pktgen_display_set_color(\"stats.port.sizes\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Pkts/s Rx:Tx\");\n\n    pktgen_display_set_color(\"stats.mac\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"MBits/s Rx:Tx\");\n\n    pktgen_display_set_color(\"stats.port.totals\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Max Pkts/s Rx:Tx\");\n\n    pktgen_display_set_color(\"stats.port.totlbl\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Total Rx Pkts\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"      Tx Pkts\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"      Rx:Tx MBs\");\n\n    pktgen_display_set_color(\"stats.port.errlbl\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Errors Rx/Tx/missed\");\n\n    row = PKT_SIZE_ROW;\n    pktgen_display_set_color(\"stats.port.sizelbl\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Broadcast\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Multicast\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Rx Sizes 64\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"         65-127\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"         128-255\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"         256-511\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"         512-1023\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"         1024-1522\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Runts/Jumbos\");\n\n    if (pktgen.flags & TX_DEBUG_FLAG) {\n        pktgen_display_set_color(\"stats.port.errlbl\");\n        scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Tx Overrun\");\n        scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"   Pkts per Queue\");\n        scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"   Cycles per Queue\");\n\n        scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Rx Missed\");\n        scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"   mcasts\");\n        scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"   No Mbuf\");\n    }\n\n    /* Labels for static fields */\n    pktgen_display_set_color(\"stats.stat.label\");\n    ip_row = row;\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Rx/Tx queue count\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Tx Count/% Rate\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Pkt Size/Rx:Tx Burst\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Port Src/Dest\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"Type:VLAN ID:Flags\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"IP  Destination\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"    Source\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"MAC Destination\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"    Source\");\n    scrn_printf(row++, 1, \"%-*s\", COLUMN_WIDTH_0, \"NUMA/Vend:ID/PCI\");\n    row++;\n\n    /* Get the last location to use for the window starting row. */\n    pktgen.last_row = row;\n    display_dashline(pktgen.last_row);\n\n    /* Display the colon after the row label. */\n    pktgen_print_div(PORT_FLAGS_ROW, pktgen.last_row - 1, COLUMN_WIDTH_0 - 1);\n\n    sp          = pktgen.starting_port;\n    display_cnt = 0;\n    for (pid = 0; pid < pktgen.nb_ports_per_page; pid++) {\n        pinfo = l2p_get_port_pinfo(pid + sp);\n        if (pinfo == NULL)\n            break;\n        pktgen_display_set_color(\"stats.stat.values\");\n\n        pkt = &pinfo->seq_pkt[SINGLE_PKT];\n\n        /* Display Port information Src/Dest IP addr, Netmask, Src/Dst MAC addr */\n        col = (COLUMN_WIDTH_1 * pid) + COLUMN_WIDTH_0;\n        row = ip_row;\n\n        pktgen_display_set_color(\"stats.rate.count\");\n        snprintf(buff, sizeof(buff), \"%d/%d\", l2p_get_rxcnt(pid), l2p_get_txcnt(pid));\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n        pktgen_transmit_count_rate(pid, buff, sizeof(buff));\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        pktgen_display_set_color(\"stats.stat.values\");\n        snprintf(buff, sizeof(buff), \"%d /%3d:%3d\", pkt->pkt_size + RTE_ETHER_CRC_LEN,\n                 pinfo->rx_burst, pinfo->tx_burst);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n        snprintf(buff, sizeof(buff), \"%5d/%5d\", pkt->sport, pkt->dport);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n        snprintf(buff, sizeof(buff), \"%s / %s:%04x:%04x\",\n                 (pkt->ethType == RTE_ETHER_TYPE_IPV4)   ? \"IPv4\"\n                 : (pkt->ethType == RTE_ETHER_TYPE_IPV6) ? \"IPv6\"\n                 : (pkt->ethType == RTE_ETHER_TYPE_ARP)  ? \"ARP\"\n                                                         : \"Other\",\n                 (pkt->ipProto == PG_IPPROTO_TCP)                               ? \"TCP\"\n                 : (pkt->ipProto == PG_IPPROTO_ICMP)                            ? \"ICMP\"\n                 : (rte_atomic64_read(&pinfo->port_flags) & SEND_VXLAN_PACKETS) ? \"VXLAN\"\n                                                                                : \"UDP\",\n                 pkt->vlanid, pkt->tcp_flags);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        pktgen_display_set_color(\"stats.ip\");\n        if (pkt->ethType == RTE_ETHER_TYPE_IPV6) {\n            scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1,\n                        inet_ntop6(buff, sizeof(buff), pkt->ip_dst_addr.addr.ipv6.a,\n                                   PG_PREFIXMAX | ((COLUMN_WIDTH_1 - 1) << 8)));\n            scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1,\n                        inet_ntop6(buff, sizeof(buff), pkt->ip_src_addr.addr.ipv6.a,\n                                   pkt->ip_src_addr.prefixlen | ((COLUMN_WIDTH_1 - 1) << 8)));\n        } else {\n            scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1,\n                        inet_ntop4(buff, sizeof(buff), htonl(pkt->ip_dst_addr.addr.ipv4.s_addr),\n                                   0xFFFFFFFF));\n            scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1,\n                        inet_ntop4(buff, sizeof(buff), htonl(pkt->ip_src_addr.addr.ipv4.s_addr),\n                                   pkt->ip_mask));\n        }\n        pktgen_display_set_color(\"stats.mac\");\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1,\n                    inet_mtoa(buff, sizeof(buff), &pkt->eth_dst_addr));\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1,\n                    inet_mtoa(buff, sizeof(buff), &pkt->eth_src_addr));\n\n        if (rte_eth_dev_info_get(pid, &dev) < 0)\n            rte_exit(EXIT_FAILURE, \"Cannot get device info for port %u\\n\", pid);\n\n        const struct rte_bus *bus = NULL;\n        if (dev.device)\n            bus = rte_bus_find_by_device(dev.device);\n        if (bus && !strcmp(rte_bus_name(bus), \"pci\")) {\n            char name[RTE_ETH_NAME_MAX_LEN];\n            char vend[8], device[8], pci[32];\n\n            vend[0] = device[0] = '\\0';\n            sscanf(rte_dev_bus_info(dev.device), \"vendor_id=%4s, device_id=%4s\", vend, device);\n\n            rte_eth_dev_get_name_by_port(pid, name);\n            strcpy(pci, rte_dev_name(dev.device));\n            snprintf(buff, sizeof(buff), \"%d/%s:%s/%s\", rte_dev_numa_node(dev.device), vend, device,\n                     &pci[5]);\n        } else\n            snprintf(buff, sizeof(buff), \"-1/0000:00:00.0\");\n        pktgen_display_set_color(\"stats.stat.values\");\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n        display_cnt++;\n    }\n\n    /* Display the string for total pkts/s rate of all ports */\n    col = (COLUMN_WIDTH_1 * display_cnt) + COLUMN_WIDTH_0;\n    pktgen_display_set_color(\"stats.total.label\");\n    scrn_printf(LINK_STATE_ROW, col, \"%*s\", COLUMN_WIDTH_3, \"---Total Rate---\");\n    scrn_eol();\n    pktgen_display_set_color(NULL);\n\n    pktgen.flags &= ~PRINT_LABELS_FLAG;\n}\n\n/**\n *\n * pktgen_get_link_status - Get the port link status.\n *\n * DESCRIPTION\n * Try to get the link status of a port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_get_link_status(port_info_t *pinfo)\n{\n    struct rte_eth_link link = (struct rte_eth_link){0};\n\n    if (rte_eth_link_get_nowait(pinfo->pid, &link) != 0)\n        return;\n\n    /* Provide safe defaults only when link is UP but speed unknown */\n    if (link.link_status == RTE_ETH_LINK_UP && link.link_speed == RTE_ETH_SPEED_NUM_UNKNOWN) {\n        link.link_speed   = RTE_ETH_SPEED_NUM_10G;\n        link.link_duplex  = RTE_ETH_LINK_FULL_DUPLEX;\n        link.link_autoneg = RTE_ETH_LINK_SPEED_AUTONEG;\n    }\n\n    /* Change if status toggled or (while up) speed/duplex/autoneg differ */\n    if (link.link_status != pinfo->link.link_status ||\n        (link.link_status == RTE_ETH_LINK_UP && (link.link_speed != pinfo->link.link_speed ||\n                                                 link.link_duplex != pinfo->link.link_duplex ||\n                                                 link.link_autoneg != pinfo->link.link_autoneg))) {\n\n        pinfo->link.link_status  = link.link_status;\n        pinfo->link.link_speed   = link.link_speed;\n        pinfo->link.link_autoneg = link.link_autoneg;\n        pinfo->link.link_duplex  = link.link_duplex;\n\n        if (link.link_status == RTE_ETH_LINK_UP)\n            pktgen_packet_rate(pinfo);\n        else {\n            /* Link down: zero pacing so UI reflects inactive state */\n            pinfo->tx_cycles = 0;\n            pinfo->tx_pps    = 0;\n        }\n    }\n}\n\n/**\n *\n * pktgen_page_stats - Display the statistics on the screen for all ports.\n *\n * DESCRIPTION\n * Display the port statistics on the screen for all ports.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_page_stats(void)\n{\n    port_info_t *pinfo;\n    unsigned int pid, col, row;\n    struct rte_eth_stats *rate, *cumm, *prev;\n\n    unsigned sp;\n    char buff[32];\n    int display_cnt;\n\n    if (pktgen.flags & PRINT_LABELS_FLAG)\n        pktgen_print_static_data();\n\n    cumm = &pktgen.cumm_rate_totals;\n    memset(cumm, 0, sizeof(struct rte_eth_stats));\n\n    /* Calculate the total values */\n    RTE_ETH_FOREACH_DEV(pid)\n    {\n        pinfo = l2p_get_port_pinfo(pid);\n        if (pinfo == NULL)\n            break;\n\n        rate = &pinfo->stats.rate;\n\n        cumm->ipackets += rate->ipackets;\n        cumm->opackets += rate->opackets;\n        cumm->ibytes += rate->ibytes;\n        cumm->obytes += rate->obytes;\n        cumm->ierrors += rate->ierrors;\n        cumm->oerrors += rate->oerrors;\n\n        if (cumm->ipackets > pktgen.max_total_ipackets)\n            pktgen.max_total_ipackets = cumm->ipackets;\n        if (cumm->opackets > pktgen.max_total_opackets)\n            pktgen.max_total_opackets = cumm->opackets;\n\n        cumm->imissed += rate->imissed;\n        cumm->rx_nombuf += rate->rx_nombuf;\n    }\n\n    sp          = pktgen.starting_port;\n    display_cnt = 0;\n    for (pid = 0; pid < pktgen.nb_ports_per_page; pid++) {\n        pinfo = l2p_get_port_pinfo(pid + sp);\n        if (pinfo == NULL)\n            break;\n\n        col = (COLUMN_WIDTH_1 * pid) + COLUMN_WIDTH_0;\n\n        /* Display the port number for the column */\n        row = PORT_FLAGS_ROW;\n        snprintf(buff, sizeof(buff), \"%d:%s\", pid + sp, pktgen_flags_string(pinfo));\n\n        pktgen_display_set_color(\"stats.port.flags\");\n        scrn_printf(row, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        /* Grab the link state of the port and display Duplex/Speed and UP/Down */\n        row = LINK_STATE_ROW;\n        pktgen_get_link_status(pinfo);\n\n        pktgen_link_state(pid + sp, buff, sizeof(buff));\n        pktgen_display_set_color(\"stats.port.status\");\n        scrn_printf(row, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        rate = &pinfo->stats.rate;\n        prev = &pinfo->stats.prev;\n\n        /* Rx/Tx pkts/s rate */\n        row = PKT_RATE_ROW;\n        pktgen_display_set_color(\"stats.port.sizes\");\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \":%'\" PRIu64, rate->ipackets, rate->opackets);\n        scrn_printf(row++, col, \"%'*s\", COLUMN_WIDTH_1, buff);\n\n        /* Rx/Tx Mbits per second */\n        pktgen_display_set_color(\"stats.port.sizes\");\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \":%'\" PRIu64,\n                 iBitsTotal(pinfo->stats.rate) / Million, oBitsTotal(pinfo->stats.rate) / Million);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        /* Max Rx/Tx packets */\n        pktgen_display_set_color(\"stats.port.sizes\");\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \":%'\" PRIu64, pktgen.max_total_ipackets,\n                 pktgen.max_total_opackets);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        /* Total Rx/Tx packets and bytes */\n        pktgen_display_set_color(\"stats.port.sizes\");\n        scrn_printf(row++, col, \"%'*llu\", COLUMN_WIDTH_1, pinfo->stats.curr.ipackets);\n        scrn_printf(row++, col, \"%'*llu\", COLUMN_WIDTH_1, pinfo->stats.curr.opackets);\n        snprintf(buff, sizeof(buff), \"%'lu:%'lu\", iBitsTotal(pinfo->stats.curr) / Million,\n                 oBitsTotal(pinfo->stats.curr) / Million);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        /* Rx/Tx Errors */\n        pktgen_display_set_color(\"stats.port.errors\");\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \"/%'\" PRIu64 \"/%'\" PRIu64, prev->ierrors,\n                 prev->oerrors, prev->imissed);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        /* Packets Sizes */\n        row = PKT_SIZE_ROW;\n        pktgen_display_set_color(\"stats.port.sizes\");\n\n        size_stats_t sizes = pinfo->stats.sizes;\n        scrn_printf(row++, col, \"%'*llu\", COLUMN_WIDTH_1, sizes.broadcast);\n        scrn_printf(row++, col, \"%'*llu\", COLUMN_WIDTH_1, sizes.multicast);\n        scrn_printf(row++, col, \"%'*llu\", COLUMN_WIDTH_1, sizes._64);\n        scrn_printf(row++, col, \"%'*llu\", COLUMN_WIDTH_1, sizes._65_127);\n        scrn_printf(row++, col, \"%'*llu\", COLUMN_WIDTH_1, sizes._128_255);\n        scrn_printf(row++, col, \"%'*llu\", COLUMN_WIDTH_1, sizes._256_511);\n        scrn_printf(row++, col, \"%'*llu\", COLUMN_WIDTH_1, sizes._512_1023);\n        scrn_printf(row++, col, \"%'*llu\", COLUMN_WIDTH_1, sizes._1024_1522);\n        snprintf(buff, sizeof(buff), \"%'\" PRIu64 \"/%'\" PRIu64, sizes.runt, sizes.jumbo);\n        scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n\n        if (pktgen.flags & TX_DEBUG_FLAG) {\n            snprintf(buff, sizeof(buff), \"%'\" PRIu64, pinfo->tx_pps);\n            scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n            snprintf(buff, sizeof(buff), \"%'\" PRIu64, pinfo->tx_cycles);\n            scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_1, buff);\n        }\n        pktgen_display_set_color(NULL);\n        display_cnt++;\n    }\n\n    /* Display the total pkts/s for all ports */\n    col = (COLUMN_WIDTH_1 * display_cnt) + COLUMN_WIDTH_0;\n    row = PKT_RATE_ROW;\n    pktgen_display_set_color(\"stats.port.sizes\");\n    snprintf(buff, sizeof(buff), \"%'\" PRIu64 \":%'\" PRIu64, cumm->ipackets, cumm->opackets);\n    scrn_printf(row++, col, \"%'*s\", COLUMN_WIDTH_3, buff);\n    scrn_eol();\n\n    pktgen_display_set_color(\"stats.port.sizes\");\n    snprintf(buff, sizeof(buff), \"%'\" PRIu64 \":%'\" PRIu64,\n             iBitsTotal(pktgen.cumm_rate_totals) / Million,\n             oBitsTotal(pktgen.cumm_rate_totals) / Million);\n    scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_3, buff);\n    scrn_eol();\n\n    pktgen_display_set_color(\"stats.port.sizes\");\n    snprintf(buff, sizeof(buff), \"%'\" PRIu64 \":%'\" PRIu64, pktgen.max_total_ipackets,\n             pktgen.max_total_opackets);\n    scrn_printf(row++, col, \"%*s\", COLUMN_WIDTH_3, buff);\n    scrn_eol();\n    pktgen_display_set_color(NULL);\n}\n\nstatic void\nprocess_xstats(port_info_t *pinfo)\n{\n    xstats_t *xs = &pinfo->stats.xstats;\n    int cnt;\n\n    if (xs->cnt == 0) {\n        /* Get count */\n        cnt = rte_eth_xstats_get_names(pinfo->pid, NULL, 0);\n        if (cnt < 0) {\n            printf(\"Error: Cannot get count of xstats\\n\");\n            return;\n        }\n        if (cnt == 0)\n            return;\n\n        xs->cnt    = cnt;\n        xs->names  = rte_calloc(NULL, cnt, sizeof(struct rte_eth_xstat_name), 0);\n        xs->xstats = rte_calloc(NULL, cnt, sizeof(struct rte_eth_xstat), 0);\n        xs->prev   = rte_calloc(NULL, cnt, sizeof(struct rte_eth_xstat), 0);\n        if (xs->names == NULL || xs->xstats == NULL || xs->prev == NULL) {\n            printf(\"Cannot allocate memory for xstats\\n\");\n            return;\n        }\n        if (rte_eth_xstats_get_names(pinfo->pid, xs->names, xs->cnt) < 0) {\n            printf(\"Error: Cannot get xstats lookup\\n\");\n            return;\n        }\n    }\n\n    if (rte_eth_xstats_get(pinfo->pid, xs->xstats, xs->cnt) < 0) {\n        printf(\"Error: Unable to get xstats\\n\");\n        return;\n    }\n\n    for (int i = 0; i < xs->cnt; i++) {\n        struct rte_eth_xstat *cur = &xs->xstats[i];\n        char *name                = xs->names[i].name;\n        uint64_t val              = cur->value;\n\n        if (val == 0)\n            continue;\n\n        /* Update size stats */\n        if (strncmp(name, \"rx_size_64\", strlen(\"rx_size_64\")) == 0)\n            pinfo->stats.sizes._64 = val;\n        else if (strncmp(name, \"rx_size_65_127\", strlen(\"rx_size_65_127\")) == 0)\n            pinfo->stats.sizes._65_127 = val;\n        else if (strncmp(name, \"rx_size_128_255\", strlen(\"rx_size_128_255\")) == 0)\n            pinfo->stats.sizes._128_255 = val;\n        else if (strncmp(name, \"rx_size_256_511\", strlen(\"rx_size_256_511\")) == 0)\n            pinfo->stats.sizes._256_511 = val;\n        else if (strncmp(name, \"rx_size_512_1023\", strlen(\"rx_size_512_1023\")) == 0)\n            pinfo->stats.sizes._512_1023 = val;\n        else if (strncmp(name, \"rx_size_1024_1522\", strlen(\"rx_size_1024_1522\")) == 0)\n            pinfo->stats.sizes._1024_1522 = val;\n        else if (strncmp(name, \"rx_oversize_errors\", strlen(\"rx_oversize_errors\")) == 0)\n            pinfo->stats.sizes.jumbo = val;\n        else if (strncmp(name, \"rx_undersized_errors\", strlen(\"rx_undersized_errors\")) == 0)\n            pinfo->stats.sizes.runt = val;\n        else if (strncmp(name, \"rx_broadcast_packets\", strlen(\"rx_broadcast_packets\")) == 0)\n            pinfo->stats.sizes.broadcast = val;\n        else if (strncmp(name, \"rx_multicast_packets\", strlen(\"rx_multicast_packets\")) == 0)\n            pinfo->stats.sizes.multicast = val;\n    }\n}\n\n/**\n *\n * pktgen_process_stats - Process statistics for all ports on timer1\n *\n * DESCRIPTION\n * When timer1 callback happens then process all of the port statistics.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_process_stats(void)\n{\n    unsigned int pid;\n    struct rte_eth_stats *curr, *rate, *prev, *base;\n    port_info_t *pinfo;\n    static unsigned int counter = 0;\n\n    counter++;\n    if (pktgen.flags & BLINK_PORTS_FLAG) {\n        RTE_ETH_FOREACH_DEV(pid)\n        {\n            if ((pktgen.blinklist & (1UL << pid)) == 0)\n                continue;\n\n            if (counter & 1)\n                rte_eth_led_on(pid);\n            else\n                rte_eth_led_off(pid);\n        }\n    }\n\n    RTE_ETH_FOREACH_DEV(pid)\n    {\n        pinfo = l2p_get_port_pinfo(pid);\n        if (pinfo == NULL)\n            break;\n\n        pktgen_get_link_status(pinfo);\n\n        curr = &pinfo->stats.curr;\n        rate = &pinfo->stats.rate;\n        prev = &pinfo->stats.prev;\n        base = &pinfo->stats.base;\n\n        memset(curr, 0, sizeof(struct rte_eth_stats));\n        rte_eth_stats_get(pid, curr);\n\n        /* Normalize the counters */\n        curr->ipackets  = curr->ipackets - base->ipackets;\n        curr->opackets  = curr->opackets - base->opackets;\n        curr->ibytes    = curr->ibytes - base->ibytes;\n        curr->obytes    = curr->obytes - base->obytes;\n        curr->ierrors   = curr->ierrors - base->ierrors;\n        curr->oerrors   = curr->oerrors - base->oerrors;\n        curr->imissed   = curr->imissed - base->imissed;\n        curr->rx_nombuf = curr->rx_nombuf - base->rx_nombuf;\n\n        /* Figure out the rate values */\n        rate->ipackets  = (curr->ipackets - prev->ipackets);\n        rate->opackets  = (curr->opackets - prev->opackets);\n        rate->ibytes    = (curr->ibytes - prev->ibytes);\n        rate->obytes    = (curr->obytes - prev->obytes);\n        rate->ierrors   = (curr->ierrors - prev->ierrors);\n        rate->oerrors   = (curr->oerrors - prev->oerrors);\n        rate->imissed   = (curr->imissed - prev->imissed);\n        rate->rx_nombuf = (curr->rx_nombuf - prev->rx_nombuf);\n\n        /* Save the current values in previous */\n        memcpy(prev, curr, sizeof(struct rte_eth_stats));\n\n        process_xstats(pinfo);\n\n        /* Snapshot per-queue counters written by worker lcores.\n         * rte_smp_rmb() ensures all worker stores are visible before the copy.\n         * snap_qstats is owned exclusively by this timer thread; the display\n         * reads from snap_qstats so it never races with the worker hot path. */\n        rte_smp_rmb();\n        int nq = RTE_MAX(l2p_get_rxcnt(pid), l2p_get_txcnt(pid));\n        for (int q = 0; q < nq && q < MAX_QUEUES_PER_PORT; q++)\n            pinfo->stats.snap_qstats[q] = pinfo->stats.qstats[q];\n    }\n}\n\nvoid\npktgen_page_qstats(uint16_t pid)\n{\n    unsigned int col, row, q, hdr, width;\n    struct rte_eth_stats *s, *r;\n    struct rte_ether_addr ethaddr;\n    char buff[128], mac_buf[32], dev_name[64];\n    uint64_t ipackets, opackets, errs;\n    port_info_t *pinfo;\n\n    pinfo = l2p_get_port_pinfo(pid);\n    s     = &pinfo->stats.curr;\n\n    display_topline(\"<Port Queue Stats>\", 0, 0, 0);\n\n    pktgen_display_set_color(\"stats.port.status\");\n    scrn_puts(\" Port %2u\", pid);\n\n    row = 3;\n    col = 1;\n\n    pktgen_display_set_color(\"stats.stat.label\");\n    scrn_printf(row + 0, col, \"%-*s\", COLUMN_WIDTH_0, \"PCI Address     :\");\n    scrn_printf(row + 1, col, \"%-*s\", COLUMN_WIDTH_0, \"Pkts Rx/Tx      :\");\n    scrn_printf(row + 2, col, \"%-*s\", COLUMN_WIDTH_0, \"Rx Errors/Missed:\");\n    scrn_printf(row + 3, col, \"%-*s\", COLUMN_WIDTH_0, \"Rate Rx/Tx      :\");\n    scrn_printf(row + 4, col, \"%-*s\", COLUMN_WIDTH_0, \"MAC Address     :\");\n    scrn_printf(row + 5, col, \"%-*s\", COLUMN_WIDTH_0, \"Link Status     :\");\n\n    col   = COLUMN_WIDTH_0;\n    width = COLUMN_WIDTH_1 + 8;\n\n    pktgen_display_set_color(\"stats.stat.values\");\n    rte_eth_dev_get_name_by_port(pid, dev_name);\n    snprintf(buff, sizeof(buff), \"%s\", dev_name);\n    scrn_printf(row + 0, col, \"%*s\", width, buff);\n\n    snprintf(buff, sizeof(buff), \"%'lu/%'lu\", s->ipackets, s->opackets);\n    scrn_printf(row + 1, col, \"%*s\", width, buff);\n\n    snprintf(buff, sizeof(buff), \"%'lu/%'lu\", s->ierrors, s->imissed);\n    scrn_printf(row + 2, col, \"%*s\", width, buff);\n\n    r = &pinfo->stats.rate;\n    snprintf(buff, sizeof(buff), \"%'lu/%'lu\", r->ipackets, r->opackets);\n    scrn_printf(row + 3, col, \"%*s\", width, buff);\n\n    rte_eth_macaddr_get(pid, &ethaddr);\n    rte_ether_format_addr(mac_buf, sizeof(mac_buf), &ethaddr);\n    snprintf(buff, sizeof(buff), \"%s\", mac_buf);\n    scrn_printf(row + 4, col, \"%*s\", width, buff);\n\n    pktgen_link_state(pid, buff, sizeof(buff));\n    pktgen_display_set_color(\"stats.port.status\");\n    scrn_printf(row + 5, col, \"%*s\", width, buff);\n\n    row += 6;\n    ipackets = opackets = errs = 0;\n    hdr                        = 0;\n    int nq                     = RTE_MAX(l2p_get_rxcnt(pid), l2p_get_txcnt(pid));\n    for (q = 0; q < (unsigned int)nq; q++) {\n        uint64_t rxpkts, txpkts, errors;\n        qstats_t *qs      = &pinfo->stats.snap_qstats[q];\n        qstats_t *prev_qs = &pinfo->stats.prev_qstats[q];\n\n        if (!hdr) {\n            hdr = 1;\n            row++;\n            pktgen_display_set_color(\"stats.port.status\");\n            scrn_printf(row++, 1, \"%-8s: %14s %14s %14s\", \"Rate/sec\", \"ipackets\", \"opackets\",\n                        \"errors\");\n            pktgen_display_set_color(\"stats.stat.values\");\n        }\n\n        rxpkts   = qs->q_ipackets - prev_qs->q_ipackets;\n        txpkts   = qs->q_opackets - prev_qs->q_opackets;\n        errors   = qs->q_errors - prev_qs->q_errors;\n        *prev_qs = *qs;\n\n        scrn_printf(row++, 1, \"  Q %2d  : %'14lu %'14lu %'14lu\", q, rxpkts, txpkts, errors);\n        ipackets += rxpkts;\n        opackets += txpkts;\n        errs += errors;\n    }\n    scrn_printf(row++, 1, \" %-7s: %'14lu %'14lu %'14lu\", \"Totals\", ipackets, opackets, errs);\n    pktgen_display_set_color(NULL);\n    display_dashline(row + 2);\n    scrn_eol();\n}\n\nstatic void\n_xstats_display(uint16_t port_id)\n{\n    port_info_t *pinfo;\n    xstats_t *xs;\n\n    if (!rte_eth_dev_is_valid_port(port_id)) {\n        printf(\"Error: Invalid port number %i\\n\", port_id);\n        return;\n    }\n    pinfo = l2p_get_port_pinfo(port_id);\n    if (pinfo == NULL)\n        return;\n\n    xs = &pinfo->stats.xstats;\n\n    /* Display xstats */\n    int idx = 0;\n    for (int idx_xstat = 0; idx_xstat < xs->cnt; idx_xstat++) {\n        uint64_t value;\n\n        value = xs->xstats[idx_xstat].value - xs->prev[idx_xstat].value;\n        if (xs->xstats[idx_xstat].value) {\n            if (idx == 0) {\n                printf(\"     \");\n                scrn_eol();\n            } else if ((idx & 1) == 0) {\n                printf(\"\\n     \");\n                scrn_eol();\n            }\n            idx++;\n\n            pktgen_display_set_color(\"stats.port.status\");\n            printf(\"%-32s\", xs->names[idx_xstat].name);\n            pktgen_display_set_color(\"stats.port.label\");\n            printf(\"| \");\n            pktgen_display_set_color(\"stats.port.rate\");\n            printf(\"%'15\" PRIu64, value);\n            pktgen_display_set_color(\"stats.port.label\");\n            printf(\" | \");\n            scrn_eol();\n        }\n    }\n    rte_memcpy(xs->prev, xs->xstats, sizeof(struct rte_eth_xstat) * xs->cnt);\n    printf(\"\\n\");\n    scrn_eol();\n    printf(\"\\n\");\n}\n\nvoid\npktgen_page_xstats(uint16_t pid)\n{\n    display_topline(\"<Port XStats Page>\", 0, 0, 0);\n\n    pktgen_display_set_color(\"stats.port.status\");\n    scrn_printf(3, 1, \"     %-32s| %15s | %-32s| %15s |\\n\", \"XStat Name\", \"Per/Second\",\n                \"XStat Name\", \"Per/Second\");\n    pktgen_display_set_color(\"top.page\");\n    printf(\"Port %3d \", pid);\n    pktgen_display_set_color(\"stats.port.status\");\n    for (int k = 0; k < 100; k++)\n        printf(\"=\");\n    printf(\"\\n\");\n    pktgen_display_set_color(\"stats.stat.label\");\n\n    _xstats_display(pid);\n\n    pktgen_display_set_color(NULL);\n    scrn_eol();\n}\n"
  },
  {
    "path": "app/pktgen-stats.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_STATS_H_\n#define _PKTGEN_STATS_H_\n\n/**\n * @file\n *\n * Per-port statistics structures and display functions for Pktgen.\n *\n * Defines queue-level, size-histogram, extended, and aggregate port\n * statistics structs, and declares the functions that collect and display them.\n */\n\n#include <rte_timer.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MAX_QUEUES_PER_PORT 32 /**< Maximum number of RX/TX queues per port */\n\n/** Per-queue packet counters. */\ntypedef struct qstats_s {\n    uint64_t q_ipackets; /**< Number of input packets */\n    uint64_t q_opackets; /**< Number of output packets */\n    uint64_t q_errors;   /**< Number of error packets */\n} qstats_t;\n\n/** Packet size histogram counters. */\ntypedef struct size_stats_s {\n    uint64_t _64;        /**< Number of 64 byte packets */\n    uint64_t _65_127;    /**< Number of 65-127 byte packets */\n    uint64_t _128_255;   /**< Number of 128-255 byte packets */\n    uint64_t _256_511;   /**< Number of 256-511 byte packets */\n    uint64_t _512_1023;  /**< Number of 512-1023 byte packets */\n    uint64_t _1024_1522; /**< Number of 1024-1522 byte packets */\n    uint64_t broadcast;  /**< Number of broadcast packets */\n    uint64_t multicast;  /**< Number of multicast packets */\n    uint64_t jumbo;      /**< Number of Jumbo frames */\n    uint64_t runt;       /**< Number of Runt frames */\n    uint64_t unknown;    /**< Number of unknown sizes */\n} size_stats_t;\n\n/** Extended NIC statistics snapshot. */\ntypedef struct xstats_s {\n    struct rte_eth_xstat_name *names; /**< Array of extended stat name strings */\n    struct rte_eth_xstat *xstats;     /**< Current extended stat values */\n    struct rte_eth_xstat *prev;       /**< Previous extended stat values (for rate calc) */\n    int cnt;                          /**< Number of extended stats entries */\n} xstats_t;\n\n/** Aggregate per-port statistics (current, previous, rate, and base). */\ntypedef struct port_stats_s {\n    struct rte_eth_stats curr; /**< current port statistics */\n    struct rte_eth_stats prev; /**< previous port statistics */\n    struct rte_eth_stats rate; /**< current packet rate statistics */\n    struct rte_eth_stats base; /**< base port statistics for normalization */\n    xstats_t xstats;           /**< Extended statistics */\n\n    size_stats_t sizes; /**< Packet size counters */\n\n    qstats_t qstats[MAX_QUEUES_PER_PORT];      /**< Hot-path: written only by worker lcores */\n    qstats_t snap_qstats[MAX_QUEUES_PER_PORT]; /**< Snapshot: written only by timer thread */\n    qstats_t prev_qstats[MAX_QUEUES_PER_PORT]; /**< Previous snapshot for rate calculation */\n} port_stats_t;\n\nstruct port_info_s;\n\n/** Query and update the Ethernet link status for a port. */\nvoid pktgen_get_link_status(struct port_info_s *info);\n\n/** Collect and compute per-port packet rates on a timer tick. */\nvoid pktgen_process_stats(void);\n\n/** Render the main statistics display page. */\nvoid pktgen_page_stats(void);\n\n/**\n * Render the per-queue statistics page for a port.\n *\n * @param pid  Port ID to display.\n */\nvoid pktgen_page_qstats(uint16_t pid);\n\n/**\n * Render the extended NIC statistics page for a port.\n *\n * @param pid  Port ID to display.\n */\nvoid pktgen_page_xstats(uint16_t pid);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_STATS_H_ */\n"
  },
  {
    "path": "app/pktgen-sys.c",
    "content": "/*-\n * Copyright(c) <2020-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#include <lua_config.h>\n\n#include \"pktgen-display.h\"\n#include \"pktgen-cpu.h\"\n#include \"pktgen-sys.h\"\n\n#include \"coreinfo.h\"\n\n/**\n *\n * pktgen_page_system - Show the system page for pktgen.\n *\n * DESCRIPTION\n * Display the pktgen system page. (Not used)\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_page_system(void)\n{\n    uint32_t i, row, col, cnt, nb_sockets, nb_cores, nb_threads;\n    static int counter = 0;\n    char buff[2048];\n\n    pktgen_display_set_color(\"top.page\");\n    display_topline(\"<System Page>\", 0, 0, 0);\n\n    cnt = coreinfo_lcore_cnt();\n    if ((cnt == 0) || (pktgen.lscpu == NULL))\n        pktgen_cpu_init();\n\n    nb_sockets = coreinfo_socket_cnt();\n    nb_cores   = coreinfo_core_cnt();\n    nb_threads = coreinfo_thread_cnt();\n\n    if ((counter++ & 3) != 0)\n        return;\n\n    row = 3;\n    col = 1;\n    pktgen_display_set_color(\"stats.total.data\");\n    scrn_printf(row++, 1, \"Number of sockets %d, cores/socket %d, threads/core %d, total %d\",\n                nb_sockets, nb_cores, nb_threads, cnt);\n\n    pktgen_display_set_color(\"stats.dyn.label\");\n    sprintf(buff, \"Socket   : \");\n    for (i = 0; i < nb_sockets; i++)\n        strncatf(buff, \"%4d      \", i);\n    scrn_printf(row, col + 2, \"%s\", buff);\n    scrn_printf(0, 0, \"Port description\");\n\n    pktgen_display_set_color(\"stats.stat.label\");\n    row++;\n    buff[0] = '\\0';\n    for (i = 0; i < nb_cores; i++) {\n        strncatf(buff, \"  Core %3d : [%2d,%2d]   \", i, sct(0, i, 0), sct(0, i, 1));\n        if (nb_sockets > 1)\n            strncatf(buff, \"[%2d,%2d]   \", sct(1, i, 0), sct(1, i, 1));\n        if (nb_sockets > 2)\n            strncatf(buff, \"[%2d,%2d]   \", sct(2, i, 0), sct(2, i, 1));\n        if (nb_sockets > 3)\n            strncatf(buff, \"[%2d,%2d]   \", sct(3, i, 0), sct(3, i, 1));\n        strncatf(buff, \"\\n\");\n    }\n    scrn_printf(row, 1, \"%s\", buff);\n\n    pktgen_display_set_color(\"stats.bdf\");\n    col = 13 + (nb_sockets * 10) + 1;\n    for (i = 0; i < pktgen.portdesc_cnt; i++)\n        scrn_printf(row + i, col, \"%s\", pktgen.portdesc[i]);\n\n    row += RTE_MAX(nb_cores, pktgen.portdesc_cnt);\n    scrn_pos(row, 1);\n    pktgen_display_set_color(\"stats.stat.values\");\n\n    row += pktgen.nb_ports + 4;\n    if (pktgen.flags & PRINT_LABELS_FLAG) {\n        pktgen.last_row = row + pktgen.nb_ports;\n        display_dashline(pktgen.last_row);\n\n        scrn_setw(pktgen.last_row);\n        scrn_printf(100, 1, \"\"); /* Put cursor on the last row. */\n    }\n    pktgen_display_set_color(NULL);\n    pktgen.flags &= ~PRINT_LABELS_FLAG;\n}\n"
  },
  {
    "path": "app/pktgen-sys.h",
    "content": "/*-\n * Copyright(c) <2020-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_SYS_H_\n#define _PKTGEN_SYS_H_\n\n/**\n * @file\n *\n * System information display page for Pktgen.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Render the system information display page to the console.\n */\nvoid pktgen_page_system(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_SYS_H_ */\n"
  },
  {
    "path": "app/pktgen-tcp.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <cli_scrn.h>\n#include <lua_config.h>\n\n#include \"pktgen.h\"\n\n#include \"pktgen-tcp.h\"\n\n/**\n *\n * pktgen_tcp_hdr_ctor - TCP header constructor routine.\n *\n * DESCRIPTION\n * Construct a TCP header in the packet buffer provided.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid *\npktgen_tcp_hdr_ctor(pkt_seq_t *pkt, void *hdr, int type, bool cksum_offload,\n                    bool cksum_requires_phdr)\n{\n    uint16_t tlen;\n\n    if (type == RTE_ETHER_TYPE_IPV4) {\n        struct rte_ipv4_hdr *ipv4 = (struct rte_ipv4_hdr *)hdr;\n        struct rte_tcp_hdr *tcp   = (struct rte_tcp_hdr *)&ipv4[1];\n\n        /* Create the TCP header */\n        ipv4->src_addr = htonl(pkt->ip_src_addr.addr.ipv4.s_addr);\n        ipv4->dst_addr = htonl(pkt->ip_dst_addr.addr.ipv4.s_addr);\n\n        ipv4->version_ihl   = (IPv4_VERSION << 4) | (sizeof(struct rte_ipv4_hdr) / 4);\n        tlen                = pkt->pkt_size - pkt->ether_hdr_size;\n        ipv4->total_length  = htons(tlen);\n        ipv4->next_proto_id = pkt->ipProto;\n\n        tcp->src_port = htons(pkt->sport);\n        tcp->dst_port = htons(pkt->dport);\n        tcp->sent_seq = htonl(pkt->tcp_seq);\n        tcp->recv_ack = htonl(pkt->tcp_ack);\n        tcp->data_off =\n            ((sizeof(struct rte_tcp_hdr) / sizeof(uint32_t)) << 4); /* Offset in words */\n        tcp->tcp_flags = pkt->tcp_flags;\n        tcp->rx_win    = htons(DEFAULT_WND_SIZE);\n        tcp->tcp_urp   = 0;\n\n        tcp->cksum = 0;\n        if (!cksum_offload)\n            tcp->cksum = rte_ipv4_udptcp_cksum(ipv4, (const void *)tcp);\n        else if (cksum_offload && cksum_requires_phdr)\n            tcp->cksum = rte_ipv4_phdr_cksum(ipv4, 0);\n\n    } else {\n        struct rte_ipv6_hdr *ipv6 = (struct rte_ipv6_hdr *)hdr;\n        struct rte_tcp_hdr *tcp   = (struct rte_tcp_hdr *)&ipv6[1];\n\n        /* Create the pseudo header and TCP information */\n        memset(&ipv6->dst_addr, 0, sizeof(struct rte_ipv6_addr));\n        memset(&ipv6->src_addr, 0, sizeof(struct rte_ipv6_addr));\n        rte_memcpy(&ipv6->dst_addr, &pkt->ip_dst_addr.addr.ipv6, sizeof(struct rte_ipv6_addr));\n        rte_memcpy(&ipv6->src_addr, &pkt->ip_src_addr.addr.ipv6, sizeof(struct rte_ipv6_addr));\n\n        tlen              = pkt->pkt_size - (pkt->ether_hdr_size + sizeof(struct rte_ipv6_hdr));\n        ipv6->payload_len = htons(tlen);\n        ipv6->proto       = pkt->ipProto;\n\n        tcp->src_port = htons(pkt->sport);\n        tcp->dst_port = htons(pkt->dport);\n        tcp->sent_seq = htonl(pkt->tcp_seq);\n        tcp->recv_ack = htonl(pkt->tcp_ack);\n        tcp->data_off =\n            ((sizeof(struct rte_tcp_hdr) / sizeof(uint32_t)) << 4); /* Offset in words */\n        tcp->tcp_flags = pkt->tcp_flags;\n        tcp->rx_win    = htons(DEFAULT_WND_SIZE);\n        tcp->tcp_urp   = 0;\n\n        tcp->cksum = 0;\n        if (!cksum_offload)\n            tcp->cksum = rte_ipv6_udptcp_cksum(ipv6, (const void *)tcp);\n    }\n\n    /* In this case we return the original value to allow IP ctor to work */\n    return hdr;\n}\n"
  },
  {
    "path": "app/pktgen-tcp.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_TCP_H_\n#define _PKTGEN_TCP_H_\n\n/**\n * @file\n *\n * TCP header construction for Pktgen transmit packets.\n */\n\n#include <pg_inet.h>\n\n#include \"pktgen-seq.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Construct a TCP header in the packet buffer.\n *\n * @param pkt\n *   Packet sequence entry providing source/destination ports and TCP flags.\n * @param hdr\n *   Pointer to the start of the TCP header region in the packet buffer.\n * @param type\n *   EtherType / address family identifier (e.g. RTE_ETHER_TYPE_IPV4).\n * @param cksum_offload\n *   When true, set the checksum to 0 and rely on hardware offload;\n *   when false, compute the checksum in software.\n * @param cksum_requires_phdr\n *   When true, include the IPv4/IPv6 pseudo-header in the checksum seed.\n * @return\n *   Pointer to the byte immediately following the completed TCP header.\n */\nvoid *pktgen_tcp_hdr_ctor(pkt_seq_t *pkt, void *hdr, int type, bool cksum_offload,\n                          bool cksum_requires_phdr);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_TCP_H_ */\n"
  },
  {
    "path": "app/pktgen-txbuff.h",
    "content": "/*-\n * Copyright(c) <2023-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) 2010-2017 Intel Corporation\n *\n * Taken from: dpdk.org (https://dpdk.org/) and modified\n */\n\n#ifndef _PKTGEN_TXBUFF_H_\n#define _PKTGEN_TXBUFF_H_\n\n/**\n * @file\n *\n * Software TX coalescing buffer for Pktgen.\n *\n * Provides a small per-port/queue packet buffer that collects mbufs and\n * flushes them to the NIC in a single rte_eth_tx_burst() call, reducing\n * PCIe overhead for bursty traffic patterns.\n */\n\n#include <rte_mbuf.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Callback invoked when packets remain unsent after a flush attempt. */\ntypedef void (*tx_buffer_error_fn)(struct rte_mbuf **unsent, uint16_t count, void *userdata);\n\n/**\n * Packet coalescing buffer used by tx_buffer() and tx_buffer_flush().\n */\nstruct eth_tx_buffer {\n    uint16_t size;           /**< Maximum number of packets the buffer can hold */\n    uint16_t length;         /**< Current number of packets held in the buffer */\n    uint16_t pid;            /**< Port ID to transmit on */\n    uint16_t qid;            /**< Queue ID to transmit on */\n    struct rte_mbuf *pkts[]; /**< Pending packets to be sent on flush or when full */\n};\n\n/**\n * Calculate the size of the Tx buffer.\n *\n * @param sz\n *   Number of stored packets.\n */\n#define TX_BUFFER_SIZE(sz) (sizeof(struct eth_tx_buffer) + (sz) * sizeof(struct rte_mbuf *))\n\n/**\n * Send any packets queued up for transmission on a port and HW queue\n *\n * This causes an explicit flush of packets previously buffered via the\n * tx_buffer() function. It returns the number of packets successfully\n * sent to the NIC.\n *\n * @param buffer\n *   Buffer of packets to be transmit.\n * @return\n *   The number of packets successfully sent to the Ethernet device.\n */\nstatic inline uint16_t\ntx_buffer_flush(struct eth_tx_buffer *buffer)\n{\n    uint16_t sent, tot_sent = 0;\n    uint16_t to_send       = buffer->length;\n    struct rte_mbuf **pkts = buffer->pkts;\n\n    if (to_send == 0)\n        return 0;\n    buffer->length = 0;\n\n    do {\n        sent = rte_eth_tx_burst(buffer->pid, buffer->qid, pkts, to_send);\n        to_send -= sent;\n        pkts += sent;\n        tot_sent += sent;\n    } while (to_send > 0);\n\n    return tot_sent;\n}\n\n/**\n * Buffer a single packet for future transmission on a port and queue\n *\n * This function takes a single mbuf/packet and buffers it for later\n * transmission on the particular port and queue specified. Once the buffer is\n * full of packets, an attempt will be made to transmit all the buffered\n * packets. In case of error, where not all packets can be transmitted, a\n * callback is called with the unsent packets as a parameter. If no callback\n * is explicitly set up, the unsent packets are just freed back to the owning\n * mempool. The function returns the number of packets actually sent i.e.\n * 0 if no buffer flush occurred, otherwise the number of packets successfully\n * flushed\n *\n * @param buffer\n *   Buffer used to collect packets to be sent.\n * @param tx_pkt\n *   Pointer to the packet mbuf to be sent.\n * @return\n *   0 = packet has been buffered for later transmission\n *   N > 0 = packet has been buffered, and the buffer was subsequently flushed,\n *     causing N packets to be sent, and the error callback to be called for\n *     the rest.\n */\nstatic __rte_always_inline uint16_t\ntx_buffer(struct eth_tx_buffer *buffer, struct rte_mbuf *tx_pkt)\n{\n    buffer->pkts[buffer->length++] = tx_pkt;\n    if (buffer->length < buffer->size)\n        return 0;\n\n    return tx_buffer_flush(buffer);\n}\n\n/**\n * Buffer a vector of packets for future transmission on a port and queue\n *\n * This function takes a vector of mbufs/packets and buffers it for later\n * transmission on the particular port and queue specified. Once the buffer is\n * full of packets, an attempt will be made to transmit all the buffered\n * packets. In case of error, where not all packets can be transmitted, a\n * callback is called with the unsent packets as a parameter. If no callback\n * is explicitly set up, the unsent packets are just freed back to the owning\n * mempool. The function returns the number of packets actually sent i.e.\n * 0 if no buffer flush occurred, otherwise the number of packets successfully\n * flushed\n *\n * @param buffer\n *   Buffer used to collect packets to be sent.\n * @param tx_pkt\n *   Pointer to the vector of mbufs to be sent.\n * @param nb_pkts\n *   Number of packets to be sent in the vector.\n * @return\n *   0 = packet has been buffered for later transmission\n *   N > 0 = packet has been buffered, and the buffer was subsequently flushed,\n *     causing N packets to be sent, and the error callback to be called for\n *     the rest.\n */\nstatic __rte_always_inline uint16_t\ntx_buffer_bulk(struct eth_tx_buffer *buffer, struct rte_mbuf **tx_pkt, uint16_t nb_pkts)\n{\n    for (uint16_t i = 0; i < nb_pkts; i++) {\n        tx_buffer(buffer, *tx_pkt);\n        tx_pkt++;\n    }\n\n    return nb_pkts;\n}\n\n/**\n * Initialize default values for buffered transmitting\n *\n * @param buffer\n *   Tx buffer to be initialized.\n * @param size\n *   Buffer size\n * @param pid\n *   Port ID\n * @param qid\n *   Queue ID\n * @return\n *   0 if no error\n */\nstatic __inline__ int\ntx_buffer_init(struct eth_tx_buffer *buffer, uint16_t size, uint16_t pid, uint16_t qid)\n{\n    int ret = 0;\n\n    if (buffer == NULL) {\n        printf(\"Cannot initialize NULL buffer\\n\");\n        return -EINVAL;\n    }\n\n    buffer->size = size;\n    buffer->pid  = pid;\n    buffer->qid  = qid;\n\n    return ret;\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_TXBUFF_H_ */\n"
  },
  {
    "path": "app/pktgen-udp.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <cli_scrn.h>\n#include \"lua_config.h\"\n\n#include \"pktgen.h\"\n\n#include \"pktgen-udp.h\"\n\n/**\n *\n * pktgen_udp_hdr_ctor - UDP header constructor routine.\n *\n * DESCRIPTION\n * Construct the UDP header in a packer buffer.\n *\n * RETURNS: next header location\n *\n * SEE ALSO:\n */\n\nvoid *\npktgen_udp_hdr_ctor(pkt_seq_t *pkt, void *hdr, int type, bool cksum_offload,\n                    bool cksum_requires_phdr)\n{\n    uint16_t tlen;\n\n    if (type == RTE_ETHER_TYPE_IPV4) {\n        struct rte_ipv4_hdr *ipv4 = hdr;\n        struct rte_udp_hdr *udp   = (struct rte_udp_hdr *)&ipv4[1];\n\n        /* Create the UDP header */\n        ipv4->src_addr = htonl(pkt->ip_src_addr.addr.ipv4.s_addr);\n        ipv4->dst_addr = htonl(pkt->ip_dst_addr.addr.ipv4.s_addr);\n\n        ipv4->version_ihl   = (IPv4_VERSION << 4) | (sizeof(struct rte_ipv4_hdr) / 4);\n        tlen                = pkt->pkt_size - pkt->ether_hdr_size;\n        ipv4->total_length  = htons(tlen);\n        ipv4->next_proto_id = pkt->ipProto;\n\n        tlen           = pkt->pkt_size - (pkt->ether_hdr_size + sizeof(struct rte_ipv4_hdr));\n        udp->dgram_len = htons(tlen);\n        udp->src_port  = htons(pkt->sport);\n        udp->dst_port  = htons(pkt->dport);\n\n        if (pkt->dport == VXLAN_PORT_ID) {\n            struct vxlan *vxlan = (struct vxlan *)&udp[1];\n\n            vxlan->vni_flags = htons(pkt->vni_flags);\n            vxlan->group_id  = htons(pkt->group_id);\n            vxlan->vxlan_id  = htonl(pkt->vxlan_id) << 8;\n        }\n\n        udp->dgram_cksum = 0;\n        if (!cksum_offload) {\n            udp->dgram_cksum = rte_ipv4_udptcp_cksum(ipv4, (const void *)udp);\n            if (udp->dgram_cksum == 0)\n                udp->dgram_cksum = 0xFFFF;\n        } else if (cksum_offload && cksum_requires_phdr)\n            udp->dgram_cksum = rte_ipv4_phdr_cksum(ipv4, 0);\n\n    } else {\n        struct rte_ipv6_hdr *ipv6 = hdr;\n        struct rte_udp_hdr *udp   = (struct rte_udp_hdr *)&ipv6[1];\n\n        /* Create the pseudo header and TCP information */\n        memset(&ipv6->dst_addr, 0, sizeof(struct rte_ipv6_addr));\n        memset(&ipv6->src_addr, 0, sizeof(struct rte_ipv6_addr));\n        rte_memcpy(&ipv6->dst_addr, &pkt->ip_dst_addr.addr.ipv6, sizeof(struct rte_ipv6_addr));\n        rte_memcpy(&ipv6->src_addr, &pkt->ip_src_addr.addr.ipv6, sizeof(struct rte_ipv6_addr));\n\n        tlen              = pkt->pkt_size - (pkt->ether_hdr_size + sizeof(struct rte_ipv6_hdr));\n        ipv6->payload_len = htons(tlen);\n        ipv6->proto       = pkt->ipProto;\n\n        udp->dgram_len = htons(tlen);\n        udp->src_port  = htons(pkt->sport);\n        udp->dst_port  = htons(pkt->dport);\n\n        udp->dgram_cksum = 0;\n        if (!cksum_offload) {\n            udp->dgram_cksum = rte_ipv6_udptcp_cksum(ipv6, (const void *)udp);\n            if (udp->dgram_cksum == 0)\n                udp->dgram_cksum = 0xFFFF;\n        }\n    }\n\n    /* Return the original pointer for IP ctor */\n    return hdr;\n}\n"
  },
  {
    "path": "app/pktgen-udp.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_UDP_H_\n#define _PKTGEN_UDP_H_\n\n/**\n * @file\n *\n * UDP header construction for Pktgen transmit packets.\n */\n\n#include <pg_inet.h>\n\n#include \"pktgen-seq.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define VXLAN_PORT_ID 4789 /**< Well-known UDP port number for VxLAN encapsulation */\n\n/**\n * Construct a UDP header in the packet buffer.\n *\n * @param pkt\n *   Packet sequence entry providing source/destination ports and payload length.\n * @param hdr\n *   Pointer to the start of the UDP header region in the packet buffer.\n * @param type\n *   EtherType / address family identifier (e.g. RTE_ETHER_TYPE_IPV4).\n * @param cksum_offload\n *   When true, set the checksum to 0 and rely on hardware offload;\n *   when false, compute the checksum in software.\n * @param cksum_requires_phdr\n *   When true, include the IPv4/IPv6 pseudo-header in the checksum seed.\n * @return\n *   Pointer to the byte immediately following the completed UDP header.\n */\nvoid *pktgen_udp_hdr_ctor(pkt_seq_t *pkt, void *hdr, int type, bool cksum_offload,\n                          bool cksum_requires_phdr);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_UDP_H_ */\n"
  },
  {
    "path": "app/pktgen-version.h",
    "content": "/*-\n * Copyright(c) <2020-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2020 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_VERSION_H_\n#define _PKTGEN_VERSION_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define PKTGEN_VER_PREFIX     \"Pktgen \"\n#define PKTGEN_VER_CREATED_BY \"Keith Wiles\"\n\n#define PKTGEN_VERSION PKTGEN_VER_PREFIX __PROJECT_VERSION\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* PKTGEN_VERSION_H_ */\n"
  },
  {
    "path": "app/pktgen-vlan.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <cli_scrn.h>\n#include \"lua_config.h\"\n\n#include \"pktgen.h\"\n#include \"pktgen-arp.h\"\n#include \"pktgen-ipv4.h\"\n#include \"pktgen-ipv6.h\"\n#include \"pktgen-vlan.h\"\n\n/**\n *\n * pktgen_process_vlan - Process a VLAN packet\n *\n * DESCRIPTION\n * Process a input VLAN packet.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_process_vlan(struct rte_mbuf *m, uint32_t pid, uint32_t qid)\n{\n    pktType_e pType;\n    struct rte_ether_hdr *eth;\n    struct rte_vlan_hdr *rte_vlan_hdr;\n\n    eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);\n\n    /* Now dealing with the inner header */\n    rte_vlan_hdr = (struct rte_vlan_hdr *)(eth + 1);\n    pType        = ntohs(rte_vlan_hdr->eth_proto);\n\n    /* No support for nested tunnel */\n    switch ((int)pType) {\n    case RTE_ETHER_TYPE_ARP:\n        pktgen_process_arp(m, pid, qid, 1);\n        break;\n    case RTE_ETHER_TYPE_IPV4:\n        pktgen_process_ping4(m, pid, qid, 1);\n        break;\n    case RTE_ETHER_TYPE_IPV6:\n        pktgen_process_ping6(m, pid, qid, 1);\n        break;\n    case UNKNOWN_PACKET: /* FALL THRU */\n    default:\n        break;\n    }\n}\n"
  },
  {
    "path": "app/pktgen-vlan.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_VLAN_H_\n#define _PKTGEN_VLAN_H_\n\n/**\n * @file\n *\n * VLAN packet processing routines for Pktgen.\n */\n\n#include <stdint.h>\n\n#include <rte_mbuf.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Process a received VLAN-tagged packet and update port statistics.\n *\n * @param m\n *   Pointer to the received mbuf.\n * @param pid\n *   Port index on which the packet was received.\n * @param qid\n *   Queue index on which the packet was received.\n */\nvoid pktgen_process_vlan(struct rte_mbuf *m, uint32_t pid, uint32_t qid);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_VLAN_H_ */\n"
  },
  {
    "path": "app/pktgen.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <stdint.h>\n#include <inttypes.h>\n#include <math.h>\n\n#include <pg_delay.h>\n#include <rte_lcore.h>\n#include <lua_config.h>\n#include <rte_net.h>\n#include <rte_arp.h>\n#include <rte_cycles.h>\n#include <rte_hexdump.h>\n\n#include \"pktgen.h\"\n#include \"pktgen-tcp.h\"\n#include \"pktgen-ipv4.h\"\n#include \"pktgen-ipv6.h\"\n#include \"pktgen-udp.h\"\n#include \"pktgen-gre.h\"\n#include \"pktgen-arp.h\"\n#include \"pktgen-vlan.h\"\n#include \"pktgen-cpu.h\"\n#include \"pktgen-display.h\"\n#include \"pktgen-random.h\"\n#include \"pktgen-log.h\"\n#include \"pktgen-gtpu.h\"\n#include \"pktgen-sys.h\"\n\n#include <pthread.h>\n#include <sched.h>\n\n/* Allocated the pktgen structure for global use */\npktgen_t pktgen;\n\nstatic inline double\nnext_poisson_time(double rateParameter)\n{\n    return -logf(1.0f - ((double)random()) / (double)(RAND_MAX)) / rateParameter;\n}\n\n/**\n *\n * wire_size - Calculate the wire size of the data in bits to be sent.\n *\n * DESCRIPTION\n * Calculate the number of bytes/bits in a burst of traffic.\n *\n * RETURNS: Number of bytes in a burst of packets.\n *\n * SEE ALSO:\n */\nstatic uint64_t\npktgen_wire_size(port_info_t *pinfo)\n{\n    uint64_t i, size = 0;\n\n    if (pktgen_tst_port_flags(pinfo, SEND_PCAP_PKTS)) {\n        pcap_info_t *pcap = l2p_get_pcap(pinfo->pid);\n\n        size = WIRE_SIZE(pcap->avg_pkt_size, uint64_t);\n    } else {\n        if (unlikely(pinfo->seqCnt > 0)) {\n            for (i = 0; i < pinfo->seqCnt; i++)\n                size += WIRE_SIZE(pinfo->seq_pkt[i].pkt_size, uint64_t);\n            size = size / pinfo->seqCnt; /* Calculate the average sized packet */\n        } else\n            size = WIRE_SIZE(pinfo->seq_pkt[SINGLE_PKT].pkt_size, uint64_t);\n    }\n    return (size * 8);\n}\n\n/**\n *\n * pktgen_packet_rate - Calculate the transmit rate.\n *\n * DESCRIPTION\n * Calculate the number of cycles to wait between sending bursts of traffic.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_packet_rate(port_info_t *port)\n{\n    uint64_t link_speed, pps, cpb, txcnt;\n\n    if (!port)\n        return;\n\n    // link speed in Megabits per second and tx_rate in percentage\n    if (port->link.link_speed == 0 || port->tx_rate == 0) {\n        port->tx_cycles = 0;\n        port->tx_pps    = 0;\n        return;\n    }\n\n    // total link speed in bits per second\n    link_speed = (uint64_t)port->link.link_speed * Million;\n\n    txcnt = l2p_get_txcnt(port->pid);\n\n    // packets per second per thread based on requested (tx_rate/txcnt)\n    pps = (((link_speed / pktgen_wire_size(port)) * (port->tx_rate / txcnt)) / 100);\n    pps = ((pps > 0) ? pps : 1);\n\n    // Do all multiplications first to reduce rounding errors.\n    // Add pps/2 to do rounding instead of truncation.\n    cpb             = (pps / 2 + (uint64_t)txcnt * port->tx_burst * rte_get_timer_hz()) / pps;\n    port->tx_cycles = cpb;\n    port->tx_pps    = pps;\n}\n\n/**\n *\n * pktgen_fill_pattern - Create the fill pattern in a packet buffer.\n *\n * DESCRIPTION\n * Create a fill pattern based on the arguments for the packet data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic inline void\npktgen_fill_pattern(uint8_t *p, uint16_t len, uint32_t type, char *user)\n{\n    uint32_t i;\n\n    switch (type) {\n    case USER_FILL_PATTERN:\n        memset(p, 0, len);\n        for (i = 0; i < len; i++)\n            p[i] = user[i & (USER_PATTERN_SIZE - 1)];\n        break;\n\n    case NO_FILL_PATTERN:\n        break;\n\n    case ZERO_FILL_PATTERN:\n        memset(p, 0, len);\n        break;\n\n    default:\n    case ABC_FILL_PATTERN: /* Byte wide ASCII pattern */\n        for (i = 0; i < len; i++)\n            p[i] = \"abcdefghijklmnopqrstuvwxyz012345\"[i & 0x1f];\n        break;\n    }\n}\n\n/**\n *\n * pktgen_find_matching_ipsrc - Find the matching IP source address\n *\n * DESCRIPTION\n * locate and return the pkt_seq_t pointer to the match IP address.\n *\n * RETURNS: index of sequence packets or -1 if no match found.\n *\n * SEE ALSO:\n */\nint\npktgen_find_matching_ipsrc(port_info_t *pinfo, uint32_t addr)\n{\n    int i, ret = -1;\n    uint32_t mask;\n\n    addr = ntohl(addr);\n\n    /* Search the sequence packets for a match */\n    for (i = 0; i < pinfo->seqCnt; i++)\n        if (addr == pinfo->seq_pkt[i].ip_src_addr.addr.ipv4.s_addr) {\n            ret = i;\n            break;\n        }\n\n    mask = size_to_mask(pinfo->seq_pkt[SINGLE_PKT].ip_src_addr.prefixlen);\n\n    /* Now try to match the single packet address */\n    if (ret == -1 ||\n        (addr & mask) == (pinfo->seq_pkt[SINGLE_PKT].ip_dst_addr.addr.ipv4.s_addr * mask))\n        ret = SINGLE_PKT;\n\n    return ret;\n}\n\n/**\n *\n * pktgen_find_matching_ipdst - Find the matching IP destination address\n *\n * DESCRIPTION\n * locate and return the pkt_seq_t pointer to the match IP address.\n *\n * RETURNS: index of sequence packets or -1 if no match found.\n *\n * SEE ALSO:\n */\nint\npktgen_find_matching_ipdst(port_info_t *pinfo, uint32_t addr)\n{\n    int i, ret = -1;\n\n    addr = ntohl(addr);\n\n    /* Search the sequence packets for a match */\n    for (i = 0; i < pinfo->seqCnt; i++)\n        if (addr == pinfo->seq_pkt[i].ip_dst_addr.addr.ipv4.s_addr) {\n            ret = i;\n            break;\n        }\n\n    /* Now try to match the single packet address */\n    if (ret == -1 && addr == pinfo->seq_pkt[SINGLE_PKT].ip_dst_addr.addr.ipv4.s_addr)\n        ret = SINGLE_PKT;\n\n    /* Now try to match the range packet address */\n    if (ret == -1 && addr == pinfo->seq_pkt[RANGE_PKT].ip_dst_addr.addr.ipv4.s_addr)\n        ret = RANGE_PKT;\n\n    return ret;\n}\n\nstatic inline tstamp_t *\npktgen_tstamp_pointer(port_info_t *pinfo __rte_unused, char *p)\n{\n    int offset = 0;\n\n    offset += sizeof(struct rte_ether_hdr);\n    offset += sizeof(struct rte_ipv4_hdr);\n    offset += sizeof(struct rte_udp_hdr);\n    offset = (offset + sizeof(uint64_t)) & ~(sizeof(uint64_t) - 1);\n\n    return (tstamp_t *)(p + offset);\n}\n\nstatic inline void\npktgen_tstamp_inject(port_info_t *pinfo, uint16_t qid)\n{\n    pkt_seq_t *pkt = &pinfo->seq_pkt[LATENCY_PKT];\n    rte_mbuf_t *mbuf;\n    l2p_port_t *port;\n    uint16_t sent;\n\n    uint64_t curr_ts = pktgen_get_time();\n    latency_t *lat   = &pinfo->latency;\n\n    if (curr_ts >= lat->latency_timo_cycles) {\n        lat->latency_timo_cycles = curr_ts + lat->latency_rate_cycles;\n\n        port = l2p_get_port(pinfo->pid);\n        if (rte_mempool_get(port->sp_mp[qid], (void **)&mbuf) == 0) {\n            uint16_t pktsize = pkt->pkt_size;\n            uint16_t to_send;\n\n            mbuf->pkt_len  = pktsize;\n            mbuf->data_len = pktsize;\n\n            /* IPv4 Header constructor */\n            pktgen_packet_ctor(pinfo, LATENCY_PKT, -2);\n\n            rte_memcpy(rte_pktmbuf_mtod(mbuf, uint8_t *), (uint8_t *)pkt->hdr, pktsize);\n\n            to_send = 1;\n            do {\n                sent = rte_eth_tx_burst(pinfo->pid, qid, &mbuf, to_send);\n                to_send -= sent;\n            } while (to_send > 0);\n\n            lat->num_latency_tx_pkts++;\n        } else\n            printf(\"*** No more latency buffers\\n\");\n    }\n}\n\nvoid\ntx_send_packets(port_info_t *pinfo, uint16_t qid, struct rte_mbuf **pkts, uint16_t nb_pkts)\n{\n    if (nb_pkts) {\n        uint16_t sent, to_send = nb_pkts;\n        qstats_t *qs = &pinfo->stats.qstats[qid];\n\n        qs->q_opackets += nb_pkts;\n        if (pktgen_tst_port_flags(pinfo, SEND_RANDOM_PKTS))\n            pktgen_rnd_bits_apply(pinfo, pkts, to_send, NULL);\n\n        do {\n            sent = rte_eth_tx_burst(pinfo->pid, qid, pkts, to_send);\n            to_send -= sent;\n            pkts += sent;\n        } while (to_send > 0);\n\n        if (qid == 0 && pktgen_tst_port_flags(pinfo, SEND_LATENCY_PKTS))\n            pktgen_tstamp_inject(pinfo, qid);\n    }\n}\n\n/* Inserts a new value into the ring of latencies */\nstatic inline void\nlatency_ring_insert(latency_ring_t *ring, uint64_t value)\n{\n    ring->data[ring->head] = value;\n    ring->head             = (ring->head + 1) % RING_SIZE;\n    if (ring->count < RING_SIZE)\n        ring->count++;\n}\n\nstatic inline void\npktgen_tstamp_check(port_info_t *pinfo, struct rte_mbuf **pkts, uint16_t nb_pkts)\n{\n    int lid = rte_lcore_id();\n    int qid = l2p_get_rxqid(lid);\n    uint64_t cycles, jitter;\n    latency_t *lat = &pinfo->latency;\n\n    for (int i = 0; i < nb_pkts; i++) {\n        tstamp_t *tstamp = pktgen_tstamp_pointer(pinfo, rte_pktmbuf_mtod(pkts[i], char *));\n\n        if (tstamp->magic != TSTAMP_MAGIC)\n            continue;\n\n        cycles        = (pktgen_get_time() - tstamp->timestamp);\n        tstamp->magic = 0UL; /* clear timestamp magic cookie */\n\n        if (tstamp->index != lat->expect_index) {\n            lat->expect_index = tstamp->index + 1;\n            lat->num_skipped++;\n            continue; /* Skip this latency packet */\n        }\n        lat->expect_index++;\n\n        lat->num_latency_pkts++;\n        lat->running_cycles += cycles;\n\n        latency_ring_insert(&lat->tail_latencies, cycles);\n\n        if (lat->min_cycles == 0 || cycles < lat->min_cycles)\n            lat->min_cycles = cycles;\n        if (lat->max_cycles == 0 || cycles > lat->max_cycles)\n            lat->max_cycles = cycles;\n\n        jitter = (cycles > lat->prev_cycles) ? cycles - lat->prev_cycles\n                                             : lat->prev_cycles - cycles;\n        if (jitter > lat->jitter_threshold_cycles)\n            lat->jitter_count++;\n\n        lat->prev_cycles = cycles;\n\n        if (pktgen_tst_port_flags(pinfo, SAMPLING_LATENCIES)) {\n            /* Record latency if it's time for sampling (seperately per queue) */\n            latsamp_stats_t *stats = &pinfo->latsamp_stats[qid];\n            uint64_t now           = pktgen_get_time();\n            uint64_t hz            = rte_get_tsc_hz();\n\n            if (stats->next == 0 || now >= stats->next) {\n                if (stats->idx < stats->num_samples) {\n                    stats->data[stats->idx] = (cycles * Billion) / hz;\n                    stats->idx++;\n                }\n\n                /* Calculate next sampling point */\n                if (pinfo->latsamp_type == LATSAMPLER_POISSON) {\n                    double next_possion_time_ns = next_poisson_time(pinfo->latsamp_rate);\n\n                    stats->next = (now + next_possion_time_ns) * (double)hz;\n                } else        // LATSAMPLER_SIMPLE or LATSAMPLER_UNSPEC\n                    stats->next = (now + hz) / pinfo->latsamp_rate;\n            }\n        }\n    }\n}\n\n/**\n *\n * pktgen_tx_flush - Flush Tx buffers from ring.\n *\n * DESCRIPTION\n * Flush TX buffers from ring.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic inline void\npktgen_tx_flush(port_info_t *pinfo, uint16_t qid)\n{\n    rte_eth_tx_done_cleanup(pinfo->pid, qid, 0);\n}\n\n/**\n *\n * pktgen_exit_cleanup - Clean up the data and other items\n *\n * DESCRIPTION\n * Clean up the data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic inline void\npktgen_exit_cleanup(uint8_t lid __rte_unused)\n{\n}\n\n/**\n *\n * pktgen_packet_ctor - Construct a complete packet with all headers and data.\n *\n * DESCRIPTION\n * Construct a packet type based on the arguments passed with all headers.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_packet_ctor(port_info_t *pinfo, int32_t seq_idx, int32_t type)\n{\n    pkt_seq_t *pkt            = &pinfo->seq_pkt[seq_idx];\n    uint16_t sport_entropy    = 0;\n    struct rte_ether_hdr *eth = (struct rte_ether_hdr *)&pkt->hdr->eth;\n    char *l3_hdr              = (char *)&eth[1]; /* Pointer to l3 hdr location for GRE header */\n    uint16_t pktsz            = (pktgen.flags & JUMBO_PKTS_FLAG) ? RTE_ETHER_MAX_JUMBO_FRAME_LEN\n                                                                 : RTE_ETHER_MAX_LEN;\n    struct rte_eth_dev_info dev_info = {0};\n    int ret;\n\n    ret = rte_eth_dev_info_get(pinfo->pid, &dev_info);\n    if (ret < 0)\n        printf(\"Error during getting device (port %u) info: %s\\n\", pinfo->pid, strerror(-ret));\n\n    /* Fill in the pattern for data space. */\n    pktgen_fill_pattern((uint8_t *)pkt->hdr, pktsz, pinfo->fill_pattern_type, pinfo->user_pattern);\n\n    if (seq_idx == LATENCY_PKT) {\n        latency_t *lat = &pinfo->latency;\n        tstamp_t *tstamp;\n\n        tstamp = pktgen_tstamp_pointer(pinfo, (char *)pkt->hdr);\n\n        tstamp->magic     = TSTAMP_MAGIC;\n        tstamp->timestamp = pktgen_get_time();\n        tstamp->index     = lat->next_index++;\n\n        if (lat->latency_entropy)\n            sport_entropy = (uint16_t)(pkt->sport + (tstamp->index % lat->latency_entropy));\n    }\n\n    /*\n     * Randomizes the source IP address and port. Only randomizes if in the \"single packet\" setting\n     * and not processing input packets.\n     * For details, see https://github.com/pktgen/Pktgen-DPDK/pull/342\n     */\n    if (pktgen_tst_port_flags(pinfo, SEND_SINGLE_PKTS) &&\n        !pktgen_tst_port_flags(pinfo, PROCESS_INPUT_PKTS)) {\n\n        if (pktgen_tst_port_flags(pinfo, RANDOMIZE_SRC_IP))\n            pkt->ip_src_addr.addr.ipv4.s_addr = pktgen_default_rnd_func();\n\n        if (pktgen_tst_port_flags(pinfo, RANDOMIZE_SRC_PT))\n            pkt->sport =\n                (pktgen_default_rnd_func() % 65535) + 1; /* Avoid port 0, the only invalid port */\n    }\n\n    /* Add GRE header and adjust rte_ether_hdr pointer if requested */\n    if (pktgen_tst_port_flags(pinfo, SEND_GRE_IPv4_HEADER))\n        l3_hdr = pktgen_gre_hdr_ctor(pinfo, pkt, (greIp_t *)l3_hdr);\n    else if (pktgen_tst_port_flags(pinfo, SEND_GRE_ETHER_HEADER))\n        l3_hdr = pktgen_gre_ether_hdr_ctor(pinfo, pkt, (greEther_t *)l3_hdr);\n    else\n        l3_hdr = pktgen_ether_hdr_ctor(pinfo, pkt);\n\n    uint64_t offload_capa = dev_info.tx_offload_capa;\n    if (likely(pkt->ethType == RTE_ETHER_TYPE_IPV4)) {\n        bool ipv4_cksum_offload = offload_capa & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM;\n        if (likely(pkt->ipProto == PG_IPPROTO_TCP)) {\n            bool tcp_cksum_offload = offload_capa & RTE_ETH_TX_OFFLOAD_TCP_CKSUM;\n            if (pkt->dport != PG_IPPROTO_L4_GTPU_PORT) {\n                /* Construct the TCP header */\n                pktgen_tcp_hdr_ctor(pkt, l3_hdr, RTE_ETHER_TYPE_IPV4, tcp_cksum_offload,\n                                    pinfo->cksum_requires_phdr);\n\n                /* IPv4 Header constructor */\n                pktgen_ipv4_ctor(pkt, l3_hdr, ipv4_cksum_offload);\n            } else {\n                /* Construct the GTP-U header */\n                pktgen_gtpu_hdr_ctor(pkt, l3_hdr, pkt->ipProto, GTPu_VERSION | GTPu_PT_FLAG, 0, 0,\n                                     0);\n\n                /* Construct the TCP header */\n                pktgen_tcp_hdr_ctor(pkt, l3_hdr, RTE_ETHER_TYPE_IPV4, tcp_cksum_offload,\n                                    pinfo->cksum_requires_phdr);\n                if (sport_entropy != 0) {\n                    struct rte_ipv4_hdr *ipv4 = (struct rte_ipv4_hdr *)l3_hdr;\n                    struct rte_tcp_hdr *tcp   = (struct rte_tcp_hdr *)&ipv4[1];\n\n                    tcp->src_port = htons(sport_entropy & 0xFFFF);\n                }\n\n                /* IPv4 Header constructor */\n                pktgen_ipv4_ctor(pkt, l3_hdr, ipv4_cksum_offload);\n            }\n        } else if (pkt->ipProto == PG_IPPROTO_UDP) {\n            bool udp_cksum_offload = offload_capa & RTE_ETH_TX_OFFLOAD_UDP_CKSUM;\n            if (pktgen_tst_port_flags(pinfo, SEND_VXLAN_PACKETS)) {\n                /* Construct the UDP header */\n                pkt->dport = VXLAN_PORT_ID;\n                pktgen_udp_hdr_ctor(pkt, l3_hdr, RTE_ETHER_TYPE_IPV4, udp_cksum_offload,\n                                    pinfo->cksum_requires_phdr);\n\n                /* IPv4 Header constructor */\n                pktgen_ipv4_ctor(pkt, l3_hdr, ipv4_cksum_offload);\n            } else if (pkt->dport != PG_IPPROTO_L4_GTPU_PORT) {\n                /* Construct the UDP header */\n                pktgen_udp_hdr_ctor(pkt, l3_hdr, RTE_ETHER_TYPE_IPV4, udp_cksum_offload,\n                                    pinfo->cksum_requires_phdr);\n\n                /* IPv4 Header constructor */\n                pktgen_ipv4_ctor(pkt, l3_hdr, ipv4_cksum_offload);\n            } else {\n                /* Construct the GTP-U header */\n                pktgen_gtpu_hdr_ctor(pkt, l3_hdr, pkt->ipProto, GTPu_VERSION | GTPu_PT_FLAG, 0, 0,\n                                     0);\n\n                /* Construct the UDP header */\n                pktgen_udp_hdr_ctor(pkt, l3_hdr, RTE_ETHER_TYPE_IPV4, udp_cksum_offload,\n                                    pinfo->cksum_requires_phdr);\n                if (sport_entropy != 0) {\n                    struct rte_ipv4_hdr *ipv4 = (struct rte_ipv4_hdr *)l3_hdr;\n                    struct rte_udp_hdr *udp   = (struct rte_udp_hdr *)&ipv4[1];\n\n                    udp->src_port = htons(sport_entropy & 0xFFFF);\n                }\n\n                /* IPv4 Header constructor */\n                pktgen_ipv4_ctor(pkt, l3_hdr, ipv4_cksum_offload);\n            }\n        } else if (pkt->ipProto == PG_IPPROTO_ICMP) {\n            struct rte_ipv4_hdr *ipv4;\n            struct rte_udp_hdr *udp;\n            struct rte_icmp_hdr *icmp;\n            uint16_t tlen;\n\n            /* Start from Ethernet header */\n            ipv4 = (struct rte_ipv4_hdr *)l3_hdr;\n            udp  = (struct rte_udp_hdr *)&ipv4[1];\n\n            /* Create the ICMP header */\n            ipv4->src_addr = htonl(pkt->ip_src_addr.addr.ipv4.s_addr);\n            ipv4->dst_addr = htonl(pkt->ip_dst_addr.addr.ipv4.s_addr);\n\n            tlen = pkt->pkt_size - (pkt->ether_hdr_size + sizeof(struct rte_ipv4_hdr));\n            ipv4->total_length  = htons(tlen);\n            ipv4->next_proto_id = pkt->ipProto;\n\n            icmp            = (struct rte_icmp_hdr *)&udp[1];\n            icmp->icmp_code = 0;\n            if ((type == -1) || (type == ICMP4_TIMESTAMP)) {\n                union icmp_data *data = (union icmp_data *)&udp[1];\n\n                icmp->icmp_type           = ICMP4_TIMESTAMP;\n                data->timestamp.ident     = 0x1234;\n                data->timestamp.seq       = 0x5678;\n                data->timestamp.originate = 0x80004321;\n                data->timestamp.receive   = 0;\n                data->timestamp.transmit  = 0;\n            } else if (type == ICMP4_ECHO) {\n                union icmp_data *data = (union icmp_data *)&udp[1];\n\n                icmp->icmp_type  = ICMP4_ECHO;\n                data->echo.ident = 0x1234;\n                data->echo.seq   = 0x5678;\n                data->echo.data  = 0;\n            }\n            icmp->icmp_cksum = 0;\n            /* ICMP4_TIMESTAMP_SIZE */\n            tlen             = pkt->pkt_size - (pkt->ether_hdr_size + sizeof(struct rte_ipv4_hdr));\n            icmp->icmp_cksum = rte_raw_cksum(icmp, tlen);\n            if (icmp->icmp_cksum == 0)\n                icmp->icmp_cksum = 0xFFFF;\n\n            /* IPv4 Header constructor */\n            pktgen_ipv4_ctor(pkt, l3_hdr, ipv4_cksum_offload);\n        }\n    } else if (pkt->ethType == RTE_ETHER_TYPE_IPV6) {\n        if (pkt->ipProto == PG_IPPROTO_TCP) {\n            bool tcp_cksum_offload = offload_capa & RTE_ETH_TX_OFFLOAD_TCP_CKSUM;\n            /* Construct the TCP header */\n            pktgen_tcp_hdr_ctor(pkt, l3_hdr, RTE_ETHER_TYPE_IPV6, tcp_cksum_offload,\n                                pinfo->cksum_requires_phdr);\n            if (sport_entropy != 0) {\n                struct rte_ipv6_hdr *ipv6 = (struct rte_ipv6_hdr *)l3_hdr;\n                struct rte_tcp_hdr *tcp   = (struct rte_tcp_hdr *)&ipv6[1];\n\n                tcp->src_port = htons(sport_entropy & 0xFFFF);\n            }\n\n            /* IPv6 Header constructor */\n            pktgen_ipv6_ctor(pkt, l3_hdr);\n        } else if (pkt->ipProto == PG_IPPROTO_UDP) {\n            bool udp_cksum_offload = offload_capa & RTE_ETH_TX_OFFLOAD_UDP_CKSUM;\n            /* Construct the UDP header */\n            pktgen_udp_hdr_ctor(pkt, l3_hdr, RTE_ETHER_TYPE_IPV6, udp_cksum_offload,\n                                pinfo->cksum_requires_phdr);\n            if (sport_entropy != 0) {\n                struct rte_ipv6_hdr *ipv6 = (struct rte_ipv6_hdr *)l3_hdr;\n                struct rte_udp_hdr *udp   = (struct rte_udp_hdr *)&ipv6[1];\n\n                udp->src_port = htons(sport_entropy & 0xFFFF);\n            }\n\n            /* IPv6 Header constructor */\n            pktgen_ipv6_ctor(pkt, l3_hdr);\n        }\n    } else if (pkt->ethType == RTE_ETHER_TYPE_ARP) {\n        /* Start from Ethernet header */\n        struct rte_arp_hdr *arp = (struct rte_arp_hdr *)l3_hdr;\n\n        arp->arp_hardware = htons(1);\n        arp->arp_protocol = htons(RTE_ETHER_TYPE_IPV4);\n        arp->arp_hlen     = RTE_ETHER_ADDR_LEN;\n        arp->arp_plen     = 4;\n\n        /* make request/reply operation selectable by user */\n        arp->arp_opcode = htons(2);\n\n        rte_ether_addr_copy(&pkt->eth_src_addr, &arp->arp_data.arp_sha);\n        *((uint32_t *)&arp->arp_data.arp_sha) = htonl(pkt->ip_src_addr.addr.ipv4.s_addr);\n\n        rte_ether_addr_copy(&pkt->eth_dst_addr, &arp->arp_data.arp_tha);\n        *((uint32_t *)((void *)&arp->arp_data + offsetof(struct rte_arp_ipv4, arp_tip))) =\n            htonl(pkt->ip_dst_addr.addr.ipv4.s_addr);\n    } else\n        pktgen_log_error(\"Unknown EtherType 0x%04x\", pkt->ethType);\n}\n\n/**\n *\n * pktgen_packet_type - Examine a packet and return the type of packet\n *\n * DESCRIPTION\n * Examine a packet and return the type of packet.\n * the packet.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic inline pktType_e\npktgen_packet_type(struct rte_mbuf *m)\n{\n    pktType_e ret;\n    struct rte_ether_hdr *eth;\n\n    eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);\n\n    ret = ntohs(eth->ether_type);\n\n    return ret;\n}\n\n/**\n *\n * pktgen_packet_classify - Examine a packet and classify it for statistics\n *\n * DESCRIPTION\n * Examine a packet and determine its type along with counting statistics around\n * the packet.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic inline void\npktgen_packet_classify(struct rte_mbuf *m, int pid, int qid)\n{\n    port_info_t *pinfo = l2p_get_port_pinfo(pid);\n\n    if (unlikely(pktgen_tst_port_flags(pinfo, PROCESS_INPUT_PKTS))) {\n        pktType_e pType = pktgen_packet_type(m);\n\n        switch ((int)pType) {\n        case RTE_ETHER_TYPE_ARP:\n            pktgen_process_arp(m, pid, qid, 0);\n            break;\n        case RTE_ETHER_TYPE_IPV4:\n            pktgen_process_ping4(m, pid, qid, 0);\n            break;\n        case RTE_ETHER_TYPE_IPV6:\n            pktgen_process_ping6(m, pid, qid, 0);\n            break;\n        case RTE_ETHER_TYPE_VLAN:\n            pktgen_process_vlan(m, pid, qid);\n            break;\n        case UNKNOWN_PACKET: /* FALL THRU */\n        default:\n            break;\n        }\n    }\n}\n\n/**\n *\n * pktgen_packet_classify_buld - Classify a set of packets in one call.\n *\n * DESCRIPTION\n * Classify a list of packets and to improve classify performance.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n#define PREFETCH_OFFSET 3\nstatic inline void\npktgen_packet_classify_bulk(struct rte_mbuf **pkts, int nb_rx, int pid, int qid)\n{\n    int j, i;\n\n    /* Prefetch first packets */\n    for (j = 0; j < PREFETCH_OFFSET && j < nb_rx; j++)\n        rte_prefetch0(rte_pktmbuf_mtod(pkts[j], void *));\n\n    /* Prefetch and handle already prefetched packets */\n    for (i = 0; i < (nb_rx - PREFETCH_OFFSET); i++) {\n        rte_prefetch0(rte_pktmbuf_mtod(pkts[j], void *));\n        j++;\n\n        pktgen_packet_classify(pkts[i], pid, qid);\n    }\n\n    /* Handle remaining prefetched packets */\n    for (; i < nb_rx; i++)\n        pktgen_packet_classify(pkts[i], pid, qid);\n}\n\n/**\n *\n * pktgen_send_special - Send a special packet to the given port.\n *\n * DESCRIPTION\n * Create a special packet in the buffer provided.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic void\npktgen_send_special(port_info_t *pinfo)\n{\n    if (!pktgen_tst_port_flags(pinfo, SEND_ARP_PING_REQUESTS))\n        return;\n\n    /* Send packets attached to the sequence packets. */\n    for (uint32_t s = 0; s < pinfo->seqCnt; s++) {\n        if (unlikely(pktgen_tst_port_flags(pinfo, SEND_GRATUITOUS_ARP)))\n            pktgen_send_arp(pinfo->pid, GRATUITOUS_ARP, s);\n        else if (unlikely(pktgen_tst_port_flags(pinfo, SEND_ARP_REQUEST)))\n            pktgen_send_arp(pinfo->pid, 0, s);\n\n        if (unlikely(pktgen_tst_port_flags(pinfo, SEND_PING4_REQUEST)))\n            pktgen_send_ping4(pinfo->pid, s);\n#ifdef INCLUDE_PING6\n        if (unlikely(pktgen_tst_port_flags(pinfo, SEND_PING6_REQUEST)))\n            pktgen_send_ping6(pinfo->pid, s);\n#endif\n    }\n\n    /* Send the requests from the Single packet setup. */\n    if (unlikely(pktgen_tst_port_flags(pinfo, SEND_GRATUITOUS_ARP)))\n        pktgen_send_arp(pinfo->pid, GRATUITOUS_ARP, SINGLE_PKT);\n    else if (unlikely(pktgen_tst_port_flags(pinfo, SEND_ARP_REQUEST)))\n        pktgen_send_arp(pinfo->pid, 0, SINGLE_PKT);\n\n    if (unlikely(pktgen_tst_port_flags(pinfo, SEND_PING4_REQUEST)))\n        pktgen_send_ping4(pinfo->pid, SINGLE_PKT);\n#ifdef INCLUDE_PING6\n    if (unlikely(pktgen_tst_port_flags(pinfo, SEND_PING6_REQUEST)))\n        pktgen_send_ping6(pinfo->pid, SINGLE_PKT);\n#endif\n\n    pktgen_clr_port_flags(pinfo, SEND_ARP_PING_REQUESTS);\n}\n\nstruct pkt_setup_s {\n    int32_t seq_idx;\n    port_info_t *pinfo;\n};\n\nstatic inline void\nmempool_setup_cb(struct rte_mempool *mp __rte_unused, void *opaque, void *obj,\n                 unsigned obj_idx __rte_unused)\n{\n    struct rte_mbuf *m               = (struct rte_mbuf *)obj;\n    struct pkt_setup_s *s            = (struct pkt_setup_s *)opaque;\n    struct rte_eth_dev_info dev_info = {0};\n    port_info_t *pinfo               = s->pinfo;\n    int32_t idx, seq_idx = s->seq_idx;\n    pkt_seq_t *pkt;\n    int ret;\n\n    ret = rte_eth_dev_info_get(pinfo->pid, &dev_info);\n    if (ret != 0)\n        printf(\"Error during getting device (port %u) info: %s\\n\", pinfo->pid, strerror(-ret));\n\n    idx = seq_idx;\n    if (pktgen_tst_port_flags(pinfo, SEND_SEQ_PKTS)) {\n        idx = pinfo->seqIdx;\n\n        /* move to the next packet in the sequence. */\n        if (unlikely(++pinfo->seqIdx >= pinfo->seqCnt))\n            pinfo->seqIdx = 0;\n    }\n    pkt = &pinfo->seq_pkt[idx];\n\n    if (idx == RANGE_PKT)\n        pktgen_range_ctor(&pinfo->range, pkt);\n\n    pktgen_packet_ctor(pinfo, idx, -1);\n\n    rte_memcpy(rte_pktmbuf_mtod(m, uint8_t *), (uint8_t *)pkt->hdr, pkt->pkt_size);\n\n    m->pkt_len  = pkt->pkt_size;\n    m->data_len = pkt->pkt_size;\n    m->l2_len   = pkt->ether_hdr_size;\n    m->l3_len   = sizeof(struct rte_ipv4_hdr);\n\n    switch (pkt->ethType) {\n    case RTE_ETHER_TYPE_IPV4:\n        if (dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM)\n            pkt->ol_flags = RTE_MBUF_F_TX_IP_CKSUM | RTE_MBUF_F_TX_IPV4;\n        break;\n\n    case RTE_ETHER_TYPE_IPV6:\n        pkt->ol_flags = RTE_MBUF_F_TX_IP_CKSUM | RTE_MBUF_F_TX_IPV6;\n        break;\n\n    case RTE_ETHER_TYPE_VLAN:\n        if (dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_VLAN_INSERT) {\n            /* TODO */\n        }\n        break;\n    default:\n        break;\n    }\n\n    switch (pkt->ipProto) {\n    case PG_IPPROTO_UDP:\n        if (dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_UDP_CKSUM)\n            pkt->ol_flags |= RTE_MBUF_F_TX_UDP_CKSUM;\n        break;\n    case PG_IPPROTO_TCP:\n        if (dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_TCP_CKSUM)\n            pkt->ol_flags |= RTE_MBUF_F_TX_TCP_CKSUM;\n        break;\n    default:\n        break;\n    }\n    m->ol_flags = pkt->ol_flags;\n}\n\n/**\n *\n * pktgen_setup_packets - Setup the default packets to be sent.\n *\n * DESCRIPTION\n * Construct the default set of packets for a given port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_setup_packets(uint16_t pid)\n{\n    l2p_port_t *port   = l2p_get_port(pid);\n    port_info_t *pinfo = l2p_get_port_pinfo(pid);\n\n    if (port == NULL)\n        rte_exit(EXIT_FAILURE, \"Invalid l2p port for %d\\n\", pid);\n\n    if (pktgen_tst_port_flags(pinfo, SETUP_TRANSMIT_PKTS)) {\n        pktgen_clr_port_flags(pinfo, SETUP_TRANSMIT_PKTS);\n\n        if (!pktgen_tst_port_flags(pinfo, SEND_PCAP_PKTS)) {\n            struct pkt_setup_s s;\n            int32_t idx = SINGLE_PKT;\n\n            if (pktgen_tst_port_flags(pinfo, SEND_RANGE_PKTS)) {\n                idx = RANGE_PKT;\n            } else if (pktgen_tst_port_flags(pinfo, SEND_SEQ_PKTS))\n                idx = FIRST_SEQ_PKT;\n            else if (pktgen_tst_port_flags(pinfo, (SEND_SINGLE_PKTS | SEND_RANDOM_PKTS)))\n                idx = SINGLE_PKT;\n\n            s.pinfo   = pinfo;\n            s.seq_idx = idx;\n\n            for (uint16_t q = 0; q < l2p_get_txcnt(pid); q++) {\n                struct rte_mempool *tx_mp = l2p_get_tx_mp(pid, q);\n                if (unlikely(tx_mp == NULL))\n                    rte_exit(EXIT_FAILURE, \"Invalid TX mempool for port %d qid %u\\n\", pid, q);\n                rte_mempool_obj_iter(tx_mp, mempool_setup_cb, &s);\n            }\n        }\n    }\n}\n\n/**\n *\n * pktgen_send_pkts - Send a set of packet buffers to a given port.\n *\n * DESCRIPTION\n * Transmit a set of packets mbufs to a given port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npktgen_send_pkts(port_info_t *pinfo, uint16_t qid, struct rte_mempool *mp)\n{\n    uint64_t txCnt;\n    struct rte_mbuf **pkts = pinfo->per_queue[qid].tx_pkts;\n\n    if (!pktgen_tst_port_flags(pinfo, SEND_FOREVER)) {\n        txCnt = pkt_atomic64_tx_count(&pinfo->current_tx_count, pinfo->tx_burst);\n        if (txCnt == 0) {\n            pktgen_clr_port_flags(pinfo, SENDING_PACKETS);\n            return;\n        }\n        if (txCnt > pinfo->tx_burst)\n            txCnt = pinfo->tx_burst;\n    } else\n        txCnt = pinfo->tx_burst;\n\n    if (rte_mempool_get_bulk(mp, (void **)pkts, txCnt) == 0)\n        tx_send_packets(pinfo, qid, pkts, txCnt);\n}\n\n/**\n *\n * pktgen_main_transmit - Determine the next packet format to transmit.\n *\n * DESCRIPTION\n * Determine the next packet format to transmit for a given port.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic inline void\npktgen_main_transmit(port_info_t *pinfo, uint16_t qid)\n{\n    uint16_t pid = pinfo->pid;\n\n    /* Transmit ARP/Ping packets if needed */\n    pktgen_send_special(pinfo);\n\n    /* When not transmitting on this port then continue. */\n    if (pktgen_tst_port_flags(pinfo, SENDING_PACKETS)) {\n        struct rte_mempool *mp = l2p_get_tx_mp(pid, qid);\n\n        if (pktgen_tst_port_flags(pinfo, SEND_PCAP_PKTS))\n            mp = l2p_get_pcap_mp(pid);\n\n        pktgen_send_pkts(pinfo, qid, mp);\n    }\n}\n\n/**\n *\n * pktgen_main_receive - Main receive routine for packets of a port.\n *\n * DESCRIPTION\n * Handle the main receive set of packets on a given port plus handle all of the\n * input processing if required.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic inline void\npktgen_main_receive(port_info_t *pinfo, uint16_t qid)\n{\n    struct rte_mbuf **pkts = pinfo->per_queue[qid].rx_pkts;\n    qstats_t *qs           = &pinfo->stats.qstats[qid];\n    uint16_t nb_rx, nb_pkts = pinfo->rx_burst, pid;\n\n    if (unlikely(pktgen_tst_port_flags(pinfo, STOP_RECEIVING_PACKETS)))\n        return;\n\n    pid = pinfo->pid;\n\n    /* Read packets from RX queues and free the mbufs */\n    if (likely((nb_rx = rte_eth_rx_burst(pid, qid, pkts, nb_pkts)) > 0)) {\n        qs->q_ipackets += nb_rx;\n\n        if (pktgen_tst_port_flags(pinfo, SEND_LATENCY_PKTS))\n            pktgen_tstamp_check(pinfo, pkts, nb_rx);\n\n        /* classify the packets and update counters */\n        pktgen_packet_classify_bulk(pkts, nb_rx, pid, qid);\n\n        if (unlikely(pinfo->dump_count > 0))\n            pktgen_packet_dump_bulk(pkts, nb_rx, pid);\n\n        if (unlikely(pktgen_tst_port_flags(pinfo, CAPTURE_PKTS))) {\n            capture_t *capture = &pktgen.capture[pg_socket_id()];\n\n            if (unlikely(capture->port == pid))\n                pktgen_packet_capture_bulk(pkts, nb_rx, capture);\n        }\n\n        rte_pktmbuf_free_bulk(pkts, nb_rx);\n    }\n}\n\n/**\n *\n * pktgen_main_rxtx_loop - Single thread loop for tx/rx packets\n *\n * DESCRIPTION\n * Handle sending and receiving packets from a given set of ports. This is the\n * main loop or thread started on a single core.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic void\npktgen_main_rxtx_loop(void)\n{\n    port_info_t *pinfo;\n    uint64_t curr_tsc, tx_next_cycle, tx_bond_cycle;\n    uint16_t pid, rx_qid, tx_qid, lid = rte_lcore_id();\n\n    if (lid == rte_get_main_lcore()) {\n        printf(\"Using %d initial lcore for Rx/Tx\\n\", lid);\n        rte_exit(0, \"using initial lcore for port\");\n    }\n\n    pinfo = l2p_get_pinfo_by_lcore(lid);\n\n    curr_tsc      = pktgen_get_time();\n    tx_next_cycle = curr_tsc;\n    tx_bond_cycle = curr_tsc + (pktgen_get_timer_hz() / 10);\n\n    pid    = pinfo->pid;\n    rx_qid = l2p_get_rxqid(lid);\n    tx_qid = l2p_get_txqid(lid);\n\n    printf(\"RX/TX lid %3d, pid %2d, qids %2d/%2d RX-MP %-16s @ %p TX-MP %-16s @ %p\\n\", lid,\n           pinfo->pid, rx_qid, tx_qid, l2p_get_rx_mp(pinfo->pid, rx_qid)->name,\n           l2p_get_rx_mp(pinfo->pid, rx_qid), l2p_get_tx_mp(pinfo->pid, tx_qid)->name,\n           l2p_get_tx_mp(pinfo->pid, tx_qid));\n\n    while (pktgen.force_quit == 0) {\n        /* Process RX */\n        pktgen_main_receive(pinfo, rx_qid);\n\n        curr_tsc = pktgen_get_time();\n\n        /* Determine when is the next time to send packets */\n        const int64_t max_tx_lag =\n            DEFAULT_MAX_TX_LAG;        // Allow some lag, ideally make this configurable.\n        int64_t dt = curr_tsc - tx_next_cycle;\n        if (dt >= 0) {\n            tx_next_cycle = curr_tsc + pinfo->tx_cycles - (dt <= max_tx_lag ? dt : 0);\n            // Process TX\n            pktgen_main_transmit(pinfo, tx_qid);\n        }\n        if (curr_tsc >= tx_bond_cycle) {\n            tx_bond_cycle = curr_tsc + (pktgen_get_timer_hz() / 10);\n            if (pktgen_tst_port_flags(pinfo, BONDING_TX_PACKETS))\n                rte_eth_tx_burst(pid, tx_qid, NULL, 0);\n        }\n    }\n\n    pktgen_log_debug(\"Exit %d\", lid);\n\n    pktgen_exit_cleanup(lid);\n}\n\n/**\n *\n * pktgen_main_tx_loop - Main transmit loop for a core, no receive packet handling\n *\n * DESCRIPTION\n * When Tx and Rx are split across two cores this routing handles the tx packets.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic void\npktgen_main_tx_loop(void)\n{\n    uint16_t tx_qid, lid = rte_lcore_id();\n    port_info_t *pinfo = l2p_get_pinfo_by_lcore(lid);\n    uint64_t curr_tsc, tx_next_cycle, tx_bond_cycle;\n\n    if (lid == rte_get_main_lcore()) {\n        printf(\"Using %d initial lcore for Rx/Tx\\n\", lid);\n        rte_exit(0, \"Invalid initial lcore assigned to a port\");\n    }\n\n    curr_tsc      = pktgen_get_time();\n    tx_next_cycle = curr_tsc + pinfo->tx_cycles;\n    tx_bond_cycle = curr_tsc + pktgen_get_timer_hz() / 10;\n\n    tx_qid = l2p_get_txqid(lid);\n\n    printf(\"TX lid %3d, pid %2d, qid %2d, TX-MP %-16s @ %p\\n\", lid, pinfo->pid, tx_qid,\n           l2p_get_tx_mp(pinfo->pid, tx_qid)->name, l2p_get_tx_mp(pinfo->pid, tx_qid));\n\n    while (unlikely(pktgen.force_quit == 0)) {\n        curr_tsc = pktgen_get_time();\n\n        /* Determine when is the next time to send packets */\n        const int64_t max_tx_lag =\n            DEFAULT_MAX_TX_LAG;        // Allow some lag, ideally make this configurable.\n        int64_t dt = curr_tsc - tx_next_cycle;\n        if (dt >= 0) {\n            tx_next_cycle = curr_tsc + pinfo->tx_cycles - (dt <= max_tx_lag ? dt : 0);\n\n            // Process TX\n            pktgen_main_transmit(pinfo, tx_qid);\n        }\n        if (unlikely(curr_tsc >= tx_bond_cycle)) {\n            tx_bond_cycle = curr_tsc + pktgen_get_timer_hz() / 10;\n            if (pktgen_tst_port_flags(pinfo, BONDING_TX_PACKETS))\n                rte_eth_tx_burst(pinfo->pid, tx_qid, NULL, 0);\n        }\n    }\n\n    pktgen_log_debug(\"Exit %d\", lid);\n\n    pktgen_exit_cleanup(lid);\n}\n\n/**\n *\n * pktgen_main_rx_loop - Handle only the rx packets for a set of ports.\n *\n * DESCRIPTION\n * When Tx and Rx processing is split between two ports this routine handles\n * only the receive packets.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nstatic void\npktgen_main_rx_loop(void)\n{\n    port_info_t *pinfo;\n    uint16_t lid = rte_lcore_id(), rx_qid = l2p_get_rxqid(lid);\n\n    if (lid == rte_get_main_lcore()) {\n        printf(\"Using %d initial lcore for Rx/Tx\\n\", lid);\n        rte_exit(0, \"using initial lcore for ports\");\n    }\n\n    pinfo  = l2p_get_pinfo_by_lcore(lid);\n    rx_qid = l2p_get_rxqid(lid);\n\n    printf(\"RX lid %3d, pid %2d, qid %2d, RX-MP %-16s @ %p\\n\", lid, pinfo->pid, rx_qid,\n           l2p_get_rx_mp(pinfo->pid, rx_qid)->name, l2p_get_rx_mp(pinfo->pid, rx_qid));\n\n    while (pktgen.force_quit == 0)\n        pktgen_main_receive(pinfo, rx_qid);\n\n    pktgen_log_debug(\"Exit %d\", lid);\n\n    pktgen_exit_cleanup(lid);\n}\n\n/**\n *\n * pktgen_launch_one_lcore - Launch a single logical core thread.\n *\n * DESCRIPTION\n * Help launching a single thread on one logical core.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nint\npktgen_launch_one_lcore(void *arg __rte_unused)\n{\n    uint16_t pid, lid = rte_lcore_id();\n\n    if ((pid = l2p_get_pid_by_lcore(lid)) >= RTE_MAX_ETHPORTS) {\n        pktgen_log_info(\"*** Logical core %3d has no work, skipping launch\", lid);\n        return 0;\n    }\n\n    switch (l2p_get_type(lid)) {\n    case LCORE_MODE_RX:\n        pktgen_main_rx_loop();\n        break;\n    case LCORE_MODE_TX:\n        pktgen_main_tx_loop();\n        break;\n    case LCORE_MODE_BOTH:\n        pktgen_main_rxtx_loop();\n        break;\n    default:\n        rte_exit(EXIT_FAILURE, \"Invalid logical core mode %d\\n\", l2p_get_type(lid));\n    }\n    return 0;\n}\n\nstatic void\n_page_display(void)\n{\n    static unsigned int counter = 0;\n\n    pktgen_display_set_color(\"top.spinner\");\n    scrn_printf(1, 1, \"%c\", \"-\\\\|/\"[(counter++ & 3)]);\n    pktgen_display_set_color(NULL);\n\n    if ((pktgen.flags & PAGE_MASK_BITS) == 0)\n        pktgen.flags |= MAIN_PAGE_FLAG;\n\n    if (pktgen.flags & MAIN_PAGE_FLAG)\n        pktgen_page_stats();\n    else if (pktgen.flags & SYSTEM_PAGE_FLAG)\n        pktgen_page_system();\n    else if (pktgen.flags & RANGE_PAGE_FLAG)\n        pktgen_page_range();\n    else if (pktgen.flags & CPU_PAGE_FLAG)\n        pktgen_page_cpu();\n    else if (pktgen.flags & SEQUENCE_PAGE_FLAG)\n        pktgen_page_seq(pktgen.curr_port);\n    else if (pktgen.flags & RND_BITFIELD_PAGE_FLAG) {\n        port_info_t *pinfo = l2p_get_port_pinfo(pktgen.curr_port);\n        pktgen_page_random_bitfields(pktgen.flags & PRINT_LABELS_FLAG, pktgen.curr_port,\n                                     pinfo->rnd_bitfields);\n    } else if (pktgen.flags & LOG_PAGE_FLAG)\n        pktgen_page_log(pktgen.flags & PRINT_LABELS_FLAG);\n    else if (pktgen.flags & LATENCY_PAGE_FLAG)\n        pktgen_page_latency();\n    else if (pktgen.flags & QSTATS_PAGE_FLAG)\n        pktgen_page_qstats(pktgen.curr_port);\n    else if (pktgen.flags & XSTATS_PAGE_FLAG)\n        pktgen_page_xstats(pktgen.curr_port);\n    else\n        pktgen_page_stats();\n}\n\n/**\n *\n * pktgen_page_display - Display the correct page based on timer callback.\n *\n * DESCRIPTION\n * When timer is active update or display the correct page of data.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_page_display(void)\n{\n    static unsigned int update_display = 1;\n\n    /* Leave if the screen is paused */\n    if (scrn_is_paused())\n        return;\n\n    scrn_save();\n\n    if (pktgen.flags & UPDATE_DISPLAY_FLAG) {\n        pktgen.flags &= ~UPDATE_DISPLAY_FLAG;\n        update_display = 1;\n    }\n\n    update_display--;\n    if (update_display == 0) {\n        update_display = UPDATE_DISPLAY_TICK_INTERVAL;\n        _page_display();\n\n        if (pktgen.flags & PRINT_LABELS_FLAG)\n            pktgen.flags &= ~PRINT_LABELS_FLAG;\n    }\n\n    scrn_restore();\n\n    pktgen_print_packet_dump();\n}\n\nstatic void *\n_timer_thread(void *arg)\n{\n    uint64_t process, page, prev;\n\n    this_scrn = arg;\n\n    pktgen.stats_timeout = pktgen.hz;\n    pktgen.page_timeout  = UPDATE_DISPLAY_TICK_RATE;\n\n    page = prev = pktgen_get_time();\n    process     = page + pktgen.stats_timeout;\n    page += pktgen.page_timeout;\n\n    pktgen.timer_running = 1;\n\n    while (pktgen.timer_running) {\n        uint64_t curr;\n\n        curr = pktgen_get_time();\n\n        if (curr >= process) {\n            process = curr + pktgen.stats_timeout;\n            pktgen_process_stats();\n            prev = curr;\n        }\n\n        if (curr >= page) {\n            page = curr + pktgen.page_timeout;\n            pktgen_page_display();\n        }\n\n        rte_pause();\n    }\n    return NULL;\n}\n\n/**\n *\n * pktgen_timer_setup - Set up the timer callback routines.\n *\n * DESCRIPTION\n * Setup the two timers to be used for display and calculating statistics.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\npktgen_timer_setup(void)\n{\n    rte_cpuset_t cpuset_data;\n    rte_cpuset_t *cpuset = &cpuset_data;\n    pthread_t tid;\n\n    CPU_ZERO(cpuset);\n\n    pthread_create(&tid, NULL, _timer_thread, this_scrn);\n\n    CPU_SET(rte_get_main_lcore(), cpuset);\n    pthread_setaffinity_np(tid, sizeof(cpuset), cpuset);\n}\n"
  },
  {
    "path": "app/pktgen.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PKTGEN_H_\n#define _PKTGEN_H_\n\n/**\n * @file\n *\n * Core pktgen header: global state, port-flag manipulation, and master control macros.\n *\n * Includes all DPDK and pktgen sub-headers, declares the global pktgen_t struct,\n * defines the per-port flag bits, packet-slot indices, display enumerations, and\n * provides inline helpers for atomic port-flag operations and time acquisition.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <string.h>\n#include <sys/queue.h>\n#include <netinet/in.h>\n#include <net/if.h>\n#include <fcntl.h>\n#include <setjmp.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <errno.h>\n#include <getopt.h>\n#include <termios.h>\n#include <sys/ioctl.h>\n#include <libgen.h>\n#include <linux/if_tun.h>\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <poll.h>\n#include <assert.h>\n#include <time.h>\n\n#include <rte_config.h>\n\n#include <rte_errno.h>\n#include <rte_log.h>\n#include <rte_tailq.h>\n#include <rte_common.h>\n#include <rte_memory.h>\n#include <rte_memcpy.h>\n#include <rte_memzone.h>\n#include <rte_malloc.h>\n#include <rte_eal.h>\n#include <rte_per_lcore.h>\n#include <rte_launch.h>\n#include <rte_atomic.h>\n#include <rte_cycles.h>\n#include <rte_prefetch.h>\n#include <rte_lcore.h>\n#include <rte_branch_prediction.h>\n#include <rte_pci.h>\n#include <rte_random.h>\n#include <rte_timer.h>\n#include <rte_ether.h>\n#include <rte_ethdev.h>\n#include <rte_ring.h>\n#include <rte_mempool.h>\n#include <rte_mbuf.h>\n#include <rte_ip.h>\n#include <rte_udp.h>\n#include <rte_tcp.h>\n#include <rte_dev.h>\n#include <rte_time.h>\n\n#include <l2p.h>\n#include <port_config.h>\n\n#include <pg_inet.h>\n#include <cksum.h>\n\n#include <coreinfo.h>\n#include <lscpu.h>\n#include <utils.h>\n\n#ifdef LUA_ENABLED\n#include \"lua_config.h\"\n#include \"lauxlib.h\"\n#endif\n\n#include \"pktgen-port-cfg.h\"\n#include \"pktgen-capture.h\"\n#include \"pktgen-log.h\"\n#include \"pktgen-latency.h\"\n#include \"pktgen-random.h\"\n#include \"pktgen-seq.h\"\n#include \"pktgen-version.h\"\n\n#include <cli.h>\n\n#ifdef LUA_ENABLED\n#include <lua_config.h>\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MAX_MATRIX_ENTRIES 128 /**< Maximum number of lcore/port mapping entries */\n#define MAX_STRING         256 /**< Maximum length of a mapping string */\n#define Million            (uint64_t)(1000000UL)    /**< One million (10^6) */\n#define Billion            (uint64_t)(1000000000UL) /**< One billion (10^9) */\n\n/** Compute total input bits including Ethernet physical overhead. */\n#define iBitsTotal(_x) (uint64_t)(((_x.ipackets * PKT_OVERHEAD_SIZE) + _x.ibytes) * 8)\n/** Compute total output bits including Ethernet physical overhead. */\n#define oBitsTotal(_x) (uint64_t)(((_x.opackets * PKT_OVERHEAD_SIZE) + _x.obytes) * 8)\n\n/** Execute @p _exp in a do-while(0) to allow use in if-else without braces. */\n#define _do(_exp) \\\n    do {          \\\n        _exp;     \\\n    } while ((0))\n\n#ifndef RTE_ETH_FOREACH_DEV\n#define RTE_ETH_FOREACH_DEV(p) for (_p = 0; _p < pktgen.nb_ports; _p++)\n#endif\n\n/**\n * Iterate over all active ports and execute @p _action for each.\n *\n * Declares local variables @c pid and @c pinfo; @p _action may reference\n * both.  Ports whose seq_pkt array is NULL are skipped.\n */\n#define forall_ports(_action)                \\\n    do {                                     \\\n        uint16_t pid;                        \\\n                                             \\\n        RTE_ETH_FOREACH_DEV(pid)             \\\n        {                                    \\\n            port_info_t *pinfo;              \\\n                                             \\\n            pinfo = l2p_get_port_pinfo(pid); \\\n            if (pinfo->seq_pkt == NULL)      \\\n                continue;                    \\\n            _action;                         \\\n        }                                    \\\n    } while ((0))\n\n/**\n * Iterate over ports selected by a portlist bitmask and execute @p _action.\n *\n * Declares local variables @c pid and @c pinfo; @p _action may reference\n * both.  Ports not in @p _portlist or with a NULL seq_pkt are skipped.\n *\n * @param _portlist  Portlist bitmask (uint64_t array).\n * @param _action    Statement to execute for each selected port.\n */\n#define foreach_port(_portlist, _action)                  \\\n    do {                                                  \\\n        uint64_t *_pl = (uint64_t *)&_portlist;           \\\n        uint16_t pid, idx, bit;                           \\\n                                                          \\\n        RTE_ETH_FOREACH_DEV(pid)                          \\\n        {                                                 \\\n            port_info_t *pinfo;                           \\\n                                                          \\\n            idx = (pid / (sizeof(uint64_t) * 8));         \\\n            bit = (pid - (idx * (sizeof(uint64_t) * 8))); \\\n            if ((_pl[idx] & (1LL << bit)) == 0)           \\\n                continue;                                 \\\n            pinfo = l2p_get_port_pinfo(pid);              \\\n            if (pinfo->seq_pkt == NULL)                   \\\n                continue;                                 \\\n            _action;                                      \\\n        }                                                 \\\n    } while ((0))\n\n/** Packet processing outcome code returned by the RX handler. */\ntypedef enum {\n    PACKET_CONSUMED = 0,      /**< Packet was handled internally by pktgen */\n    UNKNOWN_PACKET  = 0xEEEE, /**< Packet type was not recognised */\n    DROP_PACKET     = 0xFFFE, /**< Packet should be dropped */\n    FREE_PACKET     = 0xFFFF  /**< Packet buffer should be freed */\n} pktType_e;\n\nenum {\n    MAX_SCRN_ROWS = 44,  /**< Maximum number of terminal rows used */\n    MAX_SCRN_COLS = 132, /**< Maximum number of terminal columns used */\n\n    COLUMN_WIDTH_0 = 22, /**< Width of display column 0 */\n    COLUMN_WIDTH_1 = 24, /**< Width of display column 1 */\n    COLUMN_WIDTH_3 = 24, /**< Width of display column 3 */\n\n    DEFAULT_MAX_TX_LAG = 20000, /**< Max TX lag cycles; ideally make configurable */\n\n    /* Row locations for start of data */\n    PAGE_TITLE_ROWS = 1,  /**< Number of rows for the page title */\n    PORT_FLAGS_ROWS = 1,  /**< Number of rows for port flags */\n    LINK_STATE_ROWS = 1,  /**< Number of rows for link state */\n    PKT_RATE_ROWS   = 7,  /**< Number of rows for packet rate stats */\n    PKT_SIZE_ROWS   = 10, /**< Number of rows for packet size stats */\n    PKT_TOTALS_ROWS = 7,  /**< Number of rows for packet total counters */\n    IP_ADDR_ROWS    = 12, /**< Number of rows for IP address display */\n\n    PAGE_TITLE_ROW = 1,                                  /**< Row for page title */\n    PORT_FLAGS_ROW = (PAGE_TITLE_ROW + PAGE_TITLE_ROWS), /**< Row for port flags */\n    LINK_STATE_ROW = (PORT_FLAGS_ROW + PORT_FLAGS_ROWS), /**< Row for link state */\n    PKT_RATE_ROW   = (LINK_STATE_ROW + LINK_STATE_ROWS), /**< Row for packet rates */\n    PKT_SIZE_ROW   = (PKT_RATE_ROW + PKT_RATE_ROWS),     /**< Row for packet sizes */\n    PKT_TOTALS_ROW = (PKT_SIZE_ROW + PKT_SIZE_ROWS),     /**< Row for packet totals */\n    IP_ADDR_ROW    = (PKT_TOTALS_ROW + PKT_TOTALS_ROWS), /**< Row for IP addresses */\n\n    DEFAULT_NETMASK        = 0xFFFFFF00,                /**< Default network mask (/24) */\n    DEFAULT_IP_ADDR        = (192 << 24) | (168 << 16), /**< Default source IP (192.168.0.0) */\n    DEFAULT_TX_COUNT       = 0,                         /**< Default TX count (0 = forever) */\n    DEFAULT_TX_RATE        = 100,                       /**< Default TX rate (100%) */\n    DEFAULT_PRIME_COUNT    = 1,                         /**< Default prime burst count */\n    DEFAULT_SRC_PORT       = 1234,                      /**< Default source L4 port */\n    DEFAULT_DST_PORT       = 5678,                      /**< Default destination L4 port */\n    DEFAULT_TTL            = 64,                        /**< Default IP TTL */\n    DEFAULT_TCP_SEQ_NUMBER = 0x12378,                   /**< Default TCP initial sequence number */\n    MAX_TCP_SEQ_NUMBER     = UINT32_MAX / 8,            /**< Maximum TCP sequence number */\n    DEFAULT_TCP_ACK_NUMBER = 0x12390,                   /**< Default TCP initial ack number */\n    MAX_TCP_ACK_NUMBER     = UINT32_MAX / 8,            /**< Maximum TCP acknowledgement number */\n    DEFAULT_TCP_FLAGS      = ACK_FLAG,                  /**< Default TCP flags (ACK) */\n    DEFAULT_WND_SIZE       = 8192,                      /**< Default TCP window size */\n    MIN_VLAN_ID            = 1,                         /**< Minimum valid VLAN ID */\n    MAX_VLAN_ID            = 4095,                      /**< Maximum valid VLAN ID */\n    DEFAULT_VLAN_ID        = MIN_VLAN_ID,               /**< Default VLAN ID */\n    MIN_COS                = 0,                         /**< Minimum CoS value */\n    MAX_COS                = 7,                         /**< Maximum CoS value */\n    DEFAULT_COS            = MIN_COS,                   /**< Default CoS value */\n    MIN_TOS                = 0,                         /**< Minimum ToS value */\n    MAX_TOS                = 255,                       /**< Maximum ToS value */\n    DEFAULT_TOS            = MIN_TOS,                   /**< Default ToS value */\n    MAX_ETHER_TYPE_SIZE    = 0x600,                     /**< Maximum Ethernet type field value */\n    OVERHEAD_FUDGE_VALUE   = 50,                        /**< Fudge factor for overhead estimates */\n\n    DEFAULT_PORTS_PER_PAGE = 4, /**< Default number of ports shown per display page */\n    VLAN_TAG_SIZE          = 4, /**< Size of an 802.1Q VLAN tag in bytes */\n    MAX_PRIME_COUNT        = 4, /**< Maximum prime-burst packet count */\n\n    NUM_SEQ_PKTS = 16, /**< Number of sequence packet slots per port */\n\n    FIRST_SEQ_PKT  = 0,                              /**< First sequence slot index */\n    SINGLE_PKT     = (FIRST_SEQ_PKT + NUM_SEQ_PKTS), /**< Single-packet template (slot 16) */\n    SPECIAL_PKT    = (SINGLE_PKT + 1),               /**< Scratch/special packet (slot 17) */\n    RANGE_PKT      = (SPECIAL_PKT + 1),              /**< Range-mode template (slot 18) */\n    LATENCY_PKT    = (RANGE_PKT + 1),                /**< Latency probe packet (slot 19) */\n    NUM_TOTAL_PKTS = (LATENCY_PKT + 1),              /**< Total per-port packet slots */\n\n    INTER_FRAME_GAP       = 12, /**< in bytes */\n    START_FRAME_DELIMITER = 1,  /**< Starting frame delimiter bytes */\n    PKT_PREAMBLE_SIZE     = 7,  /**< in bytes */\n    /* total number of bytes in frame overhead, includes the FCS checksum byte count */\n    PKT_OVERHEAD_SIZE =\n        (INTER_FRAME_GAP + START_FRAME_DELIMITER + PKT_PREAMBLE_SIZE + RTE_ETHER_CRC_LEN),\n    MIN_v6_PKT_SIZE = 78, /**< does include FCS bytes */\n\n    MAX_RX_QUEUES  = 16, /**< RX Queues per port */\n    MAX_TX_QUEUES  = 16, /**< TX Queues per port */\n    PCAP_PAGE_SIZE = 25  /**< Size of the PCAP display page */\n};\n\n/** Compute the wire size of a packet including all physical overhead. */\n#define WIRE_SIZE(pkt_size, t) (t)(pkt_size + PKT_OVERHEAD_SIZE)\n\n/** Convenience typedef for struct rte_mbuf. */\ntypedef struct rte_mbuf rte_mbuf_t;\n\n/** Union allowing an Ethernet address to be accessed as a 64-bit integer. */\ntypedef union {\n    struct rte_ether_addr addr; /**< Ethernet address structure */\n    uint64_t u64;               /**< Raw 64-bit representation */\n} ethaddr_t;\n\n/** Global Pktgen application state: settings, port references, and display parameters. */\ntypedef struct pktgen_s {\n    int verbose;                           /**< Verbose flag */\n    int32_t argc;                          /**< Number of arguments */\n    uint32_t blinklist;                    /**< Port list for blinking the led */\n    uint32_t flags;                        /**< Flag values */\n    volatile int force_quit;               /**< Flag to force quit */\n    struct cmdline *cl;                    /**< Command Line information pointer */\n    char *argv[64];                        /**< Argument list */\n    char *hostname;                        /**< hostname */\n    int32_t socket_port;                   /**< port number */\n    volatile uint8_t timer_running;        /**< flag to denote timer is running */\n    uint16_t ident;                        /**< IPv4 ident value */\n    uint16_t last_row;                     /**< last static row of the screen */\n    uint16_t nb_ports;                     /**< Number of ports in the system */\n    uint8_t starting_port;                 /**< Starting port to display */\n    uint8_t ending_port;                   /**< Ending port to display */\n    uint8_t nb_ports_per_page;             /**< Number of ports to display per page */\n    uint16_t mbuf_dataroom;                /**< Size of data room in mbuf */\n    uint16_t mbuf_buf_size;                /**< MBUF default buf size */\n    uint16_t mbuf_headroom;                /**< Size of headroom in mbuf */\n    uint16_t nb_rxd;                       /**< Number of receive descriptors */\n    uint16_t nb_txd;                       /**< Number of transmit descriptors */\n    uint16_t curr_port;                    /**< Current Port number */\n    uint64_t hz;                           /**< Number of cycles per seconds */\n    uint64_t page_timeout;                 /**< Timeout for page update */\n    uint64_t stats_timeout;                /**< Timeout for stats update */\n    uint64_t max_total_ipackets;           /**< Total Max seen input packet rate */\n    uint64_t max_total_opackets;           /**< Total Max seen output packet rate */\n    uint64_t counter;                      /**< A debug counter */\n    uint64_t mem_used;                     /**< Display memory used counters per ports */\n    uint64_t total_mem_used;               /**< Display memory used for all ports */\n    struct rte_eth_stats cumm_rate_totals; /**< port rates total values */\n\n#ifdef LUA_ENABLED\n    luaData_t *ld;      /**< General Lua Data pointer */\n    luaData_t *ld_sock; /**< Info for Lua Socket */\n    pthread_t thread;   /**< Thread structure for Lua server */\n#endif\n\n    uint8_t *portdesc[RTE_MAX_ETHPORTS];   /**< Port descriptions from lspci */\n    uint32_t portdesc_cnt;                 /**< Number of ports in portdesc array */\n    lscpu_t *lscpu;                        /**< CPU information */\n    capture_t capture[RTE_MAX_NUMA_NODES]; /**< Packet capture, 1 struct per socket */\n} pktgen_t;\n\nenum {                                     /* Pktgen flags bits */\n       PRINT_LABELS_FLAG      = (1 << 0),  /**< Print constant labels on stats display */\n       MAC_FROM_ARP_FLAG      = (1 << 1),  /**< Configure the SRC MAC from a ARP request */\n       PROMISCUOUS_ON_FLAG    = (1 << 2),  /**< Enable promiscuous mode */\n       NUMA_SUPPORT_FLAG      = (1 << 3),  /**< Enable NUMA support */\n       IS_SERVER_FLAG         = (1 << 4),  /**< Pktgen is a Server */\n       RESERVED_05            = (1 << 5),  /**< Reserved */\n       LUA_SHELL_FLAG         = (1 << 6),  /**< Enable Lua Shell */\n       TX_DEBUG_FLAG          = (1 << 7),  /**< TX Debug output */\n       RESERVED_8             = (1 << 8),  /**< Reserved */\n       RESERVED_9             = (1 << 9),  /**< Reserved */\n       BLINK_PORTS_FLAG       = (1 << 10), /**< Blink the port leds */\n       ENABLE_THEME_FLAG      = (1 << 11), /**< Enable theme or color support */\n       CLOCK_GETTIME_FLAG     = (1 << 12), /**< Enable clock_gettime() instead of rdtsc() */\n       JUMBO_PKTS_FLAG        = (1 << 13), /**< Enable Jumbo frames */\n       RESERVED_14            = (1 << 14), /**< Reserved */\n       MAIN_PAGE_FLAG         = (1 << 15), /**< Display the main page */\n       CPU_PAGE_FLAG          = (1 << 16), /**< Display the CPU page */\n       SEQUENCE_PAGE_FLAG     = (1 << 17), /**< Display the Packet sequence page */\n       RANGE_PAGE_FLAG        = (1 << 18), /**< Display the range page */\n       RESERVED_19            = (1 << 19), /**< Reserved */\n       SYSTEM_PAGE_FLAG       = (1 << 20), /**< Display the System page */\n       RND_BITFIELD_PAGE_FLAG = (1 << 21), /**< Display the random bitfield page */\n       LOG_PAGE_FLAG          = (1 << 22), /**< Display the message log page */\n       LATENCY_PAGE_FLAG      = (1 << 23), /**< Display latency page */\n       QSTATS_PAGE_FLAG       = (1 << 24), /**< Display the port queue stats */\n       XSTATS_PAGE_FLAG       = (1 << 25), /**< Display the physical port stats */\n       RESERVED_27            = (1 << 27), /**< Reserved */\n       RESERVED_28            = (1 << 28), /**< Reserved */\n       RESERVED_29            = (1 << 29), /**< Reserved */\n       RESERVED_30            = (1 << 30), /**< Reserved */\n       UPDATE_DISPLAY_FLAG    = (1 << 31)  /**< Trigger a display refresh */\n};\n\n#define UPDATE_DISPLAY_TICK_INTERVAL 4 /**< Display stat refresh checks per second */\n#define UPDATE_DISPLAY_TICK_RATE     (pktgen.hz / UPDATE_DISPLAY_TICK_INTERVAL) /**< Tick rate */\n\n/** Bitmask of all page-select flags used to clear the current page before switching. */\n#define PAGE_MASK_BITS                                                                          \\\n    (MAIN_PAGE_FLAG | CPU_PAGE_FLAG | SEQUENCE_PAGE_FLAG | RANGE_PAGE_FLAG | SYSTEM_PAGE_FLAG | \\\n     RND_BITFIELD_PAGE_FLAG | LOG_PAGE_FLAG | LATENCY_PAGE_FLAG | XSTATS_PAGE_FLAG |            \\\n     QSTATS_PAGE_FLAG)\n\n/** The global pktgen application state singleton. */\nextern pktgen_t pktgen;\n\n/** Redraw the currently active display page. */\nvoid pktgen_page_display(void);\n\n/**\n * Construct a packet template in a sequence slot.\n *\n * @param pinfo    Per-port state.\n * @param seq_idx  Packet slot index (0 .. NUM_TOTAL_PKTS-1).\n * @param type     Protocol type override (-1 to use existing).\n */\nvoid pktgen_packet_ctor(port_info_t *pinfo, int32_t seq_idx, int32_t type);\n\n/**\n * Recalculate the inter-burst TX cycle count for a port's target rate.\n *\n * @param pinfo  Per-port state.\n */\nvoid pktgen_packet_rate(port_info_t *pinfo);\n\n/**\n * Test whether an IPv4 address matches the port's source IP.\n *\n * @return  Non-zero if @p addr matches.\n */\nint pktgen_find_matching_ipsrc(port_info_t *pinfo, uint32_t addr);\n\n/**\n * Test whether an IPv4 address matches the port's destination IP.\n *\n * @return  Non-zero if @p addr matches.\n */\nint pktgen_find_matching_ipdst(port_info_t *pinfo, uint32_t addr);\n\n/**\n * Worker function launched on each lcore.\n *\n * @param arg  Unused (required by rte_eal_remote_launch prototype).\n * @return     0 on clean exit.\n */\nint pktgen_launch_one_lcore(void *arg);\n\n/** Start the interactive CLI input loop on the main lcore. */\nvoid pktgen_input_start(void);\n\n/** Dump the per-port timer statistics to the log. */\nvoid stat_timer_dump(void);\n\n/** Clear the accumulated per-port timer statistics. */\nvoid stat_timer_clear(void);\n\n/** Set up the periodic stat-update and display-refresh timers. */\nvoid pktgen_timer_setup(void);\n\n/**\n * Rebuild packet templates for all queues on a port.\n *\n * @param pid  Port ID.\n */\nvoid pktgen_setup_packets(uint16_t pid);\n\n/**\n * Return the current time value using the configured time source.\n *\n * Returns nanoseconds via clock_gettime(CLOCK_REALTIME) when\n * CLOCK_GETTIME_FLAG is set, otherwise returns rte_rdtsc() cycles.\n *\n * @return\n *   Current time in nanoseconds or TSC cycles.\n */\nstatic inline uint64_t\npktgen_get_time(void)\n{\n    if (pktgen.flags & CLOCK_GETTIME_FLAG) {\n        struct timespec tp;\n\n        if (clock_gettime(CLOCK_REALTIME, &tp) < 0)\n            return rte_rdtsc();\n\n        return rte_timespec_to_ns(&tp);\n    } else\n        return rte_rdtsc();\n}\n\n/**\n * Return the timer frequency for the configured time source.\n *\n * @return\n *   1,000,000,000 when using clock_gettime(), otherwise rte_get_timer_hz().\n */\nstatic inline uint64_t\npktgen_get_timer_hz(void)\n{\n    if (pktgen.flags & CLOCK_GETTIME_FLAG) {\n        struct timespec tp = {.tv_nsec = 0, .tv_sec = 1};\n        return rte_timespec_to_ns(&tp);\n    } else\n        return rte_get_timer_hz();\n}\n\n/** Latency probe packet payload header. */\ntypedef struct {\n    uint32_t magic;     /**< Magic value (TSTAMP_MAGIC) for probe identification */\n    uint32_t index;     /**< Sequence index used to match TX/RX probes */\n    uint64_t timestamp; /**< TSC cycle count at transmit time */\n} tstamp_t;\n\n#define TSTAMP_MAGIC 0xf00dcafe /**< Magic value identifying a latency probe packet */\n\n/**\n * Atomically OR a set of flags into a port's port_flags atomic.\n *\n * @param pinfo  Per-port state.\n * @param flags  Bitmask of SEND_* / port-flag bits to set.\n */\nstatic __inline__ void\npktgen_set_port_flags(port_info_t *pinfo, uint64_t flags)\n{\n    uint64_t val;\n\n    do {\n        val = rte_atomic64_read(&pinfo->port_flags);\n    } while (!rte_atomic64_cmpset((volatile uint64_t *)&pinfo->port_flags.cnt, val, (val | flags)));\n}\n\n/**\n * Atomically clear a set of flags from a port's port_flags atomic.\n *\n * @param pinfo  Per-port state.\n * @param flags  Bitmask of SEND_* / port-flag bits to clear.\n */\nstatic __inline__ void\npktgen_clr_port_flags(port_info_t *pinfo, uint64_t flags)\n{\n    uint64_t val;\n\n    do {\n        val = rte_atomic64_read(&pinfo->port_flags);\n    } while (\n        !rte_atomic64_cmpset((volatile uint64_t *)&pinfo->port_flags.cnt, val, (val & ~flags)));\n}\n\n/**\n * Test whether any of the given flag bits are set in a port's port_flags.\n *\n * @param pinfo  Per-port state.\n * @param flags  Bitmask to test.\n * @return\n *   Non-zero if any bit in @p flags is currently set.\n */\nstatic __inline__ int\npktgen_tst_port_flags(port_info_t *pinfo, uint64_t flags)\n{\n    return ((rte_atomic64_read(&pinfo->port_flags) & flags) ? 1 : 0);\n}\n\n/** Enable/disable state values for CLI and command arguments. */\nenum { DISABLE_STATE = 0, /**< Disabled */ ENABLE_STATE = 1 /**< Enabled */ };\n\n/**\n * Parse an on/off/enable/disable/start state string.\n *\n * @param state  \"on\", \"enable\", or \"start\" → ENABLE_STATE; else → DISABLE_STATE.\n * @return\n *   ENABLE_STATE or DISABLE_STATE.\n */\nstatic __inline__ uint32_t\nestate(const char *state)\n{\n    return (!strcasecmp(state, \"on\") || !strcasecmp(state, \"enable\") || !strcasecmp(state, \"start\"))\n               ? ENABLE_STATE\n               : DISABLE_STATE;\n}\n\n/** Latency sampler algorithm selection. */\nenum {\n    LATSAMPLER_UNSPEC, /**< Unspecified (use default) */\n    LATSAMPLER_SIMPLE, /**< Uniform random sampling */\n    LATSAMPLER_POISSON /**< Poisson-rate sampling */\n};\n\n/**\n * Return the pktgen version string.\n *\n * @return\n *   Pointer to a static buffer containing the version string.\n */\nstatic inline const char *\npktgen_version(void)\n{\n    static char pkt_version[64];\n\n    if (pkt_version[0] != 0)\n        return pkt_version;\n\n    snprintf(pkt_version, sizeof(pkt_version), \"%s\", PKTGEN_VERSION);\n    return pkt_version;\n}\n\n/**\n * Free an existing string and duplicate a new one.\n *\n * @param str  Existing string to free (may be NULL).\n * @param new  String to duplicate (may be NULL, returns NULL in that case).\n * @return\n *   Newly duplicated string, or NULL if @p new is NULL.\n */\nstatic __inline__ char *\nstrdupf(char *str, const char *new)\n{\n    if (str)\n        free(str);\n    return (new == NULL) ? NULL : strdup(new);\n}\n\n/**\n * Execute a shell command and display its output line-by-line.\n *\n * @param cmd\n *   Shell command string to execute via popen().\n * @param display\n *   Callback invoked for each output line; receives the line and an\n *   accumulating counter, returns the updated counter.\n * @return\n *   Final value returned by @p display, or -1 on popen() failure.\n */\nstatic __inline__ int\ndo_command(const char *cmd, int (*display)(char *, int))\n{\n    FILE *f;\n    int i;\n    char *line       = NULL;\n    size_t line_size = 0;\n\n    f = popen(cmd, \"r\");\n    if (f == NULL) {\n        pktgen_log_error(\"Unable to run '%s' command\", cmd);\n        return -1;\n    }\n\n    i = 0;\n    while (getline(&line, &line_size, f) > 0)\n        i = display(line, i);\n\n    if (f)\n        pclose(f);\n    if (line)\n        free(line);\n\n    return i;\n}\n\n#ifndef MEMPOOL_F_DMA\n#define MEMPOOL_F_DMA 0\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PKTGEN_H_ */\n"
  },
  {
    "path": "app/xorshift64star.c",
    "content": "#include \"xorshift64star.h\"\n\n// The state of the \"randomization engine\".\n// Defined here so that it can be shared between files.\nuint64_t xor_state[1] = {1};\n"
  },
  {
    "path": "app/xorshift64star.h",
    "content": "/*\n * Marsaglia, George (July 2003). \"Xorshift RNGs\".\n * Journal of Statistical Software.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* https://en.wikipedia.org/wiki/Xorshift */\n\n#ifndef _XORSHIFT64STAR_H_\n#define _XORSHIFT64STAR_H_\n\n/**\n * @file\n *\n * Xorshift64* fast pseudo-random number generator.\n *\n * Based on Marsaglia (2003) \"Xorshift RNGs\", Journal of Statistical Software.\n * See https://en.wikipedia.org/wiki/Xorshift for the algorithm description.\n * The state must be seeded with a non-zero value before the first call.\n */\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Generator state (must be seeded with a non-zero value before first use). */\nextern uint64_t xor_state[1];\n\n/**\n * Generate the next 64-bit pseudo-random value using the xorshift64* algorithm.\n *\n * Updates @c xor_state[0] in place and returns the scrambled output.\n *\n * @return\n *   64-bit pseudo-random number.\n */\nstatic inline uint64_t\nxorshift64star(void)\n{\n    uint64_t x = xor_state[0]; /* The state must be seeded with a nonzero value. */\n    x ^= x >> 12;              // a\n    x ^= x << 25;              // b\n    x ^= x >> 27;              // c\n    xor_state[0] = x;\n    return x * 0x2545F4914F6CDD1D;\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _XORSHIFT64STAR_H_ */\n"
  },
  {
    "path": "cfg/2-ports.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '03:00.0',\n\t    '03:00.1',\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo', ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '1,2-7,8-13',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'allowlist': (\n\t\t'03:00.0',\n\t\t'03:00.1',\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t'-G',\n        #'-c',\n        #'-j',\n\t\t),\n\t'map': (\n\t\t'[2-4:5-7].0',\n\t\t'[8-10:11-13].1',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t#'loadfile': '2-ports'\n\t}\n"
  },
  {
    "path": "cfg/2-vf-ports.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '82:01.0',\n\t    '82:01.1',\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15-18',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'allowlist': (\n\t\t'82:01.0',\n\t\t'82:01.1',\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t'-G',\n        #'-c',\n        #'-j',\n\t\t),\n\t'map': (\n\t\t'[15:16].0',\n\t\t'[17:18].1',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t'loadfile': '2-ports'\n\t}\n"
  },
  {
    "path": "cfg/bond.cfg",
    "content": "description = 'Pktgen setup to use the bonding PMD driver, 2 bonds, 4 ports per bond.'\n\n# Setup configuration\nsetup = {\n    'exec': (\n        'sudo',\n\n        ),\n\t'devices': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n        'uio': 'igb_uio'\n\t}\n\n# Run command and options\nrun = {\n    'exec': (\n        'sudo',\n\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15-18',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\t'vdev': (\n\t\t'net_bonding0,mode=4,xmit_policy=l23,worker=0000:81:00.0,worker=0000:81:00.1,worker=0000:81:00.2,worker=0000:81:00.3',\n\t\t'net_bonding1,mode=4,xmit_policy=l23,worker=0000:83:00.0,worker=0000:83:00.1,worker=0000:83:00.2,worker=0000:83:00.3',\n\t\t),\n\n\t'blocklist': (\n#\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P'\n\t\t),\n\t'map': (\n\t\t'[15:16].8',\n\t\t'[17:18].9'\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/client_memif.cfg",
    "content": "description = 'Client side of the MEMIF PMD'\n\n# Setup configuration\nsetup = {\n    'exec': (\n        'sudo',\n\n        ),\n\t'devices': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n        'uio': 'vfio-pci'\n\t}\n\n# Run command and options\nrun = {\n    'exec': (\n        'sudo',\n\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '-l 17,18',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg2',\n\t'vdev': '=net_memif0,role=worker',\n\n\t'blocklist': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'18.0'\n\t\t),\n\t'theme': 'themes/black-white.theme',\n\t}\n"
  },
  {
    "path": "cfg/client_mif.cfg",
    "content": "description = 'Client side of the MIF PMD'\n\n# Setup configuration\nsetup = {\n    'exec': (\n        'sudo',\n\n        ),\n\t'devices': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n        'uio': 'vfio-pci'\n\t}\n\n# Run command and options\nrun = {\n    'exec': (\n        'sudo',\n\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '17,18',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': \t'7',\n\t'prefix': 'pg2',\n\t'vdev': 'net_mif0,iftype=client,rxring=2048,txring=2048,scfg=0_8192_1960_d,cache=256',\n\n\t'blocklist': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\n\t'map': '18.0',\n\n\t'theme': 'themes/black-white.theme',\n\t}\n"
  },
  {
    "path": "cfg/cnet-fwd-2.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '03:00.0',\n\t    '82:00.0'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': (\n\t'sudo',\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '2,4-7,14-18',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'allowlist': (\n\t\t'03:00.0',\n\t\t'82:00.0'\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t'-G',\n\t\t),\n\t'map': (\n\t\t'[4-5:6-7].0',\n\t\t'[14-15:16-18].1',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t'loadfile': 'cnet-fwd-2'\n\t}\n"
  },
  {
    "path": "cfg/cnet-fwd.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '03:00.0',\n\t    '03:00.1',\n\t    '05:00.0',\n\t    '05:00.1',\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '1,2-4,5-7,8-10,11-13',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'allowlist': (\n\t\t'03:00.0',\n\t\t'03:00.1',\n\t\t'05:00.0',\n\t\t'05:00.1',\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t'-G',\n        #'-j',\n\t\t),\n\t'map': (\n\t\t'[2:3-4].0',\n\t\t'[5:6-7].1',\n\t\t'[8:9-10].2',\n\t\t'[11:12-13].3',\n\t\t),\n#    'pcap': (\n#        '1:pcap/test1.pcap',\n#        ),\n\n\t'theme': 'themes/black-yellow.theme',\n\t}\n"
  },
  {
    "path": "cfg/dapi-l3fwd.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n        'sudo',\n\n        ),\n\n\t'devices': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3'\n\t\t),\n\n        'uio': 'vfio-pci'\n\t}\n\n# Run command and options\nrun = {\n    'exec': (\n        'sudo',\n\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s',\n        ),\n\n\t'cores': '14,15-18',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'blocklist': (\n\t\t'81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3'\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': ( '[15:16].0', '[17:18].1'),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/default-100G.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '82:00.0'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '2,3-4',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'blocklist': (\n\t\t#'03:00.0', '05:00.0',\n\t\t#'81:00.0', '84:00.0'\n\t\t),\n\t'allowlist': (\n\t\t'82:00.0,safe-mode-support=1',\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[3:4].0',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/default-gui.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '03:00.0'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '2,3-4',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'blocklist': (\n\t\t#'81:00.0', '84:00.0'\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t'-G',\n\t\t),\n\t'map': (\n\t\t'[3:4].0'\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/default.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '5e:00.0', '5e:00.1', 'af:00.0', 'af:00.1'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '2,3-4,5-6,28-29,30-31',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'blocklist': (\n\t\t#'03:00.0', '05:00.0',\n\t\t#'81:00.0', '84:00.0'\n\t\t),\n\t'allowlist': (\n\t\t'5e:00.0', '5e:00.1',\n\t\t'af:00.0', 'af:00.1'\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[3:4].0',\n\t\t'[5:6].1',\n\t\t'[28:29].2',\n\t\t'[30:31].3'\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t#'shared': '/usr/local/lib/x86_64-linux-gnu/dpdk/pmds-21.1'\n\t}\n"
  },
  {
    "path": "cfg/dfs.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n\t'exec': (\n\t'sudo',\n\t\t),\n\n\t'devices': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3'\n\t\t),\n\t# UIO module type, igb_uio, vfio-pci or uio_pci_generic\n\t'uio': 'vfio-pci'\n\t}\n\n# Run command and options\nrun = {\n\t'exec': ('sudo',),\n\n\t# Application name and use app_path to help locate the app\n\t'app_name': 'pktgen',\n\n\t# using (sdk) or (target) for specific variables\n\t# add (app_name) of the application\n\t# Each path is tested for the application\n\t'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n\t\t),\n\n\t'cores': '14,15-22',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\t'shared': '/tmp/dfs',\n\n\t'blocklist': (\n\t\t#'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t#'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t'81:00.2', '81:00.3',\n\t\t'83:00.2', '83:00.3'\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[15:16].0',\n\t\t'[17:18].1',\n\t\t'[19:20].2',\n\t\t'[21:22].3'\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/dnet-echo.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t    '83:00.0', '83:00.1', '83:00.2', '83:00.3'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': (\n\t'sudo',\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15-18',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'blocklist': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t#'83:00.0', '83:00.1',\n\t\t'83:00.2', '83:00.3',\n\t\t'81:00.2', '81:00.3',\n\t\t'83:00.2', '83:00.3'\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[15:16].0',\n\t\t'[17:18].1',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t'loadfile': 'dnet-echo'\n\t}\n"
  },
  {
    "path": "cfg/four-ports.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '03:00.0',\n\t    '03:00.1',\n\t    '05:00.0',\n\t    '05:00.1',\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '1,2-4,5-7,8-10,11-13',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'allowlist': (\n\t\t'03:00.0',\n\t\t'03:00.1',\n\t\t'05:00.0',\n\t\t'05:00.1',\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t'-G',\n\t\t),\n\t'map': (\n\t\t'[2:3-4].0',\n\t\t'[5:6-7].1',\n\t\t'[8:9-10].2',\n\t\t'[11:12-13].3',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n#    'loadfile': 'test/portstats_with_delay.lua',\n#    'loadfile': 'test/portstats.lua',\n\t}\n"
  },
  {
    "path": "cfg/half-bond.cfg",
    "content": "description = 'Setup to use the bonding driver, but only one bond connected to non-bonded ports'\n\n# Setup configuration\nsetup = {\n    'exec': (\n        'sudo',\n\n        ),\n\t'devices': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'uio': 'vfio-pci'\n\t}\n\n# Run command and options\nrun = {\n    'exec': (\n        'sudo',\n\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '1,14-21,22-23',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\t'vdev': 'net_bonding0,mode=4,xmit_policy=l23,worker=0000:81:00.0,worker=0000:81:00.1,worker=0000:81:00.2,worker=0000:81:00.3',\n\n\t'blocklist': ( '81:00.0', '83:00.0' ),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[14:15].0',\n\t\t'[16:17].1',\n\t\t'[18:19].2',\n\t\t'[20:21].3',\n\t\t'[22:23].8',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/hs-fwd.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '03:00.0', '03:00.1',\n\t    '82:00.0'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '2,4-7,8-11,14-17',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'allowlist': (\n\t\t'03:00.0', '03:00.1',\n\t\t'82:00.0'\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t'-G',\n\t\t),\n\t'map': (\n\t\t'[4-5:6-7].0',\n\t\t'[8-9:10-11].1',\n\t\t'[14-15:16-17].2',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t'loadfile': 'hs-fwd'\n\t}\n"
  },
  {
    "path": "cfg/ioat.cfg",
    "content": "description = 'Slave configuration for two Pktgen\\'s running'\n\n# Setup configuration\nsetup = {\n    'exec': (\n        'sudo',\n\n        ),\n\t'devices': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'uio': 'vfio-pci'\n\t}\n\n# Run command and options\nrun = {\n    'exec': (\n        'sudo',\n\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15-18',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'ioat',\n\n\t'blocklist': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.2', '83:00.3',\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': ( '[15:16].0', '[17:18].1' ),\n\n\t'theme': 'themes/black-yellow.theme',\n\t}\n"
  },
  {
    "path": "cfg/lat.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '03:00.0', '03:00.1',\n\t    '82:00.0', '82:00.1'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': (\n\t'sudo',\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '2,4,6,8,10,14,16,18',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'allowlist': (\n\t\t'03:00.0', '03:00.1',\n\t\t'82:00.0', '82:00.1'\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t'-G',\n\t\t),\n\t'map': (\n\t\t'[4:6].0',\n\t\t'[8:10].1',\n\t\t'[14:16].2',\n\t\t'[18:20].3',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t'loadfile': 'cnet-fwd'\n\t}\n"
  },
  {
    "path": "cfg/lb-fwd.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '82:01.0', '82:01.1', '82:01.2'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '2,14-17,18-21,22-25',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'allowlist': (\n\t\t'82:01.0', '82:01.1', '82:01.2'\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t'-G',\n\t\t),\n\t'map': (\n\t\t'[14-15:16-17].0',\n\t\t'[18-19:20-21].1',\n\t\t'[22-23:24-25].2',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t'loadfile': 'lb-fwd'\n\t}\n"
  },
  {
    "path": "cfg/many-cores.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n        'sudo',\n\n        ),\n\n\t'devices': (\n\t\t'81:00.0',\n\t\t'83:00.0',\n\t\t),\n\n\t'uio': 'vfio-pci'\n\t}\n\n# Run command and options\nrun = {\n    'exec': (\n        'sudo',\n\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '1,14-23,42-51,24-27,52-55',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'blocklist': (\n\t\t'81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map':\t(\n\t\t'[14-23:42-51].0',\n\t\t'[24-27:52-55].1',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/multi-port.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t    '83:00.0', '83:00.1', '83:00.2', '83:00.3'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': (\n\t'sudo',\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15-26',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'blocklist': (\n\t\t#'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t#'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t'81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.1', '83:00.2', '83:00.3',\n\t\t'81:00.2', '81:00.3',\n\t\t'83:00.2', '83:00.3'\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[15-17:18-20].0',\n\t\t'[21-23:24-26].1',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/one-port.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '03:00.0',\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '1,2-4',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'allowlist': (\n\t\t'03:00.0',\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t'-G',\n        #'-j',\n\t\t),\n\t'map': (\n\t\t'[2:3-4].0',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t'loadfile': 'cnet-fwd'\n\t}\n"
  },
  {
    "path": "cfg/pcap.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t    '83:00.0', '83:00.1', '83:00.2', '83:00.3'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': (\n\t'sudo',\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15-22',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'blocklist': (\n\t\t#'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t#'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t'81:00.2', '81:00.3',\n\t\t'83:00.2', '83:00.3'\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[15:16].0',\n\t\t'[17:18].1',\n\t\t'[19:20].2',\n\t\t'[21:22].3'\n\t\t),\n\t'pcap': (\n\t\t'0:pcap/test1.pcap',\n\t\t'1:pcap/gtpv1-u-1024.pcap'\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/pdump.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '83:00.0', '83:00.1',\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': (\n\t'sudo',\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15-18',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'blocklist': (\n\t\t'81:00.0', '81:00.1',\n\t\t'81:00.2', '81:00.3',\n\t\t'83:00.2', '83:00.3',\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[15:16].0',\n\t\t'[17:18].1',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/pktgen-1.cfg",
    "content": "description = 'The initial side of two Pktgen\\'s running in the same system'\n\n# Setup configuration\nsetup = {\n    'exec': (\n        'sudo',\n\n        ),\n\t'devices': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'uio': 'vfio-pci'\n\t}\n\n# Run command and options\nrun = {\n    'exec': (\n        'sudo',\n\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,16-19',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg1',\n\n\t'blocklist': (\n\t\t'81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[16:17].0',\n\t\t'[18:19].1',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/pktgen-2.cfg",
    "content": "description = 'Slave configuration for two Pktgen\\'s running'\n\n# Setup configuration\nsetup = {\n    'exec': (\n        'sudo',\n\n        ),\n\t'devices': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'uio': 'vfio-pci'\n\t}\n\n# Run command and options\nrun = {\n    'exec': (\n        'sudo',\n\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '15,20-23',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg2',\n\n\t'blocklist': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.2', '83:00.3',\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[20:21].0',\n\t\t'[21:23].1'\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/server_memif.cfg",
    "content": "description = 'Using the MEMIF PMD this is the initial side'\n\n# Setup configuration\nsetup = {\n    'exec': (\n        'sudo',\n\n        ),\n\t'devices': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'uio': 'vfio-pci'\n\t}\n\n# Run command and options\nrun = {\n    'exec': (\n        'sudo',\n       ,\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg1',\n\t'vdev':\t'net_memif0,role=initial',\n\n\t'blocklist': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'15.0',\n\t\t),\n\n\t'theme': 'themes/black-white.theme'\n\t}\n"
  },
  {
    "path": "cfg/server_mif.cfg",
    "content": "description = 'Using the MIF PMD this is the initial side'\n\n# Setup configuration\nsetup = {\n    'exec': (\n        'sudo',\n\n        ),\n\t'devices': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'uio': 'vfio-pci'\n\t}\n\n# Run command and options\nrun = {\n    'exec': (\n        'sudo',\n       ,\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg1',\n\t'vdev': 'net_mif0,iftype=server',\n\n\t'blocklist': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'15.0',\n\t\t),\n\n\t'theme': 'themes/black-white.theme'\n\t}\n"
  },
  {
    "path": "cfg/socket.cfg",
    "content": "description = 'Slave configuration for two Pktgen\\'s running'\n\n# Setup configuration\nsetup = {\n    'exec': (\n        'sudo',\n\n        ),\n\t'devices': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'uio': 'vfio-pci'\n\t}\n\n# Run command and options\nrun = {\n    'exec': (\n        'sudo',\n\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15-16',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg2',\n\n\t'blocklist': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t#'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[15:16].0'\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/two-ports-shared.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t    '83:00.0', '83:00.1', '83:00.2', '83:00.3'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': (\n\t'sudo',\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15-18',\n\t'nrank': '4',\n\t'proc': 'primary',\n\t'log': '7',\n\t'prefix': 'pg',\n\t'plugin': (\n\t\t'/tmp/sofiles/librte_mempool_ring.so.1.1',\n\t\t'/tmp/sofiles/librte_pmd_i40e.so.2.1'\n\t\t),\n\n\t'blocklist': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t#'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t'83:00.2', '83:00.3'\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[15:16].0',\n\t\t'[17:18].1'\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/two-ports.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t    '83:00.0', '83:00.1', '83:00.2', '83:00.3'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'ld_path': (\n\t ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15-18',\n\t'nrank': '4',\n\t'proc': 'primary',\n\t'log': '7',\n\t'prefix': 'pg',\n\t'plugin': (\n\t\t),\n\n\t'blocklist': (\n\t\t'81:00.0', '81:00.1', '81:00.2', '81:00.3',\n\t\t#'83:00.0', '83:00.1', '83:00.2', '83:00.3',\n\t\t'83:00.2', '83:00.3'\n\t\t),\n\n\t'opts': (\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[15:16].0',\n\t\t'[17:18].1'\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "cfg/tx_perf.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '82:00.0',\n\t    '82:00.1',\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '14,15-18',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'allowlist': (\n\t\t'82:00.0',\n\t\t#'82:00.1',\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t'-G',\n        #'-c',\n        #'-j',\n\t\t),\n\t'map': (\n\t\t'[15:16-17].0',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t'loadfile': 'tx_perf'\n\t}\n"
  },
  {
    "path": "cfg/vfio-fwd.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '82:02.0', '82:02.1'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n\t\t'/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '2,14-17,18-21',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'allowlist': (\n\t\t'82:02.0', '82:02.1'\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t'-G',\n\t\t),\n\t'map': (\n\t\t'[14-15:16-17].0',\n\t\t'[18-19:20-21].1',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t'loadfile': 'vfio-fwd'\n\t}\n"
  },
  {
    "path": "cfg/xdp-100.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '86:00.0'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n        ),\n\n\t'cores': '28,29-30',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'blocklist': (\n\t\t),\n\t'allowlist': (\n\t\t'86:00.0'\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[29:30].0'\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t'loadfile': 'xdp-100G',\n\t#'shared': '/usr/local/lib/x86_64-linux-gnu/dpdk/pmds-21.1'\n\t}\n"
  },
  {
    "path": "cfg/xdp-40.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '5e:00.0',\n\t    '5e:00.1'\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': ('sudo',),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n\t\t'./usr/local/bin/%(app_name)s',\n        ),\n\n\t'cores': '10,11-12,13-14',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'blocklist': (\n\t\t),\n\t'allowlist': (\n\t\t'5e:00.0',\n\t\t'5e:00.1',\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[11:12].0',\n\t\t'[13:14].1',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme',\n\t'loadfile': 'xdp-40G',\n\t#'shared': '/usr/local/lib/x86_64-linux-gnu/dpdk/pmds-21.1'\n\t}\n"
  },
  {
    "path": "cfg/xl710.cfg",
    "content": "description = 'A Pktgen default simple configuration'\n\n# Setup configuration\nsetup = {\n    'exec': (\n\t'sudo',\n        ),\n\n    'devices': (\n\t    '81:00.0', '83:00.0',\n\t    ),\n    # UIO module type, igb_uio, vfio-pci or uio_pci_generic\n    'uio': 'vfio-pci'\n    }\n\n# Run command and options\nrun = {\n    'exec': (\n\t'sudo',\n        ),\n\n    # Application name and use app_path to help locate the app\n    'app_name': 'pktgen',\n\n    # using (sdk) or (target) for specific variables\n    # add (app_name) of the application\n    # Each path is tested for the application\n    'app_path': (\n        './usr/local/bin/%(app_name)s',\n        '/usr/local/bin/%(app_name)s'\n        ),\n\n\t'cores': '1,14-27',\n\t'nrank': '4',\n\t'proc': 'auto',\n\t'log': '7',\n\t'prefix': 'pg',\n\n\t'blocklist': (\n\t\t#'81:00.0', '83:00.0',\n\t\t'85:00.0', '85:00.1', '85:00.2', '85:00.3',\n\t\t),\n\n\t'opts': (\n\t\t'-v',\n\t\t'-T',\n\t\t'-P',\n\t\t),\n\t'map': (\n\t\t'[14:15-20].0',\n\t\t'[21-26:27].1',\n\t\t),\n\n\t'theme': 'themes/black-yellow.theme'\n\t}\n"
  },
  {
    "path": "docs/LUA_API.md",
    "content": "# Pktgen Lua API (selected)\n\nThis document describes the Lua functions exported by the `pktgen` module that were recently updated/clarified, and the table shapes they return.\n\n## Conventions\n\n- Most functions return a Lua table keyed by numeric port id (`pid`).\n- Many returned tables include an `n` field holding the number of ports in the returned table.\n- Unless explicitly stated, tables may contain additional fields beyond what is documented here.\n\n## `pktgen.portStats(portlist, type)`\n\nReturns per-port statistics.\n\n- `portlist`: port list string understood by pktgen (e.g. `\"0\"`, `\"0-3\"`, `\"0,2\"`).\n\n### Signature\n\n`pktgen.portStats(portlist)`\n\n### Return value\n\nA table with:\n\n- Numeric keys: one entry per port id.\n- `n`: number of ports returned.\n\nEach per-port entry contains:\n\nThe per-port entry table mirrors a subset of the C `port_stats_t` structure.\n\n### Per-port table layout\n\nEach port entry contains:\n\n- `curr`: a table containing:\n  - `ipackets`, `opackets`, `ibytes`, `obytes`\n  - `ierrors`, `oerrors`, `rx_nombuf`, `imissed`\n- `sizes`: packet size distribution:\n  - `_64`, `_65_127`, `_128_255`, `_256_511`, `_512_1023`, `_1024_1522`\n  - `broadcast`, `multicast`, `jumbo`, `runt`, `unknown`\n- `qstats`: per-queue tables keyed by queue id.\n  - The number of entries is limited to the number of **configured Rx queues** for the port.\n  - Queue ids start at `0`.\n  - Each queue entry has:\n  - `ipackets`, `opackets`, `errors`\n\n### Example\n\n```lua\n-- Get the current port stats snapshot for ports 0-1\nlocal t = pktgen.portStats(\"0-1\")\n\nfor pid = 0, 1 do\n  print(\"port\", pid, \"curr rx\", t[pid].curr.ipackets)\n  print(\"port\", pid, \"curr tx\", t[pid].curr.opackets)\nend\n```\n\n## `pktgen.portInfo(portlist)`\n\nReturns per-port configuration and informational fields.\n\nNotes:\n\n- This API intentionally does **not** include `port_stats_t`.\n- This API intentionally does **not** include `rte_eth_stats` (`stats`/`totals` style counters).\n\n### Return value\n\nA table with:\n\n- Numeric keys: one entry per port id.\n- `n`: number of ports returned.\n\nEach per-port entry includes a set of informational subtables (non-exhaustive):\n\n- `total_stats`: pktgen global totals (e.g. `max_ipackets`, `max_opackets`, cumulative rate totals)\n- `info`: transmit configuration (pattern type, tx_count, tx_rate, pkt_size, bursts, eth/proto type, vlanid)\n- `l2_l3_info`: L2/L3 fields (ports, TTL, src/dst IP, src/dst MAC)\n- `tx_debug`: internal tx debug fields\n- `802.1p`: QoS fields (`cos`, `dscp`, `ipp`)\n- `VxLAN`: VxLAN fields and `link_state`\n- `pci_vendor`: PCI/vendor string (top-level field, if available)\n\n### Example\n\n```lua\nlocal info = pktgen.portInfo(\"0\")\nprint(\"link\", info[0].VxLAN.link_state)\nprint(\"tx rate\", info[0].info.tx_rate)\n```\n\n## Example output of scripts/port_stats_dump.lua\n\n```lua\nPktgen> load scripts/port_stats_dump.lua\nPktgen:/> load scripts/port_stats_dump.lua\n\nExecuting 'scripts/port_stats_dump.lua'\npktgen.portStats(\"0-1\")\n{\n  [0] =\n  {\n    [\"curr\"] =\n    {\n      [\"ibytes\"] = 0\n      [\"ierrors\"] = 0\n      [\"imissed\"] = 0\n      [\"ipackets\"] = 0\n      [\"obytes\"] = 0\n      [\"oerrors\"] = 0\n      [\"opackets\"] = 0\n      [\"rx_nombuf\"] = 0\n    }\n    [\"qstats\"] =\n    {\n      [0] =\n      {\n        [\"errors\"] = 0\n        [\"ipackets\"] = 0\n        [\"opackets\"] = 0\n      }\n      [1] =\n      {\n        [\"errors\"] = 0\n        [\"ipackets\"] = 0\n        [\"opackets\"] = 0\n      }\n    }\n    [\"sizes\"] =\n    {\n      [\"_1024_1522\"] = 0\n      [\"_128_255\"] = 0\n      [\"_256_511\"] = 0\n      [\"_512_1023\"] = 0\n      [\"_64\"] = 0\n      [\"_65_127\"] = 0\n      [\"broadcast\"] = 0\n      [\"jumbo\"] = 0\n      [\"multicast\"] = 0\n      [\"runt\"] = 0\n      [\"unknown\"] = 0\n    }\n  }\n  [1] =\n  {\n    [\"curr\"] =\n    {\n      [\"ibytes\"] = 0\n      [\"ierrors\"] = 0\n      [\"imissed\"] = 0\n      [\"ipackets\"] = 0\n      [\"obytes\"] = 0\n      [\"oerrors\"] = 0\n      [\"opackets\"] = 0\n      [\"rx_nombuf\"] = 0\n    }\n    [\"qstats\"] =\n    {\n      [0] =\n      {\n        [\"errors\"] = 0\n        [\"ipackets\"] = 0\n        [\"opackets\"] = 0\n      }\n      [1] =\n      {\n        [\"errors\"] = 0\n        [\"ipackets\"] = 0\n        [\"opackets\"] = 0\n      }\n    }\n    [\"sizes\"] =\n    {\n      [\"_1024_1522\"] = 0\n      [\"_128_255\"] = 0\n      [\"_256_511\"] = 0\n      [\"_512_1023\"] = 0\n      [\"_64\"] = 0\n      [\"_65_127\"] = 0\n      [\"broadcast\"] = 0\n      [\"jumbo\"] = 0\n      [\"multicast\"] = 0\n      [\"runt\"] = 0\n      [\"unknown\"] = 0\n    }\n  }\n  [\"n\"] = 2\n}\n```\n"
  },
  {
    "path": "docs/QUICKSTART.md",
    "content": "# Pktgen Quick Start\n\nThis guide provides a concise path from a clean system to generating traffic with Pktgen.\n\n## 1. Prerequisites\n\n- A modern Linux distribution (tested on Ubuntu 22.04+)\n- Root (or sudo) access\n- Git, build-essential (gcc/clang, make), Python 3\n- Meson & Ninja: `pip install meson ninja` (or distro packages)\n- libbsd headers: `sudo apt install libbsd-dev`\n- Huge pages configured (e.g. 1G or 2M pages) and IOMMU enabled if using `vfio-pci`\n- Latest DPDK source (<https://www.dpdk.org/>)\n\n## 2. Build and Install DPDK\n\n```bash\ngit clone https://github.com/DPDK/dpdk.git\ncd dpdk\nmeson setup build\nmeson compile -C build\nsudo meson install -C build\nsudo ldconfig\n```\n\nIf `libdpdk.pc` is placed under a non-standard path (commonly `/usr/local/lib/x86_64-linux-gnu/pkgconfig`), export:\n\n```bash\nexport PKG_CONFIG_PATH=/usr/local/lib/x86_64-linux-gnu/pkgconfig:$PKG_CONFIG_PATH\n```\n\n## 3. Clone and Build Pktgen\n\n```bash\ngit clone https://github.com/pktgen/Pktgen-DPDK.git\ncd Pktgen-DPDK\nmeson setup builddir\nmeson compile -C builddir\n```\n\n(Optional) Using Make wrapper:\n\n```bash\nmake        # builds via meson/ninja\nmake rebuild\nmake rebuildlua\n```\n\n## 4. Device Preparation\n\nBind NIC ports for DPDK use (one-time per boot). You can use `./tools/run.py -s <cfg>` or standard DPDK binding tools. Example with run script:\n\n```bash\nsudo ./tools/run.py -s default\n```\n\nManual invocation example (adjust cores & ports):\n\n## 5. Launch Pktgen\n\nUsing a configuration file:\n\n```bash\nsudo ./builddir/app/pktgen -l 0-3 -n 4 -- -P -m \"[1:2].0\" -T\n```\n\nWhere:\n\n- `-l 0-3` lcore list (control + workers)\n- `-n 4` memory channels\n- After `--` are Pktgen options; `-P` promiscuous, `-m` maps core pairs to port, `-T` enables themed/color output\n\n## 6. Basic Console Commands\n\nInside the interactive console (`Pktgen>` prompt):\n\n```text\nhelp                 # list commands\nport 0               # focus on port 0\nset 0 size 512       # change packet size\nstart 0              # start transmitting on port 0\nstop 0               # stop transmitting\npage stats           # view statistics page\nsave test.lua        # save current configuration to lua script\n\nRemote execution via socket (default port 22022):\n```\n\n## 7. Lua Scripting\n\nRun a script at startup:\n\n```bash\necho \"print(pktgen.info.Pktgen_Version)\" | socat - TCP4:localhost:22022\n```\n\n## 8. Performance Tips\n\n- Pin isolated cores (`isolcpus=` kernel parameter or `taskset`)\n- Keep NIC and memory allocation on the same NUMA node\n- Ensure sufficient mbufs (configured in source or options)\n- Disable power management / frequency scaling for consistent latency\n- Use latest stable DPDK + recent CPU microcode\n\n## 9. Troubleshooting\n\n| Missing `libdpdk.so` | `ldconfig` not updated | Run `sudo ldconfig` or adjust `/etc/ld.so.conf.d` |\n| Permission denied | IOMMU / vfio not enabled | Add `intel_iommu=on` (or `amd_iommu=on`) to GRUB and reboot |\n| Low performance | NUMA mismatch / core sharing | Reassign cores & ensure local memory |\n| Socket connect fails | Port blocked/firewall | Check `22022` listening, adjust firewall |\n\n## 10. Next Steps\n\n- Explore `cfg/` configs for multi-port examples\n- Use `page range` or `page seq` for more advanced packet patterns\n- Capture traffic with Wireshark/tcpdump to validate packet contents\n\n---\nFor deeper details refer to the main README or full documentation site.\n"
  },
  {
    "path": "docs/STYLE.md",
    "content": "# Documentation & Markdown Style Guide\n\nThis guide summarizes the conventions enforced (and encouraged) for all Markdown and docs in this repository.\nIt complements `CONTRIBUTING.md` and the root `.markdownlint.yml` configuration.\n\n## 1. Linting\n\n- All Markdown must pass `markdownlint` (locally via the pre-commit hook and in CI).\n- Disabled rule: MD013 (line length) – long lines allowed where readability (tables / URLs / command lines) benefits.\n- Do not locally override rules unless absolutely necessary; prefer restructuring content.\n\n## 2. Headings\n\n- Use ATX (`#`, `##`, `###`) style only.\n- Start at a single `#` per file (title) and increment by one level; avoid skipping levels.\n- Surround each heading with exactly one blank line above and below.\n- Avoid trailing punctuation (no final colon, period, or question mark) unless part of a proper noun.\n\n## 3. Lists\n\n- Ordered lists: use `1.` for every item (markdownlint will auto-render numbers).\n- Unordered lists: use `-` (not `*` or `+`).\n- Put a blank line before and after a list block (unless tightly bound to a parent list item sub-block).\n- Indent nested list content by two spaces.\n\n## 4. Code Blocks\n\n- Always use fenced blocks (backticks). Indented code blocks are not allowed.\n- Supply a language hint whenever possible (`bash`, `python`, `lua`, `text`, `console`).\n- Surround fenced blocks with one blank line before and after.\n- For shell commands, omit the leading `$` unless demonstrating interactive copy/paste prevention; if shown, also show output to satisfy MD014.\n- Use `text` for pseudo-code or command output with mixed content.\n\n## 5. Inline Formatting\n\n- Wrap bare URLs in angle brackets `<https://example.com>`.\n- Use backticks for filenames, commands, CLI flags, environment variables, and literal code tokens.\n- Prefer emphasis (`*italic*`) sparingly; bold only for strong warnings or section callouts.\n\n## 6. Tables\n\n- Surround tables with one blank line above and below.\n- Keep header separator row aligned but do not over-focus on spacing (lint does not require column width alignment).\n- Use backticks for code-like cell content.\n\n## 7. Blockquotes\n\n- No blank lines inside a single logical blockquote group.\n- Each quoted paragraph or list line should start with `>`.\n- Use blockquotes for external notices, important upstream references, or migration notes—not for styling.\n\n## 8. Line Length & Wrapping\n\n- Long lines are acceptable (MD013 disabled). Do not hard-wrap paragraphs unless clarity significantly improves.\n- Keep tables and link references on single lines when feasible.\n\n## 9. File Naming & Placement\n\n- Use `ALL_CAPS.md` for top-level meta-docs (README, LICENSE, CONTRIBUTING, INSTALL).\n- Place style / guide / topic-specific docs under `docs/`.\n- Use descriptive filenames (e.g. `QUICKSTART.md`, `STYLE.md`).\n\n## 10. Tone & Clarity\n\n- Prefer imperative, concise wording (\"Run the script\" vs. \"You should run\").\n- Avoid marketing language—be factual and actionable.\n- Provide context before commands; describe what a block does if non-obvious.\n\n## 11. Common Patterns\n\n| Pattern | Example |\n|---------|---------|\n| Command sequence | `meson setup builddir && meson compile -C builddir` |\n| Config file snippet | ``setup = { 'devices': ('81:00.0',) }`` |\n| Shell pipeline | ``echo \\\"test\\\" \\| socat - TCP4:localhost:22022`` |\n| Lua script | ``printf(\"Hello!\\n\")`` |\n| Output capture | ``Pktgen Version: 25.08.0`` |\n\n## 12. Adding New Docs\n\n- Link new end-user docs from the main `README.md` or an appropriate existing doc section.\n- Keep `QUICKSTART.md` focused—avoid duplicating deep details already covered elsewhere.\n- When adding a new feature, include: purpose, quick example, limitations, and any performance considerations.\n\n## 13. Do / Avoid Summary\n\n| Do | Avoid |\n|----|-------|\n| Provide a runnable example for new features | Giant monolithic examples without explanation |\n| Use consistent fenced code | Mixing indentation and fenced styles |\n| Reference existing sections instead of duplicating content | Copy-pasting large README segments |\n| Keep tables compact and scannable | Over-formatting table spacing |\n| Add language hints to all code blocks | Leaving unlabeled fences |\n\n## 14. Exceptions\n\nRare cases (generated content, pasted command output, license headers) may intentionally violate some guidelines—these should be minimal and explained in a comment or preceding sentence.\n\n---\nQuestions or suggestions? Update this file or open an issue.\n"
  },
  {
    "path": "docs/api/doxy-api-index.md",
    "content": "API\n===\n\n<!--\n  SPDX-License-Identifier: BSD-3-Clause\n  Copyright(c) 2013-2017 6WIND S.A.\n-->\n\nThe public API headers are grouped by topics:\n\n- **device**:\n  [dev]                (@ref rte_dev.h),\n  [ethdev]             (@ref rte_ethdev.h),\n  [ethctrl]            (@ref rte_eth_ctrl.h),\n  [rte_flow]           (@ref rte_flow.h),\n  [rte_tm]             (@ref rte_tm.h),\n  [rte_mtr]            (@ref rte_mtr.h),\n  [bbdev]              (@ref rte_bbdev.h),\n  [cryptodev]          (@ref rte_cryptodev.h),\n  [security]           (@ref rte_security.h),\n  [compressdev]        (@ref rte_compressdev.h),\n  [compress]           (@ref rte_comp.h),\n  [eventdev]           (@ref rte_eventdev.h),\n  [event_eth_rx_adapter]   (@ref rte_event_eth_rx_adapter.h),\n  [event_eth_tx_adapter]   (@ref rte_event_eth_tx_adapter.h),\n  [event_timer_adapter]    (@ref rte_event_timer_adapter.h),\n  [event_crypto_adapter]   (@ref rte_event_crypto_adapter.h),\n  [rawdev]             (@ref rte_rawdev.h),\n  [metrics]            (@ref rte_metrics.h),\n  [bitrate]            (@ref rte_bitrate.h),\n  [latency]            (@ref rte_latencystats.h),\n  [devargs]            (@ref rte_devargs.h),\n  [PCI]                (@ref rte_pci.h),\n  [vdev]               (@ref rte_bus_vdev.h),\n  [vfio]               (@ref rte_vfio.h)\n\n- **device specific**:\n  [softnic]            (@ref rte_eth_softnic.h),\n  [bond]               (@ref rte_eth_bond.h),\n  [vhost]              (@ref rte_vhost.h),\n  [vdpa]               (@ref rte_vdpa.h),\n  [KNI]                (@ref rte_kni.h),\n  [ixgbe]              (@ref rte_pmd_ixgbe.h),\n  [i40e]               (@ref rte_pmd_i40e.h),\n  [ice]                (@ref rte_pmd_ice.h),\n  [bnxt]               (@ref rte_pmd_bnxt.h),\n  [dpaa]               (@ref rte_pmd_dpaa.h),\n  [dpaa2]              (@ref rte_pmd_dpaa2.h),\n  [dpaa2_mempool]      (@ref rte_dpaa2_mempool.h),\n  [dpaa2_cmdif]        (@ref rte_pmd_dpaa2_cmdif.h),\n  [dpaa2_qdma]         (@ref rte_pmd_dpaa2_qdma.h),\n  [crypto_scheduler]   (@ref rte_cryptodev_scheduler.h)\n\n- **memory**:\n  [memseg]             (@ref rte_memory.h),\n  [memzone]            (@ref rte_memzone.h),\n  [mempool]            (@ref rte_mempool.h),\n  [malloc]             (@ref rte_malloc.h),\n  [memcpy]             (@ref rte_memcpy.h)\n\n- **timers**:\n  [cycles]             (@ref rte_cycles.h),\n  [timer]              (@ref rte_timer.h),\n  [alarm]              (@ref rte_alarm.h)\n\n- **locks**:\n  [atomic]             (@ref rte_atomic.h),\n  [mcslock]            (@ref rte_mcslock.h),\n  [rwlock]             (@ref rte_rwlock.h),\n  [spinlock]           (@ref rte_spinlock.h),\n  [ticketlock]         (@ref rte_ticketlock.h),\n  [RCU]                (@ref rte_rcu_qsbr.h)\n\n- **CPU arch**:\n  [branch prediction]  (@ref rte_branch_prediction.h),\n  [cache prefetch]     (@ref rte_prefetch.h),\n  [SIMD]               (@ref rte_vect.h),\n  [byte order]         (@ref rte_byteorder.h),\n  [CPU flags]          (@ref rte_cpuflags.h),\n  [CPU pause]          (@ref rte_pause.h),\n  [I/O access]         (@ref rte_io.h)\n\n- **CPU multicore**:\n  [interrupts]         (@ref rte_interrupts.h),\n  [launch]             (@ref rte_launch.h),\n  [lcore]              (@ref rte_lcore.h),\n  [per-lcore]          (@ref rte_per_lcore.h),\n  [service cores]      (@ref rte_service.h),\n  [keepalive]          (@ref rte_keepalive.h),\n  [power/freq]         (@ref rte_power.h)\n\n- **layers**:\n  [ethernet]           (@ref rte_ether.h),\n  [ARP]                (@ref rte_arp.h),\n  [HIGIG]              (@ref rte_higig.h),\n  [ICMP]               (@ref rte_icmp.h),\n  [ESP]                (@ref rte_esp.h),\n  [IPsec]              (@ref rte_ipsec.h),\n  [IPsec group]        (@ref rte_ipsec_group.h),\n  [IPsec SA]           (@ref rte_ipsec_sa.h),\n  [IPsec SAD]          (@ref rte_ipsec_sad.h),\n  [IP]                 (@ref rte_ip.h),\n  [SCTP]               (@ref rte_sctp.h),\n  [TCP]                (@ref rte_tcp.h),\n  [UDP]                (@ref rte_udp.h),\n  [GTP]                (@ref rte_gtp.h),\n  [GRO]                (@ref rte_gro.h),\n  [GSO]                (@ref rte_gso.h),\n  [frag/reass]         (@ref rte_ip_frag.h),\n  [LPM IPv4 route]     (@ref rte_lpm.h),\n  [LPM IPv6 route]     (@ref rte_lpm6.h),\n  [VXLAN]              (@ref rte_vxlan.h)\n\n- **QoS**:\n  [metering]           (@ref rte_meter.h),\n  [scheduler]          (@ref rte_sched.h),\n  [RED congestion]     (@ref rte_red.h)\n\n- **hashes**:\n  [hash]               (@ref rte_hash.h),\n  [jhash]              (@ref rte_jhash.h),\n  [thash]              (@ref rte_thash.h),\n  [FBK hash]           (@ref rte_fbk_hash.h),\n  [CRC hash]           (@ref rte_hash_crc.h)\n\n- **classification**\n  [reorder]            (@ref rte_reorder.h),\n  [distributor]        (@ref rte_distributor.h),\n  [EFD]                (@ref rte_efd.h),\n  [ACL]                (@ref rte_acl.h),\n  [member]             (@ref rte_member.h),\n  [flow classify]      (@ref rte_flow_classify.h),\n  [BPF]                (@ref rte_bpf.h)\n\n- **containers**:\n  [mbuf]               (@ref rte_mbuf.h),\n  [mbuf pool ops]      (@ref rte_mbuf_pool_ops.h),\n  [ring]               (@ref rte_ring.h),\n  [stack]              (@ref rte_stack.h),\n  [tailq]              (@ref rte_tailq.h),\n  [bitmap]             (@ref rte_bitmap.h)\n\n- **packet framework**:\n  * [port]             (@ref rte_port.h):\n    [ethdev]           (@ref rte_port_ethdev.h),\n    [ring]             (@ref rte_port_ring.h),\n    [frag]             (@ref rte_port_frag.h),\n    [reass]            (@ref rte_port_ras.h),\n    [sched]            (@ref rte_port_sched.h),\n    [kni]              (@ref rte_port_kni.h),\n    [src/sink]         (@ref rte_port_source_sink.h)\n  * [table]            (@ref rte_table.h):\n    [lpm IPv4]         (@ref rte_table_lpm.h),\n    [lpm IPv6]         (@ref rte_table_lpm_ipv6.h),\n    [ACL]              (@ref rte_table_acl.h),\n    [hash]             (@ref rte_table_hash.h),\n    [array]            (@ref rte_table_array.h),\n    [stub]             (@ref rte_table_stub.h)\n  * [pipeline]         (@ref rte_pipeline.h)\n    [port_in_action]   (@ref rte_port_in_action.h)\n    [table_action]     (@ref rte_table_action.h)\n  * [graph]            (@ref rte_graph.h):\n    [graph_worker]     (@ref rte_graph_worker.h)\n  * graph_nodes:\n    [eth_node]         (@ref rte_node_eth_api.h),\n    [ip4_node]         (@ref rte_node_ip4_api.h)\n\n- **basic**:\n  [approx fraction]    (@ref rte_approx.h),\n  [random]             (@ref rte_random.h),\n  [config file]        (@ref rte_cfgfile.h),\n  [key/value args]     (@ref rte_kvargs.h),\n  [string]             (@ref rte_string_fns.h)\n\n- **debug**:\n  [jobstats]           (@ref rte_jobstats.h),\n  [telemetry]          (@ref rte_telemetry.h),\n  [pdump]              (@ref rte_pdump.h),\n  [hexdump]            (@ref rte_hexdump.h),\n  [debug]              (@ref rte_debug.h),\n  [log]                (@ref rte_log.h),\n  [errno]              (@ref rte_errno.h),\n  [trace]              (@ref rte_trace.h),\n  [trace_point]        (@ref rte_trace_point.h)\n\n- **misc**:\n  [EAL config]         (@ref rte_eal.h),\n  [common]             (@ref rte_common.h),\n  [experimental APIs]  (@ref rte_compat.h),\n  [ABI versioning]     (@ref rte_function_versioning.h),\n  [version]            (@ref rte_version.h)\n"
  },
  {
    "path": "docs/api/doxy-api.conf.in",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright 2019-2020 Intel Corperation Inc.\n\nPROJECT_NAME            = Pktgen-DPDK\nPROJECT_NUMBER          = @VERSION@\nUSE_MDFILE_AS_MAINPAGE  = @TOPDIR@/docs/api/doxy-api-index.md\nINPUT                   = @TOPDIR@/docs/api/doxy-api-index.md \\\n                          @TOPDIR@/lib/common \\\n                          @TOPDIR@/lib/cli \\\n                          @TOPDIR@/lib/lua \\\n                          @TOPDIR@/lib/plugin \\\n                          @TOPDIR@/lib/utils \\\n                          @TOPDIR@/lib/vec \\\n                          @TOPDIR@/app\nINPUT                   += @API_EXAMPLES@\nFILE_PATTERNS           = pktgen-*.h\nPREDEFINED              = __DOXYGEN__ \\\n\t\t\t   VFIO_PRESENT \\\n                          __attribute__(x)=\n\nOPTIMIZE_OUTPUT_FOR_C   = YES\nENABLE_PREPROCESSING    = YES\nMACRO_EXPANSION         = YES\nEXPAND_ONLY_PREDEF      = YES\nEXTRACT_STATIC          = YES\nDISTRIBUTE_GROUP_DOC    = YES\nHIDE_UNDOC_MEMBERS      = YES\nHIDE_UNDOC_CLASSES      = YES\nHIDE_SCOPE_NAMES        = YES\nGENERATE_DEPRECATEDLIST = YES\nVERBATIM_HEADERS        = NO\nALPHABETICAL_INDEX      = NO\n\nHTML_DYNAMIC_SECTIONS   = YES\nSEARCHENGINE            = YES\nSORT_MEMBER_DOCS        = NO\nSOURCE_BROWSER          = YES\n\nEXAMPLE_PATH            = @TOPDIR@/examples\nEXAMPLE_PATTERNS        = *.c\nEXAMPLE_RECURSIVE       = YES\n\nOUTPUT_DIRECTORY        = @OUTPUT@\nSTRIP_FROM_PATH         = @STRIP_FROM_PATH@\nGENERATE_HTML           = YES\nHTML_OUTPUT             = @HTML_OUTPUT@\nGENERATE_LATEX          = NO\nGENERATE_MAN            = NO\n\nHAVE_DOT                = NO\n"
  },
  {
    "path": "docs/api/doxy-html-custom.sh",
    "content": "#! /bin/sh -e\n# SPDX-License-Identifier: BSD-3-Clause\n# Copyright 2013 6WIND S.A.\n\nCSS=$1\n\n# space between item and its comment\necho 'dd td:first-child {padding-right: 2em;}' >> $CSS\n"
  },
  {
    "path": "docs/api/generate_doxygen.sh",
    "content": "#! /bin/sh -e\n# SPDX-License-Identifier: BSD-3-Clause\n# Copyright 2018 Luca Boccassi <bluca@debian.org>\n\nDOXYCONF=$1\nOUTDIR=$2\nSCRIPTCSS=$3\n\n# run doxygen, capturing all the header files it processed\ndoxygen \"${DOXYCONF}\" | tee doxygen.out\necho \"$OUTDIR: $(awk '/Preprocessing/ {printf(\"%s \", substr($2, 1, length($2) - 3))}' doxygen.out)\" > $OUTDIR.d\n\n\"${SCRIPTCSS}\" \"${OUTDIR}\"/doxygen.css\n"
  },
  {
    "path": "docs/api/generate_examples.sh",
    "content": "#! /bin/sh -e\n# SPDX-License-Identifier: BSD-3-Clause\n# Copyright 2018 Luca Boccassi <bluca@debian.org>\n\nEXAMPLES_DIR=$1\nAPI_EXAMPLES=$2\n\n# generate a .d file including both C files and also build files, so we can\n# detect both file changes and file additions/deletions\necho \"$API_EXAMPLES: $(find ${EXAMPLES_DIR} -type f \\( -name '*.c' -o -name 'meson.build' \\) -printf '%p ' )\" > ${API_EXAMPLES}.d\n\nexec > \"${API_EXAMPLES}\"\nprintf '/**\\n'\nprintf '@page examples DPDK Example Programs\\n\\n'\nfind \"${EXAMPLES_DIR}\" -type f -name '*.c' -printf '@example examples/%P\\n' | LC_ALL=C sort\nprintf '*/\\n'\n"
  },
  {
    "path": "docs/api/meson.build",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) 2018 Luca Boccassi <bluca@debian.org>\n\ndoxygen = find_program('doxygen', required: get_option('enable_docs'))\n\nif not doxygen.found()\n  subdir_done()\nendif\n\n# due to the CSS customisation script, which needs to run on a file that\n# is in a subdirectory that is created at build time and thus it cannot\n# be an individual custom_target, we need to wrap the doxygen call in a\n# script to run the CSS modification afterwards\ngenerate_doxygen = find_program('generate_doxygen.sh')\ngenerate_examples = find_program('generate_examples.sh')\ngenerate_css = find_program('doxy-html-custom.sh')\n\nhtmldir = join_paths(get_option('datadir'), 'docs', 'dpdk')\n\n# due to the following bug: https://github.com/mesonbuild/meson/issues/4107\n# if install is set to true it will override build_by_default and it will\n# cause the target to always be built. If install were to be always set to\n# false it would be impossible to install the docs.\n# So use a configure option for now.\nexample = custom_target('examples.dox',\n\toutput: 'examples.dox',\n\tcommand: [generate_examples, join_paths(meson.project_source_root(), 'examples'), '@OUTPUT@'],\n\tdepfile: 'examples.dox.d',\n\tinstall: get_option('enable_docs'),\n\tinstall_dir: htmldir,\n\tbuild_by_default: get_option('enable_docs'))\n\ncdata = configuration_data()\ncdata.set('VERSION', meson.project_version())\ncdata.set('API_EXAMPLES', join_paths(meson.project_build_root(), 'docs', 'api', 'examples.dox'))\ncdata.set('OUTPUT', join_paths(meson.project_build_root(), 'docs', 'api'))\ncdata.set('HTML_OUTPUT', 'api')\ncdata.set('TOPDIR', meson.project_source_root())\ncdata.set('STRIP_FROM_PATH', meson.project_source_root())\n\ndoxy_conf = configure_file(input: 'doxy-api.conf.in',\n\toutput: 'doxy-api.conf',\n\tconfiguration: cdata)\n\ndoxy_build = custom_target('doxygen',\n\tdepends: example,\n\tinput: doxy_conf,\n\toutput: 'api',\n\tdepfile: 'api.d',\n\tcommand: [generate_doxygen, '@INPUT@', '@OUTPUT@', generate_css],\n\tinstall: get_option('enable_docs'),\n\tinstall_dir: htmldir,\n\tbuild_by_default: get_option('enable_docs'))\n\ndoc_targets += doxy_build\ndoc_target_names += 'Doxygen_API'\n"
  },
  {
    "path": "docs/meson.build",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2019-2026> Intel Corporation\n\ndoc_targets = []\ndoc_target_names = []\n\n# Take out API doxygen for now.\nsubdir('api')\n\nsubdir('source')\n\nif doc_targets.length() == 0\n    message = 'No docs targets found'\nelse\n    message = 'Building docs:'\nendif\nrun_target('docs', command: ['echo', message, doc_target_names],\n    depends: doc_targets)\n"
  },
  {
    "path": "docs/source/changes.rst",
    "content": ":tocdepth: 1\n\n.. _changes:\n\nChanges in Pktgen\n=================\n\nThis section previously embedded the raw `changelog.txt` file.\n\nThe standalone changelog file has been removed per project direction. For historical release notes and detailed change history, please refer to the GitHub Releases page:\n\nhttps://github.com/pktgen/Pktgen-DPDK/releases\n\nOlder point-in-time changes may also be visible in commit history using:\n\n```\ngit log --oneline --decorate --graph\n```\n"
  },
  {
    "path": "docs/source/cli_design.rst",
    "content": "CLI design notes (lib/cli)\n==========================\n\nThis page documents the design and usage of the CLI library shipped in this\nrepository under ``lib/cli``.\n\nFor a developer-focused walkthrough (with examples and pointers to the\nimplementation), see the in-tree document:\n\n* ``lib/cli/DESIGN.md``\n\nWhen the optional ``myst_parser`` Sphinx extension is available, the Markdown\nsource can also be included directly in the documentation build.\n\nOverview\n--------\n\nThe CLI is a small shell-like environment with:\n\n* A directory-like node tree (commands, files, aliases, directories)\n* History and line editing\n* TAB completion\n* Optional map-driven parsing for command variants\n\nKey ideas\n---------\n\n* The active CLI instance is stored in TLS as ``this_cli``.\n* The command tree is made of ``struct cli_node`` entries.\n* Commands are implemented as ``int cmd(int argc, char **argv)`` callbacks.\n* Command variants can be selected by matching argv against a ``struct cli_map[]``\n  table via ``cli_mapping()``.\n\nAuto-complete\n-------------\n\nTAB completion is primarily shell-like (commands and paths), but it can also be\ncontext-aware if a command has an associated map table registered via\n``cli_register_cmd_map()`` / ``cli_register_cmd_maps()``.\n\nSee also\n--------\n\n* ``cli.rst`` and ``cli_lib.rst`` in this documentation set\n* ``lib/cli/DESIGN.md`` in the source tree\n"
  },
  {
    "path": "docs/source/commands.rst",
    "content": ".. _commands:\n\n``*** Pktgen ***``\nCopyright &copy \\<2015-2026\\>, Intel Corporation.\n\nREADME for setting up Pktgen with DPDK on Ubuntu 10.04 to 20.04 desktop, it\nshould work on most Linux systems as long as the kernel has hugeTLB page support.\n\nNote: Tested with Ubuntu 18.04 and up to 20.04 versions\nLinux 3.5.0-25-generic #39-Ubuntu SMP Mon Feb 25 18:26:58 UTC 2013 x86_64\n\nI am using Ubuntu 16.10 x86_64 (64 bit support) for running Pktgen-DPDK on a\nCrownpass Dual socket board running at 2.4GHz with 32GB of ram 16GB per socket.\nThe current kernel version is 4.4.0-66-generic (as of 2018-04-01) support, but should\nwork on just about any new Linux kernel version.\n\nCurrently using as of 2020-05 Ubuntu 20.04 Kernel 5.6.0+ system.\n\nTo get hugeTLB page support your Linux kernel must be at least 2.6.33 and in the\nDPDK documents it talks about how you can upgrade your Linux kernel.\n\nHere is another document on how to upgrade your Linux kernel.\nUbuntu 10.04 is 2.6.32 by default so upgraded to kernel 2.6.34 using this HOWTO:\nhttp://usablesoftware.wordpress.com/2010/05/26/switch-to-a-newer-kernel-in-ubuntu-10-04/\n\nThe pktgen output display needs 132 columns and about 42 lines to display\ncurrently. I am using an xterm of 132x42, but you can have a larger display\nand maybe a bit smaller. If you are displaying more then 4-6 ports then you\nwill need a wider display. Pktgen allows you to view a set of ports if they\ndo not all fit on the screen at one time via the 'page' command.\n\nType 'help' at the 'Pktgen>' prompt to see the complete Pktgen command line\ncommands. Pktgen uses VT100 control codes or escape codes to display the screens,\nwhich means your terminal must support VT100. The Hyperterminal in windows is not\ngoing to work for Pktgen as it has a few problems with VT100 codes.\n\nPktgen has a number of modes to send packets single, range, random, sequeue and\nPCAP modes. Each mode has its own set of packet buffers and you must configure\neach mode to work correctly. The single packet mode is the information displayed\nat startup screen or when using the 'page main or page 0' command. The other\nscreens can be accessed using 'page seq|range|rnd|pcap|stats' command.\n\nThe pktgen program as built can send up to 16 packets per port in a sequence\nand you can configure a port using the 'seq' pktgen command. A script file\ncan be loaded from the shell command line via the -f option and you can 'load'\na script file from within pktgen as well.\n\nIn the BIOS make sure the HPET High Precision Event Timer is enabled. Also\nmake sure hyper-threading is enabled.\n\n** NOTE **\nOn a 10GB NIC if the transceivers are not attached the screen updates will go\nvery slow.\n\n\nPktgen command line directory format\n====================================\n\n-- Pktgen Ver: 3.2.x (DPDK 17.05.0-rc0)  Powered by DPDK ---------------\n\nShow the commands inside the ``pktgen/bin`` directory::\n\n\tPktgen:/> ls\n\t[pktgen]        [sbin]          dpdk-version    copyright\n    Pktgen:/> ls pktgen/bin\n    off             on              rate            plugin          dbg\n    set             pcap            stp             str             stop\n    start           disable         enable          range           theme\n    page            seq             sequence        ping4           port\n    restart         rst             reset           cls             redisplay\n    save            load            geom            geometry        clr\n    clear           help\n\nShowin the ``1s`` command at root::\n\n\tPktgen:/> ls\n\t[pktgen]        [sbin]          copyright\n    Pktgen:/> ls sbin\n    version         echo            script          env             path\n    hugepages       cmap            more            history         quit\n    screen.clear    pwd             cd              ls              rm\n    mkdir           chelp           sleep           delay\n\nThe case of using ``ls -l`` in a subdirectory::\n\n    Pktgen:/> cd sbin\n    Pktgen:/sbin/> ls -l\n    version          Command : Display version information\n    echo             Command : simple echo a string to the screen\n    script           Command : load and process cli command files\n    env              Command : Show/del/get/set environment variables\n    path             Command : display the execution path for commands\n    hugepages        Command : hugepages # display hugepage info\n    cmap             Command : cmap # display the core mapping\n    more             Command : more <file> # display a file content\n    history          Command : history # display the current history\n    quit             Command : quit # quit the application\n    screen.clear     Command : screen.clear # clear the screen\n    pwd              Command : pwd # display current working directory\n    cd               Command : cd <dir> # change working directory\n    ls               Command : ls [-lr] <dir> # list current directory\n    rm               Command : remove a file or directory\n    mkdir            Command : create a directory\n    chelp            Command : CLI help - display information for DPDK\n    sleep            Command : delay a number of seconds\n    delay            Command : delay a number of milliseconds\n\nShow help using ``ls -l`` command in pktgen directory::\n\n    Pktgen:/sbin/> cd ../pktgen/bin\n    Pktgen:/pktgen/bin/> ls -l\n    off              Alias : disable screen\n    on               Alias : enable screen\n    rate             Command : Rate setup commands\n    plugin           Command : Plugin a shared object file\n    dbg              Command : debug commands\n    set              Command : set a number of options\n    pcap             Command : pcap commands\n    stp              Alias : stop all\n    str              Alias : start all\n    stop             Command : stop features\n    start            Command : start features\n    disable          Command : disable features\n    enable           Command : enable features\n    range            Command : Range commands\n    theme            Command : Set, save, show the theme\n    page             Command : change page displays\n    seq              Alias : sequence\n    sequence         Command : sequence command\n    ping4            Command : Send a ping packet for IPv4\n    port             Command : Switch between ports\n    restart          Command : restart port\n    rst              Alias : reset all\n    reset            Command : reset pktgen configuration\n    cls              Alias : redisplay\n    redisplay        Command : redisplay the screen\n    save             Command : save the current state\n    load             Command : load command file\n    geom             Alias : geometry\n    geometry         Command : show the screen geometry\n    clr              Alias : clear all stats\n    clear            Command : clear stats, ...\n    help             Command : help command\n\n    Pktgen:/pktgen/bin/>\n\nRuntime Options and Commands\n============================\n\nWhile the ``pktgen`` application is running you will see a command prompt as\nfollows::\n\n   Pktgen:/>\n\nFrom this you can get help or issue runtime commands::\n\n   Pktgen:/> help\n\n   set <portlist> <xxx> value    - Set a few port values\n   save <path-to-file>           - Save a configuration file using the\n                                   filename\n   load <path-to-file>           - Load a command/script file from the\n                                   given path\n   ...\n\n\nThe ``page`` commands to show different screens::\n\n        ** Pktgen Help Information **\n\n    page [0-7]                         - Show the port pages or configuration or sequence page\n    page main                          - Display page zero\n    page range                         - Display the range packet page\n    page cpu                           - Display the CPU page\n    page pcap                          - Display the pcap page\n    page cpu                           - Display some information about the CPU system\n    page next                          - Display next page of PCAP packets.\n    page sequence | seq                - sequence will display a set of packets for a given port\n                                        Note: use the 'port <number>' to display a new port sequence\n    page rnd                           - Display the random bitfields to packets for a given port\n                                        Note: use the 'port <number>' to display a new port sequence\n    page log                           - Display the log messages page\n    page latency                       - Display the latency page\n    page stats                         - Display physical ports stats for all ports\n    page xstats                        - Display port XSTATS values\n    page rate                          - Display Rate Pacing values\n\nList of the ``enable/disable`` commands::\n\n    enable|disable <portlist> process  - Enable or Disable processing of ARP/ICMP/IPv4/IPv6 packets\n    enable|disable <portlist> mpls     - Enable/disable sending MPLS entry in packets\n    enable|disable <portlist> qinq     - Enable/disable sending Q-in-Q header in packets\n    enable|disable <portlist> gre      - Enable/disable GRE support\n    enable|disable <portlist> gre_eth  - Enable/disable GRE with Ethernet frame payload\n    enable|disable <portlist> vlan     - Enable/disable VLAN tagging\n    enable|disable <portlist> garp     - Enable or Disable Gratuitous ARP packet processing\n    enable|disable <portlist> random   - Enable/disable Random packet support\n    enable|disable <portlist> latency  - Enable/disable latency testing\n    enable|disable <portlist> pcap     - Enable or Disable sending pcap packets on a portlist\n    enable|disable <portlist> blink    - Blink LED on port(s)\n    enable|disable <portlist> rx_tap   - Enable/Disable RX Tap support\n    enable|disable <portlist> tx_tap   - Enable/Disable TX Tap support\n    enable|disable <portlist> icmp     - Enable/Disable sending ICMP packets\n    enable|disable <portlist> range    - Enable or Disable the given portlist for sending a range of packets\n    enable|disable <portlist> capture  - Enable/disable packet capturing on a portlist, disable to save capture\n                                        Disable capture on a port to save the data into the currect working directory.\n    enable|disable <portlist> bonding  - Enable call TX with zero packets for bonding driver\n    enable|disable <portlist> vxlan    - Send VxLAN packets\n    enable|disable <portlist> rate     - Enable/Disable Rate Packing on given ports\n    enable|disable mac_from_arp        - Enable/disable MAC address from ARP packet\n    enable|disable screen              - Enable/disable updating the screen and unlock/lock window\n        off                            - screen off shortcut\n        on                             - screen on shortcut\n\nList of the ``set`` commands::\n\n        note: <portlist>               - a list of ports (no spaces) e.g. 2,4,6-9,12 or the word 'all'\n    set <portlist> count <value>       - number of packets to transmit\n    set <portlist> size <value>        - size of the packet to transmit\n    set <portlist> rate <percent>      - Packet rate in percentage\n    set <portlist> txburst <value>     - number of packets in a Tx burst\n    set <portlist> rxburst <value>     - number of packets in a Rx burst\n    set <portlist> tx_cycles <value>   - DEBUG to set the number of cycles per TX burst\n    set <portlist> sport <value>       - Source port number for TCP\n    set <portlist> dport <value>       - Destination port number for TCP\n    set <portlist> ttl <value>         - Set the TTL value for the single port more\n    set <portlist> seq_cnt|seqcnt|seqCnt <value>\n                                    - Set the number of packet in the sequence to send [0-16]\n    set <portlist> prime <value>       - Set the number of packets to send on prime command\n    set <portlist> dump <value>        - Dump the next 1-32 received packets to the screen\n                                        Dumped packets are in the log, use 'page log' to view\n    set <portlist> vlan|vlanid <value> - Set the VLAN ID value for the portlist\n    set <portlist> jitter <value>      - Set the jitter threshold in micro-seconds\n    set <portlist> src|dst mac <addr>  - Set MAC addresses 00:11:22:33:44:55 or 0011:2233:4455 format\n    set <portlist> type ipv4|ipv6|vlan|arp - Set the packet type to IPv4 or IPv6 or VLAN\n    set <portlist> proto udp|tcp|icmp  - Set the packet protocol to UDP or TCP or ICMP per port\n    set <portlist> pattern <type>      - Set the fill pattern type\n                    type - abc        - Default pattern of abc string\n                            none       - No fill pattern, maybe random data\n                            zero       - Fill of zero bytes\n                            user       - User supplied string of max 16 bytes\n    set <portlist> user pattern <string> - A 16 byte string, must set 'pattern user' command\n    set <portlist> [src|dst] ip ipaddr - Set IP addresses, Source must include network mask e.g. 10.1.2.3/24\n    set <portlist> qinqids <id1> <id2> - Set the Q-in-Q ID's for the portlist\n    set <portlist> rnd <idx> <off> <mask> - Set random mask for all transmitted packets from portlist\n        idx: random mask index slot\n        off: offset in bytes to apply mask value\n        mask: up to 32 bit long mask specification (empty to disable):\n            0: bit will be 0\n            1: bit will be 1\n            .: bit will be ignored (original value is retained)\n            X: bit will get random value\n    set <portlist> cos <value>         - Set the CoS value for the portlist\n    set <portlist> tos <value>         - Set the ToS value for the portlist\n    set <portlist> vxlan <flags> <group id> <vxlan_id> - Set the vxlan values\n    set ports_per_page <value>         - Set ports per page value 1 - 6\n\nThe ``range`` commands::\n\n  -- Setup the packet range values --\n     note: SMMI = start|min|max|inc (start, minimum, maximum, increment)\n\n    range <portlist> src|dst mac <SMMI> <etheraddr> - Set destination/source MAC address\n        e.g: range 0 src mac start 00:00:00:00:00:00\n            range 0 dst mac max 00:12:34:56:78:90\n        or  range 0 src mac 00:00:00:00:00:00 00:00:00:00:00:00 00:12:34:56:78:90 00:00:00:01:01:01\n    range <portlist> src|dst ip <SMMI> <ipaddr>   - Set source IP start address\n        e.g: range 0 dst ip start 0.0.0.0\n            range 0 dst ip min 0.0.0.0\n            range 0 dst ip max 1.2.3.4\n            range 0 dst ip inc 0.0.1.0\n        or  range 0 dst ip 0.0.0.0 0.0.0.0 1.2.3.4 0.0.1.0\n    range <portlist> proto tcp|udp                - Set the IP protocol type\n    range <portlist> src|dst port <SMMI> <value>  - Set UDP/TCP source/dest port number\n        or  range <portlist> src|dst port <start> <min> <max> <inc>\n    range <portlist> vlan <SMMI> <value>          - Set vlan id start address\n        or  range <portlist> vlan <start> <min> <max> <inc>\n    range <portlist> size <SMMI> <value>          - Set pkt size start address\n        or  range <portlist> size <start> <min> <max> <inc>\n    range <portlist> teid <SMMI> <value>          - Set TEID value\n        or  range <portlist> teid <start> <min> <max> <inc>\n    range <portlist> mpls entry <hex-value>       - Set MPLS entry value\n    range <portlist> qinq index <val1> <val2>     - Set QinQ index values\n    range <portlist> gre key <value>              - Set GRE key value\n    range <portlist> cos <SMMI> <value>           - Set cos value\n    range <portlist> tos <SMMI> <value>           - Set tos value\n\nThe ``sequence`` commands::\n\n    sequence <seq#> <portlist> dst <Mac> src <Mac> dst <IP> src <IP> sport <val> dport <val> ipv4|ipv6 udp|tcp|icmp vlan <val> size <val> [teid <val>]\n    sequence <seq#> <portlist> <dst-Mac> <src-Mac> <dst-IP> <src-IP> <sport> <dport> ipv4|ipv6 udp|tcp|icmp <vlanid> <pktsize> [<teid>]\n    sequence <seq#> <portlist> cos <cos> tos <tos>\n    sequence <seq#> <portlist> vxlan <flags> gid <group_id> vid <vxlan_id>\n                                    - Set the sequence packet information, make sure the src-IP\n                                        has the netmask value eg 1.2.3.4/24\n\n\nThe ``pcap`` commands::\n\n    pcap show                          - Show PCAP information\n    pcap index                         - Move the PCAP file index to the given packet number,  0 - rewind, -1 - end of file\n    pcap filter <portlist> <string>    - PCAP filter string to filter packets on receive\n\nThe ``start|stop`` commands::\n\n    start <portlist>                   - Start transmitting packets\n    stop <portlist>                    - Stop transmitting packets\n    stp                                - Stop all ports from transmitting\n    str                                - Start all ports transmitting\n    start <portlist> prime             - Transmit packets on each port listed. See set prime command above\n    start <portlist> arp <type>        - Send a ARP type packet\n       type - request | gratuitous | req | grat\n\nThe ``debug`` commands::\n    dbg l2p                            - Dump out internal lcore to port mapping\n    dbg tx_dbg                         - Enable tx debug output\n    dbg mempool <portlist> <type>      - Dump out the mempool info for a given type\n    dbg pdump <portlist>               - Hex dump the first packet to be sent, single packet mode only\n    dbg memzone                        - List all of the current memzones\n    dbg memseg                         - List all of the current memsegs\n    dbg hexdump <addr> <len>           - hex dump memory at given address\n    dbg break                          - break into the debugger\n    dbg memcpy [loop-cnt KBytes]       - run a memcpy test\n\nThe odd or special commands::\n\n    save <path-to-file>                - Save a configuration file using the filename\n    load <path-to-file>                - Load a command/script file from the given path\n    script <filename>                  - Execute the Lua script code in file (www.lua.org). (if Lua is enabled)\n    lua 'lua string'                   - Execute the Lua code in the string needs quotes (if Lua is enabled)\n    clear <portlist> stats             - Clear the statistics\n    clr                                - Clear all Statistices\n    reset <portlist>                   - Reset the configuration the ports to the default\n    rst                                - Reset the configuration for all ports\n    ports per page [1-6]               - Set the number of ports displayed per page\n    port <number>                      - Sets the sequence packets to display for a given port\n    restart <portlist>                 - Restart or stop a ethernet port and restart\n    ping4 <portlist>                   - Send a IPv4 ICMP echo request on the given portlist\n\nThe ``theme`` commands::\n    theme <item> <fg> <bg> <attr>      - Set color for item with fg/bg color and attribute value\n    theme show                         - List the item strings, colors and attributes to the items\n    theme save <filename>              - Save the current color theme to a file\n\nThe ``plugin`` commands::\n    plugin                             - Show the plugins currently installed\n    plugin load <filename>             - Load a plugin file\n    plugin load <filename> <path>      - Load a plugin file at path\n    plugin rm|delete <plugin>          - Remove or delete a plugin\n\nThe ``rate` commands for packet pacing::\n\n    rate <portlist> count <value>        - number of packets to transmit\n    rate <portlist> size <value>         - size of the packet to transmit\n    rate <portlist> rate <percent>       - Packet rate in percentage\n    rate <portlist> burst <value>        - number of packets in a burst\n    rate <portlist> sport <value>        - Source port number for TCP\n    rate <portlist> dport <value>        - Destination port number for TCP\n    rate <portlist> ttl <value>          - Set the TTL value for the single port more\n    rate <portlist> src|dst mac <addr>   - Set MAC addresses 00:11:22:33:44:55 or 0011:2233:4455 format\n    rate <portlist> type ipv4|ipv6|vlan|arp - Set the packet type to IPv4 or IPv6 or VLAN\n    rate <portlist> proto udp|tcp|icmp   - Set the packet protocol to UDP or TCP or ICMP per port\n    rate <portlist> [src|dst] ip ipaddr  - Set IP addresses, Source must include network mask e.g. 10.1.2.3/24\n    rate <portlist> fps <value>          - Set the frame per second value e.g. 60fps\n    rate <portlist> lines <value>        - Set the number of video lines, e.g. 720\n    rate <portlist> pixels <value>       - Set the number of pixels per line, e.g. 1280\n    rate <portlist> color bits <value>   - Set the color bit size 8, 16, 24, ...\n    rate <portlist> payload size <value> - Set the payload size\n    rate <portlist> overhead <value>     - Set the packet overhead + payload = total packet size\n\nThe flags::\n\n        Flags: P------------------ - Promiscuous mode enabled\n                E                  - ICMP Echo enabled\n                 B                 - Bonding enabled LACP 802.3ad\n                  I                - Process packets on input enabled\n                   *               - Using TAP interface for this port can be [-rt*]\n                    g              - Process GARP packets\n                     C             - Capture received packets\n                      ------       - Modes Single, pcap, sequence, latency, random, Rate\n                            ------ - Modes VLAN, VxLAN, MPLS, QnQ, GRE IPv4, GRE ETH\n    Notes: <state>    - Use enable|disable or on|off to set the state.\n        <portlist>    - a list of ports (no spaces) as 2,4,6-9,12 or 3-5,8 or 5 or the word 'all'\n        Colors best seen on a black background for now\n\n\nSeveral commands take common arguments such as:\n\n* ``portlist``: A list of ports such as ``2,4,6-9,12`` or the word ``all``.\n* ``state``: This is usually ``on`` or ``off`` but will also accept ``enable``\n  or ``disable``.\n\nFor example::\n\n   Pktgen:/> set all seq_cnt 1\n\n\nThe ``set`` command can also be used to set the MAC address with a format like\n``00:11:22:33:44:55`` or ``0011:2233:4455``::\n\n   set <portlist> src|dst mac etheraddr\n\nThe ``set`` command can also be used to set IP addresses::\n\n   set <portlist> src|dst ip ipaddr\n\n\nseq\n---\n\nThe ``seq`` command sets the flow parameters for a sequence of packets::\n\n   seq <seq#> <portlist> dst-Mac src-Mac dst-IP src-IP\n                         sport dport ipv4|ipv6|vlan udp|tcp|icmp vid pktsize\n\nWhere the arguments are:\n\n  * ``<seq#>``: The packet sequence number.\n  * ``<portlist>``: A portlist as explained above.\n  * ``dst-Mac``: The destination MAC address.\n  * ``src-Mac``: The source MAC address.\n  * ``dst-IP``: The destination IP address.\n  * ``src-IP``: The source IP address. Make sure the src-IP has the netmask value such as ``1.2.3.4/24``.\n  * ``sport``: The source port.\n  * ``dport``: The destination port.\n  * ``IP``: The IP layer. One of ``ipv4|ipv6|vlan``.\n  * ``Transport``: The transport. One of ``udp|tcp|icmp``.\n  * ``vid``: The VLAN ID.\n  * ``pktsize``: The packet size.\n\n\nsave\n----\n\nThe ``save`` command saves the current configuration of a file::\n\n   save <path-to-file>\n\n\nload\n----\n\nThe ``load`` command loads a configuration from a file::\n\n   load <path-to-file>\n\nThe is most often used with a configuration file written with the ``save``\ncommand, see above.\n\n\nports per page\n--------------\n\nThe ``ports per page`` (ports per page) command sets the number of ports displayed per\npage::\n\n   ports per page [1-6]\n\n\nscript\n------\n\nThe ``script`` command execute the Lua code in specified file::\n\n   script <filename>\n\nSee :ref:`scripts`.\n\n\npages\n-----\n\nThe Random or rnd page.\n::\n\n  Port 0           <Random bitfield Page>  Copyright(c) <2010-2026>, Intel Corporation\n    Index   Offset     Act?  Mask [0 = 0 bit, 1 = 1 bit, X = random bit, . = ignore]\n       0        0      No   00000000 00000000 00000000 00000000\n       1        0      No   00000000 00000000 00000000 00000000\n       2        0      No   00000000 00000000 00000000 00000000\n       3        0      No   00000000 00000000 00000000 00000000\n       4        0      No   00000000 00000000 00000000 00000000\n       5        0      No   00000000 00000000 00000000 00000000\n       6        0      No   00000000 00000000 00000000 00000000\n       7        0      No   00000000 00000000 00000000 00000000\n       8        0      No   00000000 00000000 00000000 00000000\n       9        0      No   00000000 00000000 00000000 00000000\n       10       0      No   00000000 00000000 00000000 00000000\n       11       0      No   00000000 00000000 00000000 00000000\n       12       0      No   00000000 00000000 00000000 00000000\n       13       0      No   00000000 00000000 00000000 00000000\n       14       0      No   00000000 00000000 00000000 00000000\n       15       0      No   00000000 00000000 00000000 00000000\n       16       0      No   00000000 00000000 00000000 00000000\n       17       0      No   00000000 00000000 00000000 00000000\n       18       0      No   00000000 00000000 00000000 00000000\n       19       0      No   00000000 00000000 00000000 00000000\n       20       0      No   00000000 00000000 00000000 00000000\n       21       0      No   00000000 00000000 00000000 00000000\n       22       0      No   00000000 00000000 00000000 00000000\n       23       0      No   00000000 00000000 00000000 00000000\n       24       0      No   00000000 00000000 00000000 00000000\n       25       0      No   00000000 00000000 00000000 00000000\n       26       0      No   00000000 00000000 00000000 00000000\n       27       0      No   00000000 00000000 00000000 00000000\n       28       0      No   00000000 00000000 00000000 00000000\n       29       0      No   00000000 00000000 00000000 00000000\n       30       0      No   00000000 00000000 00000000 00000000\n       31       0      No   00000000 00000000 00000000 00000000\n       -- Pktgen Ver: 3.2.4 (DPDK 17.05.0-rc0)  Powered by DPDK -----\n\nThe sequence or seq page.\n::\n\n\t<Sequence Page>  Copyright(c) <2010-2026>, Intel Corporation\n\t  Port   :  0, Sequence Count:  8 of 16                                                                            GTPu\n\t    * Seq:            Dst MAC           Src MAC          Dst IP            Src IP    Port S/D Protocol:VLAN  Size  TEID\n\t    *   0:  3c:fd:fe:9c:5c:d9 3c:fd:fe:9c:5c:d8     192.168.1.1    192.168.0.1/24   1234/5678 IPv4/TCP:0001   64     0\n\t    *   1:  3c:fd:fe:9c:5c:d9 3c:fd:fe:9c:5c:d8     192.168.1.1    192.168.0.1/24   1234/5678 IPv4/TCP:0001   64     0\n\t    *   2:  3c:fd:fe:9c:5c:d9 3c:fd:fe:9c:5c:d8     192.168.1.1    192.168.0.1/24   1234/5678 IPv4/TCP:0001   64     0\n\t    *   3:  3c:fd:fe:9c:5c:d9 3c:fd:fe:9c:5c:d8     192.168.1.1    192.168.0.1/24   1234/5678 IPv4/TCP:0001   64     0\n\t    *   4:  3c:fd:fe:9c:5c:d9 3c:fd:fe:9c:5c:d8     192.168.1.1    192.168.0.1/24   1234/5678 IPv4/TCP:0001   64     0\n\t    *   5:  3c:fd:fe:9c:5c:d9 3c:fd:fe:9c:5c:d8     192.168.1.1    192.168.0.1/24   1234/5678 IPv4/TCP:0001   64     0\n\t    *   6:  3c:fd:fe:9c:5c:d9 3c:fd:fe:9c:5c:d8     192.168.1.1    192.168.0.1/24   1234/5678 IPv4/TCP:0001   64     0\n\t    *   7:  3c:fd:fe:9c:5c:d9 3c:fd:fe:9c:5c:d8     192.168.1.1    192.168.0.1/24   1234/5678 IPv4/TCP:0001   64     0\n\n\t    -- Pktgen Ver: 3.2.4 (DPDK 17.05.0-rc0)  Powered by DPDK ---------------\n\nThe CPU information page.\n::\n\n\t<CPU Page>  Copyright(c) <2010-2026>, Intel Corporation\n\n\tKernel: Linux rkwiles-DESK1.intel.com 4.4.0-66-generic #87-Ubuntu SMP Fri Mar 3 15:29:05 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux\n\n\tModel Name: Intel(R) Xeon(R) CPU E5-2699 v3 @ 2.30GHz\n\tCPU Speed : 1201.031\n\tCache Size: 46080 KB\n\n\tCPU Flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm epb tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm xsaveopt cqm_llc cqm_occup_llc dtherm ida arat pln pts\n\t  2 sockets, 18 cores per socket and 2 threads per core.\n\t  Socket   :    0         1\n\t  Core   0 : [ 0,36]   [18,54]\n\t  Core   1 : [ 1,37]   [19,55]\n\t  Core   2 : [ 2,38]   [20,56]\n\t  Core   3 : [ 3,39]   [21,57]\n\t  Core   4 : [ 4,40]   [22,58]\n\t  Core   5 : [ 5,41]   [23,59]\n\t  Core   6 : [ 6,42]   [24,60]\n\t  Core   7 : [ 7,43]   [25,61]\n\t  Core   8 : [ 8,44]   [26,62]\n\t  Core   9 : [ 9,45]   [27,63]\n\t  Core  10 : [10,46]   [28,64]\n\t  Core  11 : [11,47]   [29,65]\n\t  Core  12 : [12,48]   [30,66]\n\t  Core  13 : [13,49]   [31,67]\n\t  Core  14 : [14,50]   [32,68]\n\t  Core  15 : [15,51]   [33,69]\n\t  Core  16 : [16,52]   [34,70]\n\t  Core  17 : [17,53]   [35,71]\n\nThe latency page.\n::\n\n\t-- Ports 0-3 of 8   <Main Page>  Copyright(c) <2010-2026>, Intel Corporation\n\t\tFlags:Port         :   P----S---------:0   P--------------:1   P--------------:2   P--------------:3\n\t\tLink State         :       <UP-10000-FD>       <UP-10000-FD>       <UP-10000-FD>       <UP-10000-FD>     ----TotalRate----\n\t\tPkts/s Max/Rx      :                 0/0                 0/0                 0/0                 0/0                   0/0\n\t\t       Max/Tx      :                 0/0                 0/0                 0/0                 0/0                   0/0\n\t\tMBits/s Rx/Tx      :                 0/0                 0/0                 0/0                 0/0                   0/0\n\t\t                   :\n\t\tLatency usec       :                   0                   0                   0                   0\n\t\tJitter Threshold   :                  50                  50                  50                  50\n\t\tJitter count       :                   0                   0                   0                   0\n\t\tTotal Rx pkts      :                   0                   0                   0                   0\n\t\tJitter percent     :                   0                   0                   0                   0\n\t\t                   :\n\t\tPattern Type       :             abcd...             abcd...             abcd...             abcd...\n\t\tTx Count/% Rate    :       Forever /100%       Forever /100%       Forever /100%       Forever /100%\n\t\tPktSize/Rx:Tx Burst:         64 / 32: 64           64 /   32           64 /   32           64 /   32\n\t\tSrc/Dest Port      :         1234 / 5678         1234 / 5678         1234 / 5678         1234 / 5678\n\t\tPkt Type:VLAN ID   :     IPv4 / TCP:0001     IPv4 / TCP:0001     IPv4 / TCP:0001     IPv4 / TCP:0001\n\t\tDst  IP Address    :         192.168.1.1         192.168.0.1         192.168.3.1         192.168.2.1\n\t\tSrc  IP Address    :      192.168.0.1/24      192.168.1.1/24      192.168.2.1/24      192.168.3.1/24\n\t\tDst MAC Address    :   3c:fd:fe:9c:5c:d9   3c:fd:fe:9c:5c:d8   3c:fd:fe:9c:5c:db   3c:fd:fe:9c:5c:da\n\t\tSrc MAC Address    :   3c:fd:fe:9c:5c:d8   3c:fd:fe:9c:5c:d9   3c:fd:fe:9c:5c:da   3c:fd:fe:9c:5c:db\n\t\tVendID/PCI Addr    :   8086:1572/04:00.0   8086:1572/04:00.1   8086:1572/04:00.2   8086:1572/04:00.3\n\n\t\t-- Pktgen Ver: 3.2.4 (DPDK 17.05.0-rc0)  Powered by DPDK ---------------\n\nThe config or cfg page.\n::\n\n\t<CPU Page>  Copyright(c) <2010-2026>, Intel Corporation\n\t 2 sockets, 18 cores, 2 threads\n\t  Socket   :    0         1      Port description\n\t  Core   0 : [ 0,36]   [18,54]   0000:04:00.0 : Intel Corporation X710 for 10GbE SFP+ (rev 01)\n\t  Core   1 : [ 1,37]   [19,55]   0000:04:00.1 : Intel Corporation X710 for 10GbE SFP+ (rev 01)\n\t  Core   2 : [ 2,38]   [20,56]   0000:04:00.2 : Intel Corporation X710 for 10GbE SFP+ (rev 01)\n\t  Core   3 : [ 3,39]   [21,57]   0000:04:00.3 : Intel Corporation X710 for 10GbE SFP+ (rev 01)\n\t  Core   4 : [ 4,40]   [22,58]   0000:05:00.0 : Intel Corporation I350 Gigabit Network Connection (rev 01)\n\t  Core   5 : [ 5,41]   [23,59]   0000:05:00.1 : Intel Corporation I350 Gigabit Network Connection (rev 01)\n\t  Core   6 : [ 6,42]   [24,60]   0000:81:00.0 : Intel Corporation X710 for 10GbE SFP+ (rev 01)\n\t  Core   7 : [ 7,43]   [25,61]   0000:81:00.1 : Intel Corporation X710 for 10GbE SFP+ (rev 01)\n\t  Core   8 : [ 8,44]   [26,62]   0000:81:00.2 : Intel Corporation X710 for 10GbE SFP+ (rev 01)\n\t  Core   9 : [ 9,45]   [27,63]   0000:81:00.3 : Intel Corporation X710 for 10GbE SFP+ (rev 01)\n\t  Core  10 : [10,46]   [28,64]   0000:82:00.0 : Intel Corporation XL710 for 40GbE QSFP+ (rev 02)\n\t  Core  11 : [11,47]   [29,65]   0000:83:00.0 : Intel Corporation XL710 for 40GbE QSFP+ (rev 02)\n\t  Core  12 : [12,48]   [30,66]\n\t  Core  13 : [13,49]   [31,67]\n\t  Core  14 : [14,50]   [32,68]\n\t  Core  15 : [15,51]   [33,69]\n\t  Core  16 : [16,52]   [34,70]\n\t  Core  17 : [17,53]   [35,71]\n\n\t  -- Pktgen Ver: 3.2.4 (DPDK 17.05.0-rc0)  Powered by DPDK ---------------\n\n\nHere is the ``page range`` screen.\n::\n\n\t    Port #                           Port-0              Port-1              Port-2              Port-3\n\t    dst.ip            :         192.168.1.1         192.168.2.1         192.168.3.1         192.168.4.1\n\t        inc           :             0.0.0.1             0.0.0.1             0.0.0.1             0.0.0.1\n\t        min           :         192.168.1.1         192.168.2.1         192.168.3.1         192.168.4.1\n\t        max           :       192.168.1.254       192.168.2.254       192.168.3.254       192.168.4.254\n\t                      :\n\t    src.ip            :         192.168.0.1         192.168.1.1         192.168.2.1         192.168.3.1\n\t        inc           :             0.0.0.0             0.0.0.0             0.0.0.0             0.0.0.0\n\t        min           :         192.168.0.1         192.168.1.1         192.168.2.1         192.168.3.1\n\t        max           :       192.168.0.254       192.168.1.254       192.168.2.254       192.168.3.254\n\t                      :\n\t    ip_proto          :                 TCP                 TCP                 TCP                 TCP\n\t                      :\n\t    dst.port / inc    :             0/    1           256/    1           512/    1           768/    1\n\t         min / max    :             0/  254           256/  510           512/  766           768/ 1022\n\t                      :\n\t    src.port / inc    :             0/    1           256/    1           512/    1           768/    1\n\t         min / max    :             0/  254           256/  510           512/  766           768/ 1022\n\t                      :\n\t    vlan.id / inc     :              1/   0              1/   0              1/   0              1/   0\n\t        min / max     :              1/4095              1/4095              1/4095              1/4095\n\t                      :\n\t    pkt.size / inc    :             64/   0             64/   0             64/   0             64/   0\n\t         min / max    :             64/1518             64/1518             64/1518             64/1518\n\t                      :\n\t    dst.mac           :   3c:fd:fe:9c:5c:d9   3c:fd:fe:9c:5c:d8   3c:fd:fe:9c:5c:db   3c:fd:fe:9c:5c:da\n\t        inc           :   00:00:00:00:00:00   00:00:00:00:00:00   00:00:00:00:00:00   00:00:00:00:00:00\n\t        min           :   00:00:00:00:00:00   00:00:00:00:00:00   00:00:00:00:00:00   00:00:00:00:00:00\n\t        max           :   00:00:00:00:00:00   00:00:00:00:00:00   00:00:00:00:00:00   00:00:00:00:00:00\n\t                      :\n\t    src.mac           :   3c:fd:fe:9c:5c:d8   3c:fd:fe:9c:5c:d9   3c:fd:fe:9c:5c:da   3c:fd:fe:9c:5c:db\n\t        inc           :   00:00:00:00:00:00   00:00:00:00:00:00   00:00:00:00:00:00   00:00:00:00:00:00\n\t        min           :   00:00:00:00:00:00   00:00:00:00:00:00   00:00:00:00:00:00   00:00:00:00:00:00\n\t        max           :   00:00:00:00:00:00   00:00:00:00:00:00   00:00:00:00:00:00   00:00:00:00:00:00\n\t                      :\n\t    gtpu.teid / inc   :             0/    0             0/    0             0/    0             0/    0\n\t          min / max   :             0/    0             0/    0             0/    0             0/    0\n\t    -- Pktgen Ver: 3.2.4 (DPDK 17.05.0-rc0)  Powered by DPDK ---------------\n\n\t    Pktgen:/>\n\ns\n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "# -*- coding: utf-8 -*-\n\nfrom sphinx.highlighting import PygmentsBridge\nfrom pygments.formatters.latex import LatexFormatter\n\n\nproject = 'Pktgen'\ncopyright = '2010-2024, Keith Wiles'\n\n# Version of Pktgen-DPDK\nversion = '23.06.1'\nrelease = version\n\nextensions = []\n\nsource_suffix = {\n    '.rst': 'restructuredtext',\n}\n\n# Optional Markdown support for developer-friendly docs.\n# If myst_parser is installed, Sphinx can render .md sources referenced by\n# toctrees (e.g. docs/QUICKSTART.md and lib/cli/DESIGN.md).\ntry:\n    import myst_parser  # noqa: F401\nexcept Exception:  # pragma: no cover\n    pass\nelse:\n    extensions.append('myst_parser')\n    source_suffix['.md'] = 'markdown'\n\ninitial_doc = 'index'\npygments_style = 'sphinx'\nhtml_theme = 'default'\nhtml_add_permalinks = ''\nhtmlhelp_basename = 'Pktgendoc'\n\nlatex_documents = [\n    ('index',\n     'pktgen.tex',\n     'Pktgen-DPDK Documentation',\n     'Keith Wiles', 'manual'),\n]\n\nlatex_preamble = \"\"\"\n\\\\usepackage{upquote}\n\\\\usepackage[utf8]{inputenc}\n\\\\usepackage{DejaVuSansMono}\n\\\\usepackage[T1]{fontenc}\n\\\\usepackage{helvet}\n\\\\renewcommand{\\\\familydefault}{\\\\sfdefault}\n\n\\\\RecustomVerbatimEnvironment{Verbatim}{Verbatim}{xleftmargin=5mm}\n\"\"\"\n\nlatex_elements = {\n    'papersize': 'a4paper',\n    'pointsize': '11pt',\n    'preamble': latex_preamble,\n}\n\n\nman_pages = [\n    ('index',\n     'pktgen',\n     'Pktgen Documentation',\n     ['Keith Wiles'],\n     1)\n]\n\ntexinfo_documents = [\n    ('index', 'Pktgen',\n     'Pktgen Documentation',\n     'Keith Wiles',\n     'Pktgen',\n     'One line description of project.',\n     'Miscellaneous'),\n]\n\n\nclass CustomLatexFormatter(LatexFormatter):\n    def __init__(self, **options):\n        super(CustomLatexFormatter, self).__init__(**options)\n        self.verboptions = r\"formatcom=\\footnotesize\"\n\nPygmentsBridge.latex_formatter = CustomLatexFormatter\n"
  },
  {
    "path": "docs/source/contents.rst",
    "content": "\nThe Pktgen Application\n======================\n\n**Pktgen**, (*Packet* *Gen*-erator) is a software based traffic generator\npowered by the DPDK fast packet processing framework.\n\nSome of the features of Pktgen are:\n\n* It is capable of generating 10Gbit wire rate traffic with 64 byte frames.\n* It can act as a transmitter or receiver at line rate.\n* It has a runtime environment to configure, and start and stop traffic flows.\n* It can display real time metrics for a number of ports.\n* It can generate packets in sequence by iterating source or destination MAC,\n  IP addresses or ports.\n* It can handle packets with UDP, TCP, ARP, ICMP, GRE, MPLS and\n  Queue-in-Queue.\n* It can be controlled remotely over a TCP connection.\n* It is configurable via Lua and can run command scripts to set up repeatable\n  test cases.\n* The software is fully available under a BSD licence.\n\n\nPktgen was created 2010 by Keith Wiles @ windriver.com, now at intel.com\n\n.. only:: html\n\n   See the sections below for more details.\n\n   Contents:\n\n\n.. toctree::\n\t:maxdepth: 1\n\n\tgetting_started.rst\n\trunning.rst\n\tusage_eal.rst\n\tusage_pktgen.rst\n\tcommands.rst\n\tcli.rst\n\tcli_lib.rst\n\tcli_design.rst\n\tscripts.rst\n\tlua.rst\n\tsocket.rst\n\tchanges.rst\n\tcopyright.rst\n\tlicense.rst\n"
  },
  {
    "path": "docs/source/copyright.rst",
    "content": ".. _copyright:\n\nCopyright and License\n=====================\n\n**Copyright(c) <2010-2026>, Intel Corporation All rights reserved.**\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n- Redistributions of source code must retain the above copyright\n  notice, this list of conditions and the following disclaimer.\n\n- Redistributions in binary form must reproduce the above copyright\n  notice, this list of conditions and the following disclaimer in\n  the documentation and/or other materials provided with the\n  distribution.\n\n- Neither the name of Intel Corporation nor the names of its\n  contributors may be used to endorse or promote products derived\n  from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\nFOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\nCOPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\nHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\nSTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\nOF THE POSSIBILITY OF SUCH DAMAGE.\n\nSPDX-License-Identifier: BSD-3-Clause\n\nPktgen: Created 2010 by Keith Wiles @ windriver.com, now at intel.com\n"
  },
  {
    "path": "docs/source/custom.css",
    "content": "/* Override readthedocs theme */\n\n/* Spacing before a list item must be bigger than spacing inside the item.\n * Complex list items start with a p.first element. */\n.section li > .first {\n\tmargin-top: 18px;\n}\n"
  },
  {
    "path": "docs/source/getting_started.rst",
    "content": ".. _getting_started:\n\nGetting Started with Pktgen\n===========================\n\nThis section contains instructions on how to get up and running with `DPDK\n<http://dpdk.org/>`_ and the ``pktgen`` traffic generator application.\n\nThese instructions relate to setting up DPDK and ``pktgen`` on an Ubuntu\ndesktop system. However, the should work on any recent Linux system with\nkernel support for hugeTLB/hugepages.\n\n\nSystem requirements\n-------------------\n\nThe main system requirement is that the DPDK packet processing framework is\nsupported.\n\nThe `DPDK Linux Getting Started Guide\n<https://doc.dpdk.org/guides/linux_gsg/index.html>`_ has a section on the\n`System Requirements\n<https://doc.dpdk.org/guides/linux_gsg/sys_reqs.html>`_ that explains the\nBIOS, System and Toolchain requirements to compile and run a DPDK based\napplication such as ``pktgen``. Ensure that your system meets those requirements\nbefore proceeding.\n\nYou will also need a `DPDK supported NIC <https://core.dpdk.org/supported/nics/>`_.\n\nThe current version of ``pktgen`` was developed and tested using Ubuntu 13.10\nx86_64, kernel version 3.5.0-25, on a Westmere Dual socket board running at\n2.4GHz with 12GB of ram 6GB per socket.\n\n\nSetting up hugeTLB/hugepage support\n-----------------------------------\n\nTo get hugeTLB/hugepage support your Linux kernel must be at least 2.6.33 and\nthe ``HUGETLBFS`` kernel option must be enabled.\n\nThe DPDK Linux Getting Started Guide has a section on the `Use of Hugepages in\nthe Linux Environment\n<https://doc.dpdk.org/guides/linux_gsg/sys_reqs.html#use-of-hugepages-in-the-linux-environment>`_.\n\nOnce you have made the required changed make sure you have HUGE TLB support in the kernel with the following commands::\n\n   $ grep -i huge /boot/config-2.6.35-24-generic\n   CONFIG_HUGETLBFS=y\n   CONFIG_HUGETLB_PAGE=y\n\n   $ grep -i huge /proc/meminfo\n\n   HugePages_Total:      128\n   HugePages_Free:       128\n   HugePages_Rsvd:        0\n   HugePages_Surp:        0\n   Hugepagesize:       2048 kB\n\n\nThe values in Total and Free may be different depending on your system.\n\nYou will need to edit the ``/etc/sysctl.conf`` file to setup the hugepages\nsize::\n\n   $ sudo vi /etc/sysctl.conf\n   Add to the bottom of the file:\n   vm.nr_hugepages=256\n\nYou can configure the ``vm.nr_hugepages=256`` as required. In some cases\nmaking it too small will effect the performance of pktgen or cause it to\nterminate on startup.\n\nYou will also need to edit the ``/etc/fstab`` file to mount the hugepages at\nstartup::\n\n   $ sudo vi /etc/fstab\n   Add to the bottom of the file:\n   huge /mnt/huge hugetlbfs defaults 0 0\n\n   $ sudo mkdir /mnt/huge\n   $ sudo chmod 777 /mnt/huge\n\nYou should also reboot your machine as the huge pages must be setup just after\nboot to make sure there is enough contiguous memory for the 2MB pages.\n\n.. Note::\n\n   If you start an application that makes extensive use of hugepages, such as\n   Eclipse or WR Workbench, before starting ``pktgen`` for the first time\n   after reboot, ``pktgen`` may fail to load. In this case you should close\n   the other application that is using hugepages.\n\n\n\nBIOS settings\n-------------\n\nIn the BIOS make sure that the HPET High Precision Event Timer is\nenabled. Also make sure hyper-threading is enabled. See the DPDK documentation\non `enabling additional BIOS functionality\n<https://doc.dpdk.org/guides/linux_gsg/enable_func.html#enabling-additional-functionality>`_\nfor more details.\n\n\nTerminal display\n----------------\n\nThe ``pktgen`` output display requires 132 columns and about 42 lines to\ndisplay correctly. The author uses an xterm of 132x42, but you can also have a\nlarger display and maybe a bit smaller. If you are displaying more then 4-6\nports then you will need a wider display.\n\nPktgen allows you to view a set ports via the ``page`` runtime command if they\ndo not all fit on the screen at one time, see :ref:`commands`.\n\nPktgen uses VT100 control codes display its output screens, which means your\nterminal must support VT100.\n\nIt is also best to set your terminal background to black when working with the\ndefault ``pktgen`` color scheme.\n\n\n\nGet the source code\n-------------------\n\nPktgen requires the DPDK source code to build.\n\nThe main ``dpdk`` and ``pktgen`` git repositories are hosted on `dpdk.org\n<http://www.dpdk.org/browse/>`_.\n\nThe ``dpdk`` code can be cloned as follows::\n\n   git clone git://dpdk.org/dpdk\n   # or:\n   git clone http://dpdk.org/git/dpdk\n\nThe ``pktgen`` code can be cloned as follows::\n\n   git clone git://dpdk.org/apps/pktgen-dpdk\n   # or:\n   git clone http://dpdk.org/git/apps/pktgen-dpdk\n\nIn the instructions below the repository close directories are referred to as\n``DPDKInstallDir`` and ``PktgenInstallDir``.\n\nYou will also require the Linux kernel headers to allow DPDK to build its\nkernel modules. On Ubuntu you can install them as follows (where the version\nmatches the kernel version)::\n\n   $ sudo apt-get install linux-headers-3.5.0-32-generic\n\nDPDK can also work with a ``libpcap`` driver which is sometimes useful for\ntesting without a real NIC or for low speed packet capture. Install the\n``libpcap`` development libs using your package manage. For example::\n\n    $ sudo apt-get install libpcap-dev\n\n\nBuild DPDK and Pktgen\n---------------------\n\nSet up the environmental variables required by DPDK::\n\n   export RTE_SDK=<DPDKInstallDir>\n   export RTE_TARGET=x86_64-native-linux-gcc\n   or\n   export RTE_TARGET=x86_64-native-linuxapp-gcc\n\n   # or use clang if you have it installed:\n   export RTE_TARGET=x86_64-native-linux-clang\n   or\n   export RTE_TARGET=x86_64-native-linuxapp-clang\n\nCreate the DPDK build tree::\n\n   $ cd $RTE_SDK\n   $ make install T=x86_64-native-linux-gcc\n   or\n   $ make install T=x86_64-native-linuxapp-gcc\n\nThis above command will create the `x86_64-pktgen-linux-gcc` directory in\nthe top level of the ``$RTE_SDK`` directory. It will also build the basic DPDK\nlibraries, kernel modules and build tree.\n\nPktgen can then be built as follows::\n\n   $ cd <PktgenInstallDir>\n   $ make\n\n\nSetting up your environment\n---------------------------\n\nIn the ``PktgenInstallDir``/tools level directory there is ``run.py`` script,\nwhich should be run once per boot with the -s option to setup the ports. The\nsame configuration file is also used to run pktgen by removing the -s option.\n\n.. Note::\n\n   The run.py script will do the sudo to root internally,\n   which means the ``sudo`` is not required.\n\nThe script contains the commands required to set up the environment::\n\n   $ cd <PktgenInstallDir>/tools\n   $ ./run.py -s default  # setup system using the cfg/default.cfg file\n\nThe run.py script is a python script and tries to configure the system to run a\nDPDK application. You will probably have to change the configuration files to match your\nsystem.\n\nTo run pktgen with the default.cfg configuration::\n\n   $ cd <PktgenInstallDir>/tools\n   $ run.py default\n\nThe ``run.py`` command use python data files to configure setup and run pktgen.\nThe configuration files are located in the ``PktgenInstallDir``/cfg directory. These\nfiles allow for setup and running pktgen and can be configured to match you system\nor new configuration files can be created.\n\nHere is the default.cfg file::\n\n   # Setup configuration\n   setup = {\n    'devices': [\n        '81:00.0 81:00.1 81:00.2 81:00.3',\n        '85:00.0 85:00.1 85:00.2 85:00.3',\n        '83:00.0'\n        ],\n\n    'opts': [\n        '-b igb_uio'\n        ]\n    }\n\n   # Run command and options\n   run = {\n    'dpdk': [\n        '-l 1,1-5,10-13',\n        '-n 4',\n        '--proc-type auto',\n        '--log-level 7',\n        '--socket-mem 2048,2048',\n        '--file-prefix pg'\n        ],\n\n    'blocklist': [\n        #'-b 81:00.0 -b 81:00.1 -b 81:00.2 -b 81:00.3',\n        #'-b 85:00.0 -b 85:00.1 -b 85:00.2 -b 85:00.3',\n        '-b 81:00.0 -b 81:00.1',\n        '-b 85:00.0 -b 85:00.1',\n        '-b 83:00.0'\n        ],\n\n    'pktgen': [\n        '-T',\n        '-P',\n        '-m [2:3].0',\n        '-m [4:5].1',\n        '-m [10:11].2',\n        '-m [12:13].3',\n        ],\n\n    'misc': [\n        '-f themes/black-yellow.theme'\n       ]\n   }\n\nWe have two sections one for setup and the other for running pktgen.\n\nThe ``modprobe uio`` command, in the setup script, loads the UIO support\nmodule into the kernel as well as loafing the igb-uio.ko module.\n\nThe two echo commands, in the setup script, set up the huge pages for a two\nsocket system. If you only have a single socket system then remove the second\necho command. The last command in the script is used to display the hugepage\nsetup.\n\nYou may also wish to edit your ``.bashrc``, ``.profile`` or ``.cshrc`` files to\npermanently add the environment variables that you set up above::\n\n   export RTE_SDK=<DPDKInstallDir>\n   export RTE_TARGET=x86_64-native-linux-gcc\n   or\n   export RTE_TARGET=x86_64-native-linux-appgcc\n\n\nRunning the application\n-----------------------\n\nOnce the above steps have been completed and the ``pktgen`` application has\nbeen compiled you can run it using the commands shown in the :ref:`running`\nsection.\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": "\nThe Pktgen Application\n======================\n\n**Pktgen**, (*Packet* *Gen*-erator) is a software based traffic generator\npowered by the DPDK fast packet processing framework.\n\nSome of the features of Pktgen are:\n\n* It is capable of generating 10Gbit wire rate traffic with 64 byte frames.\n* It can act as a transmitter or receiver at line rate.\n* It has a runtime environment to configure, and start and stop traffic flows.\n* It can display real time metrics for a number of ports.\n* It can generate packets in sequence by iterating source or destination MAC,\n  IP addresses or ports.\n* It can handle packets with UDP, TCP, ARP, ICMP, GRE, MPLS and\n  Queue-in-Queue.\n* It can be controlled remotely over a TCP connection.\n* It is configurable via Lua and can run command scripts to set up repeatable\n  test cases.\n* The software is fully available under a BSD licence.\n\n\nPktgen was created 2010 by Keith Wiles @ windriver.com, now at intel.com\n\n.. only:: html\n\n   See the sections below for more details.\n\n   Contents:\n\n\n.. toctree::\n\t:maxdepth: 1\n\n\t../../docs/QUICKSTART\n\tgetting_started.rst\n\trunning.rst\n\tusage_eal.rst\n\tusage_pktgen.rst\n\tcommands.rst\n\tcli.rst\n\tcli_lib.rst\n\tcli_design.rst\n\tscripts.rst\n\tlua.rst\n\tsocket.rst\n\tchanges.rst\n\tcopyright.rst\n\tlicense.rst\n"
  },
  {
    "path": "docs/source/license.rst",
    "content": ".. _license:\n\nThird Party License Notices\n===========================\n\nThis document contains third party intellectual property (IP) notices for the\nIntel Corp® Packet Generation distribution. Certain licenses and license\nnotices may appear in other parts of the product distribution in accordance\nwith the license requirements. \"Intel Corp\", is a registered trademark of Intel\nCorp. All other third-party trademarks are the property of their respective\nowners.\n\nLua\n---\n\nLua Version 5.3.0.\n\nLua is a lightweight, embeddable scripting language. Lua combines simple\nprocedural syntax with powerful data description constructs based on\nassociative arrays and extensible semantics. Lua is dynamically typed, runs by\ninterpreting bytecode for a register-based virtual machine, and has automatic\nmemory management with incremental garbage collection, making it ideal for\nconfiguration, scripting, and rapid prototyping.\n\n\n**Copyright(c) 1994-2015 Lua.org, PUC-Rio.**\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and tom permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n\nWarranty Disclaimer & Limitation of Liability\n---------------------------------------------\n\n**OPEN SOURCE SOFTWARE**: \"Open Source Software\" is software that may be\ndelivered with the Software and is licensed in accordance with open source\nlicenses, including, but not limited to, any software licensed under Academic\nFree License, Apache Software License, Artistic License, BSD License, GNU\nGeneral Public License, GNU Library General Public License, GNU Lesser Public\nLicense, Mozilla Public License, Python License or any other similar license.\n\n**DISCLAIMER OF WARRANTIES**: WIND RIVER AND ITS LICENSORS DISCLAIM ALL\nWARRANTIES, EXPRESS, IMPLIED AND STATUTORY INCLUDING, WITHOUT LIMITATION, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE\nAND NON-INFRINGEMENT OF THIRD PARTY RIGHTS WITH RESPECT TO OPEN SOURCE\nSOFTWARE. NO ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY WIND RIVER, ITS\nDEALERS, DISTRIBUTORS, AGENTS OR EMPLOYEES SHALL IN ANY WAY INCREASE THE SCOPE\nOF THIS WARRANTY. Some jurisdictions do not allow the limitation or exclusion\nof implied warranties or how long an implied warranty may last, so the above\nlimitations may not apply to Customer. This warranty gives Customer specific\nlegal rights and Customer may have other rights that vary from jurisdiction to\njurisdiction.\n\n**LIMITATION OF LIABILITY**: WIND RIVER AND ITS LICENSORS SHALL NOT BE LIABLE\nFOR ANY INCIDENTAL, SPECIAL, CONSEQUENTIAL OR INDIRECT DAMAGES OF ANY KIND\n(INCLUDING DAMAGES FOR INTERRUPTION OF BUSINESS, PROCUREMENT OF SUBSTITUTE\nGOODS, LOSS OF PROFITS, OR THE LIKE) REGARDLESS OF THE FORM OF ACTION WHETHER\nIN CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT PRODUCT LIABILITY OR ANY\nOTHER LEGAL OR EQUITABLE THEORY, ARISING OUT OF OR RELATED TO OPEN SOURCE\nSOFTWARE, EVEN IF WIND RIVER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGES. IN NO EVENT WILL WIND RIVER’S AGGREGATE CUMULATIVE LIABILITY FOR ANY\nCLAIMS ARISING OUT OF OR RELATED TO OPEN SOURCE SOFTWARE EXCEED ONE HUNDRED\nDOLLARS ($100). Some jurisdictions do not allow the exclusion or limitation of\nincidental or consequential damages so this limitation and exclusion may not\napply to Customer.\n\nTHE WARRANTY DISCLAIMER AND LIMITED LIABILITY ARE FUNDAMENTAL ELEMENTS OF THE\nBASIS OF THE BARGAIN BETWEEN WIND RIVER AND CUSTOMER. WIND RIVER WOULD NOT BE\nABLE TO PROVIDE OPEN SOURCE SOFTWARE WITHOUT SUCH LIMITATIONS.\n"
  },
  {
    "path": "docs/source/lua.rst",
    "content": ".. _lua:\n\nUsing Lua with Pktgen\n=====================\n\nLua is a high level dynamic programming language. It is small and lightweight\nand can easily be embedded in applications written in other languages. It is\nalso suitable for loading and wrapping dynamic libraries.\n\nLua is used in ``pktgen`` to script and configure the application and also to\nplug into DPDK functions to expose configuration and statistics.\n\n\nThe following are some of the examples included in the ``test`` directory of\n``pktgen`` repository.\n\n\n\nExample: Hello World\n--------------------\n\nA simple \"hello world\" example to ensure that everything is working correctly:\n\n\n.. literalinclude:: ../../test/hello-world.lua\n   :language: lua\n\n\n\nExample: Info\n-------------\n\nA simple example to print out some metadata and configuration information from\n``pktgen``:\n\n.. literalinclude:: ../../test/info.lua\n   :language: lua\n\n\nExample: More Info\n------------------\n\nAnother example to print out data from a running ``pktgen`` instance:\n\n.. literalinclude:: ../../test/test3.lua\n   :language: lua\n\n\nExample: Sequence\n-----------------\n\nAn example to set a packet sequence:\n\n.. literalinclude:: ../../test/set_seq.lua\n   :language: lua\n   :tab-width: 4\n\n\nExample: Main\n-------------\n\nA more complex example showing most of the features available via the Lua interface and also show interaction with the user:\n\n.. literalinclude:: ../../test/main.lua\n   :language: lua\n"
  },
  {
    "path": "docs/source/meson.build",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2018-2026> Intel Corporation\n\nsphinx = find_program('sphinx-build', required: get_option('enable_docs'))\n\nif not sphinx.found()\n\tsubdir_done()\nendif\n\nhtmldir = join_paths(get_option('datadir'), 'docs', 'dpdk')\nhtml_guides = custom_target('html_guides',\n\tinput: files('index.rst'),\n\toutput: 'html',\n\tcommand: [sphinx_wrapper, sphinx, meson.current_source_dir(), meson.current_build_dir()],\n\tdepfile: '.html.d',\n\tbuild_by_default: get_option('enable_docs'),\n\tinstall: get_option('enable_docs'),\n\tinstall_dir: htmldir)\n\ninstall_data(files('custom.css'),\n\t\t\tinstall_dir: join_paths(htmldir,'_static', 'css'))\n\ndoc_targets += html_guides\ndoc_target_names += 'HTML_Guides'\n"
  },
  {
    "path": "docs/source/running.rst",
    "content": ".. _running:\n\nRunning Pktgen\n==============\n\n\nA sample commandline to start a ``pktgen`` instance would look something like\nthe following, which you may need 'sudo -E' added to the front if not superuser.\nThe -E option of sudo passes environment variables to sudo shell as the scripts\nneed the RTE_SDK and RTE_TARGET variables::\n\n   ./app/pktgen -l 0-4 -n 3 -- -P -m \"[1:3].0, [2:4].1\n\nPktgen, like other DPDK applications splits its commandline arguments into\narguments for the DPDK Environmental Abstraction Layer (EAL) and arguments for\nthe application itself. The two sets of arguments are separated using the\nstandard convention of ``--`` as shown above.\n\nThese commandline arguments are explained in the :ref:`usage_eal` and\n:ref:`usage_pktgen`.\n\nThe output when running ``pktgen`` will look something like the following::\n\n   -----------------------\n\n   Copyright notices\n\n   -----------------------\n   EAL: Detected lcore 0 as core 0 on socket 0\n   EAL: Detected lcore 1 as core 1 on socket 0\n   ...\n   EAL: PCI device 0000:07:00.1 on NUMA socket 0\n   EAL:   probe driver: 8086:1521 rte_igb_pmd\n   EAL:   0000:07:00.1 not managed by UIO driver, skipping\n   Lua 5.2.1  Copyright (C) 1994-2012 Lua.org, PUC-Rio\n\n   >>> Packet Burst 16, RX Desc 256, TX Desc 256, mbufs/port 2048, mbuf cache 256\n\n   === port to lcore mapping table (# lcores 5) ===\n      lcore:     0     1     2     3     4\n   port   0:  D: T  1: 0  0: 0  0: 1  0: 0 =  1: 1\n   port   1:  D: T  0: 0  1: 0  0: 0  0: 1 =  1: 1\n   Total   :  0: 0  1: 0  1: 0  0: 1  0: 1\n       Display and Timer on lcore 0, rx:tx counts per port/lcore\n\n   Configuring 6 ports, MBUF Size 1984, MBUF Cache Size 256\n   Lcore:\n       1, type  RX , rx_cnt  1, tx_cnt  0, RX (pid:qid): ( 0: 0) , TX (pid:qid):\n       2, type  RX , rx_cnt  1, tx_cnt  0, RX (pid:qid): ( 1: 0) , TX (pid:qid):\n       3, type  TX , rx_cnt  0, tx_cnt  1, RX (pid:qid): , TX (pid:qid): ( 0: 0)\n       4, type  TX , rx_cnt  0, tx_cnt  1, RX (pid:qid): , TX (pid:qid): ( 1: 0)\n\n   Port :\n       0, nb_lcores  2, private 0x7d08d8, lcores:  1  3\n       1, nb_lcores  2, private 0x7d1c48, lcores:  2  4\n\n\n   Initialize Port 0 -- TxQ 1, RxQ 1,  Src MAC 90:e2:ba:5a:f7:90\n       Create: Default RX  0:0 - Mem (MBUFs 2048 x (1984 + 64)) + 790720 = 4869 KB\n       Create: Default TX  0:0 - Mem (MBUFs 2048 x (1984 + 64)) + 790720 = 4869 KB\n       Create: Range TX    0:0 - Mem (MBUFs 2048 x (1984 + 64)) + 790720 = 4869 KB\n       Create: Sequence TX 0:0 - Mem (MBUFs 2048 x (1984 + 64)) + 790720 = 4869 KB\n       Create: Special TX  0:0 - Mem (MBUFs   64 x (1984 + 64)) + 790720 =  901 KB\n\n                                                      Port memory used =  20373 KB\n\n   Initialize Port 1 -- TxQ 1, RxQ 1,  Src MAC 90:e2:ba:5a:f7:91\n       Create: Default RX  1:0 - Mem (MBUFs 2048 x (1984 + 64)) + 790720 = 4869 KB\n       Create: Default TX  1:0 - Mem (MBUFs 2048 x (1984 + 64)) + 790720 = 4869 KB\n       Create: Range TX    1:0 - Mem (MBUFs 2048 x (1984 + 64)) + 790720 = 4869 KB\n       Create: Sequence TX 1:0 - Mem (MBUFs 2048 x (1984 + 64)) + 790720 = 4869 KB\n       Create: Special TX  1:0 - Mem (MBUFs   64 x (1984 + 64)) + 790720 =  901 KB\n\n                                                      Port memory used =  20373 KB\n                                                     Total memory used =  40746 KB\n\n   Port  0: Link Up - speed 10000 Mbps - full-duplex <Enable promiscuous mode>\n   Port  1: Link Up - speed 10000 Mbps - full-duplex <Enable promiscuous mode>\n\n\n   === Display processing on lcore 0\n   === RX processing on lcore  1, rxcnt 1, port/qid, 0/0\n   === RX processing on lcore  2, rxcnt 1, port/qid, 1/0\n   === TX processing on lcore  3, txcnt 1, port/qid, 0/0\n   === TX processing on lcore  4, txcnt 1, port/qid, 1/0\n   ...\n\n\nOnce ``pktgen`` is running you will see an output like the following::\n\n\t| Ports 0-3 of 8   <Main Page>  Copyright(c) <2010-2026>, Intel Corporation\n\t  Flags:Port       :   P--------------:0   P--------------:1   P--------------:2   P--------------:3\n\tLink State         :       <UP-10000-FD>       <UP-10000-FD>       <UP-10000-FD>       <UP-10000-FD>     ----TotalRate----\n\tPkts/s Max/Rx      :                 0/0                 0/0                 0/0                 0/0                   0/0\n\t       Max/Tx      :                 0/0                 0/0                 0/0                 0/0                   0/0\n\tMBits/s Rx/Tx      :                 0/0                 0/0                 0/0                 0/0                   0/0\n\tBroadcast          :                   0                   0                   0                   0\n\tMulticast          :                   0                   0                   0                   0\n\t  64 Bytes         :                   0                   0                   0                   0\n\t  65-127           :                   0                   0                   0                   0\n\t  128-255          :                   0                   0                   0                   0\n\t  256-511          :                   0                   0                   0                   0\n\t  512-1023         :                   0                   0                   0                   0\n\t  1024-1522        :                   0                   0                   0                   0\n\tRunts/Jumbos       :                 0/0                 0/0                 0/0                 0/0\n\tErrors Rx/Tx       :                 0/0                 0/0                 0/0                 0/0\n\tTotal Rx Pkts      :                   0                   0                   0                   0\n\t      Tx Pkts      :                   0                   0                   0                   0\n\t      Rx MBs       :                   0                   0                   0                   0\n\t      Tx MBs       :                   0                   0                   0                   0\n\tARP/ICMP Pkts      :                 0/0                 0/0                 0/0                 0/0\n\t                   :\n\tPattern Type       :             abcd...             abcd...             abcd...             abcd...\n\tTx Count/% Rate    :       Forever /100%       Forever /100%       Forever /100%       Forever /100%\n\tPktSize/Rx:Tx Burst:           64 /   32           64 /   32           64 /   32           64 /   32\n\tSrc/Dest Port      :         1234 / 5678         1234 / 5678         1234 / 5678         1234 / 5678\n\tPkt Type:VLAN ID   :     IPv4 / TCP:0001     IPv4 / TCP:0001     IPv4 / TCP:0001     IPv4 / TCP:0001\n\tDst  IP Address    :         192.168.1.1         192.168.0.1         192.168.3.1         192.168.2.1\n\tSrc  IP Address    :      192.168.0.1/24      192.168.1.1/24      192.168.2.1/24      192.168.3.1/24\n\tDst MAC Address    :   3c:fd:fe:9c:5c:d9   3c:fd:fe:9c:5c:d8   3c:fd:fe:9c:5c:db   3c:fd:fe:9c:5c:da\n\tSrc MAC Address    :   3c:fd:fe:9c:5c:d8   3c:fd:fe:9c:5c:d9   3c:fd:fe:9c:5c:da   3c:fd:fe:9c:5c:db\n\tVendID/PCI Addr    :   8086:1572/04:00.0   8086:1572/04:00.1   8086:1572/04:00.2   8086:1572/04:00.3\n\n\t-- Pktgen Ver: 3.2.4 (DPDK 17.05.0-rc0)  Powered by DPDK ---------------\n\n\tPktgen:/>\n\n\nThe flags displayed on the top line for each port are::\n\n       Flags: P---------------- - Promiscuous mode enabled\n               E                - ICMP Echo enabled\n                A               - Send ARP Request flag\n                 G              - Send Gratuitous ARP flag\n                  C             - TX Cleanup flag\n                   p            - PCAP enabled flag\n                    S           - Send Sequence packets enabled\n                     R          - Send Range packets enabled\n                      D         - DPI Scanning enabled (If Enabled)\n                       I        - Process packets on input enabled\n                        *       - Using TAP interface for this port can be [-rt*]\n                         L      - Send Latency packets                          V     - Send VLAN ID tag\n                          M     - Send MPLS header\n                          Q     - Send Q-in-Q tags\n                           g    - Process GARP packets\n                            g   - Perform GRE with IPv4 payload\n                            G   - Perform GRE with Ethernet payload\n                             C  - Capture received packets\n                              R - Random bitfield(s) are applied\n\n      Notes. <state> - Use enable|disable or on|off to set the state.\n          <portlist> - a list of ports (no spaces) as 2,4,6-9,12 or 3-5,8 or 5 or the word 'all'\n\t       Color best seen on a black background for now\n\t       To see a set of example Lua commands see the files in wr-examples/pktgen/test\n\nThe ``pktgen`` default colors and theme work best on a black background. If\nrequired, it is possible to set other color themes, (see :ref:`commands`).\n"
  },
  {
    "path": "docs/source/scripts.rst",
    "content": ".. _scripts:\n\n\nRunning Script Files\n====================\n\nPktgen can read and run files with default values and configurations via\nthe ``-f`` commandline option (:ref:`usage_pktgen`).\n\nThese files can either be ``.pkt`` files with Pktgen :ref:`runtime commands\n<commands>` as shown in the previous section or ``.lua`` files with the same\ncommands and options in Lua syntax.\n\nFor example here is a ``pktgen`` instance that read a ``.pkt`` file::\n\n   pktgen -l 0-4 -n 3 --proc-type auto --socket-mem 128,128 -- \\\n                -P -m \"[1:3].0, [2:4].1\" -f test/set_seq.pkt\n\nWhere the ``test/set_seq.pkt`` (included in the ``pktgen`` repository) is as\nfollows::\n\n   seq 0 all 0000:4455:6677 0000:1234:5678 10.11.0.1 10.10.0.1/16 5 6 ipv4 udp 1 128\n   set all seqCnt 1\n\nThe Lua version (``test/set_seq.lua`` in ``pktgen`` repository) is clearer and\nallows extension through standard Lua or user defined functions:\n\n.. literalinclude:: ../../test/set_seq.lua\n   :language: lua\n   :lines: 3-\n   :tab-width: 4\n\nThe Lua interface is explained in the next section :ref:`lua`.\n"
  },
  {
    "path": "docs/source/socket.rst",
    "content": ".. _socket:\n\nSocket Support for Pktgen\n=========================\n\nPktgen provides a TCP socket connection to allow you to control it from a\nremote console or program.\n\nThe TCP connection uses port 22022, 0x5606, and presents a Lua command shell\ninterface.\n\nIf you telnet on port 22022 to a machine running ``pktgen`` you will get a Lua\ncommand shell like interface. This interface does not have a command line\nprompt, but you can issue Lua code or load script files from the local disk of\nthe machine. You can also send programs to the remote ``pktgen`` machine to\nload scripts and run scripts.\n\nAnother way to connect remotely to ``pktgen`` is to use the ``socat`` program\non a Linux machine::\n\n   $ socat -d -d READLINE TCP4:localhost:22022\n\nThis will create a connection and then wait for Lua command scripts. You can\nalso send ``pktgen`` a command script file and display the output::\n\n\n   $ socat - TCP4:localhost:22022 < test/hello-world.lua\n\n   Lua Version      : Lua 5.3\n   Pktgen Version   : 2.9.0\n   Pktgen Copyright : Copyright(c) `<2010-2026>`, Intel Corp.\n   Pktgen Authors   : Keith Wiles @ Wind River Systems\n\n   Hello World!!!!\n\nWhere the the ``test/hello-world.lua`` looks like this:\n\n.. literalinclude:: ../../test/hello-world.lua\n   :language: lua\n\nHere is another ``socat`` example which loads a file from the local disk where\n``pktgen`` is running and then we execute the file with a user defined\nfunction::\n\n   $ socat READLINE TCP4:172.25.40.163:22022\n   f,e = loadfile(\"test/hello-world.lua\")\n   f()\n   Lua Version      : Lua 5.3\n   Pktgen Version   : 2.9.0\n   Pktgen Copyright : Copyright(c) `<2010-2026>`, Intel Corp.\n   Pktgen Authors   : Keith Wiles @ Wind River Systems\n\n   Hello World!!!!\n   <Control-D>\n\n\nYou can also just send it commands via echo::\n\n   $ echo \"f,e = loadfile('test/hello-world.lua'); f();\" \\\n          | socat - TCP4:172.25.40.163:22022\n   Lua Version      : Lua 5.3\n   Pktgen Version   : 2.9.0\n   Pktgen Copyright : Copyright(c) `<2010-2026>`, Intel Corp.\n   Pktgen Authors   : Keith Wiles @ Wind River Systems\n\n   Hello World!!!!\n"
  },
  {
    "path": "docs/source/usage_eal.rst",
    "content": ".. _usage_eal:\n\n\nEAL Commandline Options\n=======================\n\nPktgen, like other DPDK applications splits commandline arguments into\narguments for the DPDK Environmental Abstraction Layer (EAL) and arguments for\nthe application itself. The two sets of arguments are separated using the\nstandard convention of ``--``::\n\nPktgen executable is located at ``./app/app/${RTE_TARGET}/pktgen``\n\n   pktgen -l 0-4 -n 3 -- -P -m \"[1:3].0, [2:4].1\n\nThe usual EAL commandline usage for ``pktgen`` is::\n\n   pktgen -c COREMASK -n NUM \\\n                [-m NB] \\\n                [-r NUM] \\\n                [-b <domain:bus:devid.func>] \\\n                [--proc-type primary|secondary|auto] -- [pktgen options]\n\nThe full list of EAL arguments are::\n\n   EAL options:\n     -c COREMASK         : A hexadecimal bitmask of cores to run on\n     -n NUM              : Number of memory channels\n     -v                  : Display version information on startup\n     -d LIB.so           : Add driver (can be used multiple times)\n     -m MB               : Memory to allocate (see also --socket-mem)\n     -r NUM              : Force number of memory ranks (don't detect)\n     --xen-dom0          : Support application running on Xen Domain0 without\n                           hugetlbfs\n     --syslog            : Set syslog facility\n     --socket-mem        : Memory to allocate on specific\n                           sockets (use comma separated values)\n     --huge-dir          : Directory where hugetlbfs is mounted\n     --proc-type         : Type of this process\n     --file-prefix       : Prefix for hugepage filenames\n     --pci-blocklist, -b : Add a PCI device in block list.\n                           Prevent EAL from using this PCI device. The argument\n                           format is <domain:bus:devid.func>.\n     --pci-allowlist, -w : Add a PCI device in allow list.\n                           Only use the specified PCI devices. The argument\n                           format is <[domain:]bus:devid.func>. This option\n                           can be present several times (once per device).\n                           NOTE: PCI allowlist cannot be used with -b option\n     --vdev              : Add a virtual device.\n                           The argument format is <driver><id>[,key=val,...]\n                           (ex: --vdev=eth_pcap0,iface=eth2).\n     --vmware-tsc-map    : Use VMware TSC map instead of native RDTSC\n     --base-virtaddr     : Specify base virtual address\n     --vfio-intr         : Specify desired interrupt mode for VFIO\n                           (legacy|msi|msix)\n     --create-uio-dev    : Create /dev/uioX (usually done by hotplug)\n\n   EAL options for DEBUG use only:\n     --no-huge           : Use malloc instead of hugetlbfs\n     --no-pci            : Disable pci\n     --no-hpet           : Disable hpet\n     --no-shconf         : No shared config (mmap'd files)\n\n\nThe ``-c COREMASK`` and ``-n NUM`` arguments are required. The other arguments\nare optional.\n\nPktgen requires 2 logical cores (lcore) in order to run. The first lcore, 0,\nis used for the ``pktgen`` commandline, for timers and for displaying the\nruntime metrics text on the terminal. The additional lcores ``1-n`` are used\nto do the packet receive and transmits along with anything else related to\npackets.\n\nYou do not need to start at the actual system lcore 0. The application will\nuse the first lcore in the coremask bitmap.\n\n\nA more typical commandline to start a ``pktgen`` instance would be::\n\n   pktgen -l 0-4 -n 3 --proc-type auto --socket-mem 256,256\n                -b 0000:03:00.0 -b 0000:03:00.1 \\\n                 --file-prefix pg \\\n                -- -P -m \"[1:3].0, [2:4].1\n\nThe coremask ``-c 0x1f`` (0b11111) indicates 5 lcores are used, as the first\nlcore is used by Pktgen for display and timers.\n\nThe ``--socket-mem 256,256`` DPDK command will allocate 256M from each CPU\n(two in this case).\n\nThe :ref:`usage_pktgen` are shown in the next section.\n"
  },
  {
    "path": "docs/source/usage_pktgen.rst",
    "content": ".. _usage_pktgen:\n\nPktgen Commandline Options\n==========================\n\nThe Pktgen commandline usage is::\n\n   ./app/app/``$(target}``/pktgen [EAL options] -- \\\n\t\t\t\t[-h] [-P] [-G] [-T] [-f cmd_file] \\\n\t\t\t\t[-l log_file] [-s P:PCAP_file] [-m <string>]\n\nThe :ref:`usage_eal` were shown in the previous section.\n\nThe ``pktgen`` arguments are::\n\nUsage: pktgen [EAL options] -- [-h] [-P] [-G] [-T] [-f cmd_file] [-l log_file] [-s P:PCAP_file] [-m <string>]\n  -s P:file    PCAP packet stream file, 'P' is the port number\n  -f filename  Command file (.pkt) to execute or a Lua script (.lua) file\n  -l filename  Write log to filename\n  -I           use CLI\n  -P           Enable PROMISCUOUS mode on all ports\n  -g address   Optional IP address and port number default is (localhost:0x5606)\n               If -g is used that enable socket support as a server application\n  -G           Enable socket support using default server values localhost:0x5606\n  -N           Enable NUMA support\n  -T           Enable the color output\n  -h           Display the help information\n\n\nWhere the options are:\n\n* ``-h``: Display the usage/help information shown above::\n\n     lspci | grep Ethernet\n\n  This shows a list of all ports in the system. Some ports may not be usable\n  by DPDK/Pktgen.  The first port listed is bit 0 or least signification bit\n  in the ``-c`` EAL coremask. Another method is to compile and run the DPDK\n  sample application ``testpmd`` to list out the ports DPDK is able to use::\n\n     ./test_pmd -c 0x3 -n 2\n\n* ``-s P:file``: The PCAP packet file to stream. ``P`` is the port number.\n\n* ``-f filename``: The script command file (.pkt) to execute or a Lua script\n  (.lua) file. See :ref:`scripts`.\n\n* ``-l filename``: The filename to write a log to.\n\n* ``-P``: Enable PROMISCUOUS mode on all ports.\n\n* ``-G``: Enable socket support using default server values of\n  localhost:0x5606. See :ref:`socket`.\n\n* ``-g address``: Same as ``-G`` but with an optional IP address and port\n  number. See :ref:`socket`.\n\n* ``-T``: Enable color terminal output in VT100\n\n* ``-N``: Enable NUMA support.\n\n* ``-m <string>``: Matrix for mapping ports to logical cores. The format of the\n  port mapping string is defined with a BNF-like grammar as follows::\n\n      BNF: (or kind of BNF)\n      <matrix-string>   := \"\"\" <lcore-port> { \",\" <lcore-port>} \"\"\"\n      <lcore-port>      := <lcore-list> \".\" <port-list>\n      <lcore-list>      := \"[\" <rx-list> \":\" <tx-list> \"]\"\n      <port-list>       := \"[\" <rx-list> \":\" <tx-list>\"]\"\n      <rx-list>         := <num> { \"/\" (<num> | <list>) }\n      <tx-list>         := <num> { \"/\" (<num> | <list>) }\n      <list>            := <num> { \"/\" (<range> | <list>) }\n      <range>           := <num> \"-\" <num> { \"/\" <range> }\n      <num>             := <digit>+\n      <digit>           := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9\n\n  For example::\n\n      1.0, 2.1, 3.2                 - core 1 handles port 0 rx/tx,\n                                      core 2 handles port 1 rx/tx\n                                      core 3 handles port 2 rx/tx\n      1.[0-2], 2.3, ...             - core 1 handle ports 0,1,2 rx/tx,\n                                      core 2 handle port 3 rx/tx\n      [0-1].0, [2/4-5].1, ...       - cores 0-1 handle port 0 rx/tx,\n                                      cores 2,4,5 handle port 1 rx/tx\n      [1:2].0, [4:6].1, ...         - core 1 handles port 0 rx,\n                                      core 2 handles port 0 tx,\n      [1:2].[0-1], [4:6].[2/3], ... - core 1 handles port 0 & 1 rx,\n                                      core 2 handles port  0 & 1 tx\n      [1:2-3].0, [4:5-6].1, ...     - core 1 handles port 0 rx, cores 2,3 handle port 0 tx\n                                      core 4 handles port 1 rx & core 5,6 handles port 1 tx\n      [1-2:3].0, [4-5:6].1, ...     - core 1,2 handles port 0 rx, core 3 handles port 0 tx\n                                      core 4,5 handles port 1 rx & core 6 handles port 1 tx\n      [1-2:3-5].0, [4-5:6/8].1, ... - core 1,2 handles port 0 rx, core 3,4,5 handles port 0 tx\n                                      core 4,5 handles port 1 rx & core 6,8 handles port 1 tx\n      [1:2].[0:0-7], [3:4].[1:0-7], - core 1 handles port 0 rx, core 2 handles ports 0-7 tx\n                                      core 3 handles port 1 rx & core 4 handles port 0-7 tx\n      BTW: you can use \"{}\" instead of \"[]\" as it does not matter to the syntax.\n\nGrouping can use ``{}`` instead of ``[]`` if required.\n\nMultiple Instances of Pktgen or other application\n=================================================\n\nOne possible solution I use and if you have enough ports available to use.\nLets say you need two ports for your application, but you have 4 ports in\nyour system. I physically loop back the cables to have port 0 connect to\nport 2 and port 1 connected to port 3. Now I can give two ports to my\napplication and two ports to Pktgen.\n\nSetup if pktgen and your application you have to startup each one a bit\ndifferently to make sure they share the resources like memory and the\nports. I will use two Pktgen running on the same machine, which just means\nyou have to setup your application as one of the applications.\n\nIn my machine I have 8 10G ports and 72 lcores between 2 sockets. Plus I\nhave 1024 hugepages per socket for a total of 2048.\n\n  Example commands::\n\n     # lspci | grep Ether\n     06:00.0 Ethernet controller: Intel Corporation Ethernet Converged Network Adapter X520-Q1 (rev 01)\n     06:00.1 Ethernet controller: Intel Corporation Ethernet Converged Network Adapter X520-Q1 (rev 01)\n     08:00.0 Ethernet controller: Intel Corporation Ethernet Converged Network Adapter X520-Q1 (rev 01)\n     08:00.1 Ethernet controller: Intel Corporation Ethernet Converged Network Adapter X520-Q1 (rev 01)\n     09:00.0 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)\n     09:00.1 Ethernet controller: Intel Corporation I350 Gigabit Network Connection (rev 01)\n     83:00.1 Ethernet controller: Intel Corporation DH8900CC Null Device (rev 21)\n     87:00.0 Ethernet controller: Intel Corporation Ethernet Converged Network Adapter X520-Q1 (rev 01)\n     87:00.1 Ethernet controller: Intel Corporation Ethernet Converged Network Adapter X520-Q1 (rev 01)\n     89:00.0 Ethernet controller: Intel Corporation Ethernet Converged Network Adapter X520-Q1 (rev 01)\n     89:00.1 Ethernet controller: Intel Corporation Ethernet Converged Network Adapter X520-Q1 (rev 01)\n\n     ./app/app/${target}/pktgen -l 2-11 -n 3 --proc-type auto \\\n\t\t--socket-mem 512,512 --file-prefix pg1 \\\n\t\t-b 09:00.0 -b 09:00.1 -b 83:00.1 -b 06:00.0 \\\n\t\t-b 06:00.1 -b 08:00.0 -b 08:00.1 -- \\\n\t\t-T -P -m \"[4:6].0, [5:7].1, [8:10].2, [9:11].3\" \\\n\t\t-f themes/black-yellow.theme\n\n     ./app/app/${target}/pktgen -l 2,4-11 -n 3 --proc-type auto \\\n\t\t--socket-mem 512,512 --file-prefix pg2 \\\n\t\t-b 09:00.0 -b 09:00.1 -b 83:00.1 -b 87:00.0 \\\n\t\t-b 87:00.1 -b 89:00.0 -b 89:00.1 -- \\\n\t\t-T -P -m \"[12:16].0, [13:17].1, [14:18].2, [15:19].3\" \\\n\t\t-f themes/black-yellow.theme\n\nNotice I block list the three onboard devices and then block list the\nother 4 ports I will not be using for each of the pktgen instances.\n\nI need 8+1 lcores for each instance for Pktgen use. The -c option of ff2\nand FF004 lcores, the ff value are used for port handling and the 2/4 is\nused because pktgen needs the first lcore for display and timers.\n\nThe -m option then assigns lcores to the ports.\n\nThe information from above is taken from two new files pktgen-initial.sh\nand pktgen-worker.sh, have a look at them and adjust as you need.\n\nPktgen can also be configured using the :ref:`commands`.\n"
  },
  {
    "path": "examples/meson.build",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2023-2026> Intel Corporation\n\nif fgen_dep.found()\n    subdirs =['pktperf']\n    foreach d:subdirs\n        subdir(d)\n    endforeach\nendif\n"
  },
  {
    "path": "examples/pktperf/README.md",
    "content": "## PKTPERF example application to verify Rx/Tx performance with multiple cores\n\nThe `pktperf` example application is used to verify Rx/Tx performance with multiple cores or queues. The application is configured from the command line arguments and does not have CLI to configure on the fly.\n\n---\n```console\n**Copyright &copy; <2023-2026>, Intel Corporation. All rights reserved.**\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\n- Redistributions of source code must retain the above copyright\n  notice, this list of conditions and the following disclaimer.\n\n- Redistributions in binary form must reproduce the above copyright\n  notice, this list of conditions and the following disclaimer in\n  the documentation and/or other materials provided with the\n  distribution.\n\n- Neither the name of Intel Corporation nor the names of its\n  contributors may be used to endorse or promote products derived\n  from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\nFOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\nCOPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\nHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\nSTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED\nOF THE POSSIBILITY OF SUCH DAMAGE.\n\nSPDX-License-Identifier: BSD-3-Clause\n\npktperf: Created 2023 by Keith Wiles @ Intel.com\n```\n---\n\n### Overview\n\nThe implementation uses multiple Rx/Tx queues per port, which is required to achieve maximum performance for receiving and transmitting frames. If your NIC does not support multiple queues, but does support multiple `virtual functions` per physical port, then you should create multiple `virtual functions` network devices and configure them using the `-m` mapping parameters. A single core can only be attached to one network device or port, which means you can not have a single core processing more then one network device or port.\n\nHaving a single core processing more then one network device or port was almost never used and made the code more complicated. This is why the implementation was removed from the `pktperf` application.\n\n### Command line arguments\n\nThe command line arguments contain the standard DPDK arguments with the pktperf parameters after the '--' option. Please look at the DPDK documentation for the EAL arguments. The pktperf parameter can be displayed using the -h or --help option after the '--' option.\n\n```console\npktperf [EAL options] -- [-b burst] [-s size] [-r rate] [-d rxd/txd] [-m map] [-T secs] [-P] [-M mbufs] [-v] [-h]\n\t-b|--burst-count <burst> Number of packets for Rx/Tx burst (default 32)\n\t-s|--pkt-size <size>     Packet size in bytes (default 64) includes FCS bytes\n\t-r|--rate <rate>         Packet TX rate percentage 0=off (default 100)\n\t-d|--descriptors <Rx/Tx> Number of RX/TX descriptors (default 1,024/1,024)\n\t-m|--map <map>           Core to Port/queue mapping '[Rx-Cores:Tx-Cores].port'\n\t-T|--timeout <secs>      Timeout period in seconds (default 1 second)\n\t-P|--no-promiscuous      Turn off promiscuous mode (default On)\n\t-M|--mbuf-count <count>  Number of mbufs to allocate (default 8,192, max 131,072)\n\t-v|--verbose             Verbose output\n\t-h|--help                Print this help\n```\n### Command line example\n\n```bash\nsudo builddir/examples/pktperf/pktperf -l 1,2-9,14-21 -a 03:00.0 -a 82:00.0 -- -m \"2-5:6-9.0\" -m \"14-17:18-21.1\"\n```\n\n## CPU/Socket layout\n\n```bash\n======================================================================\nCore and Socket Information (as reported by '/sys/devices/system/cpu')\n======================================================================\n\ncores =  [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14]\nsockets =  [0, 1]\n\n        Socket 0        Socket 1\n        --------        --------\nCore 0  [0, 28]         [14, 42]\nCore 1  [1, 29]         [15, 43]\nCore 2  [2, 30]         [16, 44]\nCore 3  [3, 31]         [17, 45]\nCore 4  [4, 32]         [18, 46]\nCore 5  [5, 33]         [19, 47]\nCore 6  [6, 34]         [20, 48]\nCore 8  [7, 35]         [21, 49]\nCore 9  [8, 36]         [22, 50]\nCore 10 [9, 37]         [23, 51]\nCore 11 [10, 38]        [24, 52]\nCore 12 [11, 39]        [25, 53]\nCore 13 [12, 40]        [26, 54]\nCore 14 [13, 41]        [27, 55]\n```\n\nThe `-m` argument defines the core to port mapping `<RxCores:TxCores>.<port>` the `:` (colon) is used to specify the the Rx and Tx cores for the port mapping. Leaving off the ':' is equivalent to running Rx and Tx processing on the specified core(s). When present the left side denotes the core(s) to use for receive processing and the right side denotes the core(s) to use for transmit processing.\n\n### Example console output\n\n```bash\nPort    : Rate Statistics per queue (-)\n 0 >> Link up at 40 Gbps FDX Autoneg, WireSize 672 Bits, PPS 59,523,809, Cycles/Burst 5,120\n  RxQs  :    9,512,816    8,870,944    9,235,916    9,272,516 Total:   36,892,192\n  TxQs  :    6,182,144   12,364,288    6,182,144   12,364,288 Total:   37,092,864\n  TxDrop:    8,779,360    2,596,544    8,779,072    2,596,192 Total:   22,751,168\n  NoMBUF:            0            0            0            0 Total:            0\n  TxTime:          351          384          372          330 Total:        1,437\n  RxMissed:      248,108, ierr: 0, oerr: 0, RxNoMbuf: 0\n 1 >> Link up at 100 Gbps FDX Autoneg, WireSize 672 Bits, PPS 148,809,523, Cycles/Burst 2,048\n  RxQs  :   23,465,206   24,484,244   24,600,096   25,202,408 Total:   97,751,954\n  TxQs  :   23,915,040   23,915,040   23,915,040   23,915,040 Total:   95,660,160\n  TxDrop:   13,339,456   13,352,064   13,327,936   13,353,056 Total:   53,372,512\n  NoMBUF:            0            0            0            0 Total:            0\n  TxTime:          489          483          474          564 Total:        2,010\n  RxMissed:            0, ierr: 0, oerr: 0, RxNoMbuf: 0\n\nBurst: 32, MBUF Count: 12,864, PktSize:64, Rx/Tx 1,024/1,024, Rate 100%\n```\n"
  },
  {
    "path": "examples/pktperf/meson.build",
    "content": "sources = files('pktperf.c', 'parse.c', 'port.c', 'stats.c', 'utils.c')\n\ncflags = []\n\ndeps = [dpdk, common, utils, fgen_dep]\n\ndeps += [cc.find_library('rte_net_i40e', dirs: [dpdk_libs_path], required: false)]\ndeps += [cc.find_library('rte_net_ixgbe', dirs: [dpdk_libs_path], required: false)]\ndeps += [cc.find_library('rte_net_ice', dirs: [dpdk_libs_path], required: false)]\ndeps += [cc.find_library('rte_bus_vdev', dirs: [dpdk_libs_path], required: false)]\n\ndeps += [dependency('threads')]\ndeps += [dependency('numa', required: true)]\ndeps += [dependency('pcap', required: true)]\ndeps += [cc.find_library('dl', required: false)]\ndeps += [cc.find_library('m', required: false)]\n\npktperf = executable('pktperf',\n\t\tsources,\n\t\tc_args: cflags,\n\t\tinstall: true,\n\t\tdependencies: [deps, dpdk_bond])\n"
  },
  {
    "path": "examples/pktperf/parse.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) 2023-2026 Intel Corporation\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <sys/queue.h>\n#include <setjmp.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <errno.h>\n#include <getopt.h>\n#include <signal.h>\n#include <stdbool.h>\n#include <alloca.h>\n#include <locale.h>\n#include <pthread.h>\n\n#include <pktperf.h>\n\n#define BURST_COUNT_OPT \"burst-count\"\n#define PKT_SIZE_OPT    \"pkt-size\"\n#define TX_RATE_OPT     \"tx-rate\"\n#define RX_TX_DESC_OPT  \"descriptors\"\n#define MAPPING_OPT     \"map\"\n#define TIMEOUT_OPT     \"timeout\"\n#define PROMISCUOUS_OPT \"no-promiscuous\"\n#define JUMBO_OPT       \"jumbo\"\n#define MBUF_COUNT_OPT  \"mbuf-count\"\n#define FGEN_STRING_OPT \"fgen\"\n#define FGEN_FILE_OPT   \"fgen-file\"\n#define VERBOSE_OPT     \"verbose\"\n#define TCP_OPT         \"tcp\"\n#define UDP_OPT         \"udp\"\n#define HELP_OPT        \"help\"\n\n// clang-format off\nstatic const struct option lgopts[] = {\n    {BURST_COUNT_OPT,\t    1, 0, 'b'},\n    {PKT_SIZE_OPT,\t        1, 0, 's'},\n    {TX_RATE_OPT,           1, 0, 'r'},\n    {RX_TX_DESC_OPT,        1, 0, 'd'},\n    {MAPPING_OPT,\t\t    1, 0, 'm'},\n\t{TIMEOUT_OPT,\t\t    1, 0, 'T'},\n    {MBUF_COUNT_OPT,        1, 0, 'M'},\n\t{PROMISCUOUS_OPT,       0, 0, 'P'},\n\t{JUMBO_OPT,             0, 0, 'j'},\n\t{FGEN_STRING_OPT,       0, 0, 'f'},\n\t{FGEN_FILE_OPT,         0, 0, 'F'},\n    {VERBOSE_OPT,           0, 0, 'v'},\n    {TCP_OPT,               0, 0, 't'},\n    {UDP_OPT,               0, 0, 'u'},\n\t{HELP_OPT,\t\t\t    0, 0, 'h'},\n\t{NULL,\t\t\t\t    0, 0, 0}\n};\n// clang-format on\n\nstatic const char *short_options = \"t:b:s:r:d:m:T:M:F:f:Pjvhtu\";\n\n/* display usage */\nvoid\nusage(int err)\n{\n    printf(\n        \"pktperf [EAL options] -- [-b burst] [-s size] [-r rate] [-d rxd/txd] [-m map] [-T secs] \"\n        \"[-P] [-M mbufs] [-v] [-h]\\n\"\n        \"\\t-b|--burst-count <burst> Number of packets for Rx/Tx burst (default %d)\\n\"\n        \"\\t-s|--pkt-size <size>     Packet size in bytes (default %'d) includes FCS bytes\\n\"\n        \"\\t-r|--rate <rate>         Packet TX rate percentage 0=off (default %'d)\\n\"\n        \"\\t-d|--descriptors <Rx/Tx> Number of RX/TX descriptors (default %'d/%'d)\\n\"\n        \"\\t-m|--map <map>           Core to Port/queue mapping '[Rx-Cores:Tx-Cores].port'\\n\"\n        \"\\t-T|--timeout <secs>      Timeout period in seconds (default %d second)\\n\"\n        \"\\t-P|--no-promiscuous      Turn off promiscuous mode (default On)\\n\"\n        \"\\t-M|--mbuf-count <count>  Number of mbufs to allocate (default %'d, max %'d)\\n\"\n        \"\\t-t|--tcp                 Use TCP\\n\"\n        \"\\t-u|--udp                 Use UDP (default UDP)\\n\"\n        \"\\t-j|--jumbo               Enable jumbo frame support\\n\"\n        \"\\t-f|--fgen <string>       FGEN string to load\\n\"\n        \"\\t-F|--fgen-file <file>    FGEN file to load\\n\"\n        \"\\t-v|--verbose             Verbose output\\n\"\n        \"\\t-h|--help                Print this help\\n\",\n        DEFAULT_BURST_COUNT, DEFAULT_PKT_SIZE, DEFAULT_TX_RATE, DEFAULT_RX_DESC, DEFAULT_TX_DESC,\n        DEFAULT_TIMEOUT_PERIOD, DEFAULT_MBUF_COUNT, MAX_MBUF_COUNT);\n\n    exit(err);\n}\n\nstatic struct rte_mempool *\ncreate_pktmbuf_pool(const char *type, uint16_t lid, uint16_t pid, uint16_t qid, uint32_t nb_mbufs,\n                    uint32_t cache_size)\n{\n    struct rte_mempool *mp;\n    char name[RTE_MEMZONE_NAMESIZE];\n\n    /* Create the pktmbuf pool one per lcore/port */\n    snprintf(name, sizeof(name) - 1, \"%s-%u/%u/%u\", type, lid, pid, qid);\n\n    printf(\"Creating %s mbuf pool for lcore %3u, port %2u, qid %2u, MBUF Count %'u, size %'u on \"\n           \"NUMA %d\\n\",\n           name, lid, pid, qid, nb_mbufs, info->mbuf_size, pg_eth_dev_socket_id(pid));\n\n    mp = rte_pktmbuf_pool_create(name, info->mbuf_count, cache_size, 0, info->mbuf_size,\n                                 pg_eth_dev_socket_id(pid));\n    return mp;\n}\n\nstatic int\nparse_cores(l2p_port_t *port, const char *cores, int mode)\n{\n    char *core_map  = NULL;\n    int num_cores   = 0, l, h, num_fields;\n    char *fields[3] = {0}, *f0, *f1;\n    char name[64];\n\n    core_map = alloca(MAX_ALLOCA_SIZE);\n    if (!core_map)\n        ERR_RET(\"out of memory for core string\\n\");\n\n    snprintf(core_map, MAX_ALLOCA_SIZE - 1, \"%s\", cores);\n\n    num_fields = rte_strsplit(core_map, strlen(core_map), fields, RTE_DIM(fields), '-');\n    if (num_fields <= 0 || num_fields > 2)\n        ERR_RET(\"invalid core mapping '%s'\\n\", cores);\n    DBG_PRINT(\"num_fields: %d from cores '%s'\\n\", num_fields, cores);\n    f0 = fields[0];\n    f1 = (fields[1] == NULL) ? f0 : fields[1];\n    f0 = pg_strtrimset(f0, \"[]\");\n    f0 = pg_strtrimset(f0, \"{}\");\n    f1 = pg_strtrimset(f1, \"[]\");\n    f1 = pg_strtrimset(f1, \"{}\");\n\n    DBG_PRINT(\"range of cores specified: %s - %s\\n\", f0, f1);\n    l = strtol(f0, NULL, 10);\n    h = strtol(f1, NULL, 10);\n\n    DBG_PRINT(\"lcore: %d to %d\\n\", l, h);\n    do {\n        l2p_lport_t *lport;\n        int32_t sid = pg_eth_dev_socket_id(port->pid);\n\n        lport = info->lports[l];\n        if (lport == NULL) {\n            snprintf(name, sizeof(name) - 1, \"lport-%u:%u\", l, port->pid);\n            lport = rte_zmalloc_socket(name, sizeof(l2p_lport_t), RTE_CACHE_LINE_SIZE, sid);\n            if (!lport)\n                ERR_RET(\"Failed to allocate memory for lport info\\n\");\n            lport->lid = l;\n\n            info->lports[l] = lport;\n        } else\n            ERR_PRINT(\"lcore %u already in use\\n\", l);\n\n        num_cores++;\n        lport->port = port;\n        lport->mode = mode;\n        switch (mode) {\n        case LCORE_MODE_RX:\n            lport->rx_qid = port->num_rx_qids++;\n            DBG_PRINT(\"lcore %u:%u:%u is in RX mode\\n\", l, lport->port->pid, lport->rx_qid);\n\n            port->rx_mp[lport->rx_qid] = create_pktmbuf_pool(\n                \"Rx\", lport->lid, port->pid, lport->rx_qid, info->mbuf_count, MEMPOOL_CACHE_SIZE);\n            if (port->rx_mp[lport->rx_qid] == NULL)\n                ERR_RET(\"Unable to allocate Rx pktmbuf pool for lid/port %d/%d\\n\", lport->lid,\n                        port->pid);\n            break;\n        case LCORE_MODE_TX:\n            lport->tx_qid = port->num_tx_qids++;\n            DBG_PRINT(\"lcore %u:%u:%u is in TX mode\\n\", l, lport->port->pid, lport->tx_qid);\n\n            port->tx_mp[lport->tx_qid] = create_pktmbuf_pool(\n                \"Tx\", lport->lid, port->pid, lport->tx_qid, info->mbuf_count, MEMPOOL_CACHE_SIZE);\n            if (port->tx_mp[lport->tx_qid] == NULL)\n                ERR_RET(\"Unable to allocate Tx pktmbuf pool for lid/port %d/%d\\n\", lport->lid,\n                        port->pid);\n            break;\n        case LCORE_MODE_BOTH:\n            lport->rx_qid = port->num_rx_qids++;\n            lport->tx_qid = port->num_tx_qids++;\n            DBG_PRINT(\"lcore %u:%u:%u is in RX/TX mode\\n\", l, lport->port->pid, lport->rx_qid);\n\n            port->rx_mp[lport->rx_qid] = create_pktmbuf_pool(\n                \"Rx\", lport->lid, port->pid, lport->rx_qid, info->mbuf_count, MEMPOOL_CACHE_SIZE);\n            if (port->rx_mp[lport->rx_qid] == NULL)\n                ERR_RET(\"Unable to allocate Rx pktmbuf pool for lid/port %d/%d\\n\", lport->lid,\n                        port->pid);\n\n            port->tx_mp[lport->tx_qid] = create_pktmbuf_pool(\n                \"Tx\", lport->lid, port->pid, lport->tx_qid, info->mbuf_count, MEMPOOL_CACHE_SIZE);\n            if (port->tx_mp[lport->tx_qid] == NULL)\n                ERR_RET(\"Unable to allocate Tx pktmbuf pool for lid/port %d/%d\\n\", lport->lid,\n                        port->pid);\n            break;\n        default:\n            ERR_RET(\"invalid port mode\\n\");\n            break;\n        }\n\n        DBG_PRINT(\"lcore: %u port: %u qid: %u/%u name:'%s'\\n\", lport->lid, port->pid, lport->rx_qid,\n                  lport->tx_qid, name);\n\n    } while (l++ < h);\n\n    DBG_PRINT(\"num_cores: %d\\n\", num_cores);\n    return num_cores;\n}\n\nstatic int\nparse_mapping(const char *map)\n{\n    char *fields[3] = {0}, *f0, *f1, *lcores[3] = {0}, *c0, *c1;\n    char *mapping = NULL;\n    int num_fields, num_cores, num_lcores;\n    uint16_t pid;\n\n    if (!map || strlen(map) == 0)\n        ERR_RET(\"no mapping specified or string empty\\n\");\n\n    mapping = alloca(MAX_ALLOCA_SIZE);\n    if (!mapping)\n        ERR_RET(\"unable to allocate map string\\n\");\n    snprintf(mapping, MAX_ALLOCA_SIZE - 1, \"%s\", map);\n    DBG_PRINT(\"Mapping: '%s'\\n\", mapping);\n\n    /* parse map into a lcore list and port number */\n    num_fields = rte_strsplit(mapping, strlen(mapping), fields, RTE_DIM(fields), '.');\n    if (num_fields != 2)\n        ERR_RET(\"Invalid mapping format '%s'\\n\", mapping);\n    f0 = fields[0];\n    f1 = (fields[1] == NULL) ? f0 : fields[1];\n    f0 = pg_strtrimset(f0, \"[]\");\n    f0 = pg_strtrimset(f0, \"{}\");\n    f1 = pg_strtrimset(f1, \"[]\");\n    f1 = pg_strtrimset(f1, \"{}\");\n    DBG_PRINT(\"Mapping: fields(%u) lcore '%s', port '%s'\\n\", num_fields, f0, f1);\n\n    pid = strtol(f1, NULL, 10);\n    if (pid >= RTE_MAX_ETHPORTS)\n        ERR_RET(\"Invalid port number '%s'\\n\", f1);\n    DBG_PRINT(\"Mapping: Port %u\\n\", pid);\n\n    info->ports[pid].pid = pid;\n\n    num_lcores = rte_strsplit(f0, strlen(f0), lcores, RTE_DIM(lcores), ':');\n    if (num_lcores <= 0 || num_lcores > 2)\n        ERR_RET(\"Invalid mapping format '%s'\\n\", fields[0]);\n    c0 = lcores[0];\n    c1 = (lcores[1] == NULL) ? c0 : lcores[1];\n    if (num_lcores == 1) {\n        num_cores = parse_cores(&info->ports[pid], c0, LCORE_MODE_BOTH);\n        if (num_cores <= 0)\n            ERR_RET(\"Invalid mapping format '%s'\\n\", c0);\n        DBG_PRINT(\"num_cores for both Rx/Tx: %d\\n\", num_cores);\n    } else {\n        num_cores = parse_cores(&info->ports[pid], c0, LCORE_MODE_RX);\n        if (num_cores <= 0)\n            ERR_RET(\"Invalid mapping format '%s'\\n\", c0);\n        DBG_PRINT(\"num_cores for RX: %d\\n\", num_cores);\n\n        num_cores = parse_cores(&info->ports[pid], c1, LCORE_MODE_TX);\n        if (num_cores <= 0)\n            ERR_RET(\"Invalid mapping format '%s'\\n\", c1);\n        DBG_PRINT(\"num_cores for TX: %d\\n\", num_cores);\n    }\n\n    return EXIT_SUCCESS;\n}\n\nstatic void\nvalidate_args(void)\n{\n    if (info->tx_rate > MAX_TX_RATE)\n        info->tx_rate = DEFAULT_TX_RATE;\n    DBG_PRINT(\"Packet Tx rate: %'u\\n\", info->tx_rate);\n\n    if (info->burst_count <= 0)\n        info->burst_count = DEFAULT_BURST_COUNT;\n    else if (info->burst_count > MAX_BURST_COUNT)\n        info->burst_count = MAX_BURST_COUNT;\n    DBG_PRINT(\"RX/TX burst count: %'u\\n\", info->burst_count);\n\n    if (info->pkt_size == 0)\n        info->pkt_size = DEFAULT_PKT_SIZE;\n    else if (info->pkt_size > MAX_PKT_SIZE)\n        info->pkt_size = MAX_PKT_SIZE;\n    DBG_PRINT(\"Packet size: %'u\\n\", info->pkt_size);\n\n    if (info->mbuf_count < DEFAULT_MBUF_COUNT)\n        info->mbuf_count = DEFAULT_MBUF_COUNT;\n    else if (info->mbuf_count > MAX_MBUF_COUNT) {\n        ERR_PRINT(\"invalid MBUF Count value 1 <= %'u <= %'d (default %'d)\\n\", info->mbuf_count,\n                  MAX_MBUF_COUNT, DEFAULT_MBUF_COUNT);\n        usage(EXIT_FAILURE);\n    }\n    DBG_PRINT(\"Timeout period: %'u\\n\", info->timeout_secs);\n\n    if (info->nb_rxd < MIN_RX_DESC || info->nb_rxd > MAX_RX_DESC)\n        info->nb_rxd = DEFAULT_RX_DESC;\n    if (info->nb_txd < MIN_TX_DESC || info->nb_txd > MAX_TX_DESC)\n        info->nb_txd = DEFAULT_TX_DESC;\n    DBG_PRINT(\"Rx/Tx Ring Size: %'u/%'u\\n\", info->nb_rxd, info->nb_txd);\n\n    if (info->timeout_secs <= 0)\n        info->timeout_secs = DEFAULT_TIMEOUT_PERIOD;\n    else if (info->timeout_secs > MAX_TIMEOUT_PERIOD) {\n        ERR_PRINT(\"invalid timeout value 1 <= %'u <= %'d (default %d)\\n\", info->timeout_secs,\n                  MAX_TIMEOUT_PERIOD, DEFAULT_TIMEOUT_PERIOD);\n        usage(EXIT_FAILURE);\n    }\n    DBG_PRINT(\"Timeout period: %'u\\n\", info->timeout_secs);\n\n    printf(\"num_ports: %'u, nb_rxd %'u, nb_txd %'u, burst %'u, lcores %'u\\n\", info->num_ports,\n           info->nb_rxd, info->nb_txd, info->burst_count, rte_lcore_count());\n    info->mbuf_count += info->num_ports * (info->nb_rxd + info->nb_txd + info->burst_count +\n                                           (rte_lcore_count() * MEMPOOL_CACHE_SIZE));\n    info->mbuf_count = RTE_MAX(info->mbuf_count, DEFAULT_MBUF_COUNT);\n\n    DBG_PRINT(\"TX packet application started, Burst size %'u, Packet size %'u, Rate %u%%\\n\",\n              info->burst_count, info->pkt_size, info->tx_rate);\n\n    if (info->num_mappings == 0) {\n        ERR_PRINT(\"No port mappings specified, use '-m' option\\n\");\n        usage(EXIT_FAILURE);\n    }\n}\n\n/* Parse the argument given on the command line of the application */\nstatic int\nparse_args(int argc, char **argv)\n{\n    int opt, ret;\n    char **argvopt;\n    int option_index;\n    char rxtx_desc[64];\n    char *descs[3];\n\n    argvopt = argv;\n\n    info->promiscuous_on = DEFAULT_PROMISCUOUS_MODE;\n    info->jumbo_frame_on = DEFAULT_JUMBO_FRAME_MODE;\n    info->timeout_secs   = DEFAULT_TIMEOUT_PERIOD;\n    info->burst_count    = DEFAULT_BURST_COUNT;\n    info->nb_rxd         = DEFAULT_RX_DESC;\n    info->nb_txd         = DEFAULT_TX_DESC;\n    info->mbuf_count     = DEFAULT_MBUF_COUNT;\n    info->mbuf_size      = RTE_MBUF_DEFAULT_BUF_SIZE;\n    info->pkt_size       = DEFAULT_PKT_SIZE;\n    info->tx_rate        = DEFAULT_TX_RATE;\n    info->ip_proto       = IPPROTO_UDP;\n    info->force_quit     = false;\n    info->verbose        = false;\n\n    while ((opt = getopt_long(argc, argvopt, short_options, lgopts, &option_index)) != EOF) {\n        switch (opt) {\n        case 'b': /* RX/TX burst option */\n            info->burst_count = strtoul(optarg, NULL, 10);\n            break;\n\n        case 's': /* Packet size option */\n            info->pkt_size = strtoul(optarg, NULL, 10);\n            break;\n\n        case 'r': /* Tx Rate option */\n            info->tx_rate = strtoul(optarg, NULL, 10);\n            break;\n\n        case 'd': /* Number of Rx/Tx descriptors */\n            snprintf(rxtx_desc, sizeof(rxtx_desc) - 1, \"%s\", optarg);\n            if (rte_strsplit(rxtx_desc, strlen(rxtx_desc), descs, RTE_DIM(descs), '/') != 2)\n                ERR_RET(\"Invalid Rx/Tx descriptors '%s'\\n\", optarg);\n            info->nb_rxd = strtoul(descs[0], NULL, 10);\n            info->nb_txd = strtoul(descs[1], NULL, 10);\n            break;\n\n        case 'm': /* Mapping option */\n            DBG_PRINT(\"Mapping: '%s'\\n\", optarg);\n\n            /* place mapping strings into a list for processing later */\n            info->mappings[info->num_mappings++] = strdup(optarg);\n            break;\n\n        case 'T': /* Timeout option */\n            info->timeout_secs = strtol(optarg, NULL, 0);\n            break;\n\n        case 'M': /* MBUF Count */\n            info->mbuf_count = strtol(optarg, NULL, 0);\n            break;\n\n        case 'P': /* Promiscuous option */\n            info->promiscuous_on = 0;\n            DBG_PRINT(\"Promiscuous mode: off\\n\");\n            break;\n\n        case 'f': /* FGEN string */\n            if (fgen_load_strings(info->fgen, &optarg, 1) < 0) {\n                ERR_PRINT(\"Unable to load FGEN string '%s'\\n\", optarg);\n                usage(EXIT_FAILURE);\n            }\n            break;\n\n        case 'F': /* FGEN file */\n            if (fgen_load_files(info->fgen, &optarg, 1) < 0) {\n                ERR_PRINT(\"Unable to load FGEN file '%s'\\n\", optarg);\n                usage(EXIT_FAILURE);\n            }\n            break;\n\n        case 't': /* TCP */\n            info->ip_proto = IPPROTO_TCP;\n            break;\n\n        case 'u': /* UDP */\n            info->ip_proto = IPPROTO_UDP;\n            break;\n\n        case 'j': /* Jumbo frames */\n            info->mbuf_size      = JUMBO_MBUF_SIZE;\n            info->jumbo_frame_on = 1;\n            break;\n\n        case 'v': /* Verbose option */\n            info->verbose = true;\n            break;\n\n        case 'h': /* Help option */\n            usage(EXIT_SUCCESS);\n            break;\n\n        default:\n            usage(EXIT_FAILURE);\n            break;\n        }\n    }\n    validate_args();\n\n    ret    = optind - 1;\n    optind = 1; /* reset getopt lib */\n\n    return ret;\n}\n\nint\nparse_configuration(int argc, char **argv)\n{\n    /* parse application arguments (after the EAL ones) */\n    if (parse_args(argc, argv) < 0)\n        ERR_RET(\"Invalid PKTPERF arguments\\n\");\n\n    for (int i = 0; i < info->num_mappings; i++)\n        parse_mapping(info->mappings[i]);\n\n    for (int pid = 0; pid < info->num_ports; pid++) {\n        if (port_setup(&info->ports[pid]) < 0)\n            ERR_RET(\"Port setup failed\\n\");\n    }\n\n    if ((info->fgen = fgen_create(0)) == NULL)\n        ERR_RET(\"FGEN creation failed\\n\");\n\n    return 0;\n}\n"
  },
  {
    "path": "examples/pktperf/pktperf.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) 2023-2026 Intel Corporation\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <sys/queue.h>\n#include <setjmp.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <errno.h>\n#include <getopt.h>\n#include <signal.h>\n#include <stdbool.h>\n#include <alloca.h>\n#include <locale.h>\n#include <pthread.h>\n\n#include <pktperf.h>\n\n#include <fgen.h>\n\ntxpkts_info_t *info;\n\nstatic __inline__ void\nmbuf_iterate_cb(struct rte_mempool *mp, void *opaque, void *obj, unsigned obj_idx __rte_unused)\n{\n    l2p_lport_t *lport = (l2p_lport_t *)opaque;\n    struct rte_mbuf *m = (struct rte_mbuf *)obj;\n    uint16_t plen      = info->pkt_size - RTE_ETHER_CRC_LEN;\n\n    if (plen < RTE_ETHER_MIN_LEN - RTE_ETHER_CRC_LEN) {\n        printf(\"Invalid packet size %u, setting to minimum %u\\n\", plen,\n               RTE_ETHER_MIN_LEN - RTE_ETHER_CRC_LEN);\n        plen = RTE_ETHER_MIN_LEN - RTE_ETHER_CRC_LEN;\n    }\n    packet_constructor(lport, rte_pktmbuf_mtod(m, uint8_t *), info->ip_proto);\n\n    m->pool     = mp;\n    m->next     = NULL;\n    m->data_len = plen;\n    m->pkt_len  = plen;\n    m->port     = 0;\n    m->ol_flags = 0;\n}\n\nstatic __inline__ void\ndo_rx_process(l2p_lport_t *lport, struct rte_mbuf **mbufs, uint32_t n_mbufs, uint64_t curr_tsc)\n{\n    l2p_port_t *port = lport->port;\n    qstats_t *c;\n    uint16_t nb_pkts, rx_qid;\n\n    rx_qid = lport->rx_qid;\n    c      = &port->pq[rx_qid].curr;\n\n    /* drain the RX queue */\n    nb_pkts = rte_eth_rx_burst(port->pid, rx_qid, mbufs, n_mbufs);\n    if (nb_pkts) {\n        for (uint16_t i = 0; i < nb_pkts; i++)\n            c->q_ibytes[rx_qid] += rte_pktmbuf_pkt_len(mbufs[i]);\n        c->q_ipackets[rx_qid] += nb_pkts;\n\n        rte_pktmbuf_free_bulk(mbufs, nb_pkts);\n        c->q_rx_time[rx_qid] = rte_rdtsc() - curr_tsc;\n    }\n}\n\nstatic __inline__ void\ndo_tx_process(l2p_lport_t *lport, struct rte_mbuf **mbufs, uint16_t n_mbufs, uint64_t curr_tsc)\n{\n    l2p_port_t *port = lport->port;\n    struct rte_mempool *mp;\n    qstats_t *c;\n    uint16_t pid, tx_qid;\n\n    pid    = port->pid;\n    tx_qid = lport->tx_qid;\n    c      = &port->pq[tx_qid].curr;\n    mp     = lport->port->tx_mp[tx_qid];\n\n    /* Use mempool routines instead of pktmbuf to make sure the mbufs is not altered */\n    if (rte_mempool_get_bulk(mp, (void **)mbufs, n_mbufs) == 0) {\n        uint16_t plen = info->pkt_size - RTE_ETHER_CRC_LEN;\n        uint16_t sent, send = n_mbufs;\n\n        for (uint16_t i = 0; i < n_mbufs; i++) {\n            if (mbufs[i]->data_len != plen || mbufs[i]->pkt_len != plen) {\n                printf(\"Mismatch mbuf lengths mbuf[%u] data_len=%u pkt_len=%u, resetting\\n\", i,\n                       mbufs[i]->data_len, mbufs[i]->pkt_len);\n                /* Reset mbuf fields that might have been changed */\n                mbufs[i]->data_len = plen;\n                mbufs[i]->pkt_len  = plen;\n                mbufs[i]->next     = NULL;\n                mbufs[i]->port     = 0;\n                mbufs[i]->ol_flags = 0;\n            }\n        }\n\n        do {\n            sent = rte_eth_tx_burst(pid, tx_qid, mbufs, send);\n            send -= sent;\n            mbufs += sent;\n        } while (send > 0);\n\n        c->q_opackets[tx_qid] += n_mbufs;\n        c->q_obytes[tx_qid] += (n_mbufs * plen); /* does not include FCS */\n\n        c->q_tx_time[tx_qid] = rte_rdtsc() - curr_tsc;\n    } else\n        c->q_no_txmbufs[tx_qid]++;\n}\n\n/* main processing loop */\nstatic void\nrx_loop(void)\n{\n    l2p_lport_t *lport;\n    uint16_t rx_burst = info->burst_count * 2;\n    struct rte_mbuf *mbufs[rx_burst];\n\n    lport = info->lports[rte_lcore_id()];\n\n    printf(\"Starting Rx loop for lcore:port:queue %3u:%2u:%2u\\n\", rte_lcore_id(), lport->port->pid,\n           lport->rx_qid);\n\n    while (!info->force_quit)\n        do_rx_process(lport, mbufs, rx_burst, rte_rdtsc());\n\n    DBG_PRINT(\"Exiting loop for lcore:port:queue %3u:%2u:%2u\\n\", rte_lcore_id(), lport->port->pid,\n              lport->rx_qid);\n}\n\nstatic void\ntx_loop(void)\n{\n    l2p_lport_t *lport;\n    l2p_port_t *port;\n    uint64_t curr_tsc, burst_tsc;\n    uint16_t tx_burst = info->burst_count;\n    struct rte_mbuf *mbufs[tx_burst];\n\n    lport = info->lports[rte_lcore_id()];\n    port  = lport->port;\n\n    printf(\"Starting Tx loop for lcore:port:queue %3u:%2u:%2u\\n\", rte_lcore_id(), port->pid,\n           lport->tx_qid);\n\n    pthread_spin_lock(&port->tx_lock);\n    if (port->tx_inited[lport->tx_qid] == 0) {\n        port->tx_inited[lport->tx_qid] = 1;\n        /* iterate over all buffers in the pktmbuf pool and setup the packet data */\n        rte_mempool_obj_iter(port->tx_mp[lport->tx_qid], mbuf_iterate_cb, (void *)lport);\n    }\n    pthread_spin_unlock(&port->tx_lock);\n\n    burst_tsc = rte_rdtsc() + port->tx_cycles;\n\n    while (!info->force_quit) {\n        curr_tsc = rte_rdtsc();\n\n        if (unlikely(curr_tsc >= burst_tsc)) {\n            burst_tsc = curr_tsc + port->tx_cycles;\n\n            if (likely(port->tx_cycles))\n                do_tx_process(lport, mbufs, tx_burst, curr_tsc);\n        }\n    }\n    DBG_PRINT(\"Exiting loop for lcore:port:queue %3u:%2u:%2u\\n\", rte_lcore_id(), port->pid,\n              lport->tx_qid);\n}\n\nstatic void\nrxtx_loop(void)\n{\n    l2p_lport_t *lport;\n    l2p_port_t *port;\n    uint64_t curr_tsc, burst_tsc;\n    uint16_t rx_burst = info->burst_count * 2;\n    uint16_t tx_burst = info->burst_count;\n    struct rte_mbuf *mbufs[rx_burst];\n\n    lport = info->lports[rte_lcore_id()];\n    port  = lport->port;\n\n    printf(\"Starting Rx/Tx loop for lcore:port:queue %3u:%2u:%2u.%2u\\n\", rte_lcore_id(), port->pid,\n           lport->rx_qid, lport->tx_qid);\n\n    pthread_spin_lock(&port->tx_lock);\n    if (port->tx_inited[lport->tx_qid] == 0) {\n        port->tx_inited[lport->tx_qid] = 1;\n        /* iterate over all buffers in the pktmbuf pool and setup the packet data */\n        rte_mempool_obj_iter(port->tx_mp[lport->tx_qid], mbuf_iterate_cb, (void *)lport);\n    }\n    pthread_spin_unlock(&port->tx_lock);\n\n    burst_tsc = rte_rdtsc() + port->tx_cycles;\n\n    while (!info->force_quit) {\n        curr_tsc = rte_rdtsc();\n\n        do_rx_process(lport, mbufs, rx_burst, curr_tsc);\n\n        if (unlikely(curr_tsc >= burst_tsc)) {\n            burst_tsc = curr_tsc + port->tx_cycles;\n\n            if (likely(port->tx_cycles))\n                do_tx_process(lport, mbufs, tx_burst, curr_tsc);\n        }\n    }\n    DBG_PRINT(\"Exiting loop for lcore:port:queue %3u:%2u:%2u.%u\\n\", rte_lcore_id(), port->pid,\n              lport->rx_qid, lport->tx_qid);\n}\n\nstatic int\ntxpkts_launch_one_lcore(__rte_unused void *dummy)\n{\n    l2p_lport_t *lport = info->lports[rte_lcore_id()];\n\n    if (lport == NULL)\n        ERR_RET(\"lport is NULL lcore(%u)\\n\", rte_lcore_id());\n    if (lport->port == NULL)\n        ERR_RET(\"lport->port is NULL lcore(%u)\\n\", rte_lcore_id());\n    if (lport->port->pid >= RTE_MAX_ETHPORTS)\n        ERR_RET(\"lport->port->pid is invalid (%u) lcore(%u)\\n\", lport->port->pid, rte_lcore_id());\n\n    switch (lport->mode) {\n    case LCORE_MODE_RX:\n        rx_loop();\n        break;\n    case LCORE_MODE_TX:\n        tx_loop();\n        break;\n    case LCORE_MODE_BOTH:\n        rxtx_loop();\n        break;\n    case LCORE_MODE_UNKNOWN:\n    default:\n        ERR_RET(\"Invalid mode %u\\n\", lport->mode);\n        break;\n    }\n    return 0;\n}\n\nstatic void\nsignal_handler(int signum)\n{\n    if (signum == SIGINT || signum == SIGTERM) {\n        DBG_PRINT(\"\\n\\nSignal %d received, preparing to exit...\\n\", signum);\n        info->force_quit = true;\n    }\n}\n\nstatic int\ninitialize_dpdk(int argc, char **argv)\n{\n    int ret = 0;\n\n    signal(SIGINT, signal_handler);\n    signal(SIGTERM, signal_handler);\n    srandom(RANDOM_SEED);\n    setlocale(LC_ALL, \"\");\n\n    if ((ret = rte_eal_init(argc, argv)) < 0)\n        ERR_RET(\"Invalid DPDK arguments\\n\");\n\n    argc -= ret; /* Skip DPDK configuration options */\n    argv += ret;\n\n    if ((info->num_ports = rte_eth_dev_count_avail()) == 0)\n        ERR_RET(\"No Ethernet ports found - bye\\n\");\n\n    if (parse_configuration(argc, argv) < 0)\n        ERR_RET(\"Invalid configuration\\n\");\n\n    return 0;\n}\n\nstatic int\nlaunch_lcore_threads(void)\n{\n    int lid;\n\n    /* launch per-lcore init on every worker lcore */\n    if (rte_eal_mp_remote_launch(txpkts_launch_one_lcore, NULL, SKIP_MAIN) != 0)\n        ERR_RET(\"Failed to launch lcore threads\\n\");\n\n    /* Scroll the screen to keep console output for debugging */\n    for (int i = 0; i < 10; i++)\n        PRINT(\"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\");\n\n    /* Display the statistics  */\n    do {\n        print_stats();\n        rte_delay_us_sleep(info->timeout_secs * Million);\n    } while (!info->force_quit);\n\n    RTE_LCORE_FOREACH_WORKER(lid)\n    {\n        DBG_PRINT(\"Waiting for lcore %d to exit\\n\", lid);\n        if (rte_eal_wait_lcore(lid) < 0)\n            ERR_RET(\"Error waiting for lcore %d to exit\\n\", lid);\n    }\n\n    for (uint16_t portid = 0; portid < info->num_ports; portid++) {\n        DBG_PRINT(\"Closing port %d... \", portid);\n        if (rte_eth_dev_stop(portid) == 0)\n            rte_eth_dev_close(portid);\n        DBG_PRINT(\"\\n\");\n    }\n\n    return rte_eal_cleanup();\n}\n\nstatic txpkts_info_t *\ninfo_alloc(void)\n{\n    txpkts_info_t *txinfo;\n\n    txinfo = (txpkts_info_t *)calloc(1, sizeof(txpkts_info_t));\n    if (txinfo) {\n        for (int i = 0; i < RTE_MAX_ETHPORTS; i++) {\n            int ret;\n\n            txinfo->ports[i].pid = RTE_MAX_ETHPORTS + 1; /* set to invalid port id */\n\n            ret = pthread_spin_init(&txinfo->ports[i].tx_lock, PTHREAD_PROCESS_PRIVATE);\n            if (ret != 0) {\n                free(txinfo);\n                ERR_RET_NULL(\"Unable to initialize tx_lock for port %d: %s\\n\", i, strerror(ret));\n            }\n        }\n    }\n\n    return txinfo;\n}\n\nint\nmain(int argc, char **argv)\n{\n    info = info_alloc();\n    if (info) {\n        if (initialize_dpdk(argc, argv) == 0) {\n            if (launch_lcore_threads() == 0) {        // Waits for all threads to exit\n                free(info);\n                return EXIT_SUCCESS;\n            }\n        }\n        free(info);\n    }\n\n    return EXIT_FAILURE;\n}\n"
  },
  {
    "path": "examples/pktperf/pktperf.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) 2023-2026 Intel Corporation\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <rte_common.h>\n#include <rte_log.h>\n#include <rte_malloc.h>\n#include <rte_memory.h>\n#include <rte_memcpy.h>\n#include <rte_eal.h>\n#include <rte_launch.h>\n#include <rte_cycles.h>\n#include <rte_prefetch.h>\n#include <rte_lcore.h>\n#include <rte_per_lcore.h>\n#include <rte_branch_prediction.h>\n#include <rte_interrupts.h>\n#include <rte_random.h>\n#include <rte_debug.h>\n#include <rte_ether.h>\n#include <rte_ethdev.h>\n#include <rte_mempool.h>\n#include <rte_mbuf.h>\n#include <rte_string_fns.h>\n\n#include <fgen_common.h>\n#include <fgen.h>\n#include <utils.h>\n\n#define PRINT(format, args...)  \\\n    do {                        \\\n        printf(format, ##args); \\\n        fflush(stdout);         \\\n    } while (0)\n\n#define INFO_PRINT(format, args...)                                \\\n    do {                                                           \\\n        char buf[64];                                              \\\n        snprintf(buf, sizeof(buf), \"%s(%'d)\", __func__, __LINE__); \\\n        printf(\"INFO>%-24s:\" format, buf, ##args);                 \\\n        fflush(stdout);                                            \\\n    } while (0)\n\n#define ERR_PRINT(format, args...)                                 \\\n    do {                                                           \\\n        char buf[64];                                              \\\n        snprintf(buf, sizeof(buf), \"%s(%'d)\", __func__, __LINE__); \\\n        printf(\"ERROR>%-24s:\" format, buf, ##args);                \\\n        fflush(stdout);                                            \\\n    } while (0)\n\n#define ERR_RET(format, args...)                                   \\\n    do {                                                           \\\n        char buf[64];                                              \\\n        snprintf(buf, sizeof(buf), \"%s(%'d)\", __func__, __LINE__); \\\n        printf(\"ERROR>%-24s:\" format, buf, ##args);                \\\n        fflush(stdout);                                            \\\n        return EXIT_FAILURE;                                       \\\n    } while (0)\n\n#define ERR_RET_NULL(format, args...)                              \\\n    do {                                                           \\\n        char buf[64];                                              \\\n        snprintf(buf, sizeof(buf), \"%s(%'d)\", __func__, __LINE__); \\\n        printf(\"ERROR>%-24s:\" format, buf, ##args);                \\\n        fflush(stdout);                                            \\\n        return NULL;                                               \\\n    } while (0)\n\n#define DBG_PRINT(format, args...)                                     \\\n    do {                                                               \\\n        if (info->verbose) {                                           \\\n            char buf[64];                                              \\\n            snprintf(buf, sizeof(buf), \"%s(%'d)\", __func__, __LINE__); \\\n            printf(\"DEBUG> %-24s:\" format, buf, ##args);               \\\n            fflush(stdout);                                            \\\n        }                                                              \\\n    } while (0)\n\n#define JUMBO_ETHER_MTU     9216        // 9K total size of the Ethernet jumbo frame\n#define JUMBO_DATAROOM_SIZE 9000        // 9K data room size in the Ethernet jumbo frame\n#define JUMBO_HEADROOM_SIZE \\\n    (JUMBO_ETHER_MTU - JUMBO_DATAROOM_SIZE)        // 9K headroom size in the Ethernet jumbo frame\n#define JUMBO_MBUF_SIZE (JUMBO_ETHER_MTU + RTE_PKTMBUF_HEADROOM)\n\nenum {\n    DEFAULT_PKT_SIZE         = 64,           /* Default packet size */\n    DEFAULT_TX_RATE          = 100,          /* Default TX rate */\n    DEFAULT_RX_DESC          = 1024,         /* RX descriptors by default */\n    DEFAULT_TX_DESC          = 1024,         /* TX descriptors by default */\n    DEFAULT_BURST_COUNT      = 32,           /* default RX/TX burst count */\n    DEFAULT_TX_DRAIN_US      = 100,          /* default TX drain every ~100us */\n    DEFAULT_TIMEOUT_PERIOD   = 1,            /* 1 seconds default */\n    DEFAULT_PROMISCUOUS_MODE = 1,            /* default to enabled */\n    DEFAULT_JUMBO_FRAME_MODE = 0,            /* default to disabled for jumbo frames */\n    DEFAULT_MBUF_COUNT       = (8 * 1024),   /* default to 16K mbufs */\n    MAX_MBUF_COUNT           = (128 * 1024), /* max to 128K mbufs */\n    MIN_RX_DESC              = 512,          /* Minimum number of RX descriptors */\n    MIN_TX_DESC              = 512,          /* Minimum number of TX descriptors */\n    MAX_RX_DESC              = 4096,         /* Maximum number of RX descriptors */\n    MAX_TX_DESC              = 4096,         /* Maximum number of TX descriptors */\n    MAX_QUEUES_PER_PORT      = 16,           /* Max number of queues per port */\n    MAX_MAPPINGS             = 32,           /* Max number of mappings */\n    MAX_TX_RATE              = 100,          /* Max TX rate percentage */\n    MAX_PKT_SIZE             = 1518,         /* Maximum packet size */\n    MAX_ALLOCA_SIZE          = 1024,         /* Maximum size of an allocation */\n    MAX_BURST_COUNT          = 512,          /* max burst count */\n    MAX_CHECK_TIME           = 40,           /* (40 * CHECK_INTERVAL) is 10s */\n\n    RANDOM_SEED           = 0x19560630,                     /* Random seed */\n    MEMPOOL_CACHE_SIZE    = RTE_MEMPOOL_CACHE_MAX_SIZE / 2, /* Size of mempool cache */\n    PKT_BUFF_SIZE         = 2048,                           /* Size of packet buffers */\n    MAX_TIMEOUT_PERIOD    = (60 * 60),                      /* 1 hour max */\n    CHECK_INTERVAL        = 250,                            /* Link status check interval 250ms */\n    INTER_FRAME_GAP       = 12,                             /* inter-frame gap in bytes */\n    START_FRAME_DELIMITER = 1,                              /* Start Frame Delimiter in bytes*/\n    PKT_PREAMBLE_SIZE     = 7,                              /* Packet preamble in bytes */\n    PKT_OVERHEAD_SIZE =\n        (INTER_FRAME_GAP + START_FRAME_DELIMITER + PKT_PREAMBLE_SIZE + RTE_ETHER_CRC_LEN),\n};\n#define Million (uint64_t)(1000000UL)\n#define Billion (uint64_t)(1000000000UL)\n\nenum { LCORE_MODE_UNKNOWN = 0, LCORE_MODE_RX = 1, LCORE_MODE_TX = 2, LCORE_MODE_BOTH = 3 };\n\ntypedef struct qstats_s {\n    uint64_t q_ipackets[MAX_QUEUES_PER_PORT]; /** queue Rx packets. */\n    uint64_t q_ibytes[MAX_QUEUES_PER_PORT];   /** successfully received queue bytes */\n\n    uint64_t q_opackets[MAX_QUEUES_PER_PORT]; /** queue Tx packets */\n    uint64_t q_obytes[MAX_QUEUES_PER_PORT];   /** successfully transmitted queue bytes */\n\n    uint64_t q_rx_time[MAX_QUEUES_PER_PORT];    /* Cycles to receive a burst of packets */\n    uint64_t q_tx_drops[MAX_QUEUES_PER_PORT];   /* Tx dropped packets per queue */\n    uint64_t q_tx_time[MAX_QUEUES_PER_PORT];    /* Cycles to transmit a burst of packets */\n    uint64_t q_no_txmbufs[MAX_QUEUES_PER_PORT]; /* Number of times no mbufs were allocated */\n} qstats_t __rte_cache_aligned;\n\ntypedef struct pq_s { /* Port/Queue structure */\n    qstats_t curr;    /* Current statistics */\n    qstats_t prev;    /* Previous statistics */\n    qstats_t rate;    /* Rate statistics */\n} pq_t;\n\ntypedef struct l2p_port_s {\n    rte_atomic16_t inited;                            /* Port initialized flag */\n    pthread_spinlock_t tx_lock;                       /* Tx port lock */\n    volatile uint16_t tx_inited[MAX_QUEUES_PER_PORT]; /* Tx port initialized flag */\n    uint16_t pid;                                     /* Port ID attached to lcore */\n    uint16_t num_rx_qids;                             /* Number of Rx queues */\n    uint16_t num_tx_qids;                             /* Number of Tx queues */\n    uint16_t mtu_size;                                /* MTU size */\n    uint16_t cksum_requires_phdr; /* Flag to indicate if pseudo-header is needed for port */\n    uint64_t tx_cycles;           /* Tx cycles */\n    uint64_t bpp;                 /* Bits per packet */\n    uint64_t pps;                 /* Packets per second */\n    uint64_t ppt;                 /* Packets per thread */\n    struct rte_mempool *rx_mp[MAX_QUEUES_PER_PORT]; /* Rx pktmbuf mempool per queue */\n    struct rte_mempool *tx_mp[MAX_QUEUES_PER_PORT]; /* Tx pktmbuf mempool per queue */\n    struct rte_eth_link link;                       /* Port link status */\n    struct rte_ether_addr mac_addr;                 /* MAC addresses of Port */\n    struct rte_eth_stats stats;                     /* Port statistics */\n    struct rte_eth_stats pstats;                    /* Previous port statistics */\n    pq_t pq[MAX_QUEUES_PER_PORT];                   /* port/queue information */\n} l2p_port_t;\n\ntypedef struct l2p_lport_s { /* Each lcore has one port/queue attached */\n    uint16_t mode;           /* TXPKTS_MODE_RX or TXPKTS_MODE_TX or BOTH */\n    uint16_t lid;            /* Lcore ID */\n    uint16_t rx_qid;         /* Queue ID attached to Rx lcore */\n    uint16_t tx_qid;         /* Queue ID attached to Tx lcore */\n    l2p_port_t *port;        /* Port structure */\n} l2p_lport_t;\n\ntypedef struct {\n    volatile bool force_quit; /* force quit flag */\n    bool verbose;             /* verbose flag */\n    uint16_t num_lcores;      /* number total of lcores */\n    uint16_t num_ports;       /* number total of ports */\n    uint16_t num_mappings;    /* number total of mappings */\n\n    l2p_lport_t\n        *lports[RTE_MAX_LCORE] __rte_cache_aligned; /* Array of lcore/port structure pointers */\n    l2p_port_t ports[RTE_MAX_ETHPORTS] __rte_cache_aligned; /* Array of port structures */\n    char *mappings[MAX_MAPPINGS]; /* Array of string port/queue mappings */\n\n    /* Configuration values from command line options */\n    uint32_t mbuf_count;     /* Number of mbufs to allocate per port. */\n    uint32_t mbuf_size;      /* Size of the MBUFS being used */\n    uint16_t tx_rate;        /* packet TX rate percentage in whole numbers */\n    uint16_t promiscuous_on; /* Ports set in promiscuous mode off by default. */\n    uint16_t jumbo_frame_on; /* Enable jumbo frame support */\n    uint16_t burst_count;    /* Burst size for RX and TX */\n    uint16_t pkt_size;       /* Packet size with FCS */\n    uint16_t nb_rxd;         /* number of RX descriptors */\n    uint16_t nb_txd;         /* number of TX descriptors */\n    uint16_t timeout_secs;   /* Statistics print timeout */\n    uint16_t ip_proto;       /* IP protocol type */\n    fgen_t *fgen;            /* Packet generator */\n    const char *fgen_file;   /* File to use for packet generator */\n} txpkts_info_t;\n\nextern txpkts_info_t *info;\n\nint parse_configuration(int argc, char **argv);\nvoid packet_rate(l2p_port_t *port);\nvoid print_stats(void);\nint port_setup(l2p_port_t *port);\nvoid packet_constructor(l2p_lport_t *lport, uint8_t *pkt, uint16_t proto);\n\nvoid usage(int err);\n\nstatic __inline__ int\npg_socket_id(void)\n{\n    int sid = rte_socket_id();\n\n    return (sid == -1) ? 0 : sid;\n}\n\nstatic __inline__ int\npg_eth_dev_socket_id(int pid)\n{\n    int sid = rte_eth_dev_socket_id(pid);\n\n    return (sid == -1) ? 0 : sid;\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "examples/pktperf/port.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) 2023-2026 Intel Corporation\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <sys/queue.h>\n#include <setjmp.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <errno.h>\n#include <getopt.h>\n#include <signal.h>\n#include <stdbool.h>\n#include <alloca.h>\n#include <locale.h>\n\n#include <pktperf.h>\n\n/**\n * An array of drivers that require a pseudo-header calculation before the checksum calculation.\n * The names used are the ones used by DPDK.\n */\nstatic const char *DRIVERS_REQUIRING_PHDR[] = {\n    \"net_ixgbe\",\n    // TODO: Add the others\n};\n\nstatic struct rte_eth_conf port_conf = {\n    .rxmode =\n        {\n            .mq_mode          = RTE_ETH_MQ_RX_RSS,\n            .max_lro_pkt_size = RTE_ETHER_MAX_LEN,\n            .offloads         = RTE_ETH_RX_OFFLOAD_CHECKSUM,\n            .mtu              = RTE_ETHER_MTU,\n        },\n    .txmode =\n        {\n            .mq_mode = RTE_ETH_MQ_TX_NONE,\n        },\n\n    .rx_adv_conf =\n        {\n            .rss_conf =\n                {\n                    .rss_key = NULL,\n                    .rss_hf  = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP |\n                               RTE_ETH_RSS_SCTP | RTE_ETH_RSS_L2_PAYLOAD,\n                },\n        },\n    .intr_conf =\n        {\n            .lsc = 0,\n        },\n};\n\n/**\n * Determines whether the pseudo-header is required when calculating the checksum.\n * Depends on the original NIC driver (e.g., ixgbe NICs expect the pseudo-header)\n * See Table 1.133: https://doc.dpdk.org/guides/nics/overview.html\n */\nbool\nis_cksum_phdr_required(const char *driver_name)\n{\n    size_t num_drivers = RTE_DIM(DRIVERS_REQUIRING_PHDR);\n\n    for (size_t i = 0; i < num_drivers; i++) {\n        if (DRIVERS_REQUIRING_PHDR[i] == NULL)\n            break;\n        if (strcmp(driver_name, DRIVERS_REQUIRING_PHDR[i]) == 0)\n            return true;\n    }\n\n    return false;\n}\n\nstatic uint32_t\neth_dev_get_overhead_len(uint32_t max_rx_pktlen, uint16_t max_mtu)\n{\n    uint32_t overhead_len;\n\n    if (max_mtu != UINT16_MAX && max_rx_pktlen > max_mtu)\n        overhead_len = max_rx_pktlen - max_mtu;\n    else\n        overhead_len = RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN;\n\n    return overhead_len;\n}\n\nint\nport_setup(l2p_port_t *port)\n{\n    uint16_t pid;\n    struct rte_eth_rxconf rxq_conf;\n    struct rte_eth_txconf txq_conf;\n    struct rte_eth_conf conf = port_conf;\n    struct rte_eth_dev_info dev_info;\n\n    if (!port)\n        ERR_RET(\"%s: port is NULL\\n\", __func__);\n\n    pid = port->pid;\n\n    port->mtu_size = RTE_ETHER_MTU;\n\n    if (rte_atomic16_cmpset(&port->inited.cnt, 0, 1) > 0) {\n        int ret;\n\n        DBG_PRINT(\"Initializing port %u\\n\", pid);\n\n        ret = rte_eth_dev_info_get(pid, &dev_info);\n        if (ret != 0)\n            ERR_RET(\"Error during getting device (port %u) info: %s\\n\", pid, strerror(-ret));\n        DBG_PRINT(\"Driver: %s\\n\", dev_info.driver_name);\n\n        /* Determines if pseudo-header is needed, based on the driver type */\n        port->cksum_requires_phdr = is_cksum_phdr_required(dev_info.driver_name);\n        printf(\"   Checksum offload Pseudo-header required: %s\\n\",\n               port->cksum_requires_phdr ? \"Yes\" : \"No\");\n\n        if (info->jumbo_frame_on) {\n            uint32_t eth_overhead_len;\n            uint32_t max_mtu;\n\n            conf.rxmode.max_lro_pkt_size = JUMBO_ETHER_MTU;\n            eth_overhead_len = eth_dev_get_overhead_len(dev_info.max_rx_pktlen, dev_info.max_mtu);\n            max_mtu          = dev_info.max_mtu - eth_overhead_len;\n            printf(\"Jumbo Frames enabled: Default Max Rx pktlen: %'u, MTU %'u, overhead len: %'u, \"\n                   \"New MTU %'d\\n\",\n                   dev_info.max_rx_pktlen, dev_info.max_mtu, eth_overhead_len, max_mtu);\n\n            /* device may have higher theoretical MTU e.g. for infiniband */\n            if (max_mtu > JUMBO_ETHER_MTU)\n                max_mtu = JUMBO_ETHER_MTU;\n            printf(\"Jumbo Frames enabled: Using Max MTU: %'d\", max_mtu);\n\n            conf.rxmode.mtu = max_mtu;\n#if 0        // FIXME: Tx performance takes a big hit when enabled\n            if (dev_info.rx_offload_capa & RTE_ETH_RX_OFFLOAD_SCATTER)\n                conf.rxmode.offloads |= RTE_ETH_RX_OFFLOAD_SCATTER;\n            if (dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_MULTI_SEGS)\n                conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_MULTI_SEGS;\n#endif\n        }\n\n        if (dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE)\n            conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE;\n#if 0        // FIXME: performance drops a lot when enabled\n        if (dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_TCP_CKSUM) {\n            printf(\"   Enabling Tx TCP_CKSUM offload\");\n            conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_TCP_CKSUM;\n        }\n\n        if (dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_UDP_CKSUM) {\n            printf(\"   Enabling Tx UDP_CKSUM offload\\r\\n\");\n            conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_UDP_CKSUM;\n        }\n\n        if (dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) {\n            printf(\"   Enabling Tx IPV4_CKSUM offload\\r\\n\");\n            conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_IPV4_CKSUM;\n        }\n#endif\n\n        DBG_PRINT(\"Port %u configure with %u:%u queues\\n\", pid, port->num_rx_qids,\n                  port->num_tx_qids);\n\n        conf.rx_adv_conf.rss_conf.rss_key = NULL;\n        conf.rx_adv_conf.rss_conf.rss_hf &= dev_info.flow_type_rss_offloads;\n        if (dev_info.max_rx_queues == 1)\n            conf.rxmode.mq_mode = RTE_ETH_MQ_RX_NONE;\n\n        DBG_PRINT(\"Port %u configure with mode %\" PRIx64 \"\\n\", pid,\n                  conf.rx_adv_conf.rss_conf.rss_hf);\n\n        if (dev_info.max_vfs) {\n            if (conf.rx_adv_conf.rss_conf.rss_hf != 0)\n                conf.rxmode.mq_mode = RTE_ETH_MQ_RX_VMDQ_RSS;\n        }\n        conf.rxmode.offloads &= dev_info.rx_offload_capa;\n\n        DBG_PRINT(\"Port %u configure with mode %u\\n\", pid, conf.rxmode.mq_mode);\n\n        /* Configure the number of queues for a port. */\n        ret = rte_eth_dev_configure(pid, port->num_rx_qids, port->num_tx_qids, &conf);\n        if (ret < 0)\n            ERR_RET(\"Can't configure device: err=%d, port=%u\\n\", ret, pid);\n\n        ret = rte_eth_dev_adjust_nb_rx_tx_desc(pid, &info->nb_rxd, &info->nb_txd);\n        if (ret < 0)\n            ERR_RET(\"Can't adjust number of descriptors: port=%u:%s\\n\", pid, rte_strerror(-ret));\n\n        if ((ret = rte_eth_macaddr_get(pid, &port->mac_addr)) < 0)\n            ERR_RET(\"Can't get MAC address: err=%d, port=%u\\n\", ret, pid);\n\n        DBG_PRINT(\"Port %u MAC address: \" RTE_ETHER_ADDR_PRT_FMT \"\\n\", pid,\n                  RTE_ETHER_ADDR_BYTES(&port->mac_addr));\n\n        ret = rte_eth_dev_set_ptypes(pid, RTE_PTYPE_UNKNOWN, NULL, 0);\n        if (ret < 0)\n            ERR_RET(\"Port %u, Failed to disable Ptype parsing\\n\", pid);\n        DBG_PRINT(\"Port %u configured with %08x Ptypes\\n\", pid, RTE_PTYPE_UNKNOWN);\n\n        if (port->mtu_size < dev_info.min_mtu) {\n            INFO_PRINT(\"Increasing MTU from %u to %u\", port->mtu_size, dev_info.min_mtu);\n            port->mtu_size = dev_info.min_mtu;\n        }\n        if (port->mtu_size > dev_info.max_mtu) {\n            INFO_PRINT(\"Reducing MTU from %u to %u\", port->mtu_size, dev_info.max_mtu);\n            port->mtu_size = dev_info.max_mtu;\n        }\n\n        if ((ret = rte_eth_dev_set_mtu(pid, port->mtu_size)) < 0)\n            ERR_RET(\"Cannot set MTU %u on port %u, (%d)%s\", port->mtu_size, pid, -ret,\n                    rte_strerror(-ret));\n\n        DBG_PRINT(\"Port %u Rx/Tx queues %u/%u\\n\", pid, port->num_rx_qids, port->num_tx_qids);\n\n        /* Setup Rx/Tx Queues */\n        for (int q = 0; q < port->num_rx_qids; q++) {\n            uint32_t sid = pg_eth_dev_socket_id(pid);\n\n            rxq_conf          = dev_info.default_rxconf;\n            rxq_conf.offloads = conf.rxmode.offloads;\n\n            ret = rte_eth_rx_queue_setup(pid, q, info->nb_rxd, sid, &rxq_conf, port->rx_mp[q]);\n            if (ret < 0)\n                ERR_RET(\"rte_eth_rx_queue_setup:err=%d, port=%u\\n\", ret, pid);\n            DBG_PRINT(\"Port %u:%u configured with %u RX descriptors\\n\", pid, q, info->nb_rxd);\n        }\n        for (int q = 0; q < port->num_tx_qids; q++) {\n            uint32_t sid = pg_eth_dev_socket_id(pid);\n\n            txq_conf          = dev_info.default_txconf;\n            txq_conf.offloads = conf.txmode.offloads;\n\n            ret = rte_eth_tx_queue_setup(pid, q, info->nb_txd, sid, &txq_conf);\n            if (ret < 0)\n                ERR_RET(\"rte_eth_tx_queue_setup:err=%d, port=%u\\n\", ret, pid);\n            DBG_PRINT(\"Port %u:%u configured with %u TX descriptors\\n\", pid, q, info->nb_txd);\n        }\n\n        if (info->promiscuous_on) {\n            ret = rte_eth_promiscuous_enable(pid);\n            if (ret != 0)\n                DBG_PRINT(\"INFO: rte_eth_promiscuous_enable:err=%s, port=%u\\n\", rte_strerror(-ret),\n                          pid);\n            else\n                DBG_PRINT(\"Port %u promiscuous mode enabled\\n\", pid);\n        }\n        DBG_PRINT(\"Port %u promiscuous mode is '%s'\\n\", pid, info->promiscuous_on ? \"on\" : \"off\");\n\n        /* Start device */\n        ret = rte_eth_dev_start(pid);\n        if (ret < 0)\n            ERR_RET(\"rte_eth_dev_start:err=%d, port=%u\\n\", ret, pid);\n        DBG_PRINT(\"Port %u started\\n\", pid);\n    }\n    DBG_PRINT(\"Port %u initialized\\n\", pid);\n\n    return 0;\n}\n"
  },
  {
    "path": "examples/pktperf/stats.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) 2023-2026 Intel Corporation\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <sys/queue.h>\n#include <setjmp.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <errno.h>\n#include <getopt.h>\n#include <signal.h>\n#include <stdbool.h>\n#include <alloca.h>\n#include <locale.h>\n\n#include <pktperf.h>\n\n#define sprint(name, cntr, nl)                                                            \\\n    do {                                                                                  \\\n        qstats_t *r;                                                                      \\\n        uint64_t total     = 0;                                                           \\\n        uint16_t nb_queues = (port->num_rx_qids > port->num_tx_qids) ? port->num_rx_qids  \\\n                                                                     : port->num_tx_qids; \\\n        printf(\"  %-12s\", name);                                                          \\\n        for (uint16_t q = 0; q < nb_queues; q++) {                                        \\\n            r = &port->pq[q].rate;                                                        \\\n            total += r->cntr[q];                                                          \\\n            printf(\"|%'12\" PRIu64, (r->cntr[q] / info->timeout_secs));                    \\\n        }                                                                                 \\\n        printf(\"|%'14\" PRIu64 \"|\", (total / info->timeout_secs));                         \\\n        if (nl)                                                                           \\\n            printf(\"\\n\");                                                                 \\\n        fflush(stdout);                                                                   \\\n    } while (0)\n\n/* Print out statistics on packets dropped */\nvoid\nprint_stats(void)\n{\n    struct rte_eth_stats rate;\n    char link_status_text[RTE_ETH_LINK_MAX_STR_LEN];\n    char twirl[]   = \"|/-\\\\\";\n    static int cnt = 0;\n\n    const char clr[]      = {27, '[', '2', 'J', '\\0'};\n    const char top_left[] = {27, '[', '1', ';', '1', 'H', '\\0'};\n\n    /* Clear screen and move to top left */\n    printf(\"%s%s\", clr, top_left);\n\n    printf(\"Port    : Rate Statistics per queue (%c), PID:%d, \", twirl[cnt++ % 4], getpid());\n    printf(\"Size: %'u, Burst: %'u\\n\", info->pkt_size, info->burst_count);\n    printf(\"        : MBUFs:%'u Size:%'u Rx/Tx:%'d/%'d TxRate:%u%%\\n\", info->mbuf_count,\n           info->mbuf_size, info->nb_rxd, info->nb_txd, info->tx_rate);\n    printf(\"        : Mapping: \");\n    for (int i = 0; i < info->num_mappings; i++)\n        printf(\"%s \", info->mappings[i]);\n    printf(\"\\n\\n\");\n\n    for (uint16_t pid = 0; pid < info->num_ports; pid++) {\n        l2p_port_t *port = &info->ports[pid];\n\n        if (rte_atomic16_read(&port->inited) == 0) {\n            printf(\"Port %u is not initialized\\n\", pid);\n            continue;\n        }\n\n        packet_rate(port);\n\n        rte_eth_stats_get(port->pid, &port->stats);\n\n        rate.imissed   = port->stats.imissed - port->pstats.imissed;\n        rate.ierrors   = port->stats.ierrors - port->pstats.ierrors;\n        rate.oerrors   = port->stats.oerrors - port->pstats.oerrors;\n        rate.rx_nombuf = port->stats.rx_nombuf - port->pstats.rx_nombuf;\n        memcpy(&port->pstats, &port->stats, sizeof(struct rte_eth_stats));\n\n        uint16_t nb_queues = (port->num_rx_qids > port->num_tx_qids) ? port->num_rx_qids\n                                                                     : port->num_tx_qids;\n        for (uint16_t q = 0; q < nb_queues; q++) {\n            qstats_t *c, *p, *r;\n\n            c = &port->pq[q].curr;\n            p = &port->pq[q].prev;\n            r = &port->pq[q].rate;\n\n            r->q_opackets[q] = c->q_opackets[q] - p->q_opackets[q];\n            r->q_obytes[q]   = c->q_obytes[q] - p->q_obytes[q];\n\n            r->q_ipackets[q] = c->q_ipackets[q] - p->q_ipackets[q];\n            r->q_ibytes[q]   = c->q_ibytes[q] - p->q_ibytes[q];\n\n            r->q_no_txmbufs[q] = c->q_no_txmbufs[q] - p->q_no_txmbufs[q];\n            r->q_tx_drops[q]   = c->q_tx_drops[q] - p->q_tx_drops[q];\n            r->q_tx_time[q]    = c->q_tx_time[q];\n            r->q_rx_time[q]    = c->q_rx_time[q];\n\n            memcpy(p, c, sizeof(qstats_t));\n        }\n\n        memset(&port->link, 0, sizeof(port->link));\n        if (rte_eth_link_get_nowait(port->pid, &port->link) < 0) {\n            printf(\"Port %u: Failed to get link status\\n\", pid);\n            continue;\n        }\n        rte_eth_link_to_str(link_status_text, sizeof(link_status_text), &port->link);\n\n        printf(\" %2u     : %s, \", pid, link_status_text);\n        packet_rate(port);\n        printf(\"MaxPPS:%'\" PRIu64 \"\\n        : Pkts/Thread:%'\" PRIu64 \", TxCPB:%'\" PRIu64 \"\\n\\n\",\n               port->pps, port->ppt, port->tx_cycles);\n\n        printf(\"  Queue ID    \");\n        for (uint16_t q = 0; q < nb_queues; q++)\n            printf(\"|%10u  \", q);\n        printf(\"|  %11s |\\n\", \"Total\");\n        printf(\"  ------------+\");\n        for (uint16_t q = 0; q < nb_queues; q++)\n            printf(\"------------+\");\n        printf(\"--------------+\\n\");\n\n        sprint(\"RxQs\", q_ipackets, 0);\n        if (rate.ierrors)\n            printf(\" Err : %'12\" PRIu64, rate.ierrors);\n        if (rate.imissed)\n            printf(\" Miss: %'12\" PRIu64, rate.imissed);\n        printf(\"\\n\");\n        sprint(\"TxQs\", q_opackets, 0);\n        if (rate.oerrors)\n            printf(\" Err : %'12\" PRIu64, rate.oerrors);\n        printf(\"\\n\");\n        sprint(\"TxFull\", q_tx_drops, 1);        // tx_drops mean the TX ring was full\n        sprint(\"NoTxMBUF\", q_no_txmbufs, 1);\n        sprint(\"RxTime\", q_rx_time, 1);\n        sprint(\"TxTime\", q_tx_time, 1);\n        printf(\"\\n\");\n    }\n    fflush(stdout);\n}\n"
  },
  {
    "path": "examples/pktperf/utils.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) 2023-2026 Intel Corporation\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <sys/queue.h>\n#include <setjmp.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <errno.h>\n#include <getopt.h>\n#include <signal.h>\n#include <stdbool.h>\n#include <alloca.h>\n#include <locale.h>\n\n#include <pktperf.h>\n\nenum { DEFAULT_WND_SIZE = 8192 };\n\n/**\n *\n * packet_rate - Calculate the transmit rate.\n *\n * DESCRIPTION\n * Calculate the number of cycles to wait between sending bursts of traffic.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\nvoid\npacket_rate(l2p_port_t *port)\n{\n    uint64_t link_speed, bpp, pps, cpb, txcnt;\n\n    if (!port)\n        return;\n\n    // link speed in Megabits per second and tx_rate in percentage\n    if (port->link.link_speed == 0 || info->tx_rate == 0) {\n        port->tx_cycles = 0;\n        port->pps       = 0;\n        return;\n    }\n\n    // total link speed in bits per second\n    link_speed = (uint64_t)port->link.link_speed * Million;\n\n    txcnt = port->num_tx_qids;\n\n    // bits per packet includes preamble, inter-frame gap, and FCS\n    bpp = ((((uint64_t)info->pkt_size - RTE_ETHER_CRC_LEN) + PKT_OVERHEAD_SIZE) * 8);\n\n    // packets per second per thread based on requested (tx_rate/txcnt)\n    pps = (((link_speed / bpp) * (info->tx_rate / txcnt)) / 100);\n    pps = ((pps > 0) ? pps : 1);        // Make sure pps is not zero\n\n    // cycles per burst is hz divided by pps times burst count\n    cpb = (rte_get_timer_hz() / pps) * (uint64_t)info->burst_count;\n\n    port->tx_cycles = cpb * (uint64_t)txcnt;\n    port->pps       = pps;\n    port->ppt       = pps / txcnt;        // packets per thread\n    port->bpp       = bpp;\n\n    DBG_PRINT(\"      Speed:%'4\" PRIu64 \" Gbit, BPP: %'6\" PRIu64 \", PPS: %'12\" PRIu64\n              \", CPB: %'\" PRIu64 \"\\n\",\n              link_speed / Billion, bpp, pps, port->tx_cycles);\n}\n\nstatic __inline__ long\nget_rand(long range)\n{\n    return (random() >> 8) % range;\n}\n\n/*\n * IPv4/UDP packet\n * Port Src/Dest       :           1234/ 5678\n * Pkt Type            :           IPv4 / UDP\n * IP  Destination     :           198.18.1.1\n *     Source          :        198.18.0.1/24\n * MAC Destination     :    3c:fd:fe:e4:34:c0\n *     Source          :    3c:fd:fe:e4:38:40\n */\nvoid\npacket_constructor(l2p_lport_t *lport, uint8_t *pkt, uint16_t proto)\n{\n    l2p_port_t *port = lport->port;\n    uint16_t len;\n    char addr[32];\n    struct rte_ether_hdr *eth;\n    struct rte_ipv4_hdr *ipv4;\n    struct rte_udp_hdr *udp;\n    struct rte_tcp_hdr *tcp;\n    uint16_t tx_qid;\n\n    if (info->fgen_file) {\n        // Get next frame for buffer.\n        return;\n    }\n\n    tx_qid = lport->tx_qid;\n\n    eth  = (struct rte_ether_hdr *)pkt;\n    ipv4 = (struct rte_ipv4_hdr *)(pkt + sizeof(struct rte_ether_hdr));\n\n    for (unsigned int i = 0; i < RTE_ETHER_MAX_LEN; i++)\n        pkt[i] = (uint8_t)(32 + (i % (127 - 32)));\n\n    eth->dst_addr.addr_bytes[0] = 0x00;\n    eth->dst_addr.addr_bytes[1] = 0xaa;\n    eth->dst_addr.addr_bytes[2] = 0xbb;\n    eth->dst_addr.addr_bytes[3] = 0xcc;\n    eth->dst_addr.addr_bytes[4] = 0xdd;\n    eth->dst_addr.addr_bytes[5] = tx_qid;\n\n    memcpy(eth->src_addr.addr_bytes, port->mac_addr.addr_bytes, sizeof(struct rte_ether_addr));\n    eth->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);\n\n    ipv4->version_ihl     = 0x45;\n    ipv4->type_of_service = 0;\n    ipv4->total_length =\n        rte_cpu_to_be_16(info->pkt_size - sizeof(struct rte_ether_hdr) - RTE_ETHER_CRC_LEN);\n    ipv4->packet_id       = rte_cpu_to_be_16(1);\n    ipv4->fragment_offset = 0;\n    ipv4->time_to_live    = 64;\n    ipv4->next_proto_id   = proto;\n    ipv4->hdr_checksum    = 0;\n\n    snprintf(addr, sizeof(addr), \"192.18.0.%u\", (uint8_t)(get_rand(200) + 1));\n    inet_pton(AF_INET, addr, &ipv4->dst_addr);\n    snprintf(addr, sizeof(addr), \"192.18.0.%u\", (uint8_t)(get_rand(200) + 1));\n    inet_pton(AF_INET, addr, &ipv4->src_addr);\n\n    if (proto == IPPROTO_UDP) {\n        udp = (struct rte_udp_hdr *)(pkt + sizeof(struct rte_ether_hdr) +\n                                     sizeof(struct rte_ipv4_hdr));\n\n        udp->src_port = rte_cpu_to_be_16(get_rand(0xFFFE) + 1);\n        udp->dst_port = rte_cpu_to_be_16(get_rand(0xFFFE) + 1);\n\n        len              = (uint16_t)(info->pkt_size - sizeof(struct rte_ether_hdr) -\n                                      sizeof(struct rte_ipv4_hdr) - RTE_ETHER_CRC_LEN);\n        udp->dgram_len   = rte_cpu_to_be_16(len);\n        udp->dgram_cksum = 0;\n        udp->dgram_cksum = rte_ipv4_udptcp_cksum(ipv4, (const void *)udp);\n        if (udp->dgram_cksum == 0)\n            udp->dgram_cksum = 0xFFFF;\n    } else if (proto == IPPROTO_TCP) {\n        tcp = (struct rte_tcp_hdr *)(pkt + sizeof(struct rte_ether_hdr) +\n                                     sizeof(struct rte_ipv4_hdr));\n\n        tcp->src_port = rte_cpu_to_be_16(get_rand(0xFFFE) + 1);\n        tcp->dst_port = rte_cpu_to_be_16(get_rand(0xFFFE) + 1);\n        tcp->sent_seq = rte_cpu_to_be_32(0);\n        tcp->recv_ack = rte_cpu_to_be_32(0);\n        tcp->data_off =\n            ((sizeof(struct rte_tcp_hdr) / sizeof(uint32_t)) << 4); /* Offset in words */\n        tcp->tcp_flags = RTE_TCP_ACK_FLAG;\n        tcp->rx_win    = rte_cpu_to_be_16(DEFAULT_WND_SIZE);\n        tcp->tcp_urp   = 0;\n\n        tcp->cksum = 0;\n        tcp->cksum = rte_ipv4_udptcp_cksum(ipv4, (const void *)tcp);\n    }\n\n    ipv4->hdr_checksum = rte_ipv4_cksum(ipv4);\n}\n"
  },
  {
    "path": "lib/cli/DESIGN.md",
    "content": "# CLI library (lib/cli) — design and usage\n\nThis directory contains Pktgen’s interactive CLI library (a DPDK-style “cmdline workalike”).\nIt implements a small shell-like environment with a directory tree of nodes (commands, files,\naliases, and directories), history and editing, and TAB completion.\n\nIf you want the full historical narrative and sample application details, also see:\n- `lib/cli/cli.rst` (CLI sample application guide)\n- `lib/cli/cli_lib.rst` (CLI library guide)\n\nThis document is intended as a **developer-focused** overview of how the code is structured,\nhow to extend it, and how completion and map-driven parsing work in this repository.\n\n## High-level architecture\n\n### Core concepts\n\n- **CLI instance (`struct cli`)**\n  - Stored in TLS as `this_cli` (per-lcore/thread instance).\n  - Holds the current working directory, search path, history, environment variables, screen\n    state, and registered command maps.\n\n- **Node tree (`struct cli_node`)**\n  - The CLI is organized like a filesystem.\n  - Node types include: directory, command, file, alias, and string nodes.\n  - Nodes are linked in per-directory lists; directories have child lists.\n\n- **Executable search path**\n  - Similar to shell `PATH`: the CLI maintains a list of “bin directories” to search for\n    commands.\n\n### Key modules\n\n- `cli.c`\n  - Core runtime: building/managing nodes, executing commands, history integration, prompt, etc.\n  - Contains the **command→map registry** used by map-driven tooling.\n\n- `cli_input.c`, `cli_vt100.c`, `cli_scrn.c`, `cli_gapbuf.c`\n  - Terminal I/O, editing primitives, cursor movement, and the gap buffer.\n\n- `cli_search.c`\n  - Locating nodes by name/path and enumerating directories.\n\n- `cli_env.c`\n  - Environment variables and command-line substitution of `$(VAR)`.\n\n- `cli_map.c`\n  - Map (pattern) matching for command variants.\n\n- `cli_auto_complete.c`\n  - TAB completion for commands/paths and (optionally) map-driven, context-aware token\n    suggestions.\n\n## Creating commands and trees\n\nMost applications build an initial tree using the helper macros defined in `cli.h`:\n\n- `c_dir(\"/bin\")` / `c_bin(\"/bin\")`: create a directory (bin marks it as executable path)\n- `c_cmd(\"show\", show_cmd, \"...\")`: register a command callback\n- `c_file(\"copyright\", file_cb, \"...\")`: file node backed by callback\n- `c_alias(\"ll\", \"ls -l\", \"...\")`: shell-like alias\n- `c_str(\"FOO\", func, \"default\")`: string node (often used as an env-like value)\n- `c_end()`: terminator\n\nA command callback uses an argv-style signature:\n\n```c\nint my_cmd(int argc, char **argv);\n```\n\nThe CLI library does not convert arguments for you. Treat argv tokens as strings and parse\nas needed.\n\n## Map-driven parsing (cli_map)\n\nThe CLI supports two common ways to interpret a command line:\n\n1. **Direct argv parsing** in your command callback.\n2. **Map-driven selection** using `struct cli_map` and `cli_mapping()`.\n\nA map is an array of format strings with an index:\n\n```c\nstatic struct cli_map my_map[] = {\n    {10, \"show\"},\n    {20, \"show %s\"},\n    {30, \"show %P stats\"},\n    {40, \"show %P %|link|errors|missed stats\"},\n    {-1, NULL}\n};\n```\n\nThe first map entry whose format matches the user’s `argc/argv` is returned, and the index\nis typically used in a switch statement.\n\n### Format tokens\n\nThe map format is similar to `printf`, but `%` has CLI-specific meaning:\n\n- `%d`, `%D`, `%u`, `%U`, `%b`, `%n`: numeric placeholders\n- `%h`, `%H`: hex placeholders\n- `%s`: generic string placeholder\n- `%c`: comma-separated list (string)\n- `%m`: MAC-like token\n- `%4`, `%6`: IPv4/IPv6 token\n- `%P`, `%C`: portlist/corelist tokens\n- `%k`: key/value argument blob\n- `%l`: list string (may require quoting if it contains spaces)\n- `%|a|b|c`: fixed choice list (one of the options must match)\n\nSee `cli_map.c` for exact validation behavior.\n\n## Auto-complete (TAB)\n\nTAB completion provides two layers:\n\n1. **Shell-like completion** for commands, directories, and files by scanning the CLI tree and\n   search path.\n2. **Map-driven completion** when a command has a map registered.\n\n### Registering maps for completion\n\nTo enable map-driven completion, the CLI needs to know which map applies to a command.\nThe registry lives in the CLI instance and can be populated via:\n\n- `cli_register_cmd_map(cmd, map)`\n- `cli_register_cmd_maps(map)` (registers all command names referenced in the map)\n\nOnce registered, `cli_auto_complete.c` can interpret “fixed” tokens (e.g., `tcp`, `dst`, `vlan`)\nvs placeholders (e.g., `%d`, `%P`) and offer:\n\n- a list of valid next fixed tokens\n- placeholder hints such as `<portlist>` or `<comma-list>` when appropriate\n\n### Placeholder hints\n\nWhen there is no concrete completion (because the next token is a user-provided value), the\nengine may print a *hint* like `<portlist>` or `<ipv4-addr>`.\n\nIn general:\n- hints are printed when the completion prefix is empty\n- hints are not inserted into the input line\n\n## Common debugging tips\n\n- Use the CLI “help” or map dump mechanisms to verify the expected token order.\n- If completion suggests an unexpected placeholder hint, it usually means multiple map entries\n  appear compatible with the already-typed tokens.\n  - Ensure fixed keywords are represented as literal tokens or choice tokens in the map.\n  - Ensure placeholders are specific enough (e.g., `%c` for comma-list) rather than `%s`.\n\n## Where to look next\n\n- For adding a new command: locate where the application builds its `cli_tree` and add a\n  `c_cmd(...)` entry.\n- For a command with multiple syntaxes: add/extend its `struct cli_map[]` and use\n  `cli_mapping()` in the callback.\n- For completion improvements: start in `cli_auto_complete.c`, then check the command’s map\n  registration and token formats.\n"
  },
  {
    "path": "lib/cli/README",
    "content": "The cli directory is a simple command line interface.\n\nPlease read the cli.rst and libcli.rst files for more details.\n"
  },
  {
    "path": "lib/cli/cli.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n/* Created by Keith Wiles @ intel.com */\n\n/**\n * @file\n * CLI core implementation.\n *\n * Owns the CLI tree, history, prompt handling, and command execution.\n * Also includes the command-to-map registry used by map-driven features such\n * as auto-complete.\n */\n\n#include <stdio.h>\n#include <poll.h>\n#include <string.h>\n\n#include <rte_version.h>\n#include <rte_timer.h>\n#include <rte_log.h>\n#include <rte_string_fns.h>\n#include <pg_strings.h>\n#include <rte_pause.h>\n\n#include \"cli.h\"\n#include \"cli_input.h\"\n\nstatic int (*__dofile_lua)(void *, const char *);\n\nstruct cli_node_chunk {\n    TAILQ_ENTRY(cli_node_chunk) next;\n    struct cli_node *mem;\n    uint32_t count;\n};\n\nRTE_DEFINE_PER_LCORE(struct cli *, cli);\n\nint\ncli_register_cmd_map(const char *cmd, struct cli_map *map)\n{\n    struct cli *cli = this_cli;\n\n    if (!cli || !cmd || (*cmd == '\\0'))\n        return -1;\n\n    /* Unregister */\n    if (!map) {\n        for (uint32_t i = 0; i < cli->nb_cmd_maps; i++) {\n            if (cli->cmd_maps[i].cmd && !strcmp(cli->cmd_maps[i].cmd, cmd)) {\n                free(cli->cmd_maps[i].cmd);\n                memmove(&cli->cmd_maps[i], &cli->cmd_maps[i + 1],\n                        (cli->nb_cmd_maps - (i + 1)) * sizeof(cli->cmd_maps[0]));\n                cli->nb_cmd_maps--;\n                memset(&cli->cmd_maps[cli->nb_cmd_maps], 0, sizeof(cli->cmd_maps[0]));\n                return 0;\n            }\n        }\n        return 0;\n    }\n\n    /* Update existing */\n    for (uint32_t i = 0; i < cli->nb_cmd_maps; i++) {\n        if (cli->cmd_maps[i].cmd && !strcmp(cli->cmd_maps[i].cmd, cmd)) {\n            cli->cmd_maps[i].map = map;\n            return 0;\n        }\n    }\n\n    /* Grow registry if needed */\n    if (cli->nb_cmd_maps >= cli->cmd_maps_cap) {\n        uint32_t new_cap = cli->cmd_maps_cap ? (cli->cmd_maps_cap * 2) : (uint32_t)CLI_MAX_CMD_MAPS;\n        if (new_cap < cli->nb_cmd_maps + 1)\n            new_cap = cli->nb_cmd_maps + 1;\n\n        cli_cmd_map_t *new_maps = realloc(cli->cmd_maps, (size_t)new_cap * sizeof(*new_maps));\n        if (!new_maps)\n            return -1;\n\n        /* Zero the new slots */\n        if (new_cap > cli->cmd_maps_cap)\n            memset(&new_maps[cli->cmd_maps_cap], 0,\n                   (size_t)(new_cap - cli->cmd_maps_cap) * sizeof(*new_maps));\n\n        cli->cmd_maps     = new_maps;\n        cli->cmd_maps_cap = new_cap;\n    }\n\n    cli->cmd_maps[cli->nb_cmd_maps].cmd = strdup(cmd);\n    if (!cli->cmd_maps[cli->nb_cmd_maps].cmd)\n        return -1;\n\n    cli->cmd_maps[cli->nb_cmd_maps].map = map;\n    cli->nb_cmd_maps++;\n\n    return 0;\n}\n\nint\ncli_register_cmd_maps(struct cli_map *maps)\n{\n    char token[CLI_NAME_LEN];\n    char choices[CLI_MAX_PATH_LENGTH + 1];\n\n    if (!maps)\n        return -1;\n\n    for (int mi = 0; maps[mi].fmt != NULL; mi++) {\n        const char *fmt = maps[mi].fmt;\n        const char *sp;\n        size_t len;\n\n        if (!fmt)\n            continue;\n\n        while (*fmt == ' ')\n            fmt++;\n        if (*fmt == '\\0')\n            continue;\n\n        sp  = strchr(fmt, ' ');\n        len = sp ? (size_t)(sp - fmt) : strlen(fmt);\n        if (len == 0)\n            continue;\n\n        if (len >= sizeof(token))\n            len = sizeof(token) - 1;\n        memcpy(token, fmt, len);\n        token[len] = '\\0';\n\n        /* Skip tokens that are placeholders except for %|choice lists. */\n        if (token[0] == '%') {\n            if (token[1] != '|')\n                continue;\n\n            /* Register each choice (e.g. %|seq|sequence). */\n            snprintf(choices, sizeof(choices), \"%s\", token + 2);\n            char *saveptr = NULL;\n            for (char *opt = strtok_r(choices, \"|\", &saveptr); opt != NULL;\n                 opt       = strtok_r(NULL, \"|\", &saveptr)) {\n                if (*opt == '\\0')\n                    continue;\n                if (cli_register_cmd_map(opt, maps) < 0)\n                    return -1;\n            }\n            continue;\n        }\n\n        if (cli_register_cmd_map(token, maps) < 0)\n            return -1;\n    }\n\n    return 0;\n}\n\nstruct cli_map *\ncli_get_cmd_map(const char *cmd)\n{\n    struct cli *cli = this_cli;\n\n    if (!cli || !cmd || (*cmd == '\\0'))\n        return NULL;\n\n    for (uint32_t i = 0; i < cli->nb_cmd_maps; i++) {\n        if (cli->cmd_maps[i].cmd && !strcmp(cli->cmd_maps[i].cmd, cmd))\n            return cli->cmd_maps[i].map;\n    }\n\n    return NULL;\n}\n\nint\ncli_nodes_unlimited(void)\n{\n    if (!this_cli)\n        return 0;\n    return this_cli->flags & CLI_NODES_UNLIMITED;\n}\n\n/* Allocate a node from the CLI node pool */\nstatic inline struct cli_node *\ncli_alloc(void)\n{\n    struct cli *cli = this_cli;\n    struct cli_node *node;\n\n    node = (struct cli_node *)TAILQ_FIRST(&cli->free_nodes);\n    if (node)\n        TAILQ_REMOVE(&cli->free_nodes, node, next);\n    else if (cli_nodes_unlimited()) {\n        struct cli_node_chunk *chunk;\n        struct cli_node *mem;\n\n        chunk = calloc(1, sizeof(*chunk));\n        if (!chunk)\n            return NULL;\n\n        mem = calloc(CLI_DEFAULT_NB_NODES, sizeof(*mem));\n        if (!mem) {\n            free(chunk);\n            return NULL;\n        }\n\n        chunk->mem   = mem;\n        chunk->count = CLI_DEFAULT_NB_NODES;\n        TAILQ_INSERT_TAIL(&cli->node_chunks, chunk, next);\n\n        for (uint32_t i = 0; i < chunk->count; i++)\n            TAILQ_INSERT_TAIL(&cli->free_nodes, &mem[i], next);\n\n        cli->nb_nodes += chunk->count;\n\n        node = (struct cli_node *)TAILQ_FIRST(&cli->free_nodes);\n        if (node)\n            TAILQ_REMOVE(&cli->free_nodes, node, next);\n    }\n\n    return node;\n}\n\n/* Free a node back to the CLI mempool */\nstatic inline void\ncli_free(struct cli_node *node)\n{\n    TAILQ_INSERT_TAIL(&this_cli->free_nodes, node, next);\n}\n\n/* Add a directory to the executable path */\nint\ncli_add_bin(struct cli_node *dir)\n{\n    struct cli *cli = this_cli;\n\n    if (!dir || !is_directory(dir))\n        return -1;\n\n    /* Check for duplicates */\n    for (int i = 0; i < CLI_MAX_BINS; i++) {\n        struct cli_node *d = cli->bins[i];\n\n        if (!d)\n            continue;\n        if (strcmp(d->name, dir->name) == 0) {\n            RTE_LOG(WARNING, EAL, \"Adding duplicate bin directory (%s)\\n\", dir->name);\n            return 0;\n        }\n    }\n\n    // printf(\"Searching for free bin directory slot for: %s\\n\", dir->name);\n    /* Skip first special entry for current working directory */\n    for (int i = 0; i < CLI_MAX_BINS; i++) {\n        struct cli_node *d = cli->bins[i];\n\n        if (d)\n            continue;\n        cli->bins[i] = dir;\n        return 0;\n    }\n\n    return -1;\n}\n\n/* Remove a directory from the executable path */\nint\ncli_del_bin(struct cli_node *dir)\n{\n    struct cli *cli = this_cli;\n\n    if (!dir || !is_directory(dir))\n        return -1;\n\n    for (int i = 0; i < CLI_MAX_BINS; i++)\n        if (cli->bins[i] == dir) {\n            cli->bins[i] = NULL;\n\n            /* compress the list of directories */\n            if ((i + 1) < CLI_MAX_BINS) {\n                memmove(&cli->bins[i], &cli->bins[i + 1],\n                        (CLI_MAX_BINS - (i + 1)) * sizeof(void *));\n                cli->bins[CLI_MAX_BINS - 1] = NULL;\n            }\n            return 0;\n        }\n    return -1;\n}\n\n/* Add a directory to the executable path using the path string */\nint\ncli_add_bin_path(const char *path)\n{\n    struct cli_node *node;\n\n    if (cli_find_node(path, &node)) {\n        if (cli_add_bin(node))\n            return -1;\n    } else\n        return -1;\n\n    return 0;\n}\n\n/* Helper routine to remove nodes to the CLI tree */\nint\ncli_remove_node(struct cli_node *node)\n{\n    struct cli_node *parent, *n;\n\n    if (!node)\n        return 0;\n\n    parent = node->parent;\n    if (!parent) /* Can not remove '/' or root */\n        return -1;\n\n    switch (node->type) {\n    case CLI_DIR_NODE:\n        if (!TAILQ_EMPTY(&node->items))\n            while (!TAILQ_EMPTY(&node->items)) {\n                n = TAILQ_FIRST(&node->items);\n                if (cli_remove_node(n))\n                    return -1;\n            }\n        break;\n    case CLI_CMD_NODE:\n    case CLI_FILE_NODE:\n    case CLI_ALIAS_NODE:\n    case CLI_STR_NODE:\n        break;\n    default:\n        return -1;\n    }\n\n    if (is_directory(node))\n        cli_del_bin(node);\n\n    TAILQ_REMOVE(&parent->items, node, next);\n    cli_free(node);\n\n    return 0;\n}\n\n/* Helper routine to add nodes to the CLI tree */\nstatic struct cli_node *\n__add_node(const char *name, struct cli_node *parent, int type, cli_funcs_t func,\n           const char *short_desc)\n{\n    struct cli_node *node;\n\n    if (!name)\n        return NULL;\n\n    switch (type) {\n    case CLI_DIR_NODE:\n        if (parent && (strcmp(name, CLI_ROOT_NAME) == 0))\n            return NULL;\n        if (!parent && strcmp(name, CLI_ROOT_NAME))\n            return NULL;\n        if (func.cfunc)\n            return NULL;\n        break;\n    case CLI_CMD_NODE:\n        if (!parent || !func.cfunc)\n            return NULL;\n        break;\n    case CLI_FILE_NODE:\n        if (!parent || !func.ffunc)\n            return NULL;\n        break;\n    case CLI_ALIAS_NODE:\n        if (!parent || func.cfunc)\n            return NULL;\n        break;\n    case CLI_STR_NODE:\n        if (!func.sfunc && !short_desc)\n            return NULL;\n        break;\n    default:\n        return NULL;\n    }\n\n    node = cli_alloc();\n    if (node == NULL) {\n        cli_printf(\"%s: No nodes left\\n\", __func__);\n        return NULL;\n    }\n\n    node->type   = type;\n    node->parent = parent;\n\n    switch (type) {\n    case CLI_CMD_NODE:\n    case CLI_ALIAS_NODE:\n        node->cfunc = func.cfunc;\n        break;\n    case CLI_FILE_NODE:\n        node->ffunc = func.ffunc;\n        break;\n    case CLI_DIR_NODE:\n    case CLI_STR_NODE:\n        break;\n    default:\n        break;\n    }\n    node->short_desc = short_desc;\n    snprintf(node->name, sizeof(node->name), \"%s\", name);\n    node->name_sz = strlen(node->name);\n\n    if (parent)\n        TAILQ_INSERT_HEAD(&parent->items, node, next);\n\n    return node;\n}\n\n/* Add a directory to the CLI tree */\nstruct cli_node *\ncli_add_dir(const char *name, struct cli_node *dir)\n{\n    struct cli *cli = this_cli;\n    char *argv[CLI_MAX_ARGVS], *p;\n    char path[CLI_MAX_PATH_LENGTH];\n    int cnt, i;\n    struct cli_node *n, *ret;\n    cli_funcs_t funcs;\n\n    RTE_ASSERT(cli != NULL);\n    if (!name)\n        return NULL;\n\n    /* return the last node if directory path already exists */\n    if (cli_find_node((char *)(uintptr_t)name, &ret))\n        return ret;\n\n    /* Set the function structure to NULL */\n    funcs.cfunc = NULL;\n\n    p = cli->scratch;\n    if (!dir) /* Passed in a NULL to start at root node */\n        dir = cli->root.tqh_first;\n\n    memset(path, '\\0', sizeof(path));\n\n    p = cli->scratch;\n\n    /* Grab a local copy of the directory path */\n    snprintf(p, CLI_MAX_SCRATCH_LENGTH, \"%s\", name);\n\n    if (p[0] == '/') { /* Start from root */\n        dir = cli->root.tqh_first;\n        p++;           /* Skip the / in the original path */\n        path[0] = '/'; /* Add root to the path */\n    }\n\n    cnt = pg_strtok(p, \"/\", argv, CLI_MAX_ARGVS);\n\n    n = NULL;\n    for (i = 0; i < cnt; i++) {\n        /* Append each directory part to the search path (with bounds + '/') */\n        size_t used       = strnlen(path, sizeof(path));\n        size_t need_slash = (used > 0 && path[used - 1] != '/');\n        int wrote;\n\n        if (need_slash) {\n            wrote = snprintf(path + used, sizeof(path) - used, \"/\");\n            if (wrote < 0 || (size_t)wrote >= (sizeof(path) - used))\n                return NULL;\n            used += (size_t)wrote;\n        }\n\n        wrote = snprintf(path + used, sizeof(path) - used, \"%s\", argv[i]);\n        if (wrote < 0 || (size_t)wrote >= (sizeof(path) - used))\n            return NULL;\n\n        if (cli_find_node(path, &ret)) {\n            dir = ret;\n            continue;\n        }\n\n        n = __add_node(argv[i], dir, CLI_DIR_NODE, funcs, NULL);\n        if (n == NULL)\n            break;\n        dir = n;\n    }\n    return n;\n}\n\n/* Add a command executable to the CLI tree */\nstruct cli_node *\ncli_add_cmd(const char *name, struct cli_node *dir, cli_cfunc_t func, const char *short_desc)\n{\n    cli_funcs_t funcs;\n\n    funcs.cfunc = func;\n    return __add_node(name, dir, CLI_CMD_NODE, funcs, short_desc);\n}\n\n/* Add a command alias executable to the CLI tree */\nstruct cli_node *\ncli_add_alias(const char *name, struct cli_node *dir, const char *line, const char *short_desc)\n{\n    struct cli_node *alias;\n    cli_funcs_t funcs;\n\n    funcs.cfunc      = NULL;\n    alias            = __add_node(name, dir, CLI_ALIAS_NODE, funcs, short_desc);\n    alias->alias_str = (const char *)strdup(line);\n\n    return alias;\n}\n\n/* Add a file to the CLI tree */\nstruct cli_node *\ncli_add_file(const char *name, struct cli_node *dir, cli_ffunc_t func, const char *short_desc)\n{\n    cli_funcs_t funcs;\n\n    funcs.ffunc = func;\n    return __add_node(name, dir, CLI_FILE_NODE, funcs, short_desc);\n}\n\n/* Add a string to the CLI tree */\nint\ncli_add_str(const char *name, cli_sfunc_t func, const char *str)\n{\n    return cli_env_string(this_cli->env, name, func, str);\n}\n\n/* Add a directory/commands/files/... to a directory */\nint\ncli_add_tree(struct cli_node *parent, struct cli_tree *tree)\n{\n    struct cli *cli = this_cli;\n    struct cli_tree *t;\n    struct cli_dir *d;\n    struct cli_cmd *c;\n    struct cli_file *f;\n    struct cli_alias *a;\n    struct cli_str *s;\n    struct cli_node *n;\n\n    if (!tree)\n        return -1;\n\n    if (!parent)\n        parent = cli->root.tqh_first;\n\n    for (t = tree; t->type != CLI_UNK_NODE; t++) {\n        switch (t->type) {\n        case CLI_DIR_NODE:\n            d = &t->dir;\n\n            if (!(n = cli_add_dir(d->name, parent))) {\n                RTE_LOG(ERR, EAL, \"Add directory %s failed\\n\", d->name);\n                return -1;\n            }\n            if (d->bin)\n                cli_add_bin_path(d->name);\n\n            parent = n;\n            break;\n\n        case CLI_CMD_NODE:\n            c = &t->cmd;\n            if (!cli_add_cmd(c->name, parent, c->cfunc, c->short_desc)) {\n                RTE_LOG(ERR, EAL, \"Add command %s failed\\n\", c->name);\n                return -1;\n            }\n            break;\n\n        case CLI_FILE_NODE:\n            f = &t->file;\n            if (!cli_add_file(f->name, parent, f->ffunc, f->short_desc)) {\n                RTE_LOG(ERR, EAL, \"Add file %s failed\\n\", f->name);\n                return -1;\n            }\n            break;\n\n        case CLI_ALIAS_NODE:\n            a = &t->alias;\n            if (!cli_add_alias(a->name, parent, a->alias_atr, a->short_desc)) {\n                RTE_LOG(ERR, EAL, \"Add alias %s failed\\n\", a->name);\n                return -1;\n            }\n            break;\n\n        case CLI_STR_NODE:\n            s = &t->str;\n            if (cli_add_str(s->name, s->sfunc, s->string)) {\n                RTE_LOG(ERR, EAL, \"Add string %s failed\\n\", s->name);\n                return -1;\n            }\n            break;\n\n        case CLI_UNK_NODE:\n        default:\n            RTE_LOG(ERR, EAL, \"Unknown Node type %d\\n\", t->type);\n            return 0;\n        }\n    }\n\n    return 0;\n}\n\n/* execute a command or alias node in the CLI tree */\nint\ncli_execute(void)\n{\n    struct cli *cli = this_cli;\n    struct cli_node *node;\n    int argc, ret, sz;\n    struct gapbuf *gb = cli->gb;\n    char *line        = NULL, *p, *hist;\n\n    RTE_ASSERT(cli != NULL);\n\n    sz = gb_data_size(gb);\n    sz = RTE_MAX(sz, CLI_MAX_PATH_LENGTH);\n    sz = RTE_MIN(sz, (int)(CLI_MAX_SCRATCH_LENGTH - 1));\n\n    line = calloc(1, (size_t)sz + 1);\n    if (!line)\n        return -1;\n\n    /* gb_copy_to_buf() forces linebuf to be null terminated */\n    gb_copy_to_buf(gb, line, sz);\n\n    /* Trim the string of whitespace on front and back */\n    p = pg_strtrim(line);\n    if (!strlen(p))\n        goto out_ok;\n\n    if (p[0] == '#') /* Found a comment line starting with a '#' */\n        goto out_ok;\n    else if (p[0] == '!') { /* History command */\n        hist = cli_history_line(atoi(&p[1]));\n        if (!hist) {\n            cli_printf(\"Unknown history line number %d\\n\", atoi(&p[1]));\n            goto out_ok;\n        }\n        /* History lines are already trimmed and ready to be executed */\n        snprintf(line, (size_t)sz + 1, \"%s\", hist);\n#ifdef RTE_CLI_HOST_COMMANDS\n    } else if (p[0] == '@') { /* System execute a command */\n        ret = cli_system(&p[1]);\n        if (!ret)\n            cli_history_add(p);\n        free(line);\n        return ret;\n#endif\n    } else\n        cli_history_add(p);\n\n    /* Process the line for environment variable substitution */\n    cli_env_substitution(cli->env, p, sz - (p - line));\n\n    argc = pg_strqtok(p, \" \\r\\n\", cli->argv, CLI_MAX_ARGVS);\n\n    if (!argc)\n        goto out_ok;\n\n    node = cli_find_cmd(cli->argv[0]);\n    if (!node) {\n        cli_printf(\"** command not found (%s)\\n\", cli->argv[0]);\n        ret = -1;\n        goto out;\n    }\n\n    ret = -1;\n    switch (node->type) {\n    case CLI_CMD_NODE:\n        /*\n         * Reset global optind so getopt works as expected in a node's command function. The\n         * getopt man page says to set optind to 0 instead of 1 if a program scans multiple\n         * argument vectors, which can happen if multiple commands use getopt.\n         */\n        optind = 0;\n\n        cli->exe_node = node;\n        ret           = node->cfunc(argc, cli->argv);\n        cli->exe_node = NULL;\n        break;\n\n    case CLI_ALIAS_NODE:\n        /* Delete the alias history line just added */\n        cli_history_del();\n\n        cli->scratch[0] = '\\0'; /* Reset scratch to empty */\n\n        /* If there is more data after command name save it */\n        if (gb_data_size(gb) > node->name_sz)\n            gb_copy_to_buf(cli->gb, cli->scratch, gb_data_size(gb));\n\n        sz = strlen(cli->scratch);\n\n        gb_reset_buf(gb);\n\n        gb_str_insert(gb, (char *)(uintptr_t)node->alias_str, strlen(node->alias_str));\n\n        /* Add the extra line arguments */\n        sz = sz - node->name_sz;\n        if (sz > 0)\n            gb_str_insert(gb, &cli->scratch[node->name_sz], sz);\n        ret = cli_execute();\n        break;\n\n    case CLI_DIR_NODE:\n        cli_printf(\"** (%s) is a directory\\n\", cli->argv[0]);\n        break;\n\n    case CLI_FILE_NODE:\n        cli_printf(\"** (%s) is a file\\n\", cli->argv[0]);\n        break;\n\n    case CLI_STR_NODE:\n        cli_printf(\"** (%s) is a string\\n\", cli->argv[0]);\n        break;\n\n    case CLI_UNK_NODE:\n    default:\n        cli_printf(\"** unknown type (%s)\\n\", cli->argv[0]);\n        break;\n    }\n    cli_history_reset();\n\nout:\n    free(line);\n    return ret;\n\nout_ok:\n    ret = 0;\n    goto out;\n}\n\n/* Main entry point into the CLI system to start accepting user input */\nvoid\ncli_start(const char *msg)\n{\n    char c;\n\n    RTE_ASSERT(this_cli != NULL);\n\n    cli_printf(\"\\n** Version: %s, %s\\n\", rte_version(),\n               (msg == NULL) ? \"Command Line Interface\" : msg);\n\n    this_cli->plen = this_cli->prompt(0);\n\n    while (!this_cli->quit_flag) {\n        if (cli_poll(&c))\n            cli_input(&c, 1);\n        rte_pause();\n    }\n\n    cli_printf(\"\\n\");\n}\n\n/* Create a CLI root node for the tree */\nstruct cli_node *\ncli_create_root(const char *dirname)\n{\n    struct cli_node *root;\n    cli_funcs_t funcs;\n\n    funcs.cfunc = NULL;\n\n    /* Create and add the root directory */\n    root = __add_node(dirname, NULL, CLI_DIR_NODE, funcs, NULL);\n    if (!root)\n        return NULL;\n\n    TAILQ_INSERT_HEAD(&this_cli->root, root, next);\n\n    /* point at the root directory for current working directory */\n    set_cwd(root);\n\n    return root;\n}\n\n/* Default CLI prompt routine */\nstatic int\n__default_prompt(int cont)\n{\n    char *str = cli_cwd_path();\n    int len   = 0;\n\n    if (strlen(str) > 1) /* trim the trailing '/' from string */\n        str[strlen(str) - 1] = '\\0';\n\n    scrn_color(SCRN_GREEN, SCRN_NO_CHANGE, SCRN_OFF);\n    len += cli_printf(\"%s:\", (cont) ? \" >> \" : \"DPDK-cli\");\n    scrn_color(SCRN_CYAN, SCRN_NO_CHANGE, SCRN_OFF);\n    len += cli_printf(\"%s\", str);\n    scrn_color(SCRN_DEFAULT_FG, SCRN_DEFAULT_BG, SCRN_OFF);\n    len += cli_printf(\"> \");\n\n    return len;\n}\n\n/* Main entry point to create a CLI system */\nint\ncli_init(int nb_entries, uint32_t nb_hist)\n{\n    int i;\n    size_t size;\n    struct cli *cli;\n    struct cli_node *node;\n    struct cli_node *root;\n\n    cli = calloc(1, sizeof(struct cli));\n    if (cli == NULL)\n        rte_exit(EXIT_FAILURE, \"Unable to allocate CLI structure\\n\");\n\n    this_cli = cli;\n\n    cli->prompt = __default_prompt;\n\n    if (nb_entries == 0)\n        nb_entries = CLI_DEFAULT_NB_NODES;\n    else if (nb_entries == -1) {\n        cli->flags |= CLI_NODES_UNLIMITED;\n        nb_entries = CLI_DEFAULT_NB_NODES;\n    }\n    cli->nb_nodes = nb_entries;\n\n    if (nb_hist == (uint32_t)CLI_DEFAULT_HISTORY)\n        nb_hist = CLI_DEFAULT_HIST_LINES;\n\n    size          = (nb_entries * sizeof(struct cli_node));\n    cli->node_mem = calloc(1, size);\n    if (cli->node_mem == NULL)\n        rte_exit(EXIT_FAILURE, \"Unable to allocate CLI node structures\\n\");\n\n    cli->nb_hist = nb_hist;\n\n    TAILQ_INIT(&cli->root);       /* Init the directory list */\n    TAILQ_INIT(&cli->free_nodes); /* List of free nodes */\n    TAILQ_INIT(&cli->help_nodes); /* List of help nodes */\n    TAILQ_INIT(&cli->node_chunks);\n\n    CIRCLEQ_INIT(&cli->free_hist); /* List of free hist nodes */\n    CIRCLEQ_INIT(&cli->hd_hist);   /* Init the history for list head */\n\n    if (scrn_create_with_defaults(SCRN_THEME_ON))\n        goto error_exit;\n\n    cli->vt = vt100_setup();\n    if (!cli->vt)\n        goto error_exit;\n\n    cli->scratch = calloc(1, CLI_MAX_SCRATCH_LENGTH);\n    if (!cli->scratch)\n        goto error_exit;\n\n    cli->argv = calloc(CLI_MAX_ARGVS, sizeof(void *));\n    if (!cli->argv)\n        goto error_exit;\n\n    /* command -> cli_map registry */\n    cli->cmd_maps = calloc((size_t)CLI_MAX_CMD_MAPS, sizeof(*cli->cmd_maps));\n    if (!cli->cmd_maps)\n        goto error_exit;\n    cli->cmd_maps_cap = CLI_MAX_CMD_MAPS;\n\n    /* Create the pool for the number of nodes */\n    node = cli->node_mem;\n    for (i = 0; i < nb_entries; i++, node++)\n        TAILQ_INSERT_TAIL(&cli->free_nodes, node, next);\n\n    root = cli_create_root(CLI_ROOT_NAME);\n    if (!root) {\n        RTE_LOG(ERR, EAL, \"Unable to create root directory\\n\");\n        goto error_exit;\n    }\n\n    /* Set current working directory to root*/\n    set_cwd(root);\n\n    if (cli_set_history(nb_hist)) {\n        RTE_LOG(ERR, EAL, \"Unable to create history\\n\");\n        goto error_exit;\n    }\n\n    /* create and initialize the gap buffer structures */\n    cli->gb = gb_create();\n    if (!cli->gb) {\n        RTE_LOG(ERR, EAL, \"Unable to create Gap Buffer\\n\");\n        goto error_exit;\n    }\n\n    /* Startup the environment system */\n    cli->env = cli_env_create();\n    if (!cli->env)\n        goto error_exit;\n\n    return 0;\n\nerror_exit:\n    cli_destroy();\n    return -1;\n}\n\nint\ncli_create(void)\n{\n    return cli_init(CLI_DEFAULT_NODES, CLI_DEFAULT_HIST_LINES);\n}\n\nint\ncli_create_with_defaults(void)\n{\n    if (cli_init(CLI_DEFAULT_NODES, CLI_DEFAULT_HIST_LINES) == 0)\n        return cli_setup_with_defaults();\n    return -1;\n}\n\n/* Cleanup the CLI allocation of memory */\nvoid\ncli_destroy(void)\n{\n    struct cli *cli = this_cli;\n\n    if (!cli)\n        return;\n\n    gb_destroy(cli->gb);\n    vt100_free(cli->vt);\n    cli_history_delete();\n\n    free(cli->scratch);\n    free(cli->kill);\n    free(cli->argv);\n    free(cli->hist_mem);\n    free(cli->node_mem);\n\n    if (cli->cmd_maps) {\n        for (uint32_t i = 0; i < cli->nb_cmd_maps; i++)\n            free(cli->cmd_maps[i].cmd);\n        free(cli->cmd_maps);\n        cli->cmd_maps = NULL;\n    }\n    cli->nb_cmd_maps  = 0;\n    cli->cmd_maps_cap = 0;\n\n    while (!TAILQ_EMPTY(&cli->node_chunks)) {\n        struct cli_node_chunk *chunk = TAILQ_FIRST(&cli->node_chunks);\n        TAILQ_REMOVE(&cli->node_chunks, chunk, next);\n        free(chunk->mem);\n        free(chunk);\n    }\n\n    free(cli);\n\n    this_cli = NULL;\n}\n\nint\ncli_setup(cli_prompt_t prompt, cli_tree_t default_func)\n{\n    if (!this_cli)\n        return -1;\n\n    /* Set the user or default prompt routine */\n    this_cli->prompt = (prompt == NULL) ? __default_prompt : prompt;\n\n    /* when null call our default tree setup routine */\n    if (default_func == NULL)\n        default_func = cli_default_tree_init;\n\n    /* now call the user supplied func or ours if default_func was NULL */\n    return default_func();\n}\n\n/* Helper routine around the cli_create() routine */\nint\ncli_setup_with_defaults(void)\n{\n    return cli_setup(NULL, NULL);\n}\n\n/* Helper routine around the cli_create() routine */\nint\ncli_setup_with_tree(cli_tree_t tree)\n{\n    return cli_setup(NULL, tree);\n}\n\n/* Add a new prompt routine to the CLI system */\ncli_prompt_t\ncli_set_prompt(cli_prompt_t prompt)\n{\n    struct cli *cli = this_cli;\n    cli_prompt_t old;\n\n    old         = cli->prompt; /* Save old prompt function */\n    cli->prompt = prompt;      /* Install new prompt function */\n\n    if (cli->prompt == NULL) /* Set to default function if NULL */\n        cli->prompt = __default_prompt;\n\n    return old;\n}\n\n/**\n * set the callout function pointer to execute a lua file.\n */\nvoid\ncli_set_lua_callback(int (*func)(void *, const char *))\n{\n    __dofile_lua = func;\n}\n\n/**\n * Load and execute a command file or Lua script file.\n *\n */\nint\ncli_execute_cmdfile(const char *filename)\n{\n    if (filename == NULL)\n        return 0;\n\n    gb_reset_buf(this_cli->gb);\n\n    cli_printf(\"\\nExecuting '%s'\\n\", filename);\n\n    if (strstr(filename, \".lua\") || strstr(filename, \".LUA\")) {\n        if (!this_cli->user_state) {\n            cli_printf(\">>> User State for CLI not set for Lua, please build with Lua enabled\\n\");\n            return -1;\n        }\n        if (__dofile_lua) {\n            /* Execute the Lua script file. */\n            if (__dofile_lua(this_cli->user_state, filename) != 0)\n                return -1;\n        } else\n            cli_printf(\">>> Lua is not enabled in configuration!\\n\");\n    } else {\n        FILE *fd;\n        char buff[1024];\n\n        fd = fopen(filename, \"r\");\n        if (fd == NULL)\n            return -1;\n\n        /* Read and feed the lines to the cmdline parser. */\n        while (fgets(buff, sizeof(buff), fd)) {\n            cli_input(buff, strlen(buff));\n            memset(buff, 0, sizeof(buff));\n        }\n\n        fclose(fd);\n    }\n    return 0;\n}\n\nint\ncli_execute_cmdfiles(void)\n{\n    this_scrn->no_write = 1;\n    for (uint32_t i = 0; i < this_cli->cmd_files.idx; i++) {\n        const char *path;\n\n        if ((path = this_cli->cmd_files.filename[i]) == NULL)\n            continue;\n\n        if (cli_execute_cmdfile(path)) {\n            this_scrn->no_write = 0;\n            return -1;\n        }\n\n        free((char *)(uintptr_t)path);\n        this_cli->cmd_files.filename[i] = NULL;\n    }\n    this_cli->cmd_files.idx = 0;\n    this_scrn->no_write     = 0;\n    return 0;\n}\n\nint\ncli_num_cmdfiles(void)\n{\n    return this_cli->cmd_files.idx;\n}\n"
  },
  {
    "path": "lib/cli/cli.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n/* Created by Keith Wiles @ intel.com */\n\n#ifndef _CLI_H_\n#define _CLI_H_\n\n/**\n * @file\n * CLI engine — core structs, node management, and setup API.\n *\n * Defines the cli_node and cli directory-tree types, the per-lcore struct cli\n * instance, and all public functions for initialising, populating, and running\n * the interactive CLI engine.\n */\n#include <libgen.h>\n#include <sys/queue.h>\n\n#include <rte_common.h>\n#include <rte_debug.h>\n#include <rte_memory.h>\n#include <rte_per_lcore.h>\n\n#include <cli_common.h>\n#include <cli_env.h>\n#include <cli_search.h>\n\n#include <cli_file.h>\n#include <cli_gapbuf.h>\n#include <cli_help.h>\n#include <cli_history.h>\n#include <cli_map.h>\n\n#include <rte_string_fns.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define CLI_ROOT_NAME \"/\"\n#define CLI_BIN_NAME  \"bin\"\n\nenum {\n    CLI_MAX_ARGVS          = 64,   /**< Max number of args to support */\n    CLI_DEFAULT_NB_NODES   = 256,  /**< Default number of nodes */\n    CLI_DEFAULT_HIST_LINES = 128,  /**< Default number of history lines */\n    CLI_MAX_PATH_LENGTH    = 2048, /**< Max path string length */\n    CLI_MAX_SCRATCH_LENGTH = 4096, /**< Max scratch space length */\n    CLI_NAME_LEN           = 64,   /**< Max node name dir/cmd/file/.. */\n    CLI_MAX_LIST_NODES     = 128,  /**< Max number of nodes to list */\n    CLI_MAX_BINS           = 32,   /**< Max number of bin directories */\n    CLI_DEFAULT_NODES      = 0,    /**< Use default node count */\n    CLI_SCREEN_WIDTH       = 80    /**< Size of screen width */\n};\n\nenum {\n    /* Initial number of command->map registrations. The registry can grow. */\n    CLI_MAX_CMD_MAPS = 64\n};\n\n#define CLI_RECURSE_FLAG   (1 << 0)\n#define CLI_LONG_LIST_FLAG (1 << 1)\n\n/* bitmap for the node type */\ntypedef enum {\n    CLI_UNK_NODE   = 0x0000, /**< Unknown node type */\n    CLI_DIR_NODE   = 0x0001, /**< Directory node type */\n    CLI_CMD_NODE   = 0x0002, /**< Command node type */\n    CLI_FILE_NODE  = 0x0004, /**< File node type */\n    CLI_ALIAS_NODE = 0x0008, /**< Alias node type */\n    CLI_STR_NODE   = 0x0010, /**< String node type */\n} node_type_t;\n\n/* Keep this list in sync with the node_type_t enum above */\n#define CLI_NODE_TYPES {\"Unknown\", \"Directory\", \"Command\", \"File\", \"Alias\", \"String\", NULL}\n\nenum {\n    CLI_EXE_TYPE   = (CLI_CMD_NODE | CLI_ALIAS_NODE),\n    CLI_ALL_TYPE   = (CLI_EXE_TYPE | CLI_FILE_NODE | CLI_DIR_NODE),\n    CLI_OTHER_TYPE = (CLI_DIR_NODE | CLI_FILE_NODE)\n};\n\nstruct cli;\nstruct cli_node;\nstruct cli_node_chunk;\n\ntypedef struct {\n    char *cmd;\n    struct cli_map *map;\n} cli_cmd_map_t;\n\ntypedef int (*cli_cfunc_t)(int argc, char **argv);\n/**< CLI function pointer type for a command/alias node  */\ntypedef int (*cli_ffunc_t)(struct cli_node *node, char *buff, int len, uint32_t opt);\n/**< CLI function pointer type for a file type node  */\n\ntypedef int (*cli_prompt_t)(int continuation); /**< CLI prompt routine */\ntypedef int (*cli_tree_t)(void);\n/**< CLI function pointer type for user initialization */\n\n/* Generic node structure for all node types in the directory tree */\nstruct cli_node {\n    TAILQ_ENTRY(cli_node) next; /**< link list of commands */\n    struct cli_node *parent;    /**< Parent directory (NULL == ROOT) */\n    char name[CLI_NAME_LEN];    /**< Name of Node */\n    uint16_t name_sz;           /**< Number of bytes in name w/o null */\n    uint16_t fstate;            /**< File State */\n    uint16_t fflags;            /**< File flags */\n    uint16_t pad0;\n    node_type_t type; /**< Node Type Root, Dir or cmd */\n    union {\n        cli_cfunc_t cfunc; /**< Function pointer for commands */\n        cli_ffunc_t ffunc; /**< Function pointer for files */\n        cli_sfunc_t sfunc; /**< Function pointer for Strings */\n    };\n    const char *short_desc;       /**< Short description */\n    const char *alias_str;        /**< Alias string */\n    size_t foffset;               /**< Current offset in file */\n    size_t file_size;             /**< Size of file */\n    char *file_data;              /**< Pointer to file data */\n    TAILQ_HEAD(, cli_node) items; /**< List of nodes for directory */\n} __rte_cache_aligned;            /**< Structure for each node type */\n\n#define MAX_CMD_FILES 16\n\ntypedef struct {\n    char *filename[MAX_CMD_FILES];\n    uint32_t idx;\n} cli_files_t;\n\nstruct cli {\n    TAILQ_HEAD(, cli_node) root;      /**< head of node entries or root */\n    CIRCLEQ_HEAD(, cli_hist) hd_hist; /**< History circular queue */\n\n    uint32_t flags;              /**< Flags about CLI */\n    uint32_t nb_nodes;           /**< total number of nodes */\n    volatile uint16_t quit_flag; /**< When set to non-zero quit */\n\n    uint16_t plen; /**< Length of current prompt */\n\n    uint32_t nb_hist;                    /**< total number of history lines */\n    cli_files_t cmd_files;               /**< array of command filename pointers  */\n    struct cli_hist *curr_hist;          /**< Current history */\n    struct cli_node *bins[CLI_MAX_BINS]; /**< Arrays of bin directories,\n                                    first is the current working directory */\n    struct cli_node *exe_node;           /**< Node currently being executed */\n\n    struct cli_env *env;  /**< Environment variables */\n    struct gapbuf *gb;    /**< Gap buffer information */\n    struct cli_vt100 *vt; /**< vt100 information */\n    char **argv;          /**< array of argument string pointers */\n\n    cli_prompt_t prompt; /**< Prompt function pointer */\n    char *scratch;       /**< Place to build the path string */\n    char *kill;          /**< strdup() string of last kill data */\n\n    struct cli_node *node_mem; /**< Base address of node memory */\n    struct cli_hist *hist_mem; /**< Base address of history memory */\n\n    TAILQ_HEAD(, cli_node_chunk) node_chunks; /**< Extra node chunks when unlimited */\n\n    /* command -> cli_map table (growable) */\n    cli_cmd_map_t *cmd_maps;\n    uint32_t nb_cmd_maps;\n    uint32_t cmd_maps_cap;\n\n    uint64_t ac_last_tsc;  /**< Auto-complete last tab timestamp (cycles) */\n    uint32_t ac_last_hash; /**< Auto-complete last tab line hash */\n\n    TAILQ_HEAD(, help_node) help_nodes; /**< head of help */\n    TAILQ_HEAD(, cli_node) free_nodes;  /**< Free list of nodes */\n    CIRCLEQ_HEAD(, cli_hist) free_hist; /**< free list of history nodes */\n    void *user_state;                   /**< Pointer to some state variable */\n} __rte_cache_aligned;\n\n/**\n * Register a cli_map table for a command name.\n *\n * This is used by auto-complete and other helpers that want to understand\n * which tokens are fixed subcommands vs user-supplied values.\n *\n * @param cmd\n *   Command name (argv[0])\n * @param map\n *   Pointer to a cli_map array (terminated by {-1, NULL}). If NULL, unregister.\n * @return\n *   0 on success, -1 on error\n */\nint cli_register_cmd_map(const char *cmd, struct cli_map *map);\n\n/**\n * Register all command names referenced by a cli_map table.\n *\n * This scans each map entry's first token (command word) and registers that\n * command with the provided map table. If the first token is a choice token\n * (e.g. \"%|seq|sequence\"), each choice is registered.\n *\n * @param maps\n *   Pointer to a cli_map array (terminated by {-1, NULL}).\n * @return\n *   0 on success, -1 on error\n */\nint cli_register_cmd_maps(struct cli_map *maps);\n\n/**\n * Lookup a registered cli_map table by command name.\n *\n * @param cmd\n *   Command name (argv[0])\n * @return\n *   Pointer to cli_map table or NULL\n */\nstruct cli_map *cli_get_cmd_map(const char *cmd);\n\nRTE_DECLARE_PER_LCORE(struct cli *, cli);\n#define this_cli RTE_PER_LCORE(cli)\n\n/* cli.flags */\n#define CLEAR_TO_EOL    (1 << 0)\n#define DISPLAY_PROMPT  (1 << 1)\n#define PROMPT_CONTINUE (1 << 2)\n#define DELETE_CHAR     (1 << 3)\n#define CLEAR_LINE      (1 << 4)\n\n#define CLI_NODES_UNLIMITED (1 << 9) /**< Allocate nodes with no limit */\n#define CLI_YIELD_IO        (1 << 10)\n#define CLI_DEFAULT_TREE    (1 << 11)\n\n/**\n * Set one or more CLI flags.\n *\n * @param x\n *   Bitmask of flags to set in this_cli->flags.\n */\nstatic inline void\ncli_set_flag(uint32_t x)\n{\n    this_cli->flags |= x;\n}\n\n/**\n * Clear one or more CLI flags.\n *\n * @param x\n *   Bitmask of flags to clear in this_cli->flags.\n */\nstatic inline void\ncli_clr_flag(uint32_t x)\n{\n    this_cli->flags &= ~x;\n}\n\n/**\n * Test whether any of the given CLI flags are set.\n *\n * @param x\n *   Bitmask of flags to test.\n * @return\n *   Non-zero if any flag in @p x is set, zero otherwise.\n */\nstatic inline int\ncli_tst_flag(uint32_t x)\n{\n    return this_cli->flags & x;\n}\n\ntypedef union {\n    cli_cfunc_t cfunc; /**< Function pointer for commands */\n    cli_ffunc_t ffunc; /**< Function pointer for files */\n    cli_sfunc_t sfunc; /**< Function pointer for strings */\n} cli_funcs_t;         /* Internal: Used in argument list for adding nodes */\n\nstruct cli_dir {\n    const char *name; /**< directory name */\n    uint8_t bin;\n};\n\nstruct cli_cmd {\n    const char *name;       /**< Name of command */\n    cli_cfunc_t cfunc;      /**< Function pointer */\n    const char *short_desc; /**< Short description */\n}; /**< List of commands for cli_add_cmds() */\n\nstruct cli_alias {\n    const char *name;       /**< Name of command */\n    const char *alias_atr;  /**< Alias string */\n    const char *short_desc; /**< Short description */\n}; /**< List of alias for cli_add_aliases() */\n\nstruct cli_file {\n    const char *name;       /**< Name of command */\n    cli_ffunc_t ffunc;      /**< Read/Write function pointer */\n    const char *short_desc; /**< Short description */\n}; /**< List of alias for cli_add_aliases() */\n\nstruct cli_str {\n    const char *name;   /**< Name of command */\n    cli_sfunc_t sfunc;  /**< Function pointer */\n    const char *string; /**< Default string */\n}; /**< List of commands for cli_add_str() */\n\nstruct cli_tree {\n    node_type_t type; /**< type of node to create */\n    union {\n        struct cli_dir dir;     /**< directory and bin directory */\n        struct cli_cmd cmd;     /**< command nodes */\n        struct cli_file file;   /**< file nodes */\n        struct cli_alias alias; /**< alias nodes */\n        struct cli_str str;     /**< string node */\n    };\n};\n\n/**< Used to help create a directory tree */\n#define c_dir(n) {CLI_DIR_NODE, .dir = {(n), 0}}\n#define c_bin(n) {CLI_DIR_NODE, .dir = {(n), 1}}\n#define c_cmd(n, f, h)                        \\\n    {                                         \\\n        CLI_CMD_NODE, .cmd = {(n), (f), (h) } \\\n    }\n#define c_file(n, rw, h)                         \\\n    {                                            \\\n        CLI_FILE_NODE, .file = {(n), (rw), (h) } \\\n    }\n#define c_alias(n, l, h)                          \\\n    {                                             \\\n        CLI_ALIAS_NODE, .alias = {(n), (l), (h) } \\\n    }\n#define c_str(n, f, s)                        \\\n    {                                         \\\n        CLI_STR_NODE, .str = {(n), (f), (s) } \\\n    }\n#define c_end() {CLI_UNK_NODE, .dir = {NULL, 0}}\n\n/**\n * Store an arbitrary user-state pointer in the CLI instance.\n *\n * @note Uses thread variable this_cli.\n *\n * @param val\n *   Pointer to store as the user state value.\n */\nstatic inline void\ncli_set_user_state(void *val)\n{\n    this_cli->user_state = val;\n}\n\n/**\n * CLI root directory node.\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   Pointer to current working directory.\n */\nstatic inline struct cli_node *\nget_root(void)\n{\n    RTE_ASSERT(this_cli != NULL);\n    return this_cli->root.tqh_first;\n}\n\n/**\n * CLI current working directory.\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   Pointer to current working directory.\n */\nstatic inline struct cli_node *\nget_cwd(void)\n{\n    RTE_ASSERT(this_cli != NULL);\n    return this_cli->bins[0];\n}\n\n/**\n * set CLI current working directory.\n *\n * @note Uses thread variable this_cli.\n *\n * @param node\n *   Pointer to set as the current working directory\n * @return\n *   None\n */\nstatic inline void\nset_cwd(struct cli_node *node)\n{\n    RTE_ASSERT(this_cli != NULL);\n    this_cli->bins[0] = node;\n}\n\n/**\n * Check if this_cli pointer is valid\n *\n * @return\n *    1 if true else 0\n */\nstatic inline int\nis_cli_valid(void)\n{\n    return (this_cli) ? 1 : 0;\n}\n\n/**\n * Helper routine to compare two strings exactly\n *\n * @param s1\n *   Pointer to first string.\n * @param s2\n *   Pointer to second string.\n * @return\n *   0 failed to compare and 1 is equal.\n */\nstatic inline int\nis_match(const char *s1, const char *s2)\n{\n    if (!s1 || !s2)\n        return 0;\n\n    while ((*s1 != '\\0') && (*s2 != '\\0')) {\n        if (*s1++ != *s2++)\n            return 0;\n    }\n    if (*s1 != *s2)\n        return 0;\n\n    return 1;\n}\n\n/**\n * Test if the node is of a given type(s)\n *\n * @param node\n *   Pointer the cli_node structure\n * @return\n *   True if node is one of the types given\n */\nstatic inline int\nis_node(struct cli_node *node, uint32_t types)\n{\n    return node->type & types;\n}\n\n/**\n * Test if the node is a command\n *\n * @param node\n *   Pointer the cli_node structure\n * @return\n *   True if command else false if not\n */\nstatic inline int\nis_command(struct cli_node *node)\n{\n    return is_node(node, CLI_CMD_NODE);\n}\n\n/**\n * Test if the node is alias\n *\n * @param node\n *   Pointer the cli_node structure\n * @return\n *   True if alias else false if not\n */\nstatic inline int\nis_alias(struct cli_node *node)\n{\n    return is_node(node, CLI_ALIAS_NODE);\n}\n\n/**\n * Test if the node is a file\n *\n * @param node\n *   Pointer the cli_node structure\n * @return\n *   True if a file else false if not\n */\nstatic inline int\nis_file(struct cli_node *node)\n{\n    return is_node(node, CLI_FILE_NODE);\n}\n\n/**\n * Test if the node is directory\n *\n * @param node\n *   Pointer the cli_node structure\n * @return\n *   True if directory else false if not\n */\nstatic inline int\nis_directory(struct cli_node *node)\n{\n    return is_node(node, CLI_DIR_NODE);\n}\n\n/**\n * Test if the node is executable\n *\n * @param node\n *   Pointer the cli_node structure\n * @return\n *   True if executable else false if not\n */\nstatic inline int\nis_executable(struct cli_node *node)\n{\n    return is_command(node) || is_alias(node);\n}\n\n/**\n * Print out the short description for commands.\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   -1 just to remove code having to return error anyway.\n */\nstatic inline int\ncli_usage(void)\n{\n    if (this_cli && this_cli->exe_node) {\n        const char *p = this_cli->exe_node->short_desc;\n\n        cli_printf(\"  Usage: %s\\n\", (p) ? p : \"No description found\");\n    }\n    return -1;\n}\n\n/**\n * Return the string for the given node type\n *\n * @param node\n *   struct cli_node pointer\n * @return\n *   String for the node type.\n */\nstatic inline const char *\ncli_node_type(struct cli_node *node)\n{\n    const char *node_str[] = CLI_NODE_TYPES;\n    switch (node->type) {\n    case CLI_UNK_NODE:\n    default:\n        break;\n    case CLI_DIR_NODE:\n        return node_str[1];\n    case CLI_CMD_NODE:\n        return node_str[2];\n    case CLI_FILE_NODE:\n        return node_str[3];\n    case CLI_ALIAS_NODE:\n        return node_str[4];\n    }\n    return node_str[0];\n}\n\n/**\n * Create the current working directory string, which is the complete\n * path to node. Uses CLI routines to output the string to the console.\n *\n * @note Uses thread variable this_cli.\n *\n * @param node\n *   Starting node or last file/dir to be printed\n * @param path\n *   Pointer to a path buffer string.\n * @return\n *   Return the pointer to the cli->scratch buffer or buf with path string.\n */\nstatic inline char *\ncli_path_string(struct cli_node *node, char *path)\n{\n    if (!path)\n        path = this_cli->scratch;\n\n    if (!node)\n        node = get_cwd();\n\n    if (node->parent) {\n        cli_path_string(node->parent, path);\n        strcat(path, node->name);\n        strcat(path, \"/\");\n    } else\n        strcpy(path, \"/\");\n\n    return path;\n}\n\n/**\n * Return the path string for the current working directory.\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   Pointer to the scratch buffer containing the CWD path string.\n */\nstatic inline char *\ncli_cwd_path(void)\n{\n    return cli_path_string(get_cwd(), NULL);\n}\n\n/**\n * Print the current working directory string, which is the complete\n * path to node. Uses CLI routines to output the string to the console.\n *\n * @note Uses thread variable this_cli.\n *\n * @param node\n *   Starting node or last file/dir to be printed\n * @return\n *   N/A.\n */\nstatic inline void\ncli_pwd(struct cli_node *node)\n{\n    cli_printf(\"%s\", cli_path_string(node, NULL));\n}\n\n/**\n * Set the number of lines in history\n *\n * @note Uses thread variable this_cli.\n *\n * @param nb_hist\n *   Number of lines in history if zero disable history.\n * @return\n *   zero on success or -1 on error\n */\nstatic inline int\ncli_set_history_size(uint32_t nb_hist)\n{\n    return cli_set_history(nb_hist);\n}\n\n/**\n * Get the total number of lines in history\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   total number of line for history\n */\nstatic inline uint32_t\ncli_get_history_size(void)\n{\n    return this_cli->nb_hist;\n}\n\n/**\n * List the history lines\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   N/A\n */\nstatic inline void\ncli_history_list(void)\n{\n    cli_history_dump();\n}\n\n/**\n * Return the CLI root node.\n *\n * @return\n *   Pointer to root node.\n */\nstatic inline struct cli_node *\ncli_root_node(void)\n{\n    return this_cli->root.tqh_first;\n}\n\n/**\n * Create the CLI engine\n *\n * @param nb_entries\n *   Total number of commands, files, aliases and directories. If 0 then use\n *   the default number of nodes. If -1 then unlimited number of nodes.\n * @param nb_hist\n *   The number of lines to keep in history. If zero then turn off history.\n *   If the value is CLI_DEFAULT_HISTORY use CLI_DEFAULT_HIST_LINES\n * @return\n *   0 on success or -1\n */\nint cli_init(int nb_entries, uint32_t nb_hist);\n\n/**\n * Create the CLI engine with defaults\n *\n * @return\n *   0 on success or -1\n */\nint cli_create_with_defaults(void);\n\n/**\n * Create the CLI engine using compiled-in defaults.\n *\n * @return\n *   0 on success or -1 on error\n */\nint cli_create(void);\n\n/**\n * Configure the CLI with a prompt function and optional default tree.\n *\n * @param prompt\n *   Function pointer for displaying the CLI prompt, or NULL for default.\n * @param default_func\n *   Function pointer to populate the initial command tree, or NULL.\n * @return\n *   0 on success or -1 on error\n */\nint cli_setup(cli_prompt_t prompt, cli_tree_t default_func);\n\n/**\n * Create the CLI engine using system defaults.\n *\n * @return\n *   0 on success or -1\n */\nint cli_setup_with_defaults(void);\n\n/**\n * Create the CLI engine using system defaults and supplied tree init function.\n *\n * @param tree\n *   The user supplied function to init the tree or can be NULL. If NULL then\n *   a default tree is initialized with default commands.\n * @return\n *   0 on success or -1\n */\nint cli_setup_with_tree(cli_tree_t tree);\n\n/**\n * Set the CLI prompt function pointer\n *\n * @param prompt\n *   Function pointer to display the prompt\n * @return\n *   Return the old prompt function pointer or NULL if one does not exist\n */\ncli_prompt_t cli_set_prompt(cli_prompt_t prompt);\n\n/**\n * Create the root directory\n *\n * @note Uses thread variable this_cli.\n *\n * @param dirname\n *   Name of root directory, if null uses '/'\n * @return\n *   NULL on error or the root node on success.\n */\nstruct cli_node *cli_create_root(const char *dirname);\n\n/**\n * Create the default directory tree\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   0 on success or non-zero on error\n */\nint cli_default_tree_init(void);\n\n/**\n * Destroy the CLI engine\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   N/A\n */\nvoid cli_destroy(void);\n\n/**\n * Start the CLI running\n *\n * @note Uses thread variable this_cli.\n *\n * @param msg\n *   User message to be displayed on startup\n * @return\n *   N/A\n */\nvoid cli_start(const char *msg);\n\n/**\n * Execute command line string in cli->input\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   zero on success or -1 on error\n */\nint cli_execute(void);\n\n/**\n * Add a bin directory to the bin list\n *\n * @note Uses thread variable this_cli.\n *\n * @param node\n *   Directory to add to bin list\n * @return\n *   0 is ok, -1 is full\n */\nint cli_add_bin(struct cli_node *node);\n\n/**\n * Remove a bin directory from the bin list\n *\n * @note Uses thread variable this_cli.\n *\n * @param node\n *   Directory to add to bin list\n * @return\n *   0 is ok, -1 is not found\n */\nint cli_del_bin(struct cli_node *node);\n\n/**\n * Add a bin directory to the bin list using path\n *\n * @note Uses thread variable this_cli.\n *\n * @param path\n *   path to bin directory to add, must exist first.\n * @return\n *   0 is ok, -1 is full\n */\nint cli_add_bin_path(const char *path);\n\n/**\n * Add a cli directory\n *\n * @note Uses thread variable this_cli.\n *\n * @param dirname\n *   String pointing to the directory name\n * @param parent\n *   Parent node of the new directory\n * @return\n *   pointer to directory entry or NULL on error\n */\nstruct cli_node *cli_add_dir(const char *dirname, struct cli_node *parent);\n\n/**\n * Add a command to a directory\n *\n * @note Uses thread variable this_cli.\n *\n * @param name\n *   Pointer to command name string\n * @param dir\n *   Directory node pointer\n * @param func\n *   Pointer to function to execute\n * @param short_desc\n *   Short string for help to display\n * @return\n *   NULL on error or the node address for the command\n */\nstruct cli_node *cli_add_cmd(const char *name, struct cli_node *dir, cli_cfunc_t func,\n                             const char *short_desc);\n\n/**\n * Add an alias string or special command type\n *\n * @note Uses thread variable this_cli.\n *\n * @param name\n *   Pointer to command name string\n * @param dir\n *   Directory node pointer\n * @param line\n *   Pointer to alias string\n * @param short_desc\n *   Short string for help to display\n * @return\n *   NULL on error or the node address for the command\n */\nstruct cli_node *cli_add_alias(const char *name, struct cli_node *dir, const char *line,\n                               const char *short_desc);\n\n/**\n * Add an file to a directory\n *\n * @note Uses thread variable this_cli.\n *\n * @param name\n *   Pointer to command name string\n * @param dir\n *   Directory node pointer\n * @param func\n *   Pointer to a function attached to the file.\n * @param short_desc\n *   Short string for help to display\n * @return\n *   NULL on error or the node pointer.\n */\nstruct cli_node *cli_add_file(const char *name, struct cli_node *dir, cli_ffunc_t func,\n                              const char *short_desc);\n\n/**\n * Add a string to the system.\n *\n * @note Uses thread variable this_cli.\n *\n * @param name\n *   Pointer to command name string\n * @param dir\n *   Directory node pointer\n * @param func\n *   Pointer to a function attached to the string.\n * @param str\n *   Value of string if no function defined.\n * @return\n *   NULL on error or the node pointer.\n */\nint cli_add_str(const char *name, cli_sfunc_t func, const char *str);\n\n/**\n * Add a list of nodes to a directory\n *\n * @note Uses thread variable this_cli.\n *\n * @param dir\n *   Node pointer to directory for add commands\n * @param treee\n *   Pointer to list of nodes to add to the tree\n * @return\n *   -1 on error or 0 for OK\n */\nint cli_add_tree(struct cli_node *dir, struct cli_tree *tree);\n\n/**\n * Add filenames to the CLI command list.\n *\n * @param filename\n *    Path of command file.\n * @return\n *    0 is OK and -1 if error\n */\nstatic inline int\ncli_add_cmdfile(const char *filename)\n{\n    if (this_cli->cmd_files.idx >= MAX_CMD_FILES)\n        return -1;\n\n    this_cli->cmd_files.filename[this_cli->cmd_files.idx++] = strdup(filename);\n\n    return 0;\n}\n\n/**\n * execute a command file\n *\n * @note Uses thread variable this_cli.\n *\n * @param path\n *   Pointer to path to file\n * @return\n *   0 on OK or -1 on error\n */\nint cli_execute_cmdfile(const char *path);\n\n/**\n * execute a list for command files\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   0 on OK or -1 on error\n */\nint cli_execute_cmdfiles(void);\n\n/**\n * Return the number of command files registered with cli_add_cmdfile().\n *\n * @return\n *   Count of pending command files.\n */\nint cli_num_cmdfiles(void);\n\n/**\n * Remove a node from the directory tree\n *\n * @note Uses thread variable this_cli.\n *\n * @param node\n *   The pointer to the node to remove\n * @return\n *   0 on OK and -1 on error\n */\nint cli_remove_node(struct cli_node *node);\n\n/**\n * return true if allocating unlimited nodes are enabled.\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   non-zero if true else 0\n */\nint cli_nodes_unlimited(void);\n\n/**\n * shutdown the CLI command interface.\n *\n */\nstatic inline void\ncli_quit(void)\n{\n    this_cli->quit_flag = 1;\n}\n\n/**\n * Register a Lua evaluation callback used by the CLI Lua command.\n *\n * @param func\n *   Function pointer with signature int(void *lua_state, const char *script).\n *   Pass NULL to unregister.\n */\nvoid cli_set_lua_callback(int (*func)(void *, const char *));\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CLI_H_ */\n"
  },
  {
    "path": "lib/cli/cli.rst",
    "content": "..  BSD LICENSE\n   Copyright(c) <2016-2026>, Intel Corporation. All rights reserved.\n\n   Redistribution and use in source and binary forms, with or without\n   modification, are permitted provided that the following conditions\n   are met:\n\n   * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in\n   the documentation and/or other materials provided with the\n   distribution.\n   * Neither the name of Intel Corporation nor the names of its\n   contributors may be used to endorse or promote products derived\n   from this software without specific prior written permission.\n\n   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nCLI Sample Application\n===============================\n\nCLI stands for \"Command Line Interface\".\n\nThis chapter describes the CLI sample application that is part of the\nData Plane Development Kit (DPDK). The CLI is a workalike replacement for\ncmdline library in DPDK and has a simpler programming interface and programming\nmodel.\n\nThe primary goal of CLI is to allow the developer to create commands quickly\nand with very little compile or runtime configuration. Using standard Unix*\nlike constructs which are very familar to the developer. Allowing the developer\nto construct a set of commands for development or deployment of the application.\n\nThe CLI design uses a directory like design instead of a single level command\nline interface. Allowing the developer to use a directory style solution to\ncontrolling a DPDK application. The directory style design is nothing new, but\nit does have some advantages.\n\nOne advantage allows the directory path for the command to be part of the\ninformation used in executing the command. The next advantage is creating\ndirectories to make a hierarchy of commands, plus allowing whole directroy\ntrees to dynamically come and go as required by the developer.\n\nSome of the advantages are:\n\n * CLI has no global variable other then the single thread variable called *this_cli* which can only be accessed from the thread which created the CLI instance.\n * CLI supports commands, files, aliases, directories.\n    - The alias command is just a string using a simple substitution support for other commands similar to the bash shell like alias commands.\n    - Files can be static or dynamic information, can be changed on the fly and saved for later. The file is backed with a simple function callback to allow the developer to update the content or not.\n * Added support for color and cursor movement APIs similar to Pktgen if needed by the developer.\n * It is a work alike replacement for cmdline library. Both cmdline and CLI can be used in the same application if care is taken.\n * Uses a simple fake like directory layout for command and files. Allowing for command hierarchy as path to the command can allow for specific targets to be identified without having to state it on the command line.\n * Has auto-complete for commands, similar to Unix/Linux autocomplete and provides support for command option help as well.\n * Callback functions for commands are simply just argc/argv like functions.\n    - The CLI does not convert arguments for the user, it is up to the developer to decode the argv[] values.\n    - Most of the arguments converted in the current cmdline are difficult to use or not required as the developer just picks string type and does the conversion himself.\n * Dynamically be able to add and remove commands, directories, files and aliases, does not need to be statically compiled into the application.\n * No weird structures in the code and reduces the line count for testpmd from 12K to 4.5K lines. I convert testpmd to have both CMDLINE and CLI with a command line option.\n * Two methods to parse command lines, first is the standard argc/argv method in the function.\n    - The second method is to use a map of strings with simple printf like formatting to detect which command line the user typed.\n    - An ID value it returned to the used to indicate which mapping string was found to make the command line to be used in a switch statement.\n * Environment variable support using the **env** command or using an API.\n * Central help support if needed (optional).\n\nOverview\n--------\n\nThe CLI sample application is a simple application that demonstrates the\nuse of the command line interface in the DPDK. This application is a\nreadline-like interface that can be used to control a DPDK application.\n\nOne of the advantages of CLI over Cmdline is it is dynamic, which means\nnodes or items can be added and removed on the fly. Which allows adding\nnew directories, file or commands as needed or removing these items at runtime.\nThe CLI has no global modifiable variable as the one global pointer is a\nthread based variable. Which allows the developer to have multiple CLI\ncommands per thread if needed.\n\nAnother advantage is the calling of the backend function to support a\ncommand is very familar to developers as it is basically just a argc/argv\nstyle command and the developer gets the complete command line.\n\nOne other advantage is the use of MAP structures, to help identify commands\nquickly plus allowing the developer to define new versions of commands and\nbe able to identify these new versions using a simple identifier value. Look at\nthe sample application to see a simple usage.\n\nAnother advantage of CLI is how simple it is to add new directroies, files and\ncommands for user development. The basic concept is for the developer to use\nstandard Unix like designs. To add a command a developer needs to add an entry\nto the cli_tree_t structure and create a function using the following prototype:\n\n.. code-block:: c\n\n    int user_cmd(int argc, char **argv);\n\nThe argc/argv is exactly like the standard usage in a Unix* system, which allows\nfor using getopt() and other standard functions. The Cmdline structures and\ntext conversions were defined at compile time in most cases. In CLI the routine\nis passed the argc/argv information to convert these options as needed. The cli\nvariable being a thread Local Storage (TLS) all user routines a CLI routine only\nneed to access the thread variable to eliminate needing a global variable to\nreference the specific CLI instance and passing the value in the API.\n\nThe user can also set environment variables using the **env** command. These\nvariables are also parsed in the command line a direct substitution is done.\n\nThe CLI system also has support for simple files along with alias like commands.\nThese alias commands are fixed strings which are executed instead of a function\nprovided by the developer. If the user has more arguments these are appended\nto the alias string and processed as if typed on the command line.\n\n.. note::\n\n   The CLI library was designed to be used in production code and the Cmdline\n   was not validated to the same standard as other DPDK libraries. The goal\n   is to provide a production CLI design.\n\nThe CLI sample application supports some of the features of the Cmdline\nlibrary such as, completion, cut/paste and some other special bindings that\nmake configuration and debug faster and easier.\n\nThe CLI design uses some very simple VT100 control strings for displaying data\nand accepting input from the user. Some of the control strings are used to\nclear the screen or line and position the cursor on a VT100 compatible terminal.\nThe CLI screen code also supports basic color and many other VT100 commands.\n\nThe application also shows how the CLI application can be extended to handle\na list of commands and user input.\n\nThe example presents a simple command prompt **DPDK-cli:/>** similar to a Unix*\nshell command along with a directory like file system.\n\nSome of the **default** commands contained under /sbin directory are:\n\n * **ls**: list the current or provided directory files/commands.\n * **cd**: Change directory command.\n * **pwd**: print out the current working directory.\n * **history**: List the current command line history if enabled.\n * **more**: A simple command to page contents of files.\n * **help**: display a the help screen.\n * **quit**: exit the CLI application, also **Ctrl-x** will exit as well.\n * **mkdir**: add a directory to the current directory.\n * **delay**: wait for a given number of microseconds.\n * **sleep**: wait for a given number of seconds.\n * **rm**: remove a directory, file or command. Removing a file will delete the data.\n * **cls**: clear the screen and redisplay the prompt.\n * **version**: Display the current DPDK version being used.\n * **path**: display the current search path for executable commands.\n * **cmap**: Display the current system core and socket information.\n * **hugepages**: Display the current hugepage information.\n * **sizes**: a collection system structure and buffer sizes for debugging.\n * **copyright**: a file containing DPDK copyright information.\n * **env**: a command show/set/modify the environment variables.\n\nSome example commands under /bin directory are:\n\n * **ll**: an alias command to display long ls listing **ls -l**\n * **h**: alias command for **history**\n * **hello**: a simple Hello World! command.\n * **show**: has a number of commands using the map feature.\n\nUnder the /data directory is:\n\n * **pci**: a simple example file for displaying the **lspci** command in CLI.\n\n.. note::\n\n   To terminate the application, use **Ctrl-x** or the command **quit**.\n\nAuto completion\n---------------\n\nCLI does support auto completion at the file or directory level, meaning the\narguments to commands are not expanded as was done in Cmdline code. The CLI\nauto completion works similar to the standard Unix* system by expanding\ncommands and directory paths. In normal Unix* like commands the user needs to\nexecute the command asking for the help information and CLI uses this method.\n\nSpecial command features\n------------------------\n\nUsing the '!' followed by a number from the history list of commands you can\nexecute that command again. Using the UP/Down arrows the user can quickly find\nand execute or modify a previous command in history.\n\nThe user can also execute host level commands if enabled using the '@' prefix\nto a command line e.g. @ls or @lspci or ... line is passed to popen or system\nfunction to be executed and the output displayed on the console if any output.\n\nCompiling the Application\n-------------------------\n\n#.  Go to example directory:\n\n.. code-block:: console\n\n       export RTE_SDK=/path/to/rte_sdk\n       cd ${RTE_SDK}/examples/cli\n\n#.  Set the target (a default target is used if not specified). For example:\n\n.. code-block:: console\n\n       export RTE_TARGET=x86_64-native-linux-gcc\n\t   or\n       export RTE_TARGET=x86_64-native-linuxapp-gcc\n\n   Refer to the *DPDK Getting Started Guide* for possible RTE_TARGET values.\n\n#.  Build the application:\n\n.. code-block:: console\n\n       make\n\nRunning the Application\n-----------------------\n\nTo run the application in linux environment, issue the following command:\n\n.. code-block:: console\n\n   $ ./build/cli\n\n.. note::\n   The example cli application does not require to be run as superuser\n   as it does not startup DPDK by calling rte_eal_init() routine. Which means\n   it also does not use DPDK features except for a few routines not requiring\n   EAL initialization.\n\nRefer to the *DPDK Getting Started Guide* for general information on running applications\nand the Environment Abstraction Layer (EAL) options.\n\nExplanation\n-----------\n\nThe following sections provide some explanation of the code.\n\nEAL Initialization and cmdline Start\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe first task is the initialization of the Environment Abstraction Layer (EAL),\nif required for the application.\n\n.. code-block:: c\n\n   int\n   main(int argc, char **argv)\n   {\n       if (cli_create_with_tree(init_tree) ==0) {\n           cli_start(NULL, 0); /* NULL is some init message done only once */\n                               /* 0 means do not use color themes */\n           cli_destroy();\n       }\n       ...\n\nThe cli_start() function returns when the user types **Ctrl-x** or uses the\nquit command in this case, the application exits. The cli_create() call takes\nfour arguments and each has a default value if not provided. The API used here\nis the cli_create_with_tree(), which uses defaults for three of the arguments.\n\n.. code-block:: c\n\n   /**\n   * Create the CLI engine\n   *\n   * @param prompt_func\n   *   Function pointer to call for displaying the prompt.\n   * @param tree_func\n   *   The user supplied function to init the tree or can be NULL. If NULL then\n   *   a default tree is initialized with basic commands.\n   * @param nb_entries\n   *   Total number of commands, files, aliases and directories. If 0 then use\n   *   the default number of nodes. If -1 then unlimited number of nodes.\n   * @param nb_hist\n   *   The number of lines to keep in history. If zero then turn off history.\n   *   If the value is CLI_DEFAULT_HISTORY use CLI_DEFAULT_HIST_LINES\n   * @return\n   *   0 on success or -1\n   */\n   int cli_create(cli_prompt_t prompt_func, cli_tree_t tree_func,\n                       int nb_entries, uint32_t nb_hist);\n\nThe cli_create_with_tree() has only one argument which is the structure to use\nin order to setup the initial directory structure. Also the wrapper function\nint cli_create_with_defaults(void) can be used as well.\n\nConsult the cli.h header file for the default values. Also the alias node is a\nspecial alias file to allow for aliasing a command to another command.\n\nThe tree init routine is defined like:\n\n.. code-block:: c\n\n\tstatic struct cli_tree my_tree[] = {\n\t    c_dir(\"/data\"),\n\t    c_file(\"pci\", pci_file, \"display lspci information\"),\n\t    c_dir(\"/bin\"),\n\t    c_cmd(\"hello\", hello_cmd, \"Hello-World!!\"),\n\t    c_alias(\"h\", \"history\", \"display history commands\"),\n\t    c_alias(\"ll\", \"ls -l\", \"long directory listing alias\"),\n\t    c_end()\n\t};\n\n\tstatic int\n\tinit_tree(void)\n\t{\n\t    /*\n\t     * Root is created already and using system default cmds and dirs, the\n\t     * developer is not required to use the system default cmds/dirs.\n\t     */\n\t    if (cli_default_tree_init())\n\t        return -1;\n\n\t\t/* Using NULL here to start at root directory */\n\t    if (cli_add_tree(NULL, my_tree))\n\t        return -1;\n\n\t\tcli_help_add(\"Show\", show_map, show_help);\n\n\t\treturn cli_add_bin_path(\"/bin\");\n\t}\n\n\nThe above structure is used to create the tree structure at initialization\ntime. The struct cli_tree or cli_tree_t typedef can be used to setup a new\ndirectory tree or argument the default tree.\n\nThe elements are using a set of macros c_dir, c_file, c_cmd, c_alias and c_end.\nThese macros help fill out the cli_tree_t structure for the given type of item.\n\nThe developer can create his own tree structure with any commands that are\nneeded and/or call the cli_default_tree_init() routine to get the default\nstructure of commands. If the developer does not wish to call the default\nCLI routine, then he must call the cli_create_root() function first before\nadding other nodes. Other nodes can be added and removed at anytime.\n\nCLI Map command support\n~~~~~~~~~~~~~~~~~~~~~~~\n\nThe CLI command has two types of support to handle arguments normal argc/argv\nand the map system. As shown above the developer creates a directory tree and\nattaches a function to a command. The function takes the CLI pointer plus the\nargc/argv arguments and the developer can just parse the arguments to decode\nthe command arguments. Sometimes you have multiple commands or different versions\nof a command being handled by a single routine, this is were the map support\ncomes into play.\n\nThe map support defines a set of struct cli_map map[]; to help detect the\ncorrect command from the user. In the list of cli_map structures a single\nstructure contains two items a developer defined index value and a command\nstrings. The index value is used on the function to identify the specific type\nof command found in the list. The string is a special printf like string to\nhelp identify the command typed by the user. One of the first things todo in\nthe command routine is to call the cli_mapping() function passing in the CLI\npointer and the argc/argv values.The two method can be used at the same time.\n\nThe cli_mapping() command matches up the special format string with the values\nin the argc/argv array and returns the developer supplied index value or really\nthe pointer the struct cli_map instance.\n\nNow the developer can use the cli_map.index value in a switch() statement to\nlocate the command the user typed or if not found a return of -1.\n\nExample:\n\n.. code-block:: c\n\n\tstatic int\n\thello_cmd(int argc, char **argv)\n\t{\n\t    int i, opt;\n\n\t    optind = 1;\n\t    while((opt = getopt(argc, argv, \"?\")) != -1) {\n\t        switch(opt) {\n\t            case '?': cli_usage(); return 0;\n\t            default:\n\t                break;\n\t        }\n\t    }\n\n\t    cli_printf(\"Hello command said: Hello World!! \");\n\t    for(i = 1; i < argc; i++)\n\t        cli_printf(\"%s \", argv[i]);\n\t    cli_printf(\"\\n\");\n\n\t    return 0;\n\t}\n\n\tstatic int\n\tpci_file(struct cli_node *node, char *buff, int len, uint32_t opt)\n\t{\n\t\tif (is_file_open(opt)) {\n\t\t\tFILE *f;\n\n\t\t\tif (node->file_data && (node->fflags & CLI_FREE_DATA))\n\t\t\t\tfree(node->file_data);\n\n\t\t\tnode->file_data = calloc(1, 32 * 1024);\n\t\t\tif (!node->file_data)\n\t\t\t\treturn -1;\n\t\t\tnode->file_size = 32 * 1024;\n\t\t\tnode->fflags = CLI_DATA_RDONLY | CLI_FREE_DATA;\n\n\t\t\tf = popen(\"lspci\", \"r\");\n\t\t\tif (!f)\n\t\t\t\treturn -1;\n\n\t\t\tnode->file_size = fread(node->file_data, 1, node->file_size, f);\n\n\t\t\tpclose(f);\n\t        return 0;\n\t    }\n\t    return cli_file_handler(node, buff, len, opt);\n\t}\n\n\tstatic struct cli_map show_map[] = {\n\t\t{ 10, \"show %P\" },\n\t\t{ 20, \"show %P mac %m\" },\n\t\t{ 30, \"show %P vlan %d mac %m\" },\n\t\t{ 40, \"show %P %|vlan|mac\" },\n\t\t{ -1, NULL }\n\t};\n\n\tstatic const char *show_help[] = {\n\t\t\"show <portlist>\",\n\t\t\"show <portlist> mac <rte_ether_addr>\",\n\t\t\"show <portlist> vlan <vlanid> mac <rte_ether_addr>\",\n\t\t\"show <portlist> [vlan|mac]\",\n\t\tNULL\n\t};\n\n\tstatic int\n\tshow_cmd(int argc, char **argv)\n\t{\n\t\tstruct cli_map *m;\n\t\tuint32_t portlist;\n\t\tstruct rte_ether_addr mac;\n\n\t\tm = cli_mapping(Show_info.map, argc, argv);\n\t\tif (!m)\n\t\t\treturn -1;\n\n\t\tswitch(m->index) {\n\t\t\tcase 10:\n\t\t\t\tportlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\t\t\t\tcli_printf(\"   Show Portlist: %08x\\n\", portlist);\n\t\t\t\tbreak;\n\t\t\tcase 20:\n\t\t\t\tportlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\t\t\t\tpg_ether_aton(argv[3], &mac);\n\t\t\t\tcli_printf(\"   Show Portlist: %08x, MAC: \"\n\t\t\t\t\t\t\"%02x:%02x:%02x:%02x:%02x:%02x\\n\",\n\t\t\t\t\t\t   portlist,\n\t\t\t\t\t\t   mac.addr_bytes[0],\n\t\t\t\t\t\t   mac.addr_bytes[1],\n\t\t\t\t\t\t   mac.addr_bytes[2],\n\t\t\t\t\t\t   mac.addr_bytes[3],\n\t\t\t\t\t\t   mac.addr_bytes[4],\n\t\t\t\t\t\t   mac.addr_bytes[5]);\n\t\t\t\tbreak;\n\t\t\tcase 30:\n\t\t\t\tportlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\t\t\t\tpg_ether_aton(argv[5], &mac);\n\t\t\t\tcli_printf(\"   Show Portlist: %08x vlan %d MAC: \"\n\t\t\t\t\t\t\"%02x:%02x:%02x:%02x:%02x:%02x\\n\",\n\t\t\t\t\t\t   portlist,\n\t\t\t\t\t\t   atoi(argv[3]),\n\t\t\t\t\t\t   mac.addr_bytes[0],\n\t\t\t\t\t\t   mac.addr_bytes[1],\n\t\t\t\t\t\t   mac.addr_bytes[2],\n\t\t\t\t\t\t   mac.addr_bytes[3],\n\t\t\t\t\t\t   mac.addr_bytes[4],\n\t\t\t\t\t\t   mac.addr_bytes[5]);\n\t\t\t\tbreak;\n\t\t\tcase 40:\n\t\t\t\tportlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\t\t\t\tpg_ether_aton(\"1234:4567:8901\", &mac);\n\t\t\t\tcli_printf(\"   Show Portlist: %08x %s: \",\n\t\t\t\t\t\t   portlist, argv[2]);\n\t\t\t\tif (argv[2][0] == 'm')\n\t\t\t\t\tcli_printf(\"%02x:%02x:%02x:%02x:%02x:%02x\\n\",\n\t\t\t\t\t\t   mac.addr_bytes[0],\n\t\t\t\t\t\t   mac.addr_bytes[1],\n\t\t\t\t\t\t   mac.addr_bytes[2],\n\t\t\t\t\t\t   mac.addr_bytes[3],\n\t\t\t\t\t\t   mac.addr_bytes[4],\n\t\t\t\t\t\t   mac.addr_bytes[5]);\n\t\t\t\telse\n\t\t\t\t\tcli_printf(\"%d\\n\", 101);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcli_help_show_group(\"Show\");\n\t\t\t\treturn -1;\n\t\t}\n\t\treturn 0;\n\t}\n\n\tstatic struct cli_tree my_tree[] = {\n\t\tc_dir(\"/data\"),\n\t\tc_file(\"pci\",\tpci_file, \t\"display lspci information\"),\n\t\tc_dir(\"/bin\"),\n\t\tc_cmd(\"show\",\tshow_cmd, \t\"show mapping options\"),\n\t\tc_cmd(\"hello\",\thello_cmd, \t\"Hello-World!!\"),\n\t\tc_alias(\"h\", \t\"history\", \t\"display history commands\"),\n\t\tc_alias(\"ll\", \t\"ls -l\", \t\"long directory listing alias\"),\n\t\tc_end()\n\t};\n\nHere is the cli_tree for this example, note it has a lot more commands. The show_cmd\nor show command is located a number of lines down. This cli_tree creates in the\n/bin directory a number of commands, which one is the show command. The\nshow command has four different formats if you look at the show_map[].\n\nThe user types one of these commands and cli_mapping() attempts to locate the\ncorrect entry in the list. You will also notice another structure called pcap_help,\nwhich is an array of strings giving a cleaner and longer help description of\neach of the commands.\n\nThese two structure show_map/show_help can be added to the cli_help system\nto provide help for a command using a simple API.\n\n.. code-block::c\n\n\tcli_help_add(\"Show\", show_map, show_help);\n\n\tcli_help_show_group(\"Show\");\n\nor we can use the cli_help_show_all() API to show all added help information.\n\n.. code-block:: c\n\n\tcli_help_show_all(NULL);\n\nThe following is from Pktgen source code to add more help to the global\nhelp for the system.\n\n.. code-block:: c\n\n\tcli_help_add(\"Title\", NULL, title_help);\n\tcli_help_add(\"Page\", page_map, page_help);\n\tcli_help_add(\"Enable\", enable_map, enable_help);\n\tcli_help_add(\"Set\", set_map, set_help);\n\tcli_help_add(\"Range\", range_map, range_help);\n\tcli_help_add(\"Sequence\", seq_map, seq_help);\n\tcli_help_add(\"PCAP\", pcap_map, pcap_help);\n\tcli_help_add(\"Start\", start_map, start_help);\n\tcli_help_add(\"Debug\", debug_map, debug_help);\n\tcli_help_add(\"Misc\", misc_map, misc_help);\n\tcli_help_add(\"Theme\", theme_map, theme_help);\n\tcli_help_add(\"Status\", NULL, status_help);\n\nUnderstanding the CLI system\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe command line interface is defined as a fake directory tree with executables,\ndirectories and files. The user uses shell like standard commands to move about\nthe directory and execute commands. The CLI is not a powerful as the\nBash shell, but has a number of similar concepts.\n\nOur fake directory tree has a '/' or root directory which is created when\ncli_create() is called along with the default sbin directory. The user starts out\nat the root directory '/' and is allowed to cd to other directories, which could\ncontain more executables, aliases or directories. The max number of directory\nlevels is limited to the number of nodes given at startup.\n\nThe default directory tree starts out as just root (/) and a sbin directory.\nAlso it contains a file called copyright in root, which can be displayed\nusing the default 'more copyright' command.\n\nA number of default commands are predefined in the /sbin directory and are\ndefined above. Other bin directories can be added to the system if needed,\nbut a limit of CLI_MAX_BINS is defined in the cli.h header file.\n\nThe CLI structure is created at run time adding directories, commands and\naliases as needed, which is different from the cmdline interface in DPDK today.\n\nThe basic concept for a command is similar to a standard Linux executable,\nmeaning the command when executed it is passed the command line in a argc/argv\nformat to be parsed by the function. The function is attached to a command file\nin the directory tree and is executed when the user types the name of the\nfunction along with it arguments. Some examples of the default commands can be\nseen in the lib/librte_cli/cli_cmds.c file.\n"
  },
  {
    "path": "lib/cli/cli_auto_complete.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n/**\n * @file\n * CLI tab completion implementation.\n *\n * Provides shell-like completion for commands, directories, and files.\n * When a command has a registered cli_map table, completion can be driven by\n * the map tokens to offer context-aware suggestions and placeholder hints.\n */\n\n#include <fnmatch.h>\n\n#include <rte_timer.h>\n\n#include <rte_string_fns.h>\n#include <pg_strings.h>\n\n#include \"cli.h\"\n#include \"cli_input.h\"\n#include \"cli_auto_complete.h\"\n\n/*\n * gb_get_prev() has side-effects (it can move gb->point across the gap).\n * Auto-complete should never mutate cursor state just to inspect a character.\n */\nstatic inline char\n_gb_peek_prev(const struct gapbuf *gb)\n{\n    const char *point;\n\n    if (!gb)\n        return '\\0';\n\n    point = gb->point;\n    if (point == gb->egap)\n        point = gb->gap;\n\n    if (point == gb->buf) {\n        if (point == gb->gap)\n            return '\\0';\n        return *point;\n    }\n\n    return *(point - 1);\n}\n\nstatic uint32_t\n_ac_hash_line(const char *s, int arg_index, int at_new_token)\n{\n    /* 32-bit FNV-1a */\n    uint32_t h = 2166136261u;\n\n    if (s) {\n        for (const unsigned char *p = (const unsigned char *)s; *p != '\\0'; p++) {\n            h ^= (uint32_t)(*p);\n            h *= 16777619u;\n        }\n    }\n\n    h ^= (uint32_t)arg_index;\n    h *= 16777619u;\n    h ^= (uint32_t)at_new_token;\n    h *= 16777619u;\n\n    return h;\n}\n\nstatic int\n_str_list_add_unique(char ***list, int *count, const char *s)\n{\n    if (!list || !count || !s || (*s == '\\0'))\n        return -1;\n\n    for (int i = 0; i < *count; i++) {\n        if (!strcmp((*list)[i], s))\n            return 0;\n    }\n\n    char **new_list = realloc(*list, (size_t)(*count + 1) * sizeof(char *));\n    if (!new_list)\n        return -1;\n    *list = new_list;\n\n    (*list)[*count] = strdup(s);\n    if (!(*list)[*count])\n        return -1;\n\n    (*count)++;\n    return 0;\n}\n\nstatic void\n_str_list_free(char **list, int count)\n{\n    if (!list)\n        return;\n    for (int i = 0; i < count; i++)\n        free(list[i]);\n    free(list);\n}\n\nstatic void\n_print_strings(char **items, int item_cnt, const char *match)\n{\n    uint32_t mlen = 8, csize, ccnt, cnt = 0;\n    uint32_t slen = (match) ? strlen(match) : 0;\n\n    if (!items || item_cnt <= 0)\n        return;\n\n    for (int i = 0; i < item_cnt; i++)\n        mlen = RTE_MAX(mlen, (uint32_t)strlen(items[i]));\n    mlen++;\n\n    csize = mlen;\n    ccnt  = CLI_SCREEN_WIDTH / mlen;\n    if (ccnt == 0)\n        ccnt = 1;\n\n    for (int i = 0; i < item_cnt; i++) {\n        if (slen && strncmp(items[i], match, slen))\n            continue;\n\n        if (!cnt)\n            cli_printf(\"\\n\");\n\n        cli_printf(\"%-*s\", csize, items[i]);\n        if ((++cnt % ccnt) == 0)\n            cli_printf(\"\\n\");\n    }\n    if (cnt % ccnt)\n        cli_printf(\"\\n\");\n}\n\nstatic int\n_is_placeholder(const char *tok)\n{\n    return tok && tok[0] == '%' && tok[1] != '\\0';\n}\n\nstatic int\n_is_choice_token(const char *tok)\n{\n    return tok && tok[0] == '%' && tok[1] == '|';\n}\n\nstatic int\n_choice_token_contains(const char *choice_tok, const char *word)\n{\n    char tmp[CLI_MAX_PATH_LENGTH + 1];\n    char *opts[CLI_MAX_ARGVS + 1];\n\n    if (!choice_tok || !word || !_is_choice_token(choice_tok))\n        return 0;\n\n    snprintf(tmp, sizeof(tmp), \"%s\", choice_tok + 2);\n    memset(opts, 0, sizeof(opts));\n    int n = pg_strtok(tmp, \"|\", opts, CLI_MAX_ARGVS);\n    for (int i = 0; i < n; i++) {\n        if (opts[i] && !strcmp(opts[i], word))\n            return 1;\n    }\n    return 0;\n}\n\nstatic int\n_tok_all_digits(const char *s)\n{\n    if (!s || *s == '\\0')\n        return 0;\n    if (*s == '-')\n        s++;\n    if (*s == '\\0')\n        return 0;\n    for (const unsigned char *p = (const unsigned char *)s; *p != '\\0'; p++) {\n        if (!isdigit(*p))\n            return 0;\n    }\n    return 1;\n}\n\nstatic int\n_tok_all_hex_with_seps(const char *s)\n{\n    if (!s || *s == '\\0')\n        return 0;\n    for (const unsigned char *p = (const unsigned char *)s; *p != '\\0'; p++) {\n        if (isxdigit(*p) || *p == ':' || *p == '-' || *p == '.')\n            continue;\n        return 0;\n    }\n    return 1;\n}\n\nstatic int\n_tok_looks_like_ipv4(const char *s)\n{\n    int dots = 0;\n    if (!s || *s == '\\0')\n        return 0;\n    for (const unsigned char *p = (const unsigned char *)s; *p != '\\0'; p++) {\n        if (*p == '.')\n            dots++;\n        else if (isdigit(*p) || *p == '/')\n            ;\n        else\n            return 0;\n    }\n    return (dots >= 1);\n}\n\nstatic int\n_tok_looks_like_ipv6(const char *s)\n{\n    int colons = 0;\n    if (!s || *s == '\\0')\n        return 0;\n    for (const unsigned char *p = (const unsigned char *)s; *p != '\\0'; p++) {\n        if (*p == ':')\n            colons++;\n        else if (isxdigit(*p) || *p == '/' || *p == '.')\n            ;\n        else\n            return 0;\n    }\n    return (colons >= 1);\n}\n\nstatic int\n_map_tok_compatible_with_user_tok(const char *map_tok, const char *user_tok)\n{\n    if (!map_tok || !user_tok)\n        return 0;\n\n    if (_is_choice_token(map_tok))\n        return _choice_token_contains(map_tok, user_tok);\n\n    if (!_is_placeholder(map_tok))\n        return !strcmp(map_tok, user_tok);\n\n    switch (map_tok[1]) {\n    case 'd':\n    case 'D':\n    case 'u':\n    case 'U':\n    case 'b':\n    case 'n':\n        return _tok_all_digits(user_tok);\n    case 'h':\n    case 'H':\n        /* Allow 0x prefix, otherwise require hex chars. */\n        if (!strncmp(user_tok, \"0x\", 2) || !strncmp(user_tok, \"0X\", 2))\n            return _tok_all_hex_with_seps(user_tok + 2);\n        return _tok_all_hex_with_seps(user_tok);\n    case 'm':\n        /* MAC-like: hex digits with common separators (rejects keywords like 'tcp'). */\n        return _tok_all_hex_with_seps(user_tok);\n    case '4':\n        return _tok_looks_like_ipv4(user_tok);\n    case '6':\n        return _tok_looks_like_ipv6(user_tok);\n    case 'P':\n    case 'C':\n        /* Accept 'all' or a digit-based list like 0,1-3 */\n        if (!strcmp(user_tok, \"all\"))\n            return 1;\n        for (const unsigned char *p = (const unsigned char *)user_tok; *p != '\\0'; p++) {\n            if (isdigit(*p) || *p == ',' || *p == '-')\n                continue;\n            return 0;\n        }\n        return 1;\n    case 's':\n    case 'c':\n    case 'k':\n    case 'l':\n        return 1;\n    default:\n        return 1;\n    }\n}\n\nstatic const char *\n_placeholder_hint(const char *tok)\n{\n    if (!tok || tok[0] != '%' || tok[1] == '\\0')\n        return NULL;\n\n    /*\n     * Provide human-readable hints for placeholders.\n     * These are displayed only when the completion prefix is empty, and are\n     * printed (not inserted) by the completion engine.\n     */\n    switch (tok[1]) {\n    case 'd':\n        return \"<32bit number>\";\n    case 'D':\n        return \"<64bit number>\";\n    case 'h':\n        return \"<32bit hex>\";\n    case 'H':\n        return \"<64bit hex>\";\n    case 'u':\n        return \"<32bit unsigned>\";\n    case 'U':\n        return \"<64bit unsigned>\";\n    case 'P':\n        return \"<portlist>\";\n    case 'C':\n        return \"<corelist>\";\n    case 's':\n        return \"<string>\";\n    case 'c':\n        return \"<comma-list>\";\n    case 'm':\n        return \"<mac-addr>\";\n    case '4':\n        return \"<ipv4-addr>\";\n    case '6':\n        return \"<ipv6-addr>\";\n    case 'k':\n        return \"<kvargs>\";\n    case 'l':\n        return \"<list>\";\n    case 'b':\n        return \"<8bit number>\";\n    case 'n':\n        return \"<number>\";\n    default:\n        return NULL;\n    }\n}\n\nstatic int\n_is_hint_candidate(const char *s)\n{\n    size_t len;\n\n    if (!s)\n        return 0;\n\n    len = strlen(s);\n    return (len >= 3 && s[0] == '<' && s[len - 1] == '>');\n}\n\nstatic int\n_env_collect_var_candidates(const char *prefix, char ***out_list, int *out_cnt)\n{\n    struct cli_env *env = this_cli ? this_cli->env : NULL;\n    struct env_node **list;\n    int max;\n    int n;\n\n    if (!env || !out_list || !out_cnt)\n        return 0;\n\n    max = cli_env_count(env);\n    if (max <= 0)\n        return 0;\n\n    list = calloc((size_t)max, sizeof(*list));\n    if (!list)\n        return 0;\n\n    n = cli_env_get_all(env, list, max);\n    for (int i = 0; i < n; i++) {\n        const char *name;\n\n        if (!list[i])\n            continue;\n        name = list[i]->var;\n        if (!name || name[0] == '\\0')\n            continue;\n        if (prefix && *prefix && strncmp(name, prefix, strlen(prefix)))\n            continue;\n        if (_str_list_add_unique(out_list, out_cnt, name)) {\n            free(list);\n            return -1;\n        }\n    }\n\n    free(list);\n    return *out_cnt;\n}\n\nstatic int\n_portlist_collect_candidates(const char *prefix, char ***out_list, int *out_cnt)\n{\n    /* Minimal, but useful: the CLI help documents 'all' as a valid portlist. */\n    if (prefix && *prefix && strncmp(\"all\", prefix, strlen(prefix)))\n        return *out_cnt;\n\n    if (_str_list_add_unique(out_list, out_cnt, \"all\"))\n        return -1;\n\n    /* Also show the placeholder hint alongside real values when no prefix is typed. */\n    if (!prefix || !*prefix) {\n        const char *hint = _placeholder_hint(\"%P\");\n        if (hint && _str_list_add_unique(out_list, out_cnt, hint))\n            return -1;\n    }\n\n    return *out_cnt;\n}\n\nstatic int\n_map_collect_placeholder_candidates(const char *cmd, char **mtoks, int mtokc, int arg_index,\n                                    int argc, char **argv, const char *placeholder,\n                                    const char *prefix, char ***out_list, int *out_cnt)\n{\n    (void)mtokc;\n\n    if (!cmd || !mtoks || !argv || !out_list || !out_cnt)\n        return 0;\n\n    /* env get|set|del <VAR> : complete existing environment variable names */\n    if (!strcmp(cmd, \"env\") && arg_index == 2 && argc >= 2 && argv[1] &&\n        (!strcmp(argv[1], \"get\") || !strcmp(argv[1], \"set\") || !strcmp(argv[1], \"del\")))\n        return _env_collect_var_candidates(prefix, out_list, out_cnt);\n\n    /* seq/sequence <seq#> ... : show a helpful hint for the required sequence number */\n    if ((!strcmp(cmd, \"seq\") || !strcmp(cmd, \"sequence\")) && arg_index == 1 && placeholder &&\n        !strcmp(placeholder, \"%d\")) {\n        if (!prefix || !*prefix) {\n            if (_str_list_add_unique(out_list, out_cnt, \"<seq#>\"))\n                return -1;\n        }\n        return *out_cnt;\n    }\n\n    /* %P (portlist) : suggest common values like 'all' */\n    if (placeholder && placeholder[0] == '%' && placeholder[1] == 'P')\n        return _portlist_collect_candidates(prefix, out_list, out_cnt);\n\n    return 0;\n}\n\nstatic int\n_map_collect_candidates(struct cli_map *maps, int argc, char **argv, int arg_index,\n                        const char *prefix, char ***out_list, int *out_cnt)\n{\n    char fmt_copy[CLI_MAX_PATH_LENGTH + 1];\n    char *mtoks[CLI_MAX_ARGVS + 1];\n\n    if (!maps || argc <= 0 || !argv || !argv[0] || arg_index < 1)\n        return 0;\n\n    for (int mi = 0; maps[mi].fmt != NULL; mi++) {\n        memset(mtoks, 0, sizeof(mtoks));\n        snprintf(fmt_copy, sizeof(fmt_copy), \"%s\", maps[mi].fmt);\n\n        int mtokc = pg_strtok(fmt_copy, \" \", mtoks, CLI_MAX_ARGVS);\n        if (mtokc <= arg_index)\n            continue;\n\n        /* map must be for this command */\n        if (!mtoks[0])\n            continue;\n        if (_is_choice_token(mtoks[0])) {\n            if (!_choice_token_contains(mtoks[0], argv[0]))\n                continue;\n        } else if (strcmp(mtoks[0], argv[0])) {\n            continue;\n        }\n\n        /* must match already-typed tokens before the arg we are completing */\n        int ok = 1;\n        for (int i = 0; i < arg_index && i < argc && i < mtokc; i++) {\n            if (!_map_tok_compatible_with_user_tok(mtoks[i], argv[i])) {\n                ok = 0;\n                break;\n            }\n        }\n        if (!ok)\n            continue;\n\n        const char *cand_tok = mtoks[arg_index];\n        if (!cand_tok || cand_tok[0] == '\\0')\n            continue;\n\n        /* Placeholder-aware completion (e.g., env var names for env get/set/del). */\n        if (_is_placeholder(cand_tok) && !_is_choice_token(cand_tok)) {\n            const int before_cnt = *out_cnt;\n            if (_map_collect_placeholder_candidates(argv[0], mtoks, mtokc, arg_index, argc, argv,\n                                                    cand_tok, prefix, out_list, out_cnt) < 0)\n                return -1;\n\n            /* If no better candidates exist, provide a human hint like \"<portlist>\". */\n            if ((!prefix || !*prefix) && (*out_cnt == before_cnt)) {\n                const char *hint = _placeholder_hint(cand_tok);\n                if (hint && _str_list_add_unique(out_list, out_cnt, hint))\n                    return -1;\n            }\n            continue;\n        }\n\n        if (_is_choice_token(cand_tok)) {\n            /* cand_tok is like \"%|a|b|c\" */\n            char opt_copy[CLI_MAX_PATH_LENGTH + 1];\n            char *opts[CLI_MAX_ARGVS + 1];\n            memset(opts, 0, sizeof(opts));\n            snprintf(opt_copy, sizeof(opt_copy), \"%s\", cand_tok + 2);\n            int n = pg_strtok(opt_copy, \"|\", opts, CLI_MAX_ARGVS);\n            for (int oi = 0; oi < n; oi++) {\n                if (!opts[oi])\n                    continue;\n                if (prefix && *prefix && strncmp(opts[oi], prefix, strlen(prefix)))\n                    continue;\n                if (_str_list_add_unique(out_list, out_cnt, opts[oi]))\n                    return -1;\n            }\n        } else {\n            if (prefix && *prefix && strncmp(cand_tok, prefix, strlen(prefix)))\n                continue;\n            if (_str_list_add_unique(out_list, out_cnt, cand_tok))\n                return -1;\n        }\n    }\n\n    return *out_cnt;\n}\n\nstatic int\n_map_next_is_user_value(struct cli_map *maps, int argc, char **argv, int arg_index)\n{\n    char fmt_copy[CLI_MAX_PATH_LENGTH + 1];\n    char *mtoks[CLI_MAX_ARGVS + 1];\n\n    if (!maps || argc <= 0 || !argv || !argv[0] || arg_index < 1)\n        return 0;\n\n    for (int mi = 0; maps[mi].fmt != NULL; mi++) {\n        memset(mtoks, 0, sizeof(mtoks));\n        snprintf(fmt_copy, sizeof(fmt_copy), \"%s\", maps[mi].fmt);\n        int mtokc = pg_strtok(fmt_copy, \" \", mtoks, CLI_MAX_ARGVS);\n        if (mtokc <= arg_index)\n            continue;\n\n        if (!mtoks[0])\n            continue;\n        if (_is_choice_token(mtoks[0])) {\n            if (!_choice_token_contains(mtoks[0], argv[0]))\n                continue;\n        } else if (strcmp(mtoks[0], argv[0])) {\n            continue;\n        }\n\n        int ok = 1;\n        for (int i = 0; i < arg_index && i < argc && i < mtokc; i++) {\n            if (!_map_tok_compatible_with_user_tok(mtoks[i], argv[i])) {\n                ok = 0;\n                break;\n            }\n        }\n        if (!ok)\n            continue;\n\n        const char *tok = mtoks[arg_index];\n        if (!tok || tok[0] == '\\0')\n            continue;\n\n        if (_is_placeholder(tok) && !_is_choice_token(tok))\n            return 1;\n    }\n\n    return 0;\n}\n\nstatic int\n_map_current_is_user_value(struct cli_map *maps, int argc, char **argv, int arg_index)\n{\n    char fmt_copy[CLI_MAX_PATH_LENGTH + 1];\n    char *mtoks[CLI_MAX_ARGVS + 1];\n\n    if (!maps || argc <= 0 || !argv || !argv[0] || arg_index < 1)\n        return 0;\n\n    for (int mi = 0; maps[mi].fmt != NULL; mi++) {\n        memset(mtoks, 0, sizeof(mtoks));\n        snprintf(fmt_copy, sizeof(fmt_copy), \"%s\", maps[mi].fmt);\n        int mtokc = pg_strtok(fmt_copy, \" \", mtoks, CLI_MAX_ARGVS);\n        if (mtokc <= arg_index)\n            continue;\n\n        if (!mtoks[0])\n            continue;\n        if (_is_choice_token(mtoks[0])) {\n            if (!_choice_token_contains(mtoks[0], argv[0]))\n                continue;\n        } else if (strcmp(mtoks[0], argv[0])) {\n            continue;\n        }\n\n        int ok = 1;\n        for (int i = 0; i < arg_index && i < argc && i < mtokc; i++) {\n            if (!_map_tok_compatible_with_user_tok(mtoks[i], argv[i])) {\n                ok = 0;\n                break;\n            }\n        }\n        if (!ok)\n            continue;\n\n        const char *tok = mtoks[arg_index];\n        if (!tok || tok[0] == '\\0')\n            continue;\n\n        if (_is_placeholder(tok) && !_is_choice_token(tok))\n            return 1;\n    }\n\n    return 0;\n}\n\nstatic int\n_map_has_tokens_after(struct cli_map *maps, int argc, char **argv, int arg_index)\n{\n    char fmt_copy[CLI_MAX_PATH_LENGTH + 1];\n    char *mtoks[CLI_MAX_ARGVS + 1];\n\n    if (!maps || argc <= 0 || !argv || !argv[0] || arg_index < 1)\n        return 0;\n\n    for (int mi = 0; maps[mi].fmt != NULL; mi++) {\n        memset(mtoks, 0, sizeof(mtoks));\n        snprintf(fmt_copy, sizeof(fmt_copy), \"%s\", maps[mi].fmt);\n        int mtokc = pg_strtok(fmt_copy, \" \", mtoks, CLI_MAX_ARGVS);\n\n        if (!mtoks[0])\n            continue;\n        if (_is_choice_token(mtoks[0])) {\n            if (!_choice_token_contains(mtoks[0], argv[0]))\n                continue;\n        } else if (strcmp(mtoks[0], argv[0])) {\n            continue;\n        }\n\n        int ok = 1;\n        for (int i = 0; i < arg_index && i < argc && i < mtokc; i++) {\n            if (!_map_tok_compatible_with_user_tok(mtoks[i], argv[i])) {\n                ok = 0;\n                break;\n            }\n        }\n        if (!ok)\n            continue;\n\n        if (mtokc > (arg_index + 1))\n            return 1;\n    }\n\n    return 0;\n}\n\nstatic uint32_t\n_column_count(struct cli_node **nodes, uint32_t node_cnt, uint32_t *len)\n{\n    uint32_t i, mlen = 8, cs;\n\n    if (!nodes || !len)\n        return CLI_SCREEN_WIDTH / mlen;\n\n    /* Calculate the column size */\n    for (i = 0; i < node_cnt; i++)\n        mlen = RTE_MAX(mlen, strlen(nodes[i]->name));\n    mlen++; /* Make sure we have at least a space between */\n\n    *len = mlen;\n    cs   = CLI_SCREEN_WIDTH / mlen;\n\n    return cs;\n}\n\nstatic int\n_print_nodes(struct cli_node **nodes, uint32_t node_cnt, uint32_t dir_only, char *match,\n             struct cli_node **ret)\n{\n    struct cli_node *n;\n    uint32_t i, cnt = 0, ccnt, found = 0, slen, csize;\n\n    if (!node_cnt || !nodes)\n        return 0;\n\n    ccnt = _column_count(nodes, node_cnt, &csize);\n\n    slen = (match) ? strlen(match) : 0;\n\n    /* display the node names */\n    for (i = 0; i < node_cnt; i++) {\n        n = nodes[i];\n\n        if (dir_only && !is_directory(n))\n            continue;\n\n        if (slen && strncmp(n->name, match, slen))\n            continue;\n\n        if (!cnt)\n            cli_printf(\"\\n\");\n\n        cli_printf(\"%-*s\", csize, n->name);\n        if ((++cnt % ccnt) == 0)\n            cli_printf(\"\\n\");\n\n        /* Found a possible match */\n        if (ret)\n            *ret = n;\n        found++;\n    }\n\n    /* if not nodes found cnt will be zero and no CR */\n    if (cnt % ccnt)\n        cli_printf(\"\\n\");\n\n    return found;\n}\n\nstatic int\nqsort_compare(const void *p1, const void *p2)\n{\n    const struct cli_node *n1, *n2;\n\n    n1 = *(const struct cli_node *const *)p1;\n    n2 = *(const struct cli_node *const *)p2;\n\n    return strcmp(n1->name, n2->name);\n}\n\nstatic int\ncomplete_args(int argc, char **argv, uint32_t types)\n{\n    struct cli_node **nodes = NULL, *node = NULL;\n    struct gapbuf *gb;\n    char *match;\n    uint32_t node_cnt, found = 0, dir_only = 0, slen;\n\n    if (argc)\n        match = argv[argc - 1];\n    else\n        match = NULL;\n\n    gb = this_cli->gb;\n\n    if (match) {\n        uint32_t stype;\n        uint32_t slashes;\n        char *p;\n\n        /* Count the number of slashes in the path */\n        slashes = pg_strcnt(match, '/');\n\n        if (slashes) {\n            /* full path to command given */\n            if (cli_find_node(match, &node))\n                if (is_executable(node))\n                    return 0;\n\n            /* if not found get last directory in path */\n            node = cli_last_dir_in_path(match);\n\n            if ((slashes == 1) && (match && (match[0] == '/'))) {\n                match++;\n                dir_only++;\n            }\n        }\n\n        stype = CLI_ALL_TYPE; /* search for all nodes */\n        if (argc > 1)\n            stype = CLI_OTHER_TYPE; /* search for non-exe nodes */\n\n        node_cnt = cli_node_list_with_type(node, stype, (void **)&nodes);\n        p        = strrchr(match, '/');\n        if (p)\n            match = ++p;\n    } else\n        node_cnt = cli_node_list_with_type(NULL, types, (void **)&nodes);\n\n    if (node_cnt) {\n        struct cli_node *mnode = NULL;\n\n        if (node_cnt > 1)\n            qsort(nodes, node_cnt, sizeof(void *), qsort_compare);\n\n        slen = (match) ? strlen(match) : 0;\n\n        /*\n         * If there is exactly one match, do not print the candidate list.\n         * Just complete the token (shell-like behavior).\n         */\n        uint32_t match_cnt = 0;\n        for (uint32_t i = 0; i < node_cnt; i++) {\n            struct cli_node *n = nodes[i];\n\n            if (dir_only && !is_directory(n))\n                continue;\n            if (slen && strncmp(n->name, match, slen))\n                continue;\n\n            mnode = n;\n            if (++match_cnt > 1)\n                break;\n        }\n\n        if (match_cnt == 1)\n            found = 1;\n        else if (match_cnt > 1)\n            found = _print_nodes(nodes, node_cnt, dir_only, match, &mnode);\n\n        /*\n         * _match is a pointer to the last matched node\n         * _found is a flag to determine if pointer is valid\n         */\n        if (mnode && (found == 1)) { /* Found a possible match */\n            struct cli_node *node = (struct cli_node *)mnode;\n            char *s;\n            int nlen;\n\n            s = strrchr(match, '/');\n            if (s)\n                match = ++s;\n\n            slen = strlen(match);\n            nlen = (strlen(node->name) - slen);\n\n            if (nlen > 0) /* Add the rest of the matching command */\n                gb_str_insert(gb, &node->name[slen], nlen);\n\n            if (is_directory(node))\n                gb_str_insert(gb, (char *)(uintptr_t)\"/\", 1);\n            else\n                gb_str_insert(gb, (char *)(uintptr_t)\" \", 1);\n        }\n    }\n    cli_node_list_free(nodes);\n\n    return found;\n}\n\nvoid\ncli_auto_complete(void)\n{\n    char *argv[CLI_MAX_ARGVS + 1];\n    char *line = NULL;\n    int argc, size, ret;\n    int at_new_token;\n    int arg_index;\n    struct cli_map *maps = NULL;\n    char **cands         = NULL;\n    int cand_cnt         = 0;\n    uint64_t now_tsc;\n    uint32_t line_hash;\n    int force_usage = 0;\n\n    memset(argv, '\\0', sizeof(argv));\n\n    size = gb_data_size(this_cli->gb);\n    if (!size)\n        return;\n\n    size = RTE_MIN(size, (int)(CLI_MAX_SCRATCH_LENGTH - 1));\n\n    line = calloc(1, (size_t)size + 1);\n    if (!line)\n        return;\n\n    gb_copy_to_buf(this_cli->gb, line, size);\n\n    argc = pg_strqtok(line, \" \\r\\n\", argv, CLI_MAX_ARGVS);\n\n    /* Be defensive: some tokenizers may yield empty/NULL trailing tokens. */\n    while (argc > 0 && (!argv[argc - 1] || argv[argc - 1][0] == '\\0'))\n        argc--;\n\n    at_new_token = 0;\n    if (!gb_point_at_start(this_cli->gb)) {\n        char prev    = _gb_peek_prev(this_cli->gb);\n        at_new_token = (prev == ' ' || prev == '\\t' || prev == '\\n' || prev == '\\r');\n    }\n\n    /* Determine which argv slot we are completing */\n    if (argc == 0)\n        arg_index = 0;\n    else\n        arg_index = at_new_token ? argc : (argc - 1);\n\n    /* Double-tab detection: same line state within a short time window */\n    now_tsc   = rte_get_timer_cycles();\n    line_hash = _ac_hash_line(line, arg_index, at_new_token);\n    {\n        const uint64_t hz            = rte_get_timer_hz();\n        const uint64_t window_cycles = (hz) ? (hz * 4 / 10) : 0; /* ~400ms */\n\n        if (window_cycles && this_cli->ac_last_hash == line_hash &&\n            (now_tsc - this_cli->ac_last_tsc) <= window_cycles)\n            force_usage = 1;\n\n        this_cli->ac_last_hash = line_hash;\n        this_cli->ac_last_tsc  = now_tsc;\n    }\n\n    if (argc == 0) {\n        ret = complete_args(argc, argv, CLI_ALL_TYPE);\n\n        if (ret)\n            cli_redisplay_line();\n        free(line);\n        return;\n    }\n\n    /*\n     * If the user typed an exact executable command token (no trailing space),\n     * treat <Tab> as \"accept command and complete next token\".\n     *\n     * - If a map exists: complete the next token from the map.\n     * - Otherwise: fall back to the legacy \"-?\" help behavior.\n     */\n    if (argc == 1 && !at_new_token && argv[0]) {\n        struct cli_node *node = NULL;\n        if (!cli_find_node(argv[0], &node) && node && is_executable(node) &&\n            node->name[0] != '\\0' && !strcmp(node->name, argv[0])) {\n            gb_str_insert(this_cli->gb, (char *)(uintptr_t)\" \", 1);\n            cli_redisplay_line();\n\n            maps = cli_get_cmd_map(argv[0]);\n            if (maps) {\n                ret = _map_collect_candidates(maps, argc, argv, 1, NULL, &cands, &cand_cnt);\n                if (ret < 0) {\n                    _str_list_free(cands, cand_cnt);\n                    free(line);\n                    return;\n                }\n\n                if (cand_cnt == 1) {\n                    const char *ins = cands[0];\n                    if (_is_hint_candidate(ins)) {\n                        _print_strings(cands, cand_cnt, NULL);\n                        cli_redisplay_line();\n                    } else {\n                        gb_str_insert(this_cli->gb, (char *)(uintptr_t)ins, strlen(ins));\n                        gb_str_insert(this_cli->gb, (char *)(uintptr_t)\" \", 1);\n                        cli_redisplay_line();\n                    }\n                } else if (cand_cnt > 1) {\n                    _print_strings(cands, cand_cnt, NULL);\n                    cli_redisplay_line();\n                } else {\n                    /* If next token is user input, don't spam usage; otherwise show usage safely.\n                     */\n                    if (!_map_next_is_user_value(maps, argc, argv, 1)) {\n                        cli_printf(\"\\n\");\n                        cli_maps_show(maps, argc, argv);\n                        cli_redisplay_line();\n                    } else if (force_usage) {\n                        cli_printf(\"\\n\");\n                        cli_maps_show(maps, argc, argv);\n                        cli_redisplay_line();\n                    }\n                }\n\n                _str_list_free(cands, cand_cnt);\n                free(line);\n                return;\n            }\n\n            /* No map registered: fall back to legacy \"-?\" help output. */\n            {\n                int cur_size = gb_data_size(this_cli->gb);\n                cur_size     = RTE_MIN(cur_size, (int)(CLI_MAX_SCRATCH_LENGTH - 1));\n                char *save   = calloc(1, (size_t)cur_size + 1);\n\n                if (!save) {\n                    free(line);\n                    return;\n                }\n\n                gb_copy_to_buf(this_cli->gb, save, cur_size);\n                gb_str_insert(this_cli->gb, (char *)(uintptr_t)\"-?\", 2);\n                cli_execute();\n\n                gb_reset_buf(this_cli->gb);\n                gb_str_insert(this_cli->gb, save, cur_size);\n                cli_redisplay_line();\n                free(save);\n            }\n\n            free(line);\n            return;\n        }\n    }\n\n    /* If the first token is a known command with a map, try map-driven completion */\n    maps = (argc > 0 && argv[0]) ? cli_get_cmd_map(argv[0]) : NULL;\n    if (maps && arg_index >= 1) {\n        const char *prefix = NULL;\n        if (!at_new_token && arg_index < argc && argv[arg_index])\n            prefix = argv[arg_index];\n\n        ret = _map_collect_candidates(maps, argc, argv, arg_index, prefix, &cands, &cand_cnt);\n        if (ret < 0) {\n            _str_list_free(cands, cand_cnt);\n            free(line);\n            return;\n        }\n\n        /*\n         * If we are sitting on a user-value token with no suggestions (e.g. \"set 0<Tab>\"),\n         * treat Tab as end-of-token: insert a space and complete the next map token.\n         */\n        if (cand_cnt == 0 && !at_new_token && prefix && *prefix &&\n            _map_current_is_user_value(maps, argc, argv, arg_index) &&\n            _map_has_tokens_after(maps, argc, argv, arg_index)) {\n            gb_str_insert(this_cli->gb, (char *)(uintptr_t)\" \", 1);\n            cli_redisplay_line();\n\n            _str_list_free(cands, cand_cnt);\n            cands    = NULL;\n            cand_cnt = 0;\n\n            at_new_token = 1;\n            arg_index    = argc;\n            prefix       = NULL;\n\n            ret = _map_collect_candidates(maps, argc, argv, arg_index, prefix, &cands, &cand_cnt);\n            if (ret < 0) {\n                _str_list_free(cands, cand_cnt);\n                free(line);\n                return;\n            }\n        }\n\n        if (cand_cnt == 1) {\n            const char *ins = cands[0];\n            size_t plen     = (prefix) ? strlen(prefix) : 0;\n\n            if (_is_hint_candidate(ins)) {\n                _print_strings(cands, cand_cnt, prefix);\n                cli_redisplay_line();\n            } else if (plen <= strlen(ins)) {\n                gb_str_insert(this_cli->gb, (char *)(uintptr_t)&ins[plen], strlen(ins) - plen);\n                gb_str_insert(this_cli->gb, (char *)(uintptr_t)\" \", 1);\n                cli_redisplay_line();\n            }\n        } else if (cand_cnt > 1) {\n            _print_strings(cands, cand_cnt, prefix);\n            cli_redisplay_line();\n        } else if (at_new_token) {\n            /* If next token is user input, don't spam usage; otherwise show usage safely. */\n            if (!_map_next_is_user_value(maps, argc, argv, arg_index)) {\n                cli_printf(\"\\n\");\n                cli_maps_show(maps, argc, argv);\n                cli_redisplay_line();\n            } else if (force_usage) {\n                cli_printf(\"\\n\");\n                cli_maps_show(maps, argc, argv);\n                cli_redisplay_line();\n            }\n        }\n\n        _str_list_free(cands, cand_cnt);\n        free(line);\n        return;\n    }\n\n    /* no space before cursor maybe a command completion request */\n    if (gb_point_at_start(this_cli->gb) || _gb_peek_prev(this_cli->gb) != ' ') {\n        if (argc == 1) /* Only one word then look for a command */\n            ret = complete_args(argc, argv, CLI_ALL_TYPE);\n        else /* If more then one word then look for file/dir */\n            ret = complete_args(argc, argv, CLI_FILE_NODE | CLI_DIR_NODE);\n\n        /* if we get an error then redisplay the line */\n        if (ret)\n            cli_redisplay_line();\n    } else {\n        char *save = calloc(1, (size_t)size + 1);\n\n        if (!save) {\n            free(line);\n            return;\n        }\n\n        /* Call function to print out help text, plus save a copy */\n        gb_copy_to_buf(this_cli->gb, save, size);\n\n        /* Add the -? to the command */\n        gb_str_insert(this_cli->gb, (char *)(uintptr_t)\"-?\", 2);\n\n        cli_execute();\n\n        /* reset the input buffer to remove -? */\n        gb_reset_buf(this_cli->gb);\n\n        /* insert the saved string back to the input buffer */\n        gb_str_insert(this_cli->gb, save, size);\n\n        cli_redisplay_line();\n\n        free(save);\n    }\n\n    free(line);\n}\n"
  },
  {
    "path": "lib/cli/cli_auto_complete.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#ifndef _CLI_AUTO_COMPLETE_H_\n#define _CLI_AUTO_COMPLETE_H_\n\n/**\n * @file\n * CLI auto-complete.\n *\n * Handles TAB completion for commands/paths and optionally uses registered\n * cli_map tables to offer context-aware token hints.\n */\n\n#include \"cli.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Handle the tab key for auto-complete.\n *\n * This function reads the current input buffer, computes candidate\n * completions, and either inserts text (single match) or prints a candidate\n * list (multiple matches).\n *\n * @return\n *   N/A\n */\nvoid cli_auto_complete(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CLI_AUTO_COMPLETE_H_ */\n"
  },
  {
    "path": "lib/cli/cli_cmap.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2013-2026>, Intel Corporation.\n */\n\n/*\n * Prints a CPU core map on the form\n * \"S/C/T L\"\n * where\n * - S is the CPU socket ID\n * - C is the physical CPU core ID\n * - T is the hyper-thread ID\n * - L is the logical core ID\n *\n * This tool parses the information from \"/proc/cpuinfo\" which should\n * be present on all Linux systems.\n *\n * NOTE: this tool has only been tested on systems with x86/x86_64\n * CPUs so far.\n *\n * Written 2011 by Kenneth Jonsson, WindRiver.\n * Adapted to Pktgen by Keith Wiles, WindRiver 2013-01-08\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <stdint.h>\n#include <unistd.h>\n#include <fcntl.h>\n\n#include \"cli_cmap.h\"\n\nstatic char *model_name;\n\nchar *\ncmap_cpu_model(void)\n{\n    return model_name;\n}\n\nstatic const char *\nas_str(const char *line)\n{\n    if (*line != ':')\n        return as_str(line + 1);\n    return line + 1;\n}\n\nstatic unsigned\nas_int(const char *line)\n{\n    return atoi(as_str(line));\n}\n\nstatic lcore_t *\nnew_lcore(const char *line, lcore_t *rest)\n{\n    lcore_t *lc = calloc(1, sizeof(lcore_t));\n\n    lc->next  = rest;\n    lc->u.lid = as_int(line);\n\n    return lc;\n}\n\nstatic lcore_t *\nset_raw_socket_id(const char *line, lcore_t *lc)\n{\n    lc->u.sid = as_int(line);\n    printf(\"Socket ID: %u, (%s)\\n\", lc->u.sid, line);\n    return lc;\n}\n\nstatic lcore_t *\nset_raw_core_id(const char *line, lcore_t *lc)\n{\n    lc->u.cid = as_int(line);\n    return lc;\n}\n\nstatic lcore_t *\nset_model_name(const char *line, lcore_t *lc)\n{\n    if (!model_name)\n        model_name = strdup(as_str(line));\n    return lc;\n}\n\nstatic unsigned\nget_next_thread_id(const lcore_t *lc, unsigned socket_id, unsigned core_id)\n{\n    if (lc == NULL)\n        return 0;\n    if (lc->u.sid == socket_id && lc->u.cid == core_id)\n        return lc->u.tid + 1;\n    return get_next_thread_id(lc->next, socket_id, core_id);\n}\n\nstatic lcore_t *\nset_thread_id_str(const char *unused, lcore_t *lc)\n{\n    (void)unused;\n    lc->u.tid = get_next_thread_id(lc->next, lc->u.sid, lc->u.cid);\n    return lc;\n}\n\nstatic lcore_t *\nignore_line(const char *unused, lcore_t *lc)\n{\n    (void)unused;\n    return lc;\n}\n\nstatic do_line_fn\nget_matching_action(const char *line)\n{\n    // clang-format off\n    static struct action actions[] = {\n        {\"processor\", new_lcore},\n        {\"physical id\", set_raw_socket_id},\n        {\"core id\", set_raw_core_id},\n        {\"model name\", set_model_name},\n        {\"\\n\", set_thread_id_str},\n        {NULL, NULL}\n    };\n    // clang-format on\n    struct action *action;\n\n    for (action = actions; action->fn != NULL; ++action)\n        if (strncmp(action->desc, line, strlen(action->desc)) == 0)\n            return action->fn;\n\n    return ignore_line;\n}\n\n/*\n * Remaps a property value from 'from' to 'to'. This is done for all\n * logical cores.\n */\nstatic void\nremap(lcore_t *lc, unsigned from, unsigned to, getter_fn get, setter_fn set)\n{\n    if (lc) {\n        if (get(lc) == from)\n            set(lc, to);\n\n        remap(lc->next, from, to, get, set);\n    }\n}\n\n/*\n * Returns the first entry that is equal to or as close as possible to\n * 'v' in the property returned by 'get'.\n */\nstatic lcore_t *\nclosest_gte(lcore_t *lc, lcore_t *sel, unsigned v, getter_fn get)\n{\n    if (lc == NULL)\n        return sel;\n\n    if (get(lc) >= v && (sel == NULL || get(sel) - v > get(lc) - v))\n        return closest_gte(lc->next, lc, v, get);\n\n    return closest_gte(lc->next, sel, v, get);\n}\n\n/*\n * Makes the property returned and set by 'get'/'set' start from zero\n * and increase by one for each unique value that property has.\n * Ex: core id \"0,1,4,5,0,1,4,5\" -> \"0,1,2,3,0,1,2,3\"\n */\nstatic void\nzero_base(lcore_t *head, getter_fn get, setter_fn set)\n{\n    unsigned id = 0;\n    lcore_t *lc;\n\n    while ((lc = closest_gte(head, NULL, id, get)) != NULL) {\n        remap(lc, get(lc), id, get, set);\n        ++id;\n    }\n}\n\nstatic void\nget_and_free_lcore_info(lcore_t *lc, lc_info_t *get)\n{\n    if (lc) {\n        get_and_free_lcore_info(lc->next, get + 1);\n        get->word = lc->u.word;\n        free(lc);\n    }\n}\n\nstatic inline int\ncount_cores(lcore_t *lcores)\n{\n    int num = 0;\n\n    while (lcores) {\n        lcores = lcores->next;\n        num++;\n    }\n    return num;\n}\n\nstatic int\nmy_getline(char **line, size_t *line_sz, int fd)\n{\n    char *l, c;\n    size_t sz, i;\n\n    if (*line == NULL) {\n        if (*line_sz == 0)\n            *line_sz = MAX_LINE_SIZE;\n        l = calloc(1, *line_sz);\n        if (l == NULL)\n            return -1;\n        *line = l;\n    } else\n        l = *line;\n\n    for (i = 0, sz = 0; i < *line_sz; i++) {\n        if (read(fd, &c, 1) != 1)\n            return -1;\n        *l++ = c;\n        sz++;\n        if (c == '\\n')\n            break;\n    }\n    *l = '\\0';\n    return sz;\n}\n\nstatic lc_info_t lcore_info[RTE_MAX_LCORE];\n\nstruct cmap *\ncmap_create(void)\n{\n    int fd;\n    char *line = NULL;\n    struct cmap *cmap;\n    lc_info_t *lc_info = &lcore_info[0];\n    size_t line_sz     = 0;\n    lcore_t *lcores    = NULL;\n\n    memset(lcore_info, '\\0', sizeof(lcore_info));\n\n    cmap = calloc(1, sizeof(struct cmap));\n    if (!cmap)\n        return NULL;\n\n    if ((fd = open(PROC_CPUINFO, O_RDONLY)) < 0) {\n        fprintf(stderr, \"Cannot open %s on this system\\n\", PROC_CPUINFO);\n        free(cmap);\n        return NULL;\n    }\n\n    while (my_getline(&line, &line_sz, fd) >= 0)\n        lcores = get_matching_action(line)(line, lcores);\n\n    if (fd)\n        close(fd);\n    if (line)\n        free(line);\n\n    zero_base(lcores, cmap_socket_id, cmap_set_socket_id);\n    zero_base(lcores, cmap_core_id, cmap_set_core_id);\n\n    cmap->linfo = lc_info;\n\n    cmap->model     = model_name;\n    cmap->num_cores = count_cores(lcores);\n    cmap->sid_cnt   = cmap_cnt(lcores, cmap_socket_id);\n    cmap->cid_cnt   = cmap_cnt(lcores, cmap_core_id);\n    cmap->tid_cnt   = cmap_cnt(lcores, cmap_thread_id);\n\n    get_and_free_lcore_info(lcores, lc_info);\n\n    return cmap;\n}\n\nvoid\ncmap_free(struct cmap *cmap)\n{\n    free(cmap);\n}\n"
  },
  {
    "path": "lib/cli/cli_cmap.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n/**\n * @file\n * CPU core map — parses /proc/cpuinfo into a lcore/socket/core/thread table.\n *\n * Used internally by the CLI cpu-info display to map logical cores to their\n * physical socket, core, and hyper-thread IDs.\n */\n\n#ifndef __CLI_CMAP_H\n#define __CLI_CMAP_H\n\n#include <stdint.h>\n\n#define MAX_LINE_SIZE 4096\n\n#define PROC_CPUINFO \"/proc/cpuinfo\"\n\n/** Per-logical-core topology descriptor packed into a single 32-bit word. */\ntypedef union {\n    struct {\n        uint8_t lid; /**< Logical core ID */\n        uint8_t sid; /**< CPU socket ID */\n        uint8_t cid; /**< Physical CPU core ID */\n        uint8_t tid; /**< Hyper-thread ID */\n    };\n    uint32_t word; /**< Raw 32-bit representation */\n} lc_info_t;\n\n/** Linked-list node for a single logical core entry. */\ntypedef struct lcore {\n    struct lcore *next; /**< Next lcore in the singly-linked list */\n    lc_info_t u;        /**< Topology info for this lcore */\n} lcore_t;\n\n/** Aggregated CPU topology table for the whole system. */\nstruct cmap {\n    uint16_t num_cores; /**< Total number of logical cores */\n    uint16_t sid_cnt;   /**< Number of distinct socket IDs */\n    uint16_t cid_cnt;   /**< Number of distinct physical core IDs */\n    uint16_t tid_cnt;   /**< Number of distinct hyper-thread IDs */\n    lc_info_t *linfo;   /**< Flat array of per-lcore topology info */\n    char *model;        /**< CPU model name string (from /proc/cpuinfo) */\n};\n\ntypedef lcore_t *(*do_line_fn)(const char *line, lcore_t *);\ntypedef unsigned (*getter_fn)(const lcore_t *);\ntypedef void (*setter_fn)(lcore_t *, unsigned new_val);\n\ntypedef struct action {\n    const char *desc;\n    do_line_fn fn;\n} action_t;\n\n/**\n * Create a cmap structure for the current system\n *\n * @return\n *   The pointer to the cmap structure or NULL on error\n */\nstruct cmap *cmap_create(void);\n\n/**\n * Return the current CPU model string\n *\n * @return\n *   Pointer to current CPU model string.\n */\nchar *cmap_cpu_model(void);\n\n/**\n * Free up the resources attached to a cmap structure\n *\n * @param cmap\n *   A valid cmap pointer\n */\nvoid cmap_free(struct cmap *cmap);\n\n/**\n * Return the socket id for a given lcore (Internal)\n *\n * @param lc\n *   Pointer to the given lcore structure\n * @return\n *   The socket ID value\n */\nstatic inline unsigned int\ncmap_socket_id(const lcore_t *lc)\n{\n    return lc->u.sid;\n}\n\n/**\n * Set the socket id for a given lcore (Internal)\n *\n * @param lc\n *   Pointer to the given lcore structure\n * @param v\n *   Set the socket id value\n * @return\n *   N/A\n */\nstatic inline void\ncmap_set_socket_id(lcore_t *lc, unsigned v)\n{\n    lc->u.sid = v;\n}\n\n/**\n * Return the core id for a given lcore (Internal)\n *\n * @param lc\n *   Pointer to the given lcore structure\n * @return\n *   The core ID value\n */\nstatic inline unsigned int\ncmap_core_id(const lcore_t *lc)\n{\n    return lc->u.cid;\n}\n\n/**\n * Set the core id for a given lcore (Internal)\n *\n * @param lc\n *   Pointer to the given lcore structure\n * @param v\n *   Set the core id value\n * @return\n *   N/A\n */\nstatic inline void\ncmap_set_core_id(lcore_t *lc, unsigned v)\n{\n    lc->u.cid = v;\n}\n\n/**\n * Return the thread id for a given lcore (Internal)\n *\n * @param lc\n *   Pointer to the given lcore structure\n * @return\n *   The thread ID value\n */\nstatic inline unsigned int\ncmap_thread_id(const lcore_t *lc)\n{\n    return lc->u.tid;\n}\n\n/**\n * Return the count of unique values for a given topology property.\n *\n * Walks the lcore linked list and returns (max_value + 1) for the\n * property selected by @p get (e.g. socket ID, core ID, or thread ID).\n *\n * @param lc\n *   Head of the singly-linked lcore list.\n * @param get\n *   Getter function that extracts the property to count from an lcore_t.\n * @return\n *   Number of unique values (max observed value + 1), or 0 if @p get is NULL.\n */\nstatic inline unsigned int\ncmap_cnt(lcore_t *lc, getter_fn get)\n{\n    unsigned cnt = 0;\n\n    if (!get)\n        return cnt;\n\n    while (lc) {\n        if (cnt < get(lc))\n            cnt = get(lc);\n        lc = lc->next;\n    }\n    return cnt + 1;\n}\n\n#endif /*_CLI_CMAP_H */\n"
  },
  {
    "path": "lib/cli/cli_cmds.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#include <stdio.h>\n\n#include <rte_version.h>\n#include <rte_cycles.h>\n#include <rte_timer.h>\n#include <rte_devargs.h>\n#include <rte_pci.h>\n#include <rte_debug.h>\n#include <rte_log.h>\n#include <rte_string_fns.h>\n#include <pg_strings.h>\n#include <pg_delay.h>\n\n#include \"cli.h\"\n#include \"cli_input.h\"\n#include \"cli_cmds.h\"\n#include \"cli_cmap.h\"\n#include \"cli_map.h\"\n#include \"cli_file.h\"\n#include \"cli_help.h\"\n\nstatic int\n__print_help(struct cli_node *node, char *search)\n{\n    struct cli_node *cmd;\n\n    if (!node)\n        node = get_cwd();\n    else if (!is_directory(node))\n        return -1;\n\n    TAILQ_FOREACH (cmd, &node->items, next) {\n        if (is_executable(cmd)) {\n            if (search) {\n                if (strcmp(cmd->name, search) == 0) {\n                    cli_printf(\"  %-16s %s\\n\", cmd->name, cmd->short_desc);\n                    return 1;\n                }\n            } else\n                cli_printf(\"  %-16s %s\\n\", cmd->name, cmd->short_desc);\n        }\n    }\n    return 0;\n}\n\nstatic int\nchelp_cmd(int argc, char **argv)\n{\n    struct cli *cli = this_cli;\n    struct cli_node *bin;\n    char *search = NULL;\n    int i, opt, all = 0;\n\n    optind = 0;\n    while ((opt = getopt(argc, argv, \"a?\")) != -1) {\n        switch (opt) {\n        case '?':\n            cli_usage();\n            return 0;\n        case 'a':\n            all = 1;\n            break;\n        default:\n            break;\n        }\n    }\n    if (optind < argc)\n        search = argv[optind];\n\n    cli_printf(\"*** CLI Help ***\\n\");\n    cli_printf(\"  Use <command> -? to show usage for a command\\n\");\n    cli_printf(\"  Use !<NN> to execute a history line\\n\");\n    cli_printf(\"  Use @<host command> to execute a host binary\\n\");\n    cli_printf(\"  Use Up/Down arrows to access history commands\\n\\n\");\n    cli_printf(\"  Use 'chelp -a' to list all commands\\n\");\n\n    if (all == 0) {\n        /* Look in the current directory first for a command */\n        cli_printf(\"*** Current directory commands ***\\n\");\n\n        return __print_help(NULL, search);\n    }\n\n    cli_printf(\"*** All executable commands in path ***\\n\");\n\n    /* Did not find a command in local then look in the bin dirs */\n    for (i = 0; i < CLI_MAX_BINS; i++) {\n        bin = cli->bins[i];\n        if (bin == NULL)\n            continue;\n\n        cli_printf(\"%s:\\n\", bin->name);\n\n        if (__print_help(bin, search))\n            return 0;\n    }\n\n    return 0;\n}\n\nstatic int\ncd_cmd(int argc, char **argv)\n{\n    struct cli_node *node;\n\n    if (argc > 1) {\n        if (!strcmp(argv[1], \"-?\")) {\n            cli_usage();\n            return 0;\n        }\n\n        if (!cli_find_node(argv[1], &node)) {\n            cli_printf(\"** Invalid directory: %s\\n\", argv[1]);\n            return -1;\n        }\n        set_cwd(node);\n    }\n\n    return 0;\n}\n\nstatic int\npwd_cmd(int argc, char **argv)\n{\n    char *str = cli_cwd_path();\n\n    if (argc > 1 && !strcmp(argv[1], \"-?\")) {\n        cli_usage();\n        return 0;\n    }\n\n    /* trim off the trailing '/' if needed */\n    if (strlen(str) > 1)\n        str[strlen(str) - 1] = '\\0';\n\n    cli_printf(\"%s\\n\", str);\n    return 0;\n}\n\nstatic int\n__list_long_dir(struct cli_node *node, uint32_t type __rte_unused, args_t *args)\n{\n    uint16_t flags = args->arg1.u16[3];\n    uint16_t spc   = args->arg2.u16[0];\n\n    if (is_alias(node))\n        cli_printf(\"  %*s%-16s %s : %s\\n\", spc, \"\", node->name, cli_node_type(node),\n                   node->alias_str);\n    else if (is_command(node))\n        cli_printf(\"  %*s%-16s %s : %s\\n\", spc, \"\", node->name, cli_node_type(node),\n                   node->short_desc);\n    else\n        cli_printf(\"  %*s%-16s %s\\n\", spc, \"\", node->name, cli_node_type(node));\n\n    if ((flags & CLI_RECURSE_FLAG) && is_directory(node)) {\n        args->arg2.u16[0] += 2;\n        cli_scan_directory(node, __list_long_dir, type, args);\n        args->arg2.u16[0] = spc;\n    }\n\n    return 0;\n}\n\nstatic int\n__list_dir(struct cli_node *node, uint32_t flag __rte_unused, args_t *args)\n\n{\n    char buf[CLI_NAME_LEN + 1];\n    uint16_t cnt   = args->arg1.u16[0];\n    uint16_t mlen  = args->arg1.u16[1];\n    uint16_t col   = args->arg1.u16[2];\n    uint16_t flags = args->arg1.u16[3];\n\n    if (!node)\n        return -1;\n\n    if (is_directory(node)) {\n        char dbuf[CLI_NAME_LEN + 1];\n        snprintf(dbuf, sizeof(dbuf), \"[%s]\", node->name);\n        snprintf(buf, sizeof(buf), \"%-*s\", mlen, dbuf);\n    } else\n        snprintf(buf, sizeof(buf), \"%-*s\", mlen, node->name);\n\n    cli_printf(\"%s\", buf);\n    if ((++cnt % col) == 0)\n        cli_printf(\"\\n\");\n\n    if ((flags & CLI_RECURSE_FLAG) && is_directory(node)) {\n        cli_printf(\"\\n\");\n        args->arg1.u16[0] = 0;\n        cli_scan_directory(node, __list_dir, CLI_ALL_TYPE, args);\n        args->arg1.u16[0] = cnt;\n        cli_printf(\"\\n\");\n    }\n\n    args->arg1.u16[0] = cnt;\n    return 0;\n}\n\nstatic int\nls_cmd(int argc, char **argv)\n{\n    struct cli_node *node = get_cwd();\n    args_t args;\n    uint32_t flags = 0;\n    int opt;\n\n    optind = 0;\n    while ((opt = getopt(argc, argv, \"?rl\")) != -1) {\n        switch (opt) {\n        case '?':\n            cli_usage();\n            return 0;\n        case 'r':\n            flags |= CLI_RECURSE_FLAG;\n            break;\n        case 'l':\n            flags |= CLI_LONG_LIST_FLAG;\n            break;\n        default:\n            break;\n        }\n    }\n\n    if (optind < argc)\n        if (cli_find_node(argv[optind], &node) == 0) {\n            cli_printf(\"Invalid directory (%s)!!\\n\", argv[optind]);\n            return -1;\n        }\n\n    memset(&args, 0, sizeof(args));\n\n    args.arg1.u16[0] = 0;\n    args.arg1.u16[1] = 16;\n    args.arg1.u16[2] = 80 / 16;\n    args.arg1.u16[3] = flags;\n    args.arg2.u16[0] = 0;\n\n    if (flags & CLI_LONG_LIST_FLAG)\n        cli_scan_directory(node, __list_long_dir, CLI_ALL_TYPE, &args);\n    else\n        cli_scan_directory(node, __list_dir, CLI_ALL_TYPE, &args);\n\n    cli_printf(\"\\n\");\n    return 0;\n}\n\nstatic int\nscrn_cmd(int argc __rte_unused, char **argv __rte_unused)\n{\n    cli_clear_screen();\n    return 0;\n}\n\nstatic int\nquit_cmd(int argc __rte_unused, char **argv __rte_unused)\n{\n    cli_quit();\n    return 0;\n}\n\nstatic int\nhist_cmd(int argc, char **argv)\n{\n    if (argc > 1 && !strcmp(argv[1], \"-?\"))\n        cli_usage();\n    else\n        cli_history_list();\n    return 0;\n}\n\nstatic int\nmore_cmd(int argc, char **argv)\n{\n    struct cli_node *node;\n    char buf[513], c;\n    int i, len, n, k, lines = 24;\n    int opt;\n\n    optind = 0;\n    while ((opt = getopt(argc, argv, \"?n:\")) != -1) {\n        switch (opt) {\n        case '?':\n            cli_usage();\n            return 0;\n        case 'n':\n            lines = atoi(optarg);\n            break;\n        default:\n            break;\n        }\n    }\n\n    if (optind >= argc)\n        return 0;\n\n    len = (int)(sizeof(buf) - 1);\n    memset(buf, '\\0', sizeof(buf));\n\n    for (i = optind; i < argc; i++) {\n        k    = 0;\n        node = cli_file_open(argv[i], \"r\");\n        if (!node) {\n            cli_printf(\"** (%s) is not a file\\n\", argv[i]);\n            continue;\n        }\n        do {\n            n = cli_readline(node, buf, len);\n            if (n > 0)\n                cli_printf(\"%s\", buf); /* contains a newline */\n            if (++k >= lines) {\n                k = 0;\n                c = cli_pause(\"More\", NULL);\n                if ((c == vt100_escape) || (c == 'q') || (c == 'Q'))\n                    break;\n            }\n        } while (n > 0);\n        cli_file_close(node);\n    }\n\n    cli_printf(\"\\n\");\n\n    return 0;\n}\n\n/* Helper for building log strings.\n * The macro takes an existing string, a printf-like format string and optional\n * arguments. It formats the string and appends it to the existing string, while\n * avoiding possible buffer overruns.\n */\n#define strncatf(dest, fmt, ...)                               \\\n    do {                                                       \\\n        char _buff[1024];                                      \\\n        snprintf(_buff, sizeof(_buff), fmt, ##__VA_ARGS__);    \\\n        strncat(dest, _buff, sizeof(dest) - strlen(dest) - 1); \\\n    } while (0)\n\nstatic __inline__ uint8_t\nsct(struct cmap *cm, uint8_t s, uint8_t c, uint8_t t)\n{\n    lc_info_t *lc = cm->linfo;\n    uint8_t i;\n\n    for (i = 0; i < cm->num_cores; i++, lc++)\n        if (lc->sid == s && lc->cid == c && lc->tid == t)\n            return lc->lid;\n\n    return 0;\n}\n\nstatic int\ncore_cmd(int argc __rte_unused, char **argv __rte_unused)\n{\n    struct cmap *c;\n    int i;\n\n    c = cmap_create();\n\n    cli_printf(\"CPU : %s\", c->model);\n    cli_printf(\"      %d lcores, %u socket%s, %u core%s per socket and \"\n               \"%u thread%s per core\\n\",\n               c->num_cores, c->sid_cnt, c->sid_cnt > 1 ? \"s\" : \"\", c->cid_cnt,\n               c->cid_cnt > 1 ? \"s\" : \"\", c->tid_cnt, c->tid_cnt > 1 ? \"s\" : \"\");\n\n    cli_printf(\"Socket     : \");\n    for (i = 0; i < c->sid_cnt; i++)\n        cli_printf(\"%4d      \", i);\n    cli_printf(\"\\n\");\n\n    for (i = 0; i < c->cid_cnt; i++) {\n        cli_printf(\"  Core %3d : [%2d,%2d]   \", i, sct(c, 0, i, 0), sct(c, 0, i, 1));\n        if (c->sid_cnt > 1)\n            cli_printf(\"[%2d,%2d]   \", sct(c, 1, i, 0), sct(c, 1, i, 1));\n        if (c->sid_cnt > 2)\n            cli_printf(\"[%2d,%2d]   \", sct(c, 2, i, 0), sct(c, 2, i, 1));\n        if (c->sid_cnt > 3)\n            cli_printf(\"[%2d,%2d]   \", sct(c, 3, i, 0), sct(c, 3, i, 1));\n        cli_printf(\"\\n\");\n    }\n\n    cmap_free(c);\n\n    return 0;\n}\n\nstatic int\nhuge_cmd(int argc __rte_unused, char **argv __rte_unused)\n{\n    if (system(\"cat /proc/meminfo | grep -i huge\"))\n        return -1;\n    return 0;\n}\n\n#ifdef CLI_DEBUG_CMDS\nstatic int\nsizes_cmd(int argc, char **argv)\n{\n    if (argc > 1 && !strcmp(argv[1], \"-?\")) {\n        cli_usage();\n        return 0;\n    }\n\n    cli_printf(\"  sizeof(struct cli)      %zu\\n\", sizeof(struct cli));\n    cli_printf(\"  sizeof(struct cli_node) %zu\\n\", sizeof(struct cli_node));\n    cli_printf(\"  sizeof(args_t)          %zu\\n\", sizeof(args_t));\n    cli_printf(\"  Total number of Nodes   %d\\n\", this_cli->nb_nodes);\n    cli_printf(\"  Number History lines    %d\\n\", this_cli->nb_hist);\n    cli_printf(\"  CLI_DEFAULT_NB_NODES    %d\\n\", CLI_DEFAULT_NB_NODES);\n    cli_printf(\"  CLI_DEFAULT_HIST_LINES  %d\\n\", CLI_DEFAULT_HIST_LINES);\n    cli_printf(\"  CLI_MAX_SCRATCH_LENGTH  %d\\n\", CLI_MAX_SCRATCH_LENGTH);\n    cli_printf(\"  CLI_MAX_PATH_LENGTH     %d\\n\", CLI_MAX_PATH_LENGTH);\n    cli_printf(\"  CLI_NAME_LEN            %d\\n\", CLI_NAME_LEN);\n    cli_printf(\"  CLI_MAX_ARGVS           %d\\n\", CLI_MAX_ARGVS);\n    cli_printf(\"  CLI_MAX_BINS            %d\\n\", CLI_MAX_BINS);\n\n    return 0;\n}\n#endif\n\nstatic int\npath_cmd(int argc __rte_unused, char **argv __rte_unused)\n{\n    int i;\n    char *str;\n\n    cli_printf(\"  Path = .:\");\n    for (i = 1; i < CLI_MAX_BINS; i++) {\n        if (this_cli->bins[i] == NULL)\n            continue;\n        str = cli_path_string(this_cli->bins[i], NULL);\n\n        /* trim off the trailing '/' if needed */\n        if (strlen(str) > 1)\n            str[strlen(str) - 1] = '\\0';\n\n        cli_printf(\"%s:\", str);\n    }\n    cli_printf(\"\\n\");\n\n    return 0;\n}\n\nstatic const char *copyright =\n    \"   BSD LICENSE\\n\"\n    \"\\n\"\n    \"   Copyright(c) <2010-2026> Intel Corporation. All rights reserved.\\n\"\n    \"\\n\"\n    \"   Redistribution and use in source and binary forms, with or without\\n\"\n    \"   modification, are permitted provided that the following conditions\\n\"\n    \"   are met:\\n\"\n    \"\\n\"\n    \"     * Redistributions of source code must retain the above copyright\\n\"\n    \"       notice, this list of conditions and the following disclaimer.\\n\"\n    \"     * Redistributions in binary form must reproduce the above copyright\\n\"\n    \"       notice, this list of conditions and the following disclaimer in\\n\"\n    \"       the documentation and/or other materials provided with the\\n\"\n    \"       distribution.\\n\"\n    \"     * Neither the name of Intel Corporation nor the names of its\\n\"\n    \"       contributors may be used to endorse or promote products derived\\n\"\n    \"       from this software without specific prior written permission.\\n\"\n    \"\\n\"\n    \"   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\\n\"\n    \"   \\\"AS IS\\\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\\n\"\n    \"   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\\n\"\n    \"   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\\n\"\n    \"   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\\n\"\n    \"   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\\n\"\n    \"   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\\n\"\n    \"   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\\n\"\n    \"   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\\n\"\n    \"   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\\n\"\n    \"   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\\n\"\n    \"\\n\"\n    \"   SPDX-License-Identifier: BSD-3-Clause\\n\";\n\nstatic int\ncopyright_file(struct cli_node *node, char *buff, int len, uint32_t flags)\n{\n\n    if (is_file_open(flags)) {\n        node->file_data = (char *)(uintptr_t)copyright;\n        node->file_size = strlen(copyright);\n        node->fflags    = CLI_DATA_RDONLY;\n        if (is_file_eq(flags, (CLI_FILE_APPEND | CLI_FILE_WR)))\n            node->foffset = node->file_size;\n        return 0;\n    }\n    return cli_file_handler(node, buff, len, flags);\n}\n\nstatic int\nversion_file(struct cli_node *node, char *buff, int len, uint32_t flags)\n{\n    const char *data = rte_version();\n\n    if (is_file_open(flags)) {\n        node->file_data = (char *)(uintptr_t)data;\n        node->file_size = strlen(data);\n        node->fflags    = CLI_DATA_RDONLY;\n        if (is_file_eq(flags, (CLI_FILE_APPEND | CLI_FILE_WR)))\n            node->foffset = node->file_size;\n        return 0;\n    }\n    return cli_file_handler(node, buff, len, flags);\n}\n\nstatic int\nsleep_cmd(int argc __rte_unused, char **argv)\n{\n    uint32_t cnt = (atoi(argv[1]) * 4);\n\n    if (rte_get_timer_hz() == 0) {\n        cli_printf(\"pktgen_get_timer_hz() returned zero\\n\");\n        return 0;\n    }\n\n    while (cnt--)\n        rte_delay_us_sleep(250 * 1000);\n\n    return 0;\n}\n\nstatic int\ndelay_cmd(int argc __rte_unused, char **argv)\n{\n    int ms  = atoi(argv[1]);\n    int cnt = (ms / 1000) * 4;\n\n    while (cnt--) {\n        rte_delay_us_sleep(250 * 1000);\n        ms -= 250;\n    }\n    if (ms > 0)\n        rte_delay_us_sleep(ms * 1000);\n    return 0;\n}\n\nstatic int\nmkdir_cmd(int argc, char **argv)\n{\n    if (argc != 2) {\n        cli_printf(\"Must have at least one path/directory\\n\");\n        return -1;\n    }\n\n    if (!cli_add_dir(argv[1], get_cwd()))\n        return -1;\n    return 0;\n}\n\nstatic int\nrm_cmd(int argc, char **argv)\n{\n    struct cli_node *node;\n\n    if (argc != 2) {\n        cli_printf(\"usage: rm [dir|file|command]\\n\");\n        return -1;\n    }\n\n    if (!cli_find_node(argv[1], &node)) {\n        cli_printf(\"Unable to find: %s\\n\", argv[1]);\n        return -1;\n    }\n\n    return cli_remove_node(node);\n}\n\nstatic char *\nver_cmd(const char *val __rte_unused)\n{\n    return (char *)(uintptr_t)rte_version();\n}\n\nstatic struct cli_map cli_env_map[] = {\n    {10, \"env\"}, {20, \"env get %s\"}, {30, \"env set %s %s\"}, {40, \"env del %s\"}, {-1, NULL}};\n\n// clang-format off\nstatic const char *cli_env_help[] = {\n    \"env                       - Display current environment variables\",\n    \"env get <string>          - Get the requested variable\",\n    \"env set <string> <string> - Set the given variable to string\",\n    \"env del <string>          - Delete the given variable\",\n    NULL\n};\n// clang-format on\nstatic int\nenv_cmd(int argc, char **argv)\n{\n    struct cli_map *m;\n\n    m = cli_mapping(cli_env_map, argc, argv);\n    if (!m)\n        return cli_cmd_error(\"Environment command error:\", \"Env\", argc, argv);\n    switch (m->index) {\n    case 10:\n        cli_env_show(this_cli->env);\n        break;\n    case 20:\n        cli_printf(\"  \\\"%s\\\" = \\\"%s\\\"\\n\", argv[2], cli_env_get(this_cli->env, argv[2]));\n        break;\n    case 30:\n        cli_env_set(this_cli->env, argv[2], argv[3]);\n        break;\n    case 40:\n        cli_env_del(this_cli->env, argv[2]);\n        break;\n    default:\n        cli_help_show_group(\"Env\");\n        return -1;\n    }\n    return 0;\n}\n\nstatic int\nscript_cmd(int argc, char **argv)\n{\n    if (argc <= 1)\n        return -1;\n\n    for (int i = 1; i < argc; i++)\n        if (cli_execute_cmdfile(argv[1]))\n            return -1;\n    return 0;\n}\n\nstatic int\necho_cmd(int argc, char **argv)\n{\n    for (int i = 1; i < argc; i++)\n        cli_printf(\"%s \", argv[i]);\n    cli_printf(\"\\n\");\n    return 0;\n}\n\nstatic int\nversion_cmd(int argc __rte_unused, char **argv __rte_unused)\n{\n    cli_printf(\"Version: %s\\n\", rte_version());\n    return 0;\n}\n\n// clang-format off\nstatic struct cli_tree cli_default_tree[] = {\n    c_file(\"copyright\", copyright_file, \"DPDK copyright information\"),\n    c_file(\"dpdk-version\", version_file, \"DPDK version\"),\n    c_bin(\"/sbin\"),\n\n    c_cmd(\"delay\", delay_cmd, \"delay a number of milliseconds\"),\n    c_cmd(\"sleep\", sleep_cmd, \"delay a number of seconds\"),\n    c_cmd(\"chelp\", chelp_cmd, \"CLI help - display information for DPDK\"),\n    c_cmd(\"mkdir\", mkdir_cmd, \"create a directory\"),\n    c_cmd(\"rm\", rm_cmd, \"remove a file or directory\"),\n    c_cmd(\"ls\", ls_cmd, \"ls [-lr] <dir> # list current directory\"),\n    c_cmd(\"cd\", cd_cmd, \"cd <dir> # change working directory\"),\n    c_cmd(\"pwd\", pwd_cmd, \"pwd # display current working directory\"),\n    c_cmd(\"screen.clear\", scrn_cmd, \"screen.clear # clear the screen\"),\n    c_cmd(\"quit\", quit_cmd, \"quit # quit the application\"),\n    c_alias(\"exit\", \"quit\", \"exit # exit the application\"),\n    c_cmd(\"history\", hist_cmd, \"history # display the current history\"),\n    c_cmd(\"more\", more_cmd, \"more <file> # display a file content\"),\n#ifdef CLI_DEBUG_CMDS\n    c_cmd(\"sizes\", sizes_cmd, \"sizes # display some internal sizes\"),\n#endif\n    c_cmd(\"cmap\", core_cmd, \"cmap # display the core mapping\"),\n    c_cmd(\"hugepages\", huge_cmd, \"hugepages # display hugepage info\"),\n    c_cmd(\"path\", path_cmd, \"display the execution path for commands\"),\n    c_cmd(\"env\", env_cmd, \"Show/del/get/set environment variables\"),\n    c_cmd(\"script\", script_cmd, \"load and process cli command files\"),\n    c_cmd(\"echo\", echo_cmd, \"simple echo a string to the screen\"),\n    c_cmd(\"version\", version_cmd, \"Display version information\"),\n\n    /* The following are environment variables */\n    c_str(\"SHELL\", NULL, \"CLI shell\"),\n    c_str(\"DPDK_VER\", ver_cmd, \"\"),\n    c_end()\n};\n// clang-format on\n\nint\ncli_default_tree_init(void)\n{\n    int ret = 0;\n\n    if (this_cli->flags & CLI_DEFAULT_TREE)\n        return ret;\n\n    this_cli->flags |= CLI_DEFAULT_TREE;\n\n    /* Add the list of commands/dirs in cli_cmds.c file */\n    if ((ret = cli_add_tree(NULL, cli_default_tree)) == 0) {\n        cli_help_add(\"Env\", cli_env_map, cli_env_help);\n    }\n\n    if (ret) {\n        RTE_LOG(ERR, EAL, \"Unable to add commands or directories\\n\");\n        this_cli->flags &= ~CLI_DEFAULT_TREE;\n    }\n\n    return ret;\n}\n"
  },
  {
    "path": "lib/cli/cli_cmds.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#ifndef _CLI_CMDS_H_\n#define _CLI_CMDS_H_\n\n/**\n * @file\n * CLI built-in command tree.\n *\n * Provides helpers to populate the default directory structure and common\n * built-in commands (e.g., ls/cd/pwd/help/history).\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Add the default set of directories and commands.\n *\n * @note Uses a thread variable called this_cli\n *\n * @return\n *   0 is ok, -1 is error\n */\nint cli_default_tree_init(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CLI_CMDS_H_ */\n"
  },
  {
    "path": "lib/cli/cli_common.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#ifndef _CLI_COMMON_H_\n#define _CLI_COMMON_H_\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdarg.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <inttypes.h>\n\n#include <cli_scrn.h>\n\n/**\n * @file\n * CLI common output helpers.\n *\n * Provides cli_printf(), the primary console output routine used throughout\n * the CLI library, routed through the active cli_scrn file descriptor.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifndef RTE_ASSERT\n#define RTE_ASSERT RTE_VERIFY\n#endif\n\n/**\n * printf-like routine to write formatted text to the CLI console.\n *\n * Output is written to this_scrn->fd_out and flushed immediately.\n *\n * @param fmt\n *   printf-compatible format string.\n * @param ...\n *   Variable arguments for @p fmt.\n * @return\n *   Number of characters written, as returned by vfprintf().\n */\n\nstatic inline int __attribute__((format(printf, 1, 2)))\ncli_printf(const char *fmt, ...)\n{\n    va_list vaList;\n    int n;\n\n    va_start(vaList, fmt);\n    n = vfprintf(this_scrn->fd_out, fmt, vaList);\n    va_end(vaList);\n\n    fflush(this_scrn->fd_out);\n\n    return n;\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CLI_COMMON_H_ */\n"
  },
  {
    "path": "lib/cli/cli_env.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n/* Created by: Keith Wiles @ intel */\n\n/**\n * @file\n * CLI environment variable implementation.\n */\n#include <stdlib.h>\n\n#include \"cli.h\"\n#include \"cli_env.h\"\n\nstatic int\nenv_free(struct cli_env *env, struct env_node *n)\n{\n    if (!env || !n)\n        return -1;\n\n    TAILQ_REMOVE(&env->head, n, next);\n\n    free((char *)(uintptr_t)n->var);\n    free((char *)(uintptr_t)n->val);\n    free(n);\n    env->count--;\n\n    return 0;\n}\n\nstruct cli_env *\ncli_env_create(void)\n{\n    struct cli_env *env;\n\n    env = (struct cli_env *)calloc(1, sizeof(struct cli_env));\n    if (!env)\n        return NULL;\n    memset(env, '\\0', sizeof(struct cli_env));\n\n    TAILQ_INIT(&env->head);\n\n    return env;\n}\n\nvoid\ncli_env_destroy(struct cli_env *env)\n{\n    struct env_node *n;\n\n    if (!env)\n        return;\n\n    while (!TAILQ_EMPTY(&env->head)) {\n        n = TAILQ_FIRST(&env->head);\n        env_free(env, n);\n    }\n    free(env);\n}\n\nstatic struct env_node *\nfind_env(struct cli_env *env, const char *var)\n{\n    struct env_node *n;\n\n    TAILQ_FOREACH (n, &env->head, next) {\n        if (!strcmp(var, n->var))\n            return n;\n    }\n    return NULL;\n}\n\nstatic struct env_node *\n__env_set(struct cli_env *env, const char *var, const char *val)\n{\n    struct env_node *n;\n    const char *safe_val;\n\n    if (!env || !var)\n        return NULL;\n\n    safe_val = (val) ? val : \"\";\n\n    n = find_env(env, var);\n    if (n) {\n        char *new_val = strdup(safe_val);\n\n        if (!new_val)\n            return NULL;\n\n        free((char *)(uintptr_t)n->val);\n        n->val = new_val;\n        return n;\n    }\n\n    n = (struct env_node *)calloc(1, sizeof(struct env_node));\n    if (!n)\n        return NULL;\n\n    n->var = strdup(var);\n    n->val = strdup(safe_val);\n    if (!n->var || !n->val) {\n        free((char *)(uintptr_t)n->var);\n        free((char *)(uintptr_t)n->val);\n        free(n);\n        return NULL;\n    }\n\n    TAILQ_INSERT_TAIL(&env->head, n, next);\n    env->count++;\n\n    return n;\n}\n\nint\ncli_env_set(struct cli_env *env, const char *var, const char *val)\n{\n    return (__env_set(env, var, val) == NULL) ? -1 : 0;\n}\n\nint\ncli_env_string(struct cli_env *env, const char *var, cli_sfunc_t sfunc, const char *val)\n{\n    struct env_node *n;\n\n    n = __env_set(env, var, val);\n    if (!n)\n        return -1;\n    n->sfunc = sfunc;\n    return 0;\n}\n\nconst char *\ncli_env_get(struct cli_env *env, const char *var)\n{\n    struct env_node *n;\n\n    n = find_env(env, var);\n    if (!n)\n        return NULL;\n\n    return (n->sfunc) ? n->sfunc(n->val) : n->val;\n}\n\nint\ncli_env_del(struct cli_env *env, const char *var)\n{\n    return env_free(env, find_env(env, var));\n}\n\n/* strings to be substituted are of the form ${foo} or $(foo) */\nvoid\ncli_env_substitution(struct cli_env *env, char *line, int sz)\n{\n    char *p, *s, *e, *t, *tmp;\n    const char *v;\n    size_t remaining;\n\n    if (!env || !line || sz <= 0)\n        return;\n\n    tmp = calloc(1, (size_t)sz + 1);\n    if (!tmp)\n        return;\n\n    /* Determine the end of the string */\n    e = line + sz;\n\n    remaining = (size_t)sz;\n\n    for (p = line, t = tmp; (p[0] != '\\0') && (p < e); p++) {\n        /* Look for the '$' then the open bracket */\n        if (p[0] != '$')\n            goto next;\n\n        /* find opening bracket */\n        if ((p[1] != '{') && (p[1] != '('))\n            goto next;\n\n        /* find closing bracket */\n        s = strchr(p, (p[1] == '{') ? '}' : ')');\n        if (!s)\n            goto next;\n\n        /* terminate the variable string */\n        *s = '\\0';\n\n        v = cli_env_get(env, &p[2]);\n        if (!v)\n            v = \"oops!\";\n\n        if (remaining > 1) {\n            size_t vlen = strnlen(v, remaining - 1);\n            memcpy(t, v, vlen);\n            t += vlen;\n            remaining -= vlen;\n        }\n        p = s; /* Point 'p' past the variable */\n        continue;\n    next:\n        if (remaining <= 1)\n            break;\n        *t++ = *p;\n        remaining--;\n    }\n    *t = '\\0';\n\n    snprintf(line, sz, \"%s\", tmp);\n\n    free(tmp);\n}\n\nint\ncli_env_get_all(struct cli_env *env, struct env_node **list, int max_size)\n{\n    struct env_node *node;\n    int n = 0;\n\n    if (!env)\n        return 0;\n\n    TAILQ_FOREACH (node, &env->head, next) {\n        list[n++] = node;\n        if (n == max_size)\n            break;\n    }\n\n    return n;\n}\n\nvoid\ncli_env_show(struct cli_env *env)\n{\n    struct env_node *node;\n\n    TAILQ_FOREACH (node, &env->head, next) {\n        if (node->sfunc)\n            cli_printf(\"  \\\"%s\\\" = \\\"%s\\\"\\n\", node->var, node->sfunc(node->val));\n        else\n            cli_printf(\"  \\\"%s\\\" = \\\"%s\\\"\\n\", node->var, node->val);\n    }\n}\n"
  },
  {
    "path": "lib/cli/cli_env.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n/* Created by Keith Wiles @ intel.com */\n\n#include <sys/queue.h>\n\n#ifndef _CLI_ENV_H_\n#define _CLI_ENV_H_\n\n/**\n * @file\n * CLI environment variables.\n *\n * Supports simple key/value variables and dynamic variables backed by a\n * callback. Variables can be expanded in user input (e.g. $(FOO)).\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nstruct cli;\n\ntypedef char *(*cli_sfunc_t)(const char *str);\n/**< Callback to compute a dynamic environment variable string. */\n\nstruct env_node {\n    TAILQ_ENTRY(env_node) next;\n    const char *var;   /**< Variable name */\n    const char *val;   /**< Variable value (owned by env) */\n    cli_sfunc_t sfunc; /**< Optional callback to compute value */\n};\n\nstruct cli_env {\n    TAILQ_HEAD(, env_node) head; /**< link list of vars */\n    int count;                   /**< Number of variables */\n};\n\n/**\n * Create an environment variable container.\n *\n * @return\n *   NULL on error or cli_env pointer\n */\nstruct cli_env *cli_env_create(void);\n\n/**\n * Destroy an environment variable container.\n *\n * @param env\n *   Pointer to the environment structure\n */\nvoid cli_env_destroy(struct cli_env *env);\n\n/**\n * Set an environment variable.\n *\n * Adds or replaces a variable with a string value.\n *\n * @param env\n *   The cli_env pointer\n * @param var\n *   Pointer to the variable name const string\n * @param val\n *   Pointer to the string assigned to the variable\n * @return\n *   0 is OK was added or replaced or -1 if not valid\n */\nint cli_env_set(struct cli_env *env, const char *var, const char *val);\n\n/**\n * Set an environment variable with an optional callback.\n *\n * If @p sfunc is non-NULL, it may be used to generate a dynamic string value.\n *\n * @param env\n *   The cli_env pointer\n * @param var\n *   Pointer to the variable name const string.\n * @param sfunc\n *   Pointer to function (optional)\n * @param val\n *   Pointer to the string assigned to the variable\n * @return\n *   0 is OK was added or replaced or -1 if not valid\n */\nint cli_env_string(struct cli_env *env, const char *var, cli_sfunc_t sfunc, const char *val);\n\n/**\n * Get an environment variable value.\n *\n * @param env\n *   The cli_env pointer\n * @param var\n *   The const string variable name\n * @return\n *   NULL if not found or the const string\n */\nconst char *cli_env_get(struct cli_env *env, const char *var);\n\n/**\n * Remove an environment variable.\n *\n * @param env\n *   The cli_env pointer\n * @param var\n *   The const string variable name\n * @return\n *   0 is OK or -1 if not found.\n */\nint cli_env_del(struct cli_env *env, const char *var);\n\n/**\n * Perform environment variable substitution on a command line.\n *\n * Expands occurrences of $(VAR) using values in @p env.\n *\n * @param env\n *   Pointer to the enviroment structure\n * @param line\n *   Pointer to the line to parse\n * @param sz\n *   Number of total characters the line can hold.\n * @return\n *   N/A\n */\nvoid cli_env_substitution(struct cli_env *env, char *line, int sz);\n\n/**\n * Get the number of variables in the environment.\n *\n * @param env\n *   Pointer to environment structure\n * @return\n *   Number of environment variables\n */\nstatic inline int\ncli_env_count(struct cli_env *env)\n{\n    return env->count;\n}\n\n/**\n * Get a snapshot list of environment variables.\n *\n * @param env\n *   Pointer to environment list\n * @param list\n *   Array of env_node pointers to be returned\n * @param max_size\n *   Max size of the list array\n */\nint cli_env_get_all(struct cli_env *env, struct env_node **list, int max_size);\n\n/**\n * Print all environment variables.\n *\n * @param env\n *   Pointer to the cli_env structure.\n */\nvoid cli_env_show(struct cli_env *env);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CLI_ENV_H_ */\n"
  },
  {
    "path": "lib/cli/cli_file.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#include \"cli.h\"\n#include \"cli_input.h\"\n#include \"cli_file.h\"\n\nstruct cli_node *\ncli_file_open(const char *path, const char *type)\n{\n    struct cli_node *node;\n    uint32_t flags = 0;\n\n    if (!path)\n        return NULL;\n\n    if (!cli_find_node(path, &node))\n        return NULL;\n\n    if (!is_file(node))\n        return NULL;\n\n    if (type && strlen(type)) {\n        if (strchr(type, 'r'))\n            file_set(flags, CLI_FILE_RD);\n        if (strchr(type, 'w'))\n            file_set(flags, CLI_FILE_WR);\n        if (strchr(type, '+'))\n            file_set(flags, CLI_FILE_APPEND);\n    } else\n        file_set(flags, CLI_FILE_RD);\n\n    file_set(flags, CLI_FILE_OPEN);\n\n    if (node->ffunc(node, NULL, 0, flags))\n        return NULL;\n\n    return node;\n}\n\nint\ncli_file_close(struct cli_node *node)\n{\n    uint32_t flags = CLI_FILE_CLOSE;\n\n    if (!node)\n        return -1;\n    return node->ffunc(node, NULL, 0, flags);\n}\n\nint\ncli_file_read(struct cli_node *node, char *buff, int len)\n{\n    uint32_t flags = CLI_FILE_RD;\n\n    if (!node || !is_file(node))\n        return -1;\n    return node->ffunc(node, buff, len, flags);\n}\n\nint\ncli_file_write(struct cli_node *node, char *buff, int len)\n{\n    uint32_t flags = CLI_FILE_WR;\n\n    if (!node || !is_file(node))\n        return -1;\n    if (is_data_rdonly(node->fflags))\n        return -1;\n    return node->ffunc(node, buff, len, flags);\n}\n\nint\ncli_file_seek(struct cli_node *node, int offset, uint32_t whence)\n{\n    if (!node || !is_file(node))\n        return -1;\n\n    switch (whence) {\n    case CLI_SEEK_SET:\n    case CLI_SEEK_CUR:\n    case CLI_SEEK_END:\n        break;\n    default:\n        return -1;\n    }\n    return node->ffunc(node, NULL, offset, whence);\n}\n\nint\ncli_readline(struct cli_node *node, char *buff, int len)\n{\n    int i, n;\n    char c;\n\n    if (!node || !buff || !is_file(node))\n        return -1;\n    /* Needs to be optimized for performance ??? */\n    for (i = 0, c = '\\0'; i < len && c != '\\n'; i++) {\n        n = cli_file_read(node, &c, 1);\n        if (n <= 0)\n            break;\n        buff[i] = c;\n    }\n    buff[i] = '\\0';\n    return i;\n}\n\n/* Add generic function for handling files */\nint\ncli_file_handler(struct cli_node *node, char *buff, int len, uint32_t opt)\n{\n    char *p;\n\n    if (!node || !is_file(node))\n        return -1;\n\n    if (opt & (CLI_SEEK_SET | CLI_SEEK_CUR | CLI_SEEK_END)) {\n        size_t saved = node->foffset;\n\n        if (is_seek_set(opt)) {\n            if (len < 0)\n                return -1;\n            node->foffset = len;\n        } else if (is_seek_cur(opt)) {\n            if (len < 0) {\n                len = abs(len);\n                if ((size_t)len > node->file_size)\n                    node->foffset = 0;\n                else\n                    node->foffset -= len;\n            } else\n                node->foffset += len;\n        } else if (is_seek_end(opt)) {\n            if (len < 0) {\n                len = abs(len);\n                if ((size_t)len > node->file_size)\n                    node->foffset = 0;\n                else\n                    node->foffset = node->file_size - len;\n            } else\n                node->foffset = node->file_size + len;\n        }\n\n        if (node->foffset > node->file_size) {\n            if (!(node->fflags & CLI_FILE_APPEND)) {\n                node->foffset = saved;\n                if (node->fflags & (CLI_FREE_DATA | CLI_DATA_EXPAND)) {\n                    char *data;\n                    data = realloc(node->file_data, node->foffset);\n                    if (!data)\n                        return -1;\n                    node->file_data = data;\n                    node->file_size = node->foffset;\n                } else /* TODO: add code to expand the file */\n                    return -1;\n            } else {\n                node->foffset = saved;\n                return -1;\n            }\n        }\n    } else if (is_seek_cur(opt))\n        node->foffset += len;\n    else if (is_seek_end(opt))\n        node->foffset += len;\n    else if (is_file_close(opt)) {\n        if (node->file_data && (node->fflags & CLI_FREE_DATA)) {\n            free(node->file_data);\n            node->file_data = NULL;\n            node->file_size = 0;\n        }\n        node->foffset = 0;\n    } else if (is_file_open(opt)) {\n        if (is_file_append(opt)) {\n            node->fflags |= CLI_FILE_APPEND;\n            node->foffset = node->file_size;\n        }\n        if (is_file_wr(opt))\n            node->fflags |= CLI_FILE_WR;\n    } else if (is_file_rd(opt)) {\n        if (len <= 0)\n            return 0;\n\n        if (node->foffset >= node->file_size)\n            return 0;\n\n        len = RTE_MIN(len, (int)(node->file_size - node->foffset));\n\n        p = node->file_data + node->foffset;\n\n        memcpy(buff, p, len);\n\n        node->foffset += len;\n    } else if (is_file_wr(opt)) {\n        if (is_data_rdonly(node->fflags))\n            return -1;\n        if (len <= 0)\n            return 0;\n        if ((node->foffset + len) < node->file_size) {\n            p = node->file_data + node->foffset;\n            memcpy(p, buff, len);\n            node->foffset += len;\n        } else {\n            char *new_data = realloc(node->file_data, (node->foffset + len));\n\n            if (!new_data)\n                return -1;\n\n            node->file_data = new_data;\n            node->file_size = node->foffset + len;\n            node->foffset += len;\n        }\n    }\n\n    return len;\n}\n\nstruct cli_node *\ncli_file_create(const char *path, const char *type)\n{\n    struct cli_node *node, *parent;\n    char *file, *mypath;\n    char *data = NULL;\n\n    node = cli_file_open(path, type);\n    if (node)\n        return node;\n\n    mypath = strdup(path);\n    if (!mypath)\n        return NULL;\n\n    file = basename(mypath);\n\n    data = calloc(1, CLI_FILE_SIZE);\n    if (data) {\n        parent = cli_last_dir_in_path(path);\n        if (parent) {\n            node = cli_add_file(file, parent, cli_file_handler, \"\");\n            if (node) {\n                node->file_data = data;\n                node->file_size = CLI_FILE_SIZE;\n                node->fflags    = CLI_FREE_DATA;\n                if (type && strchr(type, 'r') && !strchr(type, 'w') && !strchr(type, '+'))\n                    node->fflags |= CLI_DATA_RDONLY;\n                node->foffset = 0;\n                free(mypath);\n                return node;\n            }\n        }\n    }\n\n    free(mypath);\n    free(data);\n    return NULL;\n}\n\nint\ncli_system(char *p)\n{\n    char buf[1024];\n    size_t n;\n    FILE *f;\n\n    f = popen(p, \"r\");\n    if (!f)\n        return -1;\n\n    while ((n = fread(buf, 1, sizeof(buf), f)) > 0)\n        cli_write(buf, n);\n\n    pclose(f);\n\n    return n;\n}\n"
  },
  {
    "path": "lib/cli/cli_file.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#ifndef _CLI_FILE_H_\n#define _CLI_FILE_H_\n\n/**\n * @file\n * CLI file node helpers.\n *\n * Implements in-memory file nodes used by the CLI tree. File nodes can be\n * backed by a callback for dynamic content or can store data directly.\n */\n\n#include \"cli.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define CLI_FILE_SIZE 1024\n\nenum {\n    /* File operations opt */\n    CLI_FILE_RD     = 0x0001, /**< Perform a read on the file */\n    CLI_FILE_WR     = 0x0002, /**< Perform a write on the file */\n    CLI_FILE_APPEND = 0x0004, /**< Append data to the file */\n    CLI_FILE_OPEN   = 0x0008, /**< Open the file */\n    CLI_FILE_CLOSE  = 0x0010, /**< Close the file */\n    CLI_FILE_CREATE = 0x0020, /**< Create a new file */\n\n    /* File seek operations */\n    CLI_SEEK_SET = 0x0100, /**< Seek to an absolute offset */\n    CLI_SEEK_CUR = 0x0200, /**< Seek relative to the current position */\n    CLI_SEEK_END = 0x0400, /**< Seek relative to the end of the file */\n\n    /* File information in cli_node.fflags */\n    CLI_DATA_RDONLY = 0x1000, /**< File data is read-only */\n    CLI_FREE_DATA   = 0x2000, /**< File data buffer must be freed on close */\n    CLI_DATA_EXPAND = 0x4000  /**< File buffer can be expanded dynamically */\n};\n\n#define file_set(f, v) \\\n    do {               \\\n        (f) |= (v);    \\\n    } while ((0))\n#define file_clr(f, v) \\\n    do {               \\\n        (f) &= ~(v);   \\\n    } while ((0))\n\n/** Test whether any flag in @p cmpflags is set in @p opt. Non-zero if set. */\nstatic inline int\nis_file_set(uint32_t opt, uint32_t cmpflags)\n{\n    return opt & cmpflags;\n}\n\n/** Return non-zero if CLI_FILE_RD is set in @p opt. */\nstatic inline int\nis_file_rd(uint32_t opt)\n{\n    return is_file_set(opt, CLI_FILE_RD);\n}\n\n/** Return non-zero if CLI_FILE_WR is set in @p opt. */\nstatic inline int\nis_file_wr(uint32_t opt)\n{\n    return is_file_set(opt, CLI_FILE_WR);\n}\n\n/** Return non-zero if CLI_FILE_APPEND is set in @p opt. */\nstatic inline int\nis_file_append(uint32_t opt)\n{\n    return is_file_set(opt, CLI_FILE_APPEND);\n}\n\n/** Return non-zero if CLI_FILE_OPEN is set in @p opt. */\nstatic inline int\nis_file_open(uint32_t opt)\n{\n    return is_file_set(opt, CLI_FILE_OPEN);\n}\n\n/** Return non-zero if CLI_FILE_CLOSE is set in @p opt. */\nstatic inline int\nis_file_close(uint32_t opt)\n{\n    return is_file_set(opt, CLI_FILE_CLOSE);\n}\n\n/** Return non-zero if CLI_FILE_CREATE is set in @p opt. */\nstatic inline int\nis_file_create(uint32_t opt)\n{\n    return is_file_set(opt, CLI_FILE_CREATE);\n}\n\n/** Return non-zero if CLI_DATA_RDONLY is set in @p flags. */\nstatic inline int\nis_data_rdonly(uint32_t flags)\n{\n    return is_file_set(flags, CLI_DATA_RDONLY);\n}\n\n/** Return non-zero if all bits in @p cmpflags are set in @p opt. */\nstatic inline int\nis_file_eq(uint32_t opt, uint32_t cmpflags)\n{\n    return ((opt & cmpflags) == cmpflags);\n}\n\n/** Return non-zero if CLI_SEEK_SET is set in @p opt. */\nstatic inline int\nis_seek_set(uint32_t opt)\n{\n    return is_file_set(opt, CLI_SEEK_SET);\n}\n\n/** Return non-zero if CLI_SEEK_CUR is set in @p opt. */\nstatic inline int\nis_seek_cur(uint32_t opt)\n{\n    return is_file_set(opt, CLI_SEEK_CUR);\n}\n\n/** Return non-zero if CLI_SEEK_END is set in @p opt. */\nstatic inline int\nis_seek_end(uint32_t opt)\n{\n    return is_file_set(opt, CLI_SEEK_END);\n}\n\n/**\n * Open a file.\n *\n * @param path\n *   Path string for file\n * @param type\n *   Type of open string r, w, and/or + characters\n * @return\n *   Node pointer or NULL on error\n */\nstruct cli_node *cli_file_open(const char *path, const char *type);\n\n/**\n * Close a file\n *\n * @param node\n *   Pointer to file node\n * @return\n *   0 on OK and -1 on error\n */\nint cli_file_close(struct cli_node *node);\n\n/**\n * read data from a file\n *\n * @param node\n *   Pointer to file node\n * @param buff\n *   Pointer to place to put the data\n * @param len\n *   Max Number of bytes to read\n * @return\n *   Number of bytes read and -1 on error\n */\nint cli_file_read(struct cli_node *node, char *buff, int len);\n\n/**\n * write data to a file\n *\n * @param node\n *   Pointer to file node\n * @param buff\n *   Pointer to place to get the data\n * @param len\n *   Max Number of bytes to write\n * @return\n *   Number of bytes written and -1 on error\n */\nint cli_file_write(struct cli_node *node, char *buff, int len);\n\n/**\n * Seek within a CLI file node.\n *\n * @param node\n *   Pointer to file node\n * @param offset\n *   Byte offset to apply (interpretation depends on @p whence)\n * @param whence\n *   Seek mode: CLI_SEEK_SET, CLI_SEEK_CUR, or CLI_SEEK_END\n * @return\n *   Resulting file offset after the seek, or -1 on error\n */\nint cli_file_seek(struct cli_node *node, int offset, uint32_t whence);\n\n/**\n * Read one line from a CLI file node into a buffer.\n *\n * Reads characters until a newline or end-of-file. The newline is consumed\n * but not stored.\n *\n * @param node\n *   Pointer to the file node to read from\n * @param buff\n *   Buffer to store the line data\n * @param len\n *   Maximum number of bytes the buffer can hold\n * @return\n *   Number of bytes stored in @p buff (excluding the newline), or -1 on error\n */\nint cli_readline(struct cli_node *node, char *buff, int len);\n\n/**\n * create a data file in memory will be lost at reset.\n *\n * @param path\n *   Path string for file\n * @param type\n *   Type of open string r, w, and/or + characters\n * @return\n *   Node pointer or NULL on error\n */\nstruct cli_node *cli_file_create(const char *path, const char *type);\n\n/**\n * Generic file function for basic file handling\n *\n * @param node\n *   Pointer to file node\n * @param buff\n *   place to put the line data.\n * @param len\n *   Max buff size\n * @param opt\n *   Flags for file handling\n * @return\n *   Number of bytes read not including the newline\n */\nint cli_file_handler(struct cli_node *node, char *buff, int len, uint32_t opt);\n\n/**\n * Execute a host shell command string.\n *\n * Passes @p p to the system() call. Used by CLI built-in commands that\n * need to run external programs.\n *\n * @param p\n *   Null-terminated shell command string to execute.\n * @return\n *   Return value from system(), or -1 on error.\n */\nint cli_system(char *p);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CLI_FILE_H_ */\n"
  },
  {
    "path": "lib/cli/cli_gapbuf.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n/* inspired by an email/code written by: Joseph H. Allen, 9/10/89 */\n\n#include <rte_memcpy.h>\n\n#include \"cli.h\"\n\n/* Copy the gap buffer data into a user supplied buffer */\nuint32_t\ngb_copy_to_buf(struct gapbuf *gb, char *dst, uint32_t size)\n{\n    uint32_t tcnt = 0;\n    uint32_t to_copy;\n    uint32_t left_len, right_len;\n    uint32_t left_copy, right_copy;\n\n    RTE_ASSERT(gb != NULL);\n    RTE_ASSERT(dst != NULL);\n\n    /* Always NUL terminate even if size is 0 */\n    if (size == 0) {\n        dst[0] = '\\0';\n        return 0;\n    }\n\n    /* Only copy up to the smaller of request size or data size */\n    to_copy = RTE_MIN(size, gb_data_size(gb));\n\n    left_len  = (uint32_t)(gb->gap - gb->buf);\n    right_len = (uint32_t)(gb->ebuf - gb->egap);\n\n    left_copy = RTE_MIN(left_len, to_copy);\n    if (left_copy) {\n        rte_memcpy(dst, gb->buf, left_copy);\n        dst += left_copy;\n        tcnt += left_copy;\n    }\n\n    right_copy = RTE_MIN(right_len, (to_copy - left_copy));\n    if (right_copy) {\n        rte_memcpy(dst, gb->egap, right_copy);\n        dst += right_copy;\n        tcnt += right_copy;\n    }\n\n    *dst = '\\0';\n\n    return tcnt;\n}\n\nint\ngb_reset_buf(struct gapbuf *gb)\n{\n    int size;\n\n    RTE_ASSERT(gb != NULL);\n\n    size = gb_buf_size(gb);\n    memset(gb->buf, ' ', size);\n\n    gb->point = gb->buf;\n    gb->gap   = gb->buf;\n    gb->egap  = gb->buf + size;\n    gb->ebuf  = gb->egap;\n\n    return 0;\n}\n\n/* release the buffer and allocate a new buffer with correct offsets */\nint\ngb_init_buf(struct gapbuf *gb, int size)\n{\n    RTE_ASSERT(gb != NULL);\n\n    free(gb->buf);\n\n    gb->buf = calloc(1, size);\n    if (!gb->buf)\n        return -1;\n\n    gb->ebuf = gb->buf + size;\n\n    return gb_reset_buf(gb);\n}\n\n/* Create the gap buffer structure and init the pointers */\nstruct gapbuf *\ngb_create(void)\n{\n    struct gapbuf *gb;\n\n    gb = calloc(1, sizeof(struct gapbuf));\n    if (!gb)\n        return NULL;\n\n    memset(gb, '\\0', sizeof(struct gapbuf));\n\n    gb_init_buf(gb, GB_DEFAULT_GAP_SIZE);\n\n    return gb;\n}\n\n/* Release the gap buffer data and memory */\nvoid\ngb_destroy(struct gapbuf *gb)\n{\n    if (gb) {\n        free(gb->buf);\n        free(gb);\n    }\n}\n\n/* Dump out the gap buffer and pointers in a readable format */\nvoid\ngb_dump(struct gapbuf *gb, const char *msg)\n{\n#ifdef CLI_DEBUG_ENABLED\n    char *p;\n    uint32_t i;\n\n    if (msg)\n        fprintf(stderr, \"\\n%s Gap: buf_size %u, gap_size %u\\n\", msg, gb_buf_size(gb),\n                gb_gap_size(gb));\n    else\n        fprintf(stderr, \"\\nGap: buf_size %u, gap_size %u\\n\", gb_buf_size(gb), gb_gap_size(gb));\n\n    fprintf(stderr, \"     buf   %p, \", gb->buf);\n    fprintf(stderr, \"gap   %p, \", gb->gap);\n    fprintf(stderr, \"point %p, \", gb->point);\n    fprintf(stderr, \"egap  %p, \", gb->egap);\n    fprintf(stderr, \"ebuf  %p\\n\", gb->ebuf);\n\n    fprintf(stderr, \" \");\n    for (i = 0, p = gb->buf; p < gb->ebuf; i++, p++)\n        fprintf(stderr, \"%c\", \"0123456789\"[i % 10]);\n    fprintf(stderr, \"\\n\");\n    fprintf(stderr, \"<\");\n    for (p = gb->buf; p < gb->ebuf; p++)\n        fprintf(stderr, \"%c\", ((*p >= ' ') && (*p <= '~')) ? *p : '.');\n    fprintf(stderr, \">\\n \");\n    for (p = gb->buf; p <= gb->ebuf; p++) {\n        if ((p == gb->gap) && (p == gb->egap))\n            fprintf(stderr, \"*\");\n        else if (p == gb->gap)\n            fprintf(stderr, \"[\");\n        else if (p == gb->egap)\n            fprintf(stderr, \"]\");\n        else\n            fprintf(stderr, \" \");\n    }\n    fprintf(stderr, \"\\n \");\n    for (p = gb->buf; p <= gb->ebuf; p++)\n        fprintf(stderr, \"%c\", (p == gb->point) ? '^' : ' ');\n    fprintf(stderr, \"\\n\");\n    cli_redisplay_line();\n#else\n    (void)gb;\n    (void)msg;\n#endif\n}\n"
  },
  {
    "path": "lib/cli/cli_gapbuf.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n/* inspired by an email/code written by: Joseph H. Allen, 9/10/89 */\n\n#include <stdlib.h>\n#include <string.h>\n\n#include <rte_debug.h>\n\n#ifndef _CLI_GAPBUF_H_\n#define _CLI_GAPBUF_H_\n\n/**\n * @file\n * CLI Gap Buffer support\n *\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define GB_DEFAULT_GAP_SIZE 8\n\nstruct gapbuf {\n    char *buf;   /**< Pointer to start of the allocated buffer */\n    char *ebuf;  /**< Pointer one past the end of the allocated buffer */\n    char *point; /**< Current insertion/cursor position */\n    char *gap;   /**< Pointer to the start of the gap */\n    char *egap;  /**< Pointer to the end of the gap (first char after gap) */\n};\n\n/**\n * Create the Gap Buffer structure\n *\n * @return\n *   NULL on error or pointer to struct gapbuf\n */\nstruct gapbuf *gb_create(void);\n\n/**\n * Destroy\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   N/A\n */\nvoid gb_destroy(struct gapbuf *gb);\n\n/**\n * Allocate buffer and initialize, if buffer exist free and reallocate.\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @param size\n *   Init the gap buffer to the size given\n * @return\n *   0 is OK or Error\n */\nint gb_init_buf(struct gapbuf *gb, int size);\n\n/**\n * Reset the gap buffer\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   0 is OK or Error\n */\nint gb_reset_buf(struct gapbuf *gb);\n\n/**\n * Copy the buffer data into a given buffer\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @param dst\n *   Location to copy the data into\n * @param size\n *   Total number of bytes to copy\n * @return\n *   Number of bytes copied into the buffer\n */\nuint32_t gb_copy_to_buf(struct gapbuf *gb, char *dst, uint32_t size);\n\n/**\n * Print out a debug list of the Gap buffer and pointers\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @param msg\n *   Message to print out befor dump of buffer data\n * @return\n *   N/A\n */\nvoid gb_dump(struct gapbuf *gb, const char *msg);\n\n/********************************************************/\n\n/**\n * Return the number of bytes total in the buffer includes gap size\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   The number of bytes in the buffer\n */\nstatic inline uint32_t\ngb_buf_size(struct gapbuf *gb)\n{\n    return gb->ebuf - gb->buf;\n}\n\n/**\n * Return the gap size in bytes\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   Number of bytes in the gap.\n */\nstatic inline uint32_t\ngb_gap_size(struct gapbuf *gb)\n{\n    return gb->egap - gb->gap;\n}\n\n/**\n * Number of data bytes\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   Number of data bytes\n */\nstatic inline uint32_t\ngb_data_size(struct gapbuf *gb)\n{\n    return (gb->ebuf - gb->buf) - (gb->egap - gb->gap);\n}\n\n/**\n * Return the start of the buffer address\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   The pointer to the start of the buffer\n */\nstatic inline char *\ngb_start_of_buf(struct gapbuf *gb)\n{\n    return gb->buf;\n}\n\n/**\n * Return the pointer to the gap start\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   Pointer to the gap start location\n */\nstatic inline char *\ngb_start_of_gap(struct gapbuf *gb)\n{\n    return gb->gap;\n}\n\n/**\n * Return the pointer to the end of the gap\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   The end of the gap pointer\n */\nstatic inline char *\ngb_end_of_gap(struct gapbuf *gb)\n{\n    return gb->egap;\n}\n\n/**\n * Return the pointer to the end of the buffer\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   End of buffer pointer\n */\nstatic inline char *\ngb_end_of_buf(struct gapbuf *gb)\n{\n    return gb->ebuf;\n}\n\n/**\n * Return the point location\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   Pointer to point\n */\nstatic inline char *\ngb_point_at(struct gapbuf *gb)\n{\n    return gb->point;\n}\n\n/**\n * Is point at start of buffer\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   true if point is at start of buffer\n */\nstatic inline int\ngb_point_at_start(struct gapbuf *gb)\n{\n    return (gb->point == gb->buf);\n}\n\n/**\n * is point at the end of buffer\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   true if the point is at the end of buffer\n */\nstatic inline int\ngb_point_at_end(struct gapbuf *gb)\n{\n    return (gb->ebuf == gb->point);\n}\n\n/**\n * is point at start of gap\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   true if the point is at the gap start\n */\nstatic inline int\ngb_point_at_gap(struct gapbuf *gb)\n{\n    return (gb->gap == gb->point);\n}\n\n/**\n * Set point to a given index into the buffer\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @param idx\n *   Index into the buffer to put point\n * @return\n *   N/A\n */\nstatic inline void\ngb_set_point(struct gapbuf *gb, int idx)\n{\n    if (idx == -1) {\n        gb->point = gb->ebuf;\n        return;\n    }\n    gb->point = gb->buf + idx;\n    if (gb->point > gb->gap)\n        gb->point += gb->egap - gb->gap;\n}\n\n/**\n * Get offset of point\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   Offset of point from start of buffer\n */\nstatic inline int\ngb_point_offset(struct gapbuf *gb)\n{\n    if (gb->point > gb->egap)\n        return (gb->point - gb->buf) - (gb->egap - gb->gap);\n    else\n        return gb->point - gb->buf;\n}\n\n/**\n * Return true if point is at end of buffer data.\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   True if end of buffer data\n */\nstatic inline int\ngb_eof(struct gapbuf *gb)\n{\n    return (gb->point == gb->gap) ? (gb->egap == gb->ebuf) : (gb->point == gb->ebuf);\n}\n\n/********************************************************/\n\n/**\n * Move the gap to the location of the point.\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   N/A\n */\nstatic inline void\ngb_move_gap_to_point(struct gapbuf *gb)\n{\n    if (gb->point == gb->gap)\n        return;\n\n    if (gb->point == gb->egap)\n        gb->point = gb->gap;\n    else {\n        int cnt;\n\n        if (gb->point < gb->gap) {\n            cnt = gb->gap - gb->point;\n            memmove(gb->egap - cnt, gb->point, cnt);\n            gb->egap -= cnt;\n            gb->gap = gb->point;\n        } else if (gb->point > gb->egap) {\n            cnt = gb->point - gb->egap;\n            memmove(gb->gap, gb->egap, cnt);\n            gb->gap += cnt;\n            gb->egap  = gb->point;\n            gb->point = gb->gap;\n        } else { /* This case when point is between gap and egap. */\n            cnt = gb->point - gb->gap;\n            memmove(gb->gap, gb->egap, cnt);\n            gb->egap += cnt;\n            gb->gap += cnt;\n            gb->point = gb->gap;\n        }\n    }\n}\n\n/**\n * Expand the buffer by the given bytes.\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @param more\n *   The number of bytes to increase the buffer\n * @return\n *   N/A\n */\nstatic inline void\ngb_expand_buf(struct gapbuf *gb, uint32_t more)\n{\n    if (((gb->ebuf - gb->buf) + more) > gb_buf_size(gb)) {\n        char *old = gb->buf;\n\n        more = (gb->ebuf - gb->buf) + more + GB_DEFAULT_GAP_SIZE;\n\n#ifdef __GNUC__\n#if GCC_VERSION >= 120200\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wuse-after-free\"\n#endif\n#endif\n        gb->buf = (char *)realloc(gb->buf, more);\n        if (gb->buf == NULL)\n            rte_panic(\"realloc(%d) in %s failed\\n\", more, __func__);\n\n        gb->point += (gb->buf - old);\n        gb->ebuf += (gb->buf - old);\n        gb->gap += (gb->buf - old);\n        gb->egap += (gb->buf - old);\n#ifdef __GNUC__\n#if GCC_VERSION >= 120200\n#pragma GCC diagnostic pop\n#endif\n#endif\n        gb->buf = (char *)realloc(gb->buf, more);\n    }\n}\n\n/**\n * Expand the Gap by the size given.\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @param size\n *   Increase the gap by the number of bytes given\n * @return\n *   N/A\n */\nstatic inline void\ngb_expand_gap(struct gapbuf *gb, uint32_t size)\n{\n    if (size > gb_gap_size(gb)) {\n        size += GB_DEFAULT_GAP_SIZE;\n\n        gb_expand_buf(gb, size);\n\n        memmove(gb->egap + size, gb->egap, gb->ebuf - gb->egap);\n\n        gb->egap += size;\n        gb->ebuf += size;\n    }\n}\n\n/**\n * Get the byte at the point location.\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   Byte at point\n */\nstatic inline char\ngb_get(struct gapbuf *gb)\n{\n    if (gb->point == gb->gap)\n        gb->point = gb->egap;\n\n    return *gb->point;\n}\n\n/**\n * Get the byte at the point - 1 location.\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   Byte at point\n */\nstatic inline char\ngb_get_prev(struct gapbuf *gb)\n{\n    if (gb->point == gb->egap)\n        gb->point = gb->gap;\n\n    if (gb->point == gb->buf) {\n        if (gb->point == gb->gap)\n            return '\\0';\n        else\n            return *gb->point;\n    }\n\n    return *(gb->point - 1);\n}\n\n/**\n * Get the byte at the point + 1 location.\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   Byte at point\n */\nstatic inline char\ngb_get_next(struct gapbuf *gb)\n{\n    if (gb->point == gb->gap)\n        gb->point = gb->egap;\n\n    if (gb->point == gb->ebuf)\n        return *gb->point;\n\n    return *(gb->point + 1);\n}\n\n/**\n * Put character at point\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @param c\n *   The character to put at point\n * @return\n *   N/A\n */\nstatic inline void\ngb_put(struct gapbuf *gb, char c)\n{\n    if (gb->point == gb->gap)\n        gb->point = gb->egap;\n\n    if (gb->point == gb->ebuf) {\n        gb_expand_buf(gb, 1);\n        gb->ebuf++;\n    }\n\n    *gb->point = c;\n}\n\n/**\n * Get the byte at the point location and advance point\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   Byte at point\n */\nstatic inline char\ngb_getc(struct gapbuf *gb)\n{\n    if (gb->point == gb->gap) {\n        gb->point = gb->egap + 1;\n        return *gb->egap;\n    }\n\n    return *(gb->point++);\n}\n\n/**\n * Move point left and return character at point.\n *\n * fmrgetc() (point == ehole ? *(point = hole - 1) : *(--point))\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   Character at point\n */\nstatic inline char\ngb_getc_prev(struct gapbuf *gb)\n{\n    if (gb->point == gb->egap)\n        gb->point = gb->gap;\n\n    return *(--gb->point);\n}\n\n/**\n * Put character at point and advance point\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @param c\n *   The character to put at point\n * @return\n *   N/A\n */\nstatic inline void\ngb_putc(struct gapbuf *gb, char c)\n{\n    gb_move_gap_to_point(gb);\n\n    if (gb->point == gb->ebuf) {\n        gb_expand_buf(gb, 1);\n        gb->ebuf++;\n    }\n    *(gb->gap++) = c;\n    gb->point++;\n}\n\n/**\n * Insert the character at point and move point.\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @param c\n *   The character to add to buffer\n * @return\n *   N/A\n */\nstatic inline void\ngb_insert(struct gapbuf *gb, char c)\n{\n    if (gb->point != gb->gap)\n        gb_move_gap_to_point(gb);\n\n    if (gb->gap == gb->egap)\n        gb_expand_gap(gb, 1);\n\n    gb_putc(gb, c);\n}\n\n/**\n * Delete the character(s) at point\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @param cnt\n *   Number of characters to delete at point.\n * @return\n *   N/A\n */\nstatic inline void\ngb_del(struct gapbuf *gb, int cnt)\n{\n    if (gb->point != gb->gap)\n        gb_move_gap_to_point(gb);\n\n    gb->egap += cnt;\n}\n\n/**\n * Insert a string at point and move point\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @param str\n *   String to insert at point.\n * @param size\n *   Number of bytes to insert; if zero, strlen(@p str) is used.\n * @return\n *   Number of bytes inserted\n */\nstatic inline uint32_t\ngb_str_insert(struct gapbuf *gb, char *str, uint32_t size)\n{\n    int len;\n\n    if (size == 0)\n        size = strlen(str);\n    if (size == 0)\n        return 0;\n\n    gb_move_gap_to_point(gb);\n\n    if (size > gb_gap_size(gb))\n        gb_expand_gap(gb, size);\n\n    len = size;\n    do {\n        gb_putc(gb, *str++);\n    } while (--size);\n\n    return len;\n}\n\n/********************************************************/\n\n/**\n * Left size of the data in the gap buffer\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   Number of bytes in the left size of gap buffer\n */\nstatic inline uint32_t\ngb_left_data_size(struct gapbuf *gb)\n{\n    return gb->gap - gb->buf;\n}\n\n/**\n * Right size of the data in the gap buffer\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   Number of bytes in the right size of gap buffer\n */\nstatic inline uint32_t\ngb_right_data_size(struct gapbuf *gb)\n{\n    if (gb_eof(gb))\n        return 0;\n    return gb->ebuf - gb->egap;\n}\n\n/**\n * Move point right one byte\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   N/A\n */\nstatic inline void\ngb_move_right(struct gapbuf *gb)\n{\n    if (gb->point == gb->gap)\n        gb->point = gb->egap;\n    gb->point = ((gb->point + 1) > gb->ebuf) ? gb->ebuf : (gb->point + 1);\n}\n\n/**\n * Move point left one byte\n *\n * @param gb\n *   The gapbuf structure pointer.\n * @return\n *   N/A\n */\nstatic inline void\ngb_move_left(struct gapbuf *gb)\n{\n    if (gb->point == gb->egap)\n        gb->point = gb->gap;\n    gb->point = ((gb->point - 1) < gb->buf) ? gb->buf : (gb->point - 1);\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CLI_GAPBUF_H_ */\n"
  },
  {
    "path": "lib/cli/cli_help.c",
    "content": "/*-\n * Copyright(c) <2016-2026> Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#include \"cli.h\"\n#include \"cli_input.h\"\n\nint\ncli_help_add(const char *group, struct cli_map *map, const char **help_data)\n{\n    struct help_node *node;\n\n    if (!group)\n        return -1;\n\n    node = calloc(1, sizeof(struct help_node));\n    if (!node)\n        return -1;\n\n    node->map       = map;\n    node->help_data = help_data;\n    snprintf(node->group, sizeof(node->group), \"%s\", group);\n\n    /* If a map is provided, register all command tokens it references. */\n    if (map) {\n        if (cli_register_cmd_maps(map) < 0) {\n            free(node);\n            return -1;\n        }\n    }\n\n    TAILQ_INSERT_TAIL(&this_cli->help_nodes, node, next);\n\n    return 0;\n}\n\nstatic int\n_show_help_lines(const char **h, int allow_pause)\n{\n    int j;\n    char key;\n\n    for (j = 0; h[j] != NULL; j++) {\n        if (strcmp(h[j], CLI_HELP_PAUSE)) {\n            cli_printf(\"%s\\n\", h[j]);\n            continue;\n        }\n        if (allow_pause) {\n            key = cli_pause(\"\\n  Return to Continue or ESC:\", NULL);\n            if ((key == vt100_escape) || (key == 'q') || (key == 'Q'))\n                return -1;\n        }\n    }\n\n    return 0;\n}\n\nstatic void\n_cli_help_title(const char *msg)\n{\n    scrn_pos(1, 1);\n    scrn_cls();\n\n    if (msg)\n        scrn_cprintf(1, -1, \"%s\\n\", msg);\n}\n\nint\ncli_help_show_all(const char *msg)\n{\n    struct help_node *n;\n\n    _cli_help_title(msg);\n\n    TAILQ_FOREACH (n, &this_cli->help_nodes, next) {\n        if (_show_help_lines(n->help_data, 1))\n            return -1;\n        _cli_help_title(msg);\n    }\n\n    return 0;\n}\n\nvoid\ncli_help_foreach(void (*func)(void *arg, const char **h), void *arg)\n{\n    struct help_node *n;\n\n    TAILQ_FOREACH (n, &this_cli->help_nodes, next) {\n        func(arg, n->help_data);\n    }\n}\n\nstruct help_node *\ncli_help_find_group(const char *group)\n{\n    struct help_node *n;\n\n    TAILQ_FOREACH (n, &this_cli->help_nodes, next) {\n        if (!strcmp(group, n->group))\n            return n;\n    }\n\n    return NULL;\n}\n\nint\ncli_help_show_group(const char *group)\n{\n    struct help_node *n;\n\n    n = cli_help_find_group(group);\n    if (!n)\n        return -1;\n\n    return _show_help_lines(n->help_data, 0);\n}\n\nint\ncli_cmd_error(const char *msg, const char *group, int argc, char **argv)\n{\n    int n;\n\n    if (group)\n        cli_help_show_group(group);\n    if (msg)\n        cli_printf(\"%s:\\n\", msg);\n\n    cli_printf(\"  Invalid line: <\");\n    for (n = 0; n < argc; n++)\n        cli_printf(\"%s \", argv[n]);\n    cli_printf(\">\\n\");\n\n    return -1;\n}\n"
  },
  {
    "path": "lib/cli/cli_help.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#ifndef _CLI_HELP_H_\n#define _CLI_HELP_H_\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdarg.h>\n#include <unistd.h>\n#include <inttypes.h>\n#include <sys/queue.h>\n\n/**\n * @file\n * CLI help subsystem for grouped command documentation.\n *\n * Manages named help groups, each consisting of a cli_map table and an array\n * of descriptive strings. Groups are registered via cli_help_add() and can be\n * displayed individually or all at once.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define CLI_HELP_PAUSE    \"<<PauseOutput>>\"\n#define CLI_HELP_NAME_LEN 32\n\nstruct help_node {\n    TAILQ_ENTRY(help_node) next; /**< link list of help nodes */\n    char group[CLI_HELP_NAME_LEN];\n    struct cli_map *map;\n    const char **help_data;\n};\n\n/**\n * Find the help group section defined by the group string.\n *\n * @note Uses thread variable this_cli.\n *\n * @param group\n *   The group string name to find.\n * @return\n *   NULL if not found or pointer to struct cli_info.\n */\nstruct help_node *cli_help_find_group(const char *group);\n\n/**\n * Show the map table entries\n *\n * @param msg\n *   Pointer to a message to print first.\n * @return\n *   0 on success or -1 on error\n */\nint cli_help_show_all(const char *msg);\n\n/**\n * Show the help message for the user.\n *\n * @note Uses thread variable this_cli.\n *\n * @param data\n *   Pointer to the cli_info structure.\n */\nint cli_help_show_group(const char *group);\n\n/**\n * Register a named help group with an optional command map.\n *\n * Adds the group to the per-lcore help list and registers all first-token\n * command names from @p map so that auto-complete can find them.\n *\n * @param group\n *   Name string for this help group (e.g. \"Range\", \"Set\").\n * @param map\n *   Pointer to the cli_map table for this group, or NULL if none.\n * @param hd\n *   NULL-terminated array of help strings for this group.\n * @return\n *   0 on success or -1 on error\n */\nint cli_help_add(const char *group, struct cli_map *map, const char **hd);\n\n/**\n * Find if the last item is a help request.\n *\n * @param argc\n *   Number of args in the argv list.\n * @param argv\n *   List of strings to parser\n * @return\n *   1 if true or 0 if false\n */\nstatic inline int\nis_help(int argc, char **argv)\n{\n    if (argc == 0)\n        return 0;\n\n    return !strcmp(\"-?\", argv[argc - 1]) || !strcmp(\"?\", argv[argc - 1]);\n}\n\n/**\n * Iterate over the help messages calling a given function.\n *\n * @param func\n *   A function to call for all help lines.\n * @param arg\n *   Argument pointer for function call.\n * @return\n *   N/A\n */\nvoid cli_help_foreach(void (*func)(void *arg, const char **h), void *arg);\n\n/**\n * Print an error message and show the help group for a failed command.\n *\n * @param msg\n *   Short error description to print.\n * @param group\n *   Help group name whose entries will be displayed after the error.\n * @param argc\n *   Number of arguments in @p argv (for context in the error message).\n * @param argv\n *   Argument strings from the failed command invocation.\n * @return\n *   Always returns -1 (for use as a one-liner return value).\n */\nint cli_cmd_error(const char *msg, const char *group, int argc, char **argv);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CLI_HELP_H_ */\n"
  },
  {
    "path": "lib/cli/cli_history.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#include \"cli.h\"\n\nstruct cli_hist *\ncli_hist_alloc(void)\n{\n    struct cli *cli       = this_cli;\n    struct cli_hist *hist = NULL;\n\n    if (!cli)\n        return hist;\n\n    if (!CIRCLEQ_EMPTY(&cli->free_hist)) {\n        hist = (struct cli_hist *)CIRCLEQ_FIRST(&cli->free_hist);\n        CIRCLEQ_REMOVE(&cli->free_hist, hist, next);\n    }\n    return hist;\n}\n\nvoid\ncli_hist_free(struct cli_hist *hist)\n{\n    struct cli *cli = this_cli;\n\n    if (!cli || !hist)\n        return;\n\n    free(hist->line);\n\n    hist->line = NULL;\n\n    CIRCLEQ_INSERT_TAIL(&cli->free_hist, hist, next);\n}\n\nvoid\ncli_history_add(char *line)\n{\n    struct cli *cli = this_cli;\n    struct cli_hist *h;\n\n    if (!cli || !cli->hist_mem || !line)\n        return;\n\n    /* Do not allow duplicate lines compared to the last line */\n    if (!CIRCLEQ_EMPTY(&cli->hd_hist)) {\n        h = CIRCLEQ_LAST(&cli->hd_hist);\n        if (strcmp(h->line, line) == 0)\n            return;\n    }\n\n    h = cli_hist_alloc();\n    if (!h) {\n        h = CIRCLEQ_FIRST(&cli->hd_hist);\n\n        CIRCLEQ_REMOVE(&cli->hd_hist, h, next);\n    }\n\n    free(h->line);\n    h->line = strdup(line);\n    CIRCLEQ_INSERT_TAIL(&cli->hd_hist, h, next);\n}\n\nvoid\ncli_history_del(void)\n{\n    struct cli *cli = this_cli;\n    struct cli_hist *h;\n\n    if (!cli || !cli->hist_mem)\n        return;\n\n    if (!CIRCLEQ_EMPTY(&cli->hd_hist)) {\n        h = CIRCLEQ_LAST(&cli->hd_hist);\n        CIRCLEQ_REMOVE(&cli->hd_hist, h, next);\n    }\n}\n\nchar *\ncli_history_line(int lineno)\n{\n    struct cli *cli = this_cli;\n    struct cli_hist *h;\n    int i = 0;\n\n    if (!cli || !cli->hist_mem)\n        return NULL;\n\n    if (!CIRCLEQ_EMPTY(&cli->hd_hist)) {\n        CIRCLEQ_FOREACH (h, &cli->hd_hist, next) {\n            if (i++ == lineno)\n                return h->line;\n        }\n    }\n    return NULL;\n}\n\nchar *\ncli_history_prev(void)\n{\n    struct cli *cli = this_cli;\n\n    if (!cli || !cli->hist_mem)\n        return NULL;\n\n    if (!CIRCLEQ_EMPTY(&cli->hd_hist)) {\n        struct cli_hist *hist;\n\n        if (!cli->curr_hist) {\n            cli->curr_hist = CIRCLEQ_LAST(&cli->hd_hist);\n            return cli->curr_hist->line;\n        }\n\n        if (cli->curr_hist == CIRCLEQ_FIRST(&cli->hd_hist))\n            return cli->curr_hist->line;\n\n        hist           = CIRCLEQ_LOOP_PREV(&cli->hd_hist, cli->curr_hist, next);\n        cli->curr_hist = hist;\n\n        return hist->line;\n    }\n    return NULL;\n}\n\nchar *\ncli_history_next(void)\n{\n    struct cli *cli = this_cli;\n\n    if (!cli || !cli->hist_mem)\n        return NULL;\n\n    if (!CIRCLEQ_EMPTY(&cli->hd_hist)) {\n        struct cli_hist *hist;\n\n        if (!cli->curr_hist)\n            return NULL;\n\n        if (cli->curr_hist == CIRCLEQ_LAST(&cli->hd_hist))\n            return (char *)(uintptr_t)\"\";\n\n        hist           = CIRCLEQ_LOOP_NEXT(&cli->hd_hist, cli->curr_hist, next);\n        cli->curr_hist = hist;\n\n        return hist->line;\n    }\n    return NULL;\n}\n\nvoid\ncli_history_clear(void)\n{\n    struct cli *cli = this_cli;\n    struct cli_hist *h;\n\n    if (!cli || !cli->hist_mem)\n        return;\n\n    while (!CIRCLEQ_EMPTY(&cli->hd_hist)) {\n        h = CIRCLEQ_FIRST(&cli->hd_hist);\n        CIRCLEQ_REMOVE(&cli->hd_hist, h, next);\n        cli_hist_free(h);\n    }\n}\n\nvoid\ncli_history_delete(void)\n{\n    struct cli *cli = this_cli;\n\n    if (cli) {\n        cli_history_clear();\n\n        CIRCLEQ_INIT(&cli->hd_hist);\n\n        cli->hist_mem = NULL;\n        cli->nb_hist  = 0;\n    }\n}\n\nint\ncli_set_history(uint32_t nb_hist)\n{\n    struct cli *cli = this_cli;\n    size_t size;\n\n    if (!cli)\n        return -1;\n\n    if (nb_hist == 0) {\n        cli_history_delete();\n        return 0;\n    }\n\n    if (cli->hist_mem && (nb_hist == cli->nb_hist))\n        return 0;\n\n    if (cli->hist_mem)\n        cli_history_delete();\n\n    cli->nb_hist = nb_hist;\n\n    size = nb_hist * sizeof(struct cli_hist);\n\n    cli->hist_mem = calloc(1, size);\n    if (cli->hist_mem) {\n        uint32_t i;\n        struct cli_hist *hist = cli->hist_mem;\n\n        /* Setup the history support is number of lines given */\n        for (i = 0; i < nb_hist; i++, hist++)\n            CIRCLEQ_INSERT_TAIL(&cli->free_hist, hist, next);\n\n        return 0;\n    }\n\n    return -1;\n}\n\nvoid\ncli_history_reset(void)\n{\n    this_cli->curr_hist = CIRCLEQ_FIRST(&this_cli->hd_hist);\n}\n\nvoid\ncli_history_dump(void)\n{\n    struct cli *cli = this_cli;\n    struct cli_hist *h;\n    int i = 0;\n\n    if (!cli || !cli->hist_mem || CIRCLEQ_EMPTY(&cli->hd_hist))\n        return;\n\n    CIRCLEQ_FOREACH (h, &cli->hd_hist, next) {\n        cli_printf(\"%4d: %s\\n\", i++, h->line);\n    }\n}\n"
  },
  {
    "path": "lib/cli/cli_history.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#ifndef _CLI_HISTORY_H_\n#define _CLI_HISTORY_H_\n\n/**\n * @file\n * RTE Command line history\n *\n */\n\n#include \"cli.h\"\n#include <sys/queue.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum {\n    CLI_DEFAULT_HISTORY = -1, /**< Use the default history line count */\n    CLI_NO_HISTORY      = 0,  /**< Disable history entirely */\n};\n\nstruct cli_hist {\n    CIRCLEQ_ENTRY(cli_hist) next; /**< Circular-queue linkage for history list */\n    char *line;                   /**< strdup'd copy of the saved command line */\n};\n\nstruct cli;\n\n/**\n * Create and allocate a history structure\n *\n * @note Uses the thread variable this_cli\n *\n * @return\n *   pointer to history structure or NULL on error\n */\nstruct cli_hist *cli_hist_alloc(void);\n\n/**\n * Free a CLI history structure and other memory\n *\n * @note Uses the thread variable this_cli\n *\n * @param hist\n *   Pointer to the history structure\n * @return\n *   N/A\n */\nvoid cli_hist_free(struct cli_hist *hist);\n\n/**\n * Add line to history at the end\n *\n * @note Uses the thread variable this_cli\n *\n * @param line\n *   Pointer to string to add to the history list\n * @return\n *   N/A\n */\nvoid cli_history_add(char *line);\n\n/**\n * Delete a history entry\n *\n * @note Uses the thread variable this_cli\n *\n * @return\n *   N/A\n */\nvoid cli_history_del(void);\n\n/**\n * Return the history command from line number\n *\n * @note Uses the thread variable this_cli\n *\n * @param lineno\n *   The line number of the command to return.\n * @return\n *   Pointer to line or NULL on error\n */\nchar *cli_history_line(int lineno);\n\n/**\n * Clear all of the history lines from the list\n *\n * @note Uses the thread variable this_cli\n *\n * @return\n *   N/A\n */\nvoid cli_history_clear(void);\n\n/**\n * Delete the history lines and structure\n *\n * @note Uses the thread variable this_cli\n *\n * @return\n *   N/A\n */\nvoid cli_history_delete(void);\n\n/**\n * Set the number of lines max in the history list\n *\n * @param cli\n *   Pointer to the allocated cli structure\n * @param nb_hist\n *   Number of lines max in the history list\n * @return\n *   0 is ok, -1 is error\n */\nint cli_set_history(uint32_t nb_hist);\n\n/**\n * Return the previous history line\n *\n * @note Uses the thread variable this_cli\n *\n * @return\n *   pointer to the pervious history line wrap if needed\n */\nchar *cli_history_prev(void);\n\n/**\n * Return the next (more recent) history line.\n *\n * @note Uses the thread variable this_cli. Wraps around when the end is\n *   reached.\n *\n * @return\n *   Pointer to the next history line, or NULL if history is empty.\n */\nchar *cli_history_next(void);\n\n/**\n * Reset the current history pointer to the last entry.\n *\n * @note Uses the thread variable this_cli\n */\nvoid cli_history_reset(void);\n\n/**\n * Print all history lines to the CLI console.\n *\n * @note Uses the thread variable this_cli.\n */\nvoid cli_history_dump(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CLI_HISTORY_H_ */\n"
  },
  {
    "path": "lib/cli/cli_input.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n/**\n * @file\n * CLI input handling.\n *\n * Wraps VT100/screen I/O and provides the character-by-character input loop\n * used by the interactive CLI.\n */\n\n#include <stdio.h>\n#include <poll.h>\n#include <string.h>\n\n#include <rte_version.h>\n#include <rte_timer.h>\n#include <rte_log.h>\n#include <rte_string_fns.h>\n#include <pg_strings.h>\n\n#include \"cli.h\"\n#include \"cli_input.h\"\n\nint\ncli_yield_io(void)\n{\n    if (!this_cli)\n        return 1;\n    return this_cli->flags & CLI_YIELD_IO;\n}\n\n/* The CLI write routine, using write() call */\nint\ncli_write(const void *msg, int len)\n{\n    return scrn_write(msg, len);\n}\n\nint\ncli_read(char *buf, int len)\n{\n    return scrn_read(buf, len);\n}\n\nstatic void\nhandle_input_display(char c)\n{\n    /* Only allow printable characters */\n    if ((c >= ' ') && (c <= '~')) {\n        /* Output the character typed */\n        cli_write(&c, 1);\n\n        /* Add the character to the buffer */\n        gb_insert(this_cli->gb, c);\n        if (!gb_point_at_end(this_cli->gb))\n            cli_set_flag(0);\n        else if (!gb_point_at_start(this_cli->gb))\n            cli_set_flag(0);\n    }\n    cli_display_line();\n}\n\n/* Process the input for the CLI from the user */\nvoid\ncli_input(char *str, int n)\n{\n    RTE_ASSERT(this_cli->gb != NULL);\n    RTE_ASSERT(str != NULL);\n\n    while (n--) {\n        char c = *str++;\n\n        int ret = vt100_parse_input(this_cli->vt, c);\n\n        if (ret > 0) { /* Found a vt100 key sequence */\n            vt100_do_cmd(ret);\n            handle_input_display(0);\n        } else if (ret == VT100_DONE)\n            handle_input_display(c);\n    }\n}\n\n/* Poll the I/O routine for characters */\nint\ncli_poll(char *c)\n{\n    struct pollfd fds;\n\n    fds.fd      = fileno(this_scrn->fd_in);\n    fds.events  = POLLIN;\n    fds.revents = 0;\n\n    if (poll(&fds, 1, 0)) {\n        if ((fds.revents & (POLLERR | POLLNVAL)) == 0) {\n            if ((fds.revents & POLLHUP))\n                this_cli->quit_flag = 1;\n            else if ((fds.revents & POLLIN)) {\n                int n = read(fds.fd, c, 1);\n                if (n > 0)\n                    return 1;\n            }\n        } else\n            cli_quit();\n    }\n    return 0;\n}\n\n/* Display a prompt and wait for a key press */\nchar\ncli_pause(const char *msg, const char *keys)\n{\n    char prompt[128], c;\n\n    prompt[0] = '\\0';\n\n    if (msg) {\n        snprintf(prompt, sizeof(prompt), \"%s: \", msg);\n        cli_printf(\"%s\", prompt);\n    }\n\n    if (!keys)\n        keys = \" qQ\\n\\r\" ESC;\n\n    do {\n        if (cli_poll(&c))\n            if (strchr(keys, c)) {\n                /* clear the line of the prompt */\n                cli_printf(\"\\r%*s\\r\", (int)strlen(prompt), \" \");\n                return c;\n            }\n    } while (this_cli->quit_flag == 0);\n\n    return '\\0';\n}\n"
  },
  {
    "path": "lib/cli/cli_input.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#ifndef _CLI_INPUT_H_\n#define _CLI_INPUT_H_\n\n/**\n * @file\n * RTE Command line input interface\n *\n */\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Poll for a input character\n *\n * @param c\n *   Pointer to character address.\n * @return\n *   return non-zero if character was read.\n */\nint cli_poll(char *c);\n\n/**\n * The CLI write routine, using write() call\n *\n * @note Uses thread variable this_cli.\n *\n * @param msg\n *   The string to be written\n * @param len\n *   Number of bytes to write or if -1 then strlen(msg) is used.\n * @return\n *   Number of bytes written.\n */\nint cli_write(const void *msg, int len);\n\n/**\n * The routine to read characters from the user input.\n *\n * @param buf\n *   The string buffer to put the read characters.\n * @param len\n *   The length of the <buf> array to put the input.\n * @return\n *   The number of bytes read.\n */\nint cli_read(char *buf, int len);\n\n/**\n * Query and return the current terminal cursor position (row, column).\n *\n * Sends an ANSI cursor position report request (ESC[6n) and reads back the\n * ESC[{ROW};{COL}R response. Retries until a valid response is received.\n *\n * @note Uses thread variable this_cli.\n *\n * @param row\n *   Output: 1-based row number of the cursor.\n * @param col\n *   Output: 1-based column number of the cursor.\n */\nstatic inline void\ncli_get_cursor(int *row, int *col)\n{\n    char buf[32], *p, ch;\n    int r, c, l;\n\nagain:\n    scrn_cpos();\n\n    memset(buf, 0, sizeof(buf));\n    p = buf;\n    l = sizeof(buf) - 1;\n\n    do {\n        cli_read(&ch, 1);\n        if (ch == 'R')\n            break;\n        if (ch == '\\0')\n            continue;\n        *p++ = ch;\n    } while (l--);\n\n    p = index(buf, ';');\n    if (!p)\n        goto again;\n\n    r = atoi(&buf[2]);\n    c = atoi(++p);\n    if (!r || !c)\n        goto again;\n\n    *row = r;\n    *col = c;\n}\n\n/**\n * Move the vt100 cursor to the left one character\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   N/A\n */\nstatic inline void\ncli_cursor_left(void)\n{\n    cli_write(vt100_left_arr, -1);\n}\n\n/**\n * Move the vt100 cursor to the right one character\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   N/A\n */\nstatic inline void\ncli_cursor_right(void)\n{\n    cli_write(vt100_right_arr, -1);\n}\n\n/**\n * Save the vt100 cursor location\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   N/A\n */\nstatic inline void\ncli_save_cursor(void)\n{\n    cli_write(vt100_save_cursor, -1);\n}\n\n/**\n * Restore the cursor to the saved location on the console\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   N/A\n */\nstatic inline void\ncli_restore_cursor(void)\n{\n    cli_write(vt100_restore_cursor, -1);\n}\n\n/**\n * Print out the left side of the input in the Gap Buffer.\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   N/A\n */\nstatic inline void\ncli_display_left(void)\n{\n    if (gb_left_data_size(this_cli->gb))\n        cli_write(gb_start_of_buf(this_cli->gb), gb_left_data_size(this_cli->gb));\n}\n\n/**\n * Print out the right side of the input in the Gap Buffer.\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   N/A\n */\nstatic inline void\ncli_display_right(void)\n{\n    if (gb_right_data_size(this_cli->gb))\n        cli_write(gb_end_of_gap(this_cli->gb), gb_right_data_size(this_cli->gb));\n}\n\n/**\n * Clear the console screen\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   N/A\n */\nstatic inline void\ncli_clear_screen(void)\n{\n    cli_write(vt100_clear_screen, -1);\n}\n\n/**\n * clear from cursor to end of line\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   N/A\n */\nstatic inline void\ncli_clear_to_eol(void)\n{\n    cli_write(vt100_clear_right, -1);\n}\n\n/**\n * Clear the current line or the line given\n *\n * @note Uses thread variable this_cli.\n *\n * @param lineno\n *   if lineno is -1 then clear the current line else the lineno given.\n * @return\n *   N/A\n */\nstatic inline void\ncli_clear_line(int lineno)\n{\n    if (lineno > 0)\n        cli_printf(vt100_pos_cursor, lineno, 0);\n    else\n        cli_write(\"\\r\", 1);\n\n    cli_write(vt100_clear_line, -1);\n}\n\n/**\n * Move the cursor up by the number of lines given\n *\n * @note Uses thread variable this_cli.\n *\n * @param lineno\n *   Number of lines to move the cursor\n * @return\n *   N/A\n */\nstatic inline void\ncli_move_cursor_up(int lineno)\n{\n    while (lineno--)\n        cli_printf(vt100_up_arr);\n}\n\n/**\n * Redraw the CLI prompt at the start of the current line.\n *\n * Moves to column 0, invokes the registered prompt function, then clears\n * to the end of the line.\n *\n * @note Uses thread variable this_cli.\n *\n * @param t\n *   Continuation flag passed to the prompt function (0 = normal, 1 = continuation).\n */\nstatic inline void\ncli_display_prompt(int t)\n{\n    cli_write(\"\\r\", 1);\n    this_cli->plen = this_cli->prompt(t);\n    cli_clear_to_eol();\n}\n\n/**\n * Redisplay the current input line with scrolling support.\n *\n * Renders the portion of the gap-buffer content visible within the terminal\n * window width, repositions the cursor to match the logical point offset,\n * and honours any pending display flags (CLEAR_TO_EOL, DISPLAY_PROMPT, etc.).\n *\n * @note Uses thread variable this_cli.\n */\nstatic inline void\ncli_display_line(void)\n{\n    struct gapbuf *gb = this_cli->gb;\n    char buf[gb_data_size(gb) + 16];\n    int point  = gb_point_offset(gb);\n    int len    = gb_copy_to_buf(gb, buf, gb_data_size(gb));\n    int window = (this_scrn->ncols - this_cli->plen) - 1;\n    int wstart, wend;\n\n    if (cli_tst_flag(DELETE_CHAR)) {\n        cli_clr_flag(DELETE_CHAR);\n        cli_write(\" \\b\", 2);\n        cli_set_flag(CLEAR_TO_EOL);\n    }\n    if (cli_tst_flag(CLEAR_LINE)) {\n        cli_clr_flag(CLEAR_LINE);\n        scrn_bol();\n        cli_clear_to_eol();\n        cli_set_flag(DISPLAY_PROMPT);\n    }\n    if (cli_tst_flag(CLEAR_TO_EOL)) {\n        cli_clr_flag(CLEAR_TO_EOL);\n        cli_clear_to_eol();\n    }\n    if (cli_tst_flag(DISPLAY_PROMPT)) {\n        cli_clr_flag(DISPLAY_PROMPT | PROMPT_CONTINUE);\n        cli_display_prompt(0);\n    }\n\n    if (point < window) {\n        wstart = 0;\n        if (len < window)\n            wend = point + (len - point);\n        else\n            wend = point + (window - point);\n    } else {\n        wstart = point - window;\n        wend   = wstart + window;\n    }\n\n    scrn_bol();\n    scrn_cnright(this_cli->plen);\n\n    cli_write(&buf[wstart], wend - wstart);\n\n    cli_clear_to_eol();\n\n    scrn_bol();\n    scrn_cnright(this_cli->plen + point);\n}\n\n/**\n * Print out the complete line in the Gap Buffer.\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   N/A\n */\nstatic inline void\ncli_redisplay_line(void)\n{\n    uint32_t i;\n\n    this_cli->flags |= DISPLAY_PROMPT;\n\n    cli_display_line();\n\n    gb_move_gap_to_point(this_cli->gb);\n\n    for (i = 0; i < (gb_data_size(this_cli->gb) - gb_point_offset(this_cli->gb)); i++)\n        cli_cursor_left();\n}\n\n/**\n * Add a input text string the cli input parser\n *\n * @note Uses thread variable this_cli.\n *\n * @param str\n *   Pointer to string to insert\n * @param n\n *   Number of bytes in string\n * @return\n *   N/A\n */\nvoid cli_input(char *str, int n);\n\n/**\n * Set the CLI prompt function pointer\n *\n * @param prompt\n *   Function pointer to display the prompt\n * @return\n *   Return the old prompt function pointer or NULL if one does not exist\n */\ncli_prompt_t cli_set_prompt(cli_prompt_t prompt);\n\n/**\n * Set the I/O file descriptors\n *\n * @note Uses thread variable this_cli.\n *\n * @param in\n *   File descriptor for input\n * @param out\n *   File descriptor for output\n * @return\n *   N/A\n */\nvoid cli_set_io(FILE *in, FILE *out);\n\n/**\n * Set the I/O to use stdin/stdout\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   0 on success or non-0 on error\n */\nint cli_stdin_setup(void);\n\n/**\n * Restore the stdin/stdout tty params from setup\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   N/A\n */\nvoid cli_stdin_restore(void);\n\n/**\n * Pause and wait for input character\n *\n * @note Uses thread variable this_cli.\n *\n * @param keys\n *   List of keys to force return, if NULL defaults to ESC and q/Q\n * @return\n *   character that terminated the pause or zero.\n */\nchar cli_pause(const char *msg, const char *keys);\n\n/**\n * return true if calling yield should are enabled.\n *\n * @note Uses thread variable this_cli.\n *\n * @return\n *   non-zero if true else 0\n */\nint cli_yield_io(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CLI_INPUT_H_ */\n"
  },
  {
    "path": "lib/cli/cli_lib.rst",
    "content": "..  BSD LICENSE\n   Copyright(c) <2016-2026>, Intel Corporation. All rights reserved.\n\n   Redistribution and use in source and binary forms, with or without\n   modification, are permitted provided that the following conditions\n   are met:\n\n   * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in\n   the documentation and/or other materials provided with the\n   distribution.\n   * Neither the name of Intel Corporation nor the names of its\n   contributors may be used to endorse or promote products derived\n   from this software without specific prior written permission.\n\n   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n   \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nCLI library guide\n=================\n\nCLI stands for \"Command Line Interface\".\n\nThis chapter describes the CLI library which is a part of the Data Plane\nDevelopment Kit (DPDK). The CLI is a workalike replacement for cmdline library\nin DPDK and has a simpler interface and programming model plus it is dynamic.\n\nThe primary goal of CLI is to allow the developer to create commands quickly\nand with very little compile or runtime configuration. Using standard Unix*\nlike constructs which are very familar to the developer. Allowing the developer\nto construct a set of commands for development or deployment of the application.\n\nThe CLI design uses a directory like design instead of a single level command\nline interface. Allowing the developer to use a directory style solution to\ncontrolling a DPDK application. The directory style design is nothing new, but\nit does have some advantages over a single level command structure.\n\nOne advantage allows the directory path for the command to be part of the\ninformation used in executing the command. The next advantage is creating\ndirectories to make a hierarchy of commands, plus allowing whole directroy\ntrees to dynamicly come and go as required by the developer.\n\nSome of the advantages are:\n\n * CLI has no global variable other then the single thread variable called *this_cli* which can only be accessed from the thread which created the CLI instance.\n * CLI supports commands, files, aliases, directories.\n    - The alias command is just a string using a simple substitution support for other commands similar to the bash shell like alias commands.\n    - Files can be static or dynamic information, can be changed on the fly and saved for later. The file is backed with a simple function callback to allow the developer to update the content or not.\n * Added support for color and cursor movement APIs similar to Pktgen if needed by the developer.\n * It is a work alike replacement for cmdline library. Both cmdline and CLI can be used in the same application if care is taken.\n * Uses a simple fake like directory layout for command and files. Allowing for command hierarchy as path to the command can allow for specific targets to be identified without having to state it on the command line.\n * Has auto-complete for commands, similar to Unix/Linux autocomplete and provides support for command option help as well.\n * Callback functions for commands are simply just argc/argv like functions.\n    - The CLI does not convert arguments for the user, it is up to the developer to decode the argv[] values.\n    - Most of the arguments converted in the current cmdline are difficult to use or not required as the developer just picks string type and does the conversion himself.\n * Dynamically be able to add and remove commands, directories, files and aliases, does not need to be statically compiled into the application.\n * No weird structures in the code and reduces the line count for testpmd from 12K to 4.5K lines. I convert testpmd to have both CMDLINE and CLI with a command line option.\n * Two methods to parse command lines, first is the standard argc/argv method in the function.\n    - The second method is to use a map of strings with simple printf like formatting to detect which command line the user typed.\n    - An ID value it returned to the used to indicate which mapping string was found to make the command line to be used in a switch statement.\n * Environment variable support using the **env** command or using an API.\n * Central help support if needed (optional).\n\nOverview\n--------\n\nThe CLI library is a simple set of APIs which allow the developer to quickly\ncreate a set of commands using a simple programming interface already familar\nto the developer.\n\nOne of the big advantages of CLI over Cmdline is it is dynamic, which means\nnodes or items can be added and removed on the fly. Which allows adding\nnew directories, file or commands as needed or removing these items at runtime.\nThe CLI has no global modifiable variables except for the one global pointer\nwhich is a thread based variable. Allowing the developer to have multiple CLI\ninstances running at the same time on different threads if needed.\n\nAnother big advantage is the calling of the backend function to support a\ncommand is very familar to developers as it is basically just a argc/argv\nstyle command and the developer gets the complete command line. The function\nas access to the global thread variable called **this_cli** pointing to the\nstruct cli variable.\n\n.. code--block:: c\n\n    /* Show command returns 0 on OK and -1 on error */\n\tint show_cmd(int argc, char **argv);\n\nMapping commands\n----------------\n\nOne other big advantage is the use of MAP structures, to help identify commands\nquickly plus allowing the developer to define new versions of commands and\nbe able to identify these new versions using a simple identifier value.\n\nThe format of the struct cli_map is:\n\n.. code-block:: c\n\n    struct cli_map show_map[] = {\n        /* Index value, Mapping string */\n        { 10, \"show\" },\n        { 20, \"show %s\" },\n        { 30, \"show %P stats\" },\n        { 40, \"show %P %|link|errors|missed stats\" },\n        { 0, NULL}\n    }\n\nThe map is just an array of struct cli_map entries with a unique index value\nand mapping string. The index value can be any value the developer wants. As\nthe index value is used to identify the given map string.\n\nThe map string is a special formatted string similar to sprintf(), but the\nformat values for % is different. Please look at the cli_mapping() function\ndocs for more information. The %s is for any string and %P is used to a portlist\nformat e.g. 1-3,5-7,9 as used for DPDK command line notation.\n\nThe above array is parsed to match the command line from the user. The first\nmap string that matches the user input will be returned from the call to\ncli_mapping() function.\n\nConstant values are required in the command as in index 30 'stats'. The index\n40 is using a variable fixed set of strings option, which means one of these\nfixed strings must match in that position.\n\nAnother advantage of CLI is how simple it is to add new directroies, files and\ncommands for user development. To add a command a developer needs to add an\nentry to the cli_tree structure and create a function using the above\nprototype format.\n\n.. code-block:: c\n\n    struct cli_tree my_tree[] = {\n        c_dir(\"/bin\"),\n        c_cmd(\"hello\", hello_cmd, \"simple hello world command\"),\n        c_cmd(\"show\",  show_cmd,  \"Show system information\"),\n        c_end()\n    };\n\nThe cli_tree structure is made with unions and the c_dir(), c_cmd() and c_end()\nhelp initialize the structure easily for the developer. The help and show\ncommands above use the simple argc/argv prototype above.\n\nOnly two things are required to create a command a cli_tree entry and a function\nto call. Using the cli_map and other structures are optional to make adding\nsimple commands quick and easy. The call the cli_create() command or one of its\nhelper functions cli_create_XYZ(). If have a function per command then using the\nmapping structure is optional, unless you want to have CLI parse and map\ncommands to the exact entries. If cli_map is not used then the developer needs\nto decode the argc/argv to determine the command requests.\n\nThe argc/argv is exactly like the standard usage in a Unix* system, which allows\nfor using getopt() and other standard functions. The Cmdline structures and\ntext conversions were defined at compile time in most cases, but in CLI the\ncommand routine is passed the argc/argv information to convert the strings as\nneeded. The cli variable being a thread Local Storage (TLS) all user routines\ncan access **this_cli** to gain access to the CLI structure if required at all.\n\nEnvironment variables\n---------------------\n\nThe user can also set environment variables using the **env** command. These\nvariables are also parsed in the command line as direct substitutions.\n\nAnother special file is a string file, which can be used as an environment\nvariable. When the variable is asked for the variable asks a function to return\nthe string. The value of the string normally a system value or a generated\nvalue. These types of environment variables can not be set from the command\nline as a function pointer needs to be given. The c_str() macro helps in\nsetting up these environment variables via the cli_tree structure.\n\nThe special file backed environment variable can be deleted, but can not be\nrestored without a reboot or some other command puting that variable back into\nthe environment.\n\nEnvironment variables are denoted by a $(foo) like syntax and are expanded at\nthe time of execution each time the command line is executed. Which means\nhistory lines with environment variables will be expanded again.\n\nSimple Files\n------------\n\nThe CLI system also has support for simple files along with alias like commands.\nThese simple files are backed by a function call and the other commands can read\nthese files to get constant data or generated data depending on how the backend\nfunction works.\n\nAlias commands\n--------------\nThe alias commands are fixed strings which are executed instead of a function\nprovided by the developer. If the user has more arguments these are appended\nto the alias string and processed as if typed on the command line. Also the\nenvironment variables are expanded at execution time.\n\n.. note::\n\n   The CLI library was designed to be used in production code and the Cmdline\n   was not validated to the same standard as other DPDK libraries. The goal\n   is to provide a production CLI design.\n\nThe CLI library supports some of the features of the Cmdline library such as,\ncompletion, cut/paste and some other special bindings that make configuration\nand debug faster and easier.\n\nThe CLI desin uses some very simple VT100 control strings for displaying data\nand accepting input from the user. Some of the control strings are used to\nclear the screen or line and position the cursor on a VT100 compatible terminal.\nThe CLI screen code also supports basic color and many other VT100 commands.\n\nThe example application also shows how the CLI application can be extended to\nhandle a list of commands and user input.\n\nThe example presents a simple command prompt **DPDK-cli:/>** similar to a Unix*\nshell command along with a directory like file system.\n\nSome of the **default** commands contained under /sbin directory are:\n\n * **ls**: list the current or provided directory files/commands.\n * **cd**: Change directory command.\n * **pwd**: print out the current working directory.\n * **history**: List the current command line history if enabled.\n * **more**: A simple command to page contents of files.\n * **help**: display a the help screen.\n * **quit**: exit the CLI application, also **Ctrl-x** will exit as well.\n * **mkdir**: add a directory to the current directory.\n * **delay**: wait for a given number of microseconds.\n * **sleep**: wait for a given number of seconds.\n * **rm**: remove a directory, file or command. Removing a file will delete the data.\n * **cls**: clear the screen and redisplay the prompt.\n * **version**: Display the current DPDK version being used.\n * **path**: display the current search path for executable commands.\n * **cmap**: Display the current system core and socket information.\n * **hugepages**: Display the current hugepage information.\n * **sizes**: a collection system structure and buffer sizes for debugging.\n * **copyright**: a file containing DPDK copyright information.\n * **env**: a command show/set/modify the environment variables.\n\n * **ll**: an alias command to display long ls listing **ls -l**\n * **h**: alias command for **history**\n * **hello**: a simple Hello World! command.\n * **show**: has a number of commands using the map feature.\n\nUnder the /data directory is:\n\n * **pci**: a simple example file for displaying the **lspci** command in CLI.\n\n.. note::\n\n   To terminate the application, use **Ctrl-x** or the command **quit**.\n\nAuto completion\n---------------\n\nCLI does support auto completion at the file or directory level, meaning the\narguments to commands are not expanded as was done in Cmdline code. The CLI\nauto completion works similar to the standard Unix* system by expanding\ncommands and directory paths. In normal Unix* like commands the user needs to\nexecute the command asking for help information.\n\nSpecial command features\n------------------------\n\nUsing the '!' followed by a number from the history list of commands you can\nexecute that command again. Or using the UP/Down arrows the user can quickly\nfind and execute or modify a previous command in history.\n\nThe user can also execute host level commands if enabled using the '@' prefix\nto a command line e.g. @ls or @lspci or ... line is passed to popen or system\nfunction to be executed and the output displayed on the console if any output.\n\nCompiling the Application\n-------------------------\n\n#.  Go to example directory:\n\n.. code-block:: c\n\n   export RTE_SDK=/path/to/rte_sdk\n   cd ${RTE_SDK}/examples/cli\n\n#.  Set the target (a default target is used if not specified). For example:\n\n.. code-block:: console\n\n   export RTE_TARGET=x86_64-native-linux-gcc\n   or\n   export RTE_TARGET=x86_64-native-linuxapp-gcc\n\nRefer to the *DPDK Getting Started Guide* for possible RTE_TARGET values.\n\n#.  Build the application:\n\n.. code-block:: console\n\n   make\n\nRunning the Application\n-----------------------\n\nTo run the application in linux environment, issue the following command:\n\n.. code-block:: console\n\n   $ ./build/cli\n\n.. note::\n   The example cli application does not require to be run as superuser\n   as it does not startup DPDK by calling rte_eal_init() routine. Which means\n   it also does not use DPDK features except for a few routines not requiring\n   EAL initialization.\n\nRefer to the *DPDK Getting Started Guide* for general information on running applications\nand the Environment Abstraction Layer (EAL) options.\n\nExplanation\n-----------\n\nThe following sections provide some explanation of the code.\n\nEAL Initialization and cmdline Start\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe first task is the initialization of the Environment Abstraction Layer (EAL),\nif required for the application.\n\n.. code-block:: c\n\n   int\n   main(int argc, char **argv)\n   {\n       if (cli_create_with_tree(init_tree) ==0) {\n           cli_start(NULL, 0); /* NULL is some init message done only once */\n                               /* 0 means do not use color themes */\n           cli_destroy();\n       }\n\nThe cli_start() function returns when the user types **Ctrl-x** or uses the\nquit command in this case, the application exits. The cli_create() call takes\nfour arguments and each has a default value if not provided. The API used here\nis the cli_create_with_tree(), which uses defaults for three of the arguments.\n\n.. code-block:: c\n\n   /**\n   * Create the CLI engine\n   *\n   * @param prompt_func\n   *   Function pointer to call for displaying the prompt.\n   * @param tree_func\n   *   The user supplied function to init the tree or can be NULL. If NULL then\n   *   a default tree is initialized with basic commands.\n   * @param nb_entries\n   *   Total number of commands, files, aliases and directories. If 0 then use\n   *   the default number of nodes. If -1 then unlimited number of nodes.\n   * @param nb_hist\n   *   The number of lines to keep in history. If zero then turn off history.\n   *   If the value is CLI_DEFAULT_HISTORY use CLI_DEFAULT_HIST_LINES\n   * @return\n   *   0 on success or -1\n   */\n   int cli_create(cli_prompt_t prompt_func, cli_tree_t tree_func,\n                       int nb_entries, uint32_t nb_hist);\n\nThe cli_create_with_tree() has only one argument which is the structure to use\nin order to setup the initial directory structure. Also the wrapper function\nint cli_create_with_defaults(void) can be used as well.\n\nConsult the cli.h header file for the default values. Also the alias node is a\nspecial alias file to allow for aliasing a command to another command.\n\nThe tree init routine is defined like:\n\n.. code-block:: c\n\n\tstatic struct cli_tree my_tree[] = {\n\t    c_dir(\"/data\"),\n\t    c_file(\"pci\", pci_file, \"display lspci information\"),\n\t    c_dir(\"/bin\"),\n\t    c_cmd(\"hello\", hello_cmd, \"Hello-World!!\"),\n\t    c_alias(\"h\", \"history\", \"display history commands\"),\n\t    c_alias(\"ll\", \"ls -l\", \"long directory listing alias\"),\n\t    c_end()\n\t};\n\n\tstatic int\n\tinit_tree(void)\n\t{\n\t    /*\n\t     * Root is created already and using system default cmds and dirs, the\n\t     * developer is not required to use the system default cmds/dirs.\n\t     */\n\t    if (cli_default_tree_init())\n\t        return -1;\n\n\t\t/* Using NULL here to start at root directory */\n\t    if (cli_add_tree(NULL, my_tree))\n\t        return -1;\n\n\t\tcli_help_add(\"Show\", show_map, show_help);\n\n\t\treturn cli_add_bin_path(\"/bin\");\n\t}\n\n\nThe above structure is used to create the tree structure at initialization\ntime. The struct cli_tree or cli_tree_t typedef can be used to setup a new\ndirectory tree or agument the default tree.\n\nThe elements are using a set of macros c_dir, c_file, c_cmd, c_alias and c_end.\nThese macros help fill out the cli_tree_t structure for the given type of item.\n\nThe developer can create his own tree structure with any commands that are\nneeded and/or call the cli_default_tree_init() routine to get the default\nstructure of commands. If the developer does not wish to call the default\nCLI routine, then he must call the cli_create_root() function first before\nadding other nodes. Other nodes can be added and removed at anytime.\n\nCLI Map command support\n~~~~~~~~~~~~~~~~~~~~~~~\n\nThe CLI command has two types of support to handle arguments normal argc/argv\nand the map system. As shown above the developer creates a directory tree and\nattaches a function to a command. The function takes the argc/argv as arguments\nand the developer can just parse the arguments to decode the command arguments.\nSometimes you have multiple commands or different versions of a command being\nhandled by a single routine, this is were the map support comes into play.\n\nThe map support defines a set of struct cli_map map[]; to help detect the\ncorrect command from the user. In the list of cli_map structures a single\nstructure contains two items a developer defined index value and a command\nstrings. The index value is used on the function to identify the specific type\nof command found in the list. The string is a special printf like string to\nhelp identify the command typed by the user. One of the first things todo in\nthe command routine is to call the cli_mapping() function passing in the CLI\npointer and the argc/argv values.The two method can be used at the same time.\n\nThe cli_mapping() command matches up the special format string with the values\nin the argc/argv array and returns the developer supplied index value or really\nthe pointer the struct cli_map instance.\n\nNow the developer can use the cli_map.index value in a switch() statement to\nlocate the command the user typed or if not found a return of -1.\n\nExample:\n\n.. code-block:: c\n\n\tstatic int\n\thello_cmd(int argc, char **argv)\n\t{\n\t    int i, opt;\n\n\t    optind = 1;\n\t    while((opt = getopt(argc, argv, \"?\")) != -1) {\n\t        switch(opt) {\n\t            case '?': cli_usage(); return 0;\n\t            default:\n\t                break;\n\t        }\n\t    }\n\n\t    cli_printf(\"Hello command said: Hello World!! \");\n\t    for(i = 1; i < argc; i++)\n\t        cli_printf(\"%s \", argv[i]);\n\t    cli_printf(\"\\n\");\n\n\t    return 0;\n\t}\n\n\tstatic int\n\tpci_file(struct cli_node *node, char *buff, int len, uint32_t opt)\n\t{\n\t\tif (is_file_open(opt)) {\n\t\t\tFILE *f;\n\n\t\t\tif (node->file_data && (node->fflags & CLI_FREE_DATA))\n\t\t\t\tfree(node->file_data);\n\n\t        node->file_data = calloc(1, 32 * 1024);\n\t\t\tif (!node->file_data)\n\t\t\t\treturn -1;\n\t        node->file_size = 32 * 1024;\n\t        node->fflags = CLI_DATA_RDONLY | CLI_FREE_DATA;\n\n\t\t\tf = popen(\"lspci\", \"r\");\n\t\t\tif (!f)\n\t\t\t\treturn -1;\n\n\t\t\tnode->file_size = fread(node->file_data, 1, node->file_size, f);\n\n\t\t\tpclose(f);\n\t        return 0;\n\t    }\n\t    return cli_file_handler(node, buff, len, opt);\n\t}\n\n\tstatic struct cli_map show_map[] = {\n\t\t{ 10, \"show %P\" },\n\t\t{ 20, \"show %P mac %m\" },\n\t\t{ 30, \"show %P vlan %d mac %m\" },\n\t\t{ 40, \"show %P %|vlan|mac\" },\n\t\t{ -1, NULL }\n\t};\n\n\tstatic const char *show_help[] = {\n\t\t\"show <portlist>\",\n\t\t\"show <portlist> mac <rte_ether_addr>\",\n\t\t\"show <portlist> vlan <vlanid> mac <rte_ether_addr>\",\n\t\t\"show <portlist> [vlan|mac]\",\n\t\tCLI_HELP_PAUSE,\n\t\tNULL\n\t};\n\n\tstatic int\n\tshow_cmd(int argc, char **argv)\n\t{\n\t\tstruct cli_map *m;\n\t\tuint32_t portlist;\n\t\tstruct rte_ether_addr mac;\n\n\t\tm = cli_mapping(Show_info.map, argc, argv);\n\t\tif (!m)\n\t\t\treturn -1;\n\n\t\tswitch(m->index) {\n\t\t\tcase 10:\n\t\t\t\tportlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\t\t\t\tcli_printf(\"   Show Portlist: %08x\\n\", portlist);\n\t\t\t\tbreak;\n\t\t\tcase 20:\n\t\t\t\tportlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\t\t\t\tpg_ether_aton(argv[3], &mac);\n\t\t\t\tcli_printf(\"   Show Portlist: %08x, MAC: %02x:%02x:%02x:%02x:%02x:%02x\\n\",\n\t\t\t\t\t\t   portlist,\n\t\t\t\t\t\t   mac.addr_bytes[0],\n\t\t\t\t\t\t   mac.addr_bytes[1],\n\t\t\t\t\t\t   mac.addr_bytes[2],\n\t\t\t\t\t\t   mac.addr_bytes[3],\n\t\t\t\t\t\t   mac.addr_bytes[4],\n\t\t\t\t\t\t   mac.addr_bytes[5]);\n\t\t\t\tbreak;\n\t\t\tcase 30:\n\t\t\t\tportlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\t\t\t\tpg_ether_aton(argv[5], &mac);\n\t\t\t\tcli_printf(\"   Show Portlist: %08x vlan %d MAC: %02x:%02x:%02x:%02x:%02x:%02x\\n\",\n\t\t\t\t\t\t   portlist,\n\t\t\t\t\t\t   atoi(argv[3]),\n\t\t\t\t\t\t   mac.addr_bytes[0],\n\t\t\t\t\t\t   mac.addr_bytes[1],\n\t\t\t\t\t\t   mac.addr_bytes[2],\n\t\t\t\t\t\t   mac.addr_bytes[3],\n\t\t\t\t\t\t   mac.addr_bytes[4],\n\t\t\t\t\t\t   mac.addr_bytes[5]);\n\t\t\t\tbreak;\n\t\t\tcase 40:\n\t\t\t\tportlist_parse(argv[1], pktgen.nb_ports, &portlist);\n\t\t\t\tpg_ether_aton(\"1234:4567:8901\", &mac);\n\t\t\t\tcli_printf(\"   Show Portlist: %08x %s: \",\n\t\t\t\t\t\t   portlist, argv[2]);\n\t\t\t\tif (argv[2][0] == 'm')\n\t\t\t\t\tcli_printf(\"%02x:%02x:%02x:%02x:%02x:%02x\\n\",\n\t\t\t\t\t\t   mac.addr_bytes[0],\n\t\t\t\t\t\t   mac.addr_bytes[1],\n\t\t\t\t\t\t   mac.addr_bytes[2],\n\t\t\t\t\t\t   mac.addr_bytes[3],\n\t\t\t\t\t\t   mac.addr_bytes[4],\n\t\t\t\t\t\t   mac.addr_bytes[5]);\n\t\t\t\telse\n\t\t\t\t\tcli_printf(\"%d\\n\", 101);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcli_help_show_group(\"Show\");\n\t\t\t\treturn -1;\n\t\t}\n\t\treturn 0;\n\t}\n\n\tstatic struct cli_tree my_tree[] = {\n\t\tc_dir(\"/data\"),\n\t    c_file(\"pci\",\tpci_file, \t\"display lspci information\"),\n\t    c_dir(\"/bin\"),\n\t    c_cmd(\"show\",\tshow_cmd, \t\"show mapping options\"),\n\t    c_cmd(\"hello\",\thello_cmd, \t\"Hello-World!!\"),\n\t    c_alias(\"h\", \t\"history\", \t\"display history commands\"),\n\t    c_alias(\"ll\", \t\"ls -l\", \t\"long directory listing alias\"),\n\t    c_end()\n\t};\n\nHere is the cli_tree for this example, note it has a lot more commands. The show_cmd\nor **show** command is located a number of lines down. The cli_tree creates in\nthe **/bin** directory a number of commands and the show command is one of these. The\nshow command has four different formats if you look at the **show_map[]** structure.\n\nThe user types one of these commands and cli_mapping() function attempts to locate the\ncorrect entry in the list. You will also notice another structure called\n**show_help**, which is an array of strings giving a cleaner and longer help\ndescription of each of the commands.\n\n\nUnderstanding the CLI system\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe command line interface is defined as a fake directory tree with executables,\ndirectorys and files. The user uses shell like standard commands to move about\nthe directory and execute commands. The CLI is not a powerful as the\nBash shell, but has a number of similar concepts.\n\nOur fake directory tree has a '/' or root directory which is created when\ncli_create() is called along with the default sbin directory. The user starts out\nat the root directory '/' and is allowed to cd to other directories, which could\ncontain more executables, aliases or directories. The max number of directory\nlevels is limited to the number of nodes given at startup.\n\nThe default directory tree starts out as just root (/) and a sbin directory.\nAlso it contains a file called copyright in root, which can be displayed\nusing the default 'more copyright' command.\n\nA number of default commands are predefined in the /sbin directory and are\ndefined above. Other bin directories can be added to the system if needed,\nbut a limit of CLI_MAX_BINS is defined in the cli.h header file.\n"
  },
  {
    "path": "lib/cli/cli_map.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n/**\n * @file\n * CLI map (command pattern) matching.\n *\n * Implements the lightweight printf-like token format used to select a\n * command variant based on argc/argv.\n */\n\n#include <rte_string_fns.h>\n#include <pg_strings.h>\n#include <_atoip.h>\n\n#include \"cli.h\"\n\nint\ncli_map_list_search(const char *fmt, char *item, int index)\n{\n    char *buf;\n    int size;\n    char *opts[CLI_MAX_ARGVS + 1];\n\n    size = strlen(fmt) + 1;\n    buf  = malloc(size);\n    if (!buf)\n        return -1;\n\n    memset(buf, '\\0', size);\n    memset(opts, '\\0', sizeof(opts));\n\n    snprintf(buf, size, \"%s\", fmt);\n    pg_strtok(buf, \" \", opts, CLI_MAX_ARGVS);\n\n    /* Skip the %| in the string options */\n    int ret = pg_stropt(&opts[index][2], item, \"|\");\n\n    free(buf);\n    return ret;\n}\n\nstatic int\nis_map_valid(const char *fmt, char *arg)\n{\n    int ret = 0;\n    char *p;\n    struct rte_ipaddr ip;\n\n    if (strchr(\"%bdDuUhHsnc46mkPC|l\", fmt[1]) == NULL)\n        return ret;\n\n    /* validate all of the characters matching the format */\n    do {\n        ret = 0;\n        switch (fmt[1]) {\n        case '%':\n            ret = 1;\n            break;\n        case 'b':\n            if (isdigit(*arg))\n                ret = 1;\n            break;\n        case 'd':\n            if (isdigit(*arg))\n                ret = 1;\n            break;\n        case 'D':\n            if (isdigit(*arg))\n                ret = 1;\n            break;\n        case 'u':\n            if (isdigit(*arg))\n                ret = 1;\n            break;\n        case 'U':\n            if (isdigit(*arg))\n                ret = 1;\n            break;\n        case 'h':\n            if (isxdigit(*arg))\n                ret = 1;\n            break;\n        case 'H':\n            if (isxdigit(*arg))\n                ret = 1;\n            break;\n        case 's':\n            if (isprint(*arg))\n                ret = 1;\n            break;\n        case 'c':\n            if (isprint(*arg))\n                ret = 1;\n            break;\n        case 'n':\n            if (isdigit(*arg))\n                ret = 1;\n            break;\n        case '4':\n            p = strchr(arg, '/');\n            if (_atoip(arg, p ? RTE_IPADDR_V4 | RTE_IPADDR_NETWORK : RTE_IPADDR_V4, &ip,\n                       sizeof(ip)) == 4)\n                ret = 1;\n            return ret;\n        case '6':\n            p = strchr(arg, '/');\n            if (_atoip(arg, p ? RTE_IPADDR_V6 | RTE_IPADDR_NETWORK : RTE_IPADDR_V6, &ip,\n                       sizeof(ip)) == 6)\n                ret = 1;\n            return ret;\n        case 'm':\n            if (isxdigit(*arg))\n                ret = 1;\n            break;\n        case 'k':\n            return 1;\n        /* list of ports or cores or the word all */\n        case 'P':\n            if (isdigit(*arg) || (*arg == 'a'))\n                ret = 1;\n            break;\n        case 'C':\n            if (isdigit(*arg) || (*arg == 'a'))\n                ret = 1;\n            break;\n        case '|':\n            return (pg_stropt(&fmt[1], arg, \"|\") == -1) ? 0 : 1;\n        case 'l':\n            ret = 1;\n            break;\n        default:\n            return 0;\n        }\n        arg++;\n    } while (*arg && (ret == 0));\n\n    return ret;\n}\n\nstruct cli_map *\ncli_mapping(struct cli_map *maps, int argc, char **argv)\n{\n    int nb_args, i, j, ok;\n    const char *m;\n    char line[CLI_MAX_PATH_LENGTH + 1], *map[CLI_MAX_ARGVS], *p;\n\n    memset(line, '\\0', sizeof(line));\n    memset(map, '\\0', sizeof(map));\n\n    p = line;\n    for (i = 0; (m = maps[i].fmt) != NULL; i++) {\n        snprintf(p, sizeof(line), \"%s\", m);\n\n        nb_args = pg_strtok(p, \" \", map, CLI_MAX_ARGVS);\n\n        /* display the cli MAP if present as some help */\n        if (!strcmp(\"-?\", argv[argc - 1]) || !strcmp(\"?\", argv[argc - 1])) {\n            cli_maps_show(maps, argc, argv);\n            return NULL;\n        }\n\n        if (nb_args != argc)\n            continue;\n\n        /* Scan the map entry looking for a valid match */\n        for (j = 0, ok = 1; ok && (j < argc); j++) {\n            if (map[j][0] == '%') {\n                /* Found a format '%' validate it */\n                if (!is_map_valid(map[j], argv[j]))\n                    ok = 0;\n                /* a constant string match valid */\n            } else if (!pg_strmatch(map[j], argv[j]))\n                ok = 0;\n        }\n\n        if (ok)\n            return &maps[i];\n    }\n\n    return NULL;\n}\n\nstatic void\ndecode_map(const char *fmt)\n{\n    char *argv[CLI_MAX_ARGVS + 1];\n    char line[CLI_MAX_PATH_LENGTH + 1];\n    int n, i;\n\n    memset(argv, '\\0', sizeof(argv));\n\n    snprintf(line, sizeof(line), \"%s\", fmt);\n    if (fmt[0] != '%') {\n        cli_printf(\"%s \", fmt);\n        return;\n    }\n\n    switch (fmt[1]) {\n    case '%':\n        cli_printf(\"%% \");\n        break;\n    case 'b':\n        cli_printf(\"<8bit number> \");\n        break;\n    case 'n':\n        cli_printf(\"<number> \");\n        break;\n    case 'u':\n        cli_printf(\"<32bit unsigned> \");\n        break;\n    case 'U':\n        cli_printf(\"<64bit unsigned> \");\n        break;\n    case 'd':\n        cli_printf(\"<32bit number> \");\n        break;\n    case 'D':\n        cli_printf(\"<64bit number> \");\n        break;\n    case 'h':\n        cli_printf(\"<32bit hex> \");\n        break;\n    case 'H':\n        cli_printf(\"<64bit hex> \");\n        break;\n    case 's':\n        cli_printf(\"<string> \");\n        break;\n    case 'c':\n        cli_printf(\"<comma-list> \");\n        break;\n    case '4':\n        cli_printf(\"<ipv4-addr> \");\n        break;\n    case '6':\n        cli_printf(\"<ipv6-addr> \");\n        break;\n    case 'm':\n        cli_printf(\"<mac-addr> \");\n        break;\n    case 'k':\n        cli_printf(\"<kvargs> \");\n        break;\n    case 'P':\n        cli_printf(\"<portlist> \");\n        break;\n    case 'C':\n        cli_printf(\"<corelist> \");\n        break;\n    case '|':\n        cli_printf(\"[\");\n        n = pg_strtok(&line[2], \"|\", argv, CLI_MAX_ARGVS);\n        for (i = 0; i < n; i++)\n            cli_printf(\"%s%s\", argv[i], (i < (n - 1)) ? \"|\" : \"\");\n        cli_printf(\"] \");\n        break;\n    case 'l':\n        cli_printf(\"<list> \");\n        break;\n    default:\n        cli_printf(\"<unknown> \");\n        break;\n    }\n}\n\nvoid\ncli_map_show(struct cli_map *m)\n{\n    int i, nb_args;\n    char line[CLI_MAX_PATH_LENGTH + 1], *map[CLI_MAX_ARGVS + 1], *p;\n\n    memset(line, '\\0', sizeof(line));\n    memset(map, '\\0', sizeof(map));\n\n    p = line;\n\n    snprintf(p, sizeof(line), \"%s\", m->fmt);\n\n    nb_args = pg_strtok(p, \" \", map, CLI_MAX_ARGVS);\n\n    cli_printf(\"  %s \", map[0]);\n    for (i = 1; i < nb_args; i++)\n        decode_map(map[i]);\n    cli_printf(\"\\n\");\n}\n\nvoid\ncli_maps_show(struct cli_map *maps, int argc, char **argv)\n{\n    struct cli_map *m;\n    char line[CLI_MAX_PATH_LENGTH + 1], *map[CLI_MAX_ARGVS + 1];\n    int nb_args;\n\n    if (!argc)\n        return;\n\n    cli_printf(\"\\nUsage:\\n\");\n    for (m = maps; m->fmt != NULL; m++) {\n        line[0] = '\\0';\n        map[0]  = NULL;\n\n        snprintf(line, sizeof(line), \"%s\", m->fmt);\n\n        nb_args = pg_strtok(line, \" \", map, CLI_MAX_ARGVS);\n\n        if (nb_args && !strcmp(argv[0], map[0]))\n            cli_map_show(m);\n    }\n}\n\nvoid\ncli_map_dump(struct cli_map *maps, int argc, char **argv)\n{\n    int i, nb_args;\n    struct cli_map *m;\n    char line[CLI_MAX_PATH_LENGTH + 1], *map[CLI_MAX_ARGVS + 1], *p;\n\n    memset(line, '\\0', sizeof(line));\n    memset(map, '\\0', sizeof(map));\n\n    p = line;\n\n    m = cli_mapping(maps, argc, argv);\n    if (!m) {\n        cli_printf(\"Map for %d/\", argc);\n        for (i = 0; i < argc; i++) {\n            cli_printf(\"<%s>\", argv[i]);\n            if ((i + 1) < argc)\n                cli_printf(\",\");\n        }\n        cli_printf(\"\\n\");\n    }\n\n    snprintf(p, sizeof(line), \"%s\", m->fmt);\n\n    nb_args = pg_strtok(p, \" \", map, CLI_MAX_ARGVS);\n\n    cli_printf(\"%4d - %s == %s\\n\", m->index, argv[0], map[0]);\n    for (i = 1; i < argc && i < nb_args; i++)\n        cli_printf(\"       %s == %s\\n\", argv[i], map[i]);\n}\n"
  },
  {
    "path": "lib/cli/cli_map.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n/**\n * @file\n * CLI argument map matching.\n *\n * Provides the cli_map structure and pattern-matching logic used to dispatch\n * CLI commands. Each map entry pairs a format string (with % tokens such as\n * %d, %4, %m, %P, %|opt1|opt2) with an integer index returned when the\n * user-supplied argc/argv matches that pattern.\n */\n\n#ifndef _CLI_MAP_H_\n#define _CLI_MAP_H_\n\n#include <netinet/in.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** One entry in a command dispatch table. */\nstruct cli_map {\n    int index;       /**< Value returned by cli_mapping() on a successful match */\n    const char *fmt; /**< Space-separated format string; see cli_mapping() for tokens */\n};\n\n/**\n * Parse a string <list> looking for matching mapping.\n *\n * @param maps\n *   Array string points with mapping formats using a special % formats\n *   %d - decimal number 32bit\n *   %D - decimal number 64bit\n *   %h - hexadecimal number 32 bit number\n *   %H - hexadecimal number 64 bit number\n *   %P - Portlist\n *   %C - Corelist\n *   %s - string\n *   %c - comma separated list string\n *   %m - MAC address format\n *   %4 - IPv4 address\n *   %6 - IPv6 address\n *   %k - kvargs list of <key>=<value>[,...] strings\n *   %l - list format, if constains space then quote the string first\n *   %|<fixed-list> - Fixed list of valid strings\n * @param argc\n *   Number of arguments in <argv>\n * @param argv\n *   Array of command line string pointers\n * @return\n *   return pointer map entry or NULL if not found\n */\nstruct cli_map *cli_mapping(struct cli_map *maps, int argc, char **argv);\n\n/**\n * Dump out the map entry\n *\n * @param maps\n *   The cli_map structure pointer\n * @return\n *   None\n */\nvoid cli_map_show(struct cli_map *m);\n\n/**\n * Show the map table entries\n *\n * @param maps\n *   The cli_map structure pointer\n * @return\n *   None\n */\nvoid cli_maps_show(struct cli_map *maps, int argc, char **argv);\n\n/**\n * Dump out the map table entry matching the argc/argv\n *\n * @param maps\n *   The cli_map structure pointer\n * @param argc\n *   Number of argumemts\n * @param argv\n *   List of argument strings\n * @return\n *   None\n */\nvoid cli_map_dump(struct cli_map *maps, int argc, char **argv);\n\n/**\n * Search a choice token in a cli_map format string for a matching item.\n *\n * @param fmt\n *   cli_map format string containing one or more %|opt1|opt2|… tokens.\n * @param item\n *   The string to look up within the choice token.\n * @param index\n *   Zero-based index of the choice token within @p fmt to search.\n * @return\n *   Zero-based position of @p item within the choice list, or -1 if not found.\n */\nint cli_map_list_search(const char *fmt, char *item, int index);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* CLI_MAP_H */\n"
  },
  {
    "path": "lib/cli/cli_scrn.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n/* Created by Keith Wiles @ intel.com */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <stdarg.h>\n#include <unistd.h>\n#include <signal.h>\n#include <sys/ioctl.h>\n\n#include <rte_atomic.h>\n#include <rte_malloc.h>\n\n#include <cli.h>\n#include \"cli_scrn.h\"\n#include \"cli_input.h\"\n\nstruct cli_scrn *this_scrn;\n\n// clang-format off\nvoid __attribute__((format(printf, 3, 4)))\nscrn_printf(int16_t r, int16_t c, const char *fmt, ...)\n{\n    va_list vaList;\n\n    /* In some cases a segfault was reported when this_scrn\n     * would become null.\n     */\n    if (this_scrn && this_scrn->fd_out) {\n        if ((r != 0) && (c != 0))\n            scrn_pos(r, c);\n        va_start(vaList, fmt);\n        vfprintf(this_scrn->fd_out, fmt, vaList);\n        va_end(vaList);\n        fflush(this_scrn->fd_out);\n    }\n}\n// clang-format on\n\nvoid __attribute__((format(printf, 3, 4)))\nscrn_cprintf(int16_t r, int16_t ncols, const char *fmt, ...)\n{\n    va_list vaList;\n    char str[512];\n\n    if (ncols == -1)\n        ncols = this_scrn->ncols;\n\n    va_start(vaList, fmt);\n    vsnprintf(str, sizeof(str), fmt, vaList);\n    va_end(vaList);\n\n    scrn_pos(r, scrn_center_col(ncols, str));\n    scrn_puts(\"%s\", str);\n}\n\nvoid __attribute__((format(printf, 4, 5)))\nscrn_fprintf(int16_t r, int16_t c, FILE *f, const char *fmt, ...)\n{\n    va_list vaList;\n\n    if ((r != 0) && (c != 0))\n        scrn_pos(r, c);\n    va_start(vaList, fmt);\n    vfprintf(f, fmt, vaList);\n    va_end(vaList);\n    fflush(f);\n}\n\nstatic void\nscrn_set_io(FILE *in, FILE *out)\n{\n    struct cli_scrn *scrn = this_scrn;\n\n    if (scrn) {\n        if (scrn->fd_in && (scrn->fd_in != stdin))\n            fclose(scrn->fd_in);\n\n        if (scrn->fd_out && (scrn->fd_out != stdout))\n            fclose(scrn->fd_in);\n\n        scrn->fd_in  = in;\n        scrn->fd_out = out;\n    }\n}\n\nstatic int\nscrn_stdin_setup(void)\n{\n    struct cli_scrn *scrn = this_scrn;\n    struct termios term;\n\n    if (!scrn)\n        return -1;\n\n    memcpy(&term, &scrn->oldterm, sizeof(term));\n\n    term.c_lflag &= ~(ICANON | ECHO | ISIG | IEXTEN);\n\n    if (tcsetattr(fileno(scrn->fd_in), TCSANOW, &term)) {\n        fprintf(stderr, \"%s: failed to set tty\\n\", __func__);\n        return -1;\n    }\n\n    return 0;\n}\n\nstatic void\nscrn_stdin_restore(void)\n{\n    struct cli_scrn *scrn = this_scrn;\n\n    if (!scrn)\n        return;\n\n    if (tcsetattr(fileno(scrn->fd_in), TCSANOW, &scrn->oldterm))\n        fprintf(stderr, \"%s: failed to set tty\\n\", __func__);\n}\n\nstatic void\nhandle_winch(int sig)\n{\n    struct winsize w;\n\n    if (sig != SIGWINCH)\n        return;\n\n    ioctl(0, TIOCGWINSZ, &w);\n\n    this_scrn->nrows = w.ws_row;\n    this_scrn->ncols = w.ws_col;\n\n    /* Need to refresh the screen */\n    cli_clear_line(-1);\n    cli_redisplay_line();\n}\n\nint\nscrn_create(int scrn_type, int theme)\n{\n    struct winsize w;\n    struct cli_scrn *scrn = this_scrn;\n    struct sigaction sa;\n\n    if (!scrn) {\n        printf(\"*** scrn is NULL exit\\n\");\n        exit(-1);\n    }\n\n    rte_atomic32_set(&scrn->pause, SCRN_SCRN_PAUSED);\n\n    scrn->theme = theme;\n    scrn->type  = scrn_type;\n\n    if (scrn_type == SCRN_STDIN_TYPE) {\n        if (!scrn->oldterm_saved) {\n            if (tcgetattr(fileno(scrn->fd_in), &scrn->oldterm)) {\n                fprintf(stderr, \"%s: tcgetattr failed\\n\", __func__);\n                exit(-1);\n            }\n            scrn->oldterm_saved = 1;\n        }\n        memset(&sa, 0, sizeof(struct sigaction));\n        sa.sa_handler = handle_winch;\n        sigaction(SIGWINCH, &sa, NULL);\n\n        ioctl(0, TIOCGWINSZ, &w);\n\n        scrn->nrows = w.ws_row;\n        scrn->ncols = w.ws_col;\n\n        if (scrn_stdin_setup())\n            return -1;\n    } else if (scrn_type == SCRN_NOTTY_TYPE) {\n        scrn->nrows = 24;\n        scrn->ncols = 80;\n    } else {\n        fprintf(stderr, \"%s: unexpected scrn_type %d\\n\", __func__, scrn_type);\n        return -1;\n    }\n\n    scrn_color(SCRN_DEFAULT_FG, SCRN_DEFAULT_BG, SCRN_OFF);\n\n    return 0;\n}\n\nint\nscrn_create_with_defaults(int theme)\n{\n    int scrn_type = isatty(STDIN_FILENO) ? SCRN_STDIN_TYPE : SCRN_NOTTY_TYPE;\n\n    return scrn_create(scrn_type, (theme) ? SCRN_THEME_ON : SCRN_THEME_OFF);\n}\n\nvoid\nscrn_destroy(void)\n{\n    struct cli_scrn *scrn = this_scrn;\n\n    if (scrn && (scrn->type == SCRN_STDIN_TYPE))\n        scrn_stdin_restore();\n    free(scrn);\n    this_scrn = NULL;\n}\n\nRTE_INIT(scrn_constructor)\n{\n    struct cli_scrn *scrn;\n\n    scrn = calloc(1, sizeof(struct cli_scrn));\n    if (!scrn) {\n        printf(\"*** unable to allocate cli_scrn structure\\n\");\n        exit(-1);\n    }\n\n    this_scrn = scrn;\n\n    scrn_set_io(stdin, stdout);\n}\n"
  },
  {
    "path": "lib/cli/cli_scrn.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n/* Created by Keith Wiles @ intel.com */\n\n#ifndef __CLI_SCRN_H_\n#define __CLI_SCRN_H_\n\n#include <termios.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @file\n * RTE simple cursor and color support for VT100 using ANSI color escape codes.\n *\n ***/\n\n#include <string.h>\n#include <stdio.h>\n#include <stdarg.h>\n#include <unistd.h>\n\n#include <rte_atomic.h>\n#include <rte_per_lcore.h>\n\n#define VT100_INITIALIZE -1\n\n#define vt100_open_square '['\n#define vt100_escape      0x1b\n#define vt100_del         0x7f\n#define ESC               \"\\033\"\n\n/* Key codes */\n#define vt100_word_left  ESC \"b\"\n#define vt100_word_right ESC \"f\"\n#define vt100_suppr      ESC \"[3~\"\n#define vt100_tab        \"\\011\"\n\n/* Action codes for cli_vt100 */\n#define vt100_bell     \"\\007\"\n#define vt100_bs       \"\\010\"\n#define vt100_bs_clear \"\\b \\b\"\n\n/* cursor codes */\n#define vt100_cursor_off        ESC \"[?25l\"\n#define vt100_cursor_on         ESC \"[?25h\"\n#define vt100_save_cursor       ESC \"7\"\n#define vt100_restore_cursor    ESC \"8\"\n#define vt100_line_feed         ESC \"D\"\n#define vt100_crnl              ESC \"E\"\n#define vt100_reverse_line_feed ESC \"M\"\n#define vt100_up_arr            ESC \"[A\"\n#define vt100_down_arr          ESC \"[B\"\n#define vt100_right_arr         ESC \"[C\"\n#define vt100_left_arr          ESC \"[D\"\n#define vt100_up_lines          ESC \"[%dA\"\n#define vt100_down_lines        ESC \"[%dB\"\n#define vt100_right_columns     ESC \"[%dC\"\n#define vt100_left_columns      ESC \"[%dD\"\n#define vt100_home              ESC \"[H\"\n\n#define vt100_pos          ESC \"[%d;%dH\"\n#define vt100_setw         ESC \"[%d;r\"\n#define vt100_clear_right  ESC \"[0K\"\n#define vt100_clear_left   ESC \"[1K\"\n#define vt100_clear_down   ESC \"[0J\"\n#define vt100_clear_up     ESC \"[1J\"\n#define vt100_clear_line   ESC \"[2K\"\n#define vt100_clear_screen ESC \"[2J\"\n#define vt100_pos_cursor   ESC \"[%d;%dH\"\n#define vt100_multi_right  ESC \"\\133%uC\"\n#define vt100_multi_left   ESC \"\\133%uD\"\n\n/* Result of parsing : it must be synchronized with\n * vt100_cmds_list[] in vt100_cmds.c */\nenum {\n    VT100_INVALID_KEY = 0,\n    VT100_KEY_UP_ARR,\n    VT100_KEY_DOWN_ARR,\n    VT100_KEY_RIGHT_ARR,\n    VT100_KEY_LEFT_ARR,\n    VT100_KEY_BKSPACE,\n    VT100_KEY_RETURN,\n    VT100_KEY_CTRL_A,\n    VT100_KEY_CTRL_E,\n    VT100_KEY_CTRL_K,\n    VT100_KEY_CTRL_Y,\n    VT100_KEY_CTRL_C,\n    VT100_KEY_CTRL_F,\n    VT100_KEY_CTRL_B,\n    VT100_KEY_SUPPR,\n    VT100_KEY_TAB,\n    VT100_KEY_CTRL_D,\n    VT100_KEY_CTRL_L,\n    VT100_KEY_RETURN2,\n    VT100_KEY_META_BKSPACE,\n    VT100_KEY_WLEFT,\n    VT100_KEY_WRIGHT,\n    VT100_KEY_CTRL_W,\n    VT100_KEY_CTRL_P,\n    VT100_KEY_CTRL_N,\n    VT100_KEY_META_D,\n    VT100_KEY_CTRL_X,\n    VT100_MAX_KEYS\n};\n\nextern const char *vt100_commands[];\n\nenum vt100_parse_state {\n    VT100_INIT,\n    VT100_ESCAPE,\n    VT100_ESCAPE_CSI,\n    VT100_DONE     = -1,\n    VT100_CONTINUE = -2\n};\n\n#define VT100_BUF_SIZE 8\nstruct cli_vt100 {\n    int bufpos;                   /**< Current write offset into @p buf */\n    char buf[VT100_BUF_SIZE];     /**< Accumulator for multi-byte VT100 escape sequences */\n    enum vt100_parse_state state; /**< Current parser state for escape-sequence detection */\n};\n\nstruct vt100_cmds {\n    const char *str;\n    void (*func)(void);\n};\n\n/** scrn version number */\n#define SCRN_VERSION \"2.0.0\"\n\n/* Add more defines for new types */\n#define SCRN_STDIN_TYPE 0\n#define SCRN_NOTTY_TYPE 1\n\n/** Structure to hold information about the screen and control access. */\nstruct cli_scrn {\n    rte_atomic32_t pause __rte_cache_aligned; /**< Pause the update of the screen. */\n    uint16_t nrows;                           /**< Max number of rows. */\n    uint16_t ncols;                           /**< Max number of columns. */\n    uint16_t theme;                           /**< Current theme state on or off */\n    uint16_t type;                            /**< screen I/O type */\n    uint16_t no_write;                        /**< Disable screen write */\n    uint16_t oldterm_saved;                   /**< Old terminal setup saved */\n    struct termios oldterm;                   /**< Old terminal setup information */\n    FILE *fd_out;                             /**< File descriptor for output */\n    FILE *fd_in;                              /**< File descriptor for input */\n};\n\n/** A single byte to hold port of a Red/Green/Blue color value */\ntypedef uint8_t scrn_rgb_t;\n\nextern struct cli_scrn *this_scrn;\n\n/** Enable or disable the screen from being updated */\nenum { SCRN_SCRN_RUNNING = 0, SCRN_SCRN_PAUSED = 1 };\n\n/** Enable or disable the theme or color options */\nenum { SCRN_THEME_OFF = 0, SCRN_THEME_ON = 1 };\n\n/** ANSI color codes zero based, need to add 30 or 40 for foreground or\n   background color code */\ntypedef enum {\n    SCRN_BLACK         = 0,\n    SCRN_RED           = 1,\n    SCRN_GREEN         = 2,\n    SCRN_YELLOW        = 3,\n    SCRN_BLUE          = 4,\n    SCRN_MAGENTA       = 5,\n    SCRN_CYAN          = 6,\n    SCRN_WHITE         = 7,\n    SCRN_RGB           = 8,\n    SCRN_DEFAULT_FG    = 9,\n    SCRN_DEFAULT_BG    = 9,\n    SCRN_NO_CHANGE     = 98,\n    SCRN_UNKNOWN_COLOR = 99\n} scrn_color_e;\n\n/** ANSI color codes zero based for attributes per color */\ntypedef enum {\n    SCRN_OFF             = 0,\n    SCRN_BOLD            = 1,\n    SCRN_FAINT           = 2,\n    SCRN_ITALIC          = 3,\n    SCRN_UNDERSCORE      = 4,\n    SCRN_SLOW_BLINK      = 5,\n    SCRN_FAST_BLINK      = 6,\n    SCRN_REVERSE         = 7,\n    SCRN_CONCEALED       = 8,\n    SCRN_CROSSOUT        = 9,\n    SCRN_DEFAULT_FONT    = 10,\n    SCRN_UNDERLINE_OFF   = 24,\n    SCRN_BLINK_OFF       = 25,\n    SCRN_INVERSE_OFF     = 27,\n    SCRN_REVEAL          = 28,\n    SCRN_NOT_CROSSED_OUT = 29,\n    /* 30-39 and 40-49 Foreground and Background colors */\n    SCRN_FRAMED        = 51,\n    SCRN_ENCIRCLED     = 52,\n    SCRN_OVERLINED     = 53,\n    SCRN_NOT_FRAMED    = 54,\n    SCRN_NOT_OVERLINED = 55,\n    SCRN_NO_ATTR       = 98,\n    SCRN_UNKNOWN_ATTR  = 99\n} scrn_attr_e;\n\n#define SCRN_BLINK SCRN_SLOW_BLINK\n\n/** A single byte to hold part of a Red/Green/Blue color value */\ntypedef uint8_t cli_rgb_t;\n\nstatic inline int\nscrn_write(const void *str, int len)\n{\n    if (len <= 0)\n        len = strlen(str);\n\n    if (this_scrn->no_write == 0 && write(fileno(this_scrn->fd_out), str, len) != len)\n        fprintf(stderr, \"%s: Write failed\\n\", __func__);\n\n    return len;\n}\n\nstatic inline int\nscrn_read(char *buf, int len)\n{\n    int n = 0;\n\n    if (!buf || !len)\n        return 0;\n\n    while (len--)\n        n += read(fileno(this_scrn->fd_in), buf++, 1);\n    return n;\n}\n\n// clang-format off\nstatic inline void __attribute__((format(printf, 1, 2)))\nscrn_puts(const char *fmt, ...)\n{\n    struct cli_scrn *scrn = this_scrn;\n    FILE *f;\n    va_list vaList;\n\n    f = (!scrn || !scrn->fd_out) ? stdout : scrn->fd_out;\n    va_start(vaList, fmt);\n    vfprintf(f, fmt, vaList);\n    va_end(vaList);\n    fflush(f);\n}\n// clang-format on\n\nvoid scrn_cprintf(int16_t r, int16_t ncols, const char *fmt, ...);\nvoid scrn_printf(int16_t r, int16_t c, const char *fmt, ...);\nvoid scrn_fprintf(int16_t r, int16_t c, FILE *f, const char *fmt, ...);\n\n#define _s(_x, _y)                                           \\\n    static __inline__ void _x                                \\\n    {                                                        \\\n        if (this_scrn && this_scrn->type == SCRN_STDIN_TYPE) \\\n            _y;                                              \\\n        else                                                 \\\n            scrn_puts(\"\\n\");                                 \\\n    }\n\n/** position cursor to row and column */\n_s(scrn_pos(int r, int c), scrn_puts(vt100_pos, r, c))\n\n    /** Move cursor to the top left of the screen */\n    _s(scrn_top(void), scrn_puts(\"\\033H\"))\n\n    /** Move cursor to the Home position */\n    _s(scrn_home(void), scrn_puts(\"\\033H\"))\n\n    /** Turn cursor off */\n    _s(scrn_coff(void), scrn_puts(\"\\033[?25l\"))\n\n    /** Turn cursor on */\n    _s(scrn_con(void), scrn_puts(\"\\033[?25h\"))\n\n    /** Hide cursor */\n    _s(scrn_turn_on(void), scrn_puts(\"\\033[?25h\"))\n\n    /** Display cursor */\n    _s(scrn_turn_off(void), scrn_puts(\"\\033[?25l\"))\n\n    /** Save current cursor position */\n    _s(scrn_save(void), scrn_puts(\"\\0337\"))\n\n    /** Restore the saved cursor position */\n    _s(scrn_restore(void), scrn_puts(\"\\0338\"))\n\n    /** Clear from cursor to end of line */\n    _s(scrn_eol(void), scrn_puts(\"\\033[K\"))\n\n    /** Clear from cursor to beginning of line */\n    _s(scrn_cbl(void), scrn_puts(\"\\033[1K\"))\n\n    /** Clear entire line */\n    _s(scrn_cel(void), scrn_puts(\"\\033[2K\"))\n\n    /** Clear from cursor to end of screen */\n    _s(scrn_clw(void), scrn_puts(\"\\033[J\"))\n\n    /** Clear from cursor to beginning of screen */\n    _s(scrn_clb(void), scrn_puts(\"\\033[1J\"))\n\n    /** Clear the screen, move cursor to home */\n    _s(scrn_cls(void), scrn_puts(\"\\033[2J\"))\n\n    /** Start reverse video */\n    _s(scrn_reverse(void), scrn_puts(\"\\033[7m\"))\n\n    /** Stop attribute like reverse and underscore */\n    _s(scrn_normal(void), scrn_puts(\"\\033[0m\"))\n\n    /** Scroll whole screen up r number of lines */\n    _s(scrn_scroll(int r), scrn_puts(\"\\033[%d;r\", r))\n\n    /** Scroll whole screen up r number of lines */\n    _s(scrn_scroll_up(int r), scrn_puts(\"\\033[%dS\", r))\n\n    /** Scroll whole screen down r number of lines */\n    _s(scrn_scroll_down(int r), scrn_puts(\"\\033[%dT\", r))\n\n    /** Move down nlines plus move to column 1 */\n    _s(scrn_nlines(int r), scrn_puts(\"\\033[%dE\", r))\n\n    /** Set window size, from to end of screen */\n    _s(scrn_setw(int t), scrn_puts(\"\\033[%d;r\", t))\n\n    /** Cursor position report */\n    _s(scrn_cpos(void), scrn_puts(\"\\033[6n\"))\n\n    /** Cursor move right <n> characters */\n    _s(scrn_cnright(int n), scrn_puts(\"\\033[%dC\", n))\n\n    /** Cursor move left <n> characters */\n    _s(scrn_cnleft(int n), scrn_puts(\"\\033[%dD\", n))\n\n    /** New line */\n    _s(scrn_newline(void), scrn_puts(\"\\033[20h\"))\n\n    /** Move one character right */\n    _s(scrn_cright(void), scrn_puts(\"\\033[C\"))\n\n    /** Move one character left */\n    _s(scrn_cleft(void), scrn_puts(\"\\033[D\"))\n\n    /** Move cursor to beginning of line */\n    _s(scrn_bol(void), scrn_puts(\"\\r\"))\n\n    /** Return the version string */\n    static __inline__ const char *scrn_version(void)\n{\n    return SCRN_VERSION;\n}\n\n/** Position the cursor to a line and clear the entire line */\nstatic __inline__ void\nscrn_clr_line(int r)\n{\n    scrn_pos(r, 0);\n    scrn_cel();\n}\n\n/** Position cursor to row/column and clear to end of line */\nstatic __inline__ void\nscrn_eol_pos(int r, int c)\n{\n    scrn_pos(r, c);\n    scrn_eol();\n}\n\nvoid __set_prompt(void);\n\n/** Stop screen from updating until resumed later */\nstatic __inline__ void\nscrn_pause(void)\n{\n    if (!this_scrn)\n        printf(\"**** this scrn is NULL\\n\");\n    rte_atomic32_set(&this_scrn->pause, SCRN_SCRN_PAUSED);\n    __set_prompt();\n}\n\n/** Resume the screen from a pause */\nstatic __inline__ void\nscrn_resume(void)\n{\n    rte_atomic32_set(&this_scrn->pause, SCRN_SCRN_RUNNING);\n    __set_prompt();\n}\n\n/* Is the screen in the paused state */\nstatic __inline__ int\nscrn_is_paused(void)\n{\n    return rte_atomic32_read(&this_scrn->pause) == SCRN_SCRN_PAUSED;\n}\n\n/** Output a message of the current line centered */\nstatic __inline__ int\nscrn_center_col(int16_t ncols, const char *msg)\n{\n    int16_t s;\n\n    s = ((ncols / 2) - (strlen(msg) / 2));\n    return (s <= 0) ? 1 : s;\n}\n\n/** Erase the screen by scrolling it off the display, then put cursor at the\n   bottom */\nstatic __inline__ void\nscrn_erase(int16_t nrows)\n{\n    scrn_setw(1);           /* Clear the window to full screen. */\n    scrn_pos(nrows + 1, 1); /* Put cursor on the last row. */\n}\n\n/** Output a string at a row/column for a number of times */\nstatic __inline__ void\nscrn_repeat(int16_t r, int16_t c, const char *str, int cnt)\n{\n    int i;\n\n    scrn_pos(r, c);\n    for (i = 0; i < cnt; i++)\n        scrn_printf(0, 0, \"%s\", str);\n}\n\n/** Output a column of strings at a given starting row for a given number of\n   times */\nstatic __inline__ void\nscrn_col_repeat(int16_t r, int16_t c, const char *str, int cnt)\n{\n    int i;\n\n    for (i = 0; i < cnt; i++) {\n        scrn_pos(r++, c);\n        scrn_printf(0, 0, \"%s\", str);\n    }\n}\n\n/** Set the foreground color + attribute at the current cursor position */\nstatic __inline__ void\nscrn_fgcolor(scrn_color_e color, scrn_attr_e attr)\n{\n    scrn_puts(\"\\033[%d;%dm\", attr, color + 30);\n}\n\n/** Set the background color + attribute at the current cursor position */\nstatic __inline__ void\nscrn_bgcolor(scrn_color_e color, scrn_attr_e attr)\n{\n    scrn_puts(\"\\033[%d;%dm\", attr, color + 40);\n}\n\n/** Set the foreground/background color + attribute at the current cursor\n   position */\nstatic __inline__ void\nscrn_fgbgcolor(scrn_color_e fg, scrn_color_e bg, scrn_attr_e attr)\n{\n    scrn_puts(\"\\033[%d;%d;%dm\", attr, fg + 30, bg + 40);\n}\n\n/** Main routine to set color for foreground and background nd attribute at the\n   current position */\nstatic __inline__ void\nscrn_color(scrn_color_e fg, scrn_color_e bg, scrn_attr_e attr)\n{\n\n    if ((fg != SCRN_NO_CHANGE) && (bg != SCRN_NO_CHANGE))\n        scrn_fgbgcolor(fg, bg, attr);\n    else if (fg == SCRN_NO_CHANGE)\n        scrn_bgcolor(bg, attr);\n    else if (bg == SCRN_NO_CHANGE)\n        scrn_fgcolor(fg, attr);\n}\n\n/** Setup for 256 RGB color methods. A routine to output RGB color codes if\n   supported */\nstatic __inline__ void\nscrn_rgb(uint8_t fg_bg, cli_rgb_t r, cli_rgb_t g, cli_rgb_t b)\n{\n    scrn_puts(\"\\033[%d;2;%d;%d;%dm\", fg_bg, r, g, b);\n}\n\n/** Set the foreground color + attribute at the current cursor position */\nstatic __inline__ int\nscrn_fgcolor_str(char *str, scrn_color_e color, scrn_attr_e attr)\n{\n    return snprintf(str, 16, ESC \"[%d;%dm\", attr, color + 30);\n}\n\n/** Set the background color + attribute at the current cursor position */\nstatic __inline__ int\nscrn_bgcolor_str(char *str, scrn_color_e color, scrn_attr_e attr)\n{\n    return snprintf(str, 16, ESC \"[%d;%dm\", attr, color + 40);\n}\n\n/** Set the foreground/background color + attribute at the current cursor position */\nstatic __inline__ int\nscrn_fgbgcolor_str(char *str, scrn_color_e fg, scrn_color_e bg, scrn_attr_e attr)\n{\n    return snprintf(str, 16, ESC \"[%d;%d;%dm\", attr, fg + 30, bg + 40);\n}\n\n/**\n * Main routine to set color for foreground and background and attribute at\n * the current position.\n */\nstatic __inline__ int\nscrn_color_str(char *str, scrn_color_e fg, scrn_color_e bg, scrn_attr_e attr)\n{\n    if ((fg != SCRN_NO_CHANGE) && (bg != SCRN_NO_CHANGE))\n        return scrn_fgbgcolor_str(str, fg, bg, attr);\n    else if (fg == SCRN_NO_CHANGE)\n        return scrn_bgcolor_str(str, bg, attr);\n    else if (bg == SCRN_NO_CHANGE)\n        return scrn_fgcolor_str(str, fg, attr);\n    else\n        return 0;\n}\n\n/** Setup for 256 RGB color methods. A routine to output RGB color codes if supported */\nstatic __inline__ int\nscrn_rgb_str(char *str, uint8_t fg_bg, scrn_rgb_t r, scrn_rgb_t g, scrn_rgb_t b)\n{\n    return snprintf(str, 16, ESC \"[%d;2;%d;%d;%dm\", fg_bg, r, g, b);\n}\n\n/**\n * Create and initialise the global cli_scrn instance.\n *\n * @param scrn_type\n *   Screen I/O type: SCRN_STDIN_TYPE for interactive TTY,\n *   SCRN_NOTTY_TYPE for non-TTY (pipe/file) output.\n * @param theme\n *   Initial theme state: SCRN_THEME_ON or SCRN_THEME_OFF.\n * @return\n *   0 on success or -1 on error.\n */\nint scrn_create(int scrn_type, int theme);\n\n/**\n * Create the global cli_scrn with default stdin/stdout I/O and theme off.\n *\n * @param theme\n *   Initial theme state: SCRN_THEME_ON or SCRN_THEME_OFF.\n * @return\n *   0 on success or -1 on error.\n */\nint scrn_create_with_defaults(int theme);\n\n/**\n * Destroy the global cli_scrn instance and free its resources.\n */\nvoid scrn_destroy(void);\n\n/**\n * Create the cli_vt100 structure\n *\n * @return\n * Pointer to cli_vt100 structure or NULL on error\n */\nstruct cli_vt100 *vt100_setup(void);\n\n/**\n * Destroy the cli_vt100 structure\n *\n * @param\n *  The pointer to the cli_vt100 structure.\n */\nvoid vt100_free(struct cli_vt100 *vt);\n\n/**\n * Input a new character.\n *\n * @param vt\n *   The pointer to the cli_vt100 structure\n * @param c\n *   The character to parse for cli_vt100 commands\n * @return\n *   -1 if the character is not part of a control sequence\n *   -2 if c is not the last char of a control sequence\n *   Else the index in vt100_commands[]\n */\nint vt100_parse_input(struct cli_vt100 *vt, uint8_t c);\n\n/**\n * Execute the VT100 command at the given index in vt100_commands[].\n *\n * @param idx\n *   Index into the vt100_commands[] dispatch table (VT100_KEY_* enum value).\n */\nvoid vt100_do_cmd(int idx);\n\n/**\n * Return the vt100_cmds dispatch table.\n *\n * @return\n *   Pointer to the internal vt100_cmds array.\n */\nstruct vt100_cmds *vt100_get_cmds(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __CLI_SCRN_H_ */\n"
  },
  {
    "path": "lib/cli/cli_search.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#include <rte_string_fns.h>\n#include <pg_strings.h>\n\n#include \"cli.h\"\n\nstatic int\n__count_nodes(struct cli_node *node, uint32_t flags, args_t *args)\n{\n    if (flags & node->type)\n        args->arg1.u32[0]++;\n\n    return 0;\n}\n\nuint32_t\ncli_dir_item_count(struct cli_node *node, uint32_t types)\n{\n    args_t args;\n\n    if (!node || !is_directory(node))\n        return 0;\n\n    memset(&args, '\\0', sizeof(args));\n\n    cli_scan_directory(node, __count_nodes, types, &args);\n\n    return args.arg1.u32[0];\n}\n\nuint32_t\ncli_path_item_count(uint32_t types)\n{\n    uint32_t cnt = 0, i;\n\n    /* Look in the current directory first for a command */\n    for (i = 0; i < CLI_MAX_BINS; i++)\n        cnt += cli_dir_item_count(this_cli->bins[i], types);\n\n    return cnt;\n}\n\nuint32_t\ncli_path_cmd_count(void)\n{\n    return cli_path_item_count(CLI_EXE_TYPE);\n}\n\nstatic uint32_t\nnode_list_with_type(uint32_t flags, void **ret)\n{\n    struct cli_node *n, **nodes, *bin;\n    uint32_t cnt, i, k = 0;\n\n    cnt = cli_path_item_count(flags);\n\n    if (cnt) {\n        nodes = calloc(cnt + 1, sizeof(struct cli_node *));\n        if (!nodes)\n            return 0;\n\n        for (i = 0; i < CLI_MAX_BINS; i++) {\n            bin = this_cli->bins[i];\n            if (!bin)\n                continue;\n            /*\n             * Current directory could be a bin directory skip this bin\n             * directory as the cwd has already been searched. The cwd is\n             * the first entry in the bins list.\n             */\n            if ((i > 0) && (bin == get_cwd())) {\n                continue;\n            }\n\n            TAILQ_FOREACH (n, &bin->items, next) {\n                if (n->type & flags)\n                    nodes[k++] = n;\n            }\n        }\n        *ret = nodes;\n    }\n    return k;\n}\n\nstatic uint32_t\ndir_list_with_type(struct cli_node *dir, uint32_t flags, void **ret)\n{\n    struct cli_node *n, **nodes;\n    uint32_t cnt, k = 0;\n\n    cnt = cli_dir_item_count(dir, flags);\n\n    if (cnt) {\n        nodes = calloc(cnt + 1, sizeof(struct cli_node *));\n        if (!nodes)\n            return 0;\n\n        TAILQ_FOREACH (n, &dir->items, next) {\n            if (n->type & flags)\n                nodes[k++] = n;\n        }\n        *ret = nodes;\n    }\n    return k;\n}\n\nuint32_t\ncli_node_list_with_type(struct cli_node *node, uint32_t flags, void **ret)\n{\n    if (node)\n        return dir_list_with_type(node, flags, ret);\n    else\n        return node_list_with_type(flags, ret);\n}\n\nvoid\ncli_node_list_free(void *nodes)\n{\n    free(nodes);\n}\n\nint\ncli_scan_directory(struct cli_node *dir, cli_scan_t func, uint32_t flags, args_t *args)\n{\n    struct cli_node *node;\n    int ret = 0;\n\n    if (!func)\n        return ret;\n\n    if (!dir)\n        dir = cli_root_node();\n\n    TAILQ_FOREACH (node, &dir->items, next) {\n        if (node->type & flags) {\n            ret = func(node, flags, args);\n            if (ret)\n                break;\n        }\n    }\n    return ret;\n}\n\nint\ncli_scan_path(const char *path, cli_scan_t func, uint32_t flags, args_t *args)\n{\n    struct cli_node *node;\n\n    if (cli_find_node(path, &node))\n        if (cli_scan_directory(node, func, flags, args))\n            return 1;\n    return 0;\n}\n\nstruct cli_node *\ncli_search_dir(struct cli_node *dir, const char *name, uint32_t type)\n{\n    struct cli_node *node;\n\n    if (!name || (*name == '\\0'))\n        return NULL;\n\n    if (!dir)\n        dir = get_cwd();\n    else if (!is_directory(dir))\n        return NULL;\n\n    /* Process the .. and . directories */\n    if (!strcmp(name, \"..\"))\n        return dir->parent;\n    else if (!strcmp(name, \".\"))\n        return dir;\n\n    TAILQ_FOREACH (node, &dir->items, next) {\n        if (pg_strmatch(node->name, name) && (node->type & type))\n            return node;\n    }\n\n    return NULL;\n}\n\nint\ncli_find_node(const char *path, struct cli_node **ret)\n{\n    struct cli_node *node, *dir;\n    char *my_path = NULL;\n    char *argv[CLI_MAX_ARGVS + 1];\n    int n, i;\n\n    if (!path || (*path == '\\0'))\n        return 0;\n\n    if (path[0] == '/' && path[1] == '\\0') {\n        node = cli_root_node();\n        goto _leave;\n    }\n\n    /* Skip the leading '/' */\n    my_path = strdup((path[0] == '/') ? &path[1] : path);\n    if (!my_path)\n        return 0;\n\n    n = pg_strtok(my_path, \"/\", argv, CLI_MAX_ARGVS);\n\n    /* handle the special case of leading '/' */\n    dir = (path[0] == '/') ? get_root() : get_cwd();\n\n    /* Follow the directory path if present */\n    for (i = 0, node = NULL; i < n; i++) {\n        node = cli_search_dir(dir, argv[i], CLI_ALL_TYPE);\n\n        if (!node)\n            break;\n\n        /* follow the next directory */\n        if (is_directory(node) && (i < n))\n            dir = node;\n        else\n            break;\n    }\n\n    free(my_path);\n\n_leave:\n    if (ret)\n        *ret = node;\n\n    return (node) ? 1 : 0;\n}\n\nstruct cli_node *\ncli_last_dir_in_path(const char *path)\n{\n    struct cli_node *node, *dir;\n    char *my_path = NULL;\n    char *argv[CLI_MAX_ARGVS + 1];\n    int n, i;\n\n    if (!path || (*path == '\\0'))\n        return get_cwd();\n\n    if (path[0] == '/' && path[1] == '\\0')\n        return cli_root_node();\n\n    /* Skip the leading '/' */\n    my_path = strdup((path[0] == '/') ? &path[1] : path);\n    if (!my_path)\n        return NULL;\n\n    n = pg_strtok(my_path, \"/\", argv, CLI_MAX_ARGVS);\n\n    /* handle the special case of leading '/' */\n    if (path[0] == '/')\n        dir = this_cli->root.tqh_first;\n    else\n        dir = get_cwd();\n\n    /* Follow the directory path if present */\n    for (i = 0, node = NULL; i < n; i++) {\n        node = cli_search_dir(dir, argv[i], CLI_ALL_TYPE);\n\n        if (!node)\n            break;\n\n        /* follow the next directory */\n        if (is_directory(node) && (i < n))\n            dir = node;\n        else\n            break;\n    }\n\n    free(my_path);\n\n    return dir;\n}\n\nstruct cli_node *\ncli_find_cmd(const char *path)\n{\n    struct cli_node *cmd, *dir;\n    int i;\n\n    if (cli_find_node(path, &cmd))\n        return cmd;\n\n    for (i = 0; i < CLI_MAX_BINS; i++) {\n        if ((dir = this_cli->bins[i]) == NULL)\n            continue;\n\n        cmd = cli_search_dir(dir, path, CLI_EXE_TYPE);\n        if (cmd)\n            return cmd;\n    }\n    return NULL;\n}\n"
  },
  {
    "path": "lib/cli/cli_search.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#ifndef _CLI_SEARCH_H_\n#define _CLI_SEARCH_H_\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdarg.h>\n#include <unistd.h>\n#include <inttypes.h>\n\n/**\n * @file\n * CLI node search and directory scanning.\n *\n * Provides helpers to locate nodes in the CLI tree (commands/files/directories)\n * and to enumerate nodes in a directory or along the executable search path.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Generic 64-bit argument word that can be interpreted several ways. */\ntypedef union {\n    void *voidp;     /**< Pointer value */\n    char chr[8];     /**< Up to 8 raw bytes */\n    uint64_t u64;    /**< 64-bit unsigned integer */\n    uint32_t u32[2]; /**< Two 32-bit unsigned integers */\n    uint16_t u16[4]; /**< Four 16-bit unsigned integers */\n} arg_u;\n\n/** Four-word argument block passed to cli_scan_t callbacks. */\ntypedef struct {\n    arg_u arg1; /**< Argument word 1 */\n    arg_u arg2; /**< Argument word 2 */\n    arg_u arg3; /**< Argument word 3 */\n    arg_u arg4; /**< Argument word 4 */\n} args_t;\n\nstruct cli;\nstruct cli_node;\n\n/** Callback invoked by cli_scan_directory() for each matching node. */\ntypedef int (*cli_scan_t)(struct cli_node *node, uint32_t flags, args_t *args);\n\n/**\n * Scan a directory and call the func with the node found.\n *\n * @note Uses a thread variable called this_cli.\n *\n * @param dir\n *   Node pointer to directory to scan\n * @param func\n *   cli_scan_t function pointer\n * @param flags\n *   Bitmap of node types to include (e.g., CLI_CMD_NODE|CLI_DIR_NODE)\n * @param args\n *   Optional argument block passed to @p func\n * @return\n *   0 on success or -1 on error\n */\nint cli_scan_directory(struct cli_node *dir, cli_scan_t func, uint32_t flags, args_t *args);\n\n/**\n * Find a node by path\n *\n * @note Uses a thread variable called this_cli.\n *\n * @param path\n *   Path to node\n * @param ret\n *   Pointer to pointer of a cli_node if found\n * @return\n *   1 if found else 0\n */\nint cli_find_node(const char *path, struct cli_node **ret);\n\n/**\n * Search the local and bin directories for a command\n *\n * @note Uses a thread variable called this_cli.\n *\n * @param path\n *   String for the command to use\n * @return\n *   Pointer to the command node or NULL\n */\nstruct cli_node *cli_find_cmd(const char *path);\n\n/**\n * Count the number of nodes of the given type(s) in a directory.\n *\n * @note Uses a thread variable called this_cli.\n *\n * @param n\n *   node or NULL for current working directory\n * @return\n *   Number of nodes found of this type in the directory\n */\nuint32_t cli_dir_item_count(struct cli_node *node, uint32_t types);\n\n/**\n * Count the number of commands in the execute path\n *\n * @note Uses a thread variable called this_cli.\n *\n * @return\n *   Number of nodes found of this type in the directory\n */\nuint32_t cli_path_cmd_count(void);\n\n/**\n * Return a list of nodes matching given information\n *\n * @note Uses a thread variable called this_cli.\n *\n * @param node\n *   Node to start search or use the path list.\n * @param flags\n *   Type of nodes to return\n * @param ret\n *   Pointer to an array of pointer for return value\n * @return\n *   Number of nodes found of this type in the directory\n */\nuint32_t cli_node_list_with_type(struct cli_node *node, uint32_t flags, void **ret);\n\n/**\n * Free a list returned by cli_node_list_with_type().\n *\n * @note Uses a thread variable called this_cli.\n *\n * @param node\n *   Pointer to the node to free\n * @return\n *   N/A\n */\nvoid cli_node_list_free(void *nodes);\n\n/**\n * Count the number of commands in the execute path\n *\n * @note Uses a thread variable called this_cli.\n *\n * @param types\n *   The number of nodes to count\n * @return\n *   Number of nodes found of this type in the directory\n */\nuint32_t cli_path_item_count(uint32_t types);\n\n/**\n * Find and return the last directory node in a given path string.\n *\n * @note Uses a thread variable called this_cli.\n *\n * @param path\n *   Path string to scan\n * @return\n *   Pointer to last directory node in path\n */\nstruct cli_node *cli_last_dir_in_path(const char *path);\n\n/**\n * Scan a directory for a node matching a name and type.\n *\n * @note Uses a thread variable called this_cli.\n *\n * @param dir\n *   Pointer to directory node to start with in scanning\n * @param name\n *   String to match the nodes with\n * @param type\n *   Type of nodes to include in scan.\n * @return\n *   Number of nodes found of this type in the directory\n */\nstruct cli_node *cli_search_dir(struct cli_node *dir, const char *name, uint32_t type);\n\n/**\n * Scan the directory given by @p path.\n *\n * @note Uses a thread variable called this_cli.\n *\n * @param path\n *   The path string to use\n * @param func\n *   The function to call when a match is found.\n * @param flags\n *   Type of files to include in match\n * @param args\n *   Arguments to include with function call.\n * @return\n *   Number of nodes found of this type in the directory\n */\nint cli_scan_path(const char *path, cli_scan_t func, uint32_t flags, args_t *args);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _CLI_SEARCH_H_ */\n"
  },
  {
    "path": "lib/cli/cli_vt100.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2016-2026>, Intel Corporation.\n */\n\n#include \"cli.h\"\n#include \"cli_input.h\"\n#include \"cli_scrn.h\"\n#include \"cli_auto_complete.h\"\n\nstatic inline void\nkey_up_arr(void)\n{\n    struct gapbuf *gb = this_cli->gb;\n    char *line;\n\n    line = cli_history_prev();\n    if (line) {\n        gb_reset_buf(gb);\n        gb_set_point(gb, gb_str_insert(gb, line, 0));\n        cli_set_flag(CLEAR_LINE);\n    }\n}\n\nstatic inline void\nkey_down_arr(void)\n{\n    struct gapbuf *gb = this_cli->gb;\n    char *line;\n\n    line = cli_history_next();\n    if (line) {\n        gb_reset_buf(gb);\n        gb_set_point(gb, gb_str_insert(gb, line, 0));\n        cli_set_flag(CLEAR_LINE);\n    }\n}\n\nstatic inline void\nkey_right_arr(void)\n{\n    struct gapbuf *gb = this_cli->gb;\n\n    if (!gb_eof(gb)) {\n        scrn_write(vt100_right_arr, 0);\n        gb_move_right(gb);\n    }\n}\n\nstatic inline void\nkey_left_arr(void)\n{\n    struct gapbuf *gb = this_cli->gb;\n\n    if (!gb_point_at_start(gb)) {\n        scrn_write(vt100_left_arr, 0);\n        gb_move_left(gb);\n    }\n}\n\nstatic inline void\nkey_backspace(void)\n{\n    struct gapbuf *gb = this_cli->gb;\n\n    if (!gb_point_at_start(gb)) {\n        cli_cursor_left();\n\n        gb_move_left(gb);\n\n        gb_del(gb, 1);\n\n        cli_set_flag(DELETE_CHAR);\n    }\n}\n\nstatic inline void\nkey_return(void)\n{\n    cli_write(\"\\n\", 1);\n\n    cli_execute();\n\n    /* Init buffer must be after execute of command */\n    gb_reset_buf(this_cli->gb);\n\n    /* If not quit command then print prompt */\n    if (!this_cli->quit_flag)\n        this_cli->flags |= DISPLAY_PROMPT;\n    this_cli->curr_hist = NULL;\n}\n\nstatic inline void\nkey_ctrl_a(void)\n{\n    cli_printf(vt100_multi_left, gb_left_data_size(this_cli->gb));\n    gb_set_point(this_cli->gb, 0);\n}\n\nstatic inline void\nkey_ctrl_e(void)\n{\n    cli_printf(vt100_multi_right, gb_right_data_size(this_cli->gb));\n    gb_set_point(this_cli->gb, -1);\n}\n\nstatic inline void\nkey_ctrl_k(void)\n{\n    struct cli *cli = this_cli;\n\n    cli_clear_to_eol();\n    gb_move_gap_to_point(cli->gb);\n    free(cli->kill);\n    if (gb_right_data_size(cli->gb))\n        cli->kill = strndup(gb_end_of_gap(cli->gb), gb_right_data_size(cli->gb));\n    gb_del(cli->gb, gb_right_data_size(cli->gb));\n}\n\nstatic inline void\nkey_ctrl_y(void)\n{\n    struct cli *cli = this_cli;\n\n    /* Yank and put are Not supported yet */\n    if (cli->kill) {\n        gb_str_insert(cli->gb, cli->kill, strlen(cli->kill));\n        cli_clear_line(-1);\n        cli_redisplay_line();\n    }\n}\n\nstatic inline void\nkey_ctrl_c(void)\n{\n    gb_reset_buf(this_cli->gb);\n    cli_clear_line(-1);\n    this_cli->plen      = this_cli->prompt(0);\n    this_cli->curr_hist = NULL;\n}\n\nstatic inline void\nkey_ctrl_f(void)\n{\n    key_right_arr();\n}\n\nstatic inline void\nkey_ctrl_b(void)\n{\n    key_left_arr();\n}\n\n// This is the 'super key' which has had many usages in the past. I changed it to\n// but the back space key as it seems windows or mobaxterm sends this code to delete key.\nstatic inline void\nkey_suppr(void)\n{\n    struct gapbuf *gb = this_cli->gb;\n\n    if (!gb_point_at_start(gb)) {\n        cli_cursor_left();\n\n        gb_move_left(gb);\n\n        gb_del(gb, 1);\n\n        cli_set_flag(DELETE_CHAR);\n    }\n}\n\nstatic inline void\nkey_tab(void)\n{\n    cli_auto_complete();\n}\n\nstatic inline void\nkey_ctrl_d(void)\n{\n    gb_dump(this_cli->gb, NULL);\n}\n\nstatic inline void\nkey_ctrl_l(void)\n{\n    cli_clear_screen();\n    cli_clear_line(-1);\n    cli_redisplay_line();\n}\n\nstatic inline void\nkey_return2(void)\n{\n    key_return();\n}\n\nstatic inline void\nkey_meta_backspace(void)\n{\n}\n\n/* meta+b or command+b or window+b or super+b */\nstatic inline void\nkey_word_left(void)\n{\n    do {\n        key_left_arr();\n        if (gb_get_prev(this_cli->gb) == ' ')\n            break;\n    } while (!gb_point_at_start(this_cli->gb));\n}\n\nstatic inline void\nkey_word_right(void)\n{\n    while (!gb_point_at_end(this_cli->gb)) {\n        key_right_arr();\n        if (gb_get(this_cli->gb) == ' ')\n            break;\n    }\n}\n\nstatic inline void\nkey_ctrl_w(void)\n{\n    key_meta_backspace();\n}\n\nstatic inline void\nkey_ctrl_p(void)\n{\n    key_up_arr();\n}\n\nstatic inline void\nkey_ctrl_n(void)\n{\n    key_down_arr();\n}\n\nstatic inline void\nkey_meta_d(void)\n{\n}\n\nstatic inline void\nkey_ctrl_x(void)\n{\n    this_cli->quit_flag = 1;\n}\n\nstatic inline void\nkey_invalid(void)\n{\n}\n\n/* Order must be maintained see cli_scrn.h */\nstatic struct vt100_cmds vt100_cmd_list[] = {\n    {\"Invalid\", key_invalid},\n    {vt100_up_arr, key_up_arr},         /* Move cursor up one line */\n    {vt100_down_arr, key_down_arr},     /* Move cursor down on line */\n    {vt100_right_arr, key_right_arr},   /* Move cursor right */\n    {vt100_left_arr, key_left_arr},     /* Move cursor left */\n    {\"\\177\", key_backspace},            /* Cursor Left + delete */\n    {\"\\n\", key_return},                 /* Execute command */\n    {\"\\001\", key_ctrl_a},               /* Same as key_left_arr */\n    {\"\\005\", key_ctrl_e},               /* Same as key_right_arr */\n    {\"\\013\", key_ctrl_k},               /* Kill to end of line */\n    {\"\\031\", key_ctrl_y},               /* Put the kill buffer not working */\n    {\"\\003\", key_ctrl_c},               /* Reset line and start over */\n    {\"\\006\", key_ctrl_f},               /* Same as key_right_arr */\n    {\"\\002\", key_ctrl_b},               /* Same as key_left_arr */\n    {vt100_suppr, key_suppr},           /* delete 1 char from the left */\n    {vt100_tab, key_tab},               /* Auto complete */\n    {\"\\004\", key_ctrl_d},               /* Debug output if enabled */\n    {\"\\014\", key_ctrl_l},               /* redraw screen */\n    {\"\\r\", key_return2},                /* Same as key_return */\n    {\"\\033\\177\", key_meta_backspace},   /* Delete word left */\n    {vt100_word_left, key_word_left},   /* Word left */\n    {vt100_word_right, key_word_right}, /* Word right */\n    {\"\\027\", key_ctrl_w},               /* Same as key_meta_backspace */\n    {\"\\020\", key_ctrl_p},               /* Same as key_up_arr */\n    {\"\\016\", key_ctrl_n},               /* Same as key_down_arr */\n    {\"\\033\\144\", key_meta_d},           /* Delete word right */\n    {\"\\030\", key_ctrl_x},               /* Terminate application */\n    {NULL, NULL}};\n\nvoid\nvt100_do_cmd(int idx)\n{\n    if (idx < VT100_MAX_KEYS)\n        vt100_cmd_list[idx].func();\n}\n\nstruct vt100_cmds *\nvt100_get_cmds(void)\n{\n    return vt100_cmd_list;\n}\n\nstatic int\nvt100_find_cmd(char *buf, unsigned int size)\n{\n    struct vt100_cmds *cmd;\n    size_t cmdlen;\n    int i;\n\n    for (i = 0, cmd = vt100_get_cmds(); cmd->str; cmd++, i++) {\n        cmdlen = strnlen(cmd->str, VT100_BUF_SIZE);\n        if ((size == cmdlen) && !strncmp(buf, cmd->str, cmdlen))\n            return i;\n    }\n\n    return VT100_DONE;\n}\n\nint\nvt100_parse_input(struct cli_vt100 *vt, uint8_t c)\n{\n    uint32_t size;\n\n    RTE_ASSERT(vt != NULL);\n\n    if ((vt->bufpos == VT100_INITIALIZE) || (vt->bufpos >= VT100_BUF_SIZE)) {\n        vt->state  = VT100_INIT;\n        vt->bufpos = 0;\n    }\n\n    vt->buf[vt->bufpos++] = c;\n    size                  = vt->bufpos;\n\n    switch (vt->state) {\n    case VT100_INIT:\n        if (c == vt100_escape)\n            vt->state = VT100_ESCAPE;\n        else {\n            vt->bufpos = VT100_INITIALIZE;\n            return vt100_find_cmd(vt->buf, size);\n        }\n        break;\n\n    case VT100_ESCAPE:\n        if (c == vt100_open_square)\n            vt->state = VT100_ESCAPE_CSI;\n        else if (c >= '0' && c <= vt100_del) {\n            vt->bufpos = VT100_INITIALIZE;\n            return vt100_find_cmd(vt->buf, size);\n        }\n        break;\n\n    case VT100_ESCAPE_CSI:\n        if (c >= '@' && c <= '~') {\n            vt->bufpos = VT100_INITIALIZE;\n            return vt100_find_cmd(vt->buf, size);\n        }\n        break;\n\n    default:\n        vt->bufpos = VT100_INITIALIZE;\n        break;\n    }\n\n    return VT100_CONTINUE;\n}\n\nstruct cli_vt100 *\nvt100_setup(void)\n{\n    struct cli_vt100 *vt;\n\n    vt = calloc(1, sizeof(struct cli_vt100));\n    if (!vt)\n        return NULL;\n\n    vt->bufpos = -1;\n\n    return vt;\n}\n\nvoid\nvt100_free(struct cli_vt100 *vt)\n{\n    if (vt)\n        free(vt);\n}\n"
  },
  {
    "path": "lib/cli/meson.build",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2020-2026> Intel Corporation\n\nsources = files(\n\t'cli_auto_complete.c',\n\t'cli.c',\n\t'cli_cmap.c',\n\t'cli_cmds.c',\n\t'cli_env.c',\n\t'cli_file.c',\n\t'cli_gapbuf.c',\n\t'cli_help.c',\n\t'cli_history.c',\n\t'cli_input.c',\n\t'cli_map.c',\n\t'cli_scrn.c',\n\t'cli_search.c',\n\t'cli_vt100.c')\nlibcli = library('cli', sources,\n\tdependencies: [common, utils, dpdk])\ncli = declare_dependency(link_with: libcli,\n\tinclude_directories: include_directories('.'))\n"
  },
  {
    "path": "lib/common/cksum.c",
    "content": "/*-\n *   Copyright(c) <2014-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n/* includes */\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <string.h>\n#include <sys/queue.h>\n#include <netinet/in.h>\n#include <net/if.h>\n#include <fcntl.h>\n#include <setjmp.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <errno.h>\n#include <getopt.h>\n#include <termios.h>\n#include <sys/ioctl.h>\n#include <libgen.h>\n#include <linux/if_tun.h>\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <poll.h>\n#include <assert.h>\n\n#include <rte_version.h>\n#include <rte_config.h>\n\n#include <rte_log.h>\n#include <rte_tailq.h>\n#if defined(RTE_VER_MAJOR) && (RTE_VER_MAJOR < 2)\n#include <rte_tailq_elem.h>\n#endif\n#include <rte_common.h>\n#include <rte_memory.h>\n#include <rte_memcpy.h>\n#include <rte_memzone.h>\n#include <rte_malloc.h>\n#include <rte_eal.h>\n#include <rte_per_lcore.h>\n#include <rte_launch.h>\n#include <rte_atomic.h>\n#include <rte_cycles.h>\n#include <rte_prefetch.h>\n#include <rte_lcore.h>\n#include <rte_branch_prediction.h>\n#include <rte_pci.h>\n#include <rte_random.h>\n#include <rte_timer.h>\n#include <rte_debug.h>\n#include <rte_ether.h>\n#include <rte_ethdev.h>\n#include <rte_ring.h>\n#include <rte_mempool.h>\n#include <rte_mbuf.h>\n#include <rte_lpm.h>\n#include <rte_string_fns.h>\n#include <rte_byteorder.h>\n#include <rte_errno.h>\n\n#include \"copyright_info.h\"\n#include \"pg_compat.h\"\n#include \"port_config.h\"\n\n#include \"pg_inet.h\"\n#include \"cksum.h\"\n\n/**\n * cksum - Compute a 16 bit ones complement checksum value.\n *\n * DESCRIPTION\n * A wrapper routine to compute the complete 16 bit checksum value for a given\n * piece of memory, when the data is contiguous. The <cksum> value is a previous\n * checksum value to allow the user to build a checksum using different parts\n * of memory.\n *\n * \\is\n * \\i <pBuf> Pointer to the data buffer to be checksumed.\n * \\i <size> Number of bytes to checksum.\n * \\i <cksum> Previous checksum value else give 0.\n * \\ie\n *\n * RETURNS: 16 bit checksum value.\n *\n * ERRNO: N/A\n */\nuint16_t\ncksum(void *pBuf, int32_t size, uint32_t cksum)\n{\n    return cksumDone(cksumUpdate(pBuf, size, cksum));\n}\n\n/**\n * cksumUpdate - Calaculate an 16 bit checksum and return the 32 bit value\n *\n * DESCRIPTION\n * Will need to call pktgen_cksumDone to finish computing the checksum. The <cksum>\n * value is from any previous checksum call. The routine will not fold the upper\n * 16 bits into the 32 bit checksum. The pktgen_cksumDone routine will do the\n * folding of the upper 16 bits into a 16 bit checksum.\n *\n * \\is\n * \\i <pBuf> the pointer to the data to be checksumed.\n * \\i <size> the number of bytes to include in the checksum calculation.\n * \\i <cksum> the initial starting checksum value allowing the developer to\n * ckecksum different pieces of memory to get a final value.\n * \\ie\n *\n * RETURNS: unsigned 32 bit checksum value.\n *\n * ERRNO: N/A\n */\nuint32_t\ncksumUpdate(void *pBuf, int32_t size, uint32_t cksum)\n{\n    uint32_t nWords;\n    uint16_t *pWd = (uint16_t *)pBuf;\n\n    for (nWords = (size >> 5); nWords > 0; nWords--) {\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n        cksum += *pWd++;\n    }\n\n    /* handle the odd number size */\n    for (nWords = (size & 0x1f) >> 1; nWords > 0; nWords--)\n        cksum += *pWd++;\n\n    /* Handle the odd byte length */\n    if (size & 1)\n        cksum += *pWd & htons(0xFF00);\n\n    return cksum;\n}\n\n/**\n * cksumDone - Finish up the ckecksum value by folding the checksum.\n *\n * DESCRIPTION\n * Fold the carry bits back into the checksum value to complete the 16 bit\n * checksum value. This routine is called after all of the pktgen_cksumUpdate\n * calls have been completed and the 16bit result is required.\n *\n * \\is\n * \\i <cksum> the initial 32 bit checksum and returns a 16bit folded value.\n * \\ie\n *\n * RETURNS: 16 bit checksum value.\n *\n * ERRNO: N/A\n */\nuint16_t\ncksumDone(uint32_t cksum)\n{\n    /* Fold at most twice */\n    cksum = (cksum & 0xFFFF) + (cksum >> 16);\n    cksum = (cksum & 0xFFFF) + (cksum >> 16);\n\n    return ~((uint16_t)cksum);\n}\n\n/**\n * pseudoChecksum - Compute the Pseudo Header checksum.\n *\n * DESCRIPTION\n * The pseudo header checksum is done in IP for TCP/UDP by computing the values\n * passed into the routine into a return value, which is a 32bit checksum. The\n * 32bit value contains any carry bits and will be added to the final value.\n *\n * \\is\n * \\i <src> Source IP address.\n * \\i <dst> Destination IP address.\n * \\i <pro> The protocol type.\n * \\i <len> Length of the data packet.\n * \\i <sum> Previous checksum value if needed.\n * \\ie\n *\n * RETURNS: 32bit checksum value.\n *\n * ERRNO: N/A\n */\nuint32_t\npseudoChecksum(uint32_t src, uint32_t dst, uint16_t pro, uint16_t len, uint32_t sum)\n{\n    /* Compute the Pseudo Header checksum */\n    return sum + (src & 0xFFFF) + (src >> 16) + (dst & 0xFFFF) + (dst >> 16) + ntohs(len) +\n           ntohs(pro);\n}\n\n/**\n * pseudoIPv6Checksum - Compute the Pseudo Header checksum.\n *\n * DESCRIPTION\n * The pseudo header checksum is done in IP for TCP/UDP by computing the values\n * passed into the routine into a return value, which is a 32bit checksum. The\n * 32bit value contains any carry bits and will be added to the final value.\n *\n * \\is\n * \\i <src> Source IP address pointer.\n * \\i <dst> Destination IP address pointer.\n * \\i <next_hdr> The protocol type.\n * \\i <total_len> Length of the data packet TCP data.\n * \\i <sum> Previous checksum value if needed.\n * \\ie\n *\n * RETURNS: 32bit checksum value.\n *\n * ERRNO: N/A\n */\nuint32_t\npseudoIPv6Checksum(uint16_t *src, uint16_t *dst, uint8_t next_hdr, uint32_t total_len, uint32_t sum)\n{\n    uint32_t len = htonl(total_len), i;\n\n    sum = (sum + (uint16_t)next_hdr + (len & 0xFFFF) + (len >> 16));\n\n    for (i = 0; i < 8; i++) {\n        sum += src[i];\n        sum += dst[i];\n    }\n    return sum;\n}\n"
  },
  {
    "path": "lib/common/cksum.h",
    "content": "/*-\n *   Copyright(c) <2014-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef __CKSUM_H\n#define __CKSUM_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nuint16_t cksum(void *pBuf, int32_t size, uint32_t cksum);\nuint32_t cksumUpdate(void *pBuf, int32_t size, uint32_t cksum);\nuint16_t cksumDone(uint32_t cksum);\nuint32_t pseudoChecksum(uint32_t src, uint32_t dst, uint16_t proto, uint16_t len, uint32_t cksum);\nuint32_t pseudoIPv6Checksum(uint16_t *src, uint16_t *dst, uint8_t next_hdr, uint32_t total_len,\n                            uint32_t sum);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __CKSUM_H */\n"
  },
  {
    "path": "lib/common/cmdline_parse_args.c",
    "content": "/*-\n *   Copyright(c) <2015-2026>-2016 Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <inttypes.h>\n#include <ctype.h>\n#include <string.h>\n#include <errno.h>\n#include <stdarg.h>\n#include <netinet/in.h>\n#ifndef __linux__\n#include <net/socket.h>\n#endif\n\n#include <rte_common.h>\n\n#include \"cmdline_parse.h\"\n#include \"cmdline_parse_args.h\"\n\nstruct cmdline_token_ops cmdline_token_args_ops = {\n    .parse            = cmdline_parse_args,\n    .complete_get_nb  = NULL,\n    .complete_get_elt = NULL,\n    .get_help         = cmdline_get_help_args,\n};\n\nstatic char orig_cmdline[256];\n\nvoid\ncmdline_args_free(int argc, char **argv)\n{\n    int i;\n\n    if (argc <= 0)\n        return;\n\n    for (i = 0; i < argc; i++)\n        if (argv[i])\n            free(argv[i]);\n}\n\nint\ncmdline_parse_args(cmdline_parse_token_hdr_t *tk __rte_unused, const char *buf, void *res,\n                   unsigned tk_len __rte_unused)\n{\n    unsigned int token_len = 0, len = 0;\n    char args_str[XARGS_TOKEN_SIZE + 1];\n    cmdline_args_t *pl;\n\n    if (!buf)\n        buf = \" \";\n\n    if (res == NULL)\n        return -1;\n\n    pl       = res;\n    pl->argc = 1; /* Leave the zero entry empty */\n\n    snprintf(orig_cmdline, sizeof(orig_cmdline), \"%s\", buf);\n    do {\n        while (!cmdline_isendoftoken(buf[token_len]) && (token_len < XARGS_TOKEN_SIZE)) {\n            token_len++;\n            len++;\n        }\n        if (token_len == 0)\n            break;\n\n        if (token_len >= XARGS_TOKEN_SIZE)\n            return -1;\n\n        snprintf(args_str, token_len + 1, \"%s\", buf);\n        buf += token_len;\n        if (*buf == ' ') {\n            buf++;\n            len++;\n        }\n        token_len = 0;\n\n        pl->argv[pl->argc++] = strdup(args_str);\n    } while ((*buf != '\\n') && (pl->argc < XARGS_MAX_TOKENS));\n\n    pl->argv[pl->argc] = NULL;\n    pl->cmdline        = orig_cmdline;\n\n    return len;\n}\n\nint\ncmdline_get_help_args(__rte_unused cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size)\n{\n    int ret;\n\n    ret = snprintf(dstbuf, size, \"argc/argv list of arguments\");\n    if (ret < 0)\n        return -1;\n    return 0;\n}\n"
  },
  {
    "path": "lib/common/cmdline_parse_args.h",
    "content": "/*-\n *   Copyright(c) <2015-2026>-2016 Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#ifndef _PARSE_XARGS_H_\n#define _PARSE_XARGS_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* size of a parsed string */\n#define XARGS_TOKEN_SIZE 256\n#define XARGS_MAX_TOKENS 32\n\ntypedef struct cmdline_args {\n    char *cmdline;\n    int argc;\n    char *argv[XARGS_MAX_TOKENS + 1];\n} cmdline_args_t;\n\nstruct cmdline_token_args {\n    struct cmdline_token_hdr hdr;\n    struct cmdline_args args;\n};\n\ntypedef struct cmdline_token_args cmdline_parse_token_args_t;\n\nextern struct cmdline_token_ops cmdline_token_args_ops;\n\nint cmdline_parse_args(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,\n                       unsigned tk_len);\nint cmdline_get_help_args(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size);\n\n#define TOKEN_ARGS_INITIALIZER(structure, field)     \\\n    {                                                \\\n        /* hdr */                                    \\\n        {                                            \\\n            &cmdline_token_args_ops,    /* ops */    \\\n            offsetof(structure, field), /* offset */ \\\n        },                              /* args */   \\\n        {                                            \\\n            0,                                       \\\n        },                                           \\\n    }\n\nvoid cmdline_args_free(int argc, char **argv);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PARSE_XARGS_H_ */\n"
  },
  {
    "path": "lib/common/copyright_info.c",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2010-2024 by Keith Wiles @ intel.com */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <string.h>\n#include <stdarg.h>\n#include <ctype.h>\n\n#include <rte_version.h>\n#include <rte_config.h>\n#include <rte_atomic.h>\n#include <rte_cycles.h>\n\n#include \"copyright_info.h\"\n\n#define COPYRIGHT_MSG_SHORT \"Copyright(c) <2010-2026>, Intel Corporation\"\n#define COPYRIGHT_MSG       COPYRIGHT_MSG_SHORT \". All rights reserved.\"\n#define POWERED_BY_DPDK     \"Powered by\"\n\n/**\n *\n * pg_print_copyright - Print out the copyright notices.\n *\n * DESCRIPTION\n * Output the copyright notices.\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nprint_copyright(const char *appname, const char *created_by)\n{\n    printf(\"\\n*** %s\\n\", COPYRIGHT_MSG);\n    printf(\"*** %s created by: %s >>> %s %s <<<\\n\\n\", appname, created_by, POWERED_BY_DPDK,\n           rte_version());\n}\n\n/**\n * Function returning string for Copyright message.\"\n * @return\n *     string\n */\nconst char *\ncopyright_msg(void)\n{\n    return COPYRIGHT_MSG;\n}\n\n/**\n * Function returning short string for Copyright message.\"\n * @return\n *     string\n */\nconst char *\ncopyright_msg_short(void)\n{\n    return COPYRIGHT_MSG_SHORT;\n}\n\n/**\n * Function returning string for Copyright message.\"\n * @return\n *     string\n */\nconst char *\npowered_by(void)\n{\n    return POWERED_BY_DPDK;\n}\n"
  },
  {
    "path": "lib/common/copyright_info.h",
    "content": "/*-\n *   Copyright(c) <2014-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2013-2024 by Keith Wiles @ intel.com */\n\n#ifndef _COPYRIGHT_INFO_H\n#define _COPYRIGHT_INFO_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Print out a copyright string\n *\n * @param appname\n *   The name of the application\n * @param created_by\n *   The created_by string\n */\nvoid print_copyright(const char *appname, const char *created_by);\n\n/**\n * Function returning string for Copyright message.\n * @return\n *     string\n */\nconst char *copyright_msg(void);\n\n/**\n * Function returning short string for Copyright message.\n * @return\n *     string\n */\nconst char *copyright_msg_short(void);\n\n/**\n * Function returning string for Copyright message.\n * @return\n *     string\n */\nconst char *powered_by(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _COPYRIGHT_INFO_H */\n"
  },
  {
    "path": "lib/common/coreinfo.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright (c) 2023-2026 Intel Corporation\n */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <stdint.h>\n\n#include <rte_common.h>\n\n#include <hmap.h>\n#include \"coreinfo.h\"\n\n#define MAX_LINE_SZ      4096\n#define CI_SCT_FORMAT    \"S:%04d,C:%04d,T:%04d\" /* socket_id/core_id/thread_id */\n#define CI_LCORE_FORMAT  \"L:%04d\"               /* logical core id */\n#define CI_NUM_LCORES    \"num_lcores\"\n#define CI_NUM_CORES     \"num_cores\"\n#define CI_NUM_CPU_CORES \"num_cpu_cores\"\n#define CI_NUM_SOCKETS   \"num_sockets\"\n#define CI_NUM_THREADS   \"num_threads\"\n#define CI_NUM_SIBLINGS  \"num_siblings\"\n#define CI_MODEL_NAME    \"model_name\"\n\n#define CI_LOGICAL_ID   \"processor\"\n#define CI_MODEL_ID     \"model name\"\n#define CI_SOCKET_ID    \"physical id\"\n#define CI_CORE_ID      \"core id\"\n#define CI_CPU_CORES_ID \"cpu cores\"\n#define CI_SIBLINGS_ID  \"siblings\"\n#define CI_TERMINAL_ID  \"\\n\"\n\ntypedef struct {\n    uint16_t socket_id;\n    uint16_t core_id;\n    uint16_t thread_id;\n} lcore_t;\n\ntypedef struct {\n    char *model_name;       /**< CPU model string */\n    uint16_t max_socket_id; /**< Max sockets */\n    uint16_t max_threads;   /**< Max threads per socket */\n    coreinfo_t core;        /**< Core information */\n    hmap_t *map;            /**< coreinfo map */\n} coreinfo_data_t;\nstatic coreinfo_data_t coreinfo_data, *cid = &coreinfo_data;\n\ntypedef void (*do_line_fn)(const char *line);\ntypedef unsigned (*getter_fn)(void);\ntypedef void (*setter_fn)(unsigned new_val);\n\nstatic char *\nget_value(char *line)\n{\n    if (*line == '\\0' || *line == '\\n')\n        return line;\n\n    line[strlen(line) - 1] = '\\0'; /* remove newline */\n\n    while (*line != ':')\n        line++;\n    *line++ = '\\0';\n\n    while (*line == ' ')\n        line++;\n\n    return line;\n}\n\nstatic void\nset_lcore_id(const char *line)\n{\n    cid->core.lcore_id = atoi(line);\n}\n\nstatic void\nset_socket_id(const char *line)\n{\n    cid->core.socket_id = atoi(line);\n}\n\nstatic void\nset_max_socket_id(const char *line)\n{\n    uint16_t socket_id;\n\n    socket_id = atoi(line);\n    if (socket_id > cid->max_socket_id)\n        cid->max_socket_id = socket_id;\n}\n\nstatic void\nset_core_id(const char *line)\n{\n    cid->core.core_id = atoi(line);\n}\n\nstatic void\nset_model_name(const char *line)\n{\n    if (!cid->model_name)\n        cid->model_name = strdup(line);\n}\n\nstatic void\nset_siblings(const char *line)\n{\n    if (hmap_kvp_lookup(cid->map, NULL, CI_NUM_SIBLINGS) != NULL)\n        return;\n    if (hmap_add_value(cid->map, NULL, CI_NUM_SIBLINGS, (uint16_t)atoi(line)) < 0)\n        printf(\"%s: Failed to add %s to map\\n\", __func__, CI_NUM_SIBLINGS);\n}\n\nstatic void\nset_cpu_cores(const char *line)\n{\n    if (hmap_kvp_lookup(cid->map, NULL, CI_NUM_CPU_CORES) != NULL)\n        return;\n    if (hmap_add_value(cid->map, NULL, CI_NUM_CPU_CORES, (uint16_t)atoi(line)) < 0)\n        printf(\"%s: Failed to add %s to map\\n\", __func__, CI_NUM_CPU_CORES);\n}\n\nstatic void\nlcore_terminator(const char *unused __attribute__((unused)))\n{\n    coreinfo_t *ci;\n    char buffer[64];\n    uint16_t num_cpu_cores = 0, num_sockets = 0, val = 0;\n\n    ci = calloc(1, sizeof(coreinfo_t));\n    if (!ci) {\n        printf(\"%s: Failed to allocate memory for core info\\n\", __func__);\n        return;\n    }\n    ci->socket_id = cid->core.socket_id;\n    ci->core_id   = cid->core.core_id;\n    ci->lcore_id  = cid->core.lcore_id;\n\n    hmap_get_value(cid->map, NULL, CI_NUM_CPU_CORES, &num_cpu_cores);\n    hmap_get_value(cid->map, NULL, CI_NUM_SOCKETS, &num_sockets);\n    if (num_sockets == 0)\n        num_sockets = 1;\n    ci->thread_id       = (cid->core.lcore_id / num_cpu_cores) / num_sockets;\n    cid->core.thread_id = ci->thread_id;\n\n    if (cid->core.thread_id > cid->max_threads)\n        cid->max_threads = cid->core.thread_id;\n\n    snprintf(buffer, sizeof(buffer), CI_LCORE_FORMAT, cid->core.lcore_id);\n    hmap_add_value(cid->map, NULL, buffer, (void *)ci);\n\n    snprintf(buffer, sizeof(buffer), CI_SCT_FORMAT, cid->core.socket_id, cid->core.core_id,\n             cid->core.thread_id);\n    hmap_add_value(cid->map, NULL, buffer, cid->core.lcore_id);\n\n    if (hmap_get_value(cid->map, NULL, CI_NUM_LCORES, &val) < 0)\n        val = 0;\n    val++;\n    hmap_update_value(cid->map, NULL, CI_NUM_LCORES, val);\n}\n\nstatic void\nignore_line(const char *line __attribute__((unused)))\n{\n}\n\nstatic do_line_fn\nget_matching_action(const char *line)\n{\n    // clang-format off\n    static struct action {\n        const char *desc;\n        do_line_fn fn;\n    } actions[] = {\n        {CI_LOGICAL_ID, set_lcore_id}, /* lcore ID */\n        {CI_SOCKET_ID, set_socket_id}, /* Socket ID */\n        {CI_CORE_ID, set_core_id}, /* Core ID */\n        {CI_MODEL_ID, set_model_name}, /* Model Name */\n        {CI_SIBLINGS_ID, set_siblings}, /* Number of siblings */\n        {CI_CPU_CORES_ID, set_cpu_cores}, /* Number of CPU cores */\n        {CI_TERMINAL_ID, lcore_terminator}, /* move to next lcore ID */\n        {NULL, NULL}\n    };\n    // clang-format on\n    struct action *action;\n\n    for (action = actions; action->fn != NULL; action++) {\n        if (!strncmp(action->desc, line, strlen(action->desc)))\n            return action->fn;\n    }\n\n    return ignore_line;\n}\n\ncoreinfo_t *\ncoreinfo_get(uint16_t lcore_id)\n{\n    coreinfo_t *ci = NULL;\n    char buffer[64];\n    uint16_t num_lcores = 0, num_sockets = 0, num_cores = 0, num_threads = 0;\n\n    hmap_get_value(cid->map, NULL, CI_NUM_LCORES, &num_lcores);\n    hmap_get_value(cid->map, NULL, CI_NUM_CPU_CORES, &num_cores);\n    hmap_get_value(cid->map, NULL, CI_NUM_SOCKETS, &num_sockets);\n    hmap_get_value(cid->map, NULL, CI_NUM_THREADS, &num_threads);\n\n    snprintf(buffer, sizeof(buffer), CI_LCORE_FORMAT, lcore_id);\n    if (hmap_get_value(cid->map, NULL, buffer, (void **)&ci) < 0)\n        ci = NULL;\n\n    return ci;\n}\n\nstatic int\ncoreinfo_create(void)\n{\n    FILE *f;\n    char *line, *value;\n    size_t line_sz = MAX_LINE_SZ;\n\n    cid->map = hmap_create(\"core_map\", 0, NULL);\n    if (cid->map == NULL)\n        printf(\"%s: Failed to create lcore map\\n\", __func__);\n\n    line = malloc(line_sz);\n    if (!line) {\n        printf(\"%s: Failed to allocate memory for line\\n\", __func__);\n        return -1;\n    }\n\n    if ((f = fopen(PROC_CPUINFO, \"r\")) == NULL) {\n        fprintf(stderr, \"Cannot open %s on this system\\n\", PROC_CPUINFO);\n        free(line);\n        hmap_destroy(cid->map);\n        return -1;\n    }\n\n    /* find number of sockets */\n    do {\n        if (getline(&line, &line_sz, f) == EOF)\n            break;\n\n        value = get_value(line);\n        if (value != NULL && strncmp(line, CI_SOCKET_ID, strlen(CI_SOCKET_ID)) == 0)\n            set_max_socket_id(value);\n    } while (1);\n\n    hmap_add_value(cid->map, NULL, CI_NUM_SOCKETS, (uint16_t)(cid->max_socket_id + 1));\n\n    rewind(f);\n    /* process the rest of the information */\n    do {\n        if (getline(&line, &line_sz, f) == EOF)\n            break;\n\n        value = get_value(line);\n        if (value != NULL)\n            get_matching_action(line)(value);\n    } while (1);\n\n    hmap_add_value(cid->map, NULL, CI_NUM_THREADS, (uint16_t)(cid->max_threads + 1));\n\n    free(line);\n    fclose(f);\n\n    return 0;\n}\n\nstatic uint16_t\ncoreinfo_cnt(ci_type_t typ)\n{\n    uint16_t cnt = 0;\n\n    switch (typ) {\n    case CI_NUM_LCORES_TYPE:\n        hmap_get_value(cid->map, NULL, CI_NUM_LCORES, &cnt);\n        break;\n    case CI_NUM_CORES_TYPE:\n        hmap_get_value(cid->map, NULL, CI_NUM_CORES, &cnt);\n        break;\n    case CI_NUM_SOCKETS_TYPE:\n        hmap_get_value(cid->map, NULL, CI_NUM_SOCKETS, &cnt);\n        break;\n    case CI_NUM_THREADS_TYPE:\n        hmap_get_value(cid->map, NULL, CI_NUM_THREADS, &cnt);\n        break;\n    case CI_NUM_SIBLINGS_TYPE:\n        hmap_get_value(cid->map, NULL, CI_NUM_SIBLINGS, &cnt);\n        break;\n    case CI_NUM_CPU_CORES_TYPE:\n        hmap_get_value(cid->map, NULL, CI_NUM_CPU_CORES, &cnt);\n    default:\n        break;\n    }\n    return cnt;\n}\n\nuint16_t\ncoreinfo_lcore_cnt(void)\n{\n    return coreinfo_cnt(CI_NUM_LCORES_TYPE);\n}\n\nuint16_t\ncoreinfo_core_cnt(void)\n{\n    return coreinfo_cnt(CI_NUM_CORES_TYPE);\n}\n\nuint16_t\ncoreinfo_socket_cnt(void)\n{\n    return coreinfo_cnt(CI_NUM_SOCKETS_TYPE);\n}\n\nuint16_t\ncoreinfo_thread_cnt(void)\n{\n    return coreinfo_cnt(CI_NUM_THREADS_TYPE);\n}\n\nuint16_t\ncoreinfo_siblings_cnt(void)\n{\n    return coreinfo_cnt(CI_NUM_SIBLINGS_TYPE);\n}\n\nuint16_t\ncoreinfo_cpu_cores_cnt(void)\n{\n    return coreinfo_cnt(CI_NUM_CPU_CORES_TYPE);\n}\n\nRTE_INIT(coreinfo_init)\n{\n    memset(&coreinfo_data, 0, sizeof(coreinfo_data));\n    coreinfo_create();\n}\n"
  },
  {
    "path": "lib/common/coreinfo.h",
    "content": "/*-\n * Copyright(c) <2014-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2014 by Keith Wiles @ intel.com */\n\n#ifndef __COREINFO_H\n#define __COREINFO_H\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define PROC_CPUINFO \"/proc/cpuinfo\"\n\n// clang-format off\ntypedef enum {\n    CI_NUM_LCORES_TYPE,\n    CI_NUM_SOCKETS_TYPE,\n    CI_NUM_CORES_TYPE,\n    CI_NUM_THREADS_TYPE,\n    CI_NUM_SIBLINGS_TYPE,\n    CI_NUM_CPU_CORES_TYPE\n} ci_type_t;\n// clang-format on\n\ntypedef struct coreinfo_s {\n    __extension__ union {\n        struct {\n            uint16_t lcore_id;  /* Logical core ID */\n            uint16_t core_id;   /* Physical CPU core ID */\n            uint16_t socket_id; /* CPU socket ID */\n            uint16_t thread_id; /* Hyper-thread ID */\n        };\n        uint64_t word;\n    };\n} coreinfo_t;\n\ncoreinfo_t *coreinfo_get(uint16_t lcore_id);\nuint16_t coreinfo_lcore_cnt(void);\nuint16_t coreinfo_core_cnt(void);\nuint16_t coreinfo_socket_cnt(void);\nuint16_t coreinfo_thread_cnt(void);\nuint16_t coreinfo_siblings_cnt(void);\nuint16_t coreinfo_cpu_cores_cnt(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /*_COREINFO_H */\n"
  },
  {
    "path": "lib/common/lscpu.c",
    "content": "/*-\n *   Copyright(c) <2014-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2014 by Keith Wiles @ intel.com */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <getopt.h>\n#include <stdint.h>\n\n#include <pg_strings.h>\n#include \"utils.h\"\n#include \"lscpu.h\"\n\nstatic lscpu_t *lscpu;\n\nstatic __inline__ void\nnum_cpus(__attribute__((unused)) action_t *act, char *line)\n{\n    lscpu->num_cpus = atoi(line);\n}\n\nstatic __inline__ void\nthreads_per_core(__attribute__((unused)) action_t *act, char *line)\n{\n    lscpu->threads_per_core = atoi(line);\n}\n\nstatic __inline__ void\ncores_per_socket(__attribute__((unused)) action_t *act, char *line)\n{\n    lscpu->cores_per_socket = atoi(line);\n}\n\nstatic __inline__ void\nnuma_nodes(__attribute__((unused)) action_t *act, char *line)\n{\n    lscpu->numa_nodes = atoi(line);\n}\n\nstatic __inline__ void\ncpu_mhz(action_t *act, char *line)\n{\n    if ((act->flags & ONLY_ONCE_FLAG) == 0) {\n        lscpu->cpu_mhz = pg_strdupf(lscpu->cpu_mhz, line);\n        act->flags |= ONLY_ONCE_FLAG;\n    }\n}\n\nstatic void\nnuma_nodeX_cpus(action_t *act, char *line)\n{\n    int n, i;\n    char *arr[32], *p;\n    int first, last;\n\n    memset(arr, 0, sizeof(arr));\n\n    n = pg_strparse(line, \",\", arr, (sizeof(arr) / sizeof(char *)));\n    if (n > 0)\n        for (i = 0; i < n; i++) {\n            if (arr[i] == NULL)\n                continue;\n            if ((p = strchr(arr[i], '-')))\n                p++; /* Point to second port number. */\n            else\n                p = arr[i]; /* Only one port number, make the same port. */\n            first = atoi(arr[i]);\n            last  = atoi(p);\n            while (first <= last)\n                lscpu->numa_cpus[act->arg][first++] = 1;\n        }\n}\n\nstatic __inline__ void\ncache_size(action_t *act, char *line)\n{\n    if ((act->flags & ONLY_ONCE_FLAG) == 0) {\n        lscpu->cache_size = pg_strdupf(lscpu->cache_size, line);\n        act->flags |= ONLY_ONCE_FLAG;\n    }\n}\n\nstatic __inline__ void\nmodel_name(action_t *act, char *line)\n{\n    if ((act->flags & ONLY_ONCE_FLAG) == 0) {\n        lscpu->model_name = pg_strdupf(lscpu->model_name, line);\n        act->flags |= ONLY_ONCE_FLAG;\n    }\n}\n\nstatic __inline__ void\ncpu_flags(action_t *act, char *line)\n{\n    if ((act->flags & ONLY_ONCE_FLAG) == 0) {\n        lscpu->cpu_flags = pg_strdupf(lscpu->cpu_flags, line);\n        act->flags |= ONLY_ONCE_FLAG;\n    }\n}\n\nstatic action_t *\nlscpu_match_action(char *line)\n{\n    static action_t actions[] = {{\"CPU(s)\", num_cpus, 0, 0},\n                                 {\"Thread(s) per core\", threads_per_core, 0, 0},\n                                 {\"Core(s) per socket\", cores_per_socket, 0, 0},\n                                 {\"NUMA node(s)\", numa_nodes, 0, 0},\n                                 {\"CPU MHz\", cpu_mhz, 0, 0},\n                                 {\"NUMA node0 CPU(s)\", numa_nodeX_cpus, 0, 0},\n                                 {\"NUMA node1 CPU(s)\", numa_nodeX_cpus, 1, 0},\n                                 {\"NUMA node2 CPU(s)\", numa_nodeX_cpus, 2, 0},\n                                 {\"NUMA node3 CPU(s)\", numa_nodeX_cpus, 3, 0},\n                                 {NULL, NULL, 0, 0}};\n    action_t *act;\n\n    for (act = actions; act->str != NULL; ++act)\n        if (strncmp(act->str, line, strlen(act->str)) == 0)\n            break;\n\n    return act;\n}\n\nstatic action_t *\ncpu_proc_match_action(char *line)\n{\n    static action_t actions[] = {{\"cache size\", cache_size, 0, 0},\n                                 {\"model name\", model_name, 0, 0},\n                                 {\"flags\", cpu_flags, 0, 0},\n                                 {NULL, NULL, 0, 0}};\n    action_t *act;\n\n    for (act = actions; act->str != NULL; ++act)\n        if (strncmp(act->str, line, strlen(act->str)) == 0)\n            break;\n\n    return act;\n}\n\nstatic void\nlscpu_info_get(const char *lscpu_path)\n{\n    FILE *f    = popen(lscpu_path, \"r\");\n    char *line = NULL, *p;\n    action_t *act;\n    size_t line_size;\n\n    if (f == NULL) {\n        printf(\"Unable to run 'lscpu' command\\n\");\n        return;\n    }\n\n    line_size = 0;\n    while (getline(&line, &line_size, f) > 0) {\n        line[strlen(line) - 1] = '\\0';\n        act                    = lscpu_match_action(line);\n        if (act->str) {\n            p = strchr(line, ':');\n            if (p)\n                p++;\n            act->func(act, pg_strtrim(p));\n        }\n    }\n\n    pclose(f);\n    free(line);\n}\n\nstatic void\ncpu_proc_info(const char *proc_path)\n{\n    FILE *f    = popen(proc_path, \"r\");\n    char *line = NULL, *p;\n    action_t *act;\n    size_t line_size;\n\n    if (f == NULL) {\n        printf(\"Unable to run 'CPU proc' command\\n\");\n        return;\n    }\n\n    line_size = 0;\n    while (getline(&line, &line_size, f) > 0) {\n        line[strlen(line) - 1] = '\\0';\n        act                    = cpu_proc_match_action(line);\n        if (act->str) {\n            p = strchr(line, ':');\n            if (p)\n                p++;\n            act->func(act, pg_strtrim(p));\n        }\n    }\n\n    pclose(f);\n    free(line);\n}\n\nlscpu_t *\nlscpu_info(const char *lscpu_path, const char *proc_path)\n{\n    if (lscpu == NULL)\n        lscpu = calloc(1, sizeof(lscpu_t));\n\n    if (lscpu_path == NULL)\n        lscpu_path = LSCPU_PATH;\n    if (proc_path == NULL)\n        proc_path = CPU_PROC_PATH;\n\n    lscpu_info_get(lscpu_path);\n    cpu_proc_info(proc_path);\n\n    return lscpu;\n}\n"
  },
  {
    "path": "lib/common/lscpu.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2014-2018 by Keith Wiles @ intel.com */\n\n#ifndef __LSCPU_H\n#define __LSCPU_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct action_s {\n    const char *str;\n    void (*func)(struct action_s *action, char *line);\n    int arg;\n    int flags;\n} action_t;\n\n#define ONLY_ONCE_FLAG 0x0001\n\n#define MAX_LINE_SIZE 1024\n\ntypedef struct {\n    int num_cpus;\n    int threads_per_core;\n    int cores_per_socket;\n    int numa_nodes;\n    char *cpu_mhz;\n    char *model_name;\n    char *cpu_flags;\n    char *cache_size;\n    short numa_cpus[RTE_MAX_NUMA_NODES][RTE_MAX_LCORE];\n} lscpu_t;\n\n#define LSCPU_PATH    \"/usr/bin/lscpu\"\n#define CPU_PROC_PATH \"cat /proc/cpuinfo\"\n\nlscpu_t *lscpu_info(const char *lscpu_path, const char *proc_path);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/common/meson.build",
    "content": "sources = files(\n\t'copyright_info.c',\n\t'port_config.c',\n\t'cmdline_parse_args.c',\n\t'lscpu.c',\n\t'utils.c',\n\t'coreinfo.c',\n\t'pg_strings.c',\n\t'cksum.c',\n\t'pg_inet.c',\n)\nheaders = files(\n\t'pg_inet.h',\n\t'port_config.h',\n\t'cmdline_parse_args.h',\n\t'lscpu.h',\n\t'utils.h',\n\t'coreinfo.h',\n\t'pg_strings.h',\n\t'cksum.h',\n)\nlibcommon = library('common', sources, dependencies: [hmap, dpdk])\ncommon = declare_dependency(link_with: libcommon, include_directories: include_directories('.'))\n"
  },
  {
    "path": "lib/common/pg_compat.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/**\n * @file\n *\n * Compat file for pktgen\n */\n\n#ifndef PG_COMPAT_H_\n#define PG_COMPAT_H_\n\n#include <rte_version.h>\n#include <rte_ethdev.h>\n#include <rte_ether.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define PG_JUMBO_ETHER_MTU     9216        // 9K total size of the Ethernet jumbo frame\n#define PG_JUMBO_DATAROOM_SIZE 9000        // 9K data room size in the Ethernet jumbo frame\n#define PG_JUMBO_HEADROOM_SIZE \\\n    (PG_JUMBO_ETHER_MTU -      \\\n     PG_JUMBO_DATAROOM_SIZE)        // 9K headroom size in the Ethernet jumbo frame\n\n#ifndef RTE_ETHDEV_QUEUE_STAT_CNTRS\n#define RTE_ETHDEV_QUEUE_STAT_CNTRS 16\n#endif\n\nstatic __inline__ int\npg_socket_id(void)\n{\n    int sid = rte_socket_id();\n\n    return (sid == -1) ? 0 : sid;\n}\n\nstatic __inline__ int\npg_eth_dev_socket_id(int pid)\n{\n    int sid = rte_eth_dev_socket_id(pid);\n\n    return (sid == -1) ? 0 : sid;\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* PG_COMPAT_H_ */\n"
  },
  {
    "path": "lib/common/pg_delay.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/**\n * @file\n *\n * Compat file for pktgen\n */\n\n#ifndef __DELAY_H_\n#define __DELAY_H_\n\n#include <pg_compat.h>\n#include <time.h>\n#include <rte_version.h>\n#include <rte_cycles.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __DELAY_H_ */\n"
  },
  {
    "path": "lib/common/pg_ether.h",
    "content": "/*-\n *   BSD LICENSE\n *\n *   Copyright(c) <2010-2026>-2014 Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#ifndef _ETHER_H_\n#define _ETHER_H_\n\n/**\n * @file\n *\n * Ethernet Helpers in RTE\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#include <rte_ether.h>\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _ETHER_H_ */\n"
  },
  {
    "path": "lib/common/pg_inet.c",
    "content": "/*-\n *   BSD LICENSE\n *\n *   Copyright 2016 6WIND S.A. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#include <stdio.h>\n#include <stdint.h>\n\n#include <rte_version.h>\n#include <rte_mbuf.h>\n#include <rte_mbuf_ptype.h>\n#include <rte_byteorder.h>\n#include <rte_ether.h>\n#include <rte_ip.h>\n#include <rte_tcp.h>\n#include <rte_udp.h>\n\n#include \"pg_inet.h\"\n"
  },
  {
    "path": "lib/common/pg_inet.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _PG_INET_H\n#define _PG_INET_H\n\n#ifdef RTE_MACHINE_CPUFLAG_SSE4_2\n#include <nmmintrin.h>\n#else\n#include <rte_jhash.h>\n#endif\n#include <stdio.h>\n#include <stdint.h>\n\n#include <arpa/inet.h>\n\n#include <pg_compat.h>\n#include <rte_ether.h>\n#include <rte_net.h>\n#include <rte_icmp.h>\n#include <rte_ip6.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define IPv4_VERSION 4\n#define IPv6_VERSION 6\n\n#define PG_IPADDR_V4      0x01\n#define PG_IPADDR_V6      0x02\n#define PG_IPADDR_NETWORK 0x04\n\n#define PG_INADDRSZ    4\n#define PG_IN6ADDRSZ   16\n#define PG_PREFIXMAX   128\n#define PG_V4PREFIXMAX 32\n\nstruct pg_ipaddr {\n    uint8_t family;\n    union {\n        struct in_addr ipv4;\n        struct rte_ipv6_addr ipv6;\n    };\n    unsigned int prefixlen; /* in case of network only */\n};\n\n#define PG_ISFRAG(off) ((off) & (PG_OFF_MF | PG_OFF_MASK))\n#define PG_OFF_MASK    0x1fff\n#define PG_OFF_MF      0x2000\n#define PG_OFF_DF      0x4000\n\n/******************************************************************************\n * struct rte_ipv4_hdr.proto values in the IP Header.\n *  1     ICMP        Internet Control Message            [RFC792]\n *  2     IGMP        Internet Group Management          [RFC1112]\n *  4     IP          IP in IP (encapsulation)           [RFC2003]\n *  6     TCP         Transmission Control                [RFC793]\n * 17     UDP         User Datagram                   [RFC768,JBP]\n * 41     IPv6        Ipv6                               [Deering]\n * 43     IPv6-Route  Routing Header for IPv6            [Deering]\n * 44     IPv6-Frag   Fragment Header for IPv6           [Deering]\n * 47     GRE         Generic Routing Encapsulation [RFC2784,2890]\n * 58     IPv6-ICMP   ICMP for IPv6                      [RFC1883]\n * 59     IPv6-NoNxt  No Next Header for IPv6            [RFC1883]\n * 60     IPv6-Opts   Destination Options for IPv6       [RFC1883]\n */\n#define PG_IPPROTO_NONE       0\n#define PG_IPPROTO_IP         IPPROTO_IP\n#define PG_IPPROTO_ICMP       IPPROTO_ICMP\n#define PG_IPPROTO_IGMP       IPPROTO_IGMP\n#define PG_IPPROTO_IPV4       IPPROTO_IPV4\n#define PG_IPPROTO_TCP        IPPROTO_TCP\n#define PG_IPPROTO_UDP        IPPROTO_UDP\n#define PG_IPPROTO_IPV6       IPPROTO_IPV6\n#define PG_IPPROTO_IPV6_ROUTE 43\n#define PG_IPPROTO_IPV6_FRAG  44\n#define PG_IPPROTO_GRE        IPPROTO_GRE\n#define PG_IPPROTO_ICMPV6     IPPROTO_ICMPV6\n#define PG_IPPROTO_IPV6_ICMP  IPPROTO_ICMPV6\n#define PG_IPPROTO_IPV6_NONXT 59\n#define PG_IPPROTO_IPV6_OPTS  60\n#define PG_IPPROTO_RAW        IPPROTO_RAW\n#define PG_IPPROTO_USR_DEF    255\n#define PG_IPPROTO_MAX        256\n\n#define PG_IPPROTO_L4_GTPU_PORT 2152\n\n// clang-format off\n#define IPv4(a, b, c, d) \\\n    ((uint32_t)(((a) & 0xff) << 24) | (((b) & 0xff) << 16) | (((c) & 0xff) << 8) | ((d) & 0xff))\n// clang-format on\n\n/*************************************************************************\n *\n * GTP -U Header\n *\n * +---+----+-+-+-+-+--+------------+-----------------+------------------+\n * |   |8-6 |5|4|3|2|1 |  8-15      |    16-23        |    24-31         |\n * +---+----+-+-+-+-+--+------------+-----------------+------------------+\n * |0  |Veri|P|*|E|S|PN| Message    |                                    |\n * |   |son | | | | |  |  Type      |        Total length                |\n * +---+----+-+-+-+-+--+------------+------------------------------------+\n * |32 |                TEID (only present if T=1)                       |\n * |   |                                                                 |\n * +---+----------------------------+-----------------+------------------+\n * |64 |      Sequence number       |N-PDU number     |Next extension    |\n * |   |                            |                 |header type       |\n * +---+----------------------------+-----------------+------------------+\n ***************************************************************************/\n\n#define GTPu_VERSION 0x20\n#define GTPu_PT_FLAG 0x10\n#define GTPu_E_FLAG  0x04\n#define GTPu_S_FLAG  0x02\n#define GTPu_PN_FLAG 0x01\n\ntypedef struct gtpuHdr_s {\n    uint8_t version_flags;\n    uint8_t msg_type;\n    uint16_t tot_len;\n    uint32_t teid;\n    //\tuint16_t seq_no;\t\t/**< Optional fields if E, S or PN flags set */\n    //\tuint8_t npdu_no;\n    //\tuint8_t next_ext_hdr_type;\n} __attribute__((__packed__)) gtpuHdr_t;\n\n/* IP overlay header for the pseudo header */\ntypedef struct ipOverlay_s {\n    uint32_t node[2];\n    uint8_t pad0;  /* overlays ttl */\n    uint8_t proto; /* Protocol type */\n    uint16_t len;  /* Protocol length, overlays cksum */\n    uint32_t src;  /* Source address */\n    uint32_t dst;  /* Destination address */\n} __attribute__((__packed__)) ipOverlay_t;\n\ntypedef struct ipv6Overlay_s {\n    uint8_t saddr[16];\n    uint8_t daddr[16];\n    uint32_t tcp_length;\n    uint16_t zero;\n    uint8_t pad;\n    uint8_t next_header;\n} __attribute__((__packed__)) ipv6Overlay_t;\n\ntypedef unsigned int seq_t; /* TCP Sequence type */\n\n/* The UDP/IP Pseudo header */\ntypedef struct udpip_s {\n    ipOverlay_t ip;         /* IPv4 overlay header */\n    struct rte_udp_hdr udp; /* UDP header for protocol */\n} __attribute__((__packed__)) udpip_t;\n\n/* The GTP-U/UDP/IP Pseudo header */\ntypedef struct gtpuUdpIp_s {\n    ipOverlay_t ip;         /* IPv4 overlay header */\n    struct rte_udp_hdr udp; /* UDP header for protocol */\n    gtpuHdr_t gtpu;         /* GTP-U header */\n} __attribute__((__packed__)) gtpuUdpIp_t;\n\n/* The UDP/IPv6 Pseudo header */\ntypedef struct udpipv6_s {\n    ipv6Overlay_t ip;       /* IPv6 overlay header */\n    struct rte_udp_hdr udp; /* UDP header for protocol */\n} __attribute__((__packed__)) udpipv6_t;\n\ntypedef struct tcp_flags_s {\n    const char *name;\n    uint16_t bit;\n} tcp_flags_t;\n\n/* TCP Flag bits */\nenum {\n    RS0_FLAG = 0x800,\n    RS1_FLAG = 0x400,\n    RS2_FLAG = 0x200,\n    RS3_FLAG = 0x100,\n    CWR_FLAG = 0x080,\n    ECE_FLAG = 0x040,\n    URG_FLAG = 0x020,\n    ACK_FLAG = 0x010,\n    PSH_FLAG = 0x008,\n    RST_FLAG = 0x004,\n    SYN_FLAG = 0x002,\n    FIN_FLAG = 0x001\n};\n// clang-format off\n#define TCP_FLAGS_LIST                                                              \\\n    {                                                                               \\\n        {\"rs0\", RS0_FLAG}, {\"rs1\", RS1_FLAG}, {\"rs2\", RS2_FLAG}, {\"rs3\", RS3_FLAG}, \\\n        {\"cwr\", CWR_FLAG}, {\"ece\", ECE_FLAG}, {\"urg\", URG_FLAG}, {\"ack\", ACK_FLAG}, \\\n        {\"psh\", PSH_FLAG}, {\"rst\", RST_FLAG}, {\"syn\", SYN_FLAG}, {\"fin\", FIN_FLAG}, \\\n        {NULL, 0}}\n// clang-format on\n\n/* TCP header length and flags */\n#define TCP_HDR_LENGTH_MASK 0xF000\n#define TCP_FLAGS_MASK      0x0FFF\n\n/* The TCP/IPv4 Pseudo header */\ntypedef struct tcpip_s {\n    ipOverlay_t ip;         /* IPv4 overlay header */\n    struct rte_tcp_hdr tcp; /* TCP header for protocol */\n} __attribute__((__packed__)) tcpip_t;\n\n/* The GTPu/TCP/IPv4 Pseudo header */\ntypedef struct gtpuTcpIp_s {\n    ipOverlay_t ip;         /* IPv4 overlay header */\n    struct rte_tcp_hdr tcp; /* TCP header for protocol */\n    gtpuHdr_t gtpu;         /* GTP-U header */\n} __attribute__((__packed__)) gtpuTcpIp_t;\n\n/* The TCP/IPv6 Pseudo header */\ntypedef struct tcpipv6_s {\n    ipv6Overlay_t ip;       /* IPv6 overlay header */\n    struct rte_tcp_hdr tcp; /* TCP header for protocol */\n} __attribute__((__packed__)) tcpipv6_t;\n\n/* ICMPv4 Packet data structures */\nunion icmp_data {\n    struct {\n        uint16_t ident; /* Identifier */\n        uint16_t seq;   /* Sequence Number */\n        uint32_t data;  /* Data for sequence number */\n    } echo;\n\n    struct {\n        uint32_t gateway;      /* Gateway Address */\n        uint16_t ip[10];       /* IP information */\n        uint16_t transport[4]; /* Transport information */\n    } redirect;\n\n    struct {\n        uint32_t nextHopMtu;   /* Only if code NEED_FRAG or unused */\n        uint16_t ip[10];       /* IP information */\n        uint16_t transport[4]; /* Transport information */\n    } failing_pkt;\n\n    struct {\n        uint16_t ident;     /* Identifier */\n        uint16_t seq;       /* Sequence Number */\n        uint32_t originate; /* originate time */\n        uint32_t receive;   /* receive time */\n        uint32_t transmit;  /* transmit time */\n    } timestamp;\n\n    struct {\n        uint32_t multicast;       /* Multicast information */\n        uint8_t s_qrv;            /* s_grv value */\n        uint8_t qqic;             /* qqoc value */\n        uint16_t numberOfSources; /* number of Source routes */\n        uint16_t source_addr[1];  /* source address */\n    } igmp;\n\n    struct {\n        uint8_t pointer;   /* Parameter pointer */\n        uint8_t unused[3]; /* Not used */\n    } param;               /* Parameter Problem */\n\n    struct {\n        uint8_t numAddrs;      /* Number of address */\n        uint8_t addrEntrySize; /* Size of each entry */\n        uint16_t lifetime;     /* lifetime value */\n        uint32_t advert[1];    /* advertized values */\n    } advertise;\n\n    /* Address mask */\n    struct {\n        uint16_t ident; /* Identifier */\n        uint16_t seq;   /* Sequence Number */\n        uint32_t dmask; /* Mask data */\n    } mask;\n\n} __attribute__((__packed__));\n\n#define ICMP4_TIMESTAMP_SIZE 12\n\n/* ICMPv4 Message Types */\n#define ICMP4_ECHO_REPLY        0\n#define ICMP4_DEST_UNREACHABLE  3\n#define ICMP4_SOURCE_QUENCH     4\n#define ICMP4_REDIRECT          5\n#define ICMP4_ECHO              8\n#define ICMP4_ROUTER_ADVERT     9\n#define ICMP4_ROUTER_SOLICIT    10\n#define ICMP4_TIME_EXCEEDED     11\n#define ICMP4_PARAMETER_PROBLEM 12\n#define ICMP4_TIMESTAMP         13\n#define ICMP4_TIMESTAMP_REPLY   14\n#define ICMP4_INFO_REQUEST      15\n#define ICMP4_INFO_REPLY        16\n#define ICMP4_MASK_REQUEST      17\n#define ICMP4_MASK_REPLY        18\n\n/* MPLS header\n *                        MPLS Header Format\n *\n *    0                   1                   2                   3\n *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n *   |                 Label                 | EXP |S|     TTL       |\n *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n *\n * Label: 20-bit label value\n * EXP: Experimental (QoS and ECN)\n *   - 3-bit Traffic Class field for QoS priority\n *   - Explicit Congestion Notification\n * S: Bottom-of-Stack. If set, the current label is the last in the stack.\n * TTL: Time-to-Live\n */\n\n/* Set/clear Bottom of Stack flag */\n#define MPLS_SET_BOS(mpls_label) \\\n    do {                         \\\n        mpls_label |= (1 << 8);  \\\n    } while ((0))\n#define MPLS_CLR_BOS(mpls_label) \\\n    do {                         \\\n        mpls_label &= ~(1 << 8); \\\n    } while ((0))\n\ntypedef struct mplsHdr_s {\n    uint32_t label; /**< MPLS label */\n} __attribute__((__packed__)) mplsHdr_t;\n\n/* Q-in-Q (802.1ad) header\n *                      Q-in-Q Header Format\n *\n *    0                   1                   2                   3\n *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n *   |              0x88A8           | PCP |D|         VID           |\n *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n *   |              0x8100           | PCP |D|         VID           |\n *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n *\n * 0x88A8, 0x8100: EtherType associated with the tag. 0x88A8 means outer VLAN\n *   tag, 0x8100 means inner VLAN tag.\n * PCP: Priority code point. Class of Service indicator\n * D: Drop eligible indicator\n * VID: VLAN identifier\n */\ntypedef struct qinqHdr_s {\n    uint16_t qinq_tci;  /**< Outer tag PCP, DEI, VID */\n    uint16_t vlan_tpid; /**< Must be RTE_ETHER_TYPE_VLAN (0x8100) */\n    uint16_t vlan_tci;  /**< Inner tag PCP, DEI, VID */\n    uint16_t eth_proto; /**< EtherType of encapsulated frame */\n} __attribute__((__packed__)) qinqHdr_t;\n\n/* GRE header\n *\n *                      GRE Header Format\n *\n *    0                   1                   2                   3\n *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1\n *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n *   |C| |K|S|    Reserved0    | Ver |         Protocol Type         |\n *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n *   |      Checksum (optional)      |     Reserved1 (optional)      |\n *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n *   |                         Key (optional)                        |\n *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n *   |                  Sequence Number (optional)                   |\n *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n *\n * C: 1 if the Checksum and the Reserved1 fields are present and the Checksum\n *    field contains valid information\n * K: 1 if the Key field is present\n * S: the Sequence Number field is present\n * Reserved0: must be 0\n * Ver: Version Number, must be 0\n * Protocol Type: EtherType of encapsulated packet. When IPv4 is being carried\n *   as the GRE payload, the Protocol Type field MUST be set to 0x800.\n * Checksum: the IP (one's complement) checksum sum of all the 16 bit words in\n *   the GRE header and the payload packet\n * Reserved1: must be 0\n * Key: 32bit number determined by the encapsulator\n * Sequence Number: for packet ordering purposes\n */\n\n/* GRE header */\ntypedef struct greHdr_s {\n    uint8_t reserved0_0 : 4; /**< must be 0 */\n    uint8_t seq_present : 1; /**< Sequence Number present */\n    uint8_t key_present : 1; /**< Key present */\n    uint8_t unused : 1;\n    uint8_t chk_present : 1;  /**< Checksum and Reserved1 present */\n    uint8_t version : 3;      /**< Version Number */\n    uint8_t reserved0_1 : 5;  /**< must be 0 */\n    uint16_t eth_type;        /**< EtherType of encapsulated packet */\n    uint32_t extra_fields[3]; /**< Room for Checksum+Reserved1, Key and Sequence Number fields if\n                                 present */\n} __attribute__((__packed__)) greHdr_t;\n\n/* the GRE/IPv4 header */\ntypedef struct greIp_s {\n    struct rte_ipv4_hdr ip; /* Outer IPv4 header */\n    greHdr_t gre;           /* GRE header for protocol */\n} greIp_t;\n\n/* the GRE/Ethernet header */\ntypedef struct greEther_s {\n    struct rte_ipv4_hdr ip;     /* Outer IPv4 header */\n    greHdr_t gre;               /* GRE header */\n    struct rte_ether_hdr ether; /* Inner Ethernet header */\n} greEther_t;\n\n/* Common defines for Ethernet */\n#define ETH_HW_TYPE   1  /* Ethernet hardware type */\n#define ETH_HDR_SIZE  14 /* Ethernet MAC header length */\n#define ETH_ADDR_SIZE 6  /* Ethernet MAC address length */\n#define IPV6_ADDR_LEN 16 /* IPv6 Address length */\n\n#define ETH_VLAN_ENCAP_LEN 4 /* 802.1Q VLAN encap. length */\n\n/* Extra EtherTypes */\n#define ETHER_TYPE_MPLS_UNICAST   0x8847\n#define ETHER_TYPE_MPLS_MULTICAST 0x8848\n\n#define ETHER_TYPE_Q_IN_Q        0x88A8\n#define ETHER_TYPE_TRANSP_ETH_BR 0x6558 /* Transparent Ethernet Bridge */\n\n/* RARP and ARP opcodes */\nenum { ARP_REQUEST = 1, ARP_REPLY = 2, RARP_REQUEST = 3, RARP_REPLY = 4, GRATUITOUS_ARP = 5 };\n\ntypedef union {\n    uint16_t _16[3];\n    uint8_t _8[6];\n} mac_e;\n\ntypedef union {\n    uint16_t _16[2];\n    uint32_t _32;\n} ip4_e;\n\ntypedef struct pkt_hdr_s {\n    struct rte_ether_hdr eth; /**< Ethernet header */\n    union {\n        struct rte_ipv4_hdr ipv4;                        /**< IPv4 Header */\n        struct rte_ipv6_hdr ipv6;                        /**< IPv6 Header */\n        tcpip_t tip;                                     /**< TCP + IPv4 Headers */\n        udpip_t uip;                                     /**< UDP + IPv4 Headers */\n        gtpuUdpIp_t guip;                                /**< GTP-U + UDP + IPv4 Header */\n        struct rte_icmp_hdr icmp;                        /**< ICMP + IPv4 Headers */\n        tcpipv6_t tip6;                                  /**< TCP + IPv6 Headers */\n        udpipv6_t uip6;                                  /**< UDP + IPv6 Headers */\n        uint8_t pad[128 - sizeof(struct rte_ether_hdr)]; /* Force 128 bytes */\n    } u;\n} pkt_hdr_t;\n\ntypedef struct ipv4_5tuple {\n    uint32_t ip_dst;\n    uint32_t ip_src;\n    uint16_t port_dst;\n    uint16_t port_src;\n    uint8_t proto;\n} __attribute__((__packed__)) ipv4_5tuple_t;\n\ntypedef struct l3_4route_s {\n    ipv4_5tuple_t key;\n    uint8_t ifid;\n} __attribute__((__packed__)) l3_4route_t;\n\ntypedef struct ipv6_5tuple_s {\n    uint8_t dst[IPV6_ADDR_LEN];\n    uint8_t src[IPV6_ADDR_LEN];\n    uint16_t sport;\n    uint16_t dport;\n    uint8_t proto;\n} __attribute__((__packed__)) ipv6_5tuple_t;\n\ntypedef struct l3_6route_s {\n    ipv6_5tuple_t key;\n    uint8_t ifid;\n} __attribute__((__packed__)) l3_6route_t;\n\n/*********************************************************************************/\n/**\n * Use crc32 instruction to perform a 6 byte hash.\n *\n * @param data\n *   Data to perform hash on.\n * @param data_len\n *   How many bytes to use to calculate hash value. (Not Used)\n * @param init_val\n *   Value to initialize hash generator.\n * @return\n *   32bit calculated hash value.\n */\nstatic inline uint32_t\nrte_hash6_crc(const void *data, __attribute__((unused)) uint32_t data_len, uint32_t init_val)\n{\n#ifdef RTE_MACHINE_CPUFLAG_SSE4_2\n    const uint32_t *p32 = (const uint32_t *)data;\n    const uint16_t val  = *(const uint16_t *)p32;\n\n    return _mm_crc32_u32(val, _mm_crc32_u32(*p32++, init_val));\n#else\n    return rte_jhash(data, data_len, init_val);\n#endif\n}\n\n/* ethAddrCopy( u16_t * to, u16_t * from ) - Swap two Ethernet addresses */\nstatic __inline__ void\nethAddrCopy(void *t, void *f)\n{\n    uint16_t *d = (uint16_t *)t;\n    uint16_t *s = (uint16_t *)f;\n\n    *d++ = *s++;\n    *d++ = *s++;\n    *d   = *s;\n}\n\n/* ethSwap(u16_t * to, u16_t * from) - Swap two 16 bit values */\nstatic __inline__ void\nuint16Swap(void *t, void *f)\n{\n    uint16_t *d = (uint16_t *)t;\n    uint16_t *s = (uint16_t *)f;\n    uint16_t v;\n\n    v  = *d;\n    *d = *s;\n    *s = v;\n}\n\n/* ethAddrSwap( u16_t * to, u16_t * from ) - Swap two ethernet addresses */\nstatic __inline__ void\nethAddrSwap(void *t, void *f)\n{\n    uint16_t *d = (uint16_t *)t;\n    uint16_t *s = (uint16_t *)f;\n\n    uint16Swap(d++, s++);\n    uint16Swap(d++, s++);\n    uint16Swap(d, s);\n}\n\n/* inetAddrCopy( void * t, void * f ) - Copy IPv4 address */\nstatic __inline__ void\ninetAddrCopy(void *t, void *f)\n{\n    uint32_t *d = (uint32_t *)t;\n    uint32_t *s = (uint32_t *)f;\n\n    *d = *s;\n}\n\n/* inetAddrSwap( void * t, void * f ) - Swap two IPv4 addresses */\nstatic __inline__ void\ninetAddrSwap(void *t, void *f)\n{\n    uint32_t *d = (uint32_t *)t;\n    uint32_t *s = (uint32_t *)f;\n    uint32_t v;\n\n    v  = *d;\n    *d = *s;\n    *s = v;\n}\n\n/* inet6AddrIsUnspecified( void * a )  - Unspecified Address */\nstatic __inline__ int\ninet6AddrIsUnspecified(void *a)\n{\n    return !((const uint32_t *)(a))[0] && !((const uint32_t *)(a))[1] &&\n           !((const uint32_t *)(a))[2] && !((const uint32_t *)(a))[3];\n}\n\n/* inet6AddrAdd - Apply paddr3 = paddr1 + paddr2 */\nstatic __inline__ void\ninet6AddrAdd(void *paddr1, void *paddr2, void *paddr3)\n{\n    int i;\n\n    uint8_t *addr1 = (uint8_t *)paddr1, *addr2 = (uint8_t *)paddr2, *addr3 = (uint8_t *)paddr3;\n\n    uint16_t n = 0;\n\n    for (i = 15; i >= 0; --i) {\n        n        = (uint16_t)addr1[i] + addr2[i] + (n >> 8);\n        addr3[i] = n & 0xff;\n    }\n}\n\n#ifndef _MASK_SIZE_\n#define _MASK_SIZE_\n/* mask_size(uint32_t mask) - return the number of bits in mask */\nstatic __inline__ int\nmask_size(uint32_t mask)\n{\n    if (mask == 0)\n        return 0;\n    else if (mask == 0xFF000000)\n        return 8;\n    else if (mask == 0xFFFF0000)\n        return 16;\n    else if (mask == 0xFFFFFF00)\n        return 24;\n    else if (mask == 0xFFFFFFFF)\n        return 32;\n    else {\n        int i;\n        for (i = 0; i < 32; i++)\n            if ((mask & (1 << (31 - i))) == 0)\n                break;\n        return i;\n    }\n}\n#endif\n\n/* size_to_mask( int len ) - return the mask for the mask size */\nstatic __inline__ uint32_t\nsize_to_mask(int len)\n{\n    uint32_t mask = 0;\n\n    if (len == 0)\n        mask = 0x00000000;\n    else if (len == 8)\n        mask = 0xFF000000;\n    else if (len == 16)\n        mask = 0xFFFF0000;\n    else if (len == 24)\n        mask = 0xFFFFFF00;\n    else if (len == 32)\n        mask = 0xFFFFFFFF;\n    else {\n        int i;\n\n        for (i = 0; i < len; i++)\n            mask |= (1 << (31 - i));\n    }\n    return mask;\n}\n\n#ifndef _NTOP4_\n#define _NTOP4_\n/* char * inet_ntop4(char * buff, int len, unsigned long ip_addr, unsigned long mask) - Convert IPv4\n * address to ascii */\nstatic __inline__ char *\ninet_ntop4(char *buff, int len, unsigned long ip_addr, unsigned long mask)\n{\n    char lbuf[64];\n\n    inet_ntop(AF_INET, &ip_addr, buff, len);\n    if (mask != 0xFFFFFFFF) {\n        snprintf(lbuf, sizeof(lbuf), \"%s/%d\", buff, mask_size(mask));\n        snprintf(buff, len, \"%s\", lbuf);\n    }\n    return buff;\n}\n#endif\n\n/* const char * inet_ntop6(char * buff, int len, uint8_t *ip6, unsigned prefixlen) - Convert IPv6\n * address to ascii */\nstatic __inline__ const char *\ninet_ntop6(char *buff, int len, uint8_t *ip6, unsigned prefixlen)\n{\n    char lbuf[64];\n    unsigned nprint = 0, ntrim = 0;\n\n    inet_ntop(AF_INET6, ip6, buff, len);\n    strcpy(lbuf, buff);\n\n    if (prefixlen > PG_PREFIXMAX) {\n        prefixlen ^= (nprint = prefixlen >> 8) << 8;\n        ntrim = prefixlen != PG_PREFIXMAX ? (prefixlen >= 100  ? 3\n                                             : prefixlen >= 10 ? 2\n                                                               : 1) +\n                                                1\n                                          : 0;\n        if (nprint < strlen(lbuf) + ntrim) {\n            nprint -= ntrim;\n            snprintf(buff, len, \"%.*s+%02ld\", nprint - 3, lbuf, strlen(lbuf) - nprint + 3);\n        }\n    }\n\n    if (prefixlen != PG_PREFIXMAX) {\n        snprintf(lbuf, sizeof(lbuf), \"%s/%d\", buff, prefixlen);\n        snprintf(buff, len, \"%s\", lbuf);\n    }\n\n    return buff;\n}\n\n#ifndef _MTOA_\n#define _MTOA_\n/* char * inet_mtoa(char * buff, int len, struct rte_ether_addr * eaddr) - Convert MAC address to\n * ascii */\nstatic __inline__ char *\ninet_mtoa(char *buff, int len, struct rte_ether_addr *eaddr)\n{\n    snprintf(buff, len, \"%02x:%02x:%02x:%02x:%02x:%02x\", eaddr->addr_bytes[0], eaddr->addr_bytes[1],\n             eaddr->addr_bytes[2], eaddr->addr_bytes[3], eaddr->addr_bytes[4],\n             eaddr->addr_bytes[5]);\n    return buff;\n}\n#endif\n\n/* convert a MAC address from network byte order to host 64bit number */\nstatic __inline__ uint64_t\ninet_mtoh64(struct rte_ether_addr *eaddr, uint64_t *value)\n{\n    *value = ((uint64_t)eaddr->addr_bytes[5] << 0) + ((uint64_t)eaddr->addr_bytes[4] << 8) +\n             ((uint64_t)eaddr->addr_bytes[3] << 16) + ((uint64_t)eaddr->addr_bytes[2] << 24) +\n             ((uint64_t)eaddr->addr_bytes[1] << 32) + ((uint64_t)eaddr->addr_bytes[0] << 40);\n    return *value;\n}\n\n/* convert a host 64bit number to MAC address in network byte order */\nstatic __inline__ struct rte_ether_addr *\ninet_h64tom(uint64_t value, struct rte_ether_addr *eaddr)\n{\n    eaddr->addr_bytes[5] = ((value >> 0) & 0xFF);\n    eaddr->addr_bytes[4] = ((value >> 8) & 0xFF);\n    eaddr->addr_bytes[3] = ((value >> 16) & 0xFF);\n    eaddr->addr_bytes[2] = ((value >> 24) & 0xFF);\n    eaddr->addr_bytes[1] = ((value >> 32) & 0xFF);\n    eaddr->addr_bytes[0] = ((value >> 40) & 0xFF);\n    return eaddr;\n}\n\n/**\n * Convert an IPv4/v6 address into a binary value.\n *\n * @param buf\n *   Location of string to convert\n * @param flags\n *   Set of flags for converting IPv4/v6 addresses and netmask.\n * @param res\n *   Location to put the results\n * @param ressize\n *   Length of res in bytes.\n * @return\n *   0 on OK and -1 on error\n */\nint _atoip(const char *buf, int flags, void *res, unsigned ressize);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PG_INET_H */\n"
  },
  {
    "path": "lib/common/pg_strings.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n#include \"pg_strings.h\"\n\nchar *\npg_strtrim(char *str)\n{\n    if (!str || !*str)\n        return str;\n\n    /* trim white space characters at the front */\n    while (isspace(*str))\n        str++;\n\n    /* Make sure the string is not empty */\n    if (*str) {\n        char *p = &str[strlen(str) - 1];\n\n        /* trim trailing white space characters */\n        while ((p >= str) && isspace(*p))\n            p--;\n\n        p[1] = '\\0';\n    }\n    return str;\n}\n\nint\npg_strtok(char *str, const char *delim, char **entries, int maxtokens)\n{\n    int i = 0;\n    char *saved;\n\n    if (!str || !delim || !entries || !maxtokens)\n        return -1;\n\n    do {\n        entries[i] = pg_strtrim(strtok_r(str, delim, &saved));\n        str        = NULL;\n    } while (entries[i] && (++i < maxtokens));\n\n    return i;\n}\n\nint\npg_strqtok(char *str, const char *delim, char *argv[], int maxtokens)\n{\n    char *p, *start_of_word, *s;\n    int argc                                                             = 0;\n    enum { INIT, WORD, STRING_QUOTE, STRING_TICK, STRING_BRACKET } state = WORD;\n\n    if (!str || !delim || !argv || maxtokens == 0)\n        return -1;\n\n    /* Remove white space from start and end of string */\n    s = pg_strtrim(str);\n\n    start_of_word = s;\n    for (p = s; (argc < maxtokens) && (*p != '\\0'); p++) {\n        int c = (unsigned char)*p;\n\n        if (c == '\\\\') {\n            start_of_word = ++p;\n            continue;\n        }\n\n        switch (state) {\n        case INIT:\n            if (c == '\"') {\n                state         = STRING_QUOTE;\n                start_of_word = p + 1;\n            } else if (c == '\\'') {\n                state         = STRING_TICK;\n                start_of_word = p + 1;\n            } else if (c == '{') {\n                state         = STRING_BRACKET;\n                start_of_word = p + 1;\n            } else if (!strchr(delim, c)) {\n                state         = WORD;\n                start_of_word = p;\n            }\n            break;\n\n        case STRING_QUOTE:\n            if (c == '\"') {\n                *p           = 0;\n                argv[argc++] = start_of_word;\n                state        = INIT;\n            }\n            break;\n\n        case STRING_TICK:\n            if (c == '\\'') {\n                *p           = 0;\n                argv[argc++] = start_of_word;\n                state        = INIT;\n            }\n            break;\n\n        case STRING_BRACKET:\n            if (c == '}') {\n                *p           = 0;\n                argv[argc++] = start_of_word;\n                state        = INIT;\n            }\n            break;\n\n        case WORD:\n            if (strchr(delim, c)) {\n                *p            = 0;\n                argv[argc++]  = start_of_word;\n                state         = INIT;\n                start_of_word = p + 1;\n            }\n            break;\n\n        default:\n            break;\n        }\n    }\n\n    if ((state != INIT) && (argc < maxtokens))\n        argv[argc++] = start_of_word;\n\n    if ((argc == 0) && (p != str))\n        argv[argc++] = str;\n\n    return argc;\n}\n\nint\npg_stropt(const char *list, char *str, const char *delim)\n{\n    char *argv[STR_MAX_ARGVS + 1], *buf;\n    size_t n, i;\n\n    if (!list || !str || !delim)\n        return -1;\n\n    if ((list[0] == '%') && (list[1] == '|'))\n        list += 2;\n\n    if (!*list)\n        return -1;\n\n    n = strlen(list) + 2;\n\n    buf = alloca(n);\n    if (buf) {\n        snprintf(buf, n, \"%s\", list);\n\n        n = pg_strtok(buf, delim, argv, STR_MAX_ARGVS);\n\n        for (i = 0; i < n; i++)\n            if (pg_strmatch(argv[i], str))\n                return i;\n    }\n\n    return -1;\n}\n\nint\npg_parse_corelist(const char *corelist, uint8_t *lcores, int len)\n{\n    int idx        = 0;\n    unsigned count = 0;\n    char *end      = NULL;\n    int min, max, k;\n    char cl_buf[128], *cl = cl_buf;\n\n    if (corelist == NULL)\n        return -1;\n\n    memset(lcores, 0, len);\n\n    strlcpy(cl, corelist, sizeof(cl_buf));\n\n    /* Remove all blank characters ahead and after */\n    cl = pg_strtrim(cl);\n\n    /* Get list of cores */\n    min = RTE_MAX_LCORE;\n    k   = 0;\n    do {\n        while (isblank(*cl))\n            cl++;\n\n        if (*cl == '\\0')\n            return -1;\n\n        errno = 0;\n        idx   = strtoul(cl, &end, 10);\n        if (errno || end == NULL)\n            return -1;\n\n        while (isblank(*end))\n            end++;\n\n        if (*end == '-')\n            min = idx;\n        else if ((*end == ',') || (*end == '\\0')) {\n            max = idx;\n\n            if (min == RTE_MAX_LCORE)\n                min = idx;\n\n            for (idx = min; idx <= max; idx++) {\n                lcores[idx] = 1;\n                count++;\n            }\n\n            min = RTE_MAX_LCORE;\n        } else\n            return -1;\n\n        cl = end + 1;\n\n        if (k >= len)\n            return -1;\n    } while (*end != '\\0');\n\n    return count;\n}\n"
  },
  {
    "path": "lib/common/pg_strings.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/**\n * @file\n *\n * String-related utility functions\n */\n\n#ifndef __STRINGS_H_\n#define __STRINGS_H_\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <ctype.h>\n\n#include <pg_compat.h>\n#include <rte_compat.h>\n#include <rte_ether.h>\n#include <rte_string_fns.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define __numbits(_v) __builtin_popcount(_v)\n\nenum {\n    STR_MAX_ARGVS  = 64, /**< Max number of args to support */\n    STR_TOKEN_SIZE = 128,\n};\n\n/**\n * Remove leading and trailing white space from a string.\n *\n * @param str\n *   String to be trimmed, must be null terminated\n * @return\n *   pointer to the trimmed string or NULL if <str> is Null or\n *   if string is empty then return pointer to <str>\n */\nchar *pg_strtrim(char *str);\n\n/**\n * Parse a string into a argc/argv list using a set of delimiters, but does\n * not handle quoted strings within the string being parsed.\n *\n * @param str\n *   String to be tokenized and will be modified, must be null terminated\n * @param delim\n *   A null terminated list of delimitors\n * @param entries\n *   A pointer to an array to place the token pointers\n * @param max_entries\n *   Max number of tokens to be placed in <entries>\n * @return\n *   The number of tokens in the <entries> array.\n */\nint pg_strtok(char *str, const char *delim, char **entries, int maxtokens);\n\n/**\n * Parse a string into a argc/argv list using a set of delimiters, but does\n * handle quoted strings within the string being parsed\n *\n * @param str\n *   String to be tokenized and will be modified, null terminated\n * @param delim\n *   A null terminated list of delimitors\n * @param entries\n *   A pointer to an array to place the token pointers\n * @param max_entries\n *   Max number of tokens to be placed in <entries>\n * @return\n *   The number of tokens in the <entries> array.\n */\nint pg_strqtok(char *str, const char *delim, char **entries, int maxtokens);\n\n/**\n * Parse a string <list> looking for <str> using delim character.\n *\n * @param list\n *   A string list of options with delim character between them.\n * @param str\n *   String to search for in <list>\n * @param delim\n *   A character string to use as a delim values\n * @return\n *   The index in the list of option strings, -1 if not found\n */\nint pg_stropt(const char *list, char *str, const char *delim);\n\n/**\n * Parse a corelist string and return a list of the lcores.\n *\n * @param corelist\n *    The string defining a set of cores e.g. \"1-8,22,25-29\"\n * @param lcores\n *    The pointer to a uint16_t array to update with the core id.\n * @param len\n *    The length of the uint16_t array.\n * @return\n *    -1 error or number of cores in the string.\n */\nint pg_parse_corelist(const char *corelist, uint8_t *lcores, int len);\n\n/**\n * Helper routine to compare two strings exactly\n *\n * @param s1\n *   Pointer to first string.\n * @param s2\n *   Pointer to second string.\n * @return\n *   0 failed to compare and 1 strings are equal.\n */\nstatic inline int\npg_strmatch(const char *s1, const char *s2)\n{\n    if (!s1 || !s2)\n        return 0;\n\n    while ((*s1 != '\\0') && (*s2 != '\\0')) {\n        if (*s1++ != *s2++)\n            return 0;\n    }\n    if (*s1 != *s2)\n        return 0;\n\n    return 1;\n}\n\n/**\n * Count the number of <c> characters in a string <s>\n *\n * @param s\n *   Null terminated string to search\n * @param c\n *   character to count\n * @return\n *   Number of times the character is in string.\n */\nstatic inline int\npg_strcnt(char *s, char c)\n{\n    return (s == NULL || *s == '\\0') ? 0 : pg_strcnt(s + 1, c) + (*s == c);\n}\n\n/**\n * Convert a string Ethernet MAC address to the binary form\n *\n * @param a\n *   String containing the MAC address in two forms\n *      XX:XX:XX:XX:XX:XX or XXXX:XXXX:XXX\n * @param e\n *   pointer to a struct rte_ether_addr to place the return value. If the value\n *   is null then use a static location instead.\n * @return\n *   Pointer to the struct rte_ether_addr structure;\n */\nstatic inline struct rte_ether_addr *\npg_ether_aton(const char *a, struct rte_ether_addr *e)\n{\n    int i;\n    char *end;\n    unsigned long o[RTE_ETHER_ADDR_LEN];\n    static struct rte_ether_addr rte_ether_addr;\n\n    if (!e)\n        e = &rte_ether_addr;\n\n    i = 0;\n    do {\n        errno = 0;\n        o[i]  = strtoul(a, &end, 16);\n        if (errno != 0 || end == a || (end[0] != ':' && end[0] != 0))\n            return NULL;\n        a = end + 1;\n    } while (++i != sizeof(o) / sizeof(o[0]) && end[0] != 0);\n\n    /* Junk at the end of line */\n    if (end[0] != 0)\n        return NULL;\n\n    /* Support the format XX:XX:XX:XX:XX:XX */\n    if (i == RTE_ETHER_ADDR_LEN) {\n        while (i-- != 0) {\n            if (o[i] > UINT8_MAX)\n                return NULL;\n            e->addr_bytes[i] = (uint8_t)o[i];\n        }\n        /* Support the format XXXX:XXXX:XXXX */\n    } else if (i == RTE_ETHER_ADDR_LEN / 2) {\n        while (i-- != 0) {\n            if (o[i] > UINT16_MAX)\n                return NULL;\n            e->addr_bytes[i * 2]     = (uint8_t)(o[i] >> 8);\n            e->addr_bytes[i * 2 + 1] = (uint8_t)(o[i] & 0xff);\n        }\n        /* unknown format */\n    } else\n        return NULL;\n\n    return e;\n}\n\n#ifndef _MTOA_\n#define _MTOA_\n/* char * inet_mtoa(char * buff, int len, struct rte_ether_addr * eaddr) - Convert MAC address to\n * ascii */\nstatic __inline__ char *\ninet_mtoa(char *buff, int len, struct rte_ether_addr *eaddr)\n{\n    snprintf(buff, len, \"%02x:%02x:%02x:%02x:%02x:%02x\", eaddr->addr_bytes[0], eaddr->addr_bytes[1],\n             eaddr->addr_bytes[2], eaddr->addr_bytes[3], eaddr->addr_bytes[4],\n             eaddr->addr_bytes[5]);\n    return buff;\n}\n#endif\n\n#ifndef _MASK_SIZE_\n#define _MASK_SIZE_\n/* mask_size(uint32_t mask) - return the number of bits in mask */\nstatic __inline__ int\nmask_size(uint32_t mask)\n{\n    if (mask == 0)\n        return 0;\n    else if (mask == 0xFF000000)\n        return 8;\n    else if (mask == 0xFFFF0000)\n        return 16;\n    else if (mask == 0xFFFFFF00)\n        return 24;\n    else if (mask == 0xFFFFFFFF)\n        return 32;\n    else {\n        int i;\n        for (i = 0; i < 32; i++)\n            if ((mask & (1 << (31 - i))) == 0)\n                break;\n        return i;\n    }\n}\n#endif\n\n#ifndef _NTOP4_\n#define _NTOP4_\n/* char * inet_ntop4(char * buff, int len, unsigned long ip_addr, unsigned long mask) - Convert IPv4\n * address to ascii */\nstatic __inline__ char *\ninet_ntop4(char *buff, int len, unsigned long ip_addr, unsigned long mask)\n{\n    char lbuf[64];\n\n    inet_ntop(AF_INET, &ip_addr, buff, len);\n    if (mask != 0xFFFFFFFF) {\n        snprintf(lbuf, sizeof(lbuf), \"/%d\", mask_size(mask));\n        rte_strlcat(buff, lbuf, len);\n    }\n    return buff;\n}\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __STRINGS_H_ */\n"
  },
  {
    "path": "lib/common/port_config.c",
    "content": "/*-\n * Copyright(c) <2014-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2014 by Keith Wiles @ intel.com */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <string.h>\n#include <stdarg.h>\n#include <ctype.h>\n\n#include <rte_version.h>\n#include <rte_config.h>\n#include <rte_lcore.h>\n#include <rte_atomic.h>\n#include <rte_cycles.h>\n#include <rte_pci.h>\n#include <rte_devargs.h>\n#include <rte_debug.h>\n\n#include \"port_config.h\"\n\n#define PORT_STRING_SIZE 256\n\n/**\n *\n * get_portdesc - Parse the lspci command output to find ports.\n *\n * DESCRIPTION\n * Parse the lspci command output to find valid ports to use.\n *\n * RETURNS: Number of ports found.\n *\n * SEE ALSO:\n */\n\nuint32_t\nget_portdesc(uint8_t **portdesc, uint32_t num, int verbose)\n{\n    FILE *fd;\n    uint32_t idx;\n    char buff[PORT_STRING_SIZE], *p;\n\n    if ((num <= 0) || (portdesc == NULL))\n        return 0;\n\n    /* Only parse the Ethernet cards on the PCI bus. */\n    fd = popen(\"lspci -D | grep Ethernet\", \"r\");\n    if (fd == NULL)\n        rte_panic(\"*** Unable to do lspci may need to be installed\");\n\n    if (verbose)\n        fprintf(stdout, \"\\n** Bit Mask: All ports in system\\n\");\n\n    idx = 0;\n    while (fgets(buff, sizeof(buff), fd)) {\n        const char *s = \"ethernet controller\";\n        int n         = strlen(s);\n\n        p = &buff[0];\n\n        /* add a null at the end of the string. */\n        p[strlen(buff) - 1] = 0;\n\n        if (verbose)\n            fprintf(stdout, \" 0x%016\" PRIx64 \": %s\\n\", (1UL << idx), buff);\n\n        /* remove both \"Ethernet Controller\" strings*/\n        p = strcasestr(buff, s);\n        if (p)\n            memmove(p, p + n, (strlen(buff) - ((p - buff) + n)) + 1);\n\n        p = strcasestr(buff, s);\n        n++;\n        if (p) /* Try to remove the two strings to make the line shorter */\n            memmove(p, p + n, (strlen(buff) - ((p - buff) + n)) + 1);\n\n        memmove(&buff[12], &buff[14], (strlen(buff) - 2) + 1);\n        portdesc[idx] =\n            (uint8_t *)strdup(&buff[5]); /* portdesc[idx] needs to be NULL or we lose memory. */\n\n        if (++idx >= num)\n            break;\n    }\n\n    pclose(fd);\n    if (verbose)\n        fprintf(stdout, \"\\nFound %d ports\\n\", idx);\n\n    return idx;\n}\n\n/**\n *\n * free_portdesc - Free the allocated memory for port descriptions.\n *\n * DESCRIPTION\n * Free the allocated memory for the port descriptions\n *\n * RETURNS: N/A\n *\n * SEE ALSO:\n */\n\nvoid\nfree_portdesc(uint8_t **portdesc, uint32_t num)\n{\n    uint32_t i;\n\n    for (i = 0; i < num; i++) {\n        if (portdesc[i])\n            free((char *)portdesc[i]);\n        portdesc[i] = NULL;\n    }\n}\n\n/**\n *\n * create_blocklist - Create a port blocklist.\n *\n * DESCRIPTION\n * Create a port blocklist from the port and port descriptions.\n *\n * RETURNS: Number of ports in list.\n *\n * SEE ALSO:\n */\n\nuint32_t\ncreate_blocklist(uint64_t portmask, struct rte_pci_addr *portlist, uint32_t port_cnt,\n                 uint8_t *desc[])\n{\n    uint32_t i, idx;\n    char pci_addr_str[32];\n\n    if ((portmask == 0) || (portlist == NULL) || (port_cnt == 0) || (desc == NULL))\n        return 0;\n\n    fprintf(stdout, \"Ports: Port Mask: %016\" PRIx64 \" blocklisted = --, not-blocklisted = ++\\n\",\n            portmask);\n    idx = 0;\n    for (i = 0; i < port_cnt; i++) {\n        memset(pci_addr_str, 0, sizeof(pci_addr_str));\n        if ((portmask & (1UL << i)) == 0) {\n            fprintf(stdout, \"-- %s\\n\", desc[i]);\n            snprintf(pci_addr_str, sizeof(pci_addr_str), \"%s\", desc[i]);\n            rte_devargs_add(RTE_DEVTYPE_BLOCKED, pci_addr_str);\n            idx++;\n        } else {\n            snprintf(pci_addr_str, sizeof(pci_addr_str), \"%s\", desc[i]);\n            rte_devargs_add(RTE_DEVTYPE_ALLOWED, pci_addr_str);\n            fprintf(stdout, \"++ %s\\n\", desc[i]);\n        }\n    }\n    if (desc)\n        fprintf(stdout, \"\\n\");\n\n    return idx;\n}\n"
  },
  {
    "path": "lib/common/port_config.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2013-2024 by Keith Wiles @ intel.com */\n\n#ifndef _PORT_CONFIG_H\n#define _PORT_CONFIG_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nuint32_t get_portdesc(uint8_t **portdesc, uint32_t num, int verbose);\nvoid free_portdesc(uint8_t **portdesc, uint32_t num);\nuint32_t create_blocklist(uint64_t portmask, struct rte_pci_addr *portlist, uint32_t port_cnt,\n                          uint8_t *desc[]);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PORT_CONFIG_H */\n"
  },
  {
    "path": "lib/common/utils.c",
    "content": "/*-\n *   Copyright(c) <2014-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n/* Created 2014 by Keith Wiles @ intel.com */\n\n#include <rte_config.h>\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <string.h>\n#include <sys/queue.h>\n#include <netinet/in.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <errno.h>\n#include <getopt.h>\n#include <termios.h>\n\n#include <pg_strings.h>\n#include \"utils.h\"\n\n#define MAX_PARSE_SIZE 256\n\n/******************************************************************************\n * pg_strtrim  - Remove leading and trailing white space from a string.\n *\n * SYNOPSIS\n * char * pg_strtrim\n *     (\n *     char *    str\n *     )\n *\n * DESCRIPTION\n * Remove leading and trailing white space from a string.\n *\n * RETURNS: pointer to the trimmed string or NULL <str> is Null.\n *\n * ERRNO: N/A\n *\n * \\NOMANUAL\n */\nstatic char *\n_strtrim(char *str)\n{\n    char *p;\n    int len;\n\n    if ((str != NULL) && (len = strlen(str))) {\n        /* skip white spaces at the front of the string */\n        for (; *str != 0; str++)\n            if ((*str != ' ') && (*str != '\\t') && (*str != '\\r') && (*str != '\\n'))\n                break;\n\n        len = strlen(str);\n        if (len == 0)\n            return str;\n\n        /* Trim trailing characters */\n        for (p = &str[len - 1]; p > str; p--) {\n            if ((*p != ' ') && (*p != '\\t') && (*p != '\\r') && (*p != '\\n'))\n                break;\n            *p = '\\0';\n        }\n    }\n    return str;\n}\n\nuint32_t\npg_strparse(char *str, const char *delim, char **entries, uint32_t max_entries)\n{\n    uint32_t i;\n    char *saved;\n\n    if ((str == NULL) || (delim == NULL) || (entries == NULL) || (max_entries == 0))\n        return 0;\n\n    memset(entries, '\\0', (sizeof(char *) * max_entries));\n\n    for (i = 0; i < max_entries; i++) {\n        entries[i] = strtok_r(str, delim, &saved);\n        str        = NULL;\n        if (entries[i] == NULL) /* We are done. */\n            break;\n\n        entries[i] = _strtrim(entries[i]);\n    }\n\n    return i;\n}\n\nstatic uint32_t\nskip_lst(char f, const char *lst)\n{\n    for (; *lst != '\\n'; lst++)\n        if (f == *lst)\n            return 1;\n    return 0;\n}\n\nchar *\npg_strccpy(char *t, char *f, const char *lst)\n{\n    if ((t == NULL) || (f == NULL))\n        return NULL;\n\n    *t = '\\0';\n    if (*f == '\\0')\n        return t;\n\n    while (*f != '\\0') {\n        if (skip_lst(*f, lst))\n            f++;\n        else\n            *t++ = *f++;\n    }\n    *t = '\\0';\n    return t;\n}\n"
  },
  {
    "path": "lib/common/utils.h",
    "content": "/*-\n * Copyright(c) <2010-2026>, Intel Corporation. All rights reserved.\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/* Created 2014 by Keith Wiles @ intel.com */\n\n#ifndef _UTILS_H_\n#define _UTILS_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * The function is a wrapper around strdup() and will free the previous string\n * if the pointer is present.\n */\n\nstatic __inline__ char *\npg_strdupf(char *str, char *new)\n{\n    if (str)\n        free(str);\n    return (new == NULL) ? NULL : strdup(new);\n}\n\n/**\n * Trim a set of characters like \"[]\" or \"{}\" from the start and end of string.\n *\n * @param str\n *   A null terminated string to be trimmed.\n * @param set\n *   The <set> string is a set of two character values to be removed from the\n *   <str>. Removes only one set at a time, if you have more then one set to\n *   remove then you must call the routine for each set. The <set> string must\n *   be two characters and can be any characters you\n *   want to call a set.\n * @return\n *   Pointer to the trimmed string or NULL on error\n */\nstatic __inline__ char *\npg_strtrimset(char *str, const char *set)\n{\n    int len;\n\n    len = strlen(set);\n    if ((len == 0) || (len & 1))\n        return NULL;\n\n    for (; set && (set[0] != '\\0'); set += 2) {\n        if (*str != *set)\n            continue;\n\n        if (*str == *set++)\n            str++;\n\n        len = strlen(str);\n        if (len && (str[len - 1] == *set))\n            str[len - 1] = '\\0';\n    }\n    return str;\n}\n\nuint32_t pg_strparse(char *s, const char *delim, char **entries, uint32_t max_entries);\nchar *pg_strccpy(char *t, char *f, const char *str);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _UTILS_H_ */\n"
  },
  {
    "path": "lib/hmap/README.md",
    "content": "# HMAP - Hash Map Library\n\n## Overview\n\nHMAP is a high-performance, type-safe hash map implementation optimized for DPDK applications. It provides a flexible key-value store with support for multiple data types, arrays, and efficient memory management.\n\n## Key Features\n\n- **Type-safe storage**: Native support for 12+ scalar types and their arrays\n- **Prefix-based organization**: Group related keys using optional prefixes\n- **Zero-copy arrays**: Direct pointer access to array data\n- **Cache-aligned structures**: Optimized for modern CPU architectures\n- **Thread-safe options**: Built-in mutex support for concurrent access\n- **Unified 64-bit storage**: Efficient memory layout with minimal overhead\n- **Auto-managed keys**: Keys and prefixes are automatically duplicated and freed\n\n## Important: String Value Lifetime Requirements\n\n**The caller is responsible for managing the lifetime of string VALUES, but NOT keys/prefixes.**\n\n### Memory Ownership Model\n\nHMAP uses a mixed memory ownership model:\n\n- **Keys and Prefixes**: HMAP-managed - automatically duplicated and freed by hmap\n- **String Values**: Caller-managed - must remain valid for entry lifetime\n- **String Arrays**: Caller-managed - neither the array nor its elements are copied\n- **Scalar Values**: Copied by value into hmap's internal storage\n- **Scalar Arrays**: Caller-managed - pointer is stored, data must remain valid\n\n### Benefits\n\nThis design provides:\n- **Flexible keys**: Use dynamic strings, constants, or temporaries for keys/prefixes\n- **Efficient values**: No allocation overhead for scalar values\n- **Predictable performance**: No hidden allocations except for keys/prefixes\n- **Simple cleanup**: Free your value allocations after hmap_destroy()\n\n### String Value Examples\n\n```c\n// ✓ CORRECT: String literals (most common and recommended)\nhmap_add_value(hmap, \"config\", \"app_name\", \"MyApp\");  // String literal - valid forever\n\n// ✓ CORRECT: Keys can be dynamic (automatically duplicated by hmap)\nchar key_buf[32];\nsnprintf(key_buf, sizeof(key_buf), \"port_%d\", port_num);\nhmap_add_value(hmap, \"network\", key_buf, port_config);  // key_buf duplicated internally\n\n// ✓ CORRECT: Caller-managed dynamic string value\nchar *description = strdup(\"My application description\");\nhmap_add_value(hmap, \"config\", \"description\", description);\n// ... use hmap ...\nhmap_destroy(hmap);\nfree(description);  // Free AFTER hmap is destroyed\n\n// ✓ CORRECT: Static/global strings\nstatic char global_string[] = \"Global Value\";\nhmap_add_value(hmap, \"config\", \"global\", global_string);\n\n// ✗ WRONG: Stack variable (will be invalid after function returns)\nvoid bad_example() {\n    char local_buffer[256];\n    snprintf(local_buffer, sizeof(local_buffer), \"Value %d\", 42);\n    hmap_add_value(hmap, \"test\", \"value\", local_buffer);  // DANGLING POINTER!\n}\n\n// ✗ WRONG: Freeing string while still in hmap\nchar *temp = strdup(\"temp\");\nhmap_add_value(hmap, \"test\", \"temp\", temp);\nfree(temp);  // WRONG - hmap still has pointer to freed memory!\nchar *result;\nhmap_get_value(hmap, \"test\", \"temp\", &result);  // result now points to freed memory!\n```\n\n### String Array Examples\n\n```c\n// ✓ CORRECT: Caller-managed string array\nchar **allowed_ips = malloc(3 * sizeof(char *));\nint allocated = 1;\nif (allocated) {\n\tallowed_ips[0] = strdup(\"192.168.1.1\");\n\tallowed_ips[1] = strdup(\"10.0.0.1\");\n\tallowed_ips[2] = strdup(\"172.16.0.1\");\n} else { // Or use string literals\n\tallowed_ips[0] = \"192.168.1.1\";\n\tallowed_ips[1] = \"10.0.0.1\";\n\tallowed_ips[2] = \"172.16.0.1\";\n}\n\nhmap_add_array(hmap, \"security\", \"allowed_ips\", allowed_ips, 3);\n// ... use hmap ...\nhmap_destroy(hmap);\n\n// Free AFTER hmap is destroyed only when entries are allocated by caller\nif (allocated) {\n\tfor (int i = 0; i < 3; i++)\n\t\tfree(allowed_ips[i]);\n}\nfree(allowed_ips);\n```\n\n### Key and Prefix Examples\n\n```c\n// ✓ CORRECT: String literals (most common and recommended)\nhmap_add_value(hmap, \"config\", \"port\", 8080);\n\n// ✓ CORRECT: Static/global strings\nstatic const char *prefix = \"settings\";\nstatic const char *key = \"timeout\";\nhmap_add_value(hmap, prefix, key, 30);\n\n// ✓ CORRECT: Caller-managed dynamic strings\nchar *my_key = strdup(\"dynamic_key\");\nhmap_add_value(hmap, \"prefix\", my_key, value);\n// ... later, AFTER removing from hmap or destroying hmap:\nfree(my_key);\n\n// ✗ WRONG: Local stack variable (will be invalid after function returns)\nvoid bad_example() {\n    char key[32];\n    snprintf(key, sizeof(key), \"key_%d\", 42);\n    hmap_add_value(hmap, \"test\", key, 123);  // DANGLING POINTER!\n}\n```\n\n### Complete Memory Management Example\n\n```c\nhmap_t *config = hmap_create(\"app-config\", 64, NULL);\n\n// Add string value (caller-managed)\nchar *app_name = strdup(\"MyApp\"); // can be a string literal or dynamic string\nhmap_add_value(config, \"app\", \"name\", app_name);\n\n// Add string array (caller-managed) with allocated elements\nchar **ips = malloc(2 * sizeof(char *));\nips[0] = strdup(\"192.168.1.1\");\nips[1] = strdup(\"10.0.0.1\");\nhmap_add_array(config, \"security\", \"ips\", ips, 2);\n\n// Add scalar values (copied by hmap, no management needed)\nhmap_add_value(config, \"app\", \"port\", (uint32_t)8080);\nhmap_add_value(config, \"app\", \"timeout\", (uint32_t)30);\n\n// ... use the hmap ...\n\n// Destroy hmap first\nhmap_destroy(config);\n\n// Then free your allocated memory\nfree(app_name); // because string values are caller-managed\nfor (int i = 0; i < 2; i++)\n    free(ips[i]); // free each string in the array\nfree(ips);\n```\n\n## Performance Optimization\n\n### Memory Allocation Behavior\n\nHMAP uses a **flat array with linear probing** for optimal cache performance:\n- ✓ No per-entry allocation - entries occupy pre-allocated array slots\n- ✓ Cache-friendly 32-byte KVP structures\n- ✓ Only allocates when resizing the array\n\n**Key Insight**: The main allocation overhead comes from resizing, not per-entry operations.\n\n### Best Practice: Pre-Size Your HashMap\n\n**Simply set appropriate capacity at creation to eliminate all resizing:**\n\n```c\n// ✗ BAD: Default capacity causes 5+ reallocations for 1000 entries\nhmap_t *hmap = hmap_create(\"config\", 0, HMAP_USE_DEFAULT_CAPACITY);\nfor (int i = 0; i < 1000; i++)\n    hmap_add_value(hmap, \"test\", keys[i], i);  // Multiple resizes!\n\n// ✓ GOOD: Pre-size for expected capacity - ZERO reallocations\nhmap_t *hmap = hmap_create(\"config\", 0, 1024);\nfor (int i = 0; i < 1000; i++)\n    hmap_add_value(hmap, \"test\", keys[i], i);  // No resizing needed\n```\n\n**Capacity Guidelines**:\n- Set `max_capacity` to ~1.3x expected maximum entries\n- Hash table performs best at 60-80% full\n- Example: For 1000 entries, use capacity of 1300-1500\n\n### Allocation Tracking\n\nEnable allocation tracking to measure and validate performance:\n\n```c\n// Compile with -DHMAP_TRACK_ALLOCS to enable tracking\n\n#ifdef HMAP_TRACK_ALLOCS\nuint64_t allocs, reallocs, bytes;\nhmap_get_global_alloc_stats(&allocs, &reallocs, &bytes);\nprintf(\"Allocations: %lu, Reallocations: %lu, Total bytes: %lu\\n\",\n       allocs, reallocs, bytes);\n\n// Per-hmap tracking\nuint64_t hmap_allocs = hmap_get_alloc_count(hmap);\nprintf(\"This hmap performed %lu allocations\\n\", hmap_allocs);\n#endif\n```\n\n### Performance Comparison\n\n| Approach | Allocs (1000 inserts) | Performance | Use Case |\n|----------|----------------------|-------------|----------|\n| Default capacity | 5-7 (resizing) | Good | General use |\n| **Pre-sized** | **1** | **Excellent** | **Recommended** |\n\n## Basic Usage\n\n### Creating a Hash Map\n\n```c\n#include \"hmap.h\"\n\n// Create a hash map with default capacity (1024)\nhmap_t *hmap = hmap_create(\"my-config\", 0, HMAP_DEFAULT_CAPACITY);\nif (!hmap) {\n    // Handle error\n}\n\n// Create with custom capacity\nhmap_t *hmap2 = hmap_create(\"large-map\", 0, 4096);\n```\n\n### Adding Values\n\nUsing C11 `_Generic` macros for type-safe, readable code:\n\n```c\n// Scalar values - type is automatically inferred\nhmap_add_value(hmap, \"app\", \"name\", \"my-application\");\nhmap_add_value(hmap, \"settings\", \"port\", 8080);\nhmap_add_value(hmap, \"stats\", \"counter\", -42L);\nhmap_add_value(hmap, \"metrics\", \"ratio\", 3.14159);\nhmap_add_value(hmap, \"flags\", \"debug\", true);\n\n// Add without prefix (global keys)\nhmap_add_value(hmap, NULL, \"version\", 1);\n\n// Arrays - type is automatically inferred from array pointer\nuint32_t ports[] = {8080, 8081, 8082, 8083};\nchar *names[] = {\"alice\", \"bob\", \"charlie\"};\ndouble ratios[] = {1.5, 2.5, 3.75};\n\nhmap_add_array(hmap, \"config\", \"ports\", ports, 4);\nhmap_add_array(hmap, \"users\", \"names\", names, 3);\nhmap_add_array(hmap, \"stats\", \"ratios\", ratios, 3);\n```\n\n### Retrieving Values\n\nUsing C11 `_Generic` macros for type-safe retrieval:\n\n```c\n// Scalar values - type is automatically inferred from pointer\nuint32_t port;\nchar *name;\nuint8_t debug;  // Boolean: 0=false, 1=true (stored as uint8_t)\ndouble ratio;\n\nhmap_get_value(hmap, \"settings\", \"port\", &port);\nhmap_get_value(hmap, \"app\", \"name\", &name);\nhmap_get_value(hmap, \"flags\", \"debug\", &debug);\nhmap_get_value(hmap, \"metrics\", \"ratio\", &ratio);\n\n// Arrays - type is automatically inferred from pointer\nuint32_t *ports;\nchar **names;\n\nint port_count = hmap_get_array(hmap, \"config\", \"ports\", &ports);\nint name_count = hmap_get_array(hmap, \"users\", \"names\", &names);\n\n// Access array elements\nfor (int i = 0; i < port_count; i++) {\n    printf(\"Port[%d] = %u\\n\", i, ports[i]);\n}\n```\n\n### Updating Values\n\n```c\n// Update existing value\nhmap_kvp_t *kvp = hmap_kvp_lookup(hmap, \"settings\", \"port\");\nif (kvp) {\n    hmap_val_t new_val = {.u64 = 9090};\n    hmap_kvp_update(hmap, kvp, &new_val);\n} else {\n\t// Key not found\n\thmap_add_value(hmap, \"settings\", \"port\", 9090);\n}\n```\n\n### Checking Existence\n\n```c\nhmap_kvp_t *kvp = hmap_kvp_lookup(hmap, \"settings\", \"port\");\nif (kvp) {\n    printf(\"Key exists with type: %d\\n\", kvp->type);\n} else {\n    printf(\"Key not found\\n\");\n}\n```\n\n### Iteration\n\n```c\n// List all entries (unsorted)\nhmap_list(hmap, stdout, false);\n\n// List all entries (sorted by key)\nhmap_list(hmap, stdout, true);\n\n// Iterate programmatically\nhmap_kvp_t *kvp_list[HMAP_DEFAULT_CAPACITY];\nint count = hmap_kvp_list(hmap, kvp_list, HMAP_DEFAULT_CAPACITY, false);\n\nfor (int i = 0; i < count; i++) {\n    hmap_kvp_t *kvp = kvp_list[i];\n    printf(\"Prefix: %s, Key: %s, Type: %d\\n\",\n           kvp->prefix ? kvp->prefix : \"(none)\",\n           kvp->key,\n           kvp->type);\n}\n```\n\n### Cleanup\n\n```c\n// Destroy the hash map and free all resources\nhmap_destroy(hmap);\n```\n\n## Complete Example\n\n```c\n#include <stdio.h>\n#include \"hmap.h\"\n\nint main(void)\n{\n    // Create hash map\n    hmap_t *config = hmap_create(\"app-config\", 0, 64);\n    if (!config)\n        return -1;\n\n    // Add configuration values - keys and prefixes are automatically duplicated by hmap\n    // String values need dynamic allocation (caller-managed)\n    char *app_name = strdup(\"MyApp\");\n    hmap_add_value(config, \"app\", \"name\", app_name);\n\n    // Scalar values are copied by hmap (no management needed)\n    hmap_add_value(config, \"app\", \"version\", (uint32_t)2);\n    hmap_add_value(config, \"server\", \"port\", (uint32_t)8080);\n    hmap_add_value(config, \"server\", \"ssl\", (uint8_t)1);  // Boolean as uint8_t: 0=false, 1=true\n    hmap_add_value(config, \"server\", \"timeout\", (uint32_t)30);\n\n    // Add array of allowed IPs (caller-managed allocation)\n    char **allowed_ips = malloc(3 * sizeof(char *));\n    allowed_ips[0] = strdup(\"192.168.1.1\");\n    allowed_ips[1] = strdup(\"10.0.0.1\");\n    allowed_ips[2] = strdup(\"172.16.0.1\");\n    hmap_add_array(config, \"security\", \"allowed_ips\", allowed_ips, 3);\n\n    // Retrieve and display values using simplified API\n    char *retrieved_name;\n    uint32_t version, port, timeout;\n    uint8_t ssl;  // Boolean: 0=false, 1=true\n\n    hmap_get_value(config, \"app\", \"name\", &retrieved_name);\n    hmap_get_value(config, \"app\", \"version\", &version);\n    hmap_get_value(config, \"server\", \"port\", &port);\n    hmap_get_value(config, \"server\", \"ssl\", &ssl);\n    hmap_get_value(config, \"server\", \"timeout\", &timeout);\n\n    printf(\"Application: %s v%u\\n\", retrieved_name, version);\n    printf(\"Server: port=%u, ssl=%s, timeout=%us\\n\",\n           port, ssl ? \"enabled\" : \"disabled\", timeout);\n\n    // Retrieve and display allowed IPs\n    char **ips;\n    int ip_count = hmap_get_array(config, \"security\", \"allowed_ips\", &ips);\n\n    printf(\"Allowed IPs (%d):\\n\", ip_count);\n    for (int i = 0; i < ip_count; i++) {\n        printf(\"  %d. %s\\n\", i + 1, ips[i]);\n    }\n\n    // List all configuration\n    printf(\"\\nComplete configuration:\\n\");\n    hmap_list(config, stdout, true);\n\n    // Cleanup: Destroy hmap (automatically frees keys/prefixes), then free caller-managed memory\n    hmap_destroy(config);\n\n    // Free caller-managed string value\n    free(app_name);\n\n    // Free caller-managed string array and its elements\n    for (int i = 0; i < 3; i++)\n        free(allowed_ips[i]);\n    free(allowed_ips);\n\n    return 0;\n}\n```\n\n## Supported Types\n\n### Scalar Types\n- **Strings**: `char *` - Null-terminated C strings\n- **Integers**: `int8_t`, `int16_t`, `int32_t`, `int64_t`\n- **Unsigned**: `uint8_t`, `uint16_t`, `uint32_t`, `uint64_t`\n- **Floating**: `double` - 64-bit IEEE 754\n- **Boolean**: `uint8_t` - Stored as 0 (false) or 1 (true), use uint8_t type\n- **Pointer**: `void *` - Generic pointer\n\n### Array Types\nAll scalar types have corresponding array versions:\n- `char **` - String arrays\n- `uint32_t *`, `int64_t *`, etc. - Numeric arrays\n- `uint8_t *` - Boolean arrays (0=false, 1=true)\n- `void **` - Pointer arrays\n\n## Advanced Features\n\n### Custom Hash Functions\n\n```c\n// Define custom hash function\nuint32_t my_hash(const char *prefix, const char *key) {\n    // Your hash implementation\n    return custom_hash_value;\n}\n\n// Create map with custom functions\nhmap_funcs_t funcs = {\n    .hash_fn = my_hash,\n    .cmp_fn = default_cmp,\n    .free_fn = default_free\n};\n\nhmap_t *hmap = hmap_create_with_funcs(\"custom\", 0, 128, &funcs);\n```\n\n### Prefix-Based Organization\n\nPrefixes allow logical grouping of related keys:\n\n```c\n// Group by functional area\nhmap_add_u32(hmap, \"network\", \"port\", 8080);\nhmap_add_u32(hmap, \"network\", \"timeout\", 30);\nhmap_add_bool(hmap, \"network\", \"ssl\", true);\n\nhmap_add_string(hmap, \"database\", \"host\", \"localhost\");\nhmap_add_u32(hmap, \"database\", \"port\", 5432);\nhmap_add_string(hmap, \"database\", \"name\", \"mydb\");\n\n// Retrieve by prefix\nuint32_t net_port, db_port;\nhmap_get_value(hmap, \"network\", \"port\", &net_port);    // 8080\nhmap_get_value(hmap, \"database\", \"port\", &db_port);    // 5432\n```\n\n### Memory Management\n\nHMAP manages memory automatically:\n- **Strings**: Copies are made internally, freed on destroy\n- **Arrays**: Stores pointers only - caller manages array memory\n- **String arrays**: Deep copies made for string array elements\n- **Updates**: Old values automatically freed when updated\n\n```c\n// String is copied internally\nhmap_add_string(hmap, \"app\", \"name\", \"MyApp\");\n// Safe to free or modify original string\n\n// Array pointer is stored (not copied)\nuint32_t ports[] = {8080, 8081};\nhmap_add_u32_array(hmap, \"config\", \"ports\", ports, 2);\n// Caller must keep 'ports' valid while in hmap\n\n// String array elements are deep copied\nchar *names[] = {\"alice\", \"bob\"};\nhmap_add_string_array(hmap, \"users\", \"names\", names, 2);\n// Safe to free 'names' array after adding\n```\n\n### Performance Considerations\n\n- **Initial Capacity**: Choose based on expected entries to minimize rehashing\n- **Cache Alignment**: Structures optimized for 64-byte cache lines\n- **Open Addressing**: Fast lookups with good cache locality\n- **Type-Tagged Storage**: Zero overhead for most types (64-bit values)\n- **Count in KVP**: Arrays have zero allocation overhead for metadata\n\n## Error Handling\n\nMost functions return:\n- `0` on success, `-1` on failure (add/update/get operations)\n- `NULL` on failure (creation, lookup operations)\n- Array count or `-1` on failure (array get operations)\n\n```c\nif (hmap_add_u32(hmap, \"config\", \"port\", 8080) != 0) {\n    fprintf(stderr, \"Failed to add port\\n\");\n}\n\nhmap_kvp_t *kvp = hmap_kvp_lookup(hmap, \"config\", \"port\");\nif (!kvp) {\n    fprintf(stderr, \"Key not found\\n\");\n}\n```\n\n## Best Practices\n\n1. **Use the generic macros**: `hmap_add_value()`, `hmap_add_array()`, `hmap_get_value()`, and `hmap_get_array()` provide type-safe, readable code\n2. **Choose meaningful prefixes**: Group related configuration logically\n3. **Set appropriate capacity**: Avoid rehashing by estimating entry count\n4. **Check return values**: Always verify operations succeeded\n5. **Manage array lifetimes**: Keep array data valid while referenced by hmap\n6. **Use sorted listings**: Enable sorting for better debugging/inspection\n7. **Destroy when done**: Always call `hmap_destroy()` to prevent leaks\n\n## API Reference\n\n### Creation & Destruction\n- `hmap_create()` - Create with default functions\n- `hmap_create_with_funcs()` - Create with custom functions\n- `hmap_create_with_ext_storage()` - Create with external storage\n- `hmap_destroy()` - Free all resources\n\n### Adding Values\n- **`hmap_add_value(hmap, prefix, key, value)`** - Type-safe scalar setter\n  - Supports: `char*`, `bool`, `uint64_t`, `uint32_t`, `uint16_t`, `uint8_t`, `int64_t`, `int32_t`, `int16_t`, `int8_t`, `double`, `float`, `void*`\n  - Returns: `0` on success, `-1` on failure\n\n- **`hmap_add_array(hmap, prefix, key, array, count)`** - Type-safe array setter\n  - Supports arrays of all scalar types above\n  - Returns: `0` on success, `-1` on failure\n\n### Getting Values\n- **`hmap_get_value(hmap, prefix, key, &value)`** - Type-safe scalar getter\n  - Type inferred from value pointer\n  - Returns: `0` on success, `-1` on failure\n\n- **`hmap_get_array(hmap, prefix, key, &array)`** - Type-safe array getter\n  - Type inferred from array pointer\n  - Returns: Array count on success, `-1` on failure\n\n### Lookup & Update\n- `hmap_kvp_lookup()` - Find key-value pair\n- `hmap_kvp_update()` - Update existing value\n\n### Inspection\n- `hmap_list()` - Print to file stream\n- `hmap_kvp_list()` - Get array of pointers\n- `hmap_iterate()` - Callback-based iteration\n- `hmap_get_funcs()` - Get function pointers\n\n### Implementation Details\n\nThe generic macros use C11 `_Generic` to dispatch to internal type-specific functions (prefixed with `_hmap_`). These internal functions are not part of the public API and should not be called directly.\n\n## See Also\n\n- [JCFG Library](../jcfg/) - JSON configuration parser built on HMAP\n- [Examples](../../../examples/) - Complete usage examples\n- [hmap.h](hmap.h) - Full API documentation\n"
  },
  {
    "path": "lib/hmap/hmap.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright (c) 2016-2026 Intel Corporation\n */\n\n#include <stdlib.h>               // for free, calloc, malloc, qsort\n#include <string.h>               // for strlen, strncmp, strerror, strnlen\n#include <errno.h>                // for EEXIST\n#include <bsd/sys/queue.h>        // for TAILQ_FOREACH_SAFE\n#include <bsd/string.h>           // for strlcpy\n#include <rte_log.h>\n\n#include \"hmap.h\"\n#include \"hmap_log.h\"\n#include \"hmap_helper.h\"\n\n/* Allocation tracking for performance measurement */\n#ifdef HMAP_TRACK_ALLOCS\nstatic _Atomic uint64_t hmap_alloc_count   = 0;\nstatic _Atomic uint64_t hmap_realloc_count = 0;\nstatic _Atomic uint64_t hmap_total_bytes   = 0;\n#define HMAP_ALLOC_INC()   (__atomic_add_fetch(&hmap_alloc_count, 1, __ATOMIC_SEQ_CST))\n#define HMAP_REALLOC_INC() (__atomic_add_fetch(&hmap_realloc_count, 1, __ATOMIC_SEQ_CST))\n#define HMAP_BYTES_ADD(n)  (__atomic_add_fetch(&hmap_total_bytes, (n), __ATOMIC_SEQ_CST))\n#else\n#define HMAP_ALLOC_INC() \\\n    do {                 \\\n    } while (0)\n#define HMAP_REALLOC_INC() \\\n    do {                   \\\n    } while (0)\n#define HMAP_BYTES_ADD(n) \\\n    do {                  \\\n    } while (0)\n#endif\n\nint\nhmap_set_funcs(hmap_t *hmap, hmap_funcs_t *fns)\n{\n    // clang-format off\n    hmap_funcs_t default_funcs = {\n        .hash_fn = _hmap_hash,\n        .cmp_fn  = _hmap_cmp,\n        .free_fn = _hmap_free\n    };\n    // clang-format on\n\n    if (!hmap)\n        return -1;\n\n    if (!fns)\n        fns = &default_funcs;\n\n    /*\n     * Test each function pointer to make sure the user passed a valid function pointer,\n     * if they did not use the default function.\n     */\n    hmap->fns.hash_fn = (fns->hash_fn) ? fns->hash_fn : default_funcs.hash_fn;\n    hmap->fns.free_fn = (fns->free_fn) ? fns->free_fn : default_funcs.free_fn;\n    hmap->fns.cmp_fn  = (fns->cmp_fn) ? fns->cmp_fn : default_funcs.cmp_fn;\n\n    return 0;\n}\n\nhmap_funcs_t *\nhmap_get_funcs(hmap_t *hmap)\n{\n    return (hmap) ? &hmap->fns : NULL;\n}\n\n/* Hmap's need a hash function, an equality function, and a destructor */\nhmap_t *\nhmap_create(const char *name, uint32_t max_capacity, hmap_funcs_t *funcs)\n{\n    hmap_t *hmap = NULL;\n\n    if (!name)\n        name = \"HMAP\";\n\n    if (strnlen(name, HMAP_MAX_NAME_SIZE) >= HMAP_MAX_NAME_SIZE)\n        HMAP_NULL_RET(\"Name is greater than or equal to %d bytes\\n\", HMAP_MAX_NAME_SIZE);\n\n    hmap = calloc(1, sizeof(hmap_t));\n    if (!hmap)\n        HMAP_ERR_GOTO(err_leave, \"Failed to allocate hmap structure\\n\");\n\n    HMAP_ALLOC_INC();\n    HMAP_BYTES_ADD(sizeof(hmap_t));\n\n    strlcpy(hmap->name, name, sizeof(hmap->name));\n\n    /*\n     * if max_capacity is zero, set to HMAP_DEFAULT_CAPACITY\n     * if max_capacity is less than HMAP_STARTING_CAPACITY, set to HMAP_STARTING_CAPACITY\n     */\n    if (max_capacity == HMAP_USE_DEFAULT_CAPACITY)\n        max_capacity = HMAP_DEFAULT_CAPACITY;\n    if (max_capacity < HMAP_STARTING_CAPACITY)\n        max_capacity = HMAP_STARTING_CAPACITY;\n\n    hmap->capacity = max_capacity / 4;\n    if (hmap->capacity == 0)\n        hmap->capacity = HMAP_STARTING_CAPACITY;\n    hmap->max_capacity = max_capacity;\n    hmap->alloc_count  = 0;\n\n    /* Start out with the initial capacity */\n    hmap->map = calloc(hmap->capacity, sizeof(struct hmap_kvp));\n    if (!hmap->map)\n        HMAP_ERR_GOTO(err_leave, \"Failed to allocate KVP for %d capacity\\n\", hmap->capacity);\n\n    HMAP_ALLOC_INC();\n    HMAP_BYTES_ADD(hmap->capacity * sizeof(struct hmap_kvp));\n    hmap->alloc_count++;\n\n    if (hmap_set_funcs(hmap, funcs) < 0)\n        HMAP_ERR_GOTO(err_leave, \"Failed to set function pointers\\n\");\n\n    if (hmap_mutex_create(&hmap->mutex, PTHREAD_MUTEX_RECURSIVE_NP) < 0)\n        HMAP_ERR_GOTO(err_leave, \"mutex init(hmap->mutex) failed: %s\\n\", strerror(errno));\n\n    hmap_list_lock();\n    TAILQ_INSERT_TAIL(&hmap_registry, hmap, next);\n    hmap_list_unlock();\n\n    return hmap;\n\nerr_leave:\n    if (hmap) {\n        free(hmap->map);\n        free(hmap);\n    }\n\n    return NULL;\n}\n\nint\nhmap_destroy(hmap_t *hmap)\n{\n    if (!hmap)\n        return 0;\n\n    hmap_lock(hmap);\n\n    if (hmap->map) {\n        struct hmap_kvp *kvp = hmap->map;\n\n        for (uint32_t i = 0; i < hmap->capacity; i++, kvp++)\n            hmap->fns.free_fn(kvp);\n\n        /* Indicate the hmap is not usable anymore, possible race condition */\n        hmap->max_capacity = hmap->capacity = hmap->curr_capacity = 0;\n\n        free(hmap->map);\n    }\n    hmap_list_lock();\n    TAILQ_REMOVE(&hmap_registry, hmap, next);\n    hmap_list_unlock();\n\n    hmap_unlock(hmap);\n\n    if (hmap_mutex_destroy(&hmap->mutex))\n        HMAP_ERR_RET(\"Destroy of mutex failed\\n\");\n\n    free(hmap);\n\n    return 0;\n}\n\nint\nhmap_destroy_by_name(const char *name)\n{\n    hmap_t *hmap, *tvar;\n\n    TAILQ_FOREACH_SAFE (hmap, &hmap_registry, next, tvar) {\n        if (!strncmp(name, hmap->name, strlen(name)))\n            return hmap_destroy(hmap);\n    }\n\n    return -1;\n}\n\nint\nhmap_kvp_update(hmap_t *hmap, hmap_kvp_t *kvp, hmap_val_t *val)\n{\n    if (!hmap || !kvp || !val)\n        return -1;\n\n    hmap_lock(hmap);\n\n    /*\n     * Values are caller-managed (strings, arrays, pointers).\n     * Updating an entry only overwrites the stored pointer/64-bit value.\n     */\n    kvp->v = *val;\n\n    /* Keep array count unchanged (set by add/update array helpers). */\n    if (kvp->type < HMAP_STR_ARRAY_TYPE)\n        kvp->count = 0;\n\n    hmap_unlock(hmap);\n\n    return 0;\n}\n\n/* Open addressed insertion function */\nstatic inline int\n__add_value(hmap_t *hmap, hmap_type_t type, const char *prefix, const char *key, hmap_val_t *val)\n{\n    uint32_t hash = hmap_get_hash(hmap, prefix, key);\n\n    for (uint32_t i = 0; i < hmap->capacity; i++) {\n        struct hmap_kvp *kvp;\n\n        kvp = &hmap->map[hash];\n        if (kvp->type == HMAP_EMPTY_TYPE) {\n            /* Duplicate key and prefix strings - hmap now owns them */\n            kvp->key = strdup(key);\n            if (!kvp->key)\n                return -1;\n\n            if (prefix) {\n                kvp->prefix = strdup(prefix);\n                if (!kvp->prefix) {\n                    free(kvp->key);\n                    kvp->key = NULL;\n                    return -1;\n                }\n            }\n\n            kvp->type = type;\n            if (hmap_kvp_update(hmap, kvp, val)) {\n                hmap->fns.free_fn(kvp);\n                return -1;\n            }\n\n            hmap->curr_capacity++;\n            return 0;\n        }\n        hash = (hash + 1) % hmap->capacity;\n    }\n    return -1;\n}\n\nstatic int\n_hmap_update_capacity(hmap_t *hmap)\n{\n    /* Increase the map if free space becomes too small and does not exceed max_capacity */\n    if ((hmap->curr_capacity >= (hmap->capacity - (HMAP_STARTING_CAPACITY / 4))) &&\n        ((hmap->capacity + HMAP_STARTING_CAPACITY) <= hmap->max_capacity)) {\n        struct hmap_kvp *kvp, *old_map, *new_map;\n        uint32_t old_capacity;\n\n        old_capacity = hmap->capacity;\n        old_map      = hmap->map;\n\n        hmap->capacity += HMAP_STARTING_CAPACITY;\n\n        new_map = calloc(hmap->capacity, sizeof(struct hmap_kvp));\n        if (new_map == NULL) {\n            hmap->capacity = old_capacity;\n            return -1;\n        }\n\n        HMAP_REALLOC_INC();\n        HMAP_BYTES_ADD(hmap->capacity * sizeof(struct hmap_kvp));\n        hmap->alloc_count++;\n\n        hmap->map           = new_map;\n        hmap->curr_capacity = 0;\n\n        /* Add all of the old values into the new map. */\n        kvp = old_map;\n        for (uint32_t i = 0; i < old_capacity; i++, kvp++) {\n            if (kvp->type == HMAP_EMPTY_TYPE)\n                continue;\n            if (__add_value(hmap, kvp->type, kvp->prefix, kvp->key, &kvp->v)) {\n                free(new_map);\n                hmap->map      = old_map;\n                hmap->capacity = old_capacity;\n                return -1;\n            }\n        }\n        free(old_map);\n    }\n\n    return 0;\n}\n\nint\nhmap_add(hmap_t *hmap, hmap_type_t type, const char *prefix, const char *key, hmap_val_t *val)\n{\n    int ret = -1;\n\n    if (!hmap || !key)        // prefix can be NULL\n        return ret;\n\n    /* Do not allow for duplicates in the hash map */\n    if (hmap_kvp_lookup(hmap, prefix, key))\n        return ret;\n\n    hmap_lock(hmap);\n\n    if ((ret = _hmap_update_capacity(hmap)) == 0)\n        ret = __add_value(hmap, type, prefix, key, val);\n\n    hmap_unlock(hmap);\n    return ret;\n}\n\nint\nhmap_update(hmap_t *hmap, hmap_type_t type, const char *prefix, const char *key, hmap_val_t *val)\n{\n    hmap_kvp_t *kvp;\n    int ret = -1;\n\n    if (!hmap || !key)        // prefix can be NULL\n        return ret;\n\n    hmap_lock(hmap);\n\n    // Update existing key value if it exists\n    if ((kvp = hmap_kvp_lookup(hmap, prefix, key)) != NULL) {\n        if (kvp->type != type)\n            ret = -1;\n        else\n            ret = hmap_kvp_update(hmap, kvp, val);\n    } else {\n        // Otherwise add new key/value pair\n        if ((ret = _hmap_update_capacity(hmap)) == 0)\n            ret = __add_value(hmap, type, prefix, key, val);\n    }\n\n    hmap_unlock(hmap);\n    return ret;\n}\n\nhmap_kvp_t *\nhmap_kvp_lookup(hmap_t *hmap, const char *prefix, const char *key)\n{\n    if (hmap && hmap->map && key) {\n        uint32_t hash = hmap_get_hash(hmap, prefix, key);\n\n        hmap_lock(hmap);\n        for (uint32_t i = 0; i < hmap->capacity; i++) {\n            struct hmap_kvp *kvp = &hmap->map[hash];\n\n            if (kvp->key && hmap->fns.cmp_fn(kvp, prefix, key)) {\n                hmap_unlock(hmap);\n                return kvp;\n            }\n\n            hash = (hash + 1) % hmap->capacity;\n        }\n        hmap_unlock(hmap);\n    }\n\n    return NULL;\n}\n\nint\nhmap_lookup(hmap_t *hmap, const char *prefix, const char *key, hmap_val_t *val)\n{\n    if (hmap && key) {\n        uint32_t hash = hmap_get_hash(hmap, prefix, key);\n\n        hmap_lock(hmap);\n        for (uint32_t i = 0; i < hmap->capacity; i++) {\n            struct hmap_kvp *kvp = &hmap->map[hash];\n\n            if (kvp->key && hmap->fns.cmp_fn(kvp, prefix, key)) {\n                if (val)\n                    val->u64 = kvp->v.u64;\n                hmap_unlock(hmap);\n                return 0;\n            }\n\n            hash = (hash + 1) % hmap->capacity;\n        }\n        hmap_unlock(hmap);\n    }\n\n    return -1;\n}\n\nint\nhmap_del(hmap_t *hmap, const char *prefix, const char *key)\n{\n    if (hmap && key) {\n        hmap_kvp_t *kvp = hmap_kvp_lookup(hmap, prefix, key);\n\n        hmap->fns.free_fn(kvp);\n        hmap->curr_capacity--;\n        return 0;\n    }\n    return -1;\n}\n\nint\nhmap_iterate(hmap_t *hmap, struct hmap_kvp **_kvp, uint32_t *next)\n{\n    if (hmap && hmap->map && next) {\n        hmap_lock(hmap);\n        for (uint32_t i = *next; i < hmap->capacity; i++) {\n            struct hmap_kvp *kvp = &hmap->map[i];\n\n            if (!kvp->key)\n                continue;\n            *next = ++i;\n\n            if (_kvp)\n                *_kvp = kvp;\n            hmap_unlock(hmap);\n            return 1;\n        }\n        hmap_unlock(hmap);\n    }\n    return 0;\n}\n\nstatic int\nkvp_cmp(const void *p1, const void *p2)\n{\n    struct hmap_kvp *k1 = *(struct hmap_kvp *const *)p1;\n    struct hmap_kvp *k2 = *(struct hmap_kvp *const *)p2;\n\n    // First compare prefixes (primary sort key)\n    if (k1->prefix && k2->prefix) {\n        // Both have prefixes, compare them first\n        int prefix_cmp = strcmp(k1->prefix, k2->prefix);\n        if (prefix_cmp != 0)\n            return prefix_cmp;\n        // Prefixes are equal, compare keys as secondary sort\n        return strcmp(k1->key, k2->key);\n    } else if (!k1->prefix && !k2->prefix) {\n        // Neither has prefix, just compare keys\n        return strcmp(k1->key, k2->key);\n    } else {\n        // One has prefix, one doesn't - items with prefixes come first\n        return (k1->prefix && !k2->prefix) ? -1 : 1;\n    }\n}\n\nstatic void\n_print_kvp(FILE *f, hmap_kvp_t *kvp)\n{\n    char buff[128];\n\n    if (!kvp)\n        return;\n    if (kvp->prefix)\n        snprintf(buff, sizeof(buff), \"%s:%s\", kvp->prefix, kvp->key);\n    else\n        snprintf(buff, sizeof(buff), \"%s\", kvp->key);\n\n    fprintf(f, \"%-42s \", buff);\n\n    switch (kvp->type) {\n    case HMAP_EMPTY_TYPE:\n        fprintf(f, \"%-8s:\\n\", \"Empty\");\n        break;\n    case HMAP_U64_TYPE:\n        fprintf(f, \"%-8s: %lu\\n\", \"u64\", kvp->v.u64);\n        break;\n    case HMAP_U32_TYPE:\n        fprintf(f, \"%-8s: %u\\n\", \"u32\", (uint32_t)kvp->v.u64);\n        break;\n    case HMAP_U16_TYPE:\n        fprintf(f, \"%-8s: %u\\n\", \"u16\", (uint16_t)kvp->v.u64);\n        break;\n    case HMAP_U8_TYPE: {\n        uint8_t val = (uint8_t)kvp->v.u64;\n        fprintf(f, \"%-8s: %c(%u)\\n\", \"u8\", (val >= ' ' && val <= '~') ? val : '.', val);\n    } break;\n    case HMAP_I64_TYPE:\n        fprintf(f, \"%-8s: %ld\\n\", \"i64\", (int64_t)kvp->v.u64);\n        break;\n    case HMAP_I32_TYPE:\n        fprintf(f, \"%-8s: %d\\n\", \"i32\", (int32_t)kvp->v.u64);\n        break;\n    case HMAP_I16_TYPE:\n        fprintf(f, \"%-8s: %d\\n\", \"i16\", (int16_t)kvp->v.u64);\n        break;\n    case HMAP_I8_TYPE:\n        fprintf(f, \"%-8s: %d\\n\", \"i8\", (int8_t)kvp->v.u64);\n        break;\n    case HMAP_DOUBLE_TYPE:\n        fprintf(f, \"%-8s: %g\\n\", \"double\", kvp->v.dval);\n        break;\n    case HMAP_STR_TYPE:\n        fprintf(f, \"%-8s: '%s'\\n\", \"String\", (char *)kvp->v.ptr);\n        break;\n    case HMAP_POINTER_TYPE:\n        fprintf(f, \"%-8s: %p\\n\", \"Pointer\", kvp->v.ptr);\n        break;\n    case HMAP_STR_ARRAY_TYPE: {\n        char **strs = (char **)kvp->v.ptr;\n        fprintf(f, \"%-8s: (%u)\\n\", \"Strings\", kvp->count);\n        for (uint32_t i = 0; i < kvp->count; i++)\n            fprintf(f, \"          %3u: '%s'\\n\", i, strs[i]);\n    } break;\n    case HMAP_U64_ARRAY_TYPE: {\n        uint64_t *vals = (uint64_t *)kvp->v.ptr;\n        fprintf(f, \"%-8s: (%u)\\n\", \"u64s\", kvp->count);\n        for (uint32_t i = 0; i < kvp->count; i++)\n            fprintf(f, \"         %3u: %lu\\n\", i, vals[i]);\n    } break;\n    case HMAP_U32_ARRAY_TYPE: {\n        uint32_t *vals = (uint32_t *)kvp->v.ptr;\n        fprintf(f, \"%-8s: (%u)\\n\", \"u32s\", kvp->count);\n        for (uint32_t i = 0; i < kvp->count; i++)\n            fprintf(f, \"         %3u: %u\\n\", i, vals[i]);\n    } break;\n    case HMAP_U16_ARRAY_TYPE: {\n        uint16_t *vals = (uint16_t *)kvp->v.ptr;\n        fprintf(f, \"%-8s: (%u)\\n\", \"u16s\", kvp->count);\n        for (uint32_t i = 0; i < kvp->count; i++)\n            fprintf(f, \"         %3u: %u\\n\", i, vals[i]);\n    } break;\n    case HMAP_U8_ARRAY_TYPE: {\n        uint8_t *vals = (uint8_t *)kvp->v.ptr;\n        fprintf(f, \"%-8s: (%u)\\n\", \"u8s\", kvp->count);\n        for (uint32_t i = 0; i < kvp->count; i++)\n            fprintf(f, \"         %3u: %c(%u)\\n\", i,\n                    (vals[i] >= ' ' && vals[i] <= '~') ? vals[i] : '.', vals[i]);\n    } break;\n    case HMAP_I64_ARRAY_TYPE: {\n        int64_t *vals = (int64_t *)kvp->v.ptr;\n        fprintf(f, \"%-8s: (%u)\\n\", \"I64s\", kvp->count);\n        for (uint32_t i = 0; i < kvp->count; i++)\n            fprintf(f, \"          %3u: %ld\\n\", i, vals[i]);\n    } break;\n    case HMAP_I32_ARRAY_TYPE: {\n        int32_t *vals = (int32_t *)kvp->v.ptr;\n        fprintf(f, \"%-8s: (%u)\\n\", \"I32s\", kvp->count);\n        for (uint32_t i = 0; i < kvp->count; i++)\n            fprintf(f, \"          %3u: %d\\n\", i, vals[i]);\n    } break;\n    case HMAP_I16_ARRAY_TYPE: {\n        int16_t *vals = (int16_t *)kvp->v.ptr;\n        fprintf(f, \"%-8s: (%u)\\n\", \"I16s\", kvp->count);\n        for (uint32_t i = 0; i < kvp->count; i++)\n            fprintf(f, \"          %3u: %d\\n\", i, vals[i]);\n    } break;\n    case HMAP_I8_ARRAY_TYPE: {\n        int8_t *vals = (int8_t *)kvp->v.ptr;\n        fprintf(f, \"%-8s: (%u)\\n\", \"I8s\", kvp->count);\n        for (uint32_t i = 0; i < kvp->count; i++)\n            fprintf(f, \"          %3u: %d\\n\", i, vals[i]);\n    } break;\n    case HMAP_DOUBLE_ARRAY_TYPE: {\n        double *vals = (double *)kvp->v.ptr;\n        fprintf(f, \"%-8s: (%u)\\n\", \"Doubles\", kvp->count);\n        for (uint32_t i = 0; i < kvp->count; i++)\n            fprintf(f, \"          %3u: %g\\n\", i, vals[i]);\n    } break;\n    case HMAP_PTR_ARRAY_TYPE: {\n        void **vals = (void **)kvp->v.ptr;\n        fprintf(f, \"%-8s: (%u)\\n\", \"Pointers\", kvp->count);\n        for (uint32_t i = 0; i < kvp->count; i++)\n            fprintf(f, \"          %3u: %p\\n\", i, vals[i]);\n    } break;\n    default:\n        HMAP_ERR(\"*** Unknown type %d\\n\", kvp->type);\n        break;\n    }\n}\n\nvoid\nhmap_list(FILE *f, hmap_t *hmap, bool sort)\n{\n    uint32_t next = 0;\n    hmap_kvp_t *kvp;\n\n    if (!hmap)\n        return;\n    if (!f)\n        f = stdout;\n\n    fprintf(f, \"\\n**** Hashmap dump (%s) %s ****\\n\", hmap->name, sort ? \"Sorted\" : \"Not Sorted\");\n    fprintf(f, \" %-5s: %-42s %s\\n\", \"Index\", \"Prefix:Key\", \"Type:Value\");\n\n    if (sort) {\n        // Sorted output using qsort\n        struct hmap_kvp **kvp_list;\n        int cnt = hmap_count(hmap);\n\n        kvp_list = (struct hmap_kvp **)malloc(cnt * sizeof(struct hmap_kvp *));\n        if (!kvp_list)\n            return;\n\n        for (int i = 0; hmap_iterate(hmap, &kvp, &next); i++)\n            kvp_list[i] = kvp;\n\n        qsort(kvp_list, cnt, sizeof(struct hmap_kvp *), kvp_cmp);\n\n        for (int i = 0; i < cnt; i++) {\n            fprintf(f, \" %5d: \", i);\n            _print_kvp(f, kvp_list[i]);\n        }\n\n        free(kvp_list);\n    } else {\n        // Unsorted output using simple iteration\n        for (int i = 0; hmap_iterate(hmap, &kvp, &next); i++) {\n            fprintf(f, \" %5d: \", i);\n            _print_kvp(f, kvp);\n        }\n    }\n\n    fprintf(f, \"Total count: %u\\n\", hmap_count(hmap));\n}\n\nvoid\nhmap_list_names(FILE *f)\n{\n    hmap_t *hmap, *tvar;\n\n    if (!f)\n        f = stdout;\n\n    printf(\"\\n**** Hashmap List ****\\n\");\n    TAILQ_FOREACH_SAFE (hmap, &hmap_registry, next, tvar)\n        printf(\"   %s\\n\", hmap->name);\n}\n\nvoid\nhmap_list_by_name(FILE *f, char *name, bool sort)\n{\n    hmap_t *hmap, *tvar;\n\n    if (!f)\n        f = stdout;\n\n    printf(\"\\n**** Hashmap List ****\\n\");\n    TAILQ_FOREACH_SAFE (hmap, &hmap_registry, next, tvar)\n        if (!strncmp(name, hmap->name, strlen(name)))\n            hmap_list(f, hmap, sort);\n}\n\nvoid\nhmap_list_all(FILE *f, bool sort)\n{\n    hmap_t *hmap, *tvar;\n\n    if (!f)\n        f = stdout;\n\n    TAILQ_FOREACH_SAFE (hmap, &hmap_registry, next, tvar)\n        hmap_list(f, hmap, sort);\n}\n\nRTE_INIT_PRIO(hmap_constructor, LOG)\n{\n    TAILQ_INIT(&hmap_registry);\n\n    if (hmap_mutex_create(&hmap_list_mutex, PTHREAD_MUTEX_RECURSIVE_NP) < 0)\n        HMAP_RET(\"mutex init(hmap_list_mutex) failed\\n\");\n}\n\n/* Allocation tracking functions - always available */\nvoid\nhmap_get_global_alloc_stats(uint64_t *alloc_count, uint64_t *realloc_count, uint64_t *total_bytes)\n{\n#ifdef HMAP_TRACK_ALLOCS\n    if (alloc_count)\n        *alloc_count = __atomic_load_n(&hmap_alloc_count, __ATOMIC_SEQ_CST);\n    if (realloc_count)\n        *realloc_count = __atomic_load_n(&hmap_realloc_count, __ATOMIC_SEQ_CST);\n    if (total_bytes)\n        *total_bytes = __atomic_load_n(&hmap_total_bytes, __ATOMIC_SEQ_CST);\n#else\n    if (alloc_count)\n        *alloc_count = 0;\n    if (realloc_count)\n        *realloc_count = 0;\n    if (total_bytes)\n        *total_bytes = 0;\n#endif\n}\n\nvoid\nhmap_reset_global_alloc_stats(void)\n{\n#ifdef HMAP_TRACK_ALLOCS\n    __atomic_store_n(&hmap_alloc_count, 0, __ATOMIC_SEQ_CST);\n    __atomic_store_n(&hmap_realloc_count, 0, __ATOMIC_SEQ_CST);\n    __atomic_store_n(&hmap_total_bytes, 0, __ATOMIC_SEQ_CST);\n#endif\n}\n"
  },
  {
    "path": "lib/hmap/hmap.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright (c) 2019-2026 Intel Corporation\n */\n\n#pragma once\n\n/**\n * @file\n *\n * This library provides an API for the hashmap data structure.\n *\n * PUBLIC API:\n * The hmap library exposes a minimal, type-safe public API using C11 _Generic macros:\n *   - hmap_add_value()    - Add scalar values (automatically dispatches by value type)\n *   - hmap_add_array()    - Add array values (automatically dispatches by array type)\n *   - hmap_update_value() - Update-or-add scalar values (automatically dispatches by value type)\n *   - hmap_update_array() - Update-or-add array values (automatically dispatches by array type)\n *   - hmap_get_value()    - Retrieve scalar values (automatically dispatches by pointer type)\n *   - hmap_get_array()    - Retrieve array values (automatically dispatches by pointer type)\n *\n * INTERNAL FUNCTIONS:\n * Type-specific functions (e.g., _hmap_add_string, _hmap_get_u32) are prefixed with\n * underscore and are not part of the public API. They should not be called directly.\n */\n\n// IWYU pragma: no_include <bits/stdint-uintn.h>\n\n#include <stdbool.h>          // for bool\n#include <string.h>           // for NULL\n#include <strings.h>          // for strcasecmp\n#include <stdint.h>           // for uint32_t, int64_t, uint16_t, uint64_t, uint8_t\n#include <stdio.h>            // for FILE\n#include <sys/queue.h>        // for TAILQ_ENTRY\n#include <pthread.h>          // for pthread_mutex_t\n\n#include \"rte_common.h\"\n#include \"rte_log.h\"\n#include \"hmap_log.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define HMAP_MAX_NAME_SIZE        32   /**< MAX size of HMAP name */\n#define HMAP_MAX_KEY_SIZE         256  /**< Key size */\n#define HMAP_STARTING_CAPACITY    32   /**< Starting capacity not exceeding max_capacity */\n#define HMAP_DEFAULT_CAPACITY     1024 /**< Default starting capacity not exceeding max_capacity */\n#define HMAP_USE_DEFAULT_CAPACITY 0    /**< Use the default capacity value */\n\nstruct hmap;\n\n// clang-format off\n/** Value type stored in an hmap key-value pair. */\ntypedef enum {\n    HMAP_EMPTY_TYPE,        /**< Slot is empty / unoccupied */\n    HMAP_STR_TYPE,          /**< NUL-terminated string (char *) */\n    HMAP_U64_TYPE,          /**< Unsigned 64-bit integer */\n    HMAP_U32_TYPE,          /**< Unsigned 32-bit integer */\n    HMAP_U16_TYPE,          /**< Unsigned 16-bit integer */\n    HMAP_U8_TYPE,           /**< Unsigned 8-bit integer */\n    HMAP_I64_TYPE,          /**< Signed 64-bit integer */\n    HMAP_I32_TYPE,          /**< Signed 32-bit integer */\n    HMAP_I16_TYPE,          /**< Signed 16-bit integer */\n    HMAP_I8_TYPE,           /**< Signed 8-bit integer */\n    HMAP_DOUBLE_TYPE,       /**< IEEE 754 double-precision float */\n    HMAP_POINTER_TYPE,      /**< Generic void pointer */\n    HMAP_STR_ARRAY_TYPE,    /**< Array of NUL-terminated strings (char **) */\n    HMAP_U64_ARRAY_TYPE,    /**< Array of unsigned 64-bit integers */\n    HMAP_U32_ARRAY_TYPE,    /**< Array of unsigned 32-bit integers */\n    HMAP_U16_ARRAY_TYPE,    /**< Array of unsigned 16-bit integers */\n    HMAP_U8_ARRAY_TYPE,     /**< Array of unsigned 8-bit integers */\n    HMAP_I64_ARRAY_TYPE,    /**< Array of signed 64-bit integers */\n    HMAP_I32_ARRAY_TYPE,    /**< Array of signed 32-bit integers */\n    HMAP_I16_ARRAY_TYPE,    /**< Array of signed 16-bit integers */\n    HMAP_I8_ARRAY_TYPE,     /**< Array of signed 8-bit integers */\n    HMAP_DOUBLE_ARRAY_TYPE, /**< Array of double-precision floats */\n    HMAP_PTR_ARRAY_TYPE,    /**< Array of generic pointers */\n    HMAP_NUM_TYPES          /**< Sentinel: number of defined types */\n} hmap_type_t;\n// clang-format on\n\n/**\n * @brief Unified value storage using single 64-bit value\n *\n * All scalar types stored directly, strings/pointers as uintptr_t, arrays via pointer\n */\ntypedef union {\n    uint64_t u64; /**< Universal 64-bit storage for all types */\n    double dval;  /**< Double value (64-bit IEEE 754) */\n    void *ptr;    /**< Generic pointer (for strings, pointers, arrays) */\n} hmap_val_t;\n\n/**\n * @brief Key/value pair stored in the hashmap table.\n */\ntypedef struct hmap_kvp { /**< Key/value pair (implementation-defined size) */\n    hmap_type_t type;     /**< Type of the value stored in kvp */\n    uint32_t count;       /**< Array element count (0 for non-arrays, uses padding slot) */\n    char *prefix;         /**< Prefix string value (hmap-managed, duplicated internally) */\n    char *key;            /**< String key pointer (hmap-managed, duplicated internally) */\n    hmap_val_t v;         /**< Values stored in kvp */\n} hmap_kvp_t;\n\ntypedef uint32_t (*hash_fn_t)(const char *prefix, const char *key);\ntypedef int (*cmp_fn_t)(const hmap_kvp_t *kvp, const char *prefix, const char *key);\ntypedef void (*free_fn_t)(hmap_kvp_t *kvp);\n\ntypedef struct hmap_funcs {\n    hash_fn_t hash_fn; /**< Hash function pointer */\n    cmp_fn_t cmp_fn;   /**< Compare function pointer */\n    free_fn_t free_fn; /**< User kvp free routine pointer */\n} hmap_funcs_t;\n\n/**\n * @brief A structure used to retrieve information of a hmap object\n */\ntypedef struct hmap {\n    TAILQ_ENTRY(hmap) next;        /**< List of next hmap entries */\n    char name[HMAP_MAX_NAME_SIZE]; /**< Name of hmap */\n    uint32_t capacity;             /**< Total capacity */\n    uint32_t max_capacity;         /**< Max capacity should not be exceeded */\n    uint32_t curr_capacity;        /**< Current capacity */\n    pthread_mutex_t mutex;         /**< Mutex for hmap */\n    hmap_funcs_t fns;              /**< Function pointers */\n    hmap_kvp_t *map;               /**< Pointer to the key/value table */\n    uint64_t alloc_count;          /**< Allocation counter for this hmap */\n} hmap_t;\n\n/**\n * @brief Create a hashmap.\n *\n * @param name\n *   Optional name for this hashmap (used for debugging/registry). If NULL, a default name is used.\n * @param capacity\n *   Maximum capacity in entries. If 0, a default is used. The internal table may start smaller and\n *   grow up to this limit.\n * @param funcs\n *   Optional pointer to hmap_funcs_t to override hash/compare/free callbacks. If NULL, defaults\n *   are used.\n * @return\n *   Pointer to a new hmap_t on success, NULL on error.\n */\nhmap_t *hmap_create(const char *name, uint32_t capacity, hmap_funcs_t *funcs);\n\n/**\n * @brief Get allocation statistics for a specific hmap\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @return\n *   Number of allocations performed by this hmap\n */\nstatic inline uint64_t\nhmap_get_alloc_count(hmap_t *hmap)\n{\n    return hmap ? hmap->alloc_count : 0;\n}\n\n/**\n * @brief Destroy a hashmap.\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @return\n *   0 - successful or -1 on error\n */\nint hmap_destroy(hmap_t *hmap);\n\n/**\n * @brief Destroy a hashmap by name.\n *\n * @param name\n *   The name of the hmap\n * @return\n *   0 on success or -1 on error.\n */\nint hmap_destroy_by_name(const char *name);\n\n/**\n * @brief Lookup an entry by prefix/key.\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string (can be NULL for global keys)\n * @param key\n *   The key to search for in the hashmap\n * @return\n *   Pointer to the entry if found, NULL if not found or invalid arguments.\n *\n * @note The returned pointer is owned by the hmap and is only valid while the entry exists.\n */\nhmap_kvp_t *hmap_kvp_lookup(hmap_t *hmap, const char *prefix, const char *key);\n\n/**\n * @brief Lookup an entry by prefix/key and optionally copy out its raw value.\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string (can be NULL for global keys)\n * @param key\n *   The key to search for in the hashmap\n * @param val\n *   Pointer to hmap_val_t to return value can be NULL for no return value\n * @return\n *   0 if found, -1 if not found or invalid arguments.\n */\nint hmap_lookup(hmap_t *hmap, const char *prefix, const char *key, hmap_val_t *val);\n\n/**\n * @brief Add a key/value pair to the hashmap.\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param type\n *   The hmap_type_t type for the value pointer\n * @param prefix\n *   The prefix string to locate in the hashmap table (can be NULL)\n * @param key\n *   The key value string to add\n * @param val\n *   The value pointer to store with the key/value entry.\n * @return\n *   0 on success or -1 on error (including duplicate prefix/key).\n */\nint hmap_add(hmap_t *hmap, hmap_type_t type, const char *prefix, const char *key, hmap_val_t *val);\n\n/**\n * @brief Update a key/value pair if it exists, otherwise add it.\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param type\n *   The hmap_type_t type for the value pointer\n * @param prefix\n *   The prefix string to locate in the hashmap table (can be NULL)\n * @param key\n *   The key value string to update\n * @param val\n *   The value pointer to store with the key/value entry.\n * @return\n *   0 on success or -1 on error.\n */\nint hmap_update(hmap_t *hmap, hmap_type_t type, const char *prefix, const char *key,\n                hmap_val_t *val);\n\n/**\n * @brief Update the value for an existing key/value pair returned by hmap_kvp_lookup().\n *\n * @param hmap\n *   Pointer to hmap structure\n * @param kvp\n *   The key/value pair structure to update\n * @param val\n *   The pointer to the new value.\n * @return\n *   0 on success, -1 on error.\n */\nint hmap_kvp_update(hmap_t *hmap, hmap_kvp_t *kvp, hmap_val_t *val);\n\n/**\n * @brief Internal type-specific add helpers.\n *\n * These inline functions are used by the public `hmap_add_value()` and `hmap_add_array()` macros.\n * They are not intended to be called directly.\n */\n\n/**\n * @brief Add a string value to the hmap. (internal)\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string to locate in the hashmap table\n * @param key\n *   The key value string to add\n * @param val\n *   The value to be stored in the hmap table.\n * @return\n *   0 on success or -1 on error.\n */\nstatic inline int\n_hmap_add_string(hmap_t *hmap, const char *prefix, const char *key, const char *val)\n{\n    hmap_val_t v = {.ptr = (void *)(uintptr_t)val};\n\n    return hmap_add(hmap, HMAP_STR_TYPE, prefix, key, &v);\n}\n\n/**\n * @brief Internal type-specific update helpers.\n *\n * These inline functions are used by the public `hmap_update_value()` and\n * `hmap_update_array()` macros. They are not intended to be called directly.\n */\n\n/** @brief Update-or-add a string value in the hmap. (internal) */\nstatic inline int\n_hmap_update_string(hmap_t *hmap, const char *prefix, const char *key, const char *val)\n{\n    hmap_val_t v = {.ptr = (void *)(uintptr_t)val};\n\n    return hmap_update(hmap, HMAP_STR_TYPE, prefix, key, &v);\n}\n\n/** @brief Update-or-add a uint64_t value in the hmap. (internal) */\nstatic inline int\n_hmap_update_u64(hmap_t *hmap, const char *prefix, const char *key, uint64_t val)\n{\n    hmap_val_t v = {.u64 = val};\n\n    return hmap_update(hmap, HMAP_U64_TYPE, prefix, key, &v);\n}\n\n/** @brief Update-or-add a uint32_t value in the hmap. (internal) */\nstatic inline int\n_hmap_update_u32(hmap_t *hmap, const char *prefix, const char *key, uint32_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)val};\n\n    return hmap_update(hmap, HMAP_U32_TYPE, prefix, key, &v);\n}\n\n/** @brief Update-or-add a uint16_t value in the hmap. (internal) */\nstatic inline int\n_hmap_update_u16(hmap_t *hmap, const char *prefix, const char *key, uint16_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)val};\n\n    return hmap_update(hmap, HMAP_U16_TYPE, prefix, key, &v);\n}\n\n/** @brief Update-or-add a uint8_t value in the hmap (also used for booleans). (internal) */\nstatic inline int\n_hmap_update_u8(hmap_t *hmap, const char *prefix, const char *key, uint8_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)val};\n\n    return hmap_update(hmap, HMAP_U8_TYPE, prefix, key, &v);\n}\n\n/** @brief Update-or-add an int64_t value in the hmap. (internal) */\nstatic inline int\n_hmap_update_i64(hmap_t *hmap, const char *prefix, const char *key, int64_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)val};\n\n    return hmap_update(hmap, HMAP_I64_TYPE, prefix, key, &v);\n}\n\n/** @brief Update-or-add an int32_t value in the hmap. (internal) */\nstatic inline int\n_hmap_update_i32(hmap_t *hmap, const char *prefix, const char *key, int32_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)(uint32_t)val};\n\n    return hmap_update(hmap, HMAP_I32_TYPE, prefix, key, &v);\n}\n\n/** @brief Update-or-add an int16_t value in the hmap. (internal) */\nstatic inline int\n_hmap_update_i16(hmap_t *hmap, const char *prefix, const char *key, int16_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)(uint16_t)val};\n\n    return hmap_update(hmap, HMAP_I16_TYPE, prefix, key, &v);\n}\n\n/** @brief Update-or-add an int8_t value in the hmap. (internal) */\nstatic inline int\n_hmap_update_i8(hmap_t *hmap, const char *prefix, const char *key, int8_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)(uint8_t)val};\n\n    return hmap_update(hmap, HMAP_I8_TYPE, prefix, key, &v);\n}\n\n/** @brief Update-or-add a double value in the hmap. (internal) */\nstatic inline int\n_hmap_update_double(hmap_t *hmap, const char *prefix, const char *key, double val)\n{\n    hmap_val_t v = {.dval = val};\n\n    return hmap_update(hmap, HMAP_DOUBLE_TYPE, prefix, key, &v);\n}\n\n/** @brief Update-or-add a pointer value in the hmap. (internal) */\nstatic inline int\n_hmap_update_pointer(hmap_t *hmap, const char *prefix, const char *key, void *val)\n{\n    hmap_val_t v = {.ptr = val};\n\n    return hmap_update(hmap, HMAP_POINTER_TYPE, prefix, key, &v);\n}\n\n/** @brief Update-or-add a string array in the hmap. (internal) */\nstatic inline int\n_hmap_update_string_array(hmap_t *hmap, const char *prefix, const char *key, char **val,\n                          uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_update(hmap, HMAP_STR_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Update-or-add a uint64_t array in the hmap. (internal) */\nstatic inline int\n_hmap_update_u64_array(hmap_t *hmap, const char *prefix, const char *key, uint64_t *val,\n                       uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_update(hmap, HMAP_U64_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Update-or-add a uint32_t array in the hmap. (internal) */\nstatic inline int\n_hmap_update_u32_array(hmap_t *hmap, const char *prefix, const char *key, uint32_t *val,\n                       uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_update(hmap, HMAP_U32_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Update-or-add a uint16_t array in the hmap. (internal) */\nstatic inline int\n_hmap_update_u16_array(hmap_t *hmap, const char *prefix, const char *key, uint16_t *val,\n                       uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_update(hmap, HMAP_U16_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Update-or-add a uint8_t array in the hmap. (internal) */\nstatic inline int\n_hmap_update_u8_array(hmap_t *hmap, const char *prefix, const char *key, uint8_t *val,\n                      uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_update(hmap, HMAP_U8_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Update-or-add an int64_t array in the hmap. (internal) */\nstatic inline int\n_hmap_update_i64_array(hmap_t *hmap, const char *prefix, const char *key, int64_t *val,\n                       uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_update(hmap, HMAP_I64_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Update-or-add an int32_t array in the hmap. (internal) */\nstatic inline int\n_hmap_update_i32_array(hmap_t *hmap, const char *prefix, const char *key, int32_t *val,\n                       uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_update(hmap, HMAP_I32_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Update-or-add an int16_t array in the hmap. (internal) */\nstatic inline int\n_hmap_update_i16_array(hmap_t *hmap, const char *prefix, const char *key, int16_t *val,\n                       uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_update(hmap, HMAP_I16_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Update-or-add an int8_t array in the hmap. (internal) */\nstatic inline int\n_hmap_update_i8_array(hmap_t *hmap, const char *prefix, const char *key, int8_t *val,\n                      uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_update(hmap, HMAP_I8_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Update-or-add a double array in the hmap. (internal) */\nstatic inline int\n_hmap_update_double_array(hmap_t *hmap, const char *prefix, const char *key, double *val,\n                          uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_update(hmap, HMAP_DOUBLE_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Update-or-add a pointer array in the hmap. (internal) */\nstatic inline int\n_hmap_update_pointer_array(hmap_t *hmap, const char *prefix, const char *key, void **val,\n                           uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_update(hmap, HMAP_PTR_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Add a uint64_t value to the hmap. (internal) */\nstatic inline int\n_hmap_add_u64(hmap_t *hmap, const char *prefix, const char *key, uint64_t val)\n{\n    hmap_val_t v = {.u64 = val};\n\n    return hmap_add(hmap, HMAP_U64_TYPE, prefix, key, &v);\n}\n\n/** @brief Add a uint32_t value to the hmap. (internal) */\nstatic inline int\n_hmap_add_u32(hmap_t *hmap, const char *prefix, const char *key, uint32_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)val};\n\n    return hmap_add(hmap, HMAP_U32_TYPE, prefix, key, &v);\n}\n\n/** @brief Add a uint16_t value to the hmap. (internal) */\nstatic inline int\n_hmap_add_u16(hmap_t *hmap, const char *prefix, const char *key, uint16_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)val};\n\n    return hmap_add(hmap, HMAP_U16_TYPE, prefix, key, &v);\n}\n\n/** @brief Add a uint8_t value to the hmap (also used for booleans). (internal) */\nstatic inline int\n_hmap_add_u8(hmap_t *hmap, const char *prefix, const char *key, uint8_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)val};\n\n    return hmap_add(hmap, HMAP_U8_TYPE, prefix, key, &v);\n}\n\n/** @brief Add an int64_t value to the hmap. (internal) */\nstatic inline int\n_hmap_add_i64(hmap_t *hmap, const char *prefix, const char *key, int64_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)val};\n\n    return hmap_add(hmap, HMAP_I64_TYPE, prefix, key, &v);\n}\n\n/** @brief Add an int32_t value to the hmap. (internal) */\nstatic inline int\n_hmap_add_i32(hmap_t *hmap, const char *prefix, const char *key, int32_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)(uint32_t)val};\n\n    return hmap_add(hmap, HMAP_I32_TYPE, prefix, key, &v);\n}\n\n/** @brief Add an int16_t value to the hmap. (internal) */\nstatic inline int\n_hmap_add_i16(hmap_t *hmap, const char *prefix, const char *key, int16_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)(uint16_t)val};\n\n    return hmap_add(hmap, HMAP_I16_TYPE, prefix, key, &v);\n}\n\n/** @brief Add an int8_t value to the hmap. (internal) */\nstatic inline int\n_hmap_add_i8(hmap_t *hmap, const char *prefix, const char *key, int8_t val)\n{\n    hmap_val_t v = {.u64 = (uint64_t)(uint8_t)val};\n\n    return hmap_add(hmap, HMAP_I8_TYPE, prefix, key, &v);\n}\n\n/** @brief Add a double value to the hmap. (internal) */\nstatic inline int\n_hmap_add_double(hmap_t *hmap, const char *prefix, const char *key, double val)\n{\n    hmap_val_t v = {.dval = val};\n\n    return hmap_add(hmap, HMAP_DOUBLE_TYPE, prefix, key, &v);\n}\n\n/** @brief Add a pointer value to the hmap. (internal) */\nstatic inline int\n_hmap_add_pointer(hmap_t *hmap, const char *prefix, const char *key, void *val)\n{\n    hmap_val_t v = {.ptr = val};\n\n    return hmap_add(hmap, HMAP_POINTER_TYPE, prefix, key, &v);\n}\n\n/** @brief Add a string array to the hmap. (internal) */\nstatic inline int\n_hmap_add_string_array(hmap_t *hmap, const char *prefix, const char *key, char **val,\n                       uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_add(hmap, HMAP_STR_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Add a uint64_t array to the hmap. (internal) */\nstatic inline int\n_hmap_add_u64_array(hmap_t *hmap, const char *prefix, const char *key, uint64_t *val,\n                    uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_add(hmap, HMAP_U64_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Add a uint32_t array to the hmap. (internal) */\nstatic inline int\n_hmap_add_u32_array(hmap_t *hmap, const char *prefix, const char *key, uint32_t *val,\n                    uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_add(hmap, HMAP_U32_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Add a uint16_t array to the hmap. (internal) */\nstatic inline int\n_hmap_add_u16_array(hmap_t *hmap, const char *prefix, const char *key, uint16_t *val,\n                    uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_add(hmap, HMAP_U16_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/**\n * @brief Add a uint8_t array to the hmap. (internal)\n *\n * Note: Boolean arrays are stored as uint8_t arrays (0=false, 1=true).\n *       Use this function to store boolean arrays.\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string pointer, can be NULL if a global value\n * @param key\n *   The key value string\n * @param val\n *   Pointer to the uint8_t array\n * @param count\n *   Number of elements in the array\n * @return\n *   0 - successful or -1 - failed\n */\nstatic inline int\n_hmap_add_u8_array(hmap_t *hmap, const char *prefix, const char *key, uint8_t *val, uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_add(hmap, HMAP_U8_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Add an int64_t array to the hmap. (internal) */\nstatic inline int\n_hmap_add_i64_array(hmap_t *hmap, const char *prefix, const char *key, int64_t *val, uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_add(hmap, HMAP_I64_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Add an int32_t array to the hmap. (internal) */\nstatic inline int\n_hmap_add_i32_array(hmap_t *hmap, const char *prefix, const char *key, int32_t *val, uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_add(hmap, HMAP_I32_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Add an int16_t array to the hmap. (internal) */\nstatic inline int\n_hmap_add_i16_array(hmap_t *hmap, const char *prefix, const char *key, int16_t *val, uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_add(hmap, HMAP_I16_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Add an int8_t array to the hmap. (internal) */\nstatic inline int\n_hmap_add_i8_array(hmap_t *hmap, const char *prefix, const char *key, int8_t *val, uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_add(hmap, HMAP_I8_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Add a double array to the hmap. (internal) */\nstatic inline int\n_hmap_add_double_array(hmap_t *hmap, const char *prefix, const char *key, double *val,\n                       uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_add(hmap, HMAP_DOUBLE_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/** @brief Add a pointer array to the hmap. (internal) */\nstatic inline int\n_hmap_add_pointer_array(hmap_t *hmap, const char *prefix, const char *key, void **val,\n                        uint32_t count)\n{\n    hmap_val_t v = {.ptr = val};\n    hmap_kvp_t *kvp;\n    int ret = hmap_add(hmap, HMAP_PTR_ARRAY_TYPE, prefix, key, &v);\n    if (ret == 0) {\n        kvp = hmap_kvp_lookup(hmap, prefix, key);\n        if (kvp)\n            kvp->count = count;\n    }\n    return ret;\n}\n\n/**\n * @brief Delete a hashmap entry\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string for the hashmap\n * @param key\n *   The Key to search for in the table\n * @return\n *   -1 on error or 0 on success\n */\nint hmap_del(hmap_t *hmap, const char *prefix, const char *key);\n\n/**\n * @brief Iterate over entries in the hashmap.\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param _kvp\n *   The address to place the key/value pair structure pointer\n * @param next\n *   The next entry to iterate as an index value. Initialize to 0 before the first call.\n * @return\n *   1 if an entry is returned, 0 when there are no more entries (or on invalid arguments).\n *\n * Example:\n *   uint32_t next = 0;\n *   hmap_kvp_t *kvp;\n *   while (hmap_iterate(hmap, &kvp, &next)) {\n *       // use kvp\n *   }\n */\nint hmap_iterate(hmap_t *hmap, hmap_kvp_t **_kvp, uint32_t *next);\n\n/**\n * @brief List (dump) out all of the entries in a hashmap.\n * Renamed from hmap_dump() to hmap_list() to better reflect intent.\n *\n * @param f\n *   The file descriptor to use for output of the text\n * @param hmap\n *   Pointer to the hmap structure to list\n * @param sort\n *   If true sort the output by prefix/key before listing\n */\nvoid hmap_list(FILE *f, hmap_t *hmap, bool sort);\n\n/**\n * @brief Get the current table capacity (number of slots).\n *\n * @param hmap\n *   Pointer to the hmap structure.\n * @return\n *   Current table capacity.\n */\nstatic inline uint32_t\nhmap_capacity(hmap_t *hmap)\n{\n    return hmap->capacity;\n}\n\n/**\n * @brief Get the number of populated entries currently stored.\n *\n * @param hmap\n *   Pointer to the hmap structure.\n * @return\n *   Number of active entries.\n */\nstatic inline uint32_t\nhmap_count(hmap_t *hmap)\n{\n    return hmap->curr_capacity;\n}\n\n/**\n * @brief Get the set of hmap function pointers.\n *\n * @param hmap\n *   The hmap structure pointer\n * @return\n *   NULL on error or pointer to the hmap function structure.\n */\nhmap_funcs_t *hmap_get_funcs(hmap_t *hmap);\n\n/**\n * @brief Set hmap callback functions.\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param funcs\n *   Pointer to a set of function pointers. Any NULL members fall back to defaults.\n * @return\n *   0 on success, -1 on error.\n */\nint hmap_set_funcs(hmap_t *hmap, hmap_funcs_t *funcs);\n\n/**\n * @brief Get a key/value pair from the hmap if it exists. (internal)\n *\n * @param hmap\n *   Pointer to the hmap structure.\n * @param prefix\n *   The prefix string pointer (can be NULL for global keys).\n * @param key\n *   The key string.\n * @param type\n *   Expected type of the entry.\n * @return\n *   Pointer to matching key/value pair or NULL if not found or type mismatch.\n */\nstatic inline hmap_kvp_t *\n__get_kvp(hmap_t *hmap, const char *prefix, const char *key, hmap_type_t type)\n{\n    hmap_kvp_t *kvp;\n\n    if (!hmap)\n        HMAP_NULL_RET(\"get failed - hmap not defined %s(%s)\\n\", prefix ? prefix : \"\", key);\n\n    kvp = hmap_kvp_lookup(hmap, prefix, key);\n    if (!kvp)\n        return kvp;\n    else if (kvp->type != type)\n        HMAP_NULL_RET(\"wrong type for %s(%s)\\n\", prefix ? prefix : \"\", key);\n\n    return kvp;\n}\n\n/**\n * @brief Get a uint64_t value from the hmap. (internal)\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string pointer, can be NULL if a global value\n * @param key\n *   The key value string to get the uint64_t value.\n * @param val\n *   The location to store the value.\n * @return\n *   0 - successful or -1 - failed\n */\nstatic inline int\n_hmap_get_u64(hmap_t *hmap, const char *prefix, const char *key, uint64_t *val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_U64_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = kvp->v.u64;\n\n    return 0;\n}\n\n/**\n * @brief Get a uint32_t value from the hmap. (internal)\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string pointer, can be NULL if a global value\n * @param key\n *   The key value string to get the uint32_t value.\n * @param val\n *   The location to store the value.\n * @return\n *   0 - successful or -1 - failed\n */\nstatic inline int\n_hmap_get_u32(hmap_t *hmap, const char *prefix, const char *key, uint32_t *val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_U32_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (uint32_t)kvp->v.u64;\n\n    return 0;\n}\n\n/**\n * @brief Get a uint16_t value from the hmap. (internal)\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string pointer, can be NULL if a global value\n * @param key\n *   The key value string to get the uint16_t value.\n * @param val\n *   The location to store the value.\n * @return\n *   0 - successful or -1 - failed\n */\nstatic inline int\n_hmap_get_u16(hmap_t *hmap, const char *prefix, const char *key, uint16_t *val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_U16_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (uint16_t)kvp->v.u64;\n\n    return 0;\n}\n\n/**\n * @brief Get a uint8_t value from the hmap. (internal)\n *\n * Note: Boolean values are stored as uint8_t (0=false, 1=true).\n *       Use this function to retrieve boolean values.\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string pointer, can be NULL if a global value\n * @param key\n *   The key value string to get the uint8_t value.\n * @param val\n *   The location to store the value.\n * @return\n *   0 - successful or -1 - failed\n */\nstatic inline int\n_hmap_get_u8(hmap_t *hmap, const char *prefix, const char *key, uint8_t *val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_U8_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (uint8_t)kvp->v.u64;\n\n    return 0;\n}\n\n/**\n * @brief Get a number value from the hmap as a int64_t. (internal)\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string pointer, can be NULL if a global value\n * @param key\n *   The key value string to get the number value.\n * @param val\n *   The location to store the value.\n * @return\n *   0 - successful or -1 - failed\n */\nstatic inline int\n_hmap_get_i64(hmap_t *hmap, const char *prefix, const char *key, int64_t *val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_I64_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (int64_t)kvp->v.u64;\n\n    return 0;\n}\n\n/**\n * @brief Get a number value from the hmap as a int32_t. (internal)\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string pointer, can be NULL if a global value\n * @param key\n *   The key value string to get the number value.\n * @param val\n *   The location to store the value.\n * @return\n *   0 - successful or -1 - failed\n */\nstatic inline int\n_hmap_get_i32(hmap_t *hmap, const char *prefix, const char *key, int32_t *val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_I32_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (int32_t)kvp->v.u64;\n\n    return 0;\n}\n\n/**\n * @brief Get a number value from the hmap as a int16_t. (internal)\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string pointer, can be NULL if a global value\n * @param key\n *   The key value string to get the number value.\n * @param val\n *   The location to store the value.\n * @return\n *   0 - successful or -1 - failed\n */\nstatic inline int\n_hmap_get_i16(hmap_t *hmap, const char *prefix, const char *key, int16_t *val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_I16_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (int16_t)kvp->v.u64;\n\n    return 0;\n}\n\n/**\n * @brief Get a number value from the hmap as a int8_t. (internal)\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string pointer, can be NULL if a global value\n * @param key\n *   The key value string to get the number value.\n * @param val\n *   The location to store the value.\n * @return\n *   0 - successful or -1 - failed\n */\nstatic inline int\n_hmap_get_i8(hmap_t *hmap, const char *prefix, const char *key, int8_t *val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_I8_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (int8_t)kvp->v.u64;\n\n    return 0;\n}\n\n/** @brief Get a double value from the hmap. (internal) */\nstatic inline int\n_hmap_get_double(hmap_t *hmap, const char *prefix, const char *key, double *val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_DOUBLE_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = kvp->v.dval;\n\n    return 0;\n}\n\n/**\n * @brief Get a string value from the hmap. (internal)\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string pointer, can be NULL if a global value\n * @param key\n *   The key value string to get the string value.\n * @param val\n *   A pointer to the return value or NULL if no return value is needed.\n * @return\n *   0 on success, -1 on error.\n */\nstatic inline int\n_hmap_get_string(hmap_t *hmap, const char *prefix, const char *key, char **val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_STR_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (char *)kvp->v.ptr;\n\n    return 0;\n}\n\n/**\n * @brief Get a pointer value from the hmap. (internal)\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string pointer, can be NULL if a global value\n * @param key\n *   The key string.\n * @param val\n *   A pointer to the return value or NULL if no return value is needed.\n * @return\n *   0 - successful or -1 on error\n */\nstatic inline int\n_hmap_get_pointer(hmap_t *hmap, const char *prefix, const char *key, void **val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_POINTER_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = kvp->v.ptr;\n\n    return 0;\n}\n\n/**\n * @brief Get a string array from the hmap. (internal)\n *\n * @param hmap\n *   Pointer to the hmap structure.\n * @param prefix\n *   The prefix string pointer (can be NULL for global keys).\n * @param key\n *   The key string.\n * @param val\n *   Pointer to store the array pointer.\n * @return\n *   Number of elements on success, -1 on failure.\n */\nstatic inline int\n_hmap_get_string_array(hmap_t *hmap, const char *prefix, const char *key, char ***val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_STR_ARRAY_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (char **)kvp->v.ptr;\n\n    return kvp->count;\n}\n\n/** @brief Get a uint64_t array from the hmap. (internal) */\nstatic inline int\n_hmap_get_u64_array(hmap_t *hmap, const char *prefix, const char *key, uint64_t **val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_U64_ARRAY_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (uint64_t *)kvp->v.ptr;\n\n    return kvp->count;\n}\n\n/** @brief Get a uint32_t array from the hmap. (internal) */\nstatic inline int\n_hmap_get_u32_array(hmap_t *hmap, const char *prefix, const char *key, uint32_t **val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_U32_ARRAY_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (uint32_t *)kvp->v.ptr;\n\n    return kvp->count;\n}\n\n/** @brief Get a uint16_t array from the hmap. (internal) */\nstatic inline int\n_hmap_get_u16_array(hmap_t *hmap, const char *prefix, const char *key, uint16_t **val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_U16_ARRAY_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (uint16_t *)kvp->v.ptr;\n\n    return kvp->count;\n}\n/**\n * @brief Get a uint8_t array from the hmap. (internal)\n *\n * Note: Boolean arrays are stored as uint8_t arrays (0=false, 1=true).\n *       Use this function to retrieve boolean arrays.\n *\n * @param hmap\n *   Pointer to the hmap structure\n * @param prefix\n *   The prefix string pointer, can be NULL if a global value\n * @param key\n *   The key value string\n * @param val\n *   Pointer to store the array pointer\n * @return\n *   Array count on success, -1 on failure\n */\nstatic inline int\n_hmap_get_u8_array(hmap_t *hmap, const char *prefix, const char *key, uint8_t **val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_U8_ARRAY_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (uint8_t *)kvp->v.ptr;\n\n    return kvp->count;\n}\n\n/** @brief Get an int64_t array from the hmap. (internal) */\nstatic inline int\n_hmap_get_i64_array(hmap_t *hmap, const char *prefix, const char *key, int64_t **val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_I64_ARRAY_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (int64_t *)kvp->v.ptr;\n\n    return kvp->count;\n}\n\n/** @brief Get an int32_t array from the hmap. (internal) */\nstatic inline int\n_hmap_get_i32_array(hmap_t *hmap, const char *prefix, const char *key, int32_t **val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_I32_ARRAY_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (int32_t *)kvp->v.ptr;\n\n    return kvp->count;\n}\n\n/** @brief Get an int16_t array from the hmap. (internal) */\nstatic inline int\n_hmap_get_i16_array(hmap_t *hmap, const char *prefix, const char *key, int16_t **val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_I16_ARRAY_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (int16_t *)kvp->v.ptr;\n\n    return kvp->count;\n}\n\n/** @brief Get an int8_t array from the hmap. (internal) */\nstatic inline int\n_hmap_get_i8_array(hmap_t *hmap, const char *prefix, const char *key, int8_t **val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_I8_ARRAY_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (int8_t *)kvp->v.ptr;\n\n    return kvp->count;\n}\n\n/** @brief Get a double array from the hmap. (internal) */\nstatic inline int\n_hmap_get_double_array(hmap_t *hmap, const char *prefix, const char *key, double **val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_DOUBLE_ARRAY_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (double *)kvp->v.ptr;\n\n    return kvp->count;\n}\n\n/** @brief Get a pointer array from the hmap. (internal) */\nstatic inline int\n_hmap_get_pointer_array(hmap_t *hmap, const char *prefix, const char *key, void ***val)\n{\n    hmap_kvp_t *kvp = __get_kvp(hmap, prefix, key, HMAP_PTR_ARRAY_TYPE);\n\n    if (!kvp)\n        return -1;\n\n    if (val)\n        *val = (void **)kvp->v.ptr;\n\n    return kvp->count;\n}\n\n/**\n * @brief List all of the hashmaps\n *\n * @param f\n *   The file descriptor to dump the text data.\n */\nvoid hmap_list_names(FILE *f);\n\n/**\n * @brief Dump a hashmap by name\n *\n * @param f\n *   The file descriptor to dump the text data.\n * @param name\n *  The name of the hashmap to dump\n * @param sort\n *   if true then sort the output\n */\nvoid hmap_list_by_name(FILE *f, char *name, bool sort);\n\n/**\n * @brief Dump all of the hmap lists\n *\n * @param f\n *   The file descriptor to dump the text data.\n * @param sort\n *   if true then sort the output\n */\nvoid hmap_list_all(FILE *f, bool sort);\n\n/**\n * @brief Type-safe generic getter macro for scalar values.\n *\n * This macro uses C11 _Generic to automatically dispatch to the correct\n * internal _hmap_get_*() function based on the type of the value pointer.\n *\n * @param hmap   hmap handle\n * @param prefix Prefix string (can be NULL)\n * @param key    Key string\n * @param value  Pointer to store the value (type determines which function is called)\n * @return       0 on success, -1 on failure\n *\n * Example:\n *   uint32_t count;\n *   hmap_get_value(hmap, \"config\", \"count\", &count);  // Calls _hmap_get_u32\n *\n *   uint8_t enabled;  // boolean stored as uint8_t (0=false, 1=true)\n *   hmap_get_value(hmap, NULL, \"enabled\", &enabled);  // Calls _hmap_get_u8\n */\n#define hmap_get_value(hmap, prefix, key, value) \\\n    _Generic((value),                            \\\n        char **: _hmap_get_string,               \\\n        uint64_t *: _hmap_get_u64,               \\\n        uint32_t *: _hmap_get_u32,               \\\n        uint16_t *: _hmap_get_u16,               \\\n        uint8_t *: _hmap_get_u8,                 \\\n        int64_t *: _hmap_get_i64,                \\\n        int32_t *: _hmap_get_i32,                \\\n        int16_t *: _hmap_get_i16,                \\\n        int8_t *: _hmap_get_i8,                  \\\n        double *: _hmap_get_double,              \\\n        void **: _hmap_get_pointer)((hmap), (prefix), (key), (value))\n\n/**\n * @brief Type-safe generic getter macro for array values.\n *\n * This macro uses C11 _Generic to automatically dispatch to the correct\n * internal _hmap_get_*_array() function based on the type of the value pointer.\n * Returns the array count and populates the pointer.\n *\n * @param hmap   hmap handle\n * @param prefix Prefix string (can be NULL)\n * @param key    Key string\n * @param value  Pointer to store array pointer (type determines which function is called)\n * @return       Array count on success, -1 on failure\n *\n * Example:\n *   uint32_t *ports;\n *   int count = hmap_get_array(hmap, \"config\", \"ports\", &ports);  // Calls _hmap_get_u32_array\n */\n#define hmap_get_array(hmap, prefix, key, value) \\\n    _Generic((value),                            \\\n        char ***: _hmap_get_string_array,        \\\n        uint64_t **: _hmap_get_u64_array,        \\\n        uint32_t **: _hmap_get_u32_array,        \\\n        uint16_t **: _hmap_get_u16_array,        \\\n        uint8_t **: _hmap_get_u8_array,          \\\n        int64_t **: _hmap_get_i64_array,         \\\n        int32_t **: _hmap_get_i32_array,         \\\n        int16_t **: _hmap_get_i16_array,         \\\n        int8_t **: _hmap_get_i8_array,           \\\n        double **: _hmap_get_double_array,       \\\n        void ***: _hmap_get_pointer_array)((hmap), (prefix), (key), (value))\n\n/**\n * @brief Type-safe generic setter macro for scalar values.\n *\n * This macro uses C11 _Generic to automatically dispatch to the correct\n * internal _hmap_add_*() function based on the type of the value.\n *\n * @param hmap   hmap handle\n * @param prefix Prefix string (can be NULL)\n * @param key    Key string\n * @param value  Value to store (type determines which function is called)\n * @return       0 on success, -1 on failure\n *\n * Example:\n *   hmap_add_value(hmap, \"config\", \"port\", 8080);           // Calls _hmap_add_u32\n *   hmap_add_value(hmap, \"app\", \"name\", \"MyApp\");           // Calls _hmap_add_string\n *   hmap_add_value(hmap, \"flags\", \"enabled\", 1);            // Calls _hmap_add_i32\n *   hmap_add_value(hmap, \"stats\", \"ratio\", 3.14);           // Calls _hmap_add_double\n */\n#define hmap_add_value(hmap, prefix, key, value) \\\n    _Generic((value),                            \\\n        char *: _hmap_add_string,                \\\n        const char *: _hmap_add_string,          \\\n        uint64_t: _hmap_add_u64,                 \\\n        uint32_t: _hmap_add_u32,                 \\\n        uint16_t: _hmap_add_u16,                 \\\n        uint8_t: _hmap_add_u8,                   \\\n        int64_t: _hmap_add_i64,                  \\\n        int32_t: _hmap_add_i32,                  \\\n        int16_t: _hmap_add_i16,                  \\\n        int8_t: _hmap_add_i8,                    \\\n        double: _hmap_add_double,                \\\n        float: _hmap_add_double,                 \\\n        void *: _hmap_add_pointer,               \\\n        default: _hmap_add_i32)((hmap), (prefix), (key), (value))\n\n/**\n * @brief Type-safe generic update macro for scalar values.\n *\n * This macro uses C11 _Generic to automatically dispatch to the correct\n * internal _hmap_update_*() function based on the type of the value.\n *\n * Semantics match hmap_update(): update if key exists, otherwise add.\n *\n * @param hmap   hmap handle\n * @param prefix Prefix string (can be NULL)\n * @param key    Key string\n * @param value  Value to store (type determines which function is called)\n * @return       0 on success, -1 on failure\n */\n#define hmap_update_value(hmap, prefix, key, value) \\\n    _Generic((value),                               \\\n        char *: _hmap_update_string,                \\\n        const char *: _hmap_update_string,          \\\n        uint64_t: _hmap_update_u64,                 \\\n        uint32_t: _hmap_update_u32,                 \\\n        uint16_t: _hmap_update_u16,                 \\\n        uint8_t: _hmap_update_u8,                   \\\n        int64_t: _hmap_update_i64,                  \\\n        int32_t: _hmap_update_i32,                  \\\n        int16_t: _hmap_update_i16,                  \\\n        int8_t: _hmap_update_i8,                    \\\n        double: _hmap_update_double,                \\\n        float: _hmap_update_double,                 \\\n        void *: _hmap_update_pointer,               \\\n        default: _hmap_update_i32)((hmap), (prefix), (key), (value))\n\n/**\n * @brief Type-safe generic setter macro for array values.\n *\n * This macro uses C11 _Generic to automatically dispatch to the correct\n * internal _hmap_add_*_array() function based on the type of the array pointer.\n *\n * @param hmap   hmap handle\n * @param prefix Prefix string (can be NULL)\n * @param key    Key string\n * @param array  Pointer to array data (type determines which function is called)\n * @param count  Number of elements in the array\n * @return       0 on success, -1 on failure\n *\n * Example:\n *   uint32_t ports[] = {8080, 8081, 8082};\n *   hmap_add_array(hmap, \"config\", \"ports\", ports, 3);  // Calls _hmap_add_u32_array\n *\n *   char *names[] = {\"alice\", \"bob\"};\n *   hmap_add_array(hmap, \"users\", \"names\", names, 2);   // Calls _hmap_add_string_array\n */\n#define hmap_add_array(hmap, prefix, key, array, count) \\\n    _Generic((array),                                   \\\n        char **: _hmap_add_string_array,                \\\n        uint64_t *: _hmap_add_u64_array,                \\\n        uint32_t *: _hmap_add_u32_array,                \\\n        uint16_t *: _hmap_add_u16_array,                \\\n        uint8_t *: _hmap_add_u8_array,                  \\\n        int64_t *: _hmap_add_i64_array,                 \\\n        int32_t *: _hmap_add_i32_array,                 \\\n        int16_t *: _hmap_add_i16_array,                 \\\n        int8_t *: _hmap_add_i8_array,                   \\\n        double *: _hmap_add_double_array,               \\\n        void **: _hmap_add_pointer_array)((hmap), (prefix), (key), (array), (count))\n\n/**\n * @brief Type-safe generic update macro for array values.\n *\n * This macro uses C11 _Generic to automatically dispatch to the correct\n * internal _hmap_update_*_array() function based on the type of the array pointer.\n *\n * Semantics match hmap_update(): update if key exists, otherwise add.\n *\n * @param hmap   hmap handle\n * @param prefix Prefix string (can be NULL)\n * @param key    Key string\n * @param array  Pointer to array data (type determines which function is called)\n * @param count  Number of elements in the array\n * @return       0 on success, -1 on failure\n */\n#define hmap_update_array(hmap, prefix, key, array, count) \\\n    _Generic((array),                                      \\\n        char **: _hmap_update_string_array,                \\\n        uint64_t *: _hmap_update_u64_array,                \\\n        uint32_t *: _hmap_update_u32_array,                \\\n        uint16_t *: _hmap_update_u16_array,                \\\n        uint8_t *: _hmap_update_u8_array,                  \\\n        int64_t *: _hmap_update_i64_array,                 \\\n        int32_t *: _hmap_update_i32_array,                 \\\n        int16_t *: _hmap_update_i16_array,                 \\\n        int8_t *: _hmap_update_i8_array,                   \\\n        double *: _hmap_update_double_array,               \\\n        void **: _hmap_update_pointer_array)((hmap), (prefix), (key), (array), (count))\n\n/**\n * @brief Get global allocation statistics\n *\n * Returns zeros if HMAP_TRACK_ALLOCS is not defined at compile time\n *\n * @param alloc_count\n *   Pointer to store total allocation count (can be NULL)\n * @param realloc_count\n *   Pointer to store reallocation count (can be NULL)\n * @param total_bytes\n *   Pointer to store total bytes allocated (can be NULL)\n */\nvoid hmap_get_global_alloc_stats(uint64_t *alloc_count, uint64_t *realloc_count,\n                                 uint64_t *total_bytes);\n\n/**\n * @brief Reset global allocation statistics\n *\n * No effect if HMAP_TRACK_ALLOCS is not defined at compile time\n */\nvoid hmap_reset_global_alloc_stats(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/hmap/hmap_helper.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright (c) 2022-2026 Intel Corporation\n */\n\n#pragma once\n\n/**\n * @file\n *\n * HMAP internal helper routines: mutex creation/destruction, the global\n * hmap registry, per-hmap locking, hash computation, key comparison, and\n * KVP memory management.\n */\n\n#include <errno.h>\n#include <pthread.h>\n#include <rte_log.h>\n#include \"hmap_log.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Global TAILQ registry of all created hmap instances. */\nstatic TAILQ_HEAD(hmap_hmap_list, hmap) hmap_registry;\n/** Mutex protecting @ref hmap_registry. */\nstatic pthread_mutex_t hmap_list_mutex;\n\n/**\n * Helper routine to create a mutex with a specific type.\n *\n * @param mutex\n *   The pointer to the mutex to create.\n * @param flags\n *   The attribute flags used to create the mutex i.e. recursive attribute\n * @return\n *   0 on success or -1 on failure errno is set\n */\nstatic inline int\nhmap_mutex_create(pthread_mutex_t *mutex, int flags)\n{\n    pthread_mutexattr_t attr;\n    int inited = 0, ret = EFAULT;\n\n    if (!mutex)\n        goto err;\n\n#define __do(_exp)    \\\n    do {              \\\n        ret = _exp;   \\\n        if (ret)      \\\n            goto err; \\\n    } while (0 /* CONSTCOND */)\n\n    __do(pthread_mutexattr_init(&attr));\n    inited = 1;\n\n    __do(pthread_mutexattr_settype(&attr, flags));\n\n    __do(pthread_mutex_init(mutex, &attr));\n\n    __do(pthread_mutexattr_destroy(&attr));\n\n#undef __do\n\n    return 0;\nerr:\n    if (inited) {\n        /* Do not lose the previous error value */\n        if (pthread_mutexattr_destroy(&attr))\n            HMAP_DEBUG(\"Failed to destroy mutex attribute, but is not the root cause\\n\");\n    }\n\n    errno = ret;\n    return -1;\n}\n\n/**\n * Destroy a mutex\n *\n * @param mutex\n *   Pointer to mutex to destroy.\n * @return\n *   0 on success and -1 on error with errno set.\n */\nstatic inline int\nhmap_mutex_destroy(pthread_mutex_t *mutex)\n{\n    int ret = 0;\n\n    if (mutex)\n        ret = pthread_mutex_destroy(mutex);\n\n    errno = ret;\n    return (ret != 0) ? -1 : 0;\n}\n\n/**\n * Acquire the global hmap registry mutex.\n *\n * Logs an error if the lock cannot be obtained.\n */\nstatic inline void\nhmap_list_lock(void)\n{\n    int ret = pthread_mutex_lock(&hmap_list_mutex);\n\n    if (ret)\n        HMAP_LOG(\"failed: %s\\n\", strerror(ret));\n}\n\n/**\n * Release the global hmap registry mutex.\n *\n * Logs a warning if the unlock fails.\n */\nstatic inline void\nhmap_list_unlock(void)\n{\n    int ret = pthread_mutex_unlock(&hmap_list_mutex);\n\n    if (ret)\n        HMAP_WARN(\"failed: %s\\n\", strerror(ret));\n}\n\n/**\n * Acquire the per-hmap mutex.\n *\n * @param hmap\n *   Pointer to the hmap whose mutex should be locked.\n */\nstatic inline void\nhmap_lock(hmap_t *hmap)\n{\n    int ret = pthread_mutex_lock(&hmap->mutex);\n\n    if (ret)\n        HMAP_WARN(\"failed: %s\\n\", strerror(ret));\n}\n\n/**\n * Release the per-hmap mutex.\n *\n * @param hmap\n *   Pointer to the hmap whose mutex should be unlocked.\n */\nstatic inline void\nhmap_unlock(hmap_t *hmap)\n{\n    int ret = pthread_mutex_unlock(&hmap->mutex);\n\n    if (ret)\n        HMAP_WARN(\"failed: %s\\n\", strerror(ret));\n}\n\n/**\n * Default hash function used when no custom hash is provided.\n *\n * Computes a Jenkins-style one-at-a-time hash over the concatenation of\n * @p prefix (if non-NULL) and @p key.\n *\n * @param prefix\n *   Optional namespace prefix string, or NULL.\n * @param key\n *   Key string to hash.\n * @return\n *   32-bit hash value.\n */\nstatic uint32_t\n_hmap_hash(const char *prefix, const char *key)\n{\n    const char *k;\n    uint32_t hash;\n\n    hash = 0;\n    if (prefix) {\n        k = prefix;\n        for (int i = 0; k[i]; i++) {\n            hash += k[i];\n            hash += (hash << 10);\n            hash ^= (hash >> 6);\n        }\n    }\n\n    k = key;\n    for (int i = 0; i < k[i]; i++) {\n        hash += k[i];\n        hash += (hash << 10);\n        hash ^= (hash >> 6);\n    }\n    hash += (hash << 3);\n    hash ^= (hash >> 11);\n    hash += (hash << 15);\n\n    return hash;\n}\n\n/**\n * Default key comparison function used when no custom comparator is provided.\n *\n * Returns non-zero if @p kvp matches the given prefix and key pair, zero\n * otherwise. Both prefix and kvp->prefix must be NULL or non-NULL together\n * for a match to occur.\n *\n * @param kvp\n *   Key-value pair to compare against.\n * @param prefix\n *   Optional namespace prefix, or NULL.\n * @param key\n *   Key string to match.\n * @return\n *   Non-zero on match, 0 on mismatch.\n */\nstatic inline int\n_hmap_cmp(const hmap_kvp_t *kvp, const char *prefix, const char *key)\n{\n    if (prefix && kvp->prefix)\n        return strcmp(prefix, kvp->prefix) ? 0 : strcmp(key, kvp->key) ? 0 : 1;\n    else if (!prefix && !kvp->prefix)\n        return strcmp(key, kvp->key) ? 0 : 1;\n    else\n        return 0;\n}\n\n/**\n * Free hmap-managed storage within a KVP and reset its fields.\n *\n * Releases the key and prefix strings allocated by hmap. Caller-managed\n * value storage (arrays, pointers) is NOT freed here. The hmap lock must\n * be held by the caller before invoking this function.\n *\n * @param kvp\n *   Pointer to the key-value pair to free. If NULL, the call is a no-op.\n */\nstatic void\n_hmap_free(hmap_kvp_t *kvp)\n{\n    if (!kvp)\n        return;\n\n    /* All values (strings, arrays, pointers) are caller-managed (not freed by hmap) */\n\n    /* Free hmap-managed key and prefix strings */\n    if (kvp->key) {\n        free(kvp->key);\n        kvp->key = NULL;\n    }\n    if (kvp->prefix) {\n        free(kvp->prefix);\n        kvp->prefix = NULL;\n    }\n\n    kvp->v.u64 = 0;\n    kvp->count = 0;\n    kvp->type  = HMAP_EMPTY_TYPE;\n}\n\n/**\n * Compute a bucket index for the given prefix/key pair using the hmap's hash function.\n *\n * @param hmap\n *   Pointer to the hmap.\n * @param prefix\n *   Optional namespace prefix, or NULL.\n * @param key\n *   Key string.\n * @return\n *   Bucket index in the range [0, hmap->capacity).\n */\nstatic inline uint32_t\nhmap_get_hash(hmap_t *hmap, const char *prefix, const char *key)\n{\n    return hmap->fns.hash_fn(prefix, key) % hmap->capacity;\n}\n\n/**\n * Compare a KVP against the given prefix/key pair using the hmap's comparator.\n *\n * @param hmap\n *   Pointer to the hmap.\n * @param kvp\n *   Key-value pair to compare.\n * @param prefix\n *   Optional namespace prefix, or NULL.\n * @param key\n *   Key string.\n * @return\n *   Non-zero on match, 0 on mismatch.\n */\nstatic inline int\nhmap_compare(hmap_t *hmap, const hmap_kvp_t *kvp, const char *prefix, const char *key)\n{\n    return hmap->fns.cmp_fn(kvp, prefix, key);\n}\n\n/**\n * Free a KVP using the hmap's registered free function.\n *\n * @param hmap\n *   Pointer to the hmap.\n * @param kvp\n *   Pointer to the key-value pair to free.\n */\nstatic inline void\nhmap_free_kvp(hmap_t *hmap, hmap_kvp_t *kvp)\n{\n    hmap->fns.free_fn(kvp);\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/hmap/hmap_log.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright (c) 2025-2026 Intel Corporation\n */\n\n#pragma once\n\n/**\n * @file\n *\n * HMAP Logs API\n *\n * This file provides a log API to HMAP applications.\n */\n\n#include <stdio.h>         // for NULL\n#include <stdarg.h>        // for va_list\n#include <stdint.h>        // for uint32_t\n#include <rte_common.h>\n#include <rte_log.h>\n#include <rte_branch_prediction.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Log an INFO-level message to the USER2 facility. */\n#define HMAP_LOG(args...) RTE_LOG(INFO, USER2, ##args)\n/** Log an ERR-level message to the USER2 facility. */\n#define HMAP_ERR(args...) RTE_LOG(ERR, USER2, ##args)\n/** Log a WARNING-level message to the USER2 facility. */\n#define HMAP_WARN(args...) RTE_LOG(WARNING, USER2, ##args)\n/** Log a DEBUG-level message to the USER2 facility. */\n#define HMAP_DEBUG(args...) RTE_LOG(DEBUG, USER2, ##args)\n\n/**\n * Log a message at a caller-specified level to the USER2 facility.\n *\n * @param l\n *   DPDK log level (e.g. INFO, ERR, DEBUG) passed to RTE_LOG.\n * @param args\n *   Format string followed by variable arguments, as in printf(3).\n */\n#define HMAP_PRINT(l, args...) RTE_LOG(l, USER2, ##args)\n\n/**\n * Generate an Error log message and return value\n *\n * Same as HMAP_LOG(ERR,...) define, but returns -1 to enable this style of coding.\n *   if (val == error) {\n *       HMAP_ERR(\"Error: Failed\\n\");\n *       return -1;\n *   }\n * Returning _val  to the calling function.\n */\n#define HMAP_ERR_RET_VAL(_val, ...)       \\\n    do {                                  \\\n        RTE_LOG(ERR, USER2, __VA_ARGS__); \\\n        return _val;                      \\\n    } while ((0))\n\n/**\n * Generate an Error log message and return\n *\n * Same as HMAP_LOG(ERR,...) define, but returns to enable this style of coding.\n *   if (val == error) {\n *       HMAP_ERR(\"Error: Failed\\n\");\n *       return;\n *   }\n * Returning to the calling function.\n */\n#define HMAP_RET(...) HMAP_ERR_RET_VAL(, __VA_ARGS__)\n\n/**\n * Generate an Error log message and return -1\n *\n * Same as HMAP_LOG(ERR,...) define, but returns -1 to enable this style of coding.\n *   if (val == error) {\n *       HMAP_ERR(\"Error: Failed\\n\");\n *       return -1;\n *   }\n * Returning a -1 to the calling function.\n */\n#define HMAP_ERR_RET(...) HMAP_ERR_RET_VAL(-1, __VA_ARGS__)\n\n/**\n * Generate an Error log message and return NULL\n *\n * Same as HMAP_LOG(ERR,...) define, but returns NULL to enable this style of coding.\n *   if (val == error) {\n *       HMAP_ERR(\"Error: Failed\\n\");\n *       return NULL;\n *   }\n * Returning a NULL to the calling function.\n */\n#define HMAP_NULL_RET(...) HMAP_ERR_RET_VAL(NULL, __VA_ARGS__)\n\n/**\n * Generate a Error log message and goto label\n *\n * Same as HMAP_LOG(ERR,...) define, but goes to a label to enable this style of coding.\n *   if (error condition) {\n *       HMAP_ERR(\"Error: Failed\\n\");\n *       goto lbl;\n *   }\n */\n#define HMAP_ERR_GOTO(lbl, ...)           \\\n    do {                                  \\\n        RTE_LOG(ERR, USER2, __VA_ARGS__); \\\n        goto lbl;                         \\\n    } while ((0))\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "lib/hmap/meson.build",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright (c) 2024-2026 Intel Corporation\n\nsources = files('hmap.c')\nheaders = files('hmap.h')\n\nlibhmap = library('hmap', sources, dependencies: dpdk)\nhmap = declare_dependency(link_with: libhmap, include_directories: include_directories('.'))\n"
  },
  {
    "path": "lib/lua/lua_config.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/* Create from lua.c 2018 by Keith Wiles @ intel.com */\n\n#ifdef LUA_ENABLED\n#include <sys/queue.h>\n#include <netinet/in.h>\n#include <net/if.h>\n#include <fcntl.h>\n#include <setjmp.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <errno.h>\n#include <getopt.h>\n#include <termios.h>\n#include <sys/ioctl.h>\n#include <libgen.h>\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <assert.h>\n\n#include <rte_version.h>\n#include <rte_eal.h>\n#include <rte_eal_memconfig.h>\n#include <rte_rwlock.h>\n\n#include <pg_compat.h>\n#include \"lua_config.h\"\n#include \"lua_stdio.h\"\n#include \"lua_utils.h\"\n#else\n#include \"lua_config.h\"\n#endif\n\n#ifdef LUA_ENABLED\nTAILQ_HEAD(rte_luaData_list, rte_tailq_entry);\n\nstatic struct rte_tailq_elem rte_luaData_tailq = {\n    .name = \"RTE_LUADATA\",\n};\n\nEAL_REGISTER_TAILQ(rte_luaData_tailq);\n\nstatic const char *progname = LUA_PROGNAME;\n\nstatic struct newlib_info {\n    newlib_t lib_func;\n    int order;\n} newlibs[MAX_NEW_LIBS];\n\nstatic int newlibs_idx = 0;\n\nconst char *\nlua_get_progname(void)\n{\n    return progname;\n}\n\nvoid\nlua_set_progname(const char *name)\n{\n    progname = name;\n}\n\nint\nlua_newlib_add(newlib_t n, int order)\n{\n    if (newlibs_idx >= MAX_NEW_LIBS)\n        return -1;\n\n    newlibs[newlibs_idx].order      = order;\n    newlibs[newlibs_idx++].lib_func = n;\n\n    return 0;\n}\n\nstatic int\ncmp_libs(const void *p1, const void *p2)\n{\n    const struct newlib_info *ni1 = p1;\n    const struct newlib_info *ni2 = p2;\n\n    return (ni1->order - ni2->order);\n}\n\nvoid\nlua_newlibs_init(luaData_t *ld)\n{\n    struct newlib_info _libs[MAX_NEW_LIBS];\n    int i;\n\n    memcpy(_libs, newlibs, sizeof(newlibs));\n\n    qsort(_libs, newlibs_idx, sizeof(struct newlib_info), cmp_libs);\n\n    for (i = 0; i < newlibs_idx; i++)\n        _libs[i].lib_func(ld->L);\n}\n\nstatic int\nhandle_luainit(luaData_t *ld)\n{\n    const char *name;\n    const char *init;\n\n    name = \"=\" LUA_INITVERSION;\n    init = getenv(&name[1]);\n\n    if (!init) {\n        name = \"=\" LUA_INIT;\n        init = getenv(&name[1]); /* try alternative name */\n    }\n\n    if (!init)\n        return LUA_OK;\n\n    if (init[0] == '@')\n        return lua_dofile(ld, init + 1);\n    else\n        return lua_dostring(ld, init, name);\n}\n\nluaData_t *\nlua_create_instance(void)\n{\n    luaData_t *ld;\n    struct rte_luaData_list *luaData_list = NULL;\n    struct rte_tailq_entry *te;\n\n    ld = (luaData_t *)calloc(1, sizeof(luaData_t));\n    if (!ld)\n        return NULL;\n\n    ld->client_socket = -1;\n    ld->server_socket = -1;\n\n    ld->buffer = calloc(1, LUA_BUFFER_SIZE);\n    if (!ld->buffer) {\n        free(ld);\n        return NULL;\n    }\n\n    ld->L = luaL_newstate();\n    if (!ld->L) {\n        free(ld);\n        return NULL;\n    }\n\n    ld->in  = stdin;\n    ld->out = stdout;\n    ld->err = stderr;\n\n    if (handle_luainit(ld)) {\n        free(ld);\n        DBG(\"handle_luainit() failed\\n\");\n        return NULL;\n    }\n\n    luaL_openlibs(ld->L);\n    lua_newlibs_init(ld);\n\n    luaData_list = RTE_TAILQ_CAST(rte_luaData_tailq.head, rte_luaData_list);\n\n    /* try to allocate tailq entry */\n    te = calloc(1, sizeof(*te));\n    if (te == NULL) {\n        DBG(\"Cannot allocate tailq entry!\\n\");\n        lua_close(ld->L);\n        free(ld);\n        return NULL;\n    }\n    te->data = ld;\n\n    rte_mcfg_tailq_read_lock();\n    TAILQ_INSERT_TAIL(luaData_list, te, next);\n\n    rte_mcfg_tailq_read_unlock();\n\n    /* Make sure we display the copyright string for Lua. */\n    printf(\"%s\\n\", LUA_COPYRIGHT);\n\n    return ld;\n}\n\nvoid\nlua_destroy_instance(luaData_t *ld)\n{\n    struct rte_luaData_list *luaData_list = NULL;\n    struct rte_tailq_entry *te;\n\n    if (!ld)\n        return;\n\n    luaData_list = RTE_TAILQ_CAST(rte_luaData_tailq.head, rte_luaData_list);\n\n    rte_mcfg_tailq_write_lock();\n\n    TAILQ_FOREACH (te, luaData_list, next) {\n        if (te->data == (void *)ld)\n            break;\n    }\n    if (te) {\n        TAILQ_REMOVE(luaData_list, te, next);\n        free(te);\n    }\n\n    rte_mcfg_tailq_write_unlock();\n\n    free(ld);\n}\n\nluaData_t *\nlua_find_luaData(lua_State *L)\n{\n    struct rte_luaData_list *luaData_list = NULL;\n    struct rte_tailq_entry *te;\n    luaData_t *ret_ld = NULL;\n\n    if (!L)\n        return NULL;\n\n    luaData_list = RTE_TAILQ_CAST(rte_luaData_tailq.head, rte_luaData_list);\n\n    rte_mcfg_tailq_write_lock();\n\n    TAILQ_FOREACH (te, luaData_list, next) {\n        luaData_t *ld = (luaData_t *)te->data;\n        if (ld->L == L) {\n            ret_ld = ld;\n            break;\n        }\n    }\n\n    rte_mcfg_tailq_write_unlock();\n\n    return ret_ld;\n}\n\n/*\n** Message handler used to run all chunks\n*/\nstatic int\nmsghandler(lua_State *L)\n{\n    const char *msg = lua_tostring(L, 1);\n\n    if (msg == NULL) {                           /* is error object not a string? */\n        if (luaL_callmeta(L, 1, \"__tostring\") && /* does it have a metamethod */\n            lua_type(L, -1) == LUA_TSTRING) {    /* that produces a string? */\n            DBG(\"No metadata to produce a string on object\\n\");\n            return 1; /* that is the message */\n        } else {\n            msg = lua_pushfstring(L, \"(error object is a %s value)\", luaL_typename(L, 1));\n        }\n    }\n    luaL_traceback(L, L, msg, 1); /* append a standard traceback */\n    return 1;                     /* return the traceback */\n}\n\nstatic int\n_k(lua_State *L, int status, lua_KContext ctx)\n{\n    (void)L;\n    (void)ctx;\n\n    return status;\n}\n\nint\nlua_docall(lua_State *L, int narg, int nres)\n{\n    int status;\n    int base = 0;\n\n    base = lua_gettop(L);\n\n    lua_pushcfunction(L, msghandler);\n    lua_insert(L, base);\n\n    status = _k(L, lua_pcallk(L, narg, nres, base, 0, _k), 0);\n\n    return status;\n}\n\nint\nlua_dofile(luaData_t *ld, const char *name)\n{\n    int status;\n\n    status = luaL_loadfile(ld->L, name);\n\n    if (status == LUA_OK)\n        status = lua_docall(ld->L, 0, LUA_MULTRET);\n    else\n        printf(\"lua_docall(%s) failed\\n\", name);\n\n    return report(ld->L, status);\n}\n\nint\nlua_dostring(luaData_t *ld, const char *s, const char *name)\n{\n    int status;\n\n    status = luaL_loadbuffer(ld->L, s, strlen(s), name);\n\n    if (status == LUA_OK)\n        status = lua_docall(ld->L, 0, 0);\n\n    return report(ld->L, status);\n}\n\nint\nlua_dolibrary(lua_State *L, const char *name)\n{\n    int status;\n\n    lua_getglobal(L, \"require\");\n    lua_pushstring(L, name);\n\n    status = lua_docall(L, 1, 1); /* call 'require(name)' */\n    if (status == LUA_OK)\n        lua_setglobal(L, name); /* global[name] = require return */\n\n    return report(L, status);\n}\n\nint\nlua_execute_string(luaData_t *ld, char *buffer)\n{\n    lua_State *L = ld->L;\n\n    if (!L)\n        return -1;\n\n    buffer = lua_strtrim(buffer);\n    if (!buffer)\n        return -1;\n\n    if (luaL_dostring(L, buffer) != 0) {\n        DBG(\"%s\\n\", lua_tostring(L, -1));\n        return -1;\n    }\n\n    return 0;\n}\n\nvoid\nlua_execute_close(luaData_t *ld)\n{\n    if (ld->L)\n        lua_close(ld->L);\n}\n#else\nvoid\nlua_execute_close(void *ld)\n{\n    (void)ld;\n}\n#endif\n"
  },
  {
    "path": "lib/lua/lua_config.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#ifndef _LUA_CONFIG_H_\n#define _LUA_CONFIG_H_\n\n/**\n * @file\n *\n * Core Lua instance management for Pktgen.\n *\n * Provides functions to create, configure, and destroy Lua interpreter\n * instances (luaData_t), load and execute Lua scripts/strings, register\n * new native libraries, and manage Lua's standard I/O streams.  All\n * declarations are conditionally compiled under LUA_ENABLED.\n */\n\n#ifdef LUA_ENABLED\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <signal.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <string.h>\n#include <pthread.h>\n#include <sys/queue.h>\n\n#include <rte_tailq.h>\n\n#define lua_c\n\n#include <lua.h>\n#define LUA_COMPAT_APIINTCASTS\n#include <lauxlib.h>\n#include <lualib.h>\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifdef LUA_ENABLED\n\n#if !defined(LUA_PROGNAME)\n#define LUA_PROGNAME \"lua-shell\"\n#endif\n\n#if !defined(LUA_INIT)\n#define LUA_INIT \"LUA_INIT\"\n#endif\n\n#define LUA_INITVERSION LUA_INIT \"_\" LUA_VERSION_MAJOR \"_\" LUA_VERSION_MINOR\n\n#define DBG(...)                           \\\n    do {                                   \\\n        fprintf(stderr, \"%s: \", __func__); \\\n        fprintf(stderr, __VA_ARGS__);      \\\n    } while (0)\n\n#define MAX_NEW_LIBS    16\n#define LUA_BUFFER_SIZE 8192\n#define MAX_NEW_LIBS    16\n#define LUA_EOF         -1\n\n#define IO_PREFIX  \"_IO_\"\n#define IOPREF_LEN (sizeof(IO_PREFIX) / sizeof(char) - 1)\n#define IO_INPUT   (IO_PREFIX \"input\")\n#define IO_OUTPUT  (IO_PREFIX \"output\")\n\ntypedef struct luaData {\n    TAILQ_ENTRY(luaData) node;\n    lua_State *L;          /**< Lua State pointer */\n    int32_t server_socket; /**< Server socket descriptor */\n    int32_t client_socket; /**< Client socket descriptor */\n    int32_t socket_port;   /**< Port address for socket */\n    char *buffer;          /**< Buffer for reading Lua code */\n    void *out, *in, *err;  /**< stdout, stdin, stderr */\n    char *hostname;        /**< Name of host for socket */\n} luaData_t;\n\n/** Callback type for registering a new native Lua library. */\ntypedef void (*newlib_t)(lua_State *L);\n\n/**\n * Allocate and initialise a new Lua interpreter instance.\n *\n * @return   Pointer to the new luaData_t, or NULL on failure.\n */\nluaData_t *lua_create_instance(void);\n\n/**\n * Register a native library constructor to be called during Lua initialisation.\n *\n * Libraries are called in ascending @p order when lua_newlibs_init() runs.\n *\n * @param n      Constructor function to register.\n * @param order  Relative initialisation order (lower values run first).\n * @return       0 on success, -1 if the registry is full.\n */\nint lua_newlib_add(newlib_t n, int order);\n\n/**\n * Call all registered library constructors for instance @p ld.\n *\n * Should be called once after lua_create_instance() to make all\n * registered native libraries available within the Lua state.\n *\n * @param ld   Lua instance to initialise libraries into.\n */\nvoid lua_newlibs_init(luaData_t *ld);\n\n/**\n * Call a Lua function that is already on the stack in protected mode.\n *\n * @param L     Lua state.\n * @param narg  Number of arguments on the stack.\n * @param nres  Expected number of return values.\n * @return      LUA_OK on success, or a Lua error code.\n */\nint lua_docall(lua_State *L, int narg, int nres);\n\n/**\n * Load and execute a Lua file.\n *\n * @param ld    Lua instance to use.\n * @param name  Path to the Lua source file.\n * @return      LUA_OK on success, or a Lua error code.\n */\nint lua_dofile(luaData_t *ld, const char *name);\n\n/**\n * Execute a Lua string within instance @p ld.\n *\n * @param ld    Lua instance to use.\n * @param s     Lua source code string.\n * @param name  Chunk name used in error messages (e.g. the script name).\n * @return      LUA_OK on success, or a Lua error code.\n */\nint lua_dostring(luaData_t *ld, const char *s, const char *name);\n\n/**\n * Dynamically load a Lua C library by name and open it.\n *\n * @param L     Lua state.\n * @param name  Name of the C library to load (passed to package.loadlib).\n * @return      LUA_OK on success, or a Lua error code.\n */\nint lua_dolibrary(lua_State *L, const char *name);\n\n/**\n * Execute a NUL-terminated Lua string and handle errors.\n *\n * Convenience wrapper around lua_dostring() for interactive use.\n *\n * @param ld    Lua instance to use.\n * @param str   Lua source code string.\n * @return      0 on success, non-zero on error.\n */\nint lua_execute_string(luaData_t *ld, char *str);\n\n/**\n * Close the client socket associated with instance @p ld.\n *\n * @param ld   Lua instance whose socket should be closed.\n */\nvoid lua_execute_close(luaData_t *ld);\n\n/**\n * Create and register a Lua stdio file object backed by FILE @p f.\n *\n * @param ld     Lua instance.\n * @param f      Underlying C FILE to wrap.\n * @param k      Lua global key name (e.g. \"stdout\").\n * @param fname  Lua filename string for the file object.\n */\nvoid lua_create_stdfile(luaData_t *ld, FILE *f, const char *k, const char *fname);\n\n/**\n * Redirect Lua's standard streams to the socket streams in @p ld.\n *\n * @param ld   Lua instance to configure.\n */\nvoid lua_set_stdfiles(luaData_t *ld);\n\n/**\n * Restore Lua's standard streams to the process standard streams.\n *\n * @param ld   Lua instance to reset.\n */\nvoid lua_reset_stdfiles(luaData_t *ld);\n\n/**\n * Return the current Lua programme name string.\n *\n * @return   Programme name as a NUL-terminated string.\n */\nconst char *lua_get_progname(void);\n\n/**\n * Set the Lua programme name shown in error messages.\n *\n * @param name   New programme name string (must remain valid for the lifetime\n *               of the Lua instance).\n */\nvoid lua_set_progname(const char *name);\n\n/**\n * Destroy a Lua instance and free all associated resources.\n *\n * @param ld   Lua instance to destroy.\n */\nvoid lua_destroy_instance(luaData_t *ld);\n\n/**\n * Find the luaData_t that owns Lua state @p L.\n *\n * Searches the global registry of all created Lua instances.\n *\n * @param L   Lua state to look up.\n * @return    Pointer to the owning luaData_t, or NULL if not found.\n */\nluaData_t *lua_find_luaData(lua_State *L);\n#else\n/** Close the Lua instance socket (stub when LUA_ENABLED is not defined). */\nvoid lua_execute_close(void *ld);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _LUA_CONFIG_H_ */\n"
  },
  {
    "path": "lib/lua/lua_dapi.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#define lua_dpdk_c\n#define LUA_LIB\n#define lua_c\n\n#ifdef RTE_LIBRTE_API\n#include <rte_ethdev.h>\n#include <rte_mbuf.h>\n#include <rte_cycles.h>\n#include <vec.h>\n#include <rte_timer.h>\n#include <pg_strings.h>\n#include <rte_version.h>\n\n#include <dapi.h>\n\n#include \"lua_config.h\"\n#include \"lua_stdio.h\"\n#include \"lua_dpdk.h\"\n#include \"lua_dapi.h\"\n#include \"lua_utils.h\"\n\n#ifndef __INTEL_COMPILER\n#pragma GCC diagnostic ignored \"-Wcast-qual\"\n#endif\n\nstatic const char *Dapi = \"Dapi\";\n\nstatic int\n_create(lua_State *L)\n{\n    dapi_t **dapi;\n    struct dapi *d;\n    const char *name;\n\n    validate_arg_count(L, 1);\n\n    name = luaL_checkstring(L, 1);\n    if (!name)\n        luaL_error(L, \"Name is empty\");\n\n    dapi = (struct dapi **)lua_newuserdata(L, sizeof(void *));\n\n    d = dapi_create((char *)(uintptr_t)name, 0, 0);\n    if (!d)\n        return luaL_error(L, \"create: dapi_create() failed\");\n    *dapi = d;\n\n    luaL_getmetatable(L, Dapi);\n    lua_setmetatable(L, -2);\n\n    return 1;\n}\n\nstatic int\n_destroy(lua_State *L)\n{\n    dapi_t **dapi;\n\n    validate_arg_count(L, 1);\n\n    dapi = (dapi_t **)luaL_checkudata(L, 1, Dapi);\n\n    dapi_destroy(*dapi);\n\n    return 0;\n}\n\nstatic int\n_get(lua_State *L)\n{\n    validate_arg_count(L, 2);\n\n    return 1;\n}\n\nstatic int\n_put(lua_State *L)\n{\n    validate_arg_count(L, 2);\n\n    return 0;\n}\n\nstatic int\n_tostring(lua_State *L)\n{\n    dapi_t **dapi;\n    struct dapi *d;\n    char buff[64];\n\n    dapi = (dapi_t **)luaL_checkudata(L, 1, Dapi);\n    if (!dapi || !*dapi)\n        return luaL_error(L, \"tostring, dapi is nil\");\n    d = *dapi;\n\n    lua_getmetatable(L, 1);\n    lua_getfield(L, -1, \"__name\");\n\n    snprintf(buff, sizeof(buff), \"%s<%s>\", lua_tostring(L, -1), d->name);\n\n    lua_pop(L, 3);\n\n    lua_pushstring(L, buff);\n    return 1;\n}\n\nstatic int\n_gc(lua_State *L __rte_unused)\n{\n    return 0;\n}\n\nstatic const struct luaL_Reg _methods[] = {{\"get\", _get}, {\"put\", _put}, {NULL, NULL}};\n\nstatic const struct luaL_Reg _functions[] = {\n    {\"create\", _create}, {\"destroy\", _destroy}, {NULL, NULL}};\n\nint\nluaopen_dapi(lua_State *L)\n{\n    luaL_newmetatable(L, Dapi);        // create and push new table called Vec\n    lua_pushvalue(L, -1);              // dup the table on the stack\n\n    lua_setfield(L, -2, \"__index\");        //\n\n    luaL_setfuncs(L, _methods, 0);\n\n    lua_pushstring(L, \"__tostring\");\n    lua_pushcfunction(L, _tostring);\n    lua_settable(L, -3);\n\n    lua_pushstring(L, \"__gc\");\n    lua_pushcfunction(L, _gc);\n    lua_settable(L, -3);\n    lua_pop(L, 1);\n\n    lua_getglobal(L, LUA_DPDK_LIBNAME);\n    lua_newtable(L);\n    luaL_setfuncs(L, _functions, 0);\n\n    lua_setfield(L, -2, LUA_DAPI_LIBNAME);\n\n    return 1;\n}\n#endif\n"
  },
  {
    "path": "lib/lua/lua_dapi.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#ifndef _RTE_LUA_DAPI_H_\n#define _RTE_LUA_DAPI_H_\n\n/**\n * @file\n *\n * Lua bindings for the DAPI (Data-plane API) library.\n *\n * Exposes DAPI functionality to Lua scripts under the \"dapi\" library name.\n * Only available when RTE_LIBRTE_DAPI is enabled at build time.\n */\n\n#include <rte_log.h>\n\n#define lua_c\n#include <lua.h>\n#include <lauxlib.h>\n\n#ifdef RTE_LIBRTE_DAPI\n#include <dapi.h>\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct dapi dapi_t;\n\n#define LUA_DAPI_LIBNAME \"dapi\"\n\n/**\n * Open the DAPI Lua library and register its functions.\n *\n * Called automatically by the Lua runtime when the library is required.\n *\n * @param L\n *   Lua state to register the library into.\n * @return\n *   Number of values pushed onto the Lua stack (1 — the library table).\n */\nint luaopen_dapi(lua_State *L);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _RTE_LUA_DAPI_H_ */\n"
  },
  {
    "path": "lib/lua/lua_dpdk.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#define lua_dpdk_c\n#define LUA_LIB\n#define lua_c\n\n#include <rte_common.h>\n#include <rte_ethdev.h>\n#include <rte_mbuf.h>\n#include <rte_cycles.h>\n#include <vec.h>\n#include <rte_timer.h>\n#include <rte_version.h>\n\n#include <pg_delay.h>\n#include <pg_strings.h>\n#include <portlist.h>\n\n#include \"lua_config.h\"\n#include \"lua_stdio.h\"\n#include \"lua_dpdk.h\"\n#include \"lua_pktmbuf.h\"\n#ifdef RTE_LIBRTE_DAPI\n#include <lua_dapi.h>\n#else\nint luaopen_dapi(lua_State *L);\n#endif\n#include \"lua_vec.h\"\n#include \"lua_utils.h\"\n\n#ifndef __INTEL_COMPILER\n#pragma GCC diagnostic ignored \"-Wcast-qual\"\n#endif\n\nstatic int lua_logtype;\n\nstatic __inline__ void\n__delay(int32_t t)\n{\n    int32_t n;\n\n    while (t > 0) {\n        n = (t > 10) ? 10 : t;\n        rte_delay_us_sleep(n * 1000);\n        t -= n;\n    }\n}\n\nstatic int\ndpdk_delay(lua_State *L)\n{\n    validate_arg_count(L, 1);\n\n    __delay(luaL_checkinteger(L, 1));\n\n    return 0;\n}\n\nstatic int\ndpdk_pause(lua_State *L)\n{\n    char *str;\n    int v;\n\n    validate_arg_count(L, 2);\n\n    str = (char *)luaL_checkstring(L, 1);\n    if (strlen(str) > 0)\n        lua_putstring(str);\n\n    v = luaL_checkinteger(L, 2);\n    __delay(v);\n\n    return 0;\n}\n\nstatic int\ndpdk_continue(lua_State *L)\n{\n    char buf[4], *str;\n    int n;\n\n    validate_arg_count(L, 1);\n\n    str = (char *)luaL_checkstring(L, 1);\n\n    if (strlen(str) > 0)\n        lua_putstring(str);\n\n    buf[0] = '\\0';\n    n      = fread(buf, 1, 1, (FILE *)lua_get_stdin(lua_find_luaData(L)));\n    if (n > 0)\n        buf[n] = '\\0';\n\n    lua_pushstring(L, buf);\n    return 1;\n}\n\nstatic int\ndpdk_input(lua_State *L)\n{\n    char buf[256], c, *str;\n    uint32_t n, idx;\n\n    validate_arg_count(L, 1);\n\n    str = (char *)luaL_checkstring(L, 1);\n\n    if (strlen(str) > 0)\n        lua_putstring(str);\n\n    idx      = 0;\n    buf[idx] = '\\0';\n    while (idx < (sizeof(buf) - 2)) {\n        n = fread(&c, 1, 1, (FILE *)lua_get_stdin(lua_find_luaData(L)));\n        if ((n <= 0) || (c == '\\r') || (c == '\\n'))\n            break;\n        buf[idx++] = c;\n    }\n    buf[idx] = '\\0';\n\n    lua_pushstring(L, buf);\n    return 1;\n}\n\nstatic int\ndpdk_sleep(lua_State *L)\n{\n    validate_arg_count(L, 1);\n\n    rte_delay_us_sleep((luaL_checkinteger(L, 1) * 1000) * 1000);\n    return 0;\n}\n\nstatic void\nlink_state(lua_State *L, uint16_t pid)\n{\n    struct rte_eth_link link;\n    char buff[32];\n\n    lua_pushinteger(L, pid); /* Push the table index */\n    if (rte_eth_link_get_nowait(pid, &link) < 0) {\n        fprintf(stderr, \"Port %u: Failed to get link status\\n\", pid);\n        return;\n    }\n\n    if (link.link_status)\n        snprintf(buff, sizeof(buff), \"<UP-%u-%s>\", (uint32_t)link.link_speed,\n                 (link.link_duplex == RTE_ETH_LINK_FULL_DUPLEX) ? (\"FD\") : (\"HD\"));\n    else\n        snprintf(buff, sizeof(buff), \"<--Down-->\");\n    lua_pushstring(L, buff);\n\n    /* Now set the table as an array with pid as the index. */\n    lua_rawset(L, -3);\n}\n\nstatic int\ndpdk_linkState(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t n;\n\n    validate_arg_count(L, 1);\n\n    portlist = 0;\n    portlist_parse(luaL_checkstring(L, 1), RTE_MAX_ETHPORTS, &portlist);\n\n    lua_newtable(L);\n\n    n = 0;\n    foreach_port(portlist, _do(link_state(L, pid); n++));\n\n    setf_integer(L, \"n\", n);\n\n    return 1;\n}\n\nstatic void\nport_stats(lua_State *L, uint16_t pid)\n{\n    struct rte_eth_stats stats;\n\n    rte_eth_stats_get(pid, &stats);\n\n    lua_pushinteger(L, pid); /* Push the table index */\n    lua_newtable(L);         /* Create the structure table for a packet */\n\n    setf_integer(L, \"ipackets\", stats.ipackets);\n    setf_integer(L, \"opackets\", stats.opackets);\n    setf_integer(L, \"ibytes\", stats.ibytes);\n    setf_integer(L, \"obytes\", stats.obytes);\n    setf_integer(L, \"ierrors\", stats.ierrors);\n    setf_integer(L, \"oerrors\", stats.oerrors);\n    setf_integer(L, \"rx_nombuf\", stats.rx_nombuf);\n\n    /* Now set the table as an array with pid as the index. */\n    lua_rawset(L, -3);\n}\n\nstatic int\ndpdk_portStats(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t n;\n\n    validate_arg_count(L, 1);\n\n    portlist_parse(luaL_checkstring(L, 1), RTE_MAX_ETHPORTS, &portlist);\n\n    lua_newtable(L);\n\n    n = 0;\n    foreach_port(portlist, _do(port_stats(L, pid); n++));\n\n    setf_integer(L, \"n\", n);\n\n    return 1;\n}\n\nstatic int\ndpdk_compile(lua_State *L)\n{\n    validate_arg_count(L, 3);\n\n    return 1;\n}\n\nstatic void\ndecompile_pkt(lua_State *L, uint16_t pid)\n{\n    char buff[256];\n    struct pkt_data *p = NULL;\n\n    lua_pushinteger(L, pid); /* Push the table index */\n    lua_newtable(L);         /* Create the structure table for a packet */\n\n    /* Add each member to the packet table indexed with port id. */\n    setf_string(L, \"eth_dst_addr\", inet_mtoa(buff, sizeof(buff), &p->eth_dst_addr));\n    setf_string(L, \"eth_src_addr\", inet_mtoa(buff, sizeof(buff), &p->eth_src_addr));\n    if (p->ethType == RTE_ETHER_TYPE_IPV4) {\n        setf_string(L, \"ip_dst_addr\",\n                    inet_ntop4(buff, sizeof(buff), htonl(p->ip_dst_addr), 0xFFFFFFFF));\n        setf_string(L, \"ip_src_addr\",\n                    inet_ntop4(buff, sizeof(buff), htonl(p->ip_src_addr), p->ip_mask));\n    }\n    setf_integer(L, \"dport\", p->dport);\n    setf_integer(L, \"sport\", p->sport);\n    setf_integer(L, \"vlanid\", p->vlanid);\n    setf_string(L, \"ethType\",\n                (char *)((p->ethType == RTE_ETHER_TYPE_IPV4)   ? \"ipv4\"\n                         : (p->ethType == RTE_ETHER_TYPE_IPV6) ? \"ipv6\"\n                         : (p->ethType == RTE_ETHER_TYPE_VLAN) ? \"vlan\"\n                                                               : \"unknown\"));\n    setf_string(L, \"ipProto\",\n                (char *)((p->ipProto == IPPROTO_TCP)    ? \"tcp\"\n                         : (p->ipProto == IPPROTO_ICMP) ? \"icmp\"\n                                                        : \"udp\"));\n\n    setf_integer(L, \"pktSize\", p->pktSize + RTE_ETHER_CRC_LEN);\n    /* Now set the table as an array with pid as the index. */\n    lua_rawset(L, -3);\n}\n\nstatic int\ndpdk_decompile(lua_State *L)\n{\n    portlist_t portlist;\n    uint32_t n;\n\n    validate_arg_count(L, 2);\n\n    portlist_parse(luaL_checkstring(L, 2), RTE_MAX_ETHPORTS, &portlist);\n\n    lua_newtable(L);\n\n    n = 0;\n    foreach_port(portlist, _do(decompile_pkt(L, pid); n++));\n\n    setf_integer(L, \"n\", n);\n\n    return 1;\n}\n\nstatic int\ndpdk_tx_burst(lua_State *L)\n{\n    uint16_t pid, qid;\n    uint32_t nb_pkts;\n    int rc;\n    struct rte_mbuf **mbs;\n\n    validate_arg_count(L, 4);\n\n    pid     = luaL_checkinteger(L, 1);\n    qid     = luaL_checkinteger(L, 1);\n    mbs     = (struct rte_mbuf **)luaL_checkudata(L, 3, \"mbufs\");\n    nb_pkts = luaL_checkinteger(L, 4);\n\n    rc = rte_eth_tx_burst(pid, qid, mbs, nb_pkts);\n\n    lua_pushinteger(L, rc);\n\n    return 1;\n}\n\nstatic int\ndpdk_portCount(lua_State *L)\n{\n    validate_arg_count(L, 0);\n\n    lua_pushinteger(L, rte_eth_dev_count_avail());\n\n    return 1;\n}\n\nstatic int\ndpdk_totalPorts(lua_State *L)\n{\n    validate_arg_count(L, 0);\n\n    lua_pushinteger(L, rte_eth_dev_count_total());\n\n    return 1;\n}\n\nstatic int\ndpdk_rx_burst(lua_State *L)\n{\n    uint16_t pid, qid;\n    struct rte_mbuf **mbs;\n    int rc, nb_pkts;\n\n    validate_arg_count(L, 4);\n\n    pid     = luaL_checkinteger(L, 1);\n    qid     = luaL_checkinteger(L, 1);\n    mbs     = (struct rte_mbuf **)luaL_checkudata(L, 3, \"mbufs\");\n    nb_pkts = luaL_checkinteger(L, 4);\n\n    rc = rte_eth_tx_burst(pid, qid, mbs, nb_pkts);\n\n    lua_pushinteger(L, rc);\n\n    return 1;\n}\n\nstatic int\ndpdk_version(lua_State *L)\n{\n    validate_arg_count(L, 0);\n\n    lua_pushstring(L, rte_version());\n\n    return 1;\n}\n\nstatic const luaL_Reg dpdklib[] = {\n    {\"delay\", dpdk_delay}, /* Delay a given number of milliseconds */\n    {\"pause\", dpdk_pause}, /* Delay for a given number of milliseconds and display message */\n    {\"continue\", dpdk_continue},\n    {\"sleep\", dpdk_sleep}, /* Delay a given number of seconds */\n\n    {\"portCount\", dpdk_portCount},   /* Used port count value */\n    {\"totalPorts\", dpdk_totalPorts}, /* Total ports seen by DPDK */\n    {\"port_stats\", dpdk_portStats},\n    {\"input\", dpdk_input},\n\n    {\"linkState\", dpdk_linkState}, /* Return the current link state of a port */\n\n    {\"compile\", dpdk_compile}, /* Convert a structure into a frame to be sent */\n    {\"decompile\",\n     dpdk_decompile}, /* decompile a frame into Ethernet, IP, TCP, UDP or other protocols */\n    {\"tx_burst\", dpdk_tx_burst},\n    {\"rx_bust\", dpdk_rx_burst},\n    {\"version\", dpdk_version},\n\n    {NULL, NULL}};\n\nstatic const char *rte_copyright = \"Copyright(c) <2010-2026>, Intel Corporation\";\n\nstatic int\nluaopen_dpdk(lua_State *L)\n{\n    luaL_newlib(L, dpdklib);\n\n    lua_pushstring(L, \"dinfo\"); /* Push the table index name */\n    lua_newtable(L);            /* Create the structure table for information */\n\n    setf_string(L, \"Lua_Version\", (char *)LUA_VERSION);\n    setf_string(L, \"Lua_Release\", (char *)LUA_RELEASE);\n    setf_string(L, \"Lua_Copyright\", (char *)LUA_COPYRIGHT);\n    setf_string(L, \"Lua_Authors\", (char *)LUA_AUTHORS);\n\n    setf_string(L, \"DAPI_Authors\", (char *)\"Keith Wiles @ Intel Corp\");\n    setf_string(L, \"DPDK_Version\", (char *)rte_version());\n    setf_string(L, \"DPDK_Copyright\", (char *)rte_copyright);\n\n    /* Now set the table for the info values. */\n    lua_rawset(L, -3);\n\n    return 1;\n}\n\n__attribute__((__weak__)) int\nluaopen_dapi(lua_State *L __rte_unused)\n{\n    return 0;\n}\n\nstatic void\ndpdk_lua_openlib(lua_State *L)\n{\n    lua_gc(L, LUA_GCSTOP, 0);\n\n    luaL_requiref(L, LUA_DPDK_LIBNAME, luaopen_dpdk, 1);\n    lua_pop(L, 1);\n\n    if (luaopen_pktmbuf(L))\n        lua_pop(L, 1);\n\n    if (luaopen_vec(L))\n        lua_pop(L, 1);\n\n    if (luaopen_dapi(L))\n        lua_pop(L, 1);\n\n    lua_gc(L, LUA_GCRESTART, 0);\n}\n\nRTE_INIT(dpdk_lua_init)\n{\n    lua_logtype = rte_log_register(\"librte.lua\");\n    if (lua_logtype >= 0)\n        rte_log_set_level(lua_logtype, RTE_LOG_INFO);\n\n    lua_newlib_add(dpdk_lua_openlib, 10);\n}\n"
  },
  {
    "path": "lib/lua/lua_dpdk.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#ifndef _RTE_LUA_DPDK_H_\n#define _RTE_LUA_DPDK_H_\n\n/**\n * @file\n *\n * DPDK Lua helper types, macros, and inline field accessor functions.\n *\n * Defines common structures (pkt_data), iteration macros (foreach_port),\n * argument validation helpers (validate_arg_count), and a set of\n * setf_* and getf_* inline functions for pushing and pulling values between\n * C and Lua table fields.\n */\n\n#include <stdint.h>\n#include <netinet/in.h>\n\n#include <rte_log.h>\n\n#define lua_c\n#include <lua.h>\n#include <lauxlib.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define LUA_DPDK_LIBNAME \"dpdk\"\n\n/** Emit a log message at @p level using the Lua log type. */\n#define LUA_LOG(level, fmt, args...) \\\n    rte_log(RTE_LOG_##level, lua_logtype, \"%s(): \" fmt \"\\n\", __func__, ##args)\n\n/** Execute @p _exp inside a do/while(0) block (suppresses compiler warnings). */\n#define _do(_exp) \\\n    do {          \\\n        _exp;     \\\n    } while ((0))\n\n/**\n * Iterate over all ports set in @p _portlist and execute @p _action for each.\n *\n * @param _portlist   64-bit portlist bitmap.\n * @param _action     Statement or block executed with @c pid set to each active port id.\n */\n#define foreach_port(_portlist, _action)                  \\\n    do {                                                  \\\n        uint64_t *_pl = (uint64_t *)&_portlist;           \\\n        uint16_t pid, idx, bit;                           \\\n                                                          \\\n        RTE_ETH_FOREACH_DEV(pid)                          \\\n        {                                                 \\\n            idx = (pid / (sizeof(uint64_t) * 8));         \\\n            bit = (pid - (idx * (sizeof(uint64_t) * 8))); \\\n            if ((_pl[idx] & (1LL << bit)) == 0)           \\\n                continue;                                 \\\n            _action;                                      \\\n        }                                                 \\\n    } while ((0))\n\n/**\n * Validate that exactly @p _n arguments are present on the Lua stack.\n *\n * Returns a Lua error to the caller if the count does not match.\n *\n * @param _l   Lua state.\n * @param _n   Expected argument count.\n */\n#define validate_arg_count(_l, _n)                                                     \\\n    do {                                                                               \\\n        switch (lua_gettop(_l)) {                                                      \\\n        default:                                                                       \\\n            return luaL_error(_l, \"%s, Invalid arg count should be %d\", __func__, _n); \\\n        case _n:                                                                       \\\n            break;                                                                     \\\n        }                                                                              \\\n    } while ((0))\n\nstruct pkt_data {\n    /* Packet type and information */\n    struct rte_ether_addr eth_dst_addr; /**< Destination Ethernet address */\n    struct rte_ether_addr eth_src_addr; /**< Source Ethernet address */\n\n    uint32_t ip_src_addr; /**< Source IPv4 address also used for IPv6 */\n    uint32_t ip_dst_addr; /**< Destination IPv4 address */\n    uint32_t ip_mask;     /**< IPv4 Netmask value */\n\n    uint16_t sport;          /**< Source port value */\n    uint16_t dport;          /**< Destination port value */\n    uint16_t ethType;        /**< IPv4 or IPv6 */\n    uint16_t ipProto;        /**< TCP or UDP or ICMP */\n    uint16_t vlanid;         /**< VLAN ID value if used */\n    uint16_t ether_hdr_size; /**< Size of Ethernet header in packet for VLAN ID */\n\n    uint16_t pktSize; /**< Size of packet in bytes not counting FCS */\n    uint16_t pad0;\n};\n\ntypedef struct rte_mempool pktmbuf_t;\ntypedef struct rte_mempool mempool_t;\ntypedef struct vec vec_t;\n\n/**\n * Push an integer value and set it as field @p name in the table at the top of the stack.\n *\n * @param L      Lua state.\n * @param name   Field name in the table.\n * @param value  Integer value to set.\n */\nstatic __inline__ void\nsetf_integer(lua_State *L, const char *name, lua_Integer value)\n{\n    lua_pushinteger(L, value);\n    lua_setfield(L, -2, name);\n}\n\n/**\n * Push a C closure (with no upvalues) and set it as field @p name in the table at stack top.\n *\n * @param L     Lua state.\n * @param name  Field name in the table.\n * @param fn    C function to push as a closure.\n */\nstatic __inline__ void\nsetf_function(lua_State *L, const char *name, lua_CFunction fn)\n{\n    lua_pushcclosure(L, fn, 0);\n    lua_setfield(L, -2, name);\n}\n\n/**\n * Push a NUL-terminated string and set it as field @p name in the table at stack top.\n *\n * @param L      Lua state.\n * @param name   Field name in the table.\n * @param value  String value to push.\n */\nstatic __inline__ void\nsetf_string(lua_State *L, const char *name, const char *value)\n{\n    lua_pushstring(L, value);\n    lua_setfield(L, -2, name);\n}\n\n/**\n * Push a length-delimited string and set it as field @p name in the table at stack top.\n *\n * @param L      Lua state.\n * @param name   Field name in the table.\n * @param value  String data to push (need not be NUL-terminated).\n * @param len    Number of bytes in @p value.\n */\nstatic __inline__ void\nsetf_stringLen(lua_State *L, const char *name, char *value, int len)\n{\n    lua_pushlstring(L, value, len);\n    lua_setfield(L, -2, name);\n}\n\n/**\n * Push a light userdata pointer and set it as field @p name in the table at stack top.\n *\n * @param L      Lua state.\n * @param name   Field name in the table.\n * @param value  Pointer to push as light userdata.\n */\nstatic __inline__ void\nsetf_udata(lua_State *L, const char *name, void *value)\n{\n    lua_pushlightuserdata(L, value);\n    lua_setfield(L, -2, name);\n}\n\n/**\n * Read an integer field from the Lua table at stack index 3.\n *\n * @param L      Lua state (field is read from the table at index 3).\n * @param field  Name of the integer field to retrieve.\n * @return       Field value as uint32_t, or 0 if the field is absent or not an integer.\n */\nstatic __inline__ uint32_t\ngetf_integer(lua_State *L, const char *field)\n{\n    uint32_t value = 0;\n\n    lua_getfield(L, 3, field);\n    if (lua_isinteger(L, -1))\n        value = luaL_checkinteger(L, -1);\n    lua_pop(L, 1);\n\n    return value;\n}\n\n/**\n * Read a string field from the Lua table at stack index 3.\n *\n * @param L      Lua state (field is read from the table at index 3).\n * @param field  Name of the string field to retrieve.\n * @return       Pointer to the Lua-managed string, or NULL if absent or not a string.\n */\nstatic __inline__ const char *\ngetf_string(lua_State *L, const char *field)\n{\n    const char *value = NULL;\n\n    lua_getfield(L, 3, field);\n    if (lua_isstring(L, -1))\n        value = luaL_checkstring(L, -1);\n    lua_pop(L, 1);\n\n    return value;\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _RTE_LUA_DPDK_H_ */\n"
  },
  {
    "path": "lib/lua/lua_pktmbuf.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#define lua_dpdk_c\n#define LUA_LIB\n#define lua_c\n\n#include <rte_ethdev.h>\n#include <rte_mbuf.h>\n#include <rte_cycles.h>\n#include <vec.h>\n#include <rte_timer.h>\n#include <rte_version.h>\n\n#include <pg_strings.h>\n#include \"lua_config.h\"\n#include \"lua_stdio.h\"\n#include \"lua_dpdk.h\"\n#include \"lua_pktmbuf.h\"\n#include \"lua_utils.h\"\n\n#ifndef __INTEL_COMPILER\n#pragma GCC diagnostic ignored \"-Wcast-qual\"\n#endif\n\nstatic int lua_inst;\nstatic const char *Pktmbuf = \"Pktmbuf\";\n\nstatic int\n_new(lua_State *L)\n{\n    pktmbuf_t **mbp;\n    struct rte_mempool *mp;\n    const char *name;\n    uint32_t n, size, csize;\n    char poolname[RTE_MEMPOOL_NAMESIZE];\n\n    validate_arg_count(L, 4);\n\n    name = luaL_checkstring(L, 1);\n    if (!name)\n        luaL_error(L, \"Name is empty\");\n    n = luaL_checkint(L, 2);\n    if (n == 0)\n        luaL_error(L, \"Number of entries is zero\");\n    size = luaL_checkint(L, 3);\n    if (size == 0)\n        luaL_error(L, \"Size of entries is zero\");\n    csize = luaL_checkint(L, 4);\n\n    mbp = (pktmbuf_t **)lua_newuserdata(L, sizeof(void *));\n\n    snprintf(poolname, sizeof(poolname), \"%s-%d\", name, lua_inst++);\n    mp = rte_pktmbuf_pool_create(poolname, n, csize, 0, size, pg_socket_id());\n    if (mp == NULL)\n        luaL_error(L, \"Failed to create MBUF Pool\");\n    *mbp = mp;\n\n    luaL_getmetatable(L, Pktmbuf);\n    lua_setmetatable(L, -2);\n\n    return 1;\n}\n\nstatic int\n_destroy(lua_State *L)\n{\n    pktmbuf_t **mbp;\n\n    validate_arg_count(L, 1);\n\n    mbp = (pktmbuf_t **)luaL_checkudata(L, 1, Pktmbuf);\n\n    rte_mempool_free(*mbp);\n\n    return 0;\n}\n\nstatic int\n_get(lua_State *L)\n{\n    pktmbuf_t **mbp;\n    struct rte_mbuf *m;\n\n    validate_arg_count(L, 1);\n\n    mbp = luaL_checkudata(L, 1, Pktmbuf);\n\n    if (rte_mempool_get(*mbp, (void **)&m) == 0)\n        lua_pushlightuserdata(L, m);\n\n    return 1;\n}\n\nstatic int\n_put(lua_State *L)\n{\n    struct rte_mbuf *m;\n\n    validate_arg_count(L, 2);\n\n    m = lua_touserdata(L, 2);\n\n    rte_pktmbuf_free(m);\n\n    return 0;\n}\n\nstatic int\n_tostring(lua_State *L)\n{\n    pktmbuf_t **mbp;\n    struct rte_mempool *mp;\n    char buff[64];\n\n    mbp = (pktmbuf_t **)luaL_checkudata(L, 1, Pktmbuf);\n    if (!mbp || !*mbp)\n        return luaL_error(L, \"tostring, pktmbuf is nil\");\n\n    mp = *mbp;\n\n    lua_getmetatable(L, 1);\n    lua_getfield(L, -1, \"__name\");\n\n    snprintf(buff, sizeof(buff), \"%s<%s,%d,%d,%d,%d>\", lua_tostring(L, -1), mp->name, mp->size,\n             mp->elt_size, mp->cache_size, mp->socket_id);\n    lua_pop(L, 3);\n\n    lua_pushstring(L, buff);\n    return 1;\n}\n\nstatic int\n_gc(lua_State *L __rte_unused)\n{\n    return 0;\n}\n\nstatic const struct luaL_Reg _methods[] = {{\"get\", _get}, {\"put\", _put}, {NULL, NULL}};\n\nstatic const struct luaL_Reg _functions[] = {{\"new\", _new}, {\"destroy\", _destroy}, {NULL, NULL}};\n\nint\nluaopen_pktmbuf(lua_State *L)\n{\n    luaL_newmetatable(L, Pktmbuf);        // create and push new table called Vec\n    lua_pushvalue(L, -1);                 // dup the table on the stack\n\n    lua_setfield(L, -2, \"__index\");        //\n\n    luaL_setfuncs(L, _methods, 0);\n\n    lua_pushstring(L, \"__tostring\");\n    lua_pushcfunction(L, _tostring);\n    lua_settable(L, -3);\n\n    lua_pushstring(L, \"__gc\");\n    lua_pushcfunction(L, _gc);\n    lua_settable(L, -3);\n    lua_pop(L, 1);\n\n    lua_getglobal(L, LUA_DPDK_LIBNAME);\n    lua_newtable(L);\n    luaL_setfuncs(L, _functions, 0);\n\n    lua_setfield(L, -2, LUA_PKTMBUF_LIBNAME);\n\n    return 1;\n}\n"
  },
  {
    "path": "lib/lua/lua_pktmbuf.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#ifndef _RTE_LUA_PKTMBUF_H_\n#define _RTE_LUA_PKTMBUF_H_\n\n/**\n * @file\n *\n * Lua bindings for DPDK pktmbuf mempools.\n *\n * Exposes pktmbuf mempool operations to Lua scripts under the \"pktmbuf\"\n * library name, allowing scripts to allocate and manage packet buffers.\n */\n\n#include <rte_log.h>\n\n#define lua_c\n#include <lua.h>\n#include <lauxlib.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define LUA_PKTMBUF_LIBNAME \"pktmbuf\"\n\ntypedef struct rte_mempool pktmbuf_t;\n\n/**\n * Open the pktmbuf Lua library and register its functions.\n *\n * Called automatically by the Lua runtime when the library is required.\n *\n * @param L\n *   Lua state to register the library into.\n * @return\n *   Number of values pushed onto the Lua stack (1 — the library table).\n */\nint luaopen_pktmbuf(lua_State *L);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _RTE_LUA_PKTMBUF_H_ */\n"
  },
  {
    "path": "lib/lua/lua_socket.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <sys/queue.h>\n#include <netinet/in.h>\n#include <net/if.h>\n#include <fcntl.h>\n#include <setjmp.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <errno.h>\n#include <getopt.h>\n#include <termios.h>\n#include <sys/ioctl.h>\n#include <libgen.h>\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <assert.h>\n\n#include \"lua_config.h\"\n#include \"lua_stdio.h\"\n#include \"lua_utils.h\"\n#include \"lua_socket.h\"\n\nstatic int\nserver_startup(luaData_t *ld)\n{\n    const char *err_msg = NULL;\n    struct sockaddr_in ipaddr;\n    struct hostent *pHost;\n    int linger = 1;\n\n    pthread_detach(pthread_self());\n\n    err_msg = \"gethostbyname failed\";\n    if ((pHost = gethostbyname(ld->hostname)) == NULL)\n        goto error_exit;\n\n    err_msg = \"Socket create failed\";\n    if ((ld->server_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)\n        goto error_exit;\n\n    memset(&ipaddr, 0, sizeof(ipaddr));\n    ipaddr.sin_family      = AF_INET;\n    ipaddr.sin_port        = htons(ld->socket_port);\n    ipaddr.sin_addr.s_addr = htonl(INADDR_ANY);\n\n    err_msg = \"Setsockopt failed\";\n    if (setsockopt(ld->server_socket, SOL_SOCKET, SO_REUSEADDR, &linger, sizeof(linger)) == -1)\n        goto error_exit;\n\n    err_msg = \"Bind failed\";\n    if (bind(ld->server_socket, (struct sockaddr *)&ipaddr, sizeof(ipaddr)) < 0)\n        goto error_exit;\n\n    err_msg = \"Listen failed\";\n    if (listen(ld->server_socket, 5) < 0)\n        goto error_exit;\n\n    return 0;\n\nerror_exit:\n    if (ld->server_socket != -1)\n        close(ld->server_socket);\n    ld->server_socket = -1;\n    if (err_msg) {\n        perror(err_msg);\n        fflush(stdout);\n    }\n    return -1;\n}\n\nstatic void\n_socket_open(luaData_t *ld)\n{\n    if (ld) {\n        ld->in  = fdopen(ld->client_socket, \"r\");\n        ld->out = fdopen(ld->client_socket, \"w\");\n        ld->err = fdopen(ld->client_socket, \"w\");\n    }\n}\n\nstatic void\n_socket_close(luaData_t *ld)\n{\n    if (ld) {\n        fclose(ld->in);\n        fclose(ld->out);\n        fclose(ld->err);\n    }\n}\n\n/* mark in error messages for incomplete statements */\n#define EOFMARK \"<eof>\"\n#define marklen (sizeof(EOFMARK) / sizeof(char) - 1)\n\n/*\n** Check whether 'status' signals a syntax error and the error\n** message at the top of the stack ends with the above mark for\n** incomplete statements.\n*/\nstatic int\nincomplete(lua_State *L, int status)\n{\n    if (status == LUA_ERRSYNTAX) {\n        size_t lmsg;\n        const char *msg = lua_tolstring(L, -1, &lmsg);\n\n        if (lmsg >= marklen && !strcmp(msg + lmsg - marklen, EOFMARK)) {\n            lua_pop(L, 1);\n            return 1;\n        }\n    }\n    return 0; /* else... */\n}\n\n/*\n** Read a line, and push it into the Lua stack.\n*/\nstatic int\npushline(luaData_t *ld, int firstline)\n{\n    lua_State *L = ld->L;\n    size_t l;\n    char *b = lua_readline(ld);\n\n    if (!b)\n        return 0;\n\n    l = strlen(b);\n    printf(\"%s\", b);\n    if (l > 0 && b[l - 1] == '\\n')              /* line ends with newline? */\n        b[--l] = '\\0';                          /* remove it */\n    if (firstline && b[0] == '=')               /* for compatibility with 5.2, ... */\n        lua_pushfstring(L, \"return %s\", b + 1); /* change '=' to 'return' */\n    else\n        lua_pushlstring(L, b, l);\n    return 1;\n}\n\n/*\n** Try to compile line on the stack as 'return <line>;'; on return, stack\n** has either compiled chunk or original line (if compilation failed).\n*/\nstatic int\naddreturn(lua_State *L)\n{\n    const char *line    = lua_tostring(L, -1); /* original line */\n    const char *retline = lua_pushfstring(L, \"return %s;\", line);\n    int status          = luaL_loadbuffer(L, retline, strlen(retline), \"=stdin\");\n\n    if (status == LUA_OK)\n        lua_remove(L, -2); /* remove modified line */\n    else\n        lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */\n\n    return status;\n}\n\n/*\n** Read multiple lines until a complete Lua statement\n*/\nstatic int\nmultiline(luaData_t *ld)\n{\n    lua_State *L = ld->L;\n\n    for (;;) { /* repeat until gets a complete statement */\n        size_t len;\n        const char *line = lua_tolstring(L, 1, &len);               /* get what it has */\n        int status       = luaL_loadbuffer(L, line, len, \"=stdin\"); /* try it */\n\n        if (!incomplete(L, status) || !pushline(ld, 0))\n            return status; /* cannot or should not try to add continuation line */\n\n        lua_pushliteral(L, \"\\n\"); /* add newline... */\n        lua_insert(L, -2);        /* ...between the two lines */\n        lua_concat(L, 3);         /* join them */\n    }\n}\n\n/*\n** Read a line and try to load (compile) it first as an expression (by\n** adding \"return \" in front of it) and second as a statement. Return\n** the final status of load/call with the resulting function (if any)\n** in the top of the stack.\n*/\nstatic int\nloadline(luaData_t *ld)\n{\n    lua_State *L = ld->L;\n    int status;\n\n    lua_settop(L, 0);\n    if (!pushline(ld, 1))\n        return -1; /* no input <EOF> */\n\n    status = addreturn(L);\n\n    if (status != LUA_OK)       /* 'return ...' did not work? */\n        status = multiline(ld); /* try as command, maybe with continuation lines */\n\n    lua_remove(L, 1); /* remove line from the stack */\n\n    return status;\n}\n\n/*\n** Prints (calling the Lua 'print' function) any values on the stack\n*/\nstatic void\nl_print(lua_State *L)\n{\n    int n = lua_gettop(L);\n\n    if (n > 0) { /* any result to be printed? */\n        luaL_checkstack(L, LUA_MINSTACK, \"too many results to print\");\n        if (lua_isfunction(L, 1))\n            return;\n\n        lua_getglobal(L, \"print\");\n        lua_insert(L, 1);\n        if (lua_pcall(L, n, 0, 0) != LUA_OK)\n            l_message(lua_get_progname(),\n                      lua_pushfstring(L, \"error calling 'print' (%s)\", lua_tostring(L, -1)));\n    }\n}\n\nstatic void\ndoREPL(luaData_t *ld)\n{\n    lua_State *L = ld->L;\n    int status;\n    const char *oldprogname = lua_get_progname();\n\n    lua_set_progname(NULL);\n    while ((status = loadline(ld)) != -1) {\n        if (status == LUA_OK)\n            status = lua_docall(L, 0, LUA_MULTRET);\n        if (status == LUA_OK)\n            l_print(L);\n        else\n            report(L, status);\n    }\n    if (lua_gettop(L))\n        lua_settop(L, 0); /* clear stack */\n\n    lua_set_progname(oldprogname);\n}\n\nstatic void\nhandle_server_requests(luaData_t *ld)\n{\n    struct sockaddr_in ipaddr;\n    socklen_t len;\n\n    ld->client_socket = -1;\n\n    do {\n        len = sizeof(struct sockaddr_in);\n        if ((ld->client_socket = accept(ld->server_socket, (struct sockaddr *)&ipaddr, &len)) < 0) {\n            perror(\"accept failed\");\n            break;\n        }\n\n        if (ld->client_socket > 0) {\n            _socket_open(ld);\n            lua_set_stdfiles(ld);\n\n            doREPL(ld);\n\n            lua_reset_stdfiles(ld);\n            _socket_close(ld);\n\n            close(ld->client_socket);\n            ld->client_socket = -1;\n        }\n    } while (1);\n\n    if (ld->server_socket > 0) {\n        close(ld->server_socket);\n        ld->server_socket = -1;\n    }\n}\n\nstatic void *\nlua_server(void *arg)\n{\n    luaData_t *ld = arg;\n\n    if (server_startup(ld))\n        fprintf(stderr, \"server_startup() failed!\\n\");\n\n    handle_server_requests(ld);\n\n    return NULL;\n}\n\nint rte_thread_set_name(pthread_t id, const char *name);\n\nint\nlua_start_socket(luaData_t *ld, pthread_t *pthread, char *hostname, int port)\n{\n    int r;\n\n    ld->client_socket = -1;\n    ld->server_socket = -1;\n    ld->socket_port   = port;\n    ld->hostname      = strdup((hostname) ? hostname : \"localhost\");\n\n    /* Split assert and function because using NDEBUG define will remove function */\n    r = pthread_create(pthread, NULL, lua_server, ld);\n    if (r)\n        return -1;\n\n    rte_thread_set_name(*pthread, \"pktgen-socket\");\n\n    return 0;\n}\n"
  },
  {
    "path": "lib/lua/lua_socket.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#ifndef _RTE_LUA_SOCKET_H_\n#define _RTE_LUA_SOCKET_H_\n\n/**\n * @file\n *\n * Lua TCP socket server for remote script execution.\n *\n * Starts a background thread that listens on a TCP port and executes\n * Lua code received from connected clients, enabling external control\n * of a running Pktgen instance.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <signal.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <string.h>\n#include <pthread.h>\n\n#define lua_c\n\n#include <lua.h>\n#include <lauxlib.h>\n#include <lualib.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Start the Lua TCP socket server in a background pthread.\n *\n * The server accepts connections on @p port and executes received Lua\n * strings within the @p ld Lua instance.\n *\n * @param ld\n *   Lua instance to use for executing received scripts.\n * @param pthread\n *   Output: handle of the created pthread.\n * @param hostname\n *   Hostname or IP address string to bind to.\n * @param port\n *   TCP port number to listen on.\n * @return\n *   0 on success, -1 on error.\n */\nint lua_start_socket(luaData_t *ld, pthread_t *pthread, char *hostname, int port);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _RTE_LUA_SOCKET_H_ */\n"
  },
  {
    "path": "lib/lua/lua_stdio.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#include <sys/queue.h>\n#include <netinet/in.h>\n#include <net/if.h>\n#include <fcntl.h>\n#include <setjmp.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <errno.h>\n#include <getopt.h>\n#include <termios.h>\n#include <sys/ioctl.h>\n#include <libgen.h>\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <assert.h>\n\n#include \"lua_config.h\"\n#include \"lua_stdio.h\"\n\n#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE))\n\ntypedef luaL_Stream LStream;\n\nvoid *\nlua_get_stdout(luaData_t *ld)\n{\n    if (!ld || !ld->out)\n        return stdout;\n\n    return ld->out;\n}\n\nvoid *\nlua_get_stdin(luaData_t *ld)\n{\n    if (!ld || !ld->in)\n        return stdin;\n\n    return ld->in;\n}\n\nvoid *\nlua_get_stderr(luaData_t *ld)\n{\n    if (!ld || !ld->err)\n        return stderr;\n\n    return ld->err;\n}\n\nvoid\nlua_signal_set_stdfiles(luaData_t *ld)\n{\n    lua_set_stdfiles(ld);\n    signal(SIGPIPE, SIG_IGN);\n}\n\nvoid\nlua_signal_reset_stdfiles(luaData_t *ld)\n{\n    signal(SIGPIPE, SIG_DFL);\n    lua_reset_stdfiles(ld);\n}\n\nvoid\nlua_set_stdfiles(luaData_t *ld)\n{\n    lua_State *L = ld->L;\n\n    luaL_getmetatable(L, LUA_FILEHANDLE);\n\n    if (lua_isnil(L, -1)) {\n        DBG(\"luaL_getmetatable() returned NIL\\n\");\n        return;\n    }\n\n    /* create (and set) default files */\n    lua_create_stdfile(ld, ld->in, IO_INPUT, \"stdin\");\n    lua_create_stdfile(ld, ld->out, IO_OUTPUT, \"stdout\");\n    lua_create_stdfile(ld, ld->err, NULL, \"stderr\");\n}\n\nvoid\nlua_reset_stdfiles(luaData_t *ld)\n{\n    lua_State *L = ld->L;\n\n    luaL_getmetatable(L, LUA_FILEHANDLE);\n\n    if (lua_isnil(L, -1))\n        return;\n\n    /* create (and set) default files */\n    lua_create_stdfile(ld, stdin, IO_INPUT, \"stdin\");\n    lua_create_stdfile(ld, stdout, IO_OUTPUT, \"stdout\");\n    lua_create_stdfile(ld, stderr, NULL, \"stderr\");\n}\n\n/*\n** function to (not) close the standard files stdin, stdout, and stderr\n*/\nstatic int\nio_noclose(lua_State *L)\n{\n    LStream *p = tolstream(L);\n    p->closef  = &io_noclose; /* keep file opened */\n    lua_pushnil(L);\n    lua_pushliteral(L, \"cannot close standard file\");\n    return 2;\n}\n\nstatic LStream *\nnewprefile(lua_State *L)\n{\n    LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream));\n    p->closef  = NULL; /* mark file handle as 'closed' */\n    luaL_setmetatable(L, LUA_FILEHANDLE);\n    return p;\n}\n\nvoid\nlua_create_stdfile(luaData_t *ld, FILE *f, const char *k, const char *fname)\n{\n    lua_State *L = ld->L;\n    LStream *p   = newprefile(L);\n\n    p->f      = f;\n    p->closef = &io_noclose;\n    if (k != NULL) {\n        lua_pushvalue(L, -1);\n        lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */\n    }\n    lua_setfield(L, -2, fname); /* add file to module */\n}\n"
  },
  {
    "path": "lib/lua/lua_stdio.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#ifndef _RTE_LUA_STDIO_H_\n#define _RTE_LUA_STDIO_H_\n\n/**\n * @file\n *\n * Lua stdio stream redirection helpers.\n *\n * These functions allow Lua's stdin/stdout/stderr to be redirected to\n * arbitrary FILE objects, which is needed when Lua scripts run over a\n * TCP socket rather than the process's standard streams.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <signal.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <string.h>\n#include <pthread.h>\n\n#define lua_c\n\n#include <lua.h>\n#include <lauxlib.h>\n#include <lualib.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Return the FILE pointer used as Lua's stdout for instance @p ld.\n *\n * @param ld   Lua instance.\n * @return     FILE pointer for stdout, or NULL if not set.\n */\nvoid *lua_get_stdout(luaData_t *ld);\n\n/**\n * Return the FILE pointer used as Lua's stdin for instance @p ld.\n *\n * @param ld   Lua instance.\n * @return     FILE pointer for stdin, or NULL if not set.\n */\nvoid *lua_get_stdin(luaData_t *ld);\n\n/**\n * Return the FILE pointer used as Lua's stderr for instance @p ld.\n *\n * @param ld   Lua instance.\n * @return     FILE pointer for stderr, or NULL if not set.\n */\nvoid *lua_get_stderr(luaData_t *ld);\n\n/**\n * Create and register a Lua stdio file object backed by FILE @p f.\n *\n * @param ld     Lua instance.\n * @param f      Underlying C FILE to wrap.\n * @param k      Lua global key name for this file (e.g. \"stdout\").\n * @param fname  Lua filename string associated with the file object.\n */\nvoid lua_create_stdfile(luaData_t *ld, FILE *f, const char *k, const char *fname);\n\n/**\n * Redirect Lua's stdin/stdout/stderr to the socket streams in @p ld.\n *\n * @param ld   Lua instance whose socket streams should become the std files.\n */\nvoid lua_set_stdfiles(luaData_t *ld);\n\n/**\n * Restore Lua's stdin/stdout/stderr to the process standard streams.\n *\n * @param ld   Lua instance to reset.\n */\nvoid lua_reset_stdfiles(luaData_t *ld);\n\n/**\n * Set Lua's stdio streams from within a signal handler context.\n *\n * Signal-safe variant of lua_set_stdfiles().\n *\n * @param ld   Lua instance.\n */\nvoid lua_signal_set_stdfiles(luaData_t *ld);\n\n/**\n * Reset Lua's stdio streams from within a signal handler context.\n *\n * Signal-safe variant of lua_reset_stdfiles().\n *\n * @param ld   Lua instance.\n */\nvoid lua_signal_reset_stdfiles(luaData_t *ld);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _RTE_LUA_STDIO_H_ */\n"
  },
  {
    "path": "lib/lua/lua_utils.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/* Created 2010 by Keith Wiles @ intel.com */\n\n#include <sys/queue.h>\n#include <netinet/in.h>\n#include <net/if.h>\n#include <fcntl.h>\n#include <setjmp.h>\n#include <stdarg.h>\n#include <ctype.h>\n#include <errno.h>\n#include <getopt.h>\n#include <termios.h>\n#include <sys/ioctl.h>\n#include <libgen.h>\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <assert.h>\n\n#include \"lua_config.h\"\n#include \"lua_stdio.h\"\n#include \"lua_utils.h\"\n\nchar *\nlua_strtrim(char *str)\n{\n    if (!str || !*str)\n        return str;\n\n    /* trim white space characters at the front */\n    while (isspace(*str))\n        str++;\n\n    /* Make sure the string is not empty */\n    if (*str) {\n        char *p = &str[strlen(str) - 1];\n\n        /* trim trailing white space characters */\n        while ((p >= str) && isspace(*p))\n            p--;\n\n        p[1] = '\\0';\n    }\n    return *str ? str : NULL;\n}\n"
  },
  {
    "path": "lib/lua/lua_utils.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#ifndef _RTE_LUA_UTILS_H_\n#define _RTE_LUA_UTILS_H_\n\n/**\n * @file\n *\n * Lua shell utility helpers: string trimming, console output, line reading,\n * error reporting, and Lua status checking.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <signal.h>\n#include <inttypes.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <string.h>\n#include <pthread.h>\n\n#include <lua_config.h>\n\n#define lua_c\n\n#include <lua.h>\n#include <lauxlib.h>\n#include <lualib.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Trim leading and trailing whitespace from @p str in place.\n *\n * @param str   String to trim. Modified in place.\n * @return      Pointer to the first non-whitespace character within @p str.\n */\nchar *lua_strtrim(char *str);\n\n/**\n * Write a formatted string to stdout and flush.\n *\n * @param format   printf-style format string.\n * @param ...      Variable arguments for @p format.\n */\nstatic inline void\nlua_putstring(const char *format, ...)\n{\n    va_list ap;\n\n    va_start(ap, format);\n    vfprintf(stdout, format, ap);\n    va_end(ap);\n\n    fflush(stdout);\n}\n\n/**\n * Read one line of input from the Lua instance's stdin into its buffer.\n *\n * @param ld   Lua instance whose stdin and buffer are used.\n * @return     Pointer to ld->buffer on success, or NULL on EOF/error.\n */\nstatic inline char *\nlua_readline(luaData_t *ld)\n{\n    *ld->buffer = '\\0';\n    return fgets(ld->buffer, LUA_BUFFER_SIZE, lua_get_stdin(ld));\n}\n\n/**\n * Print a Lua error message to stderr, optionally prefixed by @p pname.\n *\n * @param pname   Programme name prefix, or NULL to omit.\n * @param msg     Error message string to print.\n */\nstatic inline void\nl_message(const char *pname, const char *msg)\n{\n    if (pname)\n        fprintf(stderr, \"%s: \", pname);\n    fprintf(stderr, \"%s\\n\", msg);\n}\n\n/**\n * Check @p status and, if it indicates an error, print the top-of-stack message.\n *\n * Assumes the error object is a string (as generated by Lua or msghandler).\n * Pops the error string from the stack before returning.\n *\n * @param L       Lua state.\n * @param status  Return value from a Lua protected call (e.g. lua_pcall).\n * @return        @p status unchanged.\n */\nstatic inline int\nreport(lua_State *L, int status)\n{\n    if (status != LUA_OK) {\n        const char *msg = lua_tostring(L, -1);\n\n        l_message(lua_get_progname(), msg);\n        lua_pop(L, 1); /* remove message */\n    }\n    return status;\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _LUA_SUPPORT_H_ */\n"
  },
  {
    "path": "lib/lua/lua_vec.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#define lua_dpdk_c\n#define LUA_LIB\n#define lua_c\n\n#include <rte_ethdev.h>\n#include <rte_mbuf.h>\n#include <rte_cycles.h>\n#include <vec.h>\n#include <rte_timer.h>\n#include <rte_version.h>\n\n#include <pg_strings.h>\n#include \"lua_config.h\"\n#include \"lua_stdio.h\"\n#include \"lua_dpdk.h\"\n#include \"lua_vec.h\"\n#include \"lua_utils.h\"\n\n#ifndef __INTEL_COMPILER\n#pragma GCC diagnostic ignored \"-Wcast-qual\"\n#endif\n\nstatic const char *Vec = \"Vec\";\n\nstatic int\nvec_new(lua_State *L)\n{\n    vec_t *v;\n    int size = 0, top = lua_gettop(L);\n\n    if (top > 1)\n        return luaL_error(L, \"new, Invalid arg count should be 0 or 1\");\n\n    if (top >= 1)\n        size = luaL_checkint(L, 1);\n    if (size == 0)\n        size = vec_calc_size(0);\n\n    v = (struct vec *)lua_newuserdata(L, (sizeof(struct vec) * (size * sizeof(void *))));\n    vec_init(v, size, VEC_CREATE_FLAG);\n\n    luaL_getmetatable(L, Vec);\n    lua_setmetatable(L, -2);\n\n    return 1;\n}\n\nstatic int\nlvec_add1(lua_State *L)\n{\n    struct vec *v;\n\n    validate_arg_count(L, 2);\n\n    v = (struct vec *)luaL_checkudata(L, 1, Vec);\n\n    if (v->len >= v->tlen)\n        return 0;\n\n    v->list[v->len++] = lua_touserdata(L, 2);\n\n    return 1;\n}\n\nstatic int\nvec_tostring(lua_State *L)\n{\n    struct vec *v;\n    char buff[64];\n\n    v = (struct vec *)luaL_checkudata(L, 1, Vec);\n\n    lua_getmetatable(L, 1);\n    lua_getfield(L, -1, \"__name\");\n\n    snprintf(buff, sizeof(buff), \"%s<%d,%d,%s,0x%04x>\", lua_tostring(L, -1), v->len, v->tlen,\n             (v->vpool) ? \"V\" : \"_\", v->flags);\n    lua_pop(L, 3);\n\n    lua_pushstring(L, buff);\n    return 1;\n}\n\nstatic int\nvec_gc(lua_State *L)\n{\n    struct vec *v;\n    struct rte_mbuf *m;\n    int i;\n\n    if (lua_gettop(L) != 1)\n        return luaL_error(L, \"vec.gc, Invalid arg count should be 1\");\n    v = (struct vec *)lua_touserdata(L, 1);\n\n    vec_foreach(i, m, v)\n    {\n        if (m)\n            rte_pktmbuf_free(m);\n    }\n    vec_free(v);\n\n    return 0;\n}\n\nstatic const struct luaL_Reg vec_methods[] = {{\"add1\", lvec_add1}, {NULL, NULL}};\n\nstatic const struct luaL_Reg vec_functions[] = {{\"new\", vec_new}, {NULL, NULL}};\n\nint\nluaopen_vec(lua_State *L)\n{\n    luaL_newmetatable(L, Vec);        // create and push new table called Vec\n    lua_pushvalue(L, -1);             // dup the table on the stack\n\n    lua_setfield(L, -2, \"__index\");        //\n\n    luaL_setfuncs(L, vec_methods, 0);\n\n    lua_pushstring(L, \"__tostring\");\n    lua_pushcfunction(L, vec_tostring);\n    lua_settable(L, -3);\n\n    lua_pushstring(L, \"__gc\");\n    lua_pushcfunction(L, vec_gc);\n    lua_settable(L, -3);\n    lua_pop(L, 1);\n\n    lua_getglobal(L, LUA_DPDK_LIBNAME);\n    lua_newtable(L);\n    luaL_setfuncs(L, vec_functions, 0);\n    lua_setfield(L, -2, LUA_VEC_LIBNAME);\n\n    return 1;\n}\n"
  },
  {
    "path": "lib/lua/lua_vec.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#ifndef _RTE_LUA_VEC_H_\n#define _RTE_LUA_VEC_H_\n\n/**\n * @file\n *\n * Lua bindings for the vec (pointer vector) library.\n *\n * Exposes vec container operations to Lua scripts under the \"vec\"\n * library name.\n */\n\n#include <stdint.h>\n#include <netinet/in.h>\n\n#include <rte_log.h>\n\n#define lua_c\n#include <lua.h>\n#include <lauxlib.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define LUA_VEC_LIBNAME \"vec\"\n\n/**\n * Open the vec Lua library and register its functions.\n *\n * Called automatically by the Lua runtime when the library is required.\n *\n * @param L\n *   Lua state to register the library into.\n * @return\n *   Number of values pushed onto the Lua stack (1 — the library table).\n */\nint luaopen_vec(lua_State *L);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _RTE_LUA_VEC_H_ */\n"
  },
  {
    "path": "lib/lua/meson.build",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2017-2026> Intel Corporation\n\nif get_option('enable_lua')\n\tsources = files(\n\t\t'lua_config.c',\n\t\t'lua_dapi.c',\n\t\t'lua_dpdk.c',\n\t\t'lua_pktmbuf.c',\n\t\t'lua_socket.c',\n\t\t'lua_stdio.c',\n\t\t'lua_utils.c',\n\t\t'lua_vec.c')\n\n\tliblua = library('lua', sources,\n\t\tdependencies: [common, utils, vec, lua_dep, dpdk])\nelse\n\tsources = files('lua_config.c')\n\tliblua = library('lua', sources)\nendif\n\nlua = declare_dependency(link_with: liblua,\n\tinclude_directories: include_directories('.'))\n"
  },
  {
    "path": "lib/meson.build",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2020-2026> Intel Corporation\n\nsubdir('hmap')\nsubdir('common')\nsubdir('utils')\nsubdir('vec')\nsubdir('plugin')\nsubdir('cli')\nsubdir('lua')\n"
  },
  {
    "path": "lib/plugin/meson.build",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2018-2026> Intel Corporation\n\nsources = files('plugin.c')\nlibplugin = library('plugin', sources,\n\tdependencies: [common, dpdk])\nplugin = declare_dependency(link_with: libplugin,\n\tinclude_directories: include_directories('.'))\n"
  },
  {
    "path": "lib/plugin/plugin.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2018-2026> Intel Corporation.\n */\n\n#include <stdio.h>\n#include <unistd.h>\n#include <stdarg.h>\n#include <string.h>\n#include <stdint.h>\n#include <inttypes.h>\n#include <dlfcn.h>\n\n#include <rte_config.h>\n#include <rte_compat.h>\n#include <rte_rwlock.h>\n\n#ifdef RTE_ARCH_X86\n#include <rte_hash_crc.h>\n#define hash_func rte_hash_crc\n#else\n#include <rte_jhash.h>\n#define hash_func rte_jhash\n#endif\n\n#include \"plugin.h\"\n\nstatic rte_rwlock_t _plugin_rwlock = RTE_RWLOCK_INITIALIZER;\nrte_rwlock_t *plugin_rwlock        = &_plugin_rwlock;\n#define RTE_PLUGIN_TAILQ_RWLOCK plugin_rwlock\n\nstatic TAILQ_HEAD(plugin_list, plugin) plugin_head = TAILQ_HEAD_INITIALIZER(plugin_head);\n\nstatic struct plugin *plugin_inst[PLUGIN_MAX_INST];\n\nstatic const char *plugin_fmts[] = {\n    \"%s\", \"%s.so\", \"lib%s\", \"lib%s.so\", \"librte_%s.so\", \"librte_pmd_%s.so\", NULL};\n\nstruct plugin *\nplugin_get(int inst)\n{\n    if (inst < 0 || inst >= PLUGIN_MAX_INST)\n        return NULL;\n    return plugin_inst[inst]; /* This could be NULL as well */\n}\n\nstatic int\nplugin_add_inst(struct plugin *pin)\n{\n    int inst;\n\n    rte_rwlock_write_lock(RTE_PLUGIN_TAILQ_RWLOCK);\n\n    for (inst = 0; inst < PLUGIN_MAX_INST; inst++) {\n        if (plugin_inst[inst] == NULL) {\n            plugin_inst[inst] = pin;\n\n            rte_rwlock_write_unlock(RTE_PLUGIN_TAILQ_RWLOCK);\n            return inst;\n        }\n    }\n\n    rte_rwlock_write_unlock(RTE_PLUGIN_TAILQ_RWLOCK);\n    return -1;\n}\n\nstatic int\nplugin_del_inst(int inst)\n{\n    struct plugin *pin = plugin_get(inst);\n\n    if (!pin) {\n        PLUGIN_LOG(DEBUG, \"Plugin pointer is NULL or info is NULL\\n\");\n        return -1;\n    }\n\n    /* remove the plugin from the global list */\n    rte_rwlock_write_lock(RTE_PLUGIN_TAILQ_RWLOCK);\n\n    plugin_inst[inst] = NULL;\n\n    if (rte_atomic32_dec_and_test(&pin->refcnt)) {\n        /* remove instance when refcnt becomes zero */\n        TAILQ_REMOVE(&plugin_head, pin, next);\n\n        dlclose(pin->dl);\n\n        free(pin->plugin);\n        free(pin->path);\n        free(pin);\n    }\n\n    rte_rwlock_write_unlock(RTE_PLUGIN_TAILQ_RWLOCK);\n    return 0;\n}\n\nstatic int\nplugin_find_inst(struct plugin *pin)\n{\n    int inst;\n\n    rte_rwlock_write_lock(RTE_PLUGIN_TAILQ_RWLOCK);\n\n    for (inst = 0; inst < PLUGIN_MAX_INST; inst++) {\n        if (plugin_inst[inst] == pin) {\n            rte_rwlock_write_unlock(RTE_PLUGIN_TAILQ_RWLOCK);\n            return inst;\n        }\n    }\n\n    rte_rwlock_write_unlock(RTE_PLUGIN_TAILQ_RWLOCK);\n    return -1;\n}\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wformat-nonliteral\"\n\n/**\n * Routine to search for and open a plugin.\n *\n * @param pin\n *   The plugin structure pointer.\n * @return\n *   0 if plugin found and loaded or -1 on not found.\n */\nstatic int\nplugin_open(struct plugin *pin)\n{\n    char file[1024];\n    int i;\n\n    for (i = 0; plugin_fmts[i]; i++) {\n        char fn[64];\n\n        snprintf(fn, sizeof(fn), plugin_fmts[i], pin->plugin);\n\n        if (pin->path)\n            snprintf(file, sizeof(file), \"%s/%s\", pin->path, fn);\n        else\n            snprintf(file, sizeof(file), \"%s\", fn);\n\n        printf(\"Trying to load this file (%s)\\n\", file);\n        pin->dl = dlopen(file, RTLD_NOW | RTLD_LOCAL);\n        if (pin->dl)\n            return 0;\n        printf(\"dlopen: %s\\n\", dlerror());\n    }\n    return -1;\n}\n#pragma GCC diagnostic pop\n\nint\nplugin_get_symbol(struct plugin *pin, const char *sym, char **ret)\n{\n    char *val;\n    void *v;\n\n    if (!pin || !ret)\n        return -1;\n\n    *ret = NULL;\n    (void)dlerror(); /* Clear errors */\n\n    PLUGIN_LOG(INFO, \"dlsym %p for %s:%d\\n\", pin->dl, pin->plugin, pin->inst);\n    v = dlsym(pin->dl, sym); /* could return NULL */\n\n    val = dlerror();\n    if (val) {\n        PLUGIN_LOG(ERR, \"failed to get symbol (%s)\\n\", val);\n        return -1;\n    }\n\n    *ret = v;\n\n    return 0;\n}\n\nint\nplugin_create(char *plugin, char *path)\n{\n    struct plugin *pin;\n    struct plugin_info *info;\n    int inst, ret;\n    char sym[64];\n\n    /* Make sure we have not already loaded this plugin */\n    inst = plugin_find_by_name(plugin);\n    if (inst >= 0) {\n        pin = plugin_get(inst);\n\n        /* Did we find a new instance of the same plugin */\n        if (pin && !strcmp(path, pin->path)) {\n            info = pin->info;\n\n            PLUGIN_LOG(DEBUG, \"Plugin: '%s' - %s, version: %d.%02d.%02d-rc%d\\n\", pin->plugin,\n                       info->desc, info->major, info->minor, info->patch, info->release);\n            return inst;\n        }\n        /* not the same plugin */\n    }\n\n    /* Create the plugin structure and begin loading plugin */\n    pin = calloc(1, sizeof(struct plugin));\n    if (!pin)\n        return -1;\n    memset(pin, 0, sizeof(struct plugin));\n    pin->inst = -1;\n\n    inst = plugin_add_inst(pin);\n    if (inst < 0) {\n        PLUGIN_LOG(ERR, \"plugin_add_inst() failed for %s\\n\", plugin);\n        free(pin);\n        return -1;\n    }\n\n    pin->inst = inst;\n\n    /* Save these away for later comparsions */\n    pin->plugin = strdup(plugin);\n    pin->path   = strdup(path);\n\n    if (plugin_open(pin)) {\n        PLUGIN_LOG(ERR, \"dlopen() unable to load %s\\n\", pin->plugin);\n        PLUGIN_LOG(ERR, \"dlerror: %s\\n\", dlerror());\n        goto err_exit;\n    }\n\n    /* Look for the plugin information structure */\n    snprintf(sym, sizeof(sym), \"%s_plugin_info\", pin->plugin);\n    ret = plugin_get_symbol(pin, sym, (char **)&info);\n    if (ret < 0) {\n        PLUGIN_LOG(ERR, \"Invalid plugin %s not found\\n\", sym);\n        goto err_exit;\n    }\n    pin->info = info;\n\n    PLUGIN_LOG(DEBUG, \"Plugin: '%s' - %s, version: %d.%02d.%02d-rc%d\\n\", pin->plugin, info->desc,\n               info->major, info->minor, info->patch, info->release);\n\n    /* Create the CRC 32bit value using plugin name, path and info data */\n    pin->hash = hash_func(pin->plugin, strlen(pin->plugin), pin->hash);\n    pin->hash = hash_func(pin->path, strlen(pin->path), pin->hash);\n    pin->hash = hash_func(pin->info, sizeof(struct plugin_info), pin->hash);\n\n    /* add the plugin to the global list */\n    rte_rwlock_write_lock(RTE_PLUGIN_TAILQ_RWLOCK);\n    TAILQ_INSERT_TAIL(&plugin_head, pin, next);\n    rte_rwlock_write_unlock(RTE_PLUGIN_TAILQ_RWLOCK);\n\n    return inst;\n\nerr_exit:\n    PLUGIN_LOG(ERR, \"Failed to create plugin %s\\n\", plugin);\n    plugin_del_inst(inst);\n    return -1;\n}\n\nint\nplugin_destroy(int inst)\n{\n    if (inst < 0)\n        return -1;\n    return plugin_del_inst(inst);\n}\n\nint\nplugin_start(int inst, void *arg)\n{\n    struct plugin *pin = plugin_get(inst);\n\n    if (!pin) {\n        PLUGIN_LOG(DEBUG, \"Plugin pointer is NULL\\n\");\n        return -1;\n    }\n\n    /* if we have a entry point then call to unload plugin */\n    if (pin->info->start(inst, arg)) {\n        PLUGIN_LOG(DEBUG, \"Start of %s failed\\n\", pin->plugin);\n        return -1;\n    }\n\n    return 0;\n}\n\nint\nplugin_stop(int inst)\n{\n    struct plugin *pin = plugin_get(inst);\n\n    if (!pin) {\n        PLUGIN_LOG(DEBUG, \"Plugin pointer is NULL or info is NULL\\n\");\n        return -1;\n    }\n\n    /* if we have a entry point then call to unload plugin */\n    if (pin->info->stop(inst)) {\n        PLUGIN_LOG(DEBUG, \"Start of %s failed\\n\", pin->plugin);\n        return -1;\n    }\n\n    return 0;\n}\n\nint\nplugin_find_by_name(const char *name)\n{\n    struct plugin *pin;\n\n    rte_rwlock_write_lock(RTE_PLUGIN_TAILQ_RWLOCK);\n\n    TAILQ_FOREACH (pin, &plugin_head, next) {\n        if (!strcmp(name, pin->plugin)) {\n            rte_rwlock_write_unlock(RTE_PLUGIN_TAILQ_RWLOCK);\n            return pin->inst;\n        }\n    }\n\n    rte_rwlock_write_unlock(RTE_PLUGIN_TAILQ_RWLOCK);\n\n    return -1;\n}\n\nint\nplugin_find_by_id(uint32_t h)\n{\n    struct plugin *pin;\n\n    rte_rwlock_write_lock(RTE_PLUGIN_TAILQ_RWLOCK);\n\n    TAILQ_FOREACH (pin, &plugin_head, next) {\n        if (h == pin->hash) {\n            int inst = plugin_find_inst(pin);\n            rte_rwlock_write_unlock(RTE_PLUGIN_TAILQ_RWLOCK);\n            return inst;\n        }\n    }\n\n    rte_rwlock_write_unlock(RTE_PLUGIN_TAILQ_RWLOCK);\n\n    return -1;\n}\n\nstatic void\n_plugin_dump(FILE *f, struct plugin *pin)\n{\n    if (!f)\n        f = stderr;\n\n    if (!pin) {\n        fprintf(f, \"*** Plugin pointer is NULL\\n\");\n        return;\n    }\n\n    fprintf(f, \"%08x %-16s %6d %s\\n\", pin->hash, pin->plugin, rte_atomic32_read(&pin->refcnt),\n            (pin->info) ? pin->info->desc : \"---\");\n}\n\nvoid\nplugin_dump(FILE *f)\n{\n    struct plugin *pin = NULL;\n    int cnt            = 0;\n\n    rte_rwlock_read_lock(RTE_PLUGIN_TAILQ_RWLOCK);\n\n    fprintf(f, \"**** Plugins ****\\n\");\n    fprintf(f, \"%-8s %-16s %-6s %s\\n\", \"ID\", \"Name\", \"refcnt\", \"Description\");\n    TAILQ_FOREACH (pin, &plugin_head, next) {\n        _plugin_dump(f, pin);\n        cnt++;\n    }\n    fprintf(f, \"Total plugins found: %d\\n\", cnt);\n\n    rte_rwlock_read_unlock(RTE_PLUGIN_TAILQ_RWLOCK);\n}\n\nint libplugin_logtype;\n\nRTE_INIT(libplugin_init_log)\n{\n    libplugin_logtype = rte_log_register(\"librte.plugin\");\n    if (libplugin_logtype >= 0)\n        rte_log_set_level(libplugin_logtype, RTE_LOG_INFO);\n}\n"
  },
  {
    "path": "lib/plugin/plugin.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2018-2026> Intel Corporation.\n */\n\n#ifndef _PLUGIN_H_\n#define _PLUGIN_H_\n\n#include <sys/types.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <sys/queue.h>\n\n#include <rte_common.h>\n#include <rte_log.h>\n#include <rte_tailq.h>\n#include <rte_atomic.h>\n\n/**\n * @file\n * PLUGIN\n *\n * The plugin library provides the ability to add and remove modules written\n * in C or other languages in a .so format.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define PLUGIN_MAX_INST 32\n\nextern int libplugin_logtype;\n#define PLUGIN_LOG(level, fmt, args...) \\\n    rte_log(RTE_LOG_##level, libplugin_logtype, \"%s(%d): \" fmt, __func__, getpid(), ##args)\n\n#define PLUGIN_MKVER(_M, _m, _p, _r) .major = _M, .minor = _m, .patch = _p, .release = _r\n\n#define PLUGIN_INFO(_n, _d, _f, _v)         \\\n    struct plugin_info _n##_plugin_info = { \\\n        .desc = _d, .start = _n##_start, .stop = _n##_stop, .pfuncs = (void *)_f, _v}\n\nstruct plugin_info {\n    const char *desc; /**< Short plugin description */\n\n    int (*start)(int inst, void *arg); /**< start function optional */\n    int (*stop)(int inst);             /**< stop function optional */\n    void *pfuncs;                      /**< plugin defined functions/info */\n\n    union {\n        uint32_t version; /* 18.04.00-rc1 == 18040001 */\n        struct {\n            uint8_t major; /**< Version of Plugin */\n            uint8_t minor;\n            uint8_t patch;\n            uint8_t release;\n        };\n    };\n};\n\nstruct plugin {\n    TAILQ_ENTRY(plugin) next; /**< Next plugin pointer */\n\n    char *plugin;             /**< short name of plugin <name>.do */\n    char *path;               /**< path to plugin */\n    void *dl;                 /**< dlopen handle pointer */\n    rte_atomic32_t refcnt;    /**< reference count */\n    uint32_t hash;            /**< Plugin ID value */\n    int inst;                 /**< Plugin instance ID value */\n    struct plugin_info *info; /**< Pointer to plugin info struct */\n};\n\n/**\n * Create a plugin instance by defining the name and path if needed.\n *\n * @param name\n *   The short name of the plugin minus the .so or .do, no path included.\n * @param path\n *   The path to the plugin if not in LD_LIBRARY_PATH environment variable.\n * @return\n *   the instance number or -1 if error\n */\nint plugin_create(char *name, char *path);\n\n/**\n * Destroy a given instance of a plugin\n *\n * @param inst\n *   The inst number for the plugin.\n * @return\n *    < 0 is error and 0 is success.\n */\nint plugin_destroy(int inst);\n\n/**\n * start the plugin\n *\n * @param inst\n *   The instance number\n * @param arg\n *   User defined value to the plugin on start up.\n * @return\n *   -1 on error or 0 if OK.\n */\nint plugin_start(int inst, void *arg);\n\n/**\n * stop the plugin\n *\n * @param inst\n *   The instance number\n * @return\n *   -1 on error or 0 if OK.\n */\nint plugin_stop(int inst);\n\n/**\n * Return the struct plugin pointer by instance index\n *\n * @param inst\n *    The instance index value\n * @return\n *    The struct rte+plugin pointer for the instance or NULL\n */\nstruct plugin *plugin_get(int inst);\n\n/**\n * Locate the plugin information by name.\n *\n * @param name\n *   The short name of the plugin to be found.\n * @return\n *   instance number or -1 on error\n */\nint plugin_find_by_name(const char *name);\n\n/**\n * Locate the plugin information by ID value.\n *\n * @param id\n *   ID of the plugin.\n * @return\n *   The instance number or -1 on error\n */\nint plugin_find_by_id(uint32_t id);\n\n/**\n * Dump out the list of plugins in the system.\n *\n * @param f\n *   The file pointer used to output the information, if NULL use stderr.\n */\nvoid plugin_dump(FILE *f);\n\n/**\n * return the description for the given instance id value\n *\n * @param pin\n *   The plugin_info pointer\n * @return\n *   The description string or NULL if not found.\n */\nstatic inline const char *\nplugin_desc(struct plugin *pin)\n{\n    if (pin && pin->info)\n        return pin->info->desc;\n\n    return NULL;\n}\n\n/**\n * return the plugin hash ID for the given instance id value\n *\n * @param pin\n *   The plugin data structure pointer\n * @param id\n *   Varable to put the id value.\n * @return\n *   -1 on error or 0 on success\n */\nstatic inline int\nplugin_get_id(struct plugin *pin, uint32_t *id)\n{\n    if (!pin || !id)\n        return -1;\n\n    *id = pin->hash;\n\n    return 0;\n}\n\n/**\n * Get the version word from the plugin information\n *\n * @param inst\n *    The instance index value\n * @return\n *    The 32bit unsigned version number of the plugin\n * @return\n *    -1 is error and 0 on success\n */\nstatic inline uint32_t\nplugin_get_version(int inst)\n{\n    struct plugin *pin = plugin_get(inst);\n\n    if (!pin || !pin->info)\n        return -1;\n\n    return pin->info->version;\n}\n\n/**\n * Get the struct plugin_info structure suppled by the plugin.\n *\n * @param inst\n *    The instance index value\n * @return\n *   NULL on error or pointer to info structure.\n */\nstatic inline struct plugin_info *\nplugin_get_info(int inst)\n{\n    struct plugin *pin = plugin_get(inst);\n\n    if (!pin)\n        return NULL;\n    return pin->info;\n}\n\n/**\n * Return a pointer to a global symbol in the plugin code.\n *\n * @param ping\n *   The instance of plugin structure\n * @param symbol_name\n *   The symbol string to search for in the plugin.\n * @param ret\n *   Place to put the symbol address if found.\n * @return\n *   -1 on error or 0 if success and ret contains the address.\n */\nint plugin_get_symbol(struct plugin *pin, const char *symbol_name, char **ret);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _PLUGIN_H_ */\n"
  },
  {
    "path": "lib/utils/_atoip.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n#include \"pg_strings.h\"\n#include \"_atoip.h\"\n\n/* isblank() needs _XOPEN_SOURCE >= 600 || _ISOC99_SOURCE, so use our own. */\nstatic int\nisblank2(char c)\n{\n    if (c == ' ' || c == '\\t')\n        return 1;\n    return 0;\n}\n\nstatic int\nisendofline(char c)\n{\n    if (c == '\\n' || c == '\\r')\n        return 1;\n    return 0;\n}\n\nstatic int\niscomment(char c)\n{\n    if (c == '#')\n        return 1;\n    return 0;\n}\n\nstatic int\nrte_isendoftoken(char c)\n{\n    if (!c || iscomment(c) || isblank2(c) || isendofline(c))\n        return 1;\n    return 0;\n}\n\n/*\n * Like inet_aton() but without all the hexadecimal and shorthand.\n * return:\n *      1 if `src' is a valid dotted quad, else 0.\n * notice:\n *      does not touch `dst' unless it's returning 1.\n * author:\n *      Paul Vixie, 1996.\n */\nstatic int\ninet_ipton4(const char *src, unsigned char *dst)\n{\n    static const char digits[] = \"0123456789\";\n    int saw_digit, octets, ch;\n    unsigned char tmp[RTE_INADDRSZ], *tp;\n\n    saw_digit   = 0;\n    octets      = 0;\n    *(tp = tmp) = 0;\n    while ((ch = *src++) != '\\0') {\n        const char *pch;\n\n        if ((pch = strchr(digits, ch)) != NULL) {\n            unsigned int new = *tp * 10 + (pch - digits);\n\n            if (new > 255)\n                return 0;\n            if (!saw_digit) {\n                if (++octets > 4)\n                    return 0;\n                saw_digit = 1;\n            }\n            *tp = (unsigned char)new;\n        } else if (ch == '.' && saw_digit) {\n            if (octets == 4)\n                return 0;\n            *++tp     = 0;\n            saw_digit = 0;\n        } else\n            return 0;\n    }\n    if (octets < 4)\n        return 0;\n\n    memcpy(dst, tmp, RTE_INADDRSZ);\n    return 1;\n}\n\n/*\n * Convert presentation level address to network order binary form.\n * return:\n *      1 if `src' is a valid [RFC1884 2.2] address, else 0.\n * notice:\n *      (1) does not touch `dst' unless it's returning 1.\n *      (2) :: in a full address is silently ignored.\n * credit:\n *      inspired by Mark Andrews.\n * author:\n *      Paul Vixie, 1996.\n */\nstatic int\ninet_ipton6(const char *src, unsigned char *dst)\n{\n    static const char xdigits_l[] = \"0123456789abcdef\", xdigits_u[] = \"0123456789ABCDEF\";\n    unsigned char tmp[RTE_IN6ADDRSZ], *tp = 0, *endp = 0, *colonp = 0;\n    const char *xdigits = 0, *curtok = 0;\n    int ch = 0, saw_xdigit = 0, count_xdigit = 0;\n    unsigned int val      = 0;\n    unsigned dbloct_count = 0;\n\n    memset((tp = tmp), '\\0', RTE_IN6ADDRSZ);\n    endp   = tp + RTE_IN6ADDRSZ;\n    colonp = NULL;\n    /* Leading :: requires some special handling. */\n    if (*src == ':')\n        if (*++src != ':')\n            return 0;\n    curtok     = src;\n    saw_xdigit = count_xdigit = 0;\n    val                       = 0;\n\n    while ((ch = *src++) != '\\0') {\n        const char *pch;\n\n        if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)\n            pch = strchr((xdigits = xdigits_u), ch);\n        if (pch != NULL) {\n            if (count_xdigit >= 4)\n                return 0;\n            val <<= 4;\n            val |= (pch - xdigits);\n            if (val > 0xffff)\n                return 0;\n            saw_xdigit = 1;\n            count_xdigit++;\n            continue;\n        }\n        if (ch == ':') {\n            curtok = src;\n            if (!saw_xdigit) {\n                if (colonp)\n                    return 0;\n                colonp = tp;\n                continue;\n            } else if (*src == '\\0')\n                return 0;\n            if (tp + sizeof(int16_t) > endp)\n                return 0;\n            *tp++        = (unsigned char)((val >> 8) & 0xff);\n            *tp++        = (unsigned char)(val & 0xff);\n            saw_xdigit   = 0;\n            count_xdigit = 0;\n            val          = 0;\n            dbloct_count++;\n            continue;\n        }\n        if (ch == '.' && ((tp + RTE_INADDRSZ) <= endp) && inet_ipton4(curtok, tp) > 0) {\n            tp += RTE_INADDRSZ;\n            saw_xdigit = 0;\n            dbloct_count += 2;\n            break; /* '\\0' was seen by inet_pton4(). */\n        }\n        return 0;\n    }\n    if (saw_xdigit) {\n        if (tp + sizeof(int16_t) > endp)\n            return 0;\n        *tp++ = (unsigned char)((val >> 8) & 0xff);\n        *tp++ = (unsigned char)(val & 0xff);\n        dbloct_count++;\n    }\n    if (colonp != NULL) {\n        /* if we already have 8 double octets, having a colon means error */\n        if (dbloct_count == 8)\n            return 0;\n\n        /* Use memmove()'s to handle overlapping regions. */\n        const int n = tp - colonp;\n\n        // Move n bytes from colonp to the end of tmp\n        memmove(endp - n, colonp, n);\n\n        // Clear original memory\n        memset(colonp, 0, n);\n\n        tp = endp;\n    }\n    if (tp != endp)\n        return 0;\n    memcpy(dst, tmp, RTE_IN6ADDRSZ);\n    return 1;\n}\n\n/*\n * Convert from presentation format (which usually means ASCII printable)\n *      to network format (which is usually some kind of binary format).\n * @return:\n *      1 if the address was valid for the specified address family\n *      0 if the address wasn't valid (`dst' is untouched in this case)\n *      -1 if some other error occurred (`dst' is untouched in this case, too)\n * author:\n *      Paul Vixie, 1996.\n */\nstatic int\ninet_ipton(int af, const char *src, void *dst)\n{\n    switch (af) {\n    case AF_INET:\n        return inet_ipton4(src, dst);\n    case AF_INET6:\n        return inet_ipton6(src, dst);\n    default:\n        errno = EAFNOSUPPORT;\n        return -1;\n    }\n    /* NOTREACHED */\n}\n\nint\n_atoip(const char *buf, int flags, void *res, unsigned ressize)\n{\n    unsigned int token_len = 0;\n    char ip_str[INET6_ADDRSTRLEN + 4 + 1]; /* '+4' is for prefixlen (if any) */\n    struct rte_ipaddr ipaddr;\n    char *prefix, *prefix_end;\n    long prefixlen         = 0;\n    int default_max_prefix = 0;\n\n    if (res && ressize < sizeof(struct rte_ipaddr))\n        return -1;\n\n    if (!buf || !*buf)\n        return -1;\n\n    while (!rte_isendoftoken(buf[token_len]))\n        token_len++;\n\n    /* if token is too big... */\n    if (token_len >= INET6_ADDRSTRLEN + 4)\n        return -1;\n\n    snprintf(ip_str, token_len + 1, \"%s\", buf);\n\n    /* convert the network prefix */\n    if (flags & RTE_IPADDR_NETWORK) {\n        prefix = strrchr(ip_str, '/');\n        if (prefix == NULL) {\n            default_max_prefix = 1;\n        } else {\n            *prefix = '\\0';\n            prefix++;\n            errno     = 0;\n            prefixlen = strtol(prefix, &prefix_end, 10);\n            if (errno || (*prefix_end != '\\0') || prefixlen < 0 || prefixlen > RTE_PREFIXMAX)\n                return -1;\n            ipaddr.prefixlen = prefixlen;\n        }\n    } else\n        ipaddr.prefixlen = 0;\n\n    /* convert the IP addr */\n    if (inet_ipton(AF_INET, ip_str, &ipaddr.ipv4) == 1 && prefixlen <= RTE_V4PREFIXMAX) {\n        if (default_max_prefix)\n            ipaddr.prefixlen = RTE_V4PREFIXMAX;\n        ipaddr.family = AF_INET;\n        if (res)\n            memcpy(res, &ipaddr, sizeof(ipaddr));\n        return 4;\n    } else if (inet_ipton(AF_INET6, ip_str, &ipaddr.ipv6) == 1) {\n        ipaddr.family = AF_INET6;\n        if (default_max_prefix)\n            ipaddr.prefixlen = RTE_PREFIXMAX;\n        if (res)\n            memcpy(res, &ipaddr, sizeof(ipaddr));\n        return 6;\n    }\n    return -1;\n}\n"
  },
  {
    "path": "lib/utils/_atoip.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/**\n * @file\n *\n * String-related utility functions for IP addresses\n */\n\n#ifndef __ATOIP_H_\n#define __ATOIP_H_\n\n#include <netinet/in.h>\n\n#include <rte_string_fns.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define RTE_IPADDR_V4      0x01 /**< Flag: address is IPv4 */\n#define RTE_IPADDR_V6      0x02 /**< Flag: address is IPv6 */\n#define RTE_IPADDR_NETWORK 0x04 /**< Flag: parse as network/prefix notation */\n\n#define RTE_INADDRSZ    4   /**< Size of an IPv4 address in bytes */\n#define RTE_IN6ADDRSZ   16  /**< Size of an IPv6 address in bytes */\n#define RTE_PREFIXMAX   128 /**< Maximum IPv6 prefix length in bits */\n#define RTE_V4PREFIXMAX 32  /**< Maximum IPv4 prefix length in bits */\n\n/** Holds a parsed IPv4 or IPv6 address, optionally with a prefix length. */\nstruct rte_ipaddr {\n    uint8_t family; /**< Address family: AF_INET or AF_INET6 */\n    union {\n        struct in_addr ipv4;       /**< IPv4 address (when family == AF_INET) */\n        struct rte_ipv6_addr ipv6; /**< IPv6 address (when family == AF_INET6) */\n    };\n    unsigned int prefixlen; /**< Network prefix length in bits (0 if not a network address) */\n};\n\n/**\n * Convert an IPv4/v6 address into a binary value.\n *\n * @param buf\n *   Location of string to convert\n * @param flags\n *   Set of flags for converting IPv4/v6 addresses and netmask.\n * @param res\n *   Location to put the results\n * @param ressize\n *   Length of res in bytes.\n * @return\n *   4 or 6 on OK, indicating an IPv4/v6 address, respectively, and -1 on error\n */\nint _atoip(const char *buf, int flags, void *res, unsigned ressize);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __ATOIP_H_ */\n"
  },
  {
    "path": "lib/utils/heap.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n#include <stdio.h>\n#include <sys/queue.h>\n#include <pthread.h>\n\n#include \"heap.h\"\n\nheap_t *\nheap_create(void *addr, size_t size)\n{\n    heap_t *heap = NULL;\n    heap_entry_t *entry;\n\n    /* Make sure the size is greater or equal to sizeof(rte_heap_entry_t) */\n    if (size < sizeof(heap_entry_t))\n        return NULL;\n\n    heap = calloc(1, sizeof(heap_t));\n    if (!heap)\n        return NULL;\n\n    pthread_spin_init(&heap->sl, PTHREAD_PROCESS_PRIVATE);\n\n    heap->addr        = addr;\n    heap->total_space = size;\n\n    STAILQ_INIT(&heap->list);\n\n    entry                 = addr;\n    entry->next.stqe_next = NULL;\n    entry->size           = size;\n\n    STAILQ_INSERT_TAIL(&heap->list, entry, next);\n\n    return heap;\n}\n\n/******************************************************************************\n * simpleMemDestroy - Destroy a heap.\n *\n *   heap - is the free heap structure pointer.\n */\nint\nheap_destroy(heap_t *heap)\n{\n    /* Free the rte_heap_t structure */\n    if (heap)\n        free(heap);\n\n    return 0;\n}\n\nint\nheap_free(heap_t *heap, void *addr, size_t size)\n{\n    heap_entry_t *p;\n    heap_entry_t *q;\n\n    /* the size can not be zero */\n    if (!heap || !addr || size == 0)\n        return -1;\n\n    pthread_spin_lock(&heap->sl);\n\n    p = addr;\n\n    p->next.stqe_next = NULL;\n    p->size           = size;\n\n    STAILQ_FOREACH (q, &heap->list, next) {\n        /* insert into ascending order */\n        if (p < q->next.stqe_next)\n            break;\n    }\n\n    if (q) {\n        if (RTE_PTR_ADD(p, size) == q->next.stqe_next) {\n            p->size += q->size;\n            p->next.stqe_next = q->next.stqe_next->next.stqe_next;\n        } else /* non contiguous regions insert p into list */\n            p->next.stqe_next = q->next.stqe_next;\n\n        /* join to lower element */\n        if (RTE_PTR_ADD(q, q->size) == p) {\n            /* add size of prev region */\n            q->size += p->size;\n\n            /* point to p->next region */\n            q->next.stqe_next = p->next.stqe_next;\n        } else /* not contiguous regions  insert p into list */\n            q->next.stqe_next = p;\n    } else\n        STAILQ_INSERT_TAIL(&heap->list, p, next);\n\n    pthread_spin_unlock(&heap->sl);\n\n    return 0;\n}\n\n/******************************************************************************\n * simpleMemAlloc: allocate space for a tlv structure and data.\n *\n *   This routine will allocate a contiguous amount of memory\n *   of size (size) from the free list.\n *\n *   This routine will search a linked list for the first\n *   fit free space.\n *\n *   This routine will return a null pointer if the contiguous\n *   free space is not found.\n *\n *   heap - is the pointer to the heap structure.\n *   size - is the size of the requested memory.\n */\nvoid *\nheap_alloc(heap_t *heap, size_t size)\n{\n    heap_entry_t *hd;      /* pointer to entry free space */\n    heap_entry_t *phd;     /* prev head pointer to free list */\n    heap_entry_t *nxt_hd;  /* temp space for pointer to entry */\n    heap_entry_t *ret_ptr; /* return pointer to free space */\n\n    if (!heap || size == 0)\n        return NULL;\n\n    pthread_spin_lock(&heap->sl);\n\n    size = RTE_ALIGN_CEIL(size, sizeof(heap_entry_t));\n\n    /*\n     * If the requested size is less then sizeof(rte_heap_entry_t) then set\n     * the requested size to sizeof(rte_heap_entry_t)\n     */\n    if (size < sizeof(heap_entry_t))\n        size = sizeof(heap_entry_t);\n\n    ret_ptr = NULL;\n    hd      = STAILQ_FIRST(&heap->list);\n    phd     = (heap_entry_t *)&heap->list;\n    if (hd) {\n        if (size <= heap->total_space) {\n            do {\n                /*\n                 * if current free section size is equal to size then\n                 * point prev head.\n                 */\n                if (size == hd->size) {\n                    /* take size out of total free space */\n                    heap->total_space -= size;\n\n                    /* to entry free section in list. */\n                    phd->next.stqe_next = hd->next.stqe_next;\n\n                    ret_ptr = hd;\n                    break;\n                }\n                /*\n                 * section is larger than size build a new rte_heap_entry_t structure\n                 * in the left over space in this section.\n                 * nxt_hd is the new pointer to the structure after size.\n                 */\n                if (size <= (hd->size - sizeof(heap_entry_t))) {\n                    /* take size out of total free space */\n                    heap->total_space -= size;\n\n                    /* current entry is now moved */\n                    nxt_hd                 = RTE_PTR_ADD(hd, size);\n                    nxt_hd->next.stqe_next = hd->next.stqe_next;\n                    nxt_hd->size           = hd->size - size;\n                    phd->next.stqe_next    = nxt_hd;\n\n                    ret_ptr = hd;\n                    break;\n                }\n                phd = hd;                 /* Prev head = current head */\n                hd  = hd->next.stqe_next; /* entry free space */\n            } while (hd);\n        }\n    }\n\n    pthread_spin_unlock(&heap->sl);\n\n    return ret_ptr;\n}\n\n/******************************************************************************\n * simpleMemMalloc - Wrapper routine for simpleMemAlloc insert size into buffer.\n */\nvoid *\nheap_malloc(heap_t *heap, size_t size)\n{\n    uint64_t *addr;\n\n    if (!heap || size == 0)\n        return NULL;\n\n    if (size < sizeof(heap_entry_t))\n        size = sizeof(heap_entry_t);\n\n    addr = (uint64_t *)heap_alloc(heap, size + sizeof(uint64_t));\n    if (addr == NULL)\n        return NULL;\n\n    addr[0] = size + sizeof(uint64_t);\n\n    return &addr[1];\n}\n\n/******************************************************************************\n * simpleMemMFree - Wrapper routine for simpleMemFree extract size from buffer.\n */\nint\nheap_mfree(heap_t *heap, void *addr)\n{\n    uint64_t *p;\n    uint64_t size;\n\n    if (heap == NULL)\n        return -1;\n\n    if (addr == NULL)\n        return 0;\n\n    if (((uint64_t)addr & 3) != 0)\n        return -1;\n\n    p = (uint64_t *)addr;\n    p--;\n\n    size = *p;\n    if (size == 0)\n        return -1;\n\n    return heap_free(heap, p, size);\n}\n\n/******************************************************************************\n * debug function\n */\nvoid\nheap_dump(FILE *f, heap_t *heap)\n{\n    heap_entry_t *p;\n    uint64_t total;\n    uint64_t largest;\n    uint64_t segCount;\n\n    if (!f)\n        f = stdout;\n\n    if (!heap) {\n        fprintf(f, \"Pointer to rte_heap_entry_t structure is NULL\\n\");\n        return;\n    }\n\n    fprintf(f, \"  Free Header       = %p\\n\", (void *)heap);\n    fprintf(f, \"  Address of Heap   = %p\\n\", heap->addr);\n    fprintf(f, \"  Total free space  = %lu\\n\", heap->total_space);\n\n    total    = 0;\n    largest  = 0;\n    segCount = 0;\n\n    STAILQ_FOREACH (p, &heap->list, next) {\n        fprintf(f, \"    Size            = %ld\\n\", p->size);\n        total += p->size;\n        if (p->size > largest)\n            largest = p->size;\n        segCount++;\n    }\n    fprintf(f, \"  Total Free        = %ld\\n\", total);\n    fprintf(f, \"  Largest Free area = %ld\\n\", largest);\n    fprintf(f, \"  Segment Count     = %ld\\n\", segCount);\n}\n"
  },
  {
    "path": "lib/utils/heap.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n#ifndef __HEAP_H\n#define __HEAP_H\n\n/**\n * @file\n *\n * Fixed-size heap allocator backed by a caller-supplied memory region.\n *\n * The heap manages a contiguous block of memory supplied at creation time.\n * Allocations and frees are protected by a spinlock.  The implementation\n * is intended for use in low-latency DPDK data-plane paths where dynamic\n * system allocators are undesirable.\n */\n\n#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <inttypes.h>\n#include <sys/queue.h>\n#include <pthread.h>\n\n#include <rte_common.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct heap_entry {\n    STAILQ_ENTRY(heap_entry) next; /**< pointer to next entry */\n    size_t size;                   /**< size of free entry */\n} heap_entry_t;\n\ntypedef struct heap {\n    STAILQ_HEAD(, heap_entry) list; /**< Heap entry list */\n    void *addr;                     /**< Base Heap address pointer */\n    size_t total_space;             /**< total space in heap */\n    pthread_spinlock_t sl;          /**< Spinlock for this heap */\n} heap_t;\n\n/**\n * Create a heap over a caller-supplied memory region.\n *\n * @param addr\n *   Base address of the memory region to manage.\n * @param size\n *   Total size of the region in bytes.\n * @return\n *   Pointer to the new heap_t descriptor, or NULL on failure.\n */\nheap_t *heap_create(void *addr, size_t size);\n\n/**\n * Destroy a heap and release the heap_t descriptor.\n *\n * The underlying memory region is NOT freed; the caller is responsible for\n * managing the lifetime of the memory passed to heap_create().\n *\n * @param si\n *   Heap to destroy.\n * @return\n *   0 on success, -1 on error.\n */\nint heap_destroy(heap_t *si);\n\n/**\n * Allocate @p size bytes from heap @p si.\n *\n * The caller must track @p size to pass to heap_free().\n *\n * @param si\n *   Heap to allocate from.\n * @param size\n *   Number of bytes to allocate.\n * @return\n *   Pointer to the allocated region, or NULL if the heap has insufficient space.\n */\nvoid *heap_alloc(heap_t *si, size_t size);\n\n/**\n * Return a previously allocated block back to heap @p si.\n *\n * @param si\n *   Heap that owns the block.\n * @param addr\n *   Pointer returned by heap_alloc().\n * @param size\n *   Original allocation size passed to heap_alloc().\n * @return\n *   0 on success, -1 on error.\n */\nint heap_free(heap_t *si, void *addr, size_t size);\n\n/**\n * Allocate @p size bytes from @p si, storing the size internally for later use by heap_mfree().\n *\n * @param si\n *   Heap to allocate from.\n * @param size\n *   Number of bytes to allocate.\n * @return\n *   Pointer to the allocated region, or NULL on failure.\n */\nvoid *heap_malloc(heap_t *si, size_t size);\n\n/**\n * Free a block allocated with heap_malloc().\n *\n * The allocation size is read from internal metadata; the caller does not\n * need to track it separately (unlike heap_free()).\n *\n * @param si\n *   Heap that owns the block.\n * @param addr\n *   Pointer returned by heap_malloc().\n * @return\n *   0 on success, -1 on error.\n */\nint heap_mfree(heap_t *si, void *addr);\n\n/**\n * Dump a human-readable description of the heap's free-list to @p f.\n *\n * @param f    Output file (e.g. stdout or stderr).\n * @param si   Heap to dump.\n */\nvoid heap_dump(FILE *f, heap_t *si);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __HEAP_H */\n"
  },
  {
    "path": "lib/utils/inet_pton.c",
    "content": "/*\n * Copyright(c) 2004 by Internet Systems Consortium, Inc. (\"ISC\")\n * Copyright(c) 1996,1999 by Internet Software Consortium.\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND ISC DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT\n * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#if defined(LIBC_SCCS) && !defined(lint)\nstatic const char rcsid[] = \"$Id: inet_pton.c,v 1.3.18.2 2005/07/28 07:38:07 marka Exp $\";\n#endif /* LIBC_SCCS and not lint */\n#include <sys/cdefs.h>\n// __FBSDID(\"$FreeBSD$\");\n\n#include <sys/param.h>\n#include <sys/socket.h>\n// #include <sys/systm.h>\n#include <string.h>\n#include <arpa/inet.h>\n\n#include <netinet/in.h>\n\n// #if __FreeBSD_version < 700000\n// #define strchr index\n// #endif\n\n/*%\n * WARNING: Don't even consider trying to compile this on a system where\n * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.\n */\n\nstatic int inet_pton4(const char *src, u_char *dst);\nstatic int inet_pton6(const char *src, u_char *dst);\n\n/* int\n * inet_pton(af, src, dst)\n *\tconvert from presentation format (which usually means ASCII printable)\n *\tto network format (which is usually some kind of binary format).\n * return:\n *\t1 if the address was valid for the specified address family\n *\t0 if the address wasn't valid (`dst' is untouched in this case)\n *\t-1 if some other error occurred (`dst' is untouched in this case, too)\n * author:\n *\tPaul Vixie, 1996.\n */\nint\ninet_pton(int af, const char *src, void *dst)\n{\n    switch (af) {\n    case AF_INET:\n        return (inet_pton4(src, dst));\n    case AF_INET6:\n        return (inet_pton6(src, dst));\n    default:\n        return (-1);\n    }\n    /* NOTREACHED */\n}\n\n/* int\n * inet_pton4(src, dst)\n *\tlike inet_aton() but without all the hexadecimal and shorthand.\n * return:\n *\t1 if `src' is a valid dotted quad, else 0.\n * notice:\n *\tdoes not touch `dst' unless it's returning 1.\n * author:\n *\tPaul Vixie, 1996.\n */\nstatic int\ninet_pton4(const char *src, u_char *dst)\n{\n    static const char digits[] = \"0123456789\";\n    int saw_digit, octets, ch;\n#define NS_INADDRSZ 4\n    u_char tmp[NS_INADDRSZ], *tp;\n\n    saw_digit   = 0;\n    octets      = 0;\n    *(tp = tmp) = 0;\n    while ((ch = *src++) != '\\0') {\n        const char *pch;\n\n        if ((pch = strchr(digits, ch)) != NULL) {\n            u_int new = *tp * 10 + (pch - digits);\n\n            if (saw_digit && *tp == 0)\n                return (0);\n            if (new > 255)\n                return (0);\n            *tp = new;\n            if (!saw_digit) {\n                if (++octets > 4)\n                    return (0);\n                saw_digit = 1;\n            }\n        } else if (ch == '.' && saw_digit) {\n            if (octets == 4)\n                return (0);\n            *++tp     = 0;\n            saw_digit = 0;\n        } else\n            return (0);\n    }\n    if (octets < 4)\n        return (0);\n    memcpy(dst, tmp, NS_INADDRSZ);\n    return (1);\n}\n\n/* int\n * inet_pton6(src, dst)\n *\tconvert presentation level address to network order binary form.\n * return:\n *\t1 if `src' is a valid [RFC1884 2.2] address, else 0.\n * notice:\n *\t(1) does not touch `dst' unless it's returning 1.\n *\t(2) :: in a full address is silently ignored.\n * credit:\n *\tinspired by Mark Andrews.\n * author:\n *\tPaul Vixie, 1996.\n */\nstatic int\ninet_pton6(const char *src, u_char *dst)\n{\n    static const char xdigits_l[] = \"0123456789abcdef\", xdigits_u[] = \"0123456789ABCDEF\";\n#define NS_IN6ADDRSZ 16\n#define NS_INT16SZ   2\n    u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;\n    const char *xdigits, *curtok;\n    int ch, seen_xdigits;\n    u_int val;\n\n    memset((tp = tmp), '\\0', NS_IN6ADDRSZ);\n    endp   = tp + NS_IN6ADDRSZ;\n    colonp = NULL;\n    /* Leading :: requires some special handling. */\n    if (*src == ':')\n        if (*++src != ':')\n            return (0);\n    curtok       = src;\n    seen_xdigits = 0;\n    val          = 0;\n    while ((ch = *src++) != '\\0') {\n        const char *pch;\n\n        if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)\n            pch = strchr((xdigits = xdigits_u), ch);\n        if (pch != NULL) {\n            val <<= 4;\n            val |= (pch - xdigits);\n            if (++seen_xdigits > 4)\n                return (0);\n            continue;\n        }\n        if (ch == ':') {\n            curtok = src;\n            if (!seen_xdigits) {\n                if (colonp)\n                    return (0);\n                colonp = tp;\n                continue;\n            } else if (*src == '\\0') {\n                return (0);\n            }\n            if (tp + NS_INT16SZ > endp)\n                return (0);\n            *tp++        = (u_char)(val >> 8) & 0xff;\n            *tp++        = (u_char)val & 0xff;\n            seen_xdigits = 0;\n            val          = 0;\n            continue;\n        }\n        if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) {\n            tp += NS_INADDRSZ;\n            seen_xdigits = 0;\n            break; /*%< '\\\\0' was seen by inet_pton4(). */\n        }\n        return (0);\n    }\n    if (seen_xdigits) {\n        if (tp + NS_INT16SZ > endp)\n            return (0);\n        *tp++ = (u_char)(val >> 8) & 0xff;\n        *tp++ = (u_char)val & 0xff;\n    }\n    if (colonp != NULL) {\n        /*\n         * Since some memmove()'s erroneously fail to handle\n         * overlapping regions, we'll do the shift by hand.\n         */\n        const int n = tp - colonp;\n        int i;\n\n        if (tp == endp)\n            return (0);\n        for (i = 1; i <= n; i++) {\n            endp[-i]      = colonp[n - i];\n            colonp[n - i] = 0;\n        }\n        tp = endp;\n    }\n    if (tp != endp)\n        return (0);\n    memcpy(dst, tmp, NS_IN6ADDRSZ);\n    return (1);\n}\n\n/*! \\file */\n"
  },
  {
    "path": "lib/utils/meson.build",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2020-2026> Intel Corporation\n\nsources = files(\n\t'_atoip.c',\n\t'portlist.c',\n\t'heap.c')\nlibutils = library('utils', sources,\n\tdependencies: [common, dpdk])\nutils = declare_dependency(link_with: libutils,\n\tinclude_directories: include_directories('.'))\n"
  },
  {
    "path": "lib/utils/parson_json.c",
    "content": "/*\n   Parson ( http://kgabis.github.com/parson/ )\n   Copyright(c) 2012 - 2017 Krzysztof Gabis\n\n   Permission is hereby granted, free of charge, to any person obtaining a copy\n   of this software and associated documentation files (the \"Software\"), to deal\n   in the Software without restriction, including without limitation the rights\n   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n   copies of the Software, and to permit persons to whom the Software is\n   furnished to do so, subject to the following conditions:\n\n   The above copyright notice and this permission notice shall be included in\n   all copies or substantial portions of the Software.\n\n   THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n   THE SOFTWARE.\n */\n#ifdef _MSC_VER\n#ifndef _CRT_SECURE_NO_WARNINGS\n#define _CRT_SECURE_NO_WARNINGS\n#endif /* _CRT_SECURE_NO_WARNINGS */\n#endif /* _MSC_VER */\n\n#include \"parson_json.h\"\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <math.h>\n#include <errno.h>\n\n#include <rte_string_fns.h>\n\n/* Apparently sscanf is not implemented in some \"standard\" libraries, so don't use it, if you\n * don't have to. */\n#define sscanf THINK_TWICE_ABOUT_USING_SSCANF\n\n#define STARTING_CAPACITY 16\n#define MAX_NESTING       2048\n\n#define FLOAT_FORMAT   \"%1.17g\" /* do not increase precision without incresing NUM_BUF_SIZE */\n#define UINT64_FORMAT  \"%lx\"\n#define INTEGER_FORMAT \"%d\"\n#define NUM_BUF_SIZE                                                                              \\\n    64 /* double printed with \"%1.17g\" shouldn't be longer than 25 bytes so let's be paranoid and \\\n          use 64 */\n\n#define SIZEOF_TOKEN(a) (sizeof(a) - 1)\n#define SKIP_CHAR(str)  ((*str)++)\n#define SKIP_WHITESPACES(str)                 \\\n    while (isspace((unsigned char)(**str))) { \\\n        SKIP_CHAR(str);                       \\\n    }\n#define MAX(a, b) ((a) > (b) ? (a) : (b))\n\n#undef malloc\n#undef free\n\n#if defined(isnan) && defined(isinf)\n#define IS_NUMBER_INVALID(x) (isnan((x)) || isinf((x)))\n#else\n#define IS_NUMBER_INVALID(x) (((x) * 0.0) != 0.0)\n#endif\n\nstatic JSON_Malloc_Function parson_malloc = malloc;\nstatic JSON_Free_Function parson_free     = free;\n\n#define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */\n\n/* Type definitions */\ntypedef union json_value_value {\n    char *string;\n    double number;\n    uint64_t u64;\n    JSON_Object *object;\n    JSON_Array *array;\n    int i32;\n    int boolean;\n    int null;\n} JSON_Value_Value;\n\nstruct json_value_t {\n    JSON_Value *parent;\n    JSON_Value_Type type;\n    JSON_Value_Value value;\n};\n\nstruct json_object_t {\n    JSON_Value *wrapping_value;\n    char **names;\n    JSON_Value **values;\n    size_t count;\n    size_t capacity;\n};\n\nstruct json_array_t {\n    JSON_Value *wrapping_value;\n    JSON_Value **items;\n    size_t count;\n    size_t capacity;\n};\n\n/* Various */\nstatic char *read_file(const char *filename);\nstatic void remove_comments(char *string);\nstatic char *parson_strndup(const char *string, size_t n);\nstatic char *parson_strdup(const char *string);\nstatic int hex_char_to_int(char c);\nstatic int parse_utf16_hex(const char *string, unsigned int *result);\nstatic int num_bytes_in_utf8_sequence(unsigned char c);\nstatic int verify_utf8_sequence(const unsigned char *string, int *len);\nstatic int is_valid_utf8(const char *string, size_t string_len);\nstatic int is_decimal(const char *string, size_t length);\n\n/* JSON Object */\nstatic JSON_Object *json_object_new(JSON_Value *wrapping_value);\nstatic JSON_Status json_object_add(JSON_Object *object, const char *name, JSON_Value *value);\nstatic JSON_Status json_object_addn(JSON_Object *object, const char *name, size_t name_len,\n                                    JSON_Value *value);\nstatic JSON_Status json_object_resize(JSON_Object *object, size_t new_capacity);\nstatic JSON_Value *json_object_getn_value(const JSON_Object *object, const char *name,\n                                          size_t name_len);\nstatic JSON_Status json_object_remove_internal(JSON_Object *object, const char *name,\n                                               int free_value);\nstatic JSON_Status json_object_dotremove_internal(JSON_Object *object, const char *name,\n                                                  int free_value);\nstatic void json_object_free(JSON_Object *object);\n\n/* JSON Array */\nstatic JSON_Array *json_array_init(JSON_Value *wrapping_value);\nstatic JSON_Status json_array_add(JSON_Array *array, JSON_Value *value);\nstatic JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity);\nstatic void json_array_free(JSON_Array *array);\n\n/* JSON Value */\nstatic JSON_Value *json_value_new_string_no_copy(char *string);\n\n/* Parser */\nstatic JSON_Status skip_quotes(const char **string);\nstatic int parse_utf16(const char **unprocessed, char **processed);\nstatic char *process_string(const char *input, size_t len);\nstatic char *get_quoted_string(const char **string);\nstatic JSON_Value *parse_object_value(const char **string, size_t nesting);\nstatic JSON_Value *parse_array_value(const char **string, size_t nesting);\nstatic JSON_Value *parse_string_value(const char **string);\nstatic JSON_Value *parse_boolean_value(const char **string);\nstatic JSON_Value *parse_number_value(const char **string);\nstatic JSON_Value *parse_uint64_value(const char **string);\nstatic JSON_Value *parse_integer_value(const char **string);\nstatic JSON_Value *parse_null_value(const char **string);\nstatic JSON_Value *parse_value(const char **string, size_t nesting);\n\n/* Serialization */\nstatic int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, int is_pretty,\n                                      char *num_buf);\nstatic int json_serialize_string(const char *string, char *buf);\nstatic int append_indent(char *buf, int level);\nstatic int append_string(char *buf, const char *string);\n\n/* Various */\nstatic char *\nparson_strndup(const char *string, size_t n)\n{\n    char *output_string = (char *)parson_malloc(n + 1);\n\n    if (!output_string)\n        return NULL;\n\n    output_string[n] = '\\0';\n    rte_strlcpy(output_string, string, n);\n    return output_string;\n}\n\nstatic char *\nparson_strdup(const char *string)\n{\n    return parson_strndup(string, strlen(string));\n}\n\nstatic int\nhex_char_to_int(char c)\n{\n    if (c >= '0' && c <= '9')\n        return c - '0';\n    else if (c >= 'a' && c <= 'f')\n        return c - 'a' + 10;\n    else if (c >= 'A' && c <= 'F')\n        return c - 'A' + 10;\n\n    return -1;\n}\n\nstatic int\nparse_utf16_hex(const char *s, unsigned int *result)\n{\n    int x1, x2, x3, x4;\n\n    if (s[0] == '\\0' || s[1] == '\\0' || s[2] == '\\0' || s[3] == '\\0')\n        return 0;\n\n    x1 = hex_char_to_int(s[0]);\n    x2 = hex_char_to_int(s[1]);\n    x3 = hex_char_to_int(s[2]);\n    x4 = hex_char_to_int(s[3]);\n    if (x1 == -1 || x2 == -1 || x3 == -1 || x4 == -1)\n        return 0;\n\n    *result = (unsigned int)((x1 << 12) | (x2 << 8) | (x3 << 4) | x4);\n    return 1;\n}\n\nstatic int\nnum_bytes_in_utf8_sequence(unsigned char c)\n{\n    if (c == 0xC0 || c == 0xC1 || c > 0xF4 || IS_CONT(c))\n        return 0;\n    else if ((c & 0x80) == 0) /* 0xxxxxxx */\n        return 1;\n    else if ((c & 0xE0) == 0xC0) /* 110xxxxx */\n        return 2;\n    else if ((c & 0xF0) == 0xE0) /* 1110xxxx */\n        return 3;\n    else if ((c & 0xF8) == 0xF0) /* 11110xxx */\n        return 4;\n\n    return 0; /* won't happen */\n}\n\nstatic int\nverify_utf8_sequence(const unsigned char *string, int *len)\n{\n    unsigned int cp = 0;\n\n    *len = num_bytes_in_utf8_sequence(string[0]);\n\n    if (*len == 1)\n        cp = string[0];\n    else if (*len == 2 && IS_CONT(string[1])) {\n        cp = string[0] & 0x1F;\n        cp = (cp << 6) | (string[1] & 0x3F);\n    } else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) {\n        cp = ((unsigned char)string[0]) & 0xF;\n        cp = (cp << 6) | (string[1] & 0x3F);\n        cp = (cp << 6) | (string[2] & 0x3F);\n    } else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) {\n        cp = string[0] & 0x7;\n        cp = (cp << 6) | (string[1] & 0x3F);\n        cp = (cp << 6) | (string[2] & 0x3F);\n        cp = (cp << 6) | (string[3] & 0x3F);\n    } else\n        return 0;\n\n    /* overlong encodings */\n    if ((cp < 0x80 && *len > 1) || (cp < 0x800 && *len > 2) || (cp < 0x10000 && *len > 3))\n        return 0;\n\n    /* invalid unicode */\n    if (cp > 0x10FFFF)\n        return 0;\n\n    /* surrogate halves */\n    if (cp >= 0xD800 && cp <= 0xDFFF)\n        return 0;\n\n    return 1;\n}\n\nstatic int\nis_valid_utf8(const char *string, size_t string_len)\n{\n    int len                = 0;\n    const char *string_end = string + string_len;\n\n    while (string < string_end) {\n        if (!verify_utf8_sequence((const unsigned char *)string, &len))\n            return 0;\n\n        string += len;\n    }\n    return 1;\n}\n\nstatic int\nis_decimal(const char *string, size_t length)\n{\n    if (length > 1 && string[0] == '0' && string[1] != '.')\n        return 0;\n\n    if (length > 2 && !strncmp(string, \"-0\", 2) && string[2] != '.')\n        return 0;\n\n    while (length--)\n        if (strchr(\"xX\", string[length]))\n            return 0;\n\n    return 1;\n}\n\nstatic char *\nread_file(const char *filename)\n{\n    FILE *fp            = fopen(filename, \"r\");\n    size_t size_to_read = 0;\n    size_t size_read    = 0;\n    long pos;\n    char *file_contents;\n\n    if (!fp)\n        return NULL;\n\n    fseek(fp, 0L, SEEK_END);\n    pos = ftell(fp);\n    if (pos < 0) {\n        fclose(fp);\n        return NULL;\n    }\n    size_to_read = pos;\n    rewind(fp);\n    file_contents = (char *)parson_malloc(sizeof(char) * (size_to_read + 1));\n    if (!file_contents) {\n        fclose(fp);\n        return NULL;\n    }\n    size_read = fread(file_contents, 1, size_to_read, fp);\n    if (size_read == 0 || ferror(fp)) {\n        fclose(fp);\n        parson_free(file_contents);\n        return NULL;\n    }\n    fclose(fp);\n    file_contents[size_read] = '\\0';\n    return file_contents;\n}\n\nstatic void\nremove_comments(char *string)\n{\n    int in_string = 0, escaped = 0;\n    char *ptr, current_char;\n\n    while ((current_char = *string) != '\\0') {\n        if (current_char == '\\\\' && !escaped) {\n            escaped = 1;\n            string++;\n            continue;\n        } else if (current_char == '\\\"' && !escaped)\n            in_string = !in_string;\n        else if (!in_string) {\n            if (strncmp(string, \"/*\", 2) == 0) {\n                ptr = strstr(string + 2, \"*/\");\n                if (!ptr)\n                    return;\n\n                ptr += 2;\n                while (string != ptr)\n                    *string++ = ' ';\n            } else if (strncmp(string, \"//\", 2) == 0)\n                while (*string != '\\n')\n                    *string++ = ' ';\n        }\n        escaped = 0;\n        string++;\n    }\n}\n\n/* JSON Object */\nstatic JSON_Object *\njson_object_new(JSON_Value *wrapping_value)\n{\n    JSON_Object *new_obj = (JSON_Object *)parson_malloc(sizeof(JSON_Object));\n\n    if (new_obj == NULL)\n        return NULL;\n\n    new_obj->wrapping_value = wrapping_value;\n    new_obj->names          = (char **)NULL;\n    new_obj->values         = (JSON_Value **)NULL;\n    new_obj->capacity       = 0;\n    new_obj->count          = 0;\n    return new_obj;\n}\n\nstatic JSON_Status\njson_object_add(JSON_Object *object, const char *name, JSON_Value *value)\n{\n    if (name == NULL)\n        return JSONFailure;\n\n    return json_object_addn(object, name, strlen(name), value);\n}\n\nstatic JSON_Status\njson_object_addn(JSON_Object *object, const char *name, size_t name_len, JSON_Value *value)\n{\n    size_t index = 0;\n\n    if (object == NULL || name == NULL || value == NULL)\n        return JSONFailure;\n\n    if (json_object_getn_value(object, name, name_len) != NULL)\n        return JSONFailure;\n\n    if (object->count >= object->capacity) {\n        size_t new_capacity = MAX(object->capacity * 2, STARTING_CAPACITY);\n        if (json_object_resize(object, new_capacity) == JSONFailure)\n            return JSONFailure;\n    }\n    index                = object->count;\n    object->names[index] = parson_strndup(name, name_len);\n    if (object->names[index] == NULL)\n        return JSONFailure;\n\n    value->parent         = json_object_get_wrapping_value(object);\n    object->values[index] = value;\n    object->count++;\n    return JSONSuccess;\n}\n\nstatic JSON_Status\njson_object_resize(JSON_Object *object, size_t new_capacity)\n{\n    char **temp_names        = NULL;\n    JSON_Value **temp_values = NULL;\n\n    if ((object->names == NULL && object->values != NULL) ||\n        (object->names != NULL && object->values == NULL) || new_capacity == 0)\n        return JSONFailure; /* Shouldn't happen */\n\n    temp_names = (char **)parson_malloc(new_capacity * sizeof(char *));\n    if (temp_names == NULL)\n        return JSONFailure;\n\n    temp_values = (JSON_Value **)parson_malloc(new_capacity * sizeof(JSON_Value *));\n    if (temp_values == NULL) {\n        parson_free(temp_names);\n        return JSONFailure;\n    }\n    if (object->names != NULL && object->values != NULL && object->count > 0) {\n        memcpy(temp_names, object->names, object->count * sizeof(char *));\n        memcpy(temp_values, object->values, object->count * sizeof(JSON_Value *));\n    }\n    parson_free(object->names);\n    parson_free(object->values);\n    object->names    = temp_names;\n    object->values   = temp_values;\n    object->capacity = new_capacity;\n    return JSONSuccess;\n}\n\nstatic JSON_Value *\njson_object_getn_value(const JSON_Object *object, const char *name, size_t name_len)\n{\n    size_t i, name_length;\n\n    for (i = 0; i < json_object_get_count(object); i++) {\n        name_length = strlen(object->names[i]);\n        if (name_length != name_len)\n            continue;\n        if (strncmp(object->names[i], name, name_len) == 0)\n            return object->values[i];\n    }\n    return NULL;\n}\n\nstatic JSON_Status\njson_object_remove_internal(JSON_Object *object, const char *name, int free_value)\n{\n    size_t i = 0, last_item_index = 0;\n\n    if (object == NULL || json_object_get_value(object, name) == NULL)\n        return JSONFailure;\n\n    last_item_index = json_object_get_count(object) - 1;\n    for (i = 0; i < json_object_get_count(object); i++)\n        if (strcmp(object->names[i], name) == 0) {\n            parson_free(object->names[i]);\n            if (free_value)\n                json_value_free(object->values[i]);\n            if (i != last_item_index) { /* Replace key value pair with one from the end */\n                object->names[i]  = object->names[last_item_index];\n                object->values[i] = object->values[last_item_index];\n            }\n            object->count -= 1;\n            return JSONSuccess;\n        }\n    return JSONFailure; /* No execution path should end here */\n}\n\nstatic JSON_Status\njson_object_dotremove_internal(JSON_Object *object, const char *name, int free_value)\n{\n    JSON_Value *temp_value   = NULL;\n    JSON_Object *temp_object = NULL;\n    const char *dot_pos      = strchr(name, '.');\n\n    if (dot_pos == NULL)\n        return json_object_remove_internal(object, name, free_value);\n\n    temp_value = json_object_getn_value(object, name, dot_pos - name);\n    if (json_value_get_type(temp_value) != JSONObject)\n        return JSONFailure;\n\n    temp_object = json_value_get_object(temp_value);\n    return json_object_dotremove_internal(temp_object, dot_pos + 1, free_value);\n}\n\nstatic void\njson_object_free(JSON_Object *object)\n{\n    size_t i;\n\n    for (i = 0; i < object->count; i++) {\n        parson_free(object->names[i]);\n        json_value_free(object->values[i]);\n    }\n    parson_free(object->names);\n    parson_free(object->values);\n    parson_free(object);\n}\n\n/* JSON Array */\nstatic JSON_Array *\njson_array_init(JSON_Value *wrapping_value)\n{\n    JSON_Array *new_array = (JSON_Array *)parson_malloc(sizeof(JSON_Array));\n\n    if (new_array == NULL)\n        return NULL;\n\n    new_array->wrapping_value = wrapping_value;\n    new_array->items          = (JSON_Value **)NULL;\n    new_array->capacity       = 0;\n    new_array->count          = 0;\n    return new_array;\n}\n\nstatic JSON_Status\njson_array_add(JSON_Array *array, JSON_Value *value)\n{\n    if (array->count >= array->capacity) {\n        size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY);\n        if (json_array_resize(array, new_capacity) == JSONFailure)\n            return JSONFailure;\n    }\n    value->parent              = json_array_get_wrapping_value(array);\n    array->items[array->count] = value;\n    array->count++;\n    return JSONSuccess;\n}\n\nstatic JSON_Status\njson_array_resize(JSON_Array *array, size_t new_capacity)\n{\n    JSON_Value **new_items = NULL;\n\n    if (new_capacity == 0)\n        return JSONFailure;\n\n    new_items = (JSON_Value **)parson_malloc(new_capacity * sizeof(JSON_Value *));\n    if (new_items == NULL)\n        return JSONFailure;\n\n    if (array->items != NULL && array->count > 0)\n        memcpy(new_items, array->items, array->count * sizeof(JSON_Value *));\n    parson_free(array->items);\n    array->items    = new_items;\n    array->capacity = new_capacity;\n    return JSONSuccess;\n}\n\nstatic void\njson_array_free(JSON_Array *array)\n{\n    size_t i;\n\n    for (i = 0; i < array->count; i++)\n        json_value_free(array->items[i]);\n    parson_free(array->items);\n    parson_free(array);\n}\n\n/* JSON Value */\nstatic JSON_Value *\njson_value_new_string_no_copy(char *string)\n{\n    JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));\n\n    if (!new_value)\n        return NULL;\n\n    new_value->parent       = NULL;\n    new_value->type         = JSONString;\n    new_value->value.string = string;\n    return new_value;\n}\n\n/* Parser */\nstatic JSON_Status\nskip_quotes(const char **string)\n{\n    if (**string != '\\\"')\n        return JSONFailure;\n\n    SKIP_CHAR(string);\n    while (**string != '\\\"') {\n        if (**string == '\\0')\n            return JSONFailure;\n        else if (**string == '\\\\') {\n            SKIP_CHAR(string);\n            if (**string == '\\0')\n                return JSONFailure;\n        }\n        SKIP_CHAR(string);\n    }\n    SKIP_CHAR(string);\n    return JSONSuccess;\n}\n\nstatic int\nparse_utf16(const char **unprocessed, char **processed)\n{\n    unsigned int cp, lead, trail;\n    int parse_succeeded         = 0;\n    char *processed_ptr         = *processed;\n    const char *unprocessed_ptr = *unprocessed;\n\n    unprocessed_ptr++; /* skips u */\n    parse_succeeded = parse_utf16_hex(unprocessed_ptr, &cp);\n    if (!parse_succeeded)\n        return JSONFailure;\n\n    if (cp < 0x80)\n        processed_ptr[0] = (char)cp; /* 0xxxxxxx */\n    else if (cp < 0x800) {\n        processed_ptr[0] = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */\n        processed_ptr[1] = ((cp) & 0x3F) | 0x80;      /* 10xxxxxx */\n        processed_ptr += 1;\n    } else if (cp < 0xD800 || cp > 0xDFFF) {\n        processed_ptr[0] = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */\n        processed_ptr[1] = ((cp >> 6) & 0x3F) | 0x80;  /* 10xxxxxx */\n        processed_ptr[2] = ((cp) & 0x3F) | 0x80;       /* 10xxxxxx */\n        processed_ptr += 2;\n    } else if (cp >= 0xD800 && cp <= 0xDBFF) { /* lead surrogate (0xD800..0xDBFF) */\n        lead = cp;\n        unprocessed_ptr +=\n            4; /* should always be within the buffer, otherwise previous sscanf would fail */\n        if (*unprocessed_ptr++ != '\\\\' || *unprocessed_ptr++ != 'u')\n            return JSONFailure;\n\n        parse_succeeded = parse_utf16_hex(unprocessed_ptr, &trail);\n        if (!parse_succeeded || trail < 0xDC00 ||\n            trail > 0xDFFF) /* valid trail surrogate? (0xDC00..0xDFFF) */\n            return JSONFailure;\n\n        cp = ((((lead - 0xD800) & 0x3FF) << 10) | ((trail - 0xDC00) & 0x3FF)) + 0x010000;\n        processed_ptr[0] = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */\n        processed_ptr[1] = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */\n        processed_ptr[2] = (((cp >> 6) & 0x3F) | 0x80);  /* 10xxxxxx */\n        processed_ptr[3] = (((cp) & 0x3F) | 0x80);       /* 10xxxxxx */\n        processed_ptr += 3;\n    } else /* trail surrogate before lead surrogate */\n        return JSONFailure;\n\n    unprocessed_ptr += 3;\n    *processed   = processed_ptr;\n    *unprocessed = unprocessed_ptr;\n    return JSONSuccess;\n}\n\n/* Copies and processes passed string up to supplied length.\n   Example: \"\\u006Corem ipsum\" -> lorem ipsum */\nstatic char *\nprocess_string(const char *input, size_t len)\n{\n    const char *input_ptr = input;\n    size_t initial_size   = (len + 1) * sizeof(char);\n    size_t final_size     = 0;\n    char *output = NULL, *output_ptr = NULL, *resized_output = NULL;\n\n    output = (char *)parson_malloc(initial_size);\n    if (output == NULL)\n        goto error;\n    output_ptr = output;\n    while ((*input_ptr != '\\0') && (size_t)(input_ptr - input) < len) {\n        if (*input_ptr == '\\\\') {\n            input_ptr++;\n            switch (*input_ptr) {\n            case '\\\"':\n                *output_ptr = '\\\"';\n                break;\n            case '\\\\':\n                *output_ptr = '\\\\';\n                break;\n            case '/':\n                *output_ptr = '/';\n                break;\n            case 'b':\n                *output_ptr = '\\b';\n                break;\n            case 'f':\n                *output_ptr = '\\f';\n                break;\n            case 'n':\n                *output_ptr = '\\n';\n                break;\n            case 'r':\n                *output_ptr = '\\r';\n                break;\n            case 't':\n                *output_ptr = '\\t';\n                break;\n            case 'u':\n                if (parse_utf16(&input_ptr, &output_ptr) == JSONFailure)\n                    goto error;\n                break;\n            default:\n                goto error;\n            }\n        } else if ((unsigned char)*input_ptr < 0x20)\n            goto error; /* 0x00-0x19 are invalid characters for json string\n                           (http://www.ietf.org/rfc/rfc4627.txt) */\n        else\n            *output_ptr = *input_ptr;\n        output_ptr++;\n        input_ptr++;\n    }\n    *output_ptr = '\\0';\n    /* resize to new length */\n    final_size = (size_t)(output_ptr - output) + 1;\n    /* todo: don't resize if final_size == initial_size */\n    resized_output = (char *)parson_malloc(final_size);\n    if (resized_output == NULL)\n        goto error;\n    memcpy(resized_output, output, final_size);\n    parson_free(output);\n    return resized_output;\n\nerror:\n    parson_free(output);\n    return NULL;\n}\n\n/* Return processed contents of a string between quotes and\n   skips passed argument to a matching quote. */\nstatic char *\nget_quoted_string(const char **string)\n{\n    const char *string_start = *string;\n    size_t string_len        = 0;\n    JSON_Status status       = skip_quotes(string);\n\n    if (status != JSONSuccess)\n        return NULL;\n\n    string_len = *string - string_start - 2; /* length without quotes */\n    return process_string(string_start + 1, string_len);\n}\n\nstatic JSON_Value *\nparse_value(const char **string, size_t nesting)\n{\n    if (nesting > MAX_NESTING)\n        return NULL;\n\n    SKIP_WHITESPACES(string);\n    switch (**string) {\n    case '{':\n        return parse_object_value(string, nesting + 1);\n\n    case '[':\n        return parse_array_value(string, nesting + 1);\n\n    case '\\\"':\n        return parse_string_value(string);\n\n    case 'f':\n    case 't':\n    case 'F':\n    case 'T':\n        return parse_boolean_value(string);\n\n    case '-':\n    case '0':\n    case '1':\n    case '2':\n    case '3':\n    case '4':\n    case '5':\n    case '6':\n    case '7':\n    case '8':\n    case '9':\n        if (**string == '0' && *(*string + 1) == 'x')\n            return parse_uint64_value(string);\n        else if (strchr(*string, '.') == NULL)\n            return parse_integer_value(string);\n\n        return parse_number_value(string);\n\n    case 'n':\n        return parse_null_value(string);\n\n    default:\n        return NULL;\n    }\n}\n\nstatic JSON_Value *\nparse_object_value(const char **string, size_t nesting)\n{\n    JSON_Value *output_value = NULL, *new_value = NULL;\n    JSON_Object *output_object = NULL;\n    char *new_key              = NULL;\n\n    output_value = json_value_new_object();\n    if (output_value == NULL)\n        return NULL;\n\n    if (**string != '{') {\n        json_value_free(output_value);\n        return NULL;\n    }\n    output_object = json_value_get_object(output_value);\n    SKIP_CHAR(string);\n    SKIP_WHITESPACES(string);\n    if (**string == '}') { /* empty object */\n        SKIP_CHAR(string);\n        return output_value;\n    }\n    while (**string != '\\0') {\n        new_key = get_quoted_string(string);\n        if (new_key == NULL) {\n            json_value_free(output_value);\n            return NULL;\n        }\n        SKIP_WHITESPACES(string);\n        if (**string != ':') {\n            parson_free(new_key);\n            json_value_free(output_value);\n            return NULL;\n        }\n        SKIP_CHAR(string);\n        new_value = parse_value(string, nesting);\n        if (new_value == NULL) {\n            parson_free(new_key);\n            json_value_free(output_value);\n            return NULL;\n        }\n        if (json_object_add(output_object, new_key, new_value) == JSONFailure) {\n            parson_free(new_key);\n            json_value_free(new_value);\n            json_value_free(output_value);\n            return NULL;\n        }\n        parson_free(new_key);\n        SKIP_WHITESPACES(string);\n        if (**string != ',')\n            break;\n        SKIP_CHAR(string);\n        SKIP_WHITESPACES(string);\n    }\n    SKIP_WHITESPACES(string);\n    if (**string != '}' || /* Trim object after parsing is over */\n        json_object_resize(output_object, json_object_get_count(output_object)) == JSONFailure) {\n        json_value_free(output_value);\n        return NULL;\n    }\n    SKIP_CHAR(string);\n    return output_value;\n}\n\nstatic JSON_Value *\nparse_array_value(const char **string, size_t nesting)\n{\n    JSON_Value *output_value = NULL, *new_array_value = NULL;\n    JSON_Array *output_array = NULL;\n\n    output_value = json_value_new_array();\n    if (output_value == NULL)\n        return NULL;\n\n    if (**string != '[') {\n        json_value_free(output_value);\n        return NULL;\n    }\n    output_array = json_value_get_array(output_value);\n    SKIP_CHAR(string);\n    SKIP_WHITESPACES(string);\n    if (**string == ']') { /* empty array */\n        SKIP_CHAR(string);\n        return output_value;\n    }\n    while (**string != '\\0') {\n        new_array_value = parse_value(string, nesting);\n        if (new_array_value == NULL) {\n            json_value_free(output_value);\n            return NULL;\n        }\n        if (json_array_add(output_array, new_array_value) == JSONFailure) {\n            json_value_free(new_array_value);\n            json_value_free(output_value);\n            return NULL;\n        }\n        SKIP_WHITESPACES(string);\n        if (**string != ',')\n            break;\n        SKIP_CHAR(string);\n        SKIP_WHITESPACES(string);\n    }\n    SKIP_WHITESPACES(string);\n    if (**string != ']' || /* Trim array after parsing is over */\n        json_array_resize(output_array, json_array_get_count(output_array)) == JSONFailure) {\n        json_value_free(output_value);\n        return NULL;\n    }\n    SKIP_CHAR(string);\n    return output_value;\n}\n\nstatic JSON_Value *\nparse_string_value(const char **string)\n{\n    JSON_Value *value = NULL;\n    char *new_string  = get_quoted_string(string);\n\n    if (new_string == NULL)\n        return NULL;\n\n    value = json_value_new_string_no_copy(new_string);\n    if (value == NULL) {\n        parson_free(new_string);\n        return NULL;\n    }\n    return value;\n}\n\nstatic JSON_Value *\nparse_boolean_value(const char **string)\n{\n    size_t true_token_size  = SIZEOF_TOKEN(\"true\");\n    size_t false_token_size = SIZEOF_TOKEN(\"false\");\n\n    if (strncasecmp(\"true\", *string, true_token_size) == 0) {\n        *string += true_token_size;\n        return json_value_new_boolean(1);\n    } else if (strncasecmp(\"false\", *string, false_token_size) == 0) {\n        *string += false_token_size;\n        return json_value_new_boolean(0);\n    }\n    return NULL;\n}\n\nstatic JSON_Value *\nparse_number_value(const char **string)\n{\n    char *end;\n    double number = 0;\n\n    errno  = 0;\n    number = strtod(*string, &end);\n    if (errno || !is_decimal(*string, end - *string))\n        return NULL;\n\n    *string = end;\n    return json_value_new_number(number);\n}\n\nstatic JSON_Value *\nparse_uint64_value(const char **string)\n{\n    char *end;\n    uint64_t number = 0;\n\n    errno  = 0;\n    number = strtoul(*string, &end, 0);\n    if (errno)\n        return NULL;\n\n    *string = end;\n    return json_value_new_uint64(number);\n}\n\nstatic JSON_Value *\nparse_integer_value(const char **string)\n{\n    char *end;\n    int number = 0;\n\n    errno  = 0;\n    number = strtol(*string, &end, 0);\n    if (errno)\n        return NULL;\n\n    *string = end;\n    return json_value_new_integer(number);\n}\n\nstatic JSON_Value *\nparse_null_value(const char **string)\n{\n    size_t token_size = SIZEOF_TOKEN(\"null\");\n\n    if (strncmp(\"null\", *string, token_size) == 0) {\n        *string += token_size;\n        return json_value_new_null();\n    }\n    return NULL;\n}\n\n/* Serialization */\n#define APPEND_STRING(str)                   \\\n    do {                                     \\\n        written = append_string(buf, (str)); \\\n        if (written < 0) {                   \\\n            return -1;                       \\\n        }                                    \\\n        if (buf != NULL) {                   \\\n            buf += written;                  \\\n        }                                    \\\n        written_total += written;            \\\n    } while (0)\n\n#define APPEND_INDENT(level)                   \\\n    do {                                       \\\n        written = append_indent(buf, (level)); \\\n        if (written < 0) {                     \\\n            return -1;                         \\\n        }                                      \\\n        if (buf != NULL) {                     \\\n            buf += written;                    \\\n        }                                      \\\n        written_total += written;              \\\n    } while (0)\n\nstatic int\njson_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, int is_pretty,\n                           char *num_buf)\n{\n    const char *key = NULL, *string = NULL;\n    JSON_Value *temp_value = NULL;\n    JSON_Array *array      = NULL;\n    JSON_Object *object    = NULL;\n    size_t i = 0, count = 0;\n    double num = 0.0;\n    uint64_t u64;\n    int i32;\n    int written = -1, written_total = 0;\n\n    switch (json_value_get_type(value)) {\n    case JSONArray:\n        array = json_value_get_array(value);\n        count = json_array_get_count(array);\n        APPEND_STRING(\"[\");\n        if (count > 0 && is_pretty)\n            APPEND_STRING(\"\\n\");\n        for (i = 0; i < count; i++) {\n            if (is_pretty)\n                APPEND_INDENT(level + 1);\n            temp_value = json_array_get_value(array, i);\n            written    = json_serialize_to_buffer_r(temp_value, buf, level + 1, is_pretty, num_buf);\n            if (written < 0)\n                return -1;\n\n            if (buf != NULL)\n                buf += written;\n            written_total += written;\n            if (i < (count - 1))\n                APPEND_STRING(\",\");\n            if (is_pretty)\n                APPEND_STRING(\"\\n\");\n        }\n        if (count > 0 && is_pretty)\n            APPEND_INDENT(level);\n        APPEND_STRING(\"]\");\n        return written_total;\n\n    case JSONObject:\n        object = json_value_get_object(value);\n        count  = json_object_get_count(object);\n        APPEND_STRING(\"{\");\n        if (count > 0 && is_pretty)\n            APPEND_STRING(\"\\n\");\n        for (i = 0; i < count; i++) {\n            key = json_object_get_name(object, i);\n            if (key == NULL)\n                return -1;\n\n            if (is_pretty)\n                APPEND_INDENT(level + 1);\n            written = json_serialize_string(key, buf);\n            if (written < 0)\n                return -1;\n\n            if (buf != NULL)\n                buf += written;\n            written_total += written;\n            APPEND_STRING(\":\");\n            if (is_pretty)\n                APPEND_STRING(\" \");\n            temp_value = json_object_get_value(object, key);\n            written    = json_serialize_to_buffer_r(temp_value, buf, level + 1, is_pretty, num_buf);\n            if (written < 0)\n                return -1;\n\n            if (buf != NULL)\n                buf += written;\n            written_total += written;\n            if (i < (count - 1))\n                APPEND_STRING(\",\");\n            if (is_pretty)\n                APPEND_STRING(\"\\n\");\n        }\n        if (count > 0 && is_pretty)\n            APPEND_INDENT(level);\n        APPEND_STRING(\"}\");\n        return written_total;\n\n    case JSONString:\n        string = json_value_get_string(value);\n        if (string == NULL)\n            return -1;\n\n        written = json_serialize_string(string, buf);\n        if (written < 0)\n            return -1;\n\n        if (buf != NULL)\n            buf += written;\n        written_total += written;\n        return written_total;\n\n    case JSONBoolean:\n        if (json_value_get_boolean(value))\n            APPEND_STRING(\"true\");\n        else\n            APPEND_STRING(\"false\");\n        return written_total;\n\n    case JSONNumber:\n        num = json_value_get_number(value);\n        if (buf != NULL)\n            num_buf = buf;\n        written = sprintf(num_buf, FLOAT_FORMAT, num);\n        if (written < 0)\n            return -1;\n\n        if (buf != NULL)\n            buf += written;\n        written_total += written;\n        return written_total;\n\n    case JSONUint64:\n        u64 = json_value_get_uint64(value);\n        if (buf != NULL)\n            num_buf = buf;\n        written = sprintf(num_buf, UINT64_FORMAT, u64);\n        if (written < 0)\n            return -1;\n\n        if (buf != NULL)\n            buf += written;\n        written_total += written;\n        return written_total;\n\n    case JSONInteger:\n        i32 = json_value_get_integer(value);\n        if (buf != NULL)\n            num_buf = buf;\n        written = sprintf(num_buf, INTEGER_FORMAT, i32);\n        if (written < 0)\n            return -1;\n\n        if (buf != NULL)\n            buf += written;\n        written_total += written;\n        return written_total;\n\n    case JSONNull:\n        APPEND_STRING(\"null\");\n        return written_total;\n\n    case JSONError:\n        return -1;\n\n    default:\n        return -1;\n    }\n}\n\nstatic int\njson_serialize_string(const char *string, char *buf)\n{\n    size_t i = 0, len = strlen(string);\n    char c      = '\\0';\n    int written = -1, written_total = 0;\n\n    APPEND_STRING(\"\\\"\");\n    for (i = 0; i < len; i++) {\n        c = string[i];\n        switch (c) {\n        case '\\\"':\n            APPEND_STRING(\"\\\\\\\"\");\n            break;\n        case '\\\\':\n            APPEND_STRING(\"\\\\\\\\\");\n            break;\n        case '/':\n            APPEND_STRING(\"\\\\/\");\n            break; /* to make json embeddable in xml\\/html */\n        case '\\b':\n            APPEND_STRING(\"\\\\b\");\n            break;\n        case '\\f':\n            APPEND_STRING(\"\\\\f\");\n            break;\n        case '\\n':\n            APPEND_STRING(\"\\\\n\");\n            break;\n        case '\\r':\n            APPEND_STRING(\"\\\\r\");\n            break;\n        case '\\t':\n            APPEND_STRING(\"\\\\t\");\n            break;\n        case '\\x00':\n            APPEND_STRING(\"\\\\u0000\");\n            break;\n        case '\\x01':\n            APPEND_STRING(\"\\\\u0001\");\n            break;\n        case '\\x02':\n            APPEND_STRING(\"\\\\u0002\");\n            break;\n        case '\\x03':\n            APPEND_STRING(\"\\\\u0003\");\n            break;\n        case '\\x04':\n            APPEND_STRING(\"\\\\u0004\");\n            break;\n        case '\\x05':\n            APPEND_STRING(\"\\\\u0005\");\n            break;\n        case '\\x06':\n            APPEND_STRING(\"\\\\u0006\");\n            break;\n        case '\\x07':\n            APPEND_STRING(\"\\\\u0007\");\n            break;\n        /* '\\x08' duplicate: '\\b' */\n        /* '\\x09' duplicate: '\\t' */\n        /* '\\x0a' duplicate: '\\n' */\n        case '\\x0b':\n            APPEND_STRING(\"\\\\u000b\");\n            break;\n        /* '\\x0c' duplicate: '\\f' */\n        /* '\\x0d' duplicate: '\\r' */\n        case '\\x0e':\n            APPEND_STRING(\"\\\\u000e\");\n            break;\n        case '\\x0f':\n            APPEND_STRING(\"\\\\u000f\");\n            break;\n        case '\\x10':\n            APPEND_STRING(\"\\\\u0010\");\n            break;\n        case '\\x11':\n            APPEND_STRING(\"\\\\u0011\");\n            break;\n        case '\\x12':\n            APPEND_STRING(\"\\\\u0012\");\n            break;\n        case '\\x13':\n            APPEND_STRING(\"\\\\u0013\");\n            break;\n        case '\\x14':\n            APPEND_STRING(\"\\\\u0014\");\n            break;\n        case '\\x15':\n            APPEND_STRING(\"\\\\u0015\");\n            break;\n        case '\\x16':\n            APPEND_STRING(\"\\\\u0016\");\n            break;\n        case '\\x17':\n            APPEND_STRING(\"\\\\u0017\");\n            break;\n        case '\\x18':\n            APPEND_STRING(\"\\\\u0018\");\n            break;\n        case '\\x19':\n            APPEND_STRING(\"\\\\u0019\");\n            break;\n        case '\\x1a':\n            APPEND_STRING(\"\\\\u001a\");\n            break;\n        case '\\x1b':\n            APPEND_STRING(\"\\\\u001b\");\n            break;\n        case '\\x1c':\n            APPEND_STRING(\"\\\\u001c\");\n            break;\n        case '\\x1d':\n            APPEND_STRING(\"\\\\u001d\");\n            break;\n        case '\\x1e':\n            APPEND_STRING(\"\\\\u001e\");\n            break;\n        case '\\x1f':\n            APPEND_STRING(\"\\\\u001f\");\n            break;\n        default:\n            if (buf != NULL) {\n                buf[0] = c;\n                buf += 1;\n            }\n            written_total += 1;\n            break;\n        }\n    }\n    APPEND_STRING(\"\\\"\");\n    return written_total;\n}\n\nstatic int\nappend_indent(char *buf, int level)\n{\n    int i;\n    int written = -1, written_total = 0;\n\n    for (i = 0; i < level; i++)\n        APPEND_STRING(\"    \");\n    return written_total;\n}\n\nstatic int\nappend_string(char *buf, const char *string)\n{\n    if (buf == NULL)\n        return (int)strlen(string);\n\n    return sprintf(buf, \"%s\", string);\n}\n\n#undef APPEND_STRING\n#undef APPEND_INDENT\n\n/* Parser API */\nJSON_Value *\njson_parse_file(const char *filename)\n{\n    char *file_contents      = read_file(filename);\n    JSON_Value *output_value = NULL;\n\n    if (file_contents == NULL)\n        return NULL;\n\n    output_value = json_parse_string(file_contents);\n    parson_free(file_contents);\n    return output_value;\n}\n\nJSON_Value *\njson_parse_file_with_comments(const char *filename)\n{\n    char *file_contents      = read_file(filename);\n    JSON_Value *output_value = NULL;\n\n    if (file_contents == NULL)\n        return NULL;\n\n    output_value = json_parse_string_with_comments(file_contents);\n    parson_free(file_contents);\n    return output_value;\n}\n\nJSON_Value *\njson_parse_string(const char *string)\n{\n    if (string == NULL)\n        return NULL;\n\n    if (string[0] == '\\xEF' && string[1] == '\\xBB' && string[2] == '\\xBF')\n        string = string + 3; /* Support for UTF-8 BOM */\n    return parse_value((const char **)&string, 0);\n}\n\nJSON_Value *\njson_parse_string_with_comments(const char *string)\n{\n    JSON_Value *result        = NULL;\n    char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL;\n\n    string_mutable_copy = parson_strdup(string);\n    if (string_mutable_copy == NULL)\n        return NULL;\n\n    remove_comments(string_mutable_copy);\n    string_mutable_copy_ptr = string_mutable_copy;\n    result                  = parse_value((const char **)(uintptr_t)&string_mutable_copy_ptr, 0);\n    parson_free(string_mutable_copy);\n    return result;\n}\n\n/* JSON Object API */\n\nJSON_Value *\njson_object_get_value(const JSON_Object *object, const char *name)\n{\n    if (object == NULL || name == NULL)\n        return NULL;\n\n    return json_object_getn_value(object, name, strlen(name));\n}\n\nconst char *\njson_object_get_string(const JSON_Object *object, const char *name)\n{\n    return json_value_get_string(json_object_get_value(object, name));\n}\n\ndouble\njson_object_get_number(const JSON_Object *object, const char *name)\n{\n    return json_value_get_number(json_object_get_value(object, name));\n}\n\nuint64_t\njson_object_get_uint64(const JSON_Object *object, const char *name)\n{\n    return json_value_get_uint64(json_object_get_value(object, name));\n}\n\nint\njson_object_get_integer(const JSON_Object *object, const char *name)\n{\n    return json_value_get_integer(json_object_get_value(object, name));\n}\n\nJSON_Object *\njson_object_get_object(const JSON_Object *object, const char *name)\n{\n    return json_value_get_object(json_object_get_value(object, name));\n}\n\nJSON_Array *\njson_object_get_array(const JSON_Object *object, const char *name)\n{\n    return json_value_get_array(json_object_get_value(object, name));\n}\n\nint\njson_object_get_boolean(const JSON_Object *object, const char *name)\n{\n    return json_value_get_boolean(json_object_get_value(object, name));\n}\n\n/**************************************/\n\nconst char *\njson_object_at_get_string(const JSON_Object *object, size_t idx)\n{\n    return json_value_get_string(json_object_get_value_at(object, idx));\n}\n\ndouble\njson_object_at_get_number(const JSON_Object *object, size_t idx)\n{\n    return json_value_get_number(json_object_get_value_at(object, idx));\n}\n\nuint64_t\njson_object_at_get_uint64(const JSON_Object *object, size_t idx)\n{\n    return json_value_get_uint64(json_object_get_value_at(object, idx));\n}\n\nint\njson_object_at_get_integer(const JSON_Object *object, size_t idx)\n{\n    return json_value_get_integer(json_object_get_value_at(object, idx));\n}\n\nJSON_Object *\njson_object_at_get_object(const JSON_Object *object, size_t idx)\n{\n    return json_value_get_object(json_object_get_value_at(object, idx));\n}\n\nJSON_Array *\njson_object_at_get_array(const JSON_Object *object, size_t idx)\n{\n    return json_value_get_array(json_object_get_value_at(object, idx));\n}\n\nint\njson_object_at_get_boolean(const JSON_Object *object, size_t idx)\n{\n    return json_value_get_boolean(json_object_get_value_at(object, idx));\n}\n\nJSON_Value *\njson_object_dotget_value(const JSON_Object *object, const char *name)\n{\n    const char *dot_position = strchr(name, '.');\n\n    if (!dot_position)\n        return json_object_get_value(object, name);\n\n    object = json_value_get_object(json_object_getn_value(object, name, dot_position - name));\n    return json_object_dotget_value(object, dot_position + 1);\n}\n\nconst char *\njson_object_dotget_string(const JSON_Object *object, const char *name)\n{\n    return json_value_get_string(json_object_dotget_value(object, name));\n}\n\ndouble\njson_object_dotget_number(const JSON_Object *object, const char *name)\n{\n    return json_value_get_number(json_object_dotget_value(object, name));\n}\n\nuint64_t\njson_object_dotget_uint64(const JSON_Object *object, const char *name)\n{\n    return json_value_get_uint64(json_object_dotget_value(object, name));\n}\n\nint\njson_object_dotget_integer(const JSON_Object *object, const char *name)\n{\n    return json_value_get_integer(json_object_dotget_value(object, name));\n}\n\nJSON_Object *\njson_object_dotget_object(const JSON_Object *object, const char *name)\n{\n    return json_value_get_object(json_object_dotget_value(object, name));\n}\n\nJSON_Array *\njson_object_dotget_array(const JSON_Object *object, const char *name)\n{\n    return json_value_get_array(json_object_dotget_value(object, name));\n}\n\nint\njson_object_dotget_boolean(const JSON_Object *object, const char *name)\n{\n    return json_value_get_boolean(json_object_dotget_value(object, name));\n}\n\nint\njson_object_get_boolean_by_name(JSON_Object *obj, const char *name)\n{\n    int ret = 0;\n\n    if (obj && name)\n        ret = json_object_dotget_boolean(obj, name);\n\n    return (ret >= 0) ? ret : 0;\n}\n\nsize_t\njson_object_get_count(const JSON_Object *object)\n{\n    return object ? object->count : 0;\n}\n\nconst char *\njson_object_get_name(const JSON_Object *object, size_t index)\n{\n    if (object == NULL || index >= json_object_get_count(object))\n        return NULL;\n\n    return object->names[index];\n}\n\nJSON_Value *\njson_object_get_value_at(const JSON_Object *object, size_t index)\n{\n    if (object == NULL || index >= json_object_get_count(object))\n        return NULL;\n\n    return object->values[index];\n}\n\nJSON_Value *\njson_object_get_wrapping_value(const JSON_Object *object)\n{\n    return object->wrapping_value;\n}\n\nint\njson_object_has_value(const JSON_Object *object, const char *name)\n{\n    return json_object_get_value(object, name) != NULL;\n}\n\nint\njson_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type)\n{\n    JSON_Value *val = json_object_get_value(object, name);\n\n    return val != NULL && json_value_get_type(val) == type;\n}\n\nint\njson_object_dothas_value(const JSON_Object *object, const char *name)\n{\n    return json_object_dotget_value(object, name) != NULL;\n}\n\nint\njson_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type)\n{\n    JSON_Value *val = json_object_dotget_value(object, name);\n\n    return val != NULL && json_value_get_type(val) == type;\n}\n\n/* JSON Array API */\nJSON_Value *\njson_array_get_value(const JSON_Array *array, size_t index)\n{\n    if (array == NULL || index >= json_array_get_count(array))\n        return NULL;\n\n    return array->items[index];\n}\n\nconst char *\njson_array_get_string(const JSON_Array *array, size_t index)\n{\n    return json_value_get_string(json_array_get_value(array, index));\n}\n\ndouble\njson_array_get_number(const JSON_Array *array, size_t index)\n{\n    return json_value_get_number(json_array_get_value(array, index));\n}\n\nuint64_t\njson_array_get_uint64(const JSON_Array *array, size_t index)\n{\n    return json_value_get_uint64(json_array_get_value(array, index));\n}\n\nint\njson_array_get_integer(const JSON_Array *array, size_t index)\n{\n    return json_value_get_integer(json_array_get_value(array, index));\n}\n\nJSON_Object *\njson_array_get_object(const JSON_Array *array, size_t index)\n{\n    return json_value_get_object(json_array_get_value(array, index));\n}\n\nJSON_Array *\njson_array_get_array(const JSON_Array *array, size_t index)\n{\n    return json_value_get_array(json_array_get_value(array, index));\n}\n\nint\njson_array_get_boolean(const JSON_Array *array, size_t index)\n{\n    return json_value_get_boolean(json_array_get_value(array, index));\n}\n\nsize_t\njson_array_get_count(const JSON_Array *array)\n{\n    return array ? array->count : 0;\n}\n\nJSON_Value *\njson_array_get_wrapping_value(const JSON_Array *array)\n{\n    return array->wrapping_value;\n}\n\n/* JSON Value API */\nJSON_Value_Type\njson_value_get_type(const JSON_Value *value)\n{\n    return value ? value->type : JSONError;\n}\n\nJSON_Object *\njson_value_get_object(const JSON_Value *value)\n{\n    return json_value_get_type(value) == JSONObject ? value->value.object : NULL;\n}\n\nJSON_Array *\njson_value_get_array(const JSON_Value *value)\n{\n    return json_value_get_type(value) == JSONArray ? value->value.array : NULL;\n}\n\nconst char *\njson_value_get_string(const JSON_Value *value)\n{\n    return json_value_get_type(value) == JSONString ? value->value.string : NULL;\n}\n\ndouble\njson_value_get_number(const JSON_Value *value)\n{\n    return json_value_get_type(value) == JSONNumber ? value->value.number : 0;\n}\n\nuint64_t\njson_value_get_uint64(const JSON_Value *value)\n{\n    return json_value_get_type(value) == JSONUint64 ? value->value.u64 : 0;\n}\n\nint\njson_value_get_integer(const JSON_Value *value)\n{\n    return json_value_get_type(value) == JSONInteger ? value->value.i32 : 0;\n}\n\nint\njson_value_get_boolean(const JSON_Value *value)\n{\n    return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1;\n}\n\nJSON_Value *\njson_value_get_parent(const JSON_Value *value)\n{\n    return value ? value->parent : NULL;\n}\n\nvoid\njson_value_free(JSON_Value *value)\n{\n    if (!value)\n        return;\n\n    switch (json_value_get_type(value)) {\n    case JSONObject:\n        json_object_free(value->value.object);\n        break;\n    case JSONString:\n        parson_free(value->value.string);\n        break;\n    case JSONArray:\n        json_array_free(value->value.array);\n        break;\n    default:\n        break;\n    }\n    parson_free(value);\n}\n\nJSON_Value *\njson_value_new_object(void)\n{\n    JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));\n\n    if (!new_value)\n        return NULL;\n\n    new_value->parent       = NULL;\n    new_value->type         = JSONObject;\n    new_value->value.object = json_object_new(new_value);\n    if (!new_value->value.object) {\n        parson_free(new_value);\n        return NULL;\n    }\n    return new_value;\n}\n\nJSON_Value *\njson_value_new_array(void)\n{\n    JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));\n\n    if (!new_value)\n        return NULL;\n\n    new_value->parent      = NULL;\n    new_value->type        = JSONArray;\n    new_value->value.array = json_array_init(new_value);\n    if (!new_value->value.array) {\n        parson_free(new_value);\n        return NULL;\n    }\n    return new_value;\n}\n\nJSON_Value *\njson_value_new_string(const char *string)\n{\n    char *copy = NULL;\n    JSON_Value *value;\n    size_t string_len = 0;\n\n    if (string == NULL)\n        return NULL;\n\n    string_len = strlen(string);\n    if (!is_valid_utf8(string, string_len))\n        return NULL;\n\n    copy = parson_strndup(string, string_len);\n    if (copy == NULL)\n        return NULL;\n\n    value = json_value_new_string_no_copy(copy);\n    if (value == NULL)\n        parson_free(copy);\n    return value;\n}\n\nJSON_Value *\njson_value_new_number(double number)\n{\n    JSON_Value *new_value = NULL;\n\n    if (IS_NUMBER_INVALID(number))\n        return NULL;\n\n    new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));\n    if (new_value == NULL)\n        return NULL;\n\n    new_value->parent       = NULL;\n    new_value->type         = JSONNumber;\n    new_value->value.number = number;\n    return new_value;\n}\n\nJSON_Value *\njson_value_new_uint64(uint64_t number)\n{\n    JSON_Value *new_value = NULL;\n\n    new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));\n    if (new_value == NULL)\n        return NULL;\n\n    new_value->parent    = NULL;\n    new_value->type      = JSONUint64;\n    new_value->value.u64 = number;\n    return new_value;\n}\n\nJSON_Value *\njson_value_new_integer(int number)\n{\n    JSON_Value *new_value = NULL;\n\n    new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));\n    if (new_value == NULL)\n        return NULL;\n\n    new_value->parent    = NULL;\n    new_value->type      = JSONInteger;\n    new_value->value.i32 = number;\n    return new_value;\n}\n\nJSON_Value *\njson_value_new_boolean(int boolean)\n{\n    JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));\n\n    if (!new_value)\n        return NULL;\n\n    new_value->parent        = NULL;\n    new_value->type          = JSONBoolean;\n    new_value->value.boolean = boolean ? 1 : 0;\n    return new_value;\n}\n\nJSON_Value *\njson_value_new_null(void)\n{\n    JSON_Value *new_value = (JSON_Value *)parson_malloc(sizeof(JSON_Value));\n\n    if (!new_value)\n        return NULL;\n\n    new_value->parent = NULL;\n    new_value->type   = JSONNull;\n    return new_value;\n}\n\nJSON_Value *\njson_value_deep_copy(const JSON_Value *value)\n{\n    size_t i                 = 0;\n    JSON_Value *return_value = NULL, *temp_value_copy = NULL, *temp_value = NULL;\n    const char *temp_string = NULL, *temp_key = NULL;\n    char *temp_string_copy = NULL;\n    JSON_Array *temp_array = NULL, *temp_array_copy = NULL;\n    JSON_Object *temp_object = NULL, *temp_object_copy = NULL;\n\n    switch (json_value_get_type(value)) {\n    case JSONArray:\n        temp_array   = json_value_get_array(value);\n        return_value = json_value_new_array();\n        if (return_value == NULL)\n            return NULL;\n\n        temp_array_copy = json_value_get_array(return_value);\n        for (i = 0; i < json_array_get_count(temp_array); i++) {\n            temp_value      = json_array_get_value(temp_array, i);\n            temp_value_copy = json_value_deep_copy(temp_value);\n            if (temp_value_copy == NULL) {\n                json_value_free(return_value);\n                return NULL;\n            }\n            if (json_array_add(temp_array_copy, temp_value_copy) == JSONFailure) {\n                json_value_free(return_value);\n                json_value_free(temp_value_copy);\n                return NULL;\n            }\n        }\n        return return_value;\n\n    case JSONObject:\n        temp_object  = json_value_get_object(value);\n        return_value = json_value_new_object();\n        if (return_value == NULL)\n            return NULL;\n\n        temp_object_copy = json_value_get_object(return_value);\n        for (i = 0; i < json_object_get_count(temp_object); i++) {\n            temp_key        = json_object_get_name(temp_object, i);\n            temp_value      = json_object_get_value(temp_object, temp_key);\n            temp_value_copy = json_value_deep_copy(temp_value);\n            if (temp_value_copy == NULL) {\n                json_value_free(return_value);\n                return NULL;\n            }\n            if (json_object_add(temp_object_copy, temp_key, temp_value_copy) == JSONFailure) {\n                json_value_free(return_value);\n                json_value_free(temp_value_copy);\n                return NULL;\n            }\n        }\n        return return_value;\n\n    case JSONBoolean:\n        return json_value_new_boolean(json_value_get_boolean(value));\n\n    case JSONNumber:\n        return json_value_new_number(json_value_get_number(value));\n\n    case JSONUint64:\n        return json_value_new_uint64(json_value_get_uint64(value));\n\n    case JSONInteger:\n        return json_value_new_integer(json_value_get_integer(value));\n\n    case JSONString:\n        temp_string = json_value_get_string(value);\n        if (temp_string == NULL)\n            return NULL;\n\n        temp_string_copy = parson_strdup(temp_string);\n        if (temp_string_copy == NULL)\n            return NULL;\n\n        return_value = json_value_new_string_no_copy(temp_string_copy);\n        if (return_value == NULL)\n            parson_free(temp_string_copy);\n        return return_value;\n\n    case JSONNull:\n        return json_value_new_null();\n\n    case JSONError:\n        return NULL;\n\n    default:\n        return NULL;\n    }\n}\n\nsize_t\njson_serialization_size(const JSON_Value *value)\n{\n    char num_buf[NUM_BUF_SIZE]; /* recursively allocating buffer on stack is a bad idea, so let's do\n                                   it only once */\n    int res = json_serialize_to_buffer_r(value, NULL, 0, 0, num_buf);\n\n    return res < 0 ? 0 : (size_t)(res + 1);\n}\n\nJSON_Status\njson_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes)\n{\n    int written                 = -1;\n    size_t needed_size_in_bytes = json_serialization_size(value);\n\n    if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes)\n        return JSONFailure;\n\n    written = json_serialize_to_buffer_r(value, buf, 0, 0, NULL);\n    if (written < 0)\n        return JSONFailure;\n\n    return JSONSuccess;\n}\n\nJSON_Status\njson_serialize_to_file(const JSON_Value *value, const char *filename)\n{\n    JSON_Status return_code = JSONSuccess;\n    FILE *fp                = NULL;\n    char *serialized_string = json_serialize_to_string(value);\n\n    if (serialized_string == NULL)\n        return JSONFailure;\n\n    fp = fopen(filename, \"w\");\n    if (fp == NULL) {\n        json_free_serialized_string(serialized_string);\n        return JSONFailure;\n    }\n    if (fputs(serialized_string, fp) == EOF)\n        return_code = JSONFailure;\n    if (fclose(fp) == EOF)\n        return_code = JSONFailure;\n    json_free_serialized_string(serialized_string);\n    return return_code;\n}\n\nchar *\njson_serialize_to_string(const JSON_Value *value)\n{\n    JSON_Status serialization_result = JSONFailure;\n    size_t buf_size_bytes            = json_serialization_size(value);\n    char *buf                        = NULL;\n\n    if (buf_size_bytes == 0)\n        return NULL;\n\n    buf = (char *)parson_malloc(buf_size_bytes);\n    if (buf == NULL)\n        return NULL;\n\n    serialization_result = json_serialize_to_buffer(value, buf, buf_size_bytes);\n    if (serialization_result == JSONFailure) {\n        json_free_serialized_string(buf);\n        return NULL;\n    }\n    return buf;\n}\n\nsize_t\njson_serialization_size_pretty(const JSON_Value *value)\n{\n    char num_buf[NUM_BUF_SIZE]; /* recursively allocating buffer on stack is a bad idea, so let's do\n                                   it only once */\n    int res = json_serialize_to_buffer_r(value, NULL, 0, 1, num_buf);\n\n    return res < 0 ? 0 : (size_t)(res + 1);\n}\n\nJSON_Status\njson_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes)\n{\n    int written                 = -1;\n    size_t needed_size_in_bytes = json_serialization_size_pretty(value);\n\n    if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes)\n        return JSONFailure;\n\n    written = json_serialize_to_buffer_r(value, buf, 0, 1, NULL);\n    if (written < 0)\n        return JSONFailure;\n\n    return JSONSuccess;\n}\n\nJSON_Status\njson_serialize_to_file_pretty(const JSON_Value *value, const char *filename)\n{\n    JSON_Status return_code = JSONSuccess;\n    FILE *fp                = NULL;\n    char *serialized_string = json_serialize_to_string_pretty(value);\n\n    if (serialized_string == NULL)\n        return JSONFailure;\n\n    fp = fopen(filename, \"w\");\n    if (fp == NULL) {\n        json_free_serialized_string(serialized_string);\n        return JSONFailure;\n    }\n    if (fputs(serialized_string, fp) == EOF)\n        return_code = JSONFailure;\n    if (fclose(fp) == EOF)\n        return_code = JSONFailure;\n    json_free_serialized_string(serialized_string);\n    return return_code;\n}\n\nchar *\njson_serialize_to_string_pretty(const JSON_Value *value)\n{\n    JSON_Status serialization_result = JSONFailure;\n    size_t buf_size_bytes            = json_serialization_size_pretty(value);\n    char *buf                        = NULL;\n\n    if (buf_size_bytes == 0)\n        return NULL;\n\n    buf = (char *)parson_malloc(buf_size_bytes);\n    if (buf == NULL)\n        return NULL;\n\n    serialization_result = json_serialize_to_buffer_pretty(value, buf, buf_size_bytes);\n    if (serialization_result == JSONFailure) {\n        json_free_serialized_string(buf);\n        return NULL;\n    }\n    return buf;\n}\n\nvoid\njson_free_serialized_string(char *string)\n{\n    parson_free(string);\n}\n\nJSON_Status\njson_array_remove(JSON_Array *array, size_t ix)\n{\n    size_t to_move_bytes = 0;\n\n    if (array == NULL || ix >= json_array_get_count(array))\n        return JSONFailure;\n\n    json_value_free(json_array_get_value(array, ix));\n    to_move_bytes = (json_array_get_count(array) - 1 - ix) * sizeof(JSON_Value *);\n    memmove(array->items + ix, array->items + ix + 1, to_move_bytes);\n    array->count -= 1;\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_replace_value(JSON_Array *array, size_t ix, JSON_Value *value)\n{\n    if (array == NULL || value == NULL || value->parent != NULL ||\n        ix >= json_array_get_count(array))\n        return JSONFailure;\n\n    json_value_free(json_array_get_value(array, ix));\n    value->parent    = json_array_get_wrapping_value(array);\n    array->items[ix] = value;\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_replace_string(JSON_Array *array, size_t i, const char *string)\n{\n    JSON_Value *value = json_value_new_string(string);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_array_replace_value(array, i, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_replace_number(JSON_Array *array, size_t i, double number)\n{\n    JSON_Value *value = json_value_new_number(number);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_array_replace_value(array, i, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_replace_uint64(JSON_Array *array, size_t i, uint64_t number)\n{\n    JSON_Value *value = json_value_new_uint64(number);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_array_replace_value(array, i, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_replace_integer(JSON_Array *array, size_t i, int number)\n{\n    JSON_Value *value = json_value_new_integer(number);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_array_replace_value(array, i, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_replace_boolean(JSON_Array *array, size_t i, int boolean)\n{\n    JSON_Value *value = json_value_new_boolean(boolean);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_array_replace_value(array, i, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_replace_null(JSON_Array *array, size_t i)\n{\n    JSON_Value *value = json_value_new_null();\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_array_replace_value(array, i, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_clear(JSON_Array *array)\n{\n    size_t i = 0;\n\n    if (array == NULL)\n        return JSONFailure;\n\n    for (i = 0; i < json_array_get_count(array); i++)\n        json_value_free(json_array_get_value(array, i));\n    array->count = 0;\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_append_value(JSON_Array *array, JSON_Value *value)\n{\n    if (array == NULL || value == NULL || value->parent != NULL)\n        return JSONFailure;\n\n    return json_array_add(array, value);\n}\n\nJSON_Status\njson_array_append_string(JSON_Array *array, const char *string)\n{\n    JSON_Value *value = json_value_new_string(string);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_array_append_value(array, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_append_number(JSON_Array *array, double number)\n{\n    JSON_Value *value = json_value_new_number(number);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_array_append_value(array, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_append_uint64(JSON_Array *array, uint64_t number)\n{\n    JSON_Value *value = json_value_new_uint64(number);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_array_append_value(array, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_append_integer(JSON_Array *array, int number)\n{\n    JSON_Value *value = json_value_new_integer(number);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_array_append_value(array, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_append_boolean(JSON_Array *array, int boolean)\n{\n    JSON_Value *value = json_value_new_boolean(boolean);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_array_append_value(array, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_array_append_null(JSON_Array *array)\n{\n    JSON_Value *value = json_value_new_null();\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_array_append_value(array, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_object_set_value(JSON_Object *object, const char *name, JSON_Value *value)\n{\n    size_t i = 0;\n    JSON_Value *old_value;\n\n    if (object == NULL || name == NULL || value == NULL || value->parent != NULL)\n        return JSONFailure;\n\n    old_value = json_object_get_value(object, name);\n    if (old_value != NULL) { /* free and overwrite old value */\n        json_value_free(old_value);\n        for (i = 0; i < json_object_get_count(object); i++)\n            if (strcmp(object->names[i], name) == 0) {\n                value->parent     = json_object_get_wrapping_value(object);\n                object->values[i] = value;\n                return JSONSuccess;\n            }\n    }\n    /* add new key value pair */\n    return json_object_add(object, name, value);\n}\n\nJSON_Status\njson_object_set_string(JSON_Object *object, const char *name, const char *string)\n{\n    return json_object_set_value(object, name, json_value_new_string(string));\n}\n\nJSON_Status\njson_object_set_number(JSON_Object *object, const char *name, double number)\n{\n    return json_object_set_value(object, name, json_value_new_number(number));\n}\n\nJSON_Status\njson_object_set_uint64(JSON_Object *object, const char *name, uint64_t number)\n{\n    return json_object_set_value(object, name, json_value_new_uint64(number));\n}\n\nJSON_Status\njson_object_set_integer(JSON_Object *object, const char *name, int number)\n{\n    return json_object_set_value(object, name, json_value_new_integer(number));\n}\n\nJSON_Status\njson_object_set_boolean(JSON_Object *object, const char *name, int boolean)\n{\n    return json_object_set_value(object, name, json_value_new_boolean(boolean));\n}\n\nJSON_Status\njson_object_set_null(JSON_Object *object, const char *name)\n{\n    return json_object_set_value(object, name, json_value_new_null());\n}\n\nJSON_Status\njson_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value)\n{\n    const char *dot_pos    = NULL;\n    JSON_Value *temp_value = NULL, *new_value = NULL;\n    JSON_Object *temp_object = NULL, *new_object = NULL;\n    JSON_Status status = JSONFailure;\n    size_t name_len    = 0;\n\n    if (object == NULL || name == NULL || value == NULL)\n        return JSONFailure;\n\n    dot_pos = strchr(name, '.');\n    if (dot_pos == NULL)\n        return json_object_set_value(object, name, value);\n\n    name_len   = dot_pos - name;\n    temp_value = json_object_getn_value(object, name, name_len);\n    if (temp_value) {\n        /* Don't overwrite existing non-object (unlike json_object_set_value, but it shouldn't be\n         * changed at this point) */\n        if (json_value_get_type(temp_value) != JSONObject)\n            return JSONFailure;\n\n        temp_object = json_value_get_object(temp_value);\n        return json_object_dotset_value(temp_object, dot_pos + 1, value);\n    }\n    new_value = json_value_new_object();\n    if (new_value == NULL)\n        return JSONFailure;\n\n    new_object = json_value_get_object(new_value);\n    status     = json_object_dotset_value(new_object, dot_pos + 1, value);\n    if (status != JSONSuccess) {\n        json_value_free(new_value);\n        return JSONFailure;\n    }\n    status = json_object_addn(object, name, name_len, new_value);\n    if (status != JSONSuccess) {\n        json_object_dotremove_internal(new_object, dot_pos + 1, 0);\n        json_value_free(new_value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_object_dotset_string(JSON_Object *object, const char *name, const char *string)\n{\n    JSON_Value *value = json_value_new_string(string);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_object_dotset_value(object, name, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_object_dotset_number(JSON_Object *object, const char *name, double number)\n{\n    JSON_Value *value = json_value_new_number(number);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_object_dotset_value(object, name, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_object_dotset_uint64(JSON_Object *object, const char *name, uint64_t number)\n{\n    JSON_Value *value = json_value_new_uint64(number);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_object_dotset_value(object, name, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_object_dotset_integer(JSON_Object *object, const char *name, int number)\n{\n    JSON_Value *value = json_value_new_integer(number);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_object_dotset_value(object, name, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_object_dotset_boolean(JSON_Object *object, const char *name, int boolean)\n{\n    JSON_Value *value = json_value_new_boolean(boolean);\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_object_dotset_value(object, name, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_object_dotset_null(JSON_Object *object, const char *name)\n{\n    JSON_Value *value = json_value_new_null();\n\n    if (value == NULL)\n        return JSONFailure;\n\n    if (json_object_dotset_value(object, name, value) == JSONFailure) {\n        json_value_free(value);\n        return JSONFailure;\n    }\n    return JSONSuccess;\n}\n\nJSON_Status\njson_object_remove(JSON_Object *object, const char *name)\n{\n    return json_object_remove_internal(object, name, 1);\n}\n\nJSON_Status\njson_object_dotremove(JSON_Object *object, const char *name)\n{\n    return json_object_dotremove_internal(object, name, 1);\n}\n\nJSON_Status\njson_object_clear(JSON_Object *object)\n{\n    size_t i = 0;\n\n    if (object == NULL)\n        return JSONFailure;\n\n    for (i = 0; i < json_object_get_count(object); i++) {\n        parson_free(object->names[i]);\n        json_value_free(object->values[i]);\n    }\n    object->count = 0;\n    return JSONSuccess;\n}\n\nJSON_Status\njson_validate(const JSON_Value *schema, const JSON_Value *value)\n{\n    JSON_Value *temp_schema_value = NULL, *temp_value = NULL;\n    JSON_Array *schema_array = NULL, *value_array = NULL;\n    JSON_Object *schema_object = NULL, *value_object = NULL;\n    JSON_Value_Type schema_type = JSONError, value_type = JSONError;\n    const char *key = NULL;\n    size_t i = 0, count = 0;\n\n    if (schema == NULL || value == NULL)\n        return JSONFailure;\n\n    schema_type = json_value_get_type(schema);\n    value_type  = json_value_get_type(value);\n    if (schema_type != value_type && schema_type != JSONNull) /* null represents all values */\n        return JSONFailure;\n\n    switch (schema_type) {\n    case JSONArray:\n        schema_array = json_value_get_array(schema);\n        value_array  = json_value_get_array(value);\n        count        = json_array_get_count(schema_array);\n        if (count == 0)\n            return JSONSuccess; /* Empty array allows all types */\n\n        /* Get first value from array, rest is ignored */\n        temp_schema_value = json_array_get_value(schema_array, 0);\n        for (i = 0; i < json_array_get_count(value_array); i++) {\n            temp_value = json_array_get_value(value_array, i);\n            if (json_validate(temp_schema_value, temp_value) == JSONFailure)\n                return JSONFailure;\n        }\n        return JSONSuccess;\n\n    case JSONObject:\n        schema_object = json_value_get_object(schema);\n        value_object  = json_value_get_object(value);\n        count         = json_object_get_count(schema_object);\n        if (count == 0)\n            return JSONSuccess; /* Empty object allows all objects */\n        else if (json_object_get_count(value_object) < count)\n            return JSONFailure; /* Tested object mustn't have less name-value pairs than schema */\n\n        for (i = 0; i < count; i++) {\n            key               = json_object_get_name(schema_object, i);\n            temp_schema_value = json_object_get_value(schema_object, key);\n            temp_value        = json_object_get_value(value_object, key);\n            if (temp_value == NULL)\n                return JSONFailure;\n\n            if (json_validate(temp_schema_value, temp_value) == JSONFailure)\n                return JSONFailure;\n        }\n        return JSONSuccess;\n\n    case JSONString:\n    case JSONNumber:\n    case JSONBoolean:\n    case JSONNull:\n        return JSONSuccess; /* equality already tested before switch */\n\n    case JSONError:\n    default:\n        return JSONFailure;\n    }\n}\n\nint\njson_value_equals(const JSON_Value *a, const JSON_Value *b)\n{\n    JSON_Object *a_object = NULL, *b_object = NULL;\n    JSON_Array *a_array = NULL, *b_array = NULL;\n    const char *a_string = NULL, *b_string = NULL;\n    const char *key = NULL;\n    size_t a_count = 0, b_count = 0, i = 0;\n    JSON_Value_Type a_type, b_type;\n\n    a_type = json_value_get_type(a);\n    b_type = json_value_get_type(b);\n    if (a_type != b_type)\n        return 0;\n\n    switch (a_type) {\n    case JSONArray:\n        a_array = json_value_get_array(a);\n        b_array = json_value_get_array(b);\n        a_count = json_array_get_count(a_array);\n        b_count = json_array_get_count(b_array);\n        if (a_count != b_count)\n            return 0;\n\n        for (i = 0; i < a_count; i++)\n            if (!json_value_equals(json_array_get_value(a_array, i),\n                                   json_array_get_value(b_array, i)))\n                return 0;\n\n        return 1;\n\n    case JSONObject:\n        a_object = json_value_get_object(a);\n        b_object = json_value_get_object(b);\n        a_count  = json_object_get_count(a_object);\n        b_count  = json_object_get_count(b_object);\n        if (a_count != b_count)\n            return 0;\n\n        for (i = 0; i < a_count; i++) {\n            key = json_object_get_name(a_object, i);\n            if (!json_value_equals(json_object_get_value(a_object, key),\n                                   json_object_get_value(b_object, key)))\n                return 0;\n        }\n        return 1;\n\n    case JSONString:\n        a_string = json_value_get_string(a);\n        b_string = json_value_get_string(b);\n        if (a_string == NULL || b_string == NULL)\n            return 0; /* shouldn't happen */\n\n        return strcmp(a_string, b_string) == 0;\n\n    case JSONBoolean:\n        return json_value_get_boolean(a) == json_value_get_boolean(b);\n\n    case JSONNumber:\n        return fabs(json_value_get_number(a) - json_value_get_number(b)) < 0.000001; /* EPSILON */\n\n    case JSONUint64:\n        return json_value_get_uint64(a) - json_value_get_uint64(b);\n\n    case JSONInteger:\n        return abs(json_value_get_integer(a) - json_value_get_integer(b));\n\n    case JSONError:\n        return 1;\n\n    case JSONNull:\n        return 1;\n\n    default:\n        return 1;\n    }\n}\n\nJSON_Value_Type\njson_get_type(const JSON_Value *value)\n{\n    return json_value_get_type(value);\n}\n\nJSON_Object *\njson_get_object(const JSON_Value *value)\n{\n    return json_value_get_object(value);\n}\n\nJSON_Array *\njson_get_array(const JSON_Value *value)\n{\n    return json_value_get_array(value);\n}\n\nconst char *\njson_get_string(const JSON_Value *value)\n{\n    return json_value_get_string(value);\n}\n\ndouble\njson_get_number(const JSON_Value *value)\n{\n    return json_value_get_number(value);\n}\n\nuint64_t\njson_get_uint64(const JSON_Value *value)\n{\n    return json_value_get_uint64(value);\n}\n\nint\njson_get_integer(const JSON_Value *value)\n{\n    return json_value_get_integer(value);\n}\n\nint\njson_get_boolean(const JSON_Value *value)\n{\n    return json_value_get_boolean(value);\n}\n\nvoid\njson_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun)\n{\n    parson_malloc = malloc_fun;\n    parson_free   = free_fun;\n}\n"
  },
  {
    "path": "lib/utils/parson_json.h",
    "content": "/*\n   Parson ( http://kgabis.github.com/parson/ )\n   Copyright(c) 2012 - 2017 Krzysztof Gabis\n\n   Permission is hereby granted, free of charge, to any person obtaining a copy\n   of this software and associated documentation files (the \"Software\"), to deal\n   in the Software without restriction, including without limitation the rights\n   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n   copies of the Software, and to permit persons to whom the Software is\n   furnished to do so, subject to the following conditions:\n\n   The above copyright notice and this permission notice shall be included in\n   all copies or substantial portions of the Software.\n\n   THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n   THE SOFTWARE.\n */\n\n#ifndef parson_parson_h\n#define parson_parson_h\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stddef.h> /* size_t */\n#include <stdint.h>\n\n/* Types and enums */\ntypedef struct json_object_t JSON_Object;\ntypedef struct json_array_t JSON_Array;\ntypedef struct json_value_t JSON_Value;\n\nenum json_value_type {\n    JSONError   = -1,\n    JSONNull    = 1,\n    JSONString  = 2,\n    JSONNumber  = 3,\n    JSONObject  = 4,\n    JSONArray   = 5,\n    JSONBoolean = 6,\n    JSONUint64  = 7,\n    JSONInteger = 8\n};\ntypedef int JSON_Value_Type;\n\nenum json_result_t { JSONSuccess = 0, JSONFailure = -1 };\ntypedef int JSON_Status;\n\ntypedef void *(*JSON_Malloc_Function)(size_t);\ntypedef void (*JSON_Free_Function)(void *);\n\n/* Call only once, before calling any other function from parson API. If not called, malloc and free\n   from stdlib will be used for all allocations */\nvoid json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun);\n\n/* Parses first JSON value in a file, returns NULL in case of error */\nJSON_Value *json_parse_file(const char *filename);\n\n/* Parses first JSON value in a file and ignores comments (/ * * / and //),\n   returns NULL in case of error */\nJSON_Value *json_parse_file_with_comments(const char *filename);\n\n/*  Parses first JSON value in a string, returns NULL in case of error */\nJSON_Value *json_parse_string(const char *string);\n\n/*  Parses first JSON value in a string and ignores comments (/ * * / and //),\n    returns NULL in case of error */\nJSON_Value *json_parse_string_with_comments(const char *string);\n\n/* Serialization */\nsize_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */\nJSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);\nJSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename);\nchar *json_serialize_to_string(const JSON_Value *value);\n\n/* Pretty serialization */\nsize_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */\nJSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf,\n                                            size_t buf_size_in_bytes);\nJSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename);\nchar *json_serialize_to_string_pretty(const JSON_Value *value);\n\nvoid json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and\n                                                   json_serialize_to_string_pretty */\n\n/* Comparing */\nint json_value_equals(const JSON_Value *a, const JSON_Value *b);\n\n/* Validation\n   This is *NOT* JSON Schema. It validates json by checking if object have identically\n   named fields with matching types.\n   For example schema {\"name\":\"\", \"age\":0} will validate\n   {\"name\":\"Joe\", \"age\":25} and {\"name\":\"Joe\", \"age\":25, \"gender\":\"m\"},\n   but not {\"name\":\"Joe\"} or {\"name\":\"Joe\", \"age\":\"Cucumber\"}.\n   In case of arrays, only first value in schema is checked against all values in tested array.\n   Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays,\n   null validates values of every type.\n */\nJSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value);\n\n/*\n * JSON Object\n */\nJSON_Value *json_object_get_value(const JSON_Object *object, const char *name);\nconst char *json_object_get_string(const JSON_Object *object, const char *name);\nJSON_Object *json_object_get_object(const JSON_Object *object, const char *name);\nJSON_Array *json_object_get_array(const JSON_Object *object, const char *name);\ndouble json_object_get_number(const JSON_Object *object, const char *name); /* returns 0 on fail */\nuint64_t json_object_get_uint64(const JSON_Object *object,\n                                const char *name);                        /* returns 0 on fail */\nint json_object_get_integer(const JSON_Object *object, const char *name); /* returns -1 on fail */\nint json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */\n\nconst char *json_object_at_get_string(const JSON_Object *object, size_t idx);\nJSON_Object *json_object_at_get_object(const JSON_Object *object, size_t idx);\nJSON_Array *json_object_at_get_array(const JSON_Object *object, size_t idx);\ndouble json_object_at_get_number(const JSON_Object *object, size_t idx);   /* returns 0 on fail */\nuint64_t json_object_at_get_uint64(const JSON_Object *object, size_t idx); /* returns 0 on fail */\nint json_object_at_get_integer(const JSON_Object *object, size_t idx);\nint json_object_at_get_boolean(const JSON_Object *object, size_t idx); /* returns -1 on fail */\n\n/* dotget functions enable addressing values with dot notation in nested objects,\n   just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).\n   Because valid names in JSON can contain dots, some values may be inaccessible\n   this way. */\nJSON_Value *json_object_dotget_value(const JSON_Object *object, const char *name);\nconst char *json_object_dotget_string(const JSON_Object *object, const char *name);\nJSON_Object *json_object_dotget_object(const JSON_Object *object, const char *name);\nJSON_Array *json_object_dotget_array(const JSON_Object *object, const char *name);\ndouble json_object_dotget_number(const JSON_Object *object,\n                                 const char *name); /* returns 0 on fail */\nuint64_t json_object_dotget_uint64(const JSON_Object *object,\n                                   const char *name); /* returns 0 on fail */\nint json_object_dotget_integer(const JSON_Object *object,\n                               const char *name); /* returns -1 on fail */\nint json_object_dotget_boolean(const JSON_Object *object,\n                               const char *name); /* returns -1 on fail */\nint json_object_get_boolean_by_name(JSON_Object *obj, const char *name);\n\n/* Functions to get available names */\nsize_t json_object_get_count(const JSON_Object *object);\nconst char *json_object_get_name(const JSON_Object *object, size_t index);\nJSON_Value *json_object_get_value_at(const JSON_Object *object, size_t index);\nJSON_Value *json_object_get_wrapping_value(const JSON_Object *object);\n\n/* Functions to check if object has a value with a specific name. Returned value is 1 if object has\n * a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */\nint json_object_has_value(const JSON_Object *object, const char *name);\nint json_object_has_value_of_type(const JSON_Object *object, const char *name,\n                                  JSON_Value_Type type);\n\nint json_object_dothas_value(const JSON_Object *object, const char *name);\nint json_object_dothas_value_of_type(const JSON_Object *object, const char *name,\n                                     JSON_Value_Type type);\n\n/* Creates new name-value pair or frees and replaces old value with a new one.\n * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */\nJSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value);\nJSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string);\nJSON_Status json_object_set_number(JSON_Object *object, const char *name, double number);\nJSON_Status json_object_set_uint64(JSON_Object *object, const char *name, uint64_t number);\nJSON_Status json_object_set_integer(JSON_Object *object, const char *name, int number);\nJSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean);\nJSON_Status json_object_set_null(JSON_Object *object, const char *name);\n\n/* Works like dotget functions, but creates whole hierarchy if necessary.\n * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */\nJSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value);\nJSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string);\nJSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number);\nJSON_Status json_object_dotset_uint64(JSON_Object *object, const char *name, uint64_t number);\nJSON_Status json_object_dotset_integer(JSON_Object *object, const char *name, int number);\nJSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean);\nJSON_Status json_object_dotset_null(JSON_Object *object, const char *name);\n\n/* Frees and removes name-value pair */\nJSON_Status json_object_remove(JSON_Object *object, const char *name);\n\n/* Works like dotget function, but removes name-value pair only on exact match. */\nJSON_Status json_object_dotremove(JSON_Object *object, const char *key);\n\n/* Removes all name-value pairs in object */\nJSON_Status json_object_clear(JSON_Object *object);\n\n/*\n * JSON Array\n */\nJSON_Value *json_array_get_value(const JSON_Array *array, size_t index);\nconst char *json_array_get_string(const JSON_Array *array, size_t index);\nJSON_Object *json_array_get_object(const JSON_Array *array, size_t index);\nJSON_Array *json_array_get_array(const JSON_Array *array, size_t index);\ndouble json_array_get_number(const JSON_Array *array, size_t index);   /* returns 0 on fail */\nuint64_t json_array_get_uint64(const JSON_Array *array, size_t index); /* returns 0 on fail */\nint json_array_get_integer(const JSON_Array *array, size_t index);     /* returns -1 on fail */\nint json_array_get_boolean(const JSON_Array *array, size_t index);     /* returns -1 on fail */\nsize_t json_array_get_count(const JSON_Array *array);\nJSON_Value *json_array_get_wrapping_value(const JSON_Array *array);\n\n/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't\n * exist. Order of values in array may change during execution.  */\nJSON_Status json_array_remove(JSON_Array *array, size_t i);\n\n/* Frees and removes from array value at given index and replaces it with given one.\n * Does nothing and returns JSONFailure if index doesn't exist.\n * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */\nJSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value);\nJSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char *string);\nJSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number);\nJSON_Status json_array_replace_uint64(JSON_Array *array, size_t i, uint64_t number);\nJSON_Status json_array_replace_integer(JSON_Array *array, size_t i, int number);\nJSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean);\nJSON_Status json_array_replace_null(JSON_Array *array, size_t i);\n\n/* Frees and removes all values from array */\nJSON_Status json_array_clear(JSON_Array *array);\n\n/* Appends new value at the end of array.\n * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */\nJSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value);\nJSON_Status json_array_append_string(JSON_Array *array, const char *string);\nJSON_Status json_array_append_number(JSON_Array *array, double number);\nJSON_Status json_array_append_uint64(JSON_Array *array, uint64_t number);\nJSON_Status json_array_append_integer(JSON_Array *array, int number);\nJSON_Status json_array_append_boolean(JSON_Array *array, int boolean);\nJSON_Status json_array_append_null(JSON_Array *array);\n\n/*\n * JSON Value\n */\nJSON_Value *json_value_new_object(void);\nJSON_Value *json_value_new_array(void);\nJSON_Value *json_value_new_string(const char *string); /* copies passed string */\nJSON_Value *json_value_new_number(double number);\nJSON_Value *json_value_new_uint64(uint64_t number);\nJSON_Value *json_value_new_integer(int number);\nJSON_Value *json_value_new_boolean(int boolean);\nJSON_Value *json_value_new_null(void);\nJSON_Value *json_value_deep_copy(const JSON_Value *value);\nvoid json_value_free(JSON_Value *value);\n\nJSON_Value_Type json_value_get_type(const JSON_Value *value);\nJSON_Object *json_value_get_object(const JSON_Value *value);\nJSON_Array *json_value_get_array(const JSON_Value *value);\nconst char *json_value_get_string(const JSON_Value *value);\ndouble json_value_get_number(const JSON_Value *value);\nuint64_t json_value_get_uint64(const JSON_Value *value);\nint json_value_get_integer(const JSON_Value *value);\nint json_value_get_boolean(const JSON_Value *value);\nJSON_Value *json_value_get_parent(const JSON_Value *value);\n\n/* Same as above, but shorter */\nJSON_Value_Type json_get_type(const JSON_Value *value);\nJSON_Object *json_get_object(const JSON_Value *value);\nJSON_Array *json_get_array(const JSON_Value *value);\nconst char *json_get_string(const JSON_Value *value);\ndouble json_get_number(const JSON_Value *value);\nuint64_t json_get_uint64(const JSON_Value *value);\nint json_get_integer(const JSON_Value *value);\nint json_get_boolean(const JSON_Value *value);\n\nstatic inline const char *\njson_string_type(JSON_Value *val)\n{\n    if (!val)\n        return \"oops\";\n\n    if (json_get_type(val) == JSONString)\n        return \"String\";\n    if (json_get_type(val) == JSONNull)\n        return \"Null\";\n    if (json_get_type(val) == JSONNumber)\n        return \"Number\";\n    if (json_get_type(val) == JSONUint64)\n        return \"uint64\";\n    if (json_get_type(val) == JSONInteger)\n        return \"Integer\";\n    if (json_get_type(val) == JSONArray)\n        return \"Array\";\n    if (json_get_type(val) == JSONObject)\n        return \"Object\";\n    if (json_get_type(val) == JSONBoolean)\n        return \"Boolean\";\n\n    return \"Unknown\";\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/utils/portlist.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n#include \"pg_strings.h\"\n#include \"portlist.h\"\n\nint\nportmask_parse(const char *str, portlist_t *portmask)\n{\n    char *end = NULL;\n    portlist_t pm;\n\n    /* parse hexadecimal string */\n    pm = strtoull(str, &end, 16);\n    if ((str[0] == '\\0') || (end == NULL) || (*end != '\\0'))\n        return -1;\n\n    if (pm == 0)\n        return -1;\n\n    if (portmask)\n        *portmask = pm;\n\n    return 0;\n}\n\nstatic inline void\nset_portlist_bits(size_t low, size_t high, uint64_t *map)\n{\n    do {\n        *map |= (1LL << low++);\n    } while (low <= high);\n}\n\n#define MAX_SPLIT 64\n/* portlist = N,N,N-M,N, ... */\nint\nportlist_parse(const char *str, int nb_ports, portlist_t *portlist)\n{\n    size_t ps, pe, n;\n    char *split[MAX_SPLIT], *s, *p;\n    uint64_t map = 0;\n\n    if (!str || !*str || !portlist)\n        return -1;\n    *portlist = 0;\n\n    if (!strcmp(str, \"all\")) {\n        for (int i = 0; i < nb_ports; i++)\n            *portlist |= (1LL << i);\n        return 0;\n    }\n\n    n = strlen(str);\n    s = alloca(n + 1);\n    if (!s)\n        return -1;\n\n    memcpy(s, str, n);\n    s[n] = '\\0';\n\n    n = pg_strtok(s, \",\", split, MAX_SPLIT);\n    if (!n)\n        return 0;\n\n    for (size_t i = 0; i < n; i++) {\n        p = strchr(split[i], '-');\n\n        if (!p) {\n            ps = strtoul(split[i], NULL, 10);\n            pe = ps;\n        } else {\n            *p++ = '\\0';\n            ps   = strtoul(split[i], NULL, 10);\n            pe   = strtoul(p, NULL, 10);\n        }\n\n        if ((ps > pe) || (pe >= (sizeof(map) * 8)))\n            return -1;\n\n        if (pe > RTE_MAX_ETHPORTS)\n            pe = RTE_MAX_ETHPORTS;\n        set_portlist_bits(ps, pe, &map);\n    }\n\n    if (portlist)\n        *portlist = map;\n\n    return 0;\n}\n\nchar *\nportlist_string(uint64_t portlist, char *buf, int len)\n{\n    int i, k, j, cnt = 0;\n\n    k = 0;\n    for (i = 0; i < RTE_MAX_ETHPORTS; i++)\n        if (portlist & (1UL << i))\n            cnt++;\n\n    memset(buf, 0, len);\n\n    k    = 0;\n    j    = 0;\n    *buf = '\\0';\n    for (i = 0; (i < RTE_MAX_ETHPORTS) && (k < len); i++)\n        if (portlist & (1UL << i)) {\n            k += snprintf(&buf[k], len - k, \"%d%s\", i, (j >= cnt) ? \"\" : \", \");\n            j++;\n        }\n\n    if (k >= 2)\n        buf[k - 2] = '\\0';\n    return buf;\n}\nchar *\nportlist_print(FILE *f, uint64_t portlist, char *buf, int len)\n{\n    int i, k;\n\n    if (!f)\n        f = stdout;\n\n    k    = 0;\n    *buf = '\\0';\n    for (i = 0; (i < RTE_MAX_ETHPORTS) && (k < len); i++)\n        if (portlist & (1UL << i))\n            k += snprintf(&buf[k], len - k, \"%d, \", i);\n\n    if (k >= 2)\n        buf[k - 2] = '\\0';\n    return buf;\n}\n"
  },
  {
    "path": "lib/utils/portlist.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n\n/**\n * @file\n *\n * String-related utility function for parsing port mask.\n */\n\n#ifndef __PORTLIST_H_\n#define __PORTLIST_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <string.h>\n\n#include <rte_compat.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef uint64_t portlist_t;\n#define INVALID_PORTLIST ((portlist_t) - 1)\n\n/**\n * Parse a portlist string into a mask or bitmap value.\n *\n * @param str\n *   String to parse\n * @param nb_ports\n *   Max number of ports to set in the portlist\n * @param portlist\n *   Pointer to uint64_t value for returned bitmap\n * @return\n *   -1 on error or 0 on success.\n */\nint portlist_parse(const char *str, int nb_ports, portlist_t *portlist);\n\n/**\n * Parse a portmask string into a mask or bitmap value.\n *\n * @param str\n *   String to parse\n * @param portmask\n *   Pointer to uint64_t value for returned bitmap\n * @return\n *   -1 on error or 0 on success.\n */\nint portmask_parse(const char *str, portlist_t *portmask);\n\n/**\n * Convert a portlist bitmap to a human-readable string (e.g. \"0,1,3-5\").\n *\n * @param portlist\n *   64-bit bitmap of port indices to serialise.\n * @param buf\n *   Destination buffer for the result string.\n * @param len\n *   Size of @p buf in bytes.\n * @return\n *   Pointer to @p buf on success, or NULL on error.\n */\nchar *portlist_string(uint64_t portlist, char *buf, int len);\n\n/**\n * Convert a portlist bitmap to a string and print it to file @p f.\n *\n * @param f\n *   Output file (e.g. stdout).\n * @param portlist\n *   64-bit bitmap of port indices to serialise.\n * @param buf\n *   Scratch buffer used to build the string.\n * @param len\n *   Size of @p buf in bytes.\n * @return\n *   Pointer to @p buf on success, or NULL on error.\n */\nchar *portlist_print(FILE *f, uint64_t portlist, char *buf, int len);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __PORTLIST_H_ */\n"
  },
  {
    "path": "lib/vec/meson.build",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2020-2026> Intel Corporation\n\nsources = files('vec.c')\nlibvec = library('vec', sources, dependencies: [common, utils, dpdk])\nvec = declare_dependency(link_with: libvec, include_directories: include_directories('.'))\n"
  },
  {
    "path": "lib/vec/vec.c",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#include <rte_memory.h>\n#include <rte_timer.h>\n#include <rte_prefetch.h>\n#include <rte_mbuf.h>\n\n#include \"vec.h\"\n\nstatic void\nvec_obj_init(struct rte_mempool *mp, void *uarg __rte_unused, void *obj, unsigned idx __rte_unused)\n{\n    struct vec *v = (struct vec *)obj;\n\n    vec_reset(mp, v);\n}\n\nstruct rte_mempool *\nvec_create_pool(const char *name, unsigned int n, unsigned int entires, unsigned int cache_size)\n{\n    struct rte_mempool *mp = NULL;\n    unsigned int size      = vec_calc_size(entires);\n\n    mp =\n        rte_mempool_create(name, n, size, cache_size, 0, NULL, NULL, NULL, NULL, pg_socket_id(), 0);\n    if (mp)\n        rte_mempool_obj_iter(mp, vec_obj_init, NULL);\n\n    return mp;\n}\n\nvoid\nvec_destroy_pool(struct rte_mempool *obj)\n{\n    rte_mempool_free(obj);\n}\n\nstruct vec *\nvec_create(const char *name, unsigned int n, uint16_t flags)\n{\n    struct vec *vec;\n    uint32_t vec_size;\n\n    vec_size = vec_calc_size(n);\n\n    vec = rte_zmalloc_socket(name, vec_size, RTE_CACHE_LINE_SIZE, pg_socket_id());\n    if (vec) {\n        vec->tlen  = n;\n        vec->flags = flags;\n        vec->flags |= VEC_CREATE_FLAG;\n    }\n\n    return vec;\n}\n\nvoid\nvec_destroy(struct vec *vec)\n{\n    rte_free(vec);\n}\n\nint\nvec_to_data(struct vec *v, char *buf, size_t len)\n{\n    size_t count = 0, cnt;\n    uint16_t vlen;\n    int i;\n\n    vlen = vec_len(v);\n    for (i = 0; i < vlen && len; i++) {\n        struct rte_mbuf *m = vec_at_index(v, i);\n\n        /* set cnt to number of bytes that will fit in buffer */\n        cnt = RTE_MIN(rte_pktmbuf_pkt_len(m), len);\n\n        /* Copy the data from mbuf to buffer for cnt bytes */\n        rte_memcpy(buf, rte_pktmbuf_mtod(m, char *), cnt);\n\n        rte_pktmbuf_adj(m, cnt); /* reduce the pkt size */\n\n        count += cnt;\n        len -= cnt;\n        buf += cnt;\n\n        if (rte_pktmbuf_pkt_len(m) == 0)\n            vec_free_mbuf_at_index(v, i);\n        else\n            break;\n    }\n    vec_compact(v);\n\n    return count;\n}\n\nvoid\nvec_print(FILE *f, const char *msg, struct vec *v)\n{\n    int i, k;\n\n    if (!f)\n        f = stderr;\n    if (!msg)\n        fprintf(f, \"Vector: @ %p: \", v);\n    else\n        fprintf(f, \"Vector: %s @ %p \", msg, v);\n    fprintf(f, \" flags %04x, len %u, tlen %u\\n\", v->flags, v->len, v->tlen);\n    fprintf(f, \"        vpool %p\\nList: \", v->vpool);\n    for (i = 0, k = 0; i < v->len; i++) {\n        fprintf(f, \"%p \", v->list[i]);\n        if (++k >= 8) {\n            k = 0;\n            fprintf(f, \"\\n      \");\n        }\n    }\n    fprintf(f, \"\\n\");\n}\n"
  },
  {
    "path": "lib/vec/vec.h",
    "content": "/* SPDX-License-Identifier: BSD-3-Clause\n * Copyright(c) <2020-2026> Intel Corporation.\n */\n/* Created 2018 by Keith Wiles @ intel.com */\n\n#ifndef __VEC_H\n#define __VEC_H\n\n/**\n * @file\n *\n * DPDK mempool-backed pointer vector container.\n *\n * A vec is a fixed-capacity array of void pointers allocated from an\n * rte_mempool object.  It is used throughout Pktgen for batching\n * rte_mbuf pointers across lcore-to-port boundaries.\n */\n\n#include <rte_malloc.h>\n#include <pg_compat.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define VEC_DEFAULT_SIZE 8\n\nstruct vec {\n    uint16_t flags;    /**< Flags for Vec structure */\n    uint16_t len;      /**< Number of pointers in vector list */\n    uint16_t tlen;     /**< Total number of vectors */\n    void *vpool;       /**< pool from were the vector was allocated */\n    uint16_t pad0[20]; /**< force to cache line size */\n    void *list[0];     /**< Vector list of pointer place holder */\n};\n\n/** Flag bits stored in struct vec::flags. */\nenum {\n    VEC_FREE_FLAG      = 0x0001, /**< Vec has been returned to its mempool */\n    VEC_COMPACT_FLAG   = 0x0002, /**< Vec contains NULL gaps; compact needed */\n    VEC_DONT_FREE_FLAG = 0x0004, /**< Do not return vec to mempool on free */\n    VEC_CREATE_FLAG    = 0x0008, /**< Vec was created via vec_create() */\n    VEC_COND_WAIT_FLAG = 0x8000, /**< Internal: conditional-wait in use */\n    VEC_RESET_MASK  = (VEC_DONT_FREE_FLAG | VEC_CREATE_FLAG), /**< Flags preserved across reset */\n    VEC_CLEAR_FLAGS = 0x0000                                  /**< Clear all flags */\n};\n\n/**\n * Iterate over all entries in a vec.\n *\n * @param idx   Integer index variable (declared by caller).\n * @param var   Pointer variable set to each element on each iteration.\n * @param vec   Pointer to the struct vec to iterate over.\n */\n#define vec_foreach(idx, var, vec)                                      \\\n    for (idx = 0, var = vec_at_index((vec), idx); idx < vec_len((vec)); \\\n         idx++, var   = vec_at_index((vec), idx))\n\n/**\n * Initialise an already-allocated vec in place.\n *\n * @param v\n *   Pointer to the vec to initialise.\n * @param n\n *   Maximum number of elements (total length).\n * @param flags\n *   Initial flag bits (see VEC_* constants).\n */\nstatic inline void\nvec_init(struct vec *v, unsigned int n, uint16_t flags)\n{\n    v->len   = 0;\n    v->tlen  = n;\n    v->flags = flags;\n    v->vpool = NULL;\n}\n\n/**\n * Calculate the allocation size in bytes for a vec with @p cnt entries.\n *\n * The result is cache-line aligned. If @p cnt is zero, VEC_DEFAULT_SIZE is used.\n *\n * @param cnt\n *   Desired element capacity.\n * @return\n *   Byte size of the vec object, aligned to RTE_CACHE_LINE_SIZE.\n */\nstatic inline unsigned int\nvec_calc_size(unsigned int cnt)\n{\n    unsigned int size;\n\n    if (cnt == 0)\n        cnt = VEC_DEFAULT_SIZE;\n    size = (cnt * sizeof(void *)) + sizeof(struct vec);\n\n    return RTE_ALIGN_CEIL(size, RTE_CACHE_LINE_SIZE);\n}\n\n/**\n * Calculate how many vec entries fit within one mempool element.\n *\n * @param mp\n *   Mempool whose element size determines the entry count.\n * @return\n *   Number of void-pointer entries available in a single vec element.\n */\nstatic inline uint32_t\nvec_calc_count(struct rte_mempool *mp)\n{\n    uint32_t size = mp->elt_size;\n\n    size = (size - sizeof(struct vec) - sizeof(void *)) / sizeof(void *);\n\n    return size;\n}\n\n/** Mark vec @p v as already returned to its mempool. */\nstatic inline void\nvec_set_free(struct vec *v)\n{\n    v->flags |= VEC_FREE_FLAG;\n}\n\n/** Return non-zero if vec @p v has been freed (VEC_FREE_FLAG set). */\nstatic inline int\nvec_is_free(struct vec *v)\n{\n    return v->flags & VEC_FREE_FLAG;\n}\n\n/** Return non-zero if VEC_DONT_FREE_FLAG is set on @p vec. */\nstatic inline int\nvec_is_dont_free(struct vec *vec)\n{\n    return vec->flags & VEC_DONT_FREE_FLAG;\n}\n\n/** Set VEC_DONT_FREE_FLAG on @p vec so it is not returned to its mempool on free. */\nstatic inline void\nvec_set_dont_free(struct vec *vec)\n{\n    vec->flags |= VEC_DONT_FREE_FLAG;\n}\n\n/** Clear VEC_DONT_FREE_FLAG on @p vec, re-enabling normal mempool return. */\nstatic inline void\nvec_clr_dont_free(struct vec *vec)\n{\n    vec->flags &= ~VEC_DONT_FREE_FLAG;\n}\n\n/** Return the current number of elements in vec @p v. */\nstatic __rte_always_inline uint16_t\nvec_len(struct vec *v)\n{\n    return v->len;\n}\n\n/** Return the used size of vec @p v in bytes (len * sizeof(void*)). */\nstatic __rte_always_inline int\nvec_byte_len(struct vec *v)\n{\n    return v->len * sizeof(void *);\n}\n\n/** Set the current element count of vec @p v to @p n. */\nstatic __rte_always_inline void\nvec_set_len(struct vec *v, uint16_t n)\n{\n    v->len = n;\n}\n\n/** Set the maximum element capacity of vec @p v to @p n. */\nstatic __rte_always_inline void\nvec_set_max_len(struct vec *v, uint16_t n)\n{\n    v->tlen = n;\n}\n\n/** Decrement the element count of vec @p v by one. */\nstatic __rte_always_inline void\nvec_dec_len(struct vec *v)\n{\n    v->len--;\n}\n\n/** Increment the element count of vec @p v by one. */\nstatic __rte_always_inline void\nvec_inc_len(struct vec *v)\n{\n    v->len++;\n}\n\n/** Return the maximum element capacity of vec @p v. */\nstatic __rte_always_inline uint16_t\nvec_max_len(struct vec *v)\n{\n    return v->tlen;\n}\n\n/** Return a pointer to the start of the rte_mbuf pointer array in @p v. */\nstatic __rte_always_inline struct rte_mbuf **\nvec_list(struct vec *v)\n{\n    return (struct rte_mbuf **)&v->list[0];\n}\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Warray-bounds\"\n\n/**\n * Append one pointer to @p vec.\n *\n * @param vec   Vec to append to.\n * @param val   Pointer value to append.\n * @return      Index of the inserted element, or -1 if the vec is full.\n */\nstatic __rte_always_inline int\nvec_add1(struct vec *vec, void *val)\n{\n    if (vec->len >= vec->tlen)\n        return -1;\n\n    vec->list[vec->len++] = val;\n    return vec->len - 1;\n}\n\n/**\n * Store @p val at a specific index @p n in @p vec and increment the length.\n *\n * @param vec   Vec to write into.\n * @param val   Pointer value to store.\n * @param n     Target index.\n * @return      0 on success, -1 if the vec is already full.\n */\nstatic __rte_always_inline int\nvec_add_at_index(struct vec *vec, void *val, uint16_t n)\n{\n    if (vec->len >= vec->tlen)\n        return -1;\n    vec->list[n] = val;\n    vec->len++;\n    return 0;\n}\n\n/**\n * Return the pointer at index @p n in @p vec.\n *\n * @param vec   Vec to read from.\n * @param n     Element index.\n * @return      Pointer value, or NULL if @p n is out of range.\n */\nstatic __rte_always_inline void *\nvec_at_index(struct vec *vec, uint16_t n)\n{\n    if (n >= vec->len)\n        return NULL;\n    return vec->list[n];\n}\n\n/**\n * Overwrite the entry at index @p idx in @p vec without changing the length.\n *\n * @param vec   Vec to modify.\n * @param idx   Target index (must be within tlen).\n * @param val   New pointer value.\n */\nstatic __rte_always_inline void\nvec_set_at_index(struct vec *vec, uint16_t idx, void *val)\n{\n    if (idx < vec->tlen)\n        vec->list[idx] = val;\n}\n\n/**\n * Return a pointer to the rte_mbuf pointer at index @p n in @p vec.\n *\n * @param vec   Vec to address.\n * @param n     Element index.\n * @return      Pointer to element @p n.\n */\nstatic __rte_always_inline struct rte_mbuf **\nvec_addr(struct vec *vec, uint16_t n)\n{\n    return (struct rte_mbuf **)&vec->list[n];\n}\n\n/**\n * Return a pointer one past the last valid element of @p vec.\n *\n * @param vec   Vec to address.\n * @return      Pointer to the slot after the last element.\n */\nstatic __rte_always_inline struct rte_mbuf **\nvec_end(struct vec *vec)\n{\n    return (struct rte_mbuf **)&vec->list[vec->len];\n}\n\n/**\n * Return the number of unused slots remaining in @p vec.\n *\n * @param vec   Vec to query.\n * @return      tlen - len.\n */\nstatic __rte_always_inline int\nvec_len_remaining(struct vec *vec)\n{\n    return vec->tlen - vec->len;\n}\n\n/**\n * Return non-zero if @p v has no remaining capacity (len == tlen).\n *\n * @param v   Vec to test.\n * @return    Non-zero when full.\n */\nstatic __rte_always_inline int\nvec_is_full(struct vec *v)\n{\n    return (v->len == v->tlen);\n}\n\n#pragma GCC diagnostic pop\n\n/**\n * Reset @p vec to an empty state, preserving sticky flags (DONT_FREE, CREATE).\n *\n * @param mp    Mempool that owns @p vec (stored in vec->vpool).\n * @param vec   Vec to reset.\n */\nstatic inline void\nvec_reset(struct rte_mempool *mp, struct vec *vec)\n{\n    uint16_t flags = vec->flags;\n\n    vec->flags = (flags & VEC_RESET_MASK);\n    vec->vpool = mp;\n    vec_set_max_len(vec, vec_calc_count(mp));\n    vec_set_len(vec, 0);\n}\n\n/**\n * Search @p vec for pointer @p v and return its index.\n *\n * @param vec   Vec to search.\n * @param v     Pointer value to find.\n * @return      Index of the first matching entry, or -1 if not found.\n */\nstatic inline int\nvec_find_index(struct vec *vec, void *v)\n{\n    int i;\n\n    for (i = 0; i < vec_len(vec); i++) {\n        if (vec_at_index(vec, i) == v)\n            return i;\n    }\n    return -1;\n}\n\n/**\n * Allocate a single vec from @p mp.\n *\n * @param mp    Mempool to allocate from.\n * @return      Pointer to the allocated vec, or NULL on failure.\n */\nstatic inline struct vec *\nvec_alloc(struct rte_mempool *mp)\n{\n    struct vec *vec;\n\n    if (rte_mempool_get(mp, (void **)&vec))\n        return NULL;\n\n    return vec;\n}\n\n/**\n * Return @p vec to its owning mempool (unless VEC_DONT_FREE_FLAG is set).\n *\n * If @p vec is NULL, already freed (VEC_FREE_FLAG), or has VEC_RESET_MASK\n * flags set, the function resets it in place without returning it to the pool.\n *\n * @param vec   Vec to free. May be NULL.\n */\nstatic inline void\nvec_free(struct vec *vec)\n{\n    struct rte_mempool *mp;\n\n    if (!vec)\n        return;\n\n    if (vec_is_free(vec))\n        return;\n\n    vec_set_len(vec, 0);\n    if (vec->flags & VEC_RESET_MASK) {\n        vec->flags = (vec->flags & VEC_RESET_MASK);\n        return;\n    }\n\n    vec->flags = (vec->flags & VEC_RESET_MASK);\n    vec->flags |= VEC_FREE_FLAG;\n\n    mp = vec->vpool;\n\n    vec_reset(mp, vec);\n\n    rte_mempool_put(mp, vec);\n}\n\n/**\n * Allocate @p count vecs from @p mp in one call and reset each to its default state.\n *\n * Uses Duff's device loop unrolling for efficiency.\n *\n * @param mp\n *   Mempool from which vecs are allocated.\n * @param vecs\n *   Caller-supplied array of pointers, filled with allocated vec objects.\n * @param count\n *   Number of vecs to allocate.\n * @return\n *   0 on success, non-zero if rte_mempool_get_bulk() fails.\n */\nstatic inline int\nvec_alloc_bulk(struct rte_mempool *mp, struct vec **vecs, unsigned count)\n{\n    unsigned idx = 0;\n    int rc;\n\n    rc = rte_mempool_get_bulk(mp, (void **)vecs, count);\n    if (unlikely(rc))\n        return rc;\n\n    /* To understand duff's device on loop unwinding optimization, see\n     * https://en.wikipedia.org/wiki/Duff's_device.\n     * Here while() loop is used rather than do() while{} to avoid extra\n     * check if count is zero.\n     */\n    switch (count % 4) {\n    case 0:\n        while (idx != count) {\n            vec_reset(mp, vecs[idx++]);\n            /* fall-through */\n        case 3:\n            vec_reset(mp, vecs[idx++]);\n            /* fall-through */\n        case 2:\n            vec_reset(mp, vecs[idx++]);\n            /* fall-through */\n        case 1:\n            vec_reset(mp, vecs[idx++]);\n            /* fall-through */\n        }\n    }\n    return 0;\n}\n\n/**\n * Free @p n vecs from the @p vecs array by calling vec_free() on each.\n *\n * @param vecs   Array of vec pointers to free.\n * @param n      Number of entries in @p vecs.\n */\nstatic inline void\nvec_free_bulk(struct vec **vecs, uint32_t n)\n{\n    uint32_t i;\n\n    for (i = 0; i < n; i++)\n        vec_free(vecs[i]);\n}\n\n/**\n * Free every rte_mbuf stored in @p vec and reset its length to zero.\n *\n * Uses Duff's device loop unrolling. The vec itself is NOT returned to its\n * mempool; call vec_free() separately if that is desired.\n *\n * @param vec   Vec whose mbuf entries should be freed. May be NULL.\n */\nstatic inline void\nvec_free_mbufs(struct vec *vec)\n{\n    unsigned idx = 0, count;\n    struct rte_mbuf **mbufs;\n\n    if (!vec)\n        return;\n    count = vec_len(vec);\n    mbufs = vec_list(vec);\n    vec_set_len(vec, 0);\n\n    /* To understand duff's device on loop unwinding optimization, see\n     * https://en.wikipedia.org/wiki/Duff's_device.\n     * Here while() loop is used rather than do() while{} to avoid extra\n     * check if count is zero.\n     */\n    switch (count % 4) {\n    case 0:\n        while (idx != count) {\n            rte_pktmbuf_free(mbufs[idx++]);\n            /* fall-through */\n        case 3:\n            rte_pktmbuf_free(mbufs[idx++]);\n            /* fall-through */\n        case 2:\n            rte_pktmbuf_free(mbufs[idx++]);\n            /* fall-through */\n        case 1:\n            rte_pktmbuf_free(mbufs[idx++]);\n            /* fall-through */\n        }\n    }\n}\n\n/**\n * Set the entry at @p idx to NULL and mark @p vec for compaction.\n *\n * @param vec   Vec to modify.\n * @param idx   Index to clear (must be a valid index).\n */\nstatic inline void\nvec_clr_at_index(struct vec *vec, uint16_t idx)\n{\n    /* Assume idx and vec are valid for vec array */\n    vec->list[idx] = NULL;\n    vec->flags |= VEC_COMPACT_FLAG;\n}\n\n/**\n * Remove NULL gaps from @p vec by shifting remaining entries down.\n *\n * Only runs if VEC_COMPACT_FLAG is set; clears the flag when done.\n *\n * @param vec   Vec to compact in place.\n */\nstatic inline void\nvec_compact(struct vec *vec)\n{\n    void **l1, **l2;\n    uint16_t len;\n    int i;\n\n    if ((vec->flags & VEC_COMPACT_FLAG) == 0)\n        return;\n\n    vec->flags &= ~VEC_COMPACT_FLAG;\n\n    if (!vec->len)\n        return;\n\n    /* Compress out the NULL entries */\n    l1 = l2 = vec->list;\n\n    len      = vec->len;\n    vec->len = 0;\n\n    for (i = 0; i < len; i++) {\n        if (*l1) {\n            if (l1 == l2)\n                l2++;\n            else\n                *l2++ = *l1;\n            vec->len++;\n        }\n        l1++;\n    }\n}\n\n/**\n * Find and remove the first occurrence of @p val from @p vec.\n *\n * Compacts the vec after removal.\n *\n * @param vec   Vec to search and modify.\n * @param val   Pointer value to find and remove.\n */\nstatic inline void\nvec_find_delete(struct vec *vec, void *val)\n{\n    int idx = vec_find_index(vec, val);\n\n    if (idx != -1) {\n        vec_clr_at_index(vec, idx);\n        vec_compact(vec);\n    }\n}\n\n/**\n * Remove and return the first element from @p vec.\n *\n * @param vec   Vec to pop from.\n * @param val   Output: set to the first element's pointer value.\n * @return      1 if an element was popped, 0 if @p vec was empty.\n */\nstatic inline int\nvec_pop(struct vec *vec, void **val)\n{\n    RTE_ASSERT(vec && val);\n\n    if (vec->len) {\n        *val = vec_at_index(vec, 0);\n        vec_clr_at_index(vec, 0);\n        vec_compact(vec);\n        return 1;\n    }\n    return 0;\n}\n\n/**\n * Free the rte_mbuf at index @p idx in @p vec and NULL out its slot.\n *\n * Sets VEC_COMPACT_FLAG; out-of-range @p idx is silently ignored.\n *\n * @param vec   Vec containing the mbuf.\n * @param idx   Index of the mbuf to free.\n */\nstatic inline void\nvec_free_mbuf_at_index(struct vec *vec, uint16_t idx)\n{\n    void *val;\n\n    if (idx >= vec->len)\n        return;\n\n    /* Assume idx is valid for vec array */\n    val = vec->list[idx];\n\n    vec->list[idx] = NULL;\n\n    rte_pktmbuf_free((struct rte_mbuf *)val);\n\n    vec->flags |= VEC_COMPACT_FLAG;\n}\n\n/**\n * Move the entry at @p idx from @p from to @p to.\n *\n * On success, the source slot is cleared (compaction deferred). On failure\n * (destination full), the source entry is left intact.\n *\n * @param to    Destination vec.\n * @param from  Source vec.\n * @param idx   Index in @p from to move.\n * @return      Index assigned in @p to on success, -1 if @p to is full.\n */\nstatic inline int\nvec_move_at_index(struct vec *to, struct vec *from, uint16_t idx)\n{\n    void *v;\n    int rc;\n\n    v  = vec_at_index(from, idx);\n    rc = vec_add1(to, v);\n    if (rc >= 0)\n        vec_clr_at_index(from, idx);\n\n    return rc;\n}\n\n/**\n * Copy the entry at @p idx from @p from into @p to (source is not cleared).\n *\n * @param to    Destination vec.\n * @param from  Source vec.\n * @param idx   Index in @p from to copy.\n */\nstatic inline void\nvec_copy_at_index(struct vec *to, struct vec *from, uint16_t idx)\n{\n    void *v;\n\n    v = vec_at_index(from, idx);\n    vec_add1(to, v);\n}\n\n/**\n * Move the mbuf at @p idx from @p from to @p to; free the mbuf if @p to is full.\n *\n * @param to    Destination vec.\n * @param from  Source vec.\n * @param idx   Index of the mbuf in @p from to move.\n */\nstatic inline void\nvec_move_mbuf(struct vec *to, struct vec *from, uint16_t idx)\n{\n    if (vec_move_at_index(to, from, idx))\n        rte_pktmbuf_free(vec_at_index(from, idx));\n}\n\n/**\n * Retrieve the mbuf at @p idx from @p vec and clear its slot.\n *\n * @param vec   Vec to read from.\n * @param idx   Element index.\n * @return      Pointer to the rte_mbuf at @p idx (may be NULL if already cleared).\n */\nstatic inline struct rte_mbuf *\nvec_get_and_clr(struct vec *vec, uint16_t idx)\n{\n    struct rte_mbuf *m;\n\n    m = vec_at_index(vec, idx);\n    vec_clr_at_index(vec, idx);\n\n    return m;\n}\n\n/**\n * Create a standalone vec (allocated with rte_zmalloc, not from a mempool).\n *\n * @param name   Logical name used for allocation tracking (passed to rte_zmalloc).\n * @param n      Element capacity of the new vec.\n * @param flags  Initial flag bits (see VEC_* constants).\n * @return       Pointer to the new vec, or NULL on allocation failure.\n */\nstruct vec *vec_create(const char *name, unsigned int n, uint16_t flags);\n\n/**\n * Destroy a vec previously created with vec_create().\n *\n * @param vec   Vec to free. If NULL, the call is a no-op.\n */\nvoid vec_destroy(struct vec *vec);\n\n/**\n * Create a DPDK mempool sized to hold vecs with @p entries elements each.\n *\n * @param name         Name for the new mempool.\n * @param n            Number of vec objects in the pool.\n * @param entries      Element capacity of each vec object.\n * @param cache_size   Per-lcore cache size for the mempool.\n * @return             Pointer to the new rte_mempool, or NULL on failure.\n */\nstruct rte_mempool *vec_create_pool(const char *name, unsigned int n, unsigned int entries,\n                                    unsigned int cache_size);\n\n/**\n * Free a mempool previously created with vec_create_pool().\n *\n * @param obj   Mempool to free. If NULL, the call is a no-op.\n */\nvoid vec_destroy_pool(struct rte_mempool *obj);\n\n/**\n * Serialise the pointer values in @p v into a human-readable string.\n *\n * @param v    Vec to serialise.\n * @param buf  Destination buffer.\n * @param len  Size of @p buf in bytes.\n * @return     0 on success, -1 if @p buf is too small.\n */\nint vec_to_data(struct vec *v, char *buf, size_t len);\n\n/**\n * Print a summary of @p vec to file @p f with optional prefix @p msg.\n *\n * @param f     Output file (e.g. stdout).\n * @param msg   Optional prefix message string, or NULL.\n * @param vec   Vec to print.\n */\nvoid vec_print(FILE *f, const char *msg, struct vec *vec);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __VEC_H */\n"
  },
  {
    "path": "meson.build",
    "content": "project('pktgen', 'C',\n    version: run_command(find_program('cat', 'more'),\n        files('VERSION'), check:false).stdout().strip(),\n\n\tlicense: 'BSD',\n\tdefault_options: [\n\t\t'buildtype=release',\n\t\t'default_library=static',\n\t\t'warning_level=3',\n\t\t'werror=true'\n\t],\n\tmeson_version: '>= 0.58.0'\n)\n\npktgen_conf = configuration_data()\n\n# set up some global vars for compiler, platform, configuration, etc.\ncc = meson.get_compiler('c')\npktgen_source_root = meson.current_source_dir()\npktgen_build_root = meson.current_build_dir()\n\ntarget = target_machine.cpu_family()\nif (target != 'riscv64') and (target != 'aarch64')\n    add_project_arguments('-march=native', language: 'c')\nendif\n\nif get_option('enable-avx') and cc.has_argument('-mavx')\n    add_project_arguments('-mavx', language: 'c')\nendif\nif get_option('enable-avx2') and cc.has_argument('-mavx2')\n\tadd_project_arguments('-mavx2', language: 'c')\nendif\nadd_project_arguments('-DALLOW_EXPERIMENTAL_API', language: 'c')\nadd_project_arguments('-D_GNU_SOURCE', language: 'c')\n\n# enable extra warnings and disable any unwanted warnings\nwarning_flags = [\n\t'-Wno-pedantic',\n\t'-Wno-format-truncation',\n]\nforeach arg: warning_flags\n\tif cc.has_argument(arg)\n\t\tadd_project_arguments(arg, language: 'c')\n\tendif\nendforeach\n\nfgen_dep = dependency('libfgen', required: false)\n\nlua_dep = dependency('', required: false)\n\nif get_option('enable_lua')\n\tmessage('>>>>>>>>>>>>> Lua enabled <<<<<<<<<<<<<<')\n\tadd_project_arguments('-DLUA_ENABLED', language: 'c')\n\n\tlua_names = ['lua', 'lua-5.3', 'lua5.3', 'lua-5.4', 'lua5.4']\n\tforeach n:lua_names\n\t\tlua_dep = dependency(n, required: false)\n\t\tif not lua_dep.found()\n\t\t\tlua_dep = cc.find_library(n, required: false)\n\t\tendif\n\t\tif lua_dep.found()\n\t\t\tbreak\n\t\tendif\n\tendforeach\n\tif not lua_dep.found()\n\t\terror('unable to find Lua')\n\tendif\nendif\n\nif get_option('only_docs')\n\tsubdir('tools')\n\tsubdir('docs')\nelse\n\tdpdk = dependency('libdpdk', required: true, method: 'pkg-config')\n\tdpdk_prefix = dpdk.get_variable('prefix')\n\tmessage('prefix: ' + dpdk_prefix + ' libdir: ' + get_option('libdir'))\n\tdpdk_libs_path = join_paths(dpdk_prefix, get_option('libdir'))\n\tmessage('DPDK lib path: ' + dpdk_libs_path)\n\n\tdpdk_bond = cc.find_library('rte_net_bond', dirs: [dpdk_libs_path], required: false)\n\n\tsubdirs =['tools', 'lib', 'examples', 'app', 'docs']\n\tforeach d:subdirs\n\t\tsubdir(d)\n\tendforeach\nendif\n"
  },
  {
    "path": "meson_options.txt",
    "content": "option('enable_lua', type: 'boolean', value: false, description: 'Enable Lua support')\noption('enable_docs', type: 'boolean', value: false, description: 'build documentation')\noption('enable-avx', type: 'boolean', value: true, description: 'Try to compile with AVX support')\noption('enable-avx2', type: 'boolean', value: true, description: 'Try to compile with AVX2 support')\noption('only_docs', type: 'boolean', value: false, description: 'Only build documentation')\n"
  },
  {
    "path": "scripts/latency-samples.lua",
    "content": "-- Latency Demo script\n--\n-- SPDX-License-Identifier: BSD-3-Clause\n\npackage.path = package.path ..\";?.lua;test/?.lua;app/?.lua;../?.lua\"\n\nrequire \"Pktgen\";\n\ndefault_pktsize = 64\ndefault_sleeptime = 10\ndefault_rate = 0.1\n\nfunction getLatency(a)\n    local port_stats = {}\n    local num_lat_pkts = 0\n    local min_cycles = 0\n    local avg_cycles = 0\n    local max_cycles = 0\n    local min_us = 0\n    local avg_us = 0\n    local max_us = 0\n    local mbits_rx = 0\n    local mbits_tx = 0\n\n    if a.pktsize == 0 then a.pktsize = default_pktsize end\n    if a.sleeptime == 0 then a.sleeptime = default_sleeptime end\n    if a.rate < 0 then a.rate = default_rate end\n\n    pktgen.set(a.sendport, \"count\", 0)\n    pktgen.set(a.sendport, \"rate\", a.rate)\n    pktgen.set(a.sendport, \"size\", a.pktsize)\n\n    -- enable latency\n    pktgen.latency(a.sendport, \"enable\");\n    pktgen.latency(a.sendport, \"rate\", 1000);\n    pktgen.latency(a.sendport, \"entropy\", 12);\n    pktgen.latency(a.recvport, \"enable\");\n    pktgen.latency(a.recvport, \"rate\", 10000);\n    pktgen.latency(a.recvport, \"entropy\", 8);\n\n    pktgen.delay(100);\n\n    -- Start traffic\n    pktgen.start(a.sendport)\n    startTime = os.time()\n\n    for i = 1, a.iterations, 1 do\n        pktgen.sleep(a.sleeptime)\n        t1 = os.difftime(os.time(), startTime)\n\n        port_stats = pktgen.pktStats(a.recvport);\n        num_lat_pkts = port_stats[tonumber(a.recvport)].latency.num_pkts\n        num_skipped = port_stats[tonumber(a.recvport)].latency.num_skipped\n        min_cycles = port_stats[tonumber(a.recvport)].latency.min_cycles\n        avg_cycles = port_stats[tonumber(a.recvport)].latency.avg_cycles\n        max_cycles = port_stats[tonumber(a.recvport)].latency.max_cycles\n        min_us = port_stats[tonumber(a.recvport)].latency.min_us\n        avg_us = port_stats[tonumber(a.recvport)].latency.avg_us\n        max_us = port_stats[tonumber(a.recvport)].latency.max_us\n\n        mbits_rx = pktgen.portStats(a.recvport, \"rate\")[tonumber(a.recvport)].mbits_rx\n        mbits_tx = pktgen.portStats(a.sendport, \"rate\")[tonumber(a.sendport)].mbits_tx\n\n        printf(\"%4d %8d %14d %14d %14d %12.2f %12.2f %12.2f %6d %6d %8d\\n\", t1, num_lat_pkts,\n            min_cycles, avg_cycles, max_cycles,\n            min_us, avg_us, max_us, mbits_rx, mbits_tx, num_skipped)\n    end\n    pktgen.stop(a.sendport)\n\n    pktgen.latency(a.sendport, \"disable\");\n    pktgen.latency(a.recvport, \"disable\");\n\n    return 0\nend\n\n-- pktgen.page(\"latency\")\npktgen.screen(\"off\")\n\nprintf(\"Latency Samples\\n\")\nprintf(\"%4s %8s %14s %14s %14s %12s %12s %12s %6s %6s %8s\\n\", \"time\", \"nbPkts\",\n    \"minCycles\", \"avgCycles\", \"maxCycles\",\n    \"min_us\", \"avg_us\", \"max_us\", \"RxMB\", \"TxMB\", \"Skipped\")\npktgen.sleep(2)\n\nmin_latency = getLatency{\n    sendport=0,\n    recvport=0,\n    rate=0.1,\n    pktsize=128,\n    sleeptime=2,\n    iterations=12\n}\n\nprint(\"Done\")\n"
  },
  {
    "path": "scripts/latency.lua",
    "content": "-- Latency Demo script\n--\n-- SPDX-License-Identifier: BSD-3-Clause\n\npackage.path = package.path ..\";?.lua;test/?.lua;app/?.lua;../?.lua\"\n\nrequire \"Pktgen\";\n\nlocal port = 2;\nlocal sleeptime = 10\n\npktgen.stop(port);\n\npktgen.latency(port, \"rate\", 1000) -- 1000us\npktgen.latency(port, \"entropy\", 16) -- adjust sport (sport + (index % N))\n\nprintf(\"Setup port %d for latency packets\\n\", port);\npktgen.clr();\npktgen.delay(100);\n\npktgen.latency(port, \"enable\");\n\npktgen.start(port);\nprintf(\"Sleep for %d seconds\\n\", sleeptime);\npktgen.sleep(sleeptime);\nprintf(\"Sleep is done\\n\");\n\npktgen.stop(port);\npktgen.latency(port, \"disable\");\n\nprintf(\"Sendport Type: %s\\n\", type(port));\nprints(\"SendPort\", pktgen.pktStats(port));\nlocal port_stats = pktgen.pktStats(port);\n\nprintf(\"Number of latency packets on port %d : %d\\n\",\n    port, port_stats[port].latency.num_pkts);\n"
  },
  {
    "path": "scripts/port_stats_dump.lua",
    "content": "-- Dump portStats() including full port_stats_t (pstats) table\n--\n-- SPDX-License-Identifier: BSD-3-Clause\n--\n-- Usage examples:\n--   pktgen -f scripts/port_stats_dump.lua\n--\n-- Optional overrides (edit in this file before running):\n--   PORTLIST = \"0-3\"\n\npackage.path = package.path .. \";?.lua;test/?.lua;app/?.lua;../?.lua\"\n\nrequire \"Pktgen\"\n\nPORTLIST = PORTLIST or \"0-1\"\n\nlocal function _key_to_string(k)\n    if type(k) == \"string\" then\n        return string.format(\"%q\", k)\n    end\n    return tostring(k)\nend\n\nlocal function dump_table(value, indent, visited)\n    indent = indent or \"\"\n    visited = visited or {}\n\n    if type(value) ~= \"table\" then\n        print(indent .. tostring(value))\n        return\n    end\n\n    if visited[value] then\n        print(indent .. \"<cycle>\")\n        return\n    end\n\n    visited[value] = true\n\n    local keys = {}\n    for k in pairs(value) do\n        keys[#keys + 1] = k\n    end\n\n    table.sort(keys, function(a, b)\n        local ta, tb = type(a), type(b)\n        if ta == tb then\n            if ta == \"number\" then\n                return a < b\n            end\n            return tostring(a) < tostring(b)\n        end\n        if ta == \"number\" then\n            return true\n        end\n        if tb == \"number\" then\n            return false\n        end\n        return ta < tb\n    end)\n\n    print(indent .. \"{\")\n    local nextIndent = indent .. \"  \"\n\n    for _, k in ipairs(keys) do\n        local v = value[k]\n        io.write(nextIndent .. \"[\" .. _key_to_string(k) .. \"] = \")\n        if type(v) == \"table\" then\n            io.write(\"\\n\")\n            dump_table(v, nextIndent, visited)\n        else\n            io.write(tostring(v) .. \"\\n\")\n        end\n    end\n\n    print(indent .. \"}\")\n\n    visited[value] = nil\nend\n\nlocal stats = pktgen.portStats(PORTLIST)\nprint(string.format(\"pktgen.portStats(%q)\", PORTLIST))\ndump_table(stats)\n"
  },
  {
    "path": "scripts/rfc2544.lua",
    "content": "-- RFC2544 PktGen Throughput Test\n-- as defined by https://www.ietf.org/rfc/rfc2544.txt\n--  SPDX-License-Identifier: BSD-3-Clause\n--\n--  Improved 2x100GBE version v1.1 - Contributed by Niclas Edin 2022\n--  Set 4 CPU cores per Rx:Tx function for +200MPPS performance, using Intel Ice Lake-SP platform with C810 NICs.\n--  Using ranges for RSS multiq support and setting multiple IP pairs for better DUT load balancing\n--  To avoid 0-loss test related issues, a loss tolerance value is used to get as close to good-put as possible.\n--\n--  For C810 100GbE PPS performance and avoiding Tx degradation, MBUFS need to be increased and burst size lowered to 64 test has shown.\n--  Could also be a factor for other NICs.\n--  Also consider setting isolcpu at the kernel cmdline for best DPDK performance as per DPDK-Performance-guide documentation\n--  app/pktgen_constants.h:\n--    DEFAULT_PKT_BURST = 64, /* Increasing this number consumes memory very fast */\n--    DEFAULT_RX_DESC   = (DEFAULT_PKT_BURST * 16),\n--    DEFAULT_TX_DESC   = DEFAULT_RX_DESC * 2,\n--    MAX_MBUFS_PER_PORT = (DEFAULT_TX_DESC * 10), /* number of buffers to support per port */\n--\n--\n-- Load from within PktGen CLI.\n--\n\npackage.path = package.path ..\";?.lua;test/?.lua;app/?.lua;../?.lua\"\n\nrequire \"Pktgen\";\nlocal DEBUG\t\t\t= true;\nlocal USE_RAMPUP\t= true; \t--Rampup traffic flow before starting measurement, less accurate measurement (trial time,drops).\nlocal SHOW_STATUS\t= true;\n--\n-- Logfile for testresult\nlocal logfile \t\t= \"RFC2544_throughput_results.txt\";\n\n-- define packet sizes to test\nlocal pkt_sizes\t= { 64, 128, 256, 512, 1024, 1280, 1518 };\n\n-- Set max rate platform profile for PktGen to avoid crashing some interfaces during overload (XXV710/C810)\n-- Could be mitigated via setting MBUF as top comment describes.\n-- Need to be evaluated per pktgen platform setup with a loopback test before testing DUT\nlocal pktgen_limit\t= {\n  \t\t\t\t[64]   = { [\"rate\"] = 78 },\t-- 68% eq 200MPPS on 2x100GbE\n  \t\t\t\t[128]  = { [\"rate\"] = 100 },\n  \t\t\t\t[256]  = { [\"rate\"] = 100 },\n  \t\t\t\t[512]  = { [\"rate\"] = 100 },\n  \t\t\t\t[1024] = { [\"rate\"] = 100 },\n  \t\t\t\t[1280] = { [\"rate\"] = 100 },\n  \t\t\t\t[1518] = { [\"rate\"] = 100 },\n    \t\t\t}\n\n-- Time in milliseconds to transmit for\nlocal duration\t\t\t= 10000;\nlocal confirmDuration\t= 60000;\t\t\t-- If set to 0, Confirm run is skipped.\nlocal pauseTime\t\t\t= 3000;\nlocal graceTime\t\t\t= 5000;\t\t\t\t-- Ramp Up grace time\nlocal numIterations\t\t= 20;\t \t\t\t-- Maximum number of divide n conquer iterations\nlocal retryCount\t\t= 3;\t\t\t\t-- Number of Trial retries if Tx underrun happens\nlocal initialRate\t\t= 100;\n--\n-- Test tolerance settings\nlocal loss_tol          = 0.10;\nlocal seekPrec\t\t\t= 0.10;\t\t\t\t-- Divide n conquer seek precision\n\n-- define the ports in use TODO: Use all ports configured in PktGen instead.\nlocal sendport\t\t\t= \"0\";\nlocal recvport\t\t\t= \"1\";\n\n-- Port config to use TODO: Use json config file instead.\n-- Flows ~ num_ip * num_udpp * 4. Max 10M flows. Requires longer RampUp time in some cases.\nlocal num_ip\t\t= 10; \t\t\t\t\t-- Number of IPs per interface, max 253. Set DUT IP at .254.\nlocal num_udpp\t\t= 100;\t\t\t\t\t-- Number of UDP ports used per IP, max 9999.\nlocal srcip\t\t\t= \"192.168.10.1\";\nlocal max_srcip\t\t= \"192.168.10.\"..num_ip;\nlocal dstip\t\t\t= \"192.168.20.1\";\nlocal max_dstip\t\t= \"192.168.20.\"..num_ip;\nlocal netmask\t\t= \"/24\";\nlocal p0_udpp_src\t= 10000;\nlocal p0_udpp_dst\t= 20000;\nlocal p1_udpp_src\t= 30000;\nlocal p1_udpp_dst\t= 40000;\n--local p0_destmac\t= \"b4:96:91:00:00:01\";\t-- Comment out to test Pktgen loopback\n--local p1_destmac\t= \"b4:96:91:00:00:02\";\t-- Set to correct DUT MACs\n\n-- Global variables\nlocal sent\t\t\t= 0;\nlocal loss          = 0;\nlocal loss_limit\t= 0;\nlocal adjust_rate\t= 0;\n\nlocal function init()\n\tif SHOW_STATUS\n\tthen\n\t\tpktgen.screen(\"on\");\n\tend\n\tpktgen.page(\"0\");\n\tpktgen.pause(\"[  INFO ] Starting RFC-2544 script\\n\",5000);\n\tprint(\"[NOTICE ] See test log for results: \"..logfile);\nend\n\nprintf = function(s,...)\n           print(s:format(...));\n         end\n\nsprintf = function(s,...)\n           return s:format(...)\n         end\n\ndprint = function(...)\n\t   if DEBUG\n\t   then\n\t\t   print(...);\n\t   end\n         end\n\nfunction Round(num, dp)\n    local mult = 10^(dp or 0)\n    return math.floor(num * mult + 0.5)/mult\nend\n\nlocal function setupTraffic()\n\tpktgen.reset(\"all\");\n\n\tpktgen.set(\"all\", \"burst\", 64);\n\n\tpktgen.set_type(\"all\", \"ipv4\");\n\tpktgen.set_proto(sendport..\",\"..recvport, \"udp\");\n\n\t-- Set Single packet profile just for visibility in main screen\n\tpktgen.set_ipaddr(sendport, \"dst\", dstip);\n\tpktgen.set_ipaddr(sendport, \"src\", srcip..netmask);\n\n\tpktgen.set_ipaddr(recvport, \"dst\", srcip);\n\tpktgen.set_ipaddr(recvport, \"src\", dstip..netmask);\n\n\tpktgen.set(sendport, \"sport\", p0_udpp_src);\n\tpktgen.set(sendport, \"dport\", p0_udpp_dst);\n\tpktgen.set(recvport, \"sport\", p1_udpp_src);\n\tpktgen.set(recvport, \"dport\", p1_udpp_dst);\n\n\n\tif p0_destmac then pktgen.set_mac(sendport, \"dst\", p0_destmac); end\n\tif p1_destmac then pktgen.set_mac(recvport ,\"dst\", p1_destmac); end\n\n\t-- Set up Range configuration\n\tpktgen.range.ip_proto(\"all\", \"udp\");\n\n\tif p0_destmac then pktgen.range.dst_mac(sendport, \"start\", p0_destmac); end\n\tif p1_destmac then pktgen.range.dst_mac(recvport, \"start\", p1_destmac); end\n\n\tpktgen.range.dst_ip(sendport, \"start\", dstip);\n\tpktgen.range.dst_ip(sendport, \"inc\", \"0.0.0.1\");\n\tpktgen.range.dst_ip(sendport, \"min\", dstip);\n\tpktgen.range.dst_ip(sendport, \"max\", max_dstip);\n\n\tpktgen.range.src_ip(sendport, \"start\", srcip);\n\tpktgen.range.src_ip(sendport, \"inc\", \"0.0.0.1\");\n\tpktgen.range.src_ip(sendport, \"min\", srcip);\n\tpktgen.range.src_ip(sendport, \"max\", max_srcip);\n\n\tpktgen.range.dst_ip(recvport, \"start\", srcip);\n\tpktgen.range.dst_ip(recvport, \"inc\", \"0.0.0.1\");\n\tpktgen.range.dst_ip(recvport, \"min\", srcip);\n\tpktgen.range.dst_ip(recvport, \"max\", max_srcip);\n\n\tpktgen.range.src_ip(recvport, \"start\",dstip);\n\tpktgen.range.src_ip(recvport, \"inc\", \"0.0.0.1\");\n\tpktgen.range.src_ip(recvport, \"min\", dstip);\n\tpktgen.range.src_ip(recvport, \"max\", max_dstip);\n\n\tpktgen.range.src_port(sendport, \"start\", p0_udpp_src);\n\tpktgen.range.src_port(sendport, \"inc\", 1);\n\tpktgen.range.src_port(sendport, \"min\", p0_udpp_src);\n\tpktgen.range.src_port(sendport, \"max\", p0_udpp_src + num_udpp);\n\n\tpktgen.range.dst_port(sendport, \"start\", p0_udpp_dst);\n\tpktgen.range.dst_port(sendport, \"inc\", 1);\n\tpktgen.range.dst_port(sendport, \"min\", p0_udpp_dst);\n\tpktgen.range.dst_port(sendport, \"max\", p0_udpp_dst + num_udpp);\n\n\tpktgen.range.src_port(recvport, \"start\", p1_udpp_src);\n\tpktgen.range.src_port(recvport, \"inc\", 1);\n\tpktgen.range.src_port(recvport, \"min\", p1_udpp_src);\n\tpktgen.range.src_port(recvport, \"max\", p1_udpp_src + num_udpp);\n\n\tpktgen.range.dst_port(recvport, \"start\", p1_udpp_dst);\n\tpktgen.range.dst_port(recvport, \"inc\", 1);\n\tpktgen.range.dst_port(recvport, \"min\", p1_udpp_dst);\n\tpktgen.range.dst_port(recvport, \"max\", p1_udpp_dst + num_udpp);\n\n\tpktgen.range.ttl(\"all\", \"start\", 64);\n\tpktgen.range.ttl(\"all\", \"inc\", 0);\n\tpktgen.range.ttl(\"all\", \"min\", 64);\n\tpktgen.range.ttl(\"all\", \"max\", 64);\n\n\tpktgen.set_range(\"all\", \"on\");\n\n\tpktgen.pause(\"[  INFO ] Starting Test\\n\",2000);\n\nend\n\nlocal function logResult(s)\n\tprint(s);\n        file:write(s..\"\\n\");\n        file:flush();\nend\n\nlocal function getLinkSpeed()\n\tlocal port_link;\n\n        port_link = string.match(pktgen.linkState(sendport)[tonumber(sendport)],\"%d+\");\n\n\treturn tonumber(port_link);\nend\n\n-- Get Max PPS for one interface\nlocal function getMaxPPS(pkt_size)\n\tlocal max_pps, port_link;\n\n    port_link = string.match(pktgen.linkState(sendport)[tonumber(sendport)],\"%d+\");\n\tmax_pps = Round((port_link * 10^6)/((pkt_size + 20) * 8));\n\n\treturn max_pps;\nend\n\nlocal function getDuration(pkt_size, set_rate, num_pkts)\n\tlocal test_dur, max_pps;\n\n\tmax_pps = getMaxPPS(pkt_size) * 2; --Bidir\n    test_dur = num_pkts / (max_pps * (set_rate/100));\n\n\treturn(test_dur * 1000);\nend\n\nlocal function checkRate(pkt_size, set_rate, duration, num_pkts)\n\tlocal calc_pps, max_pps, test_rate, calc_duration, rate_ok = false;\n\n\t-- Verify that actual duration is not much lower than set duration\n\t-- This is when PktGen is unable to produce Tx Rate.\n\t-- Otherwize use the actual duration to calculate test rate\n\tcalc_duration = getDuration(pkt_size, set_rate, num_pkts);\n\tif calc_duration > (duration - 0.1)\n\tthen\n\t\tduration = calc_duration;\n\tend\n\n\tmax_pps = (getMaxPPS(pkt_size) * 2); --x2 for bidir\n    calc_pps = (num_pkts / ((duration) / 1000));\n    test_rate = Round(((calc_pps / max_pps)*100),3);\n    set_rate = Round(set_rate, 3);\n\tdprint(\"[ DEBUG ] Link:\"..(getLinkSpeed()//1000)..\"GBE | Max MPPS:\"..Round(max_pps/10^6,2)..\" | Test MPPS:\"..Round(calc_pps/10^6,2)..\" | Dur: \"..duration..\" | Calc Rate:\"..test_rate..\" < \"..set_rate..\":Set Rate\");\n\n\tif (test_rate - set_rate) < -0.1\n\tthen\n\t\tlogResult(\"[WARNING] Set rate \"..set_rate..\" is not reached (\"..test_rate..\" < \"..set_rate..\"), PktGen is unable to reach Tx target\");\n\t\tadjust_rate = test_rate;\n\t\trate_ok = false;\n\telse\n\t\tadjust_rate = set_rate;\n\t\trate_ok = true;\n\tend\n\n\treturn(rate_ok);\nend\n\nlocal function getTxPkts(set_rate, pkt_size, test_dur)\n\tlocal max_pps, num_pkts;\n\n\tmax_pps = getMaxPPS(pkt_size);\n\tnum_pkts = Round((max_pps  * (set_rate/100))*(test_dur/1000));\n\tdprint(\"[ DEBUG ] getTxPkts Rate: \"..set_rate..\"% | Num Pkts:\"..num_pkts..\" | Duration:\"..test_dur/1000);\n\n\treturn num_pkts;\nend\n\nlocal function getPPS(num_pkts, test_dur)\n\tlocal test_pps;\n\n\ttest_pps = Round(num_pkts / (test_dur/1000));\n\n\treturn test_pps;\nend\n\nlocal function getMbpsL2(num_pkts, pkt_size, test_dur)\n\tlocal test_mbps, test_pps;\n\n\ttest_pps = getPPS(num_pkts, test_dur);\n\ttest_mbps = Round((test_pps * (pkt_size * 8))/10^6,3);\n\n\treturn test_mbps;\nend\n\nlocal function getMbpsL1(num_pkts, pkt_size, test_dur)\n\tlocal test_mbps, test_pps;\n\n\ttest_pps = getPPS(num_pkts, test_dur);\n\ttest_mbps = Round((test_pps * ((pkt_size + 20)* 8))/10^6,3);\n\n\treturn test_mbps;\nend\n\n\nlocal function printLine()\n\tprint(\"-------------------------------------------------------------------------------------------------------------\");\n\tfile:write(\"--------------------------------------------------------------------------------------------------------------\\n\");\n\tfile:flush();\nend\n\n\nlocal function runTrial(pkt_size, rate, duration, count, retry)\n\tlocal num_tx, num_tx1, num_rx, num_dropped, tx_mpps, rx_mpps, tx_gbps1, rx_gbps1, tx_gbps2, rx_gbps2, rate_notif = \" \";\n\n\tpktgen.clr();\n\tif count == 1 and retry == 1\n\tthen\n\t\tpktgen.page(\"range\");\n\tend\n\tpktgen.set(\"all\", \"rate\", rate);\n\tpktgen.set(\"all\", \"size\", pkt_size);\n\n\tpktgen.range.pkt_size(\"all\", \"start\", pkt_size);\n\tpktgen.range.pkt_size(\"all\", \"inc\", 0);\n\tpktgen.range.pkt_size(\"all\", \"min\", pkt_size);\n\tpktgen.range.pkt_size(\"all\", \"max\", pkt_size);\n\tif count == 1 and retry == 1\n\tthen\n\t\tpktgen.pause(\"[  INFO ] IP Range , setting \"..pkt_size..\"B\\n\",5000);\n\t\tpktgen.page(\"0\");\n\tend\n\n\tprintLine();\n\tprint(\"[  INFO ] Starting Test...\");\n\n\t-- Use RAMPUP with graceTime seconds of traffic before test or just send exact number of packets\n\tif USE_RAMPUP\n\tthen\n\t\tpktgen.set(\"all\", \"count\", 0);\n\t\tpktgen.start(\"all\"); \t\t\t-- Bidir\n\t\tprint(\"[  INFO ] Ramping up Flows...\");\n\t\tpktgen.delay(graceTime);\n\t\tpktgen.clr();\n\telse\n\t\tpktgen.set(\"all\", \"count\", getTxPkts(rate, pkt_size, (duration)));\n\t\tpktgen.start(\"all\"); \t\t\t-- Bidir\n\t\tpktgen.delay(250); \t\t\t\t-- Extra time to account for start stop events\n\tend\n\tpktgen.delay(duration);\n\tpktgen.stop(\"all\");\n\n\tpktgen.delay(pauseTime);\n\tpktgen.pause(\"[  INFO ] Waiting for idling packets...\\n\",2000);\n\n\tstatTx = pktgen.portStats(sendport, \"port\")[tonumber(sendport)];\n\tstatRx = pktgen.portStats(recvport, \"port\")[tonumber(recvport)];\n\n\t-- Check that port Tx opackets are within reason.\n\t-- This happens if one interface LAGs behind, will result in Tx underruns in iterations.\n\t-- Included headroom for RAMP-UP mode as it is always lagging for one interface when test stops\n\tnum_tx = statTx.opackets;\n\tnum_tx1 = statRx.opackets;\n\tif (num_tx + 5000) < num_tx1\n\tthen\n\t\tprint(\"[WARNING] Port:\"..sendport..\" Tx underruns.Results might be inconclusive.(\"..Round(num_tx - num_tx1)..\")\");\n\t\tdprint(\"[ DEBUG ] CPU probably overloaded.\");\n\n\telseif (num_tx1 + 5000) < num_tx\n\tthen\n\t\tprint(\"[WARNING] Port:\"..recvport..\" Tx underruns. Results might be inconclusive.(\"..Round(num_tx1 - num_tx)..\")\");\n\t\tdprint(\"[ DEBUG ] CPU probably overloaded.\");\n\telse\n\t\tdprint(\"[ DEBUG ] No Tx underruns are detected.\");\n\tend\n\n\tnum_tx = num_tx + num_tx1;\n\tnum_rx = statRx.ipackets + statTx.ipackets;\n\tnum_dropped = num_tx - num_rx;\n\tsent = num_tx;\n\tloss = Round((1-(num_rx/num_tx))*100,3);\n\tloss_limit = Round(sent*(loss_tol/100));\n\n\n\t-- checkRate: Validate PktGen Tx rate and duration to get adjusted rate if tx underruns happens\n\tif checkRate(pkt_size, rate, duration, num_tx)\n\tthen\n\t\trate_notif = \" \";\n\telse\n\t\tprint(\"[NOTICE ] Actual Rate is: \"..adjust_rate..\"%\");\n\t\trate = adjust_rate;\n\t\trate_notif = \"!\"; -- Mark PktGen Max rate in table\n\tend\n\tduration = getDuration(pkt_size, rate, num_tx);\n\n\ttx_mpps = getPPS(num_tx, duration)/10^6;\n\trx_mpps = getPPS(num_rx, duration)/10^6;\n\ttx_gbps1 = getMbpsL1(num_tx, pkt_size, duration)/1000;\n\trx_gbps1 = getMbpsL1(num_rx, pkt_size, duration)/1000;\n\ttx_gbps2 = getMbpsL2(num_tx, pkt_size, duration)/1000;\n\trx_gbps2 = getMbpsL2(num_rx, pkt_size, duration)/1000;\n\n\tlogResult(sprintf(\"[ %4dB ] Trial %5s.%1d   | Rate:%s %-6.2f%%  | Pkt Size: %4dB | Dur.: %3.2fs | Loss Tol.: %-5.3f%%\", pkt_size, tostring(count), retry, rate_notif, rate, pkt_size, Round(duration/1000,3), loss_tol));\n\tlogResult(sprintf(\"[ %4dB ] Tx: %-11d | Rx: %-11d | Drop: %-13d (%-6.3f%%)  | Loss Lim.: %-10.0f\", pkt_size, num_tx, num_rx, num_dropped, loss, loss_limit));\n\tlogResult(sprintf(\"[ %4dB ] MPPS Tx: %-06.2f | MPPS Rx: %-06.2f | Gbps Tx L1/L2: %-6.2f / %-6.2f | Gbps Rx L1/L2: %-6.2f / %-6.2f\", pkt_size, tx_mpps, rx_mpps, tx_gbps1, tx_gbps2, rx_gbps1, rx_gbps2));\n\n\tpktgen.delay(pauseTime);\n\n\treturn num_dropped;\nend\n\nlocal function runThroughputTest(pkt_size)\n\tlocal num_dropped, max_rate, min_rate, trial_rate, max_min_diff, no_adjust = true;\n\tmax_rate = pktgen_limit[pkt_size].rate;\n\tmin_rate = 1;\n\ttrial_rate = initialRate;\n\tif trial_rate > max_rate then trial_rate = max_rate; end\n\n\tfor count=1, numIterations, 1\n\tdo\n\t\tfor retry=1, retryCount, 1\n\t\tdo\n\t\t\tnum_dropped = runTrial(pkt_size, trial_rate, duration, count, retry);\n\t\t\tif (adjust_rate + 0.1) >= trial_rate\n\t\t\tthen\n\t\t\t\tbreak;\n\t\t\telse\n\t\t\t\t-- If adjusted Tx and at 100% whilst packet drops,\n\t\t\t\t-- DUT is the bottleneck so no retries is needed.\n\t\t\t\tif trial_rate == 100 and num_dropped > loss_limit\n\t\t\t\tthen\n\t\t\t\t\tbreak;\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif retry < retryCount\n\t\t\tthen\n\t\t\t\tdprint(\"[ DEBUG ] Retrying Trial...\");\n\t\t\t\tpktgen.delay(pauseTime);\n\t\t\tend\n\t\tend\n\t\tif (adjust_rate + 0.1) < trial_rate\n\t\tthen\n\t\t\ttrial_rate = adjust_rate;\n\t\t\tmax_rate = trial_rate;\n\t\t\tno_adjust = false;\n\t\telse\n\t\t\tno_adjust = true;\n\t\tend\n\n\t\tif num_dropped <= loss_limit\n\t\tthen\n\t\t\t-- Undershoot\n\t\t\tif no_adjust\n\t\t\tthen\n\t\t\t\tmin_rate = trial_rate;\n\t\t\tend\n\t\t\t--if min_rate == pktgen_limit[pkt_size].rate then break; end\n\t\t\tif min_rate == max_rate then break; end\n\t\t\ttrial_rate = min_rate + 1 + ((max_rate - min_rate)/2);\t-- Add +1 to get to 100% if initial rate is set to a lower value.\n\t\t\tif trial_rate > max_rate then trial_rate = max_rate; end\n\t\telse\n\t\t\t-- Overshoot\n\t\t\tmax_rate = trial_rate;\n\t\t\tif count > 2\n\t\t\tthen\n\t\t\t\ttrial_rate = max_rate - ((max_rate - min_rate)/2);\n\t\t\telse\n\t\t\t\t-- First 2 iterations trying to do rapid find by using loss% to set trial rate\n\t\t\t\ttrial_rate = trial_rate - ((trial_rate/100)*loss);\n\t\t\tend\n\t\tend\n\n\t\tmax_min_diff = max_rate - min_rate;\n\t\tdprint(\"[ DEBUG ] Max - Min rate diff: \"..Round(max_min_diff,5));\n\t\tif max_min_diff <= seekPrec\n\t\tthen\n\t\t\tdprint(\"[ DEBUG ] Stopping seek iterations, reached seek Precision limit\");\n\t\t\tbreak;\n\t\tend\n\tend\n\n\tif confirmDuration > 0\n\tthen\n\t\t-- Ensure we test confirmation run with the last succesfull drop rate\n\t\ttrial_rate = min_rate;\n\n\t\t-- confirm throughput rate for at least 60 seconds\n\t\tnum_dropped = runTrial(pkt_size, trial_rate, confirmDuration, \"Final\", 0);\n\n\t\tprintLine();\n\t\tif num_dropped <= loss_limit\n\t\tthen\n\t\t\tprint(\"[RESULT ] Max rate for packet size \"  .. pkt_size .. \"B is: \" .. adjust_rate..\"%\");\n\t\t\tfile:write(\"[RESULT ] Max rate for packet size \"  .. pkt_size .. \"B is: \" .. adjust_rate .. \"%\\n\\n\");\n\t\telse\n\t\t\tprint(\"[WARNING] Max rate of \" .. trial_rate .. \"% could not be confirmed for 60 seconds as required by rfc2544.\");\n\t\t\tfile:write(\"[WARNING] Max rate of \" .. trial_rate .. \"% could not be confirmed for 60 seconds as required by rfc2544.\" .. \"\\n\\n\");\n\t\tend\n\t\tfile:flush();\n\tend\n\tpktgen.delay(pauseTime);\nend\n\nfunction main()\n\tlocal dummy;\n\tfile = io.open(logfile, \"w\");\n\tsetupTraffic();\n\tpktgen.pause(\"[  INFO ] Priming DUT...\\n\",1000);\n\tdummy = runTrial(512, 1, 5000, \"Prime\",0);\n\tfile:write(\"\\n\");\n        if dummy >= (sent * loss_tol)\n\tthen\n\t \tpktgen.pause(\"[WARNING] DUT seem to be dropping packets, stopping test.\\n\",1000);\n\telse\n\t\tpktgen.pause(\"[  INFO ] Starting Trial Runs ...\\n\",3000);\n\t\tfor _,size in pairs(pkt_sizes)\n\t\tdo\n\t\t\trunThroughputTest(size);\n\t  \tend\n\tend\n\tprintLine();\n\tfile:close();\n\tprint(\"[NOTICE ] See test log for results: \"..logfile);\nend\ninit();\nmain();\n"
  },
  {
    "path": "scripts/rfc2544_tput_test.lua",
    "content": "-- RFC2544 Throughput Test\n-- as defined by https://www.ietf.org/rfc/rfc2544.txt\n--  SPDX-License-Identifier: BSD-3-Clause\n\npackage.path = package.path ..\";?.lua;test/?.lua;app/?.lua;../?.lua\"\n\nrequire \"Pktgen\";\n\n-- define packet sizes to test\nlocal pkt_sizes\t\t= { 64, 128, 256, 512, 1024, 1280, 1518 };\n\n-- Time in seconds to transmit for\nlocal duration\t\t= 10000;\nlocal confirmDuration\t= 60000;\nlocal pauseTime\t\t= 1000;\n\n-- define the ports in use\nlocal sendport\t\t= \"0\";\nlocal recvport\t\t= \"1\";\n\n-- ip addresses to use\nlocal dstip\t\t= \"90.90.90.90\";\nlocal srcip\t\t= \"1.1.1.1\";\nlocal netmask\t\t= \"/24\";\n\nlocal initialRate\t= 50 ;\n\nlocal function setupTraffic()\n\tpktgen.set_ipaddr(sendport, \"dst\", dstip);\n\tpktgen.set_ipaddr(sendport, \"src\", srcip..netmask);\n\n\tpktgen.set_ipaddr(recvport, \"dst\", srcip);\n\tpktgen.set_ipaddr(recvport, \"src\", dstip..netmask);\n\n\tpktgen.set_proto(sendport..\",\"..recvport, \"udp\");\n\t-- set Pktgen to send continuous stream of traffic\n\tpktgen.set(sendport, \"count\", 0);\nend\n\nlocal function runTrial(pkt_size, rate, duration, count)\n\tlocal num_tx, num_rx, num_dropped;\n\n\tpktgen.clr();\n\tpktgen.set(sendport, \"rate\", rate);\n\tpktgen.set(sendport, \"size\", pkt_size);\n\n\tpktgen.start(sendport);\n\tprint(\"Running trial \" .. count .. \". % Rate: \" .. rate .. \". Packet Size: \" .. pkt_size .. \". Duration (mS):\" .. duration);\n\tfile:write(\"Running trial \" .. count .. \". % Rate: \" .. rate .. \". Packet Size: \" .. pkt_size .. \". Duration (mS):\" .. duration .. \"\\n\");\n\tpktgen.delay(duration);\n\tpktgen.stop(sendport);\n\n\tpktgen.delay(pauseTime);\n\n\tstatTx = pktgen.portStats(sendport, \"port\")[tonumber(sendport)];\n\tstatRx = pktgen.portStats(recvport, \"port\")[tonumber(recvport)];\n\tnum_tx = statTx.opackets;\n\tnum_rx = statRx.ipackets;\n\tnum_dropped = num_tx - num_rx;\n\n\tprint(\"Tx: \" .. num_tx .. \". Rx: \" .. num_rx .. \". Dropped: \" .. num_dropped);\n\tfile:write(\"Tx: \" .. num_tx .. \". Rx: \" .. num_rx .. \". Dropped: \" .. num_dropped .. \"\\n\");\n\tpktgen.delay(pauseTime);\n\n\treturn num_dropped;\nend\n\nlocal function runThroughputTest(pkt_size)\n\tlocal num_dropped, max_rate, min_rate, trial_rate;\n\n\tmax_rate = 100;\n\tmin_rate = 1;\n\ttrial_rate = initialRate;\n\tfor count=1, 10, 1\n\tdo\n\t\tnum_dropped = runTrial(pkt_size, trial_rate, duration, count);\n\t\tif num_dropped == 0\n\t\tthen\n\t\t\tmin_rate = trial_rate;\n\t\telse\n\t\t\tmax_rate = trial_rate;\n\t\tend\n\t\ttrial_rate = min_rate + ((max_rate - min_rate)/2);\n\tend\n\n\t-- Ensure we test confirmation run with the last succesfull zero-drop rate\n\ttrial_rate = min_rate;\n\n\t-- confirm throughput rate for at least 60 seconds\n\tnum_dropped = runTrial(pkt_size, trial_rate, confirmDuration, \"Confirmation\");\n\tif num_dropped == 0\n\tthen\n\t\tprint(\"Max rate for packet size \"  .. pkt_size .. \"B is: \" .. trial_rate);\n\t\tfile:write(\"Max rate for packet size \"  .. pkt_size .. \"B is: \" .. trial_rate .. \"\\n\\n\");\n\telse\n\t\tprint(\"Max rate of \" .. trial_rate .. \"% could not be confirmed for 60 seconds as required by rfc2544.\");\n\t\tfile:write(\"Max rate of \" .. trial_rate .. \"% could not be confirmed for 60 seconds as required by rfc2544.\" .. \"\\n\\n\");\n\tend\nend\n\nfunction main()\n\tfile = io.open(\"RFC2544_throughput_results.txt\", \"w\");\n\tsetupTraffic();\n\tfor _,size in pairs(pkt_sizes)\n\tdo\n\t\trunThroughputTest(size);\n\tend\n\tfile:close();\nend\n\nmain();\n"
  },
  {
    "path": "scripts/traffic-profile.lua",
    "content": "-- CGCS Demo script\n--\n-- SPDX-License-Identifier: BSD-3-Clause\n\npackage.path = package.path ..\";?.lua;test/?.lua;app/?.lua;../?.lua\"\n\nrequire \"Pktgen\";\n\nlocal time_step = 10;\t\t-- seconds\nlocal pcnt_rate = { 10, 20, 40, 60, 60, 60, 60, 60, 60, 80, 80, 80, 80, 80, 70, 70, 70, 70, 60, 60, 60, 40, 40, 70, 70, 80, 80, 40, 40, 40 };\n\nsendport\t= 0;\nrecvport\t= 0;\npkt_size\t= 64;\nlocal dstip = \"10.10.0.100\";\nlocal srcip = \"10.10.0.101\";\nlocal netmask = \"/24\";\ntotal_time = 0;\n\n-- Take two lists and create one table with a merged value of the tables.\n-- Return a set or table = { { timo, rate }, ... }\nfunction Set(step, list)\n\tlocal\tset = { };\t\t-- Must have a empty set first.\n\n\tfor i,v in ipairs(list) do\n\t\tset[i] = { timo = step, rate = v };\n\tend\n\n\treturn set;\nend\n\nfunction main()\n\tlocal sending = 0;\n\tlocal trlst = Set(time_step, pcnt_rate);\n\n\t-- Stop the port sending and reset to\n\tpktgen.stop(sendport);\n\tsleep(2);\t\t\t\t\t-- Wait for stop to happen (not really needed)\n\n\t-- Set up the default packet size fixed value for now.\n\tpktgen.set(sendport, \"size\", pkt_size);\n\n\tpktgen.set_ipaddr(sendport, \"dst\", dstip);\n\tpktgen.set_ipaddr(sendport, \"src\", srcip..netmask);\n\n\tpktgen.set_proto(sendport..\",\"..recvport, \"udp\");\n\n\ttotal_time = 0;\n\t-- v is the table to values created by the Set(x,y) function\n\tfor _,v in pairs(trlst) do\n\n\t\tprintf(\"   Percent load %d for %d seconds\\n\", v.rate, v.timo);\n\n\t\t-- Set the rate to the new value\n\t\tpktgen.set(sendport, \"rate\", v.rate);\n\n\t\t-- If not sending packets start sending them\n\t\tif ( sending == 0 ) then\n\t\t\tpktgen.start(sendport);\n\t\t\tsending = 1;\n\t\tend\n\n\t\t-- Sleep until we need to move to the next rate and timeout\n\t\tsleep(v.timo);\n\t\ttotal_time = total_time + v.timo;\n\n\tend\n\n\t-- Stop the port and do some cleanup\n\tpktgen.stop(sendport);\n\tsending = 0;\nend\n\nprintf(\"\\n**** Traffic Profile Rate for %d byte packets ***\\n\", pkt_size);\nmain();\nprintf(\"\\n*** Traffic Profile Done (Total Time %d) ***\\n\", total_time);\n"
  },
  {
    "path": "style/call_GNU_Indent.pro",
    "content": "-kr\n-bad\n-bap\n-sob\n-ut\n-i8\n"
  },
  {
    "path": "style/call_GNU_Indent.sh",
    "content": "#!/bin/sh\n\nif [ ! -n \"$1\" ]; then\necho \"Syntax is: recurse.sh dirname filesuffix\"\necho \"Syntax is: recurse.sh filename\"\necho \"Example: recurse.sh temp cpp\"\nexit 1\nfi\n\nif [ -d \"$1\" ]; then\n#echo \"Dir ${1} exists\"\nif [ -n \"$2\" ]; then\nfilesuffix=$2\nelse\nfilesuffix=\"*\"\nfi\n\n#echo \"Filtering files using suffix ${filesuffix}\"\n\nfile_list=`find ${1} -name \"*.${filesuffix}\" -type f`\nfor file2indent in $file_list\ndo\necho \"Indenting file $file2indent\"\n#!/bin/bash\nindent \"$file2indent\" -o indentoutput.tmp\nmv indentoutput.tmp \"$file2indent\"\n\ndone\nelse\nif [ -f \"$1\" ]; then\necho \"Indenting one file $1\"\n#!/bin/bash\nindent \"$1\" -o indentoutput.tmp\nmv indentoutput.tmp \"$1\"\n\nelse\necho \"ERROR: As parameter given directory or file does not exist!\"\necho \"Syntax is: call_GNU_Indent.sh dirname filesuffix\"\necho \"Syntax is: call_GNU_Indent.sh filename\"\necho \"Example: call_GNU_Indent.sh temp cpp\"\nexit 1\nfi\nfi\n"
  },
  {
    "path": "style/call_Uncrustify.cfg",
    "content": "tok_split_gte=false\nutf8_byte=false\nutf8_force=false\nindent_cmt_with_tabs=false\nindent_align_string=false\nindent_braces=false\nindent_braces_no_func=false\nindent_braces_no_class=false\nindent_braces_no_struct=false\nindent_brace_parent=false\nindent_namespace=false\nindent_extern=false\nindent_class=false\nindent_class_colon=false\nindent_else_if=false\nindent_var_def_cont=false\nindent_func_call_param=false\nindent_func_def_param=false\nindent_func_proto_param=false\nindent_func_class_param=false\nindent_func_ctor_var_param=false\nindent_template_param=false\nindent_func_param_double=false\nindent_relative_single_line_comments=false\nindent_col1_comment=false\nindent_access_spec_body=false\nindent_paren_nl=false\nindent_comma_paren=false\nindent_bool_paren=false\nindent_first_bool_expr=false\nindent_square_nl=false\nindent_preserve_sql=false\nindent_align_assign=true\nsp_balance_nested_parens=false\nalign_keep_tabs=false\nalign_with_tabs=false\nalign_on_tabstop=false\nalign_number_left=false\nalign_func_params=false\nalign_same_func_call_params=false\nalign_var_def_colon=false\nalign_var_def_attribute=false\nalign_var_def_inline=false\nalign_right_cmt_mix=false\nalign_on_operator=false\nalign_mix_var_proto=false\nalign_single_line_func=false\nalign_single_line_brace=false\nalign_nl_cont=false\nalign_left_shift=true\nalign_oc_decl_colon=false\nnl_collapse_empty_body=false\nnl_assign_leave_one_liners=false\nnl_class_leave_one_liners=false\nnl_enum_leave_one_liners=false\nnl_getset_leave_one_liners=false\nnl_func_leave_one_liners=false\nnl_if_leave_one_liners=false\nnl_multi_line_cond=false\nnl_multi_line_define=false\nnl_before_case=false\nnl_after_case=false\nnl_after_return=false\nnl_after_semicolon=true\nnl_after_brace_open=false\nnl_after_brace_open_cmt=false\nnl_after_vbrace_open=false\nnl_after_vbrace_open_empty=false\nnl_after_brace_close=false\nnl_after_vbrace_close=false\nnl_define_macro=false\nnl_squeeze_ifdef=false\nnl_ds_struct_enum_cmt=false\nnl_ds_struct_enum_close_brace=false\nnl_create_if_one_liner=false\nnl_create_for_one_liner=false\nnl_create_while_one_liner=false\nls_for_split_full=false\nls_func_split_full=false\nnl_after_multiline_comment=false\neat_blanks_after_open_brace=true\neat_blanks_before_close_brace=true\nmod_full_brace_if_chain=false\nmod_pawn_semicolon=false\nmod_full_paren_if_bool=false\nmod_remove_extra_semicolon=true\nmod_sort_import=false\nmod_sort_using=false\nmod_sort_include=false\nmod_move_case_break=false\nmod_remove_empty_return=false\ncmt_indent_multi=true\ncmt_c_group=false\ncmt_c_nl_start=false\ncmt_c_nl_end=false\ncmt_cpp_group=false\ncmt_cpp_nl_start=false\ncmt_cpp_nl_end=false\ncmt_cpp_to_c=false\ncmt_star_cont=false\ncmt_multi_check_last=true\ncmt_insert_before_preproc=false\npp_indent_at_level=false\npp_region_indent_code=false\npp_if_indent_code=false\npp_define_at_level=false\ninput_tab_size=8\noutput_tab_size=8\nnl_func_var_def_blk=1\ncode_width=80\ncmt_width=80\ncmt_reflow_mode=2\nindent_with_tabs=1\nsp_arith=force\nsp_assign=force\nsp_assign_default=force\nsp_enum_assign=force\nsp_pp_concat=force\nsp_pp_stringify=remove\nsp_bool=force\nsp_compare=force\nsp_inside_paren=remove\nsp_paren_paren=remove\nsp_paren_brace=force\nsp_before_ptr_star=force\nsp_between_ptr_star=remove\nsp_after_ptr_star=remove\nsp_after_ptr_star_func=remove\nsp_before_ptr_star_func=remove\nsp_before_byref=remove\nsp_before_sparen=force\nsp_inside_sparen=remove\nsp_after_semi_for_empty=ignore\nsp_before_square=remove\nsp_before_squares=remove\nsp_inside_square=remove\nsp_after_comma=add\nsp_before_ellipsis=add\nsp_after_operator=add\nsp_after_operator_sym=remove\nsp_sizeof_paren=remove\nsp_func_proto_paren=remove\nsp_func_def_paren=remove\nsp_inside_fparen=remove\nsp_fparen_brace=remove\nsp_func_call_paren=remove\nsp_attribute_paren=add\nsp_defined_paren=remove\nsp_else_brace=add\nsp_brace_else=add\nsp_brace_typedef=add\nsp_not=remove\nsp_inv=remove\nsp_addr=remove\nsp_member=remove\nsp_deref=remove\nsp_sign=remove\nsp_incdec=remove\nsp_cond_colon=add\nsp_cond_question=add\nnl_fdef_brace=add\nmod_full_brace_do=remove\nmod_full_brace_for=remove\nmod_full_brace_if=remove\nmod_full_brace_while=remove\nmod_paren_on_return=remove\n"
  },
  {
    "path": "style/call_Uncrustify.sh",
    "content": "#!/bin/sh\n\nif [ ! -n \"$1\" ]; then\necho \"Syntax is: recurse.sh dirname filesuffix\"\necho \"Syntax is: recurse.sh filename\"\necho \"Example: recurse.sh temp cpp\"\nexit 1\nfi\n\nif [ -d \"$1\" ]; then\n#echo \"Dir ${1} exists\"\nif [ -n \"$2\" ]; then\nfilesuffix=$2\nelse\nfilesuffix=\"*\"\nfi\n\n#echo \"Filtering files using suffix ${filesuffix}\"\n\nfile_list=`find ${1} -name \"*.${filesuffix}\" -type f`\nfor file2indent in $file_list\ndo\necho \"Indenting file $file2indent\"\n#!/bin/bash\nuncrustify -f \"$file2indent\" -c \"./call_Uncrustify.cfg\" -o indentoutput.tmp\nmv indentoutput.tmp \"$file2indent\"\n\ndone\nelse\nif [ -f \"$1\" ]; then\necho \"Indenting one file $1\"\n#!/bin/bash\nuncrustify -f \"$1\" -c \"./call_Uncrustify.cfg\" -o indentoutput.tmp\nmv indentoutput.tmp \"$1\"\n\nelse\necho \"ERROR: As parameter given directory or file does not exist!\"\necho \"Syntax is: call_Uncrustify.sh dirname filesuffix\"\necho \"Syntax is: call_Uncrustify.sh filename\"\necho \"Example: call_Uncrustify.sh temp cpp\"\nexit 1\nfi\nfi\n"
  },
  {
    "path": "test/dump.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n\npktgen.screen(\"off\");\n\nprintf(\"Lua Version      : %s\\n\", pktgen.info.Lua_Version);\nprintf(\"Pktgen Version   : %s\\n\", pktgen.info.Pktgen_Version);\nprintf(\"Pktgen Copyright : %s\\n\", pktgen.info.Pktgen_Copyright);\nprintf(\"Pktgen Authors   : %s\\n\", pktgen.info.Pktgen_Authors);\n\nprints();\n"
  },
  {
    "path": "test/generate-sequences.sh",
    "content": "#!/bin/bash\n\n# generate-flows.sh\n# quick and dirty script to generate flows based on the below settings.\n# Right now assumes just two ports.\n#\n# Contributed by gkenaley\n\n#TODO --\n# Could be improved to do multi-port and parameterized.  Maybe prompt\n# for the information\n#\n\n#For Port 0/1\nDEST_MAC=fa:16:3e:b9:de:22\nSRC_MAC=fa:16:3e:92:9c:03\nSRC_IP=192.168.30.4\nDEST_IP=192.168.30.50\nVLAN=1\nIPMODE=ipv4\nPROT=udp\nPKTSIZE=512\nFLOWS=10\n\n#IP Settings below may not be required unless using tcp/ip\nif [[ \"${PROT,,}\" != \"udp\" ]]; then\n\techo \"set ip dst 0 $SRC_IP\"\n\techo \"set ip src 0 $DEST_IP/24\"\n\techo \"set ip dst 1 $DEST_IP\"\n\techo \"set ip src 1 $SRC_IP/24\"\nfi\n\necho \"set 0,1 size $PKTSIZE\"\n\necho \"set 0,1 rate 1\"\n\n#port 0\nfor flow in `seq 0 $(($FLOWS - 1))`\ndo\n\t\t #\n\t\t #seq <seq#>   <portlist>  dst-Mac  src-Mac  dst-IP    src-IP/nm    sport   dport   ipv4|ipv6   udp|tcp|icmp   vid   pktsize\n\t\t #\n\techo \"seq $flow 0 $DEST_MAC $SRC_MAC $DEST_IP $SRC_IP/24 $((1000 + $flow)) $((2000 + $flow)) $IPMODE $PROT $VLAN $PKTSIZE\"\n\t#todo maybe add changing something other than port\n\ndone\n\n#port 1\nfor flow in `seq 0 $(($FLOWS - 1))`\ndo\n\techo \"seq $flow 1  $SRC_MAC $DEST_MAC $SRC_IP $DEST_IP/24 $((1000+$flow)) $((2000+$flow)) $IPMODE $PROT $VLAN $PKTSIZE\"\n\t#todo maybe add changing something other than port\n\ndone\n\necho \"set 0,1 seqCnt $FLOWS\"\n"
  },
  {
    "path": "test/gtpu-range.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n-- A list of the test script for Pktgen and Lua.\n-- Each command somewhat mirrors the pktgen command line versions.\n-- A couple of the arguments have be changed to be more like the others.\n--\n\nlocal function doWait(port, waitTime)\n    local idx;\n\n   pktgen.delay(1000);\n\n   if ( waitTime == 0 ) then\n       return;\n   end\n   waitTime = waitTime - 1;\n\n    -- Try to wait for the total number of packets to be sent.\n    local idx = 0;\n    while( idx < waitTime ) do\n\n        idx = idx + 1;\n\n        local sending = pktgen.isSending(port);\n        if ( sending[tonumber(port)] == \"n\" ) then\n            break;\n        end\n        pktgen.delay(1000);\n    end\n\nend\n\n\npktgen.reset(\"all\");\n\n-- 'set' commands for a number of per port values\npktgen.set(\"all\", \"rate\", 100);\n\npktgen.clear(\"all\");\npktgen.cls();\n\npktgen.pause(\"Do range commands\\n\", 1000);\npktgen.page(\"range\");\n\npktgen.dst_mac(\"all\", \"start\", \"0000:0100:0600\");\npktgen.src_mac(\"all\", \"start\", \"0000:0100:0700\");\n\npktgen.delay(1000);\npktgen.range.dst_ip(\"all\", \"start\", \"10.10.10.1\");\npktgen.range.dst_ip(\"all\", \"inc\", \"0.0.0.1\");\npktgen.range.dst_ip(\"all\", \"min\", \"10.10.10.1\");\npktgen.range.dst_ip(\"all\", \"max\", \"10.10.10.4\");\n\npktgen.delay(1000);\npktgen.range.src_ip(\"all\", \"start\", \"20.20.20.1\");\npktgen.range.src_ip(\"all\", \"inc\", \"0.0.0.1\");\npktgen.range.src_ip(\"all\", \"min\", \"20.20.20.1\");\npktgen.range.src_ip(\"all\", \"max\", \"20.20.20.8\");\n\npktgen.set_type(\"all\", \"ipv4\");\npktgen.set_proto(\"all\", \"udp\");\n\n-- GTPU on UDP port 2152\npktgen.delay(1000);\npktgen.range.dst_port(\"all\", \"start\", 2152);\npktgen.range.dst_port(\"all\", \"inc\", 0);\npktgen.range.dst_port(\"all\", \"min\", 2152);\npktgen.range.dst_port(\"all\", \"max\", 2152);\n\npktgen.delay(1000);\npktgen.range.src_port(\"all\", \"start\", 1234);\npktgen.range.src_port(\"all\", \"inc\", 1);\npktgen.range.src_port(\"all\", \"min\", 1234);\npktgen.range.src_port(\"all\", \"max\", 1244);\n\npktgen.delay(1000);\npktgen.range.gtpu_teid(\"all\", \"start\", 100);\npktgen.range.gtpu_teid(\"all\", \"inc\", 1);\npktgen.range.gtpu_teid(\"all\", \"min\", 100);\npktgen.range.gtpu_teid(\"all\", \"max\", 200);\n\npktgen.delay(1000);\npktgen.range.vlan_id(\"all\", \"start\", 100);\npktgen.range.vlan_id(\"all\", \"inc\", 1);\npktgen.range.vlan_id(\"all\", \"min\", 100);\npktgen.range.vlan_id(\"all\", \"max\", 150);\n\npktgen.delay(1000);\npktgen.range.pkt_size(\"all\", \"start\", 150);\npktgen.range.pkt_size(\"all\", \"inc\", 5);\npktgen.range.pkt_size(\"all\", \"min\", 150);\npktgen.range.pkt_size(\"all\", \"max\", 200);\n\n\npktgen.set_range(\"all\", \"on\");\npktgen.vlan(\"all\", \"on\");\n\npktgen.pause(\"Wait a second, then go back to main page\\n\", 1000);\npktgen.page(\"0\");\n--pktgen.pause(\"About to do range\\n\", 2000);\n\npktgen.pause(\"Wait a second, then go back to main page\\n\", 1000);\npktgen.page(\"0\");\n--pktgen.pause(\"About to do range\\n\", 2000);\n\npktgen.start(\"all\");\ndoWait(\"all\", 1);\n--printf(\"Done\\n\");\n"
  },
  {
    "path": "test/hello-world.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\nprintf(\"Lua Version      : %s\\n\", pktgen.info.Lua_Version);\nprintf(\"Pktgen Version   : %s\\n\", pktgen.info.Pktgen_Version);\nprintf(\"Pktgen Copyright : %s\\n\", pktgen.info.Pktgen_Copyright);\nprintf(\"Pktgen Authors   : %s\\n\", pktgen.info.Pktgen_Authors);\n\nprintf(\"\\nHello World!!!!\\n\");\n"
  },
  {
    "path": "test/info.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n\n-- A list of the test script for Pktgen and Lua.\n-- Each command somewhat mirrors the pktgen command line versions.\n-- A couple of the arguments have be changed to be more like the others.\n--\nfunction info()\n\tprintf(\"Lua Version      : %s\\n\", pktgen.info.Lua_Version);\n\n\tprintf(\"Pktgen Version   : %s\\n\",\n\t\tpktgen.info.Pktgen_Version);\n\tprintf(\"Pktgen Copyright : %s\\n\",\n\t\tpktgen.info.Pktgen_Copyright);\n\n\tprints(\"pktgen.info\",\n\t\tpktgen.info);\n\n\tprintf(\"Port Count %d\\n\",\n\t\tpktgen.portCount());\n\tprintf(\"Total port Count %d\\n\",\n\t\tpktgen.totalPorts());\nend\n\ninfo()\n"
  },
  {
    "path": "test/mac.pkt.3.4.ip-range-gen.txt",
    "content": "# mac.pkt\n#\n# Contributed by gkenaley\n\n# /home/demo/Projects/pktgen-2.8.6/pktgen -c 0xee --initial-lcore 1 -n 2 -m 1024 --proc-type auto --file-prefix pg -- -p 0x6 -m \"[6:3].0,[7:2].1\" -f ./mac.pkt.3.4.ip-range-gen\n#   OR\n#./app/app/${target}/pktgen -c 0xff --initial-lcore 1 -n 4 -m 1024 --proc-type auto --file-prefix pg -- -P -T -p 0x3 -m \"[6:3].0,[7:2].1\" -f ./themes/black-yellow.theme -f ./mac.pkt.3.4.ip-range-gen\n\n# seq   <seq#>   <portlist>  dst-Mac  src-Mac  dst-IP    src-IP/nm    sport   dport   ipv4|ipv6   udp|tcp|icmp   vid   pktsize\n\nset 0,1 type vlan\nset 0 vlan 623\nset 1 vlan 570\nenable 0,1 vlan\n\nset 0,1 proto udp\n\nset 0,1 rate 1\nset 0,1 size 512\n\n# Here is the new and improved (pktgen 3.4+) syntax for generating a range of flows with varying IP src/dst\nset 0 dst ip 192.168.20.3\nrange 0 dst ip 192.168.20.3 192.168.20.3 192.168.20.50 0.0.0.1\n\nset 0 src ip 192.168.30.3/24\nrange 0 src ip 192.168.30.3 192.168.30.3 192.168.30.50 0.0.0.1\n\nset 0 dst ip 192.168.30.3\nrange 0 dst ip 192.168.30.3 192.168.30.3 192.168.30.50 0.0.0.1\n\nset 0 src ip 192.168.20.3/24\nrange 0 src ip 192.168.20.3 192.168.20.3 192.168.20.50 0.0.0.1\n\nenable 0,1 process\n\nenable 0,1 garp\nstart 0,1 arp gratuitous\ndelay 5000\nstop 0,1\ndisable 0,1 garp\n\nstart 0,1\n\n# Ramp up throughput over 10 seconds, as is commonly done on Ixia/Spirent\n# Percentage rates are apparently relative to 40G on 40G capable cards (even 4x10G)\n# Note that some switches require up to 30 seconds to learn before bi-directional traffic starts!\n\nset 0,1 rate 5\ndelay 3000\nset 0,1 rate 10\ndelay 3000\nset 0,1 rate 15\ndelay 3000\nset 0,1 rate 16\n"
  },
  {
    "path": "test/mac.pkt.sequences.txt",
    "content": "# mac.pkt\n# -------\n# Note that DPDK cannot use default Nehalem cpu flavor extra spec, because it needs special SSE instructions\n#\n# Contributed by gkenaley\n#\n# /home/demo/Projects/pktgen-2.8.6/pktgen -c 0xee --initial-lcore 1 -n 2 -m 1024 --proc-type auto --file-prefix pg -- -p 0x6 -m \"[6:3].0,[7:2].1\" -f ./mac.pkt.sequences\n#   OR\n#./app/app/${target}/pktgen -c 0xff --initial-lcore 1 -n 4 -m 1024 --proc-type auto --file-prefix pg -- -P -T -p 0x3 -m \"[6:3].0,[7:2].1\" -f ./themes/black-yellow.theme -f ./mac.pkt.sequences\n\n# seq   <seq#>   <portlist>  dst-Mac  src-Mac  dst-IP    src-IP/nm    sport   dport   ipv4|ipv6   udp|tcp|icmp   vid   pktsize\n\nvlanid 0 681\nvlanid 1 581\nvlan 0,1 on\nproto udp 0,1\n\n# Packet size is the critical variable\nset 0,1 size 512\n\nset 0,1 rate 1\n# see http://www.subnet-calculator.com/\n\nseq 0 0 fa:16:3e:b3:aa:f5 fa:16:3e:d2:a6:b1 192.168.20.3 192.168.30.3/24 2040 2040 ipv4 udp 681 512\nseq 1 0 fa:16:3e:b3:aa:f5 fa:16:3e:d2:a6:b1 192.168.20.3 192.168.30.3/24 2041 2041 ipv4 udp 681 512\nseq 2 0 fa:16:3e:b3:aa:f5 fa:16:3e:d2:a6:b1 192.168.20.3 192.168.30.3/24 2042 2042 ipv4 udp 681 512\nseq 3 0 fa:16:3e:b3:aa:f5 fa:16:3e:d2:a6:b1 192.168.20.3 192.168.30.3/24 2043 2043 ipv4 udp 681 512\nseq 0 0 fa:16:3e:b3:aa:f5 fa:16:3e:d2:a6:b1 192.168.20.3 192.168.30.3/24 2044 2044 ipv4 udp 681 512\nseq 5 0 fa:16:3e:b3:aa:f5 fa:16:3e:d2:a6:b1 192.168.20.3 192.168.30.3/24 2045 2045 ipv4 udp 681 512\nseq 6 0 fa:16:3e:b3:aa:f5 fa:16:3e:d2:a6:b1 192.168.20.3 192.168.30.3/24 2046 2046 ipv4 udp 681 512\nseq 7 0 fa:16:3e:b3:aa:f5 fa:16:3e:d2:a6:b1 192.168.20.3 192.168.30.3/24 2047 2047 ipv4 udp 681 512\nseq 8 0 fa:16:3e:b3:aa:f5 fa:16:3e:d2:a6:b1 192.168.20.3 192.168.30.3/24 2048 2048 ipv4 udp 681 512\nseq 9 0 fa:16:3e:b3:aa:f5 fa:16:3e:d2:a6:b1 192.168.20.3 192.168.30.3/24 2049 2049 ipv4 udp 681 512\n\nseq 0 1 fa:16:3e:d2:a6:b1 fa:16:3e:b3:aa:f5 192.168.30.3 192.168.20.3/24 3040 3040 ipv4 udp 581 512\nseq 1 1 fa:16:3e:d2:a6:b1 fa:16:3e:b3:aa:f5 192.168.30.3 192.168.20.3/24 3041 3041 ipv4 udp 581 512\nseq 2 1 fa:16:3e:d2:a6:b1 fa:16:3e:b3:aa:f5 192.168.30.3 192.168.20.3/24 3042 3042 ipv4 udp 581 512\nseq 3 1 fa:16:3e:d2:a6:b1 fa:16:3e:b3:aa:f5 192.168.30.3 192.168.20.3/24 3043 3043 ipv4 udp 581 512\nseq 4 1 fa:16:3e:d2:a6:b1 fa:16:3e:b3:aa:f5 192.168.30.3 192.168.20.3/24 3044 3044 ipv4 udp 581 512\nseq 5 1 fa:16:3e:d2:a6:b1 fa:16:3e:b3:aa:f5 192.168.30.3 192.168.20.3/24 3045 3045 ipv4 udp 581 512\nseq 6 1 fa:16:3e:d2:a6:b1 fa:16:3e:b3:aa:f5 192.168.30.3 192.168.20.3/24 3046 3046 ipv4 udp 581 512\nseq 7 1 fa:16:3e:d2:a6:b1 fa:16:3e:b3:aa:f5 192.168.30.3 192.168.20.3/24 3047 3047 ipv4 udp 581 512\nseq 8 1 fa:16:3e:d2:a6:b1 fa:16:3e:b3:aa:f5 192.168.30.3 192.168.20.3/24 3048 3048 ipv4 udp 581 512\nseq 9 1 fa:16:3e:d2:a6:b1 fa:16:3e:b3:aa:f5 192.168.30.3 192.168.20.3/24 3049 3049 ipv4 udp 581 512\n\nprocess 0,1 enable\ndelay 2000\n\n#The next line is what activates sequences. If there are no sequences above, no traffic occurs (unless the next line is commented out).\nset 0,1 seqCnt 10\n\nstart 0,1\n\n# Ramp up throughput over 10 seconds, as is commonly done on Ixia/Spirent\n# Percentage rates are apparently relative to 40G when the NIC is 40G capable.\n# Note that some switches require up to 30 seconds to learn before bi-directional traffic starts!\ndelay 5000\nset 0,1 rate 5\ndelay 3000\nset 0,1 rate 10\ndelay 3000\nset 0,1 rate 13\ndelay 3000\nset 0,1 rate 20\ndelay 3000\nset 0,1 rate 23\n"
  },
  {
    "path": "test/main.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n\n-- A list of the test script for Pktgen and Lua.\n-- Each command somewhat mirrors the pktgen command line versions.\n-- A couple of the arguments have be changed to be more like the others.\n--\npktgen.screen(\"off\");\npktgen.pause(\"Screen off\\n\", 1000);\npktgen.screen(\"on\");\npktgen.pause(\"Screen on\\n\", 1000);\npktgen.screen(\"off\");\npktgen.pause(\"Screen off\\n\", 1000);\n\nprintf(\"delay for 1 second\\n\");\npktgen.delay(1000);\nprintf(\"done\\n\");\n\n-- 'set' commands for a number of per port values\npktgen.set(\"all\", \"count\", 100);\npktgen.set(\"all\", \"rate\", 50);\npktgen.set(\"all\", \"size\", 256);\npktgen.set(\"all\", \"burst\", 128);\npktgen.set(\"all\", \"sport\", 0x5678);\npktgen.set(\"all\", \"dport\", 0x9988);\npktgen.set(\"all\", \"prime\", 3);\npktgen.set(\"all\", \"seq_cnt\", 3);\n\npktgen.rnd(\"all\", 1, 20, \"XX111000.. ..xx11\");\n\npktgen.vlanid(\"all\", 55);\n\npktgen.screen(\"on\");\npktgen.pause(\"Screen on\\n\", 1000);\npktgen.screen(\"off\");\n\n-- sequence command in one line\npktgen.seq(0, \"all\", \"0000:4455:6677\", \"0000:1234:5678\", \"10.11.0.1\", \"10.10.0.1/16\", 5, 6, \"ipv4\", \"udp\", 1, 128);\n\n-- sequence command using a table of packet configurations\nlocal seq_table = {\n    [\"eth_dst_addr\"] = \"0011:4455:6677\",\n    [\"eth_src_addr\"] = \"0011:1234:5678\",\n    [\"ip_dst_addr\"] = \"10.12.0.1\",\n    [\"ip_src_addr\"] = \"10.12.0.1/16\",\n    [\"sport\"] = 9,\n    [\"dport\"] = 10,\n    [\"ethType\"] = \"ipv4\",\n    [\"ipProto\"] = \"udp\",\n    [\"vlanid\"] = 1,\n    [\"pktSize\"] = 128\n  };\npktgen.seqTable(0, \"all\", seq_table );\n\npktgen.ports_per_page(2);\npktgen.icmp_echo(\"all\", \"on\");\npktgen.send_arp(\"all\", \"g\");\npktgen.set_mac(\"0-2\",\"src\", \"0001:1122:3344\");\npktgen.set_mac(\"0-2\", \"dst\",\"0002:1122:3344\");\npktgen.mac_from_arp(\"on\");\npktgen.set_ipaddr(\"0\", \"dst\", \"10.10.2.2\");\npktgen.set_ipaddr(\"0\", \"src\", \"10.10.1.2/24\");\npktgen.set_ipaddr(\"1\", \"dst\", \"10.10.2.2\");\npktgen.set_ipaddr(\"1\", \"src\", \"10.10.2.2/24\");\npktgen.set_proto(\"all\", \"udp\");\npktgen.set_type(\"all\", \"ipv6\");\npktgen.ping4(\"all\");\n--pktgen.ping6(\"all\");\n--pktgen.show(\"all\", \"scan\");\npktgen.pcap(\"all\", \"on\");\npktgen.ports_per_page(4);\npktgen.start(\"all\");\npktgen.stop(\"all\");\npktgen.prime(\"all\");\npktgen.delay(1000);\n\npktgen.screen(\"on\");\npktgen.clear(\"all\");\npktgen.cls();\npktgen.reset(\"all\");\n\npktgen.pause(\"Do range commands\\n\", 1000);\npktgen.page(\"range\");\npktgen.range.dst_mac(\"all\", \"start\", \"0011:2233:4455\");\npktgen..rangesrc_mac(\"all\", \"start\", \"0033:2233:4455\");\n\npktgen.delay(1000);\npktgen.range.dst_ip(\"all\", \"start\", \"10.12.0.1\");\npktgen.range.dst_ip(\"all\", \"inc\", \"0.0.0.2\");\npktgen.range.dst_ip(\"all\", \"min\", \"10.12.0.1\");\npktgen.range.dst_ip(\"all\", \"max\", \"10.12.0.64\");\n\npktgen.delay(1000);\npktgen.range.src_ip(\"all\", \"start\", \"10.13.0.1\");\npktgen.range.src_ip(\"all\", \"inc\", \"0.0.0.3\");\npktgen.range.src_ip(\"all\", \"min\", \"10.13.0.1\");\npktgen.range.src_ip(\"all\", \"max\", \"10.13.0.64\");\n\npktgen.delay(1000);\npktgen.range.dst_port(\"all\", \"start\", 1234);\npktgen.range.dst_port(\"all\", \"inc\", 4);\npktgen.range.dst_port(\"all\", \"min\", 1234);\npktgen.range.dst_port(\"all\", \"max\", 2345);\n\npktgen.delay(1000);\npktgen.range.src_port(\"all\", \"start\", 5678);\npktgen.range.src_port(\"all\", \"inc\", 5);\npktgen.range.src_port(\"all\", \"min\", 1234);\npktgen.range.src_port(\"all\", \"max\", 9999);\n\npktgen.delay(1000);\npktgen.range.vlan_id(\"all\", \"start\", 1);\npktgen.range.vlan_id(\"all\", \"inc\", 0);\npktgen.range.vlan_id(\"all\", \"min\", 1);\npktgen.range.vlan_id(\"all\", \"max\", 4094);\n\npktgen.delay(1000);\npktgen.range.pkt_size(\"all\", \"start\", 128);\npktgen.range.pkt_size(\"all\", \"inc\", 2);\npktgen.range.pkt_size(\"all\", \"min\", 64);\npktgen.range.pkt_size(\"all\", \"max\", 1518);\n\npktgen.pause(\"Wait a second, then go back to main page\\n\", 1000);\n\npktgen.page(\"0\");\npktgen.pause(\"About to do range\\n\", 1000);\npktgen.set_range(\"all\", \"on\");\n\npktgen.port(2);\npktgen.process(\"all\", \"on\");\npktgen.blink(\"0\", \"on\");\npktgen.pause(\"Pause for a while, then turn off screen\\n\", 4000);\npktgen.screen(\"off\");\n\nprintf(\"Lua Version      : %s\\n\", pktgen.info.Lua_Version);\nprintf(\"Pktgen Version   : %s\\n\", pktgen.info.Pktgen_Version);\nprintf(\"Pktgen Copyright : %s\\n\", pktgen.info.Pktgen_Copyright);\n\nprints(\"pktgen.info\", pktgen.info);\n\nprintf(\"Port Count %d\\n\", pktgen.portCount());\nprintf(\"Total port Count %d\\n\", pktgen.totalPorts());\n\nprintf(\"\\nDone\\n\");\nkey = pktgen.continue(\"\\nPress any key: \");\nif ( key == \"s\" ) then\n\tpktgen.set(\"all\", \"seq_cnt\", 4);\n\tpktgen.save(\"foobar.cmd\");\n\tpktgen.continue(\"Saved foobar.cmd, press key to load that file: \");\n\tpktgen.load(\"foobar.cmd\");\nend\n"
  },
  {
    "path": "test/port_info.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n-- A list of the test script for Pktgen and Lua.\n-- Each command somewhat mirrors the pktgen command line versions.\n-- A couple of the arguments have be changed to be more like the others.\n--\n\nprints(\"portInfo\", pktgen.portInfo(\"all\"));\n"
  },
  {
    "path": "test/portstats.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n-- A list of the test script for Pktgen and Lua.\n-- Each command somewhat mirrors the pktgen command line versions.\n-- A couple of the arguments have be changed to be more like the others.\n--\n\nprints(\"portStats\", pktgen.portStats(\"all\", \"port\"));\nprints(\"portStats\", pktgen.portStats('2', 'port'));\n"
  },
  {
    "path": "test/portstats_with_delay.lua",
    "content": "package.path = package.path .. \";?.lua;test/?.lua;\"\nrequire(\"Pktgen\")\n\npktgen.start(\"0\");\nTEST_DURATION = 10\npktgen.delay(TEST_DURATION * 1000);\npktgen.stop(\"0\");\nprints(\"portStats\", pktgen.portStats('0', 'port'));\n"
  },
  {
    "path": "test/range.lua",
    "content": "pktgen.page(\"range\");\n\npktgen.range.dst_mac(\"all\", \"start\", \"000d:49ef:016d\");\npktgen.range.src_mac(\"all\", \"start\", \"000d:49ef:0163\");\n\npktgen.delay(1000);\npktgen.range.dst_ip(\"all\", \"start\", \"10.10.10.1\");\npktgen.range.dst_ip(\"all\", \"inc\", \"0.0.0.1\");\npktgen.range.dst_ip(\"all\", \"min\", \"10.10.10.1\");\npktgen.range.dst_ip(\"all\", \"max\", \"10.10.10.4\");\n\npktgen.delay(1000);\npktgen.range.src_ip(\"all\", \"start\", \"20.20.20.1\");\npktgen.range.src_ip(\"all\", \"inc\", \"0.0.0.1\");\npktgen.range.src_ip(\"all\", \"min\", \"20.20.20.1\");\npktgen.range.src_ip(\"all\", \"max\", \"20.20.20.8\");\n\npktgen.delay(1000);\npktgen.range.ttl(\"all\", \"start\", 64);\npktgen.range.ttl(\"all\", \"inc\", 0);\npktgen.range.ttl(\"all\", \"min\", 64);\npktgen.range.ttl(\"all\", \"max\", 64);\n\n--pktgen.delay(1000);\n--pktgen.range.set_type(\"all\", \"ipv6\");\n\n--pktgen.delay(1000);\n--pktgen.range.dst_ip(\"all\", \"start\", \"10a:a0a::\");\n--pktgen.range.dst_ip(\"all\", \"inc\", \"100::\");\n--pktgen.range.dst_ip(\"all\", \"min\", \"10a:a0a::\");\n--pktgen.range.dst_ip(\"all\", \"max\", \"40a:a0a::\");\n\n--pktgen.delay(1000);\n--pktgen.range.src_ip(\"all\", \"start\", \"114:1414::\");\n--pktgen.range.src_ip(\"all\", \"inc\", \"94:1414::\");\n--pktgen.range.src_ip(\"all\", \"min\", \"114:1414::\");\n--pktgen.range.src_ip(\"all\", \"max\", \"914:1414::\");\n\n--pktgen.delay(1000);\n--pktgen.range.hop_limits(\"all\", \"start\", 4);\n--pktgen.range.hop_limits(\"all\", \"inc\", 0);\n--pktgen.range.hop_limits(\"all\", \"min\", 4);\n--pktgen.range.hop_limits(\"all\", \"max\", 4);\n\n--pktgen.delay(1000);\n--pktgen.range.traffic_class(\"all\", \"start\", 32);\n--pktgen.range.traffic_class(\"all\", \"inc\", 0);\n--pktgen.range.traffic_class(\"all\", \"min\", 32);\n--pktgen.range.traffic_class(\"all\", \"max\", 32);\n"
  },
  {
    "path": "test/screen_off.lua",
    "content": "-- Lua uses '--' as comment to end of line read the\n-- manual for more comment options.\npktgen.screen(\"off\");\n"
  },
  {
    "path": "test/set_gtpu_seq.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n-- Lua uses '--' as comment to end of line read the\n-- manual for more comment options.\nrequire \"Pktgen\"\nlocal seq_table = {}\nseq_table[0] = {            -- entries can be in any order\n    [\"eth_dst_addr\"] = \"0011:4455:6677\",\n    [\"eth_src_addr\"] = \"0033:1234:5678\",\n    [\"ip_dst_addr\"] = \"10.11.0.1\",\n    [\"ip_src_addr\"] = \"10.12.0.1/16\",  -- the 16 is the size of the mask value\n    [\"sport\"] = 9000,          -- Standard port numbers\n    [\"dport\"] = 2152,          -- Standard port numbers\n    [\"ethType\"] = \"ipv4\",  -- ipv4|ipv6|vlan\n    [\"ipProto\"] = \"udp\",   -- udp|tcp|icmp\n    [\"vlanid\"] = 100,          -- 1 - 4095\n    [\"pktSize\"] = 128,     -- 64 - 1518\n    [\"gtpu_teid\"] = 1000   -- GTPu TEID (Set dport=2152)\n  };\nseq_table[1] = {            -- entries can be in any order\n    [\"eth_dst_addr\"] = \"0011:4455:6677\",\n    [\"eth_src_addr\"] = \"0033:1234:5678\",\n    [\"ip_dst_addr\"] = \"10.11.0.1\",\n    [\"ip_src_addr\"] = \"10.12.0.1/16\",  -- the 16 is the size of the mask value\n    [\"sport\"] = 9000,          -- Standard port numbers\n    [\"dport\"] = 2152,          -- Standard port numbers\n    [\"ethType\"] = \"ipv4\",  -- ipv4|ipv6|vlan\n    [\"ipProto\"] = \"udp\",   -- udp|tcp|icmp\n    [\"vlanid\"] = 100,          -- 1 - 4095\n    [\"pktSize\"] = 128,     -- 64 - 1518\n    [\"gtpu_teid\"] = 1000   -- GTPu TEID (Set dport=2152)\n  };\nseq_table.n = 2;\n-- seqTable( seq#, portlist, table );\npktgen.seqTable(0, \"all\", seq_table[0] );\npktgen.seqTable(1, \"all\", seq_table[1] );\npktgen.set(\"all\", \"seq_cnt\", 2);\npktgen.set(\"all\", \"seqCnt\", 4);\npktgen.page(\"seq\");\n"
  },
  {
    "path": "test/set_seq.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n-- Lua uses '--' as comment to end of line read the\n-- manual for more comment options.\nrequire \"Pktgen\"\nlocal seq_table = {\t\t\t-- entries can be in any order\n    [\"eth_dst_addr\"] = \"0011:4455:6677\",\n    [\"eth_src_addr\"] = \"0011:1234:5678\",\n    [\"ip_dst_addr\"] = \"10.12.0.1\",\n    [\"ip_src_addr\"] = \"10.12.0.1/16\",\t-- the 16 is the size of the mask value\n    [\"sport\"] = 9,\t\t\t-- Standard port numbers\n    [\"dport\"] = 10,\t\t\t-- Standard port numbers\n    [\"ethType\"] = \"ipv4\",\t-- ipv4|ipv6|vlan\n    [\"ipProto\"] = \"udp\",\t-- udp|tcp|icmp\n    [\"vlanid\"] = 1,\t\t\t-- 1 - 4095\n    [\"pktSize\"] = 128,\t\t-- 64 - 1518\n    [\"teid\"] = 3,\n    [\"cos\"] = 5,\n    [\"tos\"] = 6,\n    [\"tcp_flags\"] = \"fin,ack,cwr\"\n  };\n-- seqTable( seq#, portlist, table );\npktgen.seqTable(0, \"all\", seq_table );\npktgen.set(\"all\", \"seq_cnt\", 4);\n"
  },
  {
    "path": "test/simple_range.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;../?.lua\"\n\nrequire \"Pktgen\";\npkt_size = 64;\n\nlocal dstip     = \"10.12.0.1\";\nlocal min_dstip = \"10.12.0.1\";\nlocal max_dstip = \"10.12.0.64\";\nlocal inc_dstip = \"0.0.0.1\"\n\nlocal srcip     = \"10.12.0.101\";\nlocal min_srcip = \"10.12.0.101\";\nlocal max_srcip = \"10.12.0.101\";\nlocal inc_srcip = \"0.0.0.0\"\n\nlocal dst_port     = 5678\nlocal inc_dst_port = 0;\nlocal min_dst_port = dst_port\nlocal max_dst_port = dst_port;\n\nlocal src_port     = 1234\nlocal inc_src_port = 0;\nlocal min_src_port = src_port\nlocal max_src_port = src_port;\n\nprintf(\"\\nStarting Experiment!!!\\n\");\nprint(\"Pkt_size is\", pkt_size, \"\\n\");\n\npktgen.set(\"all\", \"size\", pkt_size)\n\npktgen.page(\"range\")\n\npktgen.range.dst_port(\"all\", \"start\", dst_port);\npktgen.range.dst_port(\"all\", \"inc\", inc_dst_port);\npktgen.range.dst_port(\"all\", \"min\", min_dst_port);\npktgen.range.dst_port(\"all\", \"max\", max_dst_port);\n\npktgen.range.src_port(\"all\", \"start\", src_port);\npktgen.range.src_port(\"all\", \"inc\", inc_src_port);\npktgen.range.src_port(\"all\", \"min\", min_src_port);\npktgen.range.src_port(\"all\", \"max\", max_src_port);\n\npktgen.range.dst_ip(\"all\", \"start\", dstip);\npktgen.range.dst_ip(\"all\", \"inc\", inc_dstip);\npktgen.range.dst_ip(\"all\", \"min\", min_dstip);\npktgen.range.dst_ip(\"all\", \"max\", max_dstip);\n\npktgen.range.src_ip(\"all\", \"start\", srcip);\npktgen.range.src_ip(\"all\", \"inc\", inc_srcip);\npktgen.range.src_ip(\"all\", \"min\", srcip);\npktgen.range.src_ip(\"all\", \"max\", srcip);\n\npktgen.set_range(\"0\", \"on\");\n"
  },
  {
    "path": "test/test.lua",
    "content": "-- SPDX-License-Identifier: BSD-3-Clause\n\npackage.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n\nprint(\"**** Execute Range test ********\");\n\npktgen.page(\"range\");\n\n-- Port 0 3c:fd:fe:9c:5c:d8,  Port 1 3c:fd:fe:9c:5c:b8\npktgen.range.dst_mac(\"0\", \"start\", \"3c:fd:fe:9c:5c:b8\");\npktgen.range.src_mac(\"0\", \"start\", \"3c:fd:fe:9c:5c:d8\");\n\npktgen.range.dst_ip(\"0\", \"start\", \"192.168.1.1\");\npktgen.range.dst_ip(\"0\", \"inc\", \"0.0.0.1\");\npktgen.range.dst_ip(\"0\", \"min\", \"192.168.1.1\");\npktgen.range.dst_ip(\"0\", \"max\", \"192.168.1.128\");\n\npktgen.range.src_ip(\"0\", \"start\", \"192.168.0.1\");\npktgen.range.src_ip(\"0\", \"inc\", \"0.0.0.1\");\npktgen.range.src_ip(\"0\", \"min\", \"192.168.0.1\");\npktgen.range.src_ip(\"0\", \"max\", \"192.168.0.128\");\n\npktgen.set_proto(\"0\", \"udp\");\n\npktgen.range.dst_port(\"0\", \"start\", 2000);\npktgen.range.dst_port(\"0\", \"inc\", 1);\npktgen.range.dst_port(\"0\", \"min\", 2000);\npktgen.range.dst_port(\"0\", \"max\", 4000);\n\npktgen.range.src_port(\"0\", \"start\", 5000);\npktgen.range.src_port(\"0\", \"inc\", 1);\npktgen.range.src_port(\"0\", \"min\", 5000);\npktgen.range.src_port(\"0\", \"max\", 7000);\n\npktgen.range.pkt_size(\"0\", \"start\", 64);\npktgen.range.pkt_size(\"0\", \"inc\", 0);\npktgen.range.pkt_size(\"0\", \"min\", 64);\npktgen.range.pkt_size(\"0\", \"max\", 256);\n\n-- Set up second port\npktgen.range.dst_mac(\"1\", \"start\", \"3c:fd:fe:9c:5c:d8\");\npktgen.range.src_mac(\"1\", \"start\", \"3c:fd:fe:9c:5c:b8\");\n\npktgen.range.dst_ip(\"1\", \"start\", \"192.168.0.1\");\npktgen.range.dst_ip(\"1\", \"inc\", \"0.0.0.1\");\npktgen.range.dst_ip(\"1\", \"min\", \"192.168.0.1\");\npktgen.range.dst_ip(\"1\", \"max\", \"192.168.0.128\");\n\npktgen.range.src_ip(\"1\", \"start\", \"192.168.1.1\");\npktgen.range.src_ip(\"1\", \"inc\", \"0.0.0.1\");\npktgen.range.src_ip(\"1\", \"min\", \"192.168.1.1\");\npktgen.range.src_ip(\"1\", \"max\", \"192.168.1.128\");\n\npktgen.set_proto(\"all\", \"udp\");\n\npktgen.range.dst_port(\"1\", \"start\", 5000);\npktgen.range.dst_port(\"1\", \"inc\", 1);\npktgen.range.dst_port(\"1\", \"min\", 5000);\npktgen.range.dst_port(\"1\", \"max\", 7000);\n\npktgen.range.src_port(\"1\", \"start\", 2000);\npktgen.range.src_port(\"1\", \"inc\", 1);\npktgen.range.src_port(\"1\", \"min\", 2000);\npktgen.range.src_port(\"1\", \"max\", 4000);\n\npktgen.range.pkt_size(\"1\", \"start\", 64);\npktgen.range.pkt_size(\"1\", \"inc\", 0);\npktgen.range.pkt_size(\"1\", \"min\", 64);\npktgen.range.pkt_size(\"1\", \"max\", 256);\n\npktgen.set_range(\"0\", \"on\");\npktgen.start(0)\n"
  },
  {
    "path": "test/test1.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n-- A list of the test script for Pktgen and Lua.\n-- Each command somewhat mirrors the pktgen command line versions.\n-- A couple of the arguments have be changed to be more like the others.\n--\n\nseqTable = {\n  [1] = {\n    [\"pktSize\"] = 128,\n    [\"ip_dst_addr\"] = \"10.12.0.1\",\n    [\"eth_dst_addr\"] = \"0011:4455:6677\",\n    [\"eth_src_addr\"] = \"0011:1234:5678\",\n    [\"dport\"] = 10,\n    [\"sport\"] = 9,\n    [\"tlen\"] = 42,\n    [\"ip_src_addr\"] = \"10.12.0.1/16\",\n    [\"vlanid\"] = 40,\n    [\"ipProto\"] = \"udp\",\n    [\"ethType\"] = \"ipv4\",\n  },\n  [2] = {\n    [\"pktSize\"] = 128,\n    [\"ip_dst_addr\"] = \"10.12.0.1\",\n    [\"eth_dst_addr\"] = \"0011:4455:6677\",\n    [\"eth_src_addr\"] = \"0011:1234:5678\",\n    [\"dport\"] = 10,\n    [\"sport\"] = 9,\n    [\"tlen\"] = 42,\n    [\"ip_src_addr\"] = \"10.12.0.1/16\",\n    [\"vlanid\"] = 40,\n    [\"ipProto\"] = \"udp\",\n    [\"ethType\"] = \"ipv4\",\n  },\n  [3] = {\n    [\"pktSize\"] = 128,\n    [\"ip_dst_addr\"] = \"10.12.0.1\",\n    [\"eth_dst_addr\"] = \"0011:4455:6677\",\n    [\"eth_src_addr\"] = \"0011:1234:5678\",\n    [\"dport\"] = 10,\n    [\"sport\"] = 9,\n    [\"tlen\"] = 42,\n    [\"ip_src_addr\"] = \"10.12.0.1/16\",\n    [\"vlanid\"] = 40,\n    [\"ipProto\"] = \"udp\",\n    [\"ethType\"] = \"ipv4\",\n  },\n  [0] = {\n    [\"pktSize\"] = 128,\n    [\"ip_dst_addr\"] = \"10.12.0.1\",\n    [\"eth_dst_addr\"] = \"0011:4455:6677\",\n    [\"eth_src_addr\"] = \"0011:1234:5678\",\n    [\"dport\"] = 10,\n    [\"sport\"] = 9,\n    [\"tlen\"] = 42,\n    [\"ip_src_addr\"] = \"10.12.0.1/16\",\n    [\"vlanid\"] = 40,\n    [\"ipProto\"] = \"udp\",\n    [\"ethType\"] = \"ipv4\",\n  },\n  [\"n\"] = 4,\n}\n\nprints(\"seqTable\", seqTable);\n\npktgen.seqTable(0, \"all\", seqTable[0]);\npktgen.seqTable(1, \"all\", seqTable[1]);\npktgen.seqTable(2, \"all\", seqTable[2]);\npktgen.seqTable(3, \"all\", seqTable[3]);\n\npktgen.delay(1000)\n\n-- TODO: Need to create a pktgen.seqTableN(\"all\", seqTable); like support\n"
  },
  {
    "path": "test/test2.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\npktgen.seqTable(pktgen.info.startSeqIdx, \"all\", {\n    [\"eth_dst_addr\"] = \"0011:4455:6677\",\n    [\"eth_src_addr\"] = \"0011:1234:5678\",\n    [\"ip_dst_addr\"] = \"10.11.0.1\",\n    [\"ip_src_addr\"] = \"10.11.0.1/16\",\n    [\"ethType\"] = \"ipv4\",\n    [\"ipProto\"] = \"udp\",\n    [\"tlen\"] = 42,\n    [\"sport\"] = 9,\n    [\"dport\"] = 10,\n    [\"pktSize\"] = 128,\n    [\"vlanid\"] = 40\n  });\n\nprints(\"seqTable\", pktgen.decompile(pktgen.info.startSeqIdx, \"all\"));\n\npktgen.compile(pktgen.info.startSeqIdx, \"all\", {\n    [\"eth_dst_addr\"] = \"0022:4455:6677\",\n    [\"eth_src_addr\"] = \"0022:1234:5678\",\n    [\"ip_dst_addr\"] = \"10.12.0.1\",\n    [\"ip_src_addr\"] = \"10.12.0.1/16\",\n    [\"ethType\"] = \"ipv4\",\n    [\"ipProto\"] = \"tcp\",\n    [\"tlen\"] = 42,\n    [\"sport\"] = 9,\n    [\"dport\"] = 10,\n    [\"pktSize\"] = 128,\n    [\"vlanid\"] = 40\n  });\n\nprints(\"compile\", pktgen.decompile(pktgen.info.startSeqIdx, \"all\"));\n\nprints(\"pktgen\", pktgen);\n"
  },
  {
    "path": "test/test3.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n-- A list of the test script for Pktgen and Lua.\n-- Each command somewhat mirrors the pktgen command line versions.\n-- A couple of the arguments have be changed to be more like the others.\n--\n\nprints(\"linkState\", pktgen.linkState(\"all\"));\nprints(\"isSending\", pktgen.isSending(\"all\"));\nprints(\"portSizes\", pktgen.portSizes(\"all\"));\nprints(\"pktStats\", pktgen.pktStats(\"all\"));\nprints(\"portRates\", pktgen.portStats(\"all\", \"rate\"));\nprints(\"portStats\", pktgen.portStats('2', 'port'));\n"
  },
  {
    "path": "test/test_range.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n--pktgen.page(\"range\");\n\n-- Port 0 3c:fd:fe:9c:5c:d8,  Port 1 3c:fd:fe:9c:5c:b8\npktgen.range.dst_mac(\"0\", \"start\", \"3c:fd:fe:9c:5c:b8\");\npktgen.range.src_mac(\"0\", \"start\", \"3c:fd:fe:9c:5c:d8\");\n\npktgen.range.dst_ip(\"0\", \"start\", \"192.168.1.1\");\npktgen.range.dst_ip(\"0\", \"inc\", \"0.0.0.1\");\npktgen.range.dst_ip(\"0\", \"min\", \"192.168.1.1\");\npktgen.range.dst_ip(\"0\", \"max\", \"192.168.1.128\");\n\npktgen.range.src_ip(\"0\", \"start\", \"192.168.0.1\");\npktgen.range.src_ip(\"0\", \"inc\", \"0.0.0.1\");\npktgen.range.src_ip(\"0\", \"min\", \"192.168.0.1\");\npktgen.range.src_ip(\"0\", \"max\", \"192.168.0.128\");\n\npktgen.set_proto(\"0\", \"udp\");\n\npktgen.range.dst_port(\"0\", \"start\", 2000);\npktgen.range.dst_port(\"0\", \"inc\", 1);\npktgen.range.dst_port(\"0\", \"min\", 2000);\npktgen.range.dst_port(\"0\", \"max\", 4000);\n\npktgen.range.src_port(\"0\", \"start\", 5000);\npktgen.range.src_port(\"0\", \"inc\", 1);\npktgen.range.src_port(\"0\", \"min\", 5000);\npktgen.range.src_port(\"0\", \"max\", 7000);\n\npktgen.range.pkt_size(\"0\", \"start\", 64);\npktgen.range.pkt_size(\"0\", \"inc\", 0);\npktgen.range.pkt_size(\"0\", \"min\", 64);\npktgen.range.pkt_size(\"0\", \"max\", 256);\n\n-- Set up second port\npktgen.range.dst_mac(\"1\", \"start\", \"3c:fd:fe:9c:5c:d8\");\npktgen.range.src_mac(\"1\", \"start\", \"3c:fd:fe:9c:5c:b8\");\n\npktgen.range.dst_ip(\"1\", \"start\", \"192.168.0.1\");\npktgen.range.dst_ip(\"1\", \"inc\", \"0.0.0.1\");\npktgen.range.dst_ip(\"1\", \"min\", \"192.168.0.1\");\npktgen.range.dst_ip(\"1\", \"max\", \"192.168.0.128\");\n\npktgen.range.src_ip(\"1\", \"start\", \"192.168.1.1\");\npktgen.range.src_ip(\"1\", \"inc\", \"0.0.0.1\");\npktgen.range.src_ip(\"1\", \"min\", \"192.168.1.1\");\npktgen.range.src_ip(\"1\", \"max\", \"192.168.1.128\");\n\npktgen.set_proto(\"all\", \"udp\");\n\npktgen.range.dst_port(\"1\", \"start\", 5000);\npktgen.range.dst_port(\"1\", \"inc\", 1);\npktgen.range.dst_port(\"1\", \"min\", 5000);\npktgen.range.dst_port(\"1\", \"max\", 7000);\n\npktgen.range.src_port(\"1\", \"start\", 2000);\npktgen.range.src_port(\"1\", \"inc\", 1);\npktgen.range.src_port(\"1\", \"min\", 2000);\npktgen.range.src_port(\"1\", \"max\", 4000);\n\npktgen.range.pkt_size(\"1\", \"start\", 64);\npktgen.range.pkt_size(\"1\", \"inc\", 0);\npktgen.range.pkt_size(\"1\", \"min\", 64);\npktgen.range.pkt_size(\"1\", \"max\", 256);\n\npktgen.set_range(\"all\", \"on\");\n"
  },
  {
    "path": "test/test_save.lua",
    "content": "--\n-- Pktgen - Ver: 3.0.07 (DPDK 16.07.0-rc3)\n-- Copyright(c) <2010-2026>, Intel Corporation. All rights reserved., Powered by DPDK\n\n-- Command line arguments: (DPDK args are defaults)\n-- ./app/app/x86_64-native-linux-gcc/app/pktgen -c 3fffe -n 3 -m 512 --proc-type primary -- -T -P -m [2-5:6-9].0 -m [10-13:14-17].1 -f themes/black-yellow.theme\n\n-- #######################################################################\n-- Pktgen Configuration script information:\n--   Flags 00000804\n--   Number of ports: 2\n--   Number ports per page: 4\n--   Number descriptors: RX 512 TX: 512\n--   Promiscuous mode is Enabled\n\npackage.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n--#######################################################################\n-- Global configuration:\n-- geometry 132x44\npktgen.mac_from_arp(\"disable\");\n\n-- ######################### Port  0 ##################################\n--\n-- Port:  0, Burst: 32, Rate:100%, Flags:00020020, TX Count:Forever\n--           SeqCnt:4, Prime:1 VLAN ID:0001, Link: <UP-40000-FD>\n--\n-- Set up the primary port information:\npktgen.set('0', 'count', 0);\npktgen.set('0', 'size', 64);\npktgen.set('0', 'rate', 100);\npktgen.set('0', 'burst', 32);\npktgen.set('0', 'sport', 1234);\npktgen.set('0', 'dport', 5678);\npktgen.set('0', 'prime', 1);\npktgen.set_type('0', 'ipv4');\npktgen.set_proto('0', 'tcp');\npktgen.set_ipaddr('0', 'dst', '192.168.1.1');\npktgen.set_ipaddr('0', 'src','192.168.0.1/24');\npktgen.set_mac('0', 'dst', '3c:fd:fe:9e:2c:b8');\npktgen.vlanid('0', 1);\n\npktgen.pattern('0', 'abc');\n\npktgen.jitter('0', 50);\npktgen.mpls('0', 'disable');\npktgen.range.mpls_entry('0', '0');\npktgen.qinq('0', 'disable');\npktgen.range.qinqids('0', 0, 0);\npktgen.gre('0', 'disable');\npktgen.gre_eth('0', 'disable');\npktgen.range.gre_key('0', 0);\n--\n-- Port flag values:\npktgen.icmp_echo('0', 'disable');\npktgen.pcap('0', 'disable');\npktgen.set_range('0', 'disable');\npktgen.latency('0', 'disable');\npktgen.process('0', 'disable');\npktgen.capture('0', 'disable');\npktgen.rxtap('0', 'disable');\npktgen.txtap('0', 'disable');\npktgen.vlan('0', 'disable');\n\n--\n-- Range packet information:\npktgen.range.src_mac('0', 'start', '3c:fd:fe:9e:29:78');\npktgen.range.src_mac('0', 'min', '00:00:00:00:00:00');\npktgen.range.src_mac('0', 'max', '00:00:00:00:00:00');\npktgen.range.src_mac('0', 'inc', '00:00:00:00:00:00');\npktgen.range.dst_mac('0', 'start', '3c:fd:fe:9e:2c:b8');\npktgen.range.dst_mac('0', 'min', '00:00:00:00:00:00');\npktgen.range.dst_mac('0', 'max', '00:00:00:00:00:00');\npktgen.range.dst_mac('0', 'inc', '00:00:00:00:00:00');\n\npktgen.range.src_ip('0', 'start', '192.168.0.1');\npktgen.range.src_ip('0', 'min', '192.168.0.1');\npktgen.range.src_ip('0', 'max', '192.168.0.254');\npktgen.range.src_ip(';0', 'inc', '0.0.0.0');\n\npktgen.range.dst_ip('0', 'start', '192.168.1.1');\npktgen.range.dst_ip('0', 'min', '192.168.1.1');\npktgen.range.dst_ip('0', 'max', '192.168.1.254');\npktgen.range.dst_ip('0', 'inc', '0.0.0.1');\n\npktgen.ip_proto('0', 'tcp');\n\npktgen.range.src_port('0', 'start', 0);\npktgen.range.src_port('0', 'min', 0);\npktgen.range.src_port('0', 'max', 254);\npktgen.range.src_port('0', 'inc', 1);\n\npktgen.range.dst_port('0', 'start', 0);\npktgen.range.dst_port('0', 'min', 0);\npktgen.range.dst_port('0', 'max', 254);\npktgen.range.dst_port('0', 'inc', 1);\n\npktgen.range.vlan_id('0', 'start', 1);\npktgen.range.vlan_id('0', 'min', 1);\npktgen.range.vlan_id('0', 'max', 4095);\npktgen.range.vlan_id('0', 'inc', 0);\n\npktgen.range.pkt_size('0', 'start', 64);\npktgen.range.pkt_size('0', 'min', 64);\npktgen.range.pkt_size('0', 'max', 1518);\npktgen.range.pkt_size('0', 'inc', 0);\n\n--\n-- Set up the sequence data for the port.\npktgen.set('0', 'seq_cnt', 4);\n\n-- (seqnum, port, dst_mac, src_mac, ip_dst, ip_src, sport, dport, ethType, proto, vlanid, pktSize, gtpu_teid)\n-- pktgen.seq(0, '0', '3c:fd:fe:9e:2c:b8' '3c:fd:fe:9e:29:78', '192.168.1.1', '192.168.0.1/24', 1234, 5678, 'ipv4', 'tcp', 1, 64, 0);\n-- pktgen.seq(1, '0', '3c:fd:fe:9e:2c:b8' '3c:fd:fe:9e:29:78', '192.168.1.1', '192.168.0.1/24', 1234, 5678, 'ipv4', 'tcp', 1, 64, 0);\n-- pktgen.seq(2, '0', '3c:fd:fe:9e:2c:b8' '3c:fd:fe:9e:29:78', '192.168.1.1', '192.168.0.1/24', 1234, 5678, 'ipv4', 'tcp', 1, 64, 0);\n-- pktgen.seq(3, '0', '3c:fd:fe:9e:2c:b8' '3c:fd:fe:9e:29:78', '192.168.1.1', '192.168.0.1/24', 1234, 5678, 'ipv4', 'tcp', 1, 64, 0);\nlocal seq_table = {}\nseq_table[0] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:2c:b8',\n  ['eth_src_addr'] = '3c:fd:fe:9e:29:78',\n  ['ip_dst_addr'] = '192.168.1.1',\n  ['ip_src_addr'] = '192.168.0.1',\n  ['sport'] = 1234,\n  ['dport'] = 5678,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[1] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:2c:b8',\n  ['eth_src_addr'] = '3c:fd:fe:9e:29:78',\n  ['ip_dst_addr'] = '192.168.1.1',\n  ['ip_src_addr'] = '192.168.0.1',\n  ['sport'] = 1234,\n  ['dport'] = 5678,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[2] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:2c:b8',\n  ['eth_src_addr'] = '3c:fd:fe:9e:29:78',\n  ['ip_dst_addr'] = '192.168.1.1',\n  ['ip_src_addr'] = '192.168.0.1',\n  ['sport'] = 1234,\n  ['dport'] = 5678,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[3] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:2c:b8',\n  ['eth_src_addr'] = '3c:fd:fe:9e:29:78',\n  ['ip_dst_addr'] = '192.168.1.1',\n  ['ip_src_addr'] = '192.168.0.1',\n  ['sport'] = 1234,\n  ['dport'] = 5678,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\npktgen.seqTable(0, '0', seq_table[0]);\npktgen.seqTable(1, '0', seq_table[1]);\npktgen.seqTable(2, '0', seq_table[2]);\npktgen.seqTable(3, '0', seq_table[3]);\n\n\n-- Rnd bitfeilds\npktgen.rnd('0', 0, 100, 'XXX...111.000...111.XXX.........');\npktgen.rnd('0', 1, 100, 'X11...111.000...111.XXX.........');\n-- ######################### Port  1 ##################################\n--\n-- Port:  1, Burst: 32, Rate:100%, Flags:00020020, TX Count:Forever\n--           SeqCnt:4, Prime:1 VLAN ID:0001, Link: <UP-40000-FD>\n--\n-- Set up the primary port information:\npktgen.set('1', 'count', 0);\npktgen.set('1', 'size', 64);\npktgen.set('1', 'rate', 100);\npktgen.set('1', 'burst', 32);\npktgen.set('1', 'sport', 1234);\npktgen.set('1', 'dport', 5678);\npktgen.set('1', 'prime', 1);\npktgen.set_type('1', 'ipv4');\npktgen.set_proto('1', 'tcp');\npktgen.set_ipaddr('1', 'dst', '192.168.0.1');\npktgen.set_ipaddr('1', 'src','192.168.1.1/24');\npktgen.set_mac('1', 'dst', '3c:fd:fe:9e:29:78');\npktgen.vlanid('1', 1);\n\npktgen.pattern('1', 'abc');\n\npktgen.jitter('1', 50);\npktgen.mpls('1', 'disable');\npktgen.mpls_entry('1', '0');\npktgen.qinq('1', 'disable');\npktgen.qinqids('1', 0, 0);\npktgen.gre('1', 'disable');\npktgen.gre_eth('1', 'disable');\npktgen.gre_key('1', 0);\n--\n-- Port flag values:\npktgen.icmp_echo('1', 'disable');\npktgen.pcap('1', 'disable');\npktgen.set_range('1', 'disable');\npktgen.latency('1', 'disable');\npktgen.process('1', 'disable');\npktgen.capture('1', 'disable');\npktgen.rxtap('1', 'disable');\npktgen.txtap('1', 'disable');\npktgen.vlan('1', 'disable');\n\n--\n-- Range packet information:\npktgen.range.src_mac('1', 'start', '3c:fd:fe:9e:2c:b8');\npktgen.range.src_mac('1', 'min', '00:00:00:00:00:00');\npktgen.range.src_mac('1', 'max', '00:00:00:00:00:00');\npktgen.range.src_mac('1', 'inc', '00:00:00:00:00:00');\npktgen.range.dst_mac('1', 'start', '3c:fd:fe:9e:29:78');\npktgen.range.dst_mac('1', 'min', '00:00:00:00:00:00');\npktgen.range.dst_mac('1', 'max', '00:00:00:00:00:00');\npktgen.range.dst_mac('1', 'inc', '00:00:00:00:00:00');\n\npktgen.range.src_ip('1', 'start', '192.168.1.1');\npktgen.range.src_ip('1', 'min', '192.168.1.1');\npktgen.range.src_ip('1', 'max', '192.168.1.254');\npktgen.range.src_ip(';1', 'inc', '0.0.0.0');\n\npktgen.range.dst_ip('1', 'start', '192.168.2.1');\npktgen.range.dst_ip('1', 'min', '192.168.2.1');\npktgen.range.dst_ip('1', 'max', '192.168.2.254');\npktgen.range.dst_ip('1', 'inc', '0.0.0.1');\n\npktgen.ip_proto('1', 'tcp');\n\npktgen.range.src_port('1', 'start', 256);\npktgen.range.src_port('1', 'min', 256);\npktgen.range.src_port('1', 'max', 510);\npktgen.range.src_port('1', 'inc', 1);\n\npktgen.range.dst_port('1', 'start', 256);\npktgen.range.dst_port('1', 'min', 256);\npktgen.range.dst_port('1', 'max', 510);\npktgen.range.dst_port('1', 'inc', 1);\n\npktgen.range.vlan_id('1', 'start', 1);\npktgen.range.vlan_id('1', 'min', 1);\npktgen.range.vlan_id('1', 'max', 4095);\npktgen.range.vlan_id('1', 'inc', 0);\n\npktgen.range.pkt_size('1', 'start', 64);\npktgen.range.pkt_size('1', 'min', 64);\npktgen.range.pkt_size('1', 'max', 1518);\npktgen.range.pkt_size('1', 'inc', 0);\n\n--\n-- Set up the sequence data for the port.\npktgen.set('1', 'seq_cnt', 4);\n\n-- (seqnum, port, dst_mac, src_mac, ip_dst, ip_src, sport, dport, ethType, proto, vlanid, pktSize, gtpu_teid)\n-- pktgen.seq(0, '1', '3c:fd:fe:9e:29:78' '3c:fd:fe:9e:2c:b8', '192.168.0.1', '192.168.1.1/24', 1234, 5678, 'ipv4', 'tcp', 1, 64, 0);\n-- pktgen.seq(1, '1', '3c:fd:fe:9e:29:78' '3c:fd:fe:9e:2c:b8', '192.168.0.1', '192.168.1.1/24', 1234, 5678, 'ipv4', 'tcp', 1, 64, 0);\n-- pktgen.seq(2, '1', '3c:fd:fe:9e:29:78' '3c:fd:fe:9e:2c:b8', '192.168.0.1', '192.168.1.1/24', 1234, 5678, 'ipv4', 'tcp', 1, 64, 0);\n-- pktgen.seq(3, '1', '3c:fd:fe:9e:29:78' '3c:fd:fe:9e:2c:b8', '192.168.0.1', '192.168.1.1/24', 1234, 5678, 'ipv4', 'tcp', 1, 64, 0);\nlocal seq_table = {}\nseq_table[0] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:29:78',\n  ['eth_src_addr'] = '3c:fd:fe:9e:2c:b8',\n  ['ip_dst_addr'] = '192.168.0.1',\n  ['ip_src_addr'] = '192.168.1.1',\n  ['sport'] = 1234,\n  ['dport'] = 5678,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[1] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:29:78',\n  ['eth_src_addr'] = '3c:fd:fe:9e:2c:b8',\n  ['ip_dst_addr'] = '192.168.0.1',\n  ['ip_src_addr'] = '192.168.1.1',\n  ['sport'] = 1234,\n  ['dport'] = 5678,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[2] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:29:78',\n  ['eth_src_addr'] = '3c:fd:fe:9e:2c:b8',\n  ['ip_dst_addr'] = '192.168.0.1',\n  ['ip_src_addr'] = '192.168.1.1',\n  ['sport'] = 1234,\n  ['dport'] = 5678,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[3] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:29:78',\n  ['eth_src_addr'] = '3c:fd:fe:9e:2c:b8',\n  ['ip_dst_addr'] = '192.168.0.1',\n  ['ip_src_addr'] = '192.168.1.1',\n  ['sport'] = 1234,\n  ['dport'] = 5678,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\npktgen.seqTable(0, '1', seq_table[0]);\npktgen.seqTable(1, '1', seq_table[1]);\npktgen.seqTable(2, '1', seq_table[2]);\npktgen.seqTable(3, '1', seq_table[3]);\n\n\n-- Rnd bitfeilds\npktgen.rnd('1', 1, 100, 'X11...111.000...111.XXX.........');\n-- ################################ Done #################################\n"
  },
  {
    "path": "test/test_seq.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n--\n-- Set up the sequence data for the port.\npktgen.set('0', 'seq_cnt', 8);\n\n-- (seqnum, port, dst_mac, src_mac, ip_dst, ip_src, sport, dport, ethType, proto, vlanid, pktSize, gtpu_teid)\n-- pktgen.seq(0, '0', '3c:fd:fe:9e:2c:b8' '3c:fd:fe:9e:29:78', '192.168.1.1', '192.168.0.1/24', 1234, 5678, 'ipv4', 'tcp', 1, 64, 0);\n-- pktgen.seq(1, '0', '3c:fd:fe:9e:2c:b8' '3c:fd:fe:9e:29:78', '192.168.1.1', '192.168.0.1/24', 1234, 5678, 'ipv4', 'tcp', 1, 64, 0);\n-- pktgen.seq(2, '0', '3c:fd:fe:9e:2c:b8' '3c:fd:fe:9e:29:78', '192.168.1.1', '192.168.0.1/24', 1234, 5678, 'ipv4', 'tcp', 1, 64, 0);\n-- pktgen.seq(3, '0', '3c:fd:fe:9e:2c:b8' '3c:fd:fe:9e:29:78', '192.168.1.1', '192.168.0.1/24', 1234, 5678, 'ipv4', 'tcp', 1, 64, 0);\nlocal seq_table = {}\nseq_table[0] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:2c:b8',\n  ['eth_src_addr'] = '3c:fd:fe:9e:29:78',\n  ['ip_dst_addr'] = '192.168.1.1',\n  ['ip_src_addr'] = '192.168.0.1/24',\n  ['sport'] = 12340,\n  ['dport'] = 56780,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[1] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:2c:b9',\n  ['eth_src_addr'] = '3c:fd:fe:9e:29:79',\n  ['ip_dst_addr'] = '192.168.2.1',\n  ['ip_src_addr'] = '192.168.0.2/24',\n  ['sport'] = 12341,\n  ['dport'] = 56781,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[2] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:2c:ba',\n  ['eth_src_addr'] = '3c:fd:fe:9e:29:7a',\n  ['ip_dst_addr'] = '192.168.3.1',\n  ['ip_src_addr'] = '192.168.0.3/24',\n  ['sport'] = 12342,\n  ['dport'] = 56782,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[3] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:2c:bb',\n  ['eth_src_addr'] = '3c:fd:fe:9e:29:7b',\n  ['ip_dst_addr'] = '192.168.4.1',\n  ['ip_src_addr'] = '192.168.0.4/24',\n  ['sport'] = 12343,\n  ['dport'] = 56783,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[4] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:2c:bc',\n  ['eth_src_addr'] = '3c:fd:fe:9e:29:7c',\n  ['ip_dst_addr'] = '192.168.5.1',\n  ['ip_src_addr'] = '192.168.0.5/24',\n  ['sport'] = 12344,\n  ['dport'] = 56784,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[5] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:2c:bd',\n  ['eth_src_addr'] = '3c:fd:fe:9e:29:7d',\n  ['ip_dst_addr'] = '192.168.6.1',\n  ['ip_src_addr'] = '192.168.0.6/24',\n  ['sport'] = 12345,\n  ['dport'] = 56785,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[6] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:2c:be',\n  ['eth_src_addr'] = '3c:fd:fe:9e:29:7e',\n  ['ip_dst_addr'] = '192.168.7.1',\n  ['ip_src_addr'] = '192.168.0.7/24',\n  ['sport'] = 12346,\n  ['dport'] = 56786,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\nseq_table[7] = {\n  ['eth_dst_addr'] = '3c:fd:fe:9e:2c:bf',\n  ['eth_src_addr'] = '3c:fd:fe:9e:29:7f',\n  ['ip_dst_addr'] = '192.168.8.1',\n  ['ip_src_addr'] = '192.168.0.8/24',\n  ['sport'] = 12347,\n  ['dport'] = 56787,\n  ['ethType'] = 'ipv4',\n  ['ipProto'] = 'tcp',\n  ['vlanid'] = 1,\n  ['pktSize'] = 60,\n  ['gtpu_teid'] = 0\n}\npktgen.seqTable(0, '0', seq_table[0]);\npktgen.seqTable(1, '0', seq_table[1]);\npktgen.seqTable(2, '0', seq_table[2]);\npktgen.seqTable(3, '0', seq_table[3]);\npktgen.seqTable(4, '0', seq_table[4]);\npktgen.seqTable(5, '0', seq_table[5]);\npktgen.seqTable(6, '0', seq_table[6]);\npktgen.seqTable(7, '0', seq_table[7]);\n"
  },
  {
    "path": "test/tx-rx-loopback.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\nrequire \"Pktgen\";\n\nlocal pkt_size          = 64;\nlocal duration          = 10000;\nlocal pauseTime         = 1000;\n\nlocal sendport          = \"0\";\nlocal recvport          = \"1\";\nlocal num_tx, num_rx, num_dropped;\n\n\n-- 'set' commands for a number of per port values\npktgen.set(\"all\", \"rate\", 100);\npktgen.set(\"all\", \"size\", pkt_size);\n-- send continuous stream of traffic\npktgen.set(\"all\", \"count\", 0);\n-- pktgen.set(\"all\", \"burst\", 128);\npktgen.set(\"all\", \"sport\", 0x5678);\npktgen.set(\"all\", \"dport\", 0x9988);\n-- pktgen.set(\"all\", \"prime\", 3);\n\npktgen.set_ipaddr(\"0\", \"dst\", \"172.17.25.150\");\npktgen.set_ipaddr(\"0\", \"src\", \"172.17.25.15/24\");\npktgen.set_ipaddr(\"1\", \"dst\", \"172.17.25.15\");\npktgen.set_ipaddr(\"1\", \"src\", \"172.17.25.150/24\");\npktgen.set_proto(\"all\", \"udp\");\n-- pktgen.set_type(\"all\", \"ipv4\");\n\n\n--pktgen.clr();\npktgen.start(\"all\");\npktgen.delay(duration);\npktgen.stop(\"all\");\npktgen.delay(pauseTime);\n\n\nstatTx = pktgen.portStats(sendport, \"port\")[tonumber(sendport)];\nstatRx = pktgen.portStats(recvport, \"port\")[tonumber(recvport)];\nnum_tx = statTx.opackets;\nnum_rx = statRx.ipackets;\nnum_dropped = num_tx - num_rx;\nprint(\"Tx: \" .. num_tx .. \". Rx: \" .. num_rx .. \". Dropped: \" .. num_dropped);\n\nprints(\"DEBUG portStats\", pktgen.portStats(\"all\", \"port\"));\n"
  },
  {
    "path": "test/vlan_udp_range.lua",
    "content": "package.path = package.path ..\";?.lua;test/?.lua;app/?.lua;\"\n\nrequire \"Pktgen\"\n-- A list of the test script for Pktgen and Lua.\n-- Each command somewhat mirrors the pktgen command line versions.\n-- A couple of the arguments have be changed to be more like the others.\n--\n\nlocal function doWait(port, waitTime)\n    local idx;\n\n\tpktgen.delay(1000);\n\n\tif ( waitTime == 0 ) then\n\t\treturn;\n\tend\n\twaitTime = waitTime - 1;\n\n    -- Try to wait for the total number of packets to be sent.\n    local idx = 0;\n    while( idx < waitTime ) do\n\n        idx = idx + 1;\n\n        local sending = pktgen.isSending(port);\n        if ( sending[tonumber(port)] == \"n\" ) then\n            break;\n        end\n        pktgen.delay(1000);\n    end\n\nend\n\n\npktgen.reset(\"all\");\n\n-- 'set' commands for a number of per port values\npktgen.set(\"all\", \"count\", 100);\npktgen.set(\"all\", \"rate\", 1);\npktgen.set(\"all\", \"size\", 64);\npktgen.set(\"all\", \"burst\", 128);\n\npktgen.vlanid(\"all\", 55);\npktgen.set_type(\"all\", \"ipv4\");\npktgen.set_proto(\"all\", \"udp\");\n\npktgen.clear(\"all\");\npktgen.cls();\n\npktgen.pause(\"Do range commands\\n\", 1000);\npktgen.page(\"range\");\n\npktgen.range.dst_mac(\"all\", \"start\", \"0011:2233:4455\");\npktgen.range.src_mac(\"all\", \"start\", \"0033:2233:4455\");\n\n--pktgen.delay(1000);\npktgen.range.dst_ip(\"all\", \"start\", \"10.10.10.1\");\npktgen.range.dst_ip(\"all\", \"inc\", \"0.0.0.1\");\npktgen.range.dst_ip(\"all\", \"min\", \"10.10.10.1\");\npktgen.range.dst_ip(\"all\", \"max\", \"10.10.10.64\");\n\n--pktgen.delay(1000);\npktgen.range.src_ip(\"all\", \"start\", \"11.11.11.1\");\npktgen.range.src_ip(\"all\", \"inc\", \"0.0.0.1\");\npktgen.range.src_ip(\"all\", \"min\", \"11.11.11.1\");\npktgen.range.src_ip(\"all\", \"max\", \"11.11.11.64\");\n\npktgen.set_proto(\"all\", \"udp\");\n\n--pktgen.delay(1000);\npktgen.range.dst_port(\"all\", \"start\", 2152);\npktgen.range.dst_port(\"all\", \"inc\", 0);\npktgen.range.dst_port(\"all\", \"min\", 2152);\npktgen.range.dst_port(\"all\", \"max\", 2152);\n\npktgen.delay(1000);\npktgen.range.src_port(\"all\", \"start\", 1000);\npktgen.range.src_port(\"all\", \"inc\", 1);\npktgen.range.src_port(\"all\", \"min\", 1000);\npktgen.range.src_port(\"all\", \"max\", 9999);\n\n--pktgen.delay(1000);\npktgen.range.vlan_id(\"all\", \"start\", 100);\npktgen.range.vlan_id(\"all\", \"inc\", 1);\npktgen.range.vlan_id(\"all\", \"min\", 100);\npktgen.range.vlan_id(\"all\", \"max\", 4094);\n\n--pktgen.delay(1000);\npktgen.range.pkt_size(\"all\", \"start\", 128);\npktgen.range.pkt_size(\"all\", \"inc\", 2);\npktgen.range.pkt_size(\"all\", \"min\", 64);\npktgen.range.pkt_size(\"all\", \"max\", 1518);\n\npktgen.set_range(\"all\", \"on\");\npktgen.vlan(\"all\", \"on\");\n\npktgen.pause(\"Wait a second, then go back to main page\\n\", 1000);\n\npktgen.page(\"0\");\n\npktgen.pause(\"About to do range\\n\", 2000);\n\npktgen.start(\"all\");\nprintf(\"Waiting for TX to run\\n\");\ndoWait(\"0\", 10);\nprintf(\"Done\\n\");\n\nprintf(\"Port Count %d\\n\", pktgen.portCount());\nprintf(\"Total port Count %d\\n\", pktgen.totalPorts());\n"
  },
  {
    "path": "themes/black-yellow.theme",
    "content": "theme default white white off\ntheme top.spinner cyan none bold\ntheme top.ports green none bold\ntheme top.page white none bold\ntheme top.copyright yellow none off\ntheme top.poweredby blue none bold\ntheme sep.dash blue none off\ntheme sep.text white none off\ntheme stats.port.label blue none bold\ntheme stats.port.flags blue none bold\ntheme stats.port.data blue none off\ntheme stats.port.status green none off\ntheme stats.port.linklbl green none bold\ntheme stats.port.link green none off\ntheme stats.port.ratelbl white none bold\ntheme stats.port.rate white none off\ntheme stats.port.sizelbl cyan none bold\ntheme stats.port.sizes cyan none off\ntheme stats.port.errlbl red none bold\ntheme stats.port.errors red none off\ntheme stats.port.totlbl blue none bold\ntheme stats.port.totals blue none off\ntheme stats.dyn.label blue none bold\ntheme stats.dyn.values green none off\ntheme stats.stat.label magenta none off\ntheme stats.stat.values white none off\ntheme stats.total.label red none bold\ntheme stats.total.data blue none bold\ntheme stats.colon blue none bold\ntheme stats.rate.count blue none bold\ntheme stats.bdf blue none off\ntheme stats.mac green none off\ntheme stats.ip cyan none off\ntheme pktgen.prompt green none off\ntheme on\ncls\n"
  },
  {
    "path": "themes/white-black.theme",
    "content": "theme default white none off\ntheme top.spinner cyan none bold\ntheme top.ports green none bold\ntheme top.page white none off\ntheme top.copyright yellow none off\ntheme top.poweredby blue none bold\ntheme sep.dash blue none off\ntheme sep.text white none off\ntheme stats.port.label blue none bold\ntheme stats.port.flags blue none bold\ntheme stats.port.status green none bold\ntheme stats.dyn.label yellow none off\ntheme stats.dyn.values yellow none off\ntheme stats.stat.label magenta none off\ntheme stats.stat.values white none off\ntheme stats.total.label red none bold\ntheme stats.colon blue none bold\ntheme stats.rate.count yellow none off\ntheme stats.bdf blue none bold\ntheme stats.mac green none bold\ntheme stats.ip cyan none bold\ntheme pktgen.prompt green none off\ntheme on\ncls\n"
  },
  {
    "path": "tools/call-sphinx-build.py",
    "content": "#! /usr/bin/env python3\n# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2020-2026> Intel Corporation\n#\n\nimport sys\nimport os\nfrom os.path import join\nfrom subprocess import run, PIPE, STDOUT\nfrom distutils.version import StrictVersion\n\n(sphinx, src, dst) = sys.argv[1:]  # assign parameters to variables\n\n# for sphinx version >= 1.7 add parallelism using \"-j auto\"\nver = run([sphinx, '--version'], stdout=PIPE, stderr=STDOUT).stdout.decode().split()[-1]\nsphinx_cmd =[sphinx]\nif StrictVersion(ver) >= StrictVersion('1.7'):\n\tsphinx_cmd += ['-j', 'auto']\n\n# find all the files sphinx will process so we can write them as dependencies\nsrcfiles = []\nfor root, dirs, files in os.walk(src):\n    srcfiles.extend([join(root, f) for f in files])\n\n# run sphinx, putting the html output in a \"html\" directory\nprocess = run(sphinx_cmd + ['-b', 'html', src, join(dst, 'html')], check=True)\nprint(str(process.args) + ' Done OK')\n\n# create a gcc format .d file giving all the dependencies of this doc build\nwith open(join(dst, '.html.d'), 'w') as d:\n    d.write('html: ' + ' '.join(srcfiles) + '\\n')\n"
  },
  {
    "path": "tools/dpdk-version.sh",
    "content": "#!/usr/bin/env sh\n#\n#   Copyright(c) <2020-2025> Intel Corporation. All rights reserved.\n#\n# SPDX-License-Identifier: BSD-3-Clause\n#\n# Locate the rte_version.h file and parse out the version strings.\n#\n# If RTE_SDK is set, then it is used to locate the file. If the\n# RTE_SDK variable is not set then the current working directory\n# is used.\n#\n# See usage below for more details.\n#\n\nfname=rte_version.h\n\nusage() {\n\tcat <<EOM\n  Usage: dpdk-version.sh\n\n  Locate the rte_version.h file and parse out the version information.\n\n  If RTE_SDK is set, then it is used to locate the file. If the\n  RTE_SDK variable is not set then the current working directory\n  is used.\n\n  The default output of the version is 'DPDK Version: YY.MM.MINOR'\n\te.g. 'dpdk-version.sh' gives 'DPDK Version: 17.08.0'\n\n  Options: Only one option can be used at a time, the only exception\n           is the '-v' option which must be first followed by any\n           other option.\n\n  '-h' prints usage messages\n\n  '-v' print out the location of the file used, must be first option\n    e.g. 'dpdk-version.sh -v -l' gives file path plus '-l' output.\n\n  '-l' prints out the full version string\n\te.g. 'dpdk-version.sh -l' gives 'DPDK-17.08.0-rc0'\n\n  '-s' prints a shorter version string\n\te.g. 'dpdk-version.sh -s' gives '17.08.0'\n\n  '-yy' prints the year number\n  '-mm' prints the month number\n  '-mi' prints the minor number\n  '-rc' prints the suffix string\n\n  '-m' prints out information is comma delimited format.\n\te.g. 'dpdk-version.sh -m' give 'DPDK,17,08,0,-rc0'\n\n  '-p' prints out a Python readable format of the version.\n\te.g. 'dpdk-version.sh -m' gives\n\n\tversion = {\n\t\t'prefix': 'DPDK',\n\t\t'year': '17',\n\t\t'month': '08',\n\t\t'minor': '0',\n\t\t'suffix': '-rc'\n\t}\nEOM\n\texit 1\n}\n\nif [ -z ${RTE_INCLUDE} ] ; then\n\tif [ -z ${RTE_SDK} ] ; then\n\t\techo \"*** RTE_SDK is not set, using \"${PWD}\n\t\tsdk=${PWD}\n\telse\n\t\tsdk=${RTE_SDK}\n\tfi\n\n\tfpath=${sdk}/lib/librte_eal/common/include/${fname}\nelse\n\tfpath=${RTE_INCLUDE}/${fname}\nfi\n\nif [ ! -e ${fpath} ]; then\n\techo \"File not found @ \"${fpath}\n\tusage\nfi\n\nPREFIX=`grep \"define RTE_VER_PREFIX\" ${fpath} | cut -d ' ' -f 3 | sed -e 's/\\\"//g'`\nSUFFIX=`grep \"define RTE_VER_SUFFIX\" ${fpath} | cut -d ' ' -f 3 | sed -e 's/\\\"//g'`\n\nYY=`grep \"define RTE_VER_YEAR\" ${fpath} | cut -d ' ' -f 3`\nMM=`grep \"define RTE_VER_MONTH\" ${fpath} | cut -d ' ' -f 3`\nMINOR=`grep \"define RTE_VER_MINOR\" ${fpath} | cut -d ' ' -f 3`\n\nprint_path=\"no\"\nif [ \"$1\" = \"-v\" ] ; then\n\tprint_path=\"yes\"\n\tshift\nfi\n\nif [ \"$1\" = \"-h\" ]; then\n\tusage\n\tprint_path=\"no\"\nelif [ \"$1\" = \"-l\" ]; then\n\techo ${PREFIX}-${YY}.${MM}.${MINOR}${SUFFIX}\nelif [ \"$1\" = '-s' ]; then\n\techo ${YY}.${MM}.${MINOR}\nelif [ \"$1\" = \"-yy\" ]; then\n\techo ${YY}\nelif [ \"$1\" = \"-mm\" ]; then\n\techo ${MM}\nelif [ \"$1\" = \"-mi\" ]; then\n\techo ${MINOR}\nelif [ \"$1\" = \"-rc\" ]; then\n\techo ${SUFFIX}\nelif [ \"$1\" = \"-p\" ]; then\n\techo \"dpdk_version = {\"\n\tif [ ${print_path} = \"yes\" ] ; then\n\t\techo \"    'path': '\"${fpath}\"',\"\n\t\tprint_path=\"no\"\n\tfi\n\techo \"    'prefix': '\"${PREFIX}\"',\"\n\techo \"    'year': '\"${YY}\"',\"\n\techo \"    'month': '\"${MM}\"',\"\n\techo \"    'minor': '\"${MINOR}\"',\"\n\techo \"    'suffix': '\"${SUFFIX}\"'\"\n\techo \"}\"\nelif [ \"$1\" = \"-m\" ]; then\n\techo \"\"${PREFIX}\",\"${YY}\",\"${MM}\",\"${MINOR}\",\"${SUFFIX}\"\"\nelse\n\techo \"DPDK Version: \"${YY}.${MM}.${MINOR}\nfi\nif [ ${print_path} = \"yes\" ] ; then\n\techo \"File: \"${fpath}\nfi\n"
  },
  {
    "path": "tools/format-clang.sh",
    "content": "#! /bin/bash\n#\n\nfor filename in $(find . -path ./Builddir -prune -o -name \"*.[ch]\"); do\n    if [ -f $filename ]; then\n        echo \"Process File: $filename\"\n        clang-format -style=file -i $filename; # git add $filename;\n    fi\ndone\n"
  },
  {
    "path": "tools/meson.build",
    "content": "# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2019-2026> Intel Corporation\n\n# set up map-to-def script using python, either built-in or external\npython3 = import('python').find_installation(required: false)\nif python3.found()\n    py3 = [python3]\nelse\n    py3 = ['meson', 'runpython']\nendif\nsphinx_wrapper = py3 + files('call-sphinx-build.py')\n"
  },
  {
    "path": "tools/pktgen-build.sh",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: BSD-3-Clause\n# Copyright(c) <2019-2025> Intel Corporation\n\n# A simple script to help build Pktgen using meson/ninja tools.\n# The script also creates an installed directory called ./usr in the top level directory.\n# The install directory will contain all of the includes and libraries\n# for external applications to build and link with Pktgen.\n#\n# using 'pktgen-build.sh help' or 'pktgen-build.sh -h' or 'pktgen-build.sh --help' to see help information.\n#\n\nbuildtype=\"release\"\n\ncurrdir=`pwd`\nscript_dir=$(cd \"${BASH_SOURCE[0]%/*}\" & pwd -P)\nexport sdk_path=\"${PKTGEN_SDK:-${script_dir%/*}}\"\nexport target_dir=\"${PKTGEN_TARGET:-usr/local}\"\nexport build_dir=\"${PKTGEN_BUILD:-${currdir}/builddir}\"\ninstall_path=\"${PKTGEN_DESTDIR:-${currdir}}\"\n\nexport lua_enabled=\"-Denable_lua=false\"\n\nconfigure=\"setup\"\n\nif [[ \"${build_dir}\" = /* ]]; then\n\t# absolute path to build dir. Do not prepend workdir.\n\tbuild_path=$build_dir\nelse\n\tbuild_path=${currdir}/$build_dir\nfi\n\nif [[ ! \"${install_path}\" = /* ]]; then\n\t# relative path for install path detected\n\t# prepend with currdir\n\tinstall_path=${currdir}/${install_path}\nfi\n\nif [[ \"${target_dir}\" = .* ]]; then\n\techo \"target_dir starts with . or .. if different install prefix required then use PKTGEN_DESTDIR instead\"\n\texit 1\nfi\nif [[ \"${target_dir}\" = /* ]]; then\n\techo \"target_dir absolute path detected removing leading '/'\"\n\texport target_dir=${target_dir##/}\nfi\ntarget_path=${install_path%/}/${target_dir%/}\n\necho \">>  SDK Path          : \"$sdk_path\necho \">>  Install Path      : \"$install_path\necho \">>  Build Directory   : \"$build_dir\necho \">>  Target Directory  : \"$target_dir\necho \">>  Build Path        : \"$build_path\necho \">>  Target Path       : \"$target_path\necho \"\"\n\nfunction dump_options() {\n\techo \" Build and install values:\"\n\techo \"   lua_enabled       : \"$lua_enabled\n\techo \"\"\n}\n\nfunction run_meson() {\n\tbtype=\"-Dbuildtype=\"$buildtype\n\n    echo \"meson $configure $btype $lua_enabled $build_dir\"\n\tif ! meson $configure $btype $lua_enabled $build_dir; then\n        echo \"*** ERROR: meson $configure $btype $lua_enabled $build_dir\"\n        configure=\"\"\n        return 1\n    fi\n\n    configure=\"\"\n    return 0\n}\n\nfunction ninja_build() {\n\techo \">>> Ninja build in '\"$build_path\"' buildtype=\"$buildtype\n\n\tif [[ -d $build_path ]] || [[ -f $build_path/build.ninja ]]; then\n\t\t# add reconfigure command if meson dir already exists\n\t\tconfigure=\"configure\"\n\t\t# sdk_dir must be empty if we're reconfiguring\n\t\tsdk_dir=\"\"\n\tfi\n\tif ! run_meson; then\n        return 1\n    fi\n\n\tninja -C $build_path\n\n\tif [[ $? -ne 0 ]]; then\n\t\treturn 1;\n\tfi\n\n\treturn 0\n}\n\nfunction ninja_build_docs() {\n\techo \">>> Ninja build documents in '\"$build_path\"'\"\n\n\tif [[ ! -d $build_path ]] || [[ ! -f $build_path/build.ninja ]]; then\n\t\tif ! run_meson; then\n            return 1\n        fi\n\tfi\n\n\tninja -C $build_path docs\n\n\tif [[ $? -ne 0 ]]; then\n\t\treturn 1;\n\tfi\n\treturn 0\n}\n\nninja_install() {\n\techo \">>> Ninja install to '\"$target_path\"'\"\n\n\tDESTDIR=$install_path ninja -C $build_path install\n\n\tif [[ $? -ne 0 ]]; then\n\t\techo \"*** Install failed!!\"\n\t\treturn 1;\n\tfi\n\treturn 0\n}\n\nninja_uninstall() {\n\techo \">>> Ninja uninstall to '\"$target_path\"'\"\n\n\tDESTDIR=$install_path ninja -C $build_path uninstall\n\n\tif [[ $? -ne 0 ]]; then\n\t\techo \"*** Uninstall failed!!\"\n\t\treturn 1;\n\tfi\n\treturn 0\n}\n\nusage() {\n\techo \" Usage: Build Pktgen using Meson/Ninja tools\"\n\techo \"  ** Must be in the top level directory for Pktgen\"\n\techo \"     This tool is in tools/pktgen-build.sh, but use 'make' which calls this script\"\n\techo \"     Use 'make' to build Pktgen as it allows for multiple targets i.e. 'make clean debug'\"\n\techo \"\"\n\techo \"  Command Options:\"\n\techo \"  <no args>   - create the '\"$build_dir\"' directory if not present and compile Pktgen\"\n\techo \"                If the '\"$build_dir\"' directory exists it will use ninja to build Pktgen without\"\n\techo \"                running meson unless one of the meson.build files were changed\"\n\techo \"  build       - same as 'make' with no arguments\"\n\techo \"  buildlua    - same as 'make build' except enable Lua build\"\n\techo \"  debug       - turn off optimization, may need to do 'clean' then 'debug' the first time\"\n\techo \"  debugopt    - turn optimization on with -O2, may need to do 'clean' then 'debugopt' the first time\"\n\techo \"  clean       - remove the following directory: \"$build_path\n\techo \"  install     - install the includes/libraries into \"$target_path\" directory\"\n\techo \"  uninstall   - uninstall the includes/libraries into \"$target_path\" directory\"\n\techo \"  docs        - create the document files\"\n\techo \"\"\n\techo \" Build and install environment variables:\"\n\techo \"  PKTGEN_INSTALL_PATH - The install path, defaults to Pktgen SDK directory\"\n\techo \"  PKTGEN_TARGET       - The target directory appended to install path, defaults to 'usr'\"\n\techo \"  PKTGEN_BUILD        - The build directory appended to install path, default to 'builddir'\"\n\techo \"  PKTGEN_DESTDIR      - The install destination directory\"\n\techo \"\"\n\tdump_options\n\texit\n}\n\nfor cmd in \"$@\"\ndo\n\tcase \"$cmd\" in\n\t'help' | '-h' | '--help')\n\t\tusage\n\t\t;;\n\n\t'build')\n\t\tdump_options\n\t\tninja_build && ninja_install\n\t\t;;\n\n\t'buildlua')\n\t\tlua_enabled=\"-Denable_lua=true\"\n\t\tdump_options\n\t\tninja_build && ninja_install\n\t\t;;\n\n\t'debuglua')\n\t\tlua_enabled=\"-Denable_lua=true\"\n\t\tbuildtype=\"debug\"\n\t\tdump_options\n\t\tninja_build && ninja_install\n\t\t;;\n\n\t'debug')\n\t\tbuildtype=\"debug\"\n\t\tdump_options\n\t\tninja_build && ninja_install\n\t\t;;\n\n\t'debugopt')\n\t\techo \">>> Debug Optimized build in '\"$build_path\"' and '\"$target_path\"'\"\n\t\tbuildtype=\"debugoptimized\"\n\t\tdump_options\n\t\tninja_build && ninja_install\n\t\t;;\n\n\t'clean')\n\t\tdump_options\n\t\techo \"*** Removing '\"$build_path\"' directory\"\n\t\trm -fr $build_path\n\t\t;;\n\n\t'install')\n\t\tdump_options\n\t\techo \">>> Install the includes/libraries into '\"$target_path\"' directory\"\n\t\tninja_install\n\t\t;;\n\n\t'uninstall')\n\t\tdump_options\n\t\techo \">>> Uninstall the includes/libraries from '\"$target_path\"' directory\"\n\t\tninja_uninstall\n\t\t;;\n\n\t'docs')\n\t\tdump_options\n\t\techo \">>> Create the documents '\"$build_path\"' directory\"\n\t\tninja_build_docs\n\t\t;;\n\n\t*)\n\t\tif [[ $# -gt 0 ]]; then\n\t\t\tusage\n\t\telse\n\t\t\techo \">>> Build and install Pktgen\"\n\t\t\tdump_options\n\t\t\tninja_build && ninja_install\n\t\tfi\n\t\t;;\n\tesac\ndone\n"
  },
  {
    "path": "tools/run.py",
    "content": "#! /usr/bin/env python3\n#\n#   Copyright(c) <2020-2026> Intel Corporation. All rights reserved.\n#\n#  SPDX-License-Identifier: BSD-3-Clause\n#\n\nimport sys\nimport os\nimport getopt\nimport subprocess\nimport glob\nimport types\nimport importlib.machinery\nimport importlib.util\nimport shutil\n\ndef usage():\n        '''Print usage information for the program'''\n        argv0 = os.path.basename(sys.argv[0])\n        print(\"\"\"\nUsage:\n------\n  %(argv0)s [options] [config_name]\n\n  Where config_name is the one of the defined configuration files if no config\n  file is listed then the ./default.cfg file will be used. If a cfg directory\n  is located in the current directory then it will be searched for a match.\n\n  The config_name is the name of the file without path and .cfg extension.\n\nOptions:\n--------\n    -h, --help | -u, --usage:\n            Display usage information and quit\n\n    -s, --setup:\n            Setup the environment for running the configuration file.\n\n    -l, --list:\n            Print a list of known configuration files\n\n    -n, --no-run:\n        Just create the command line and output the line without running Pktgen.\n\n    -v, --verbose\n            Print out more information\n\nExamples:\n---------\n  To display current list of configuration files:\n        %(argv0)s --list\n\n  To run a config file:\n        %(argv0)s default\n\n  The configuration file name is always suffixed by .cfg as in default.cfg.\n  The search location of the configuration files is .:./cfg\n\n        \"\"\" % locals())  # replace items from local variables\n        sys.exit(0)\n\ndef err_exit(str):\n        ''' print the error string and exit '''\n        print(str)\n        sys.exit(1)\n\ndef find_file(fn, t):\n        ''' Find the first file matching the arg value '''\n        f = os.path.splitext(fn)\n        if f[1] == t:\n            fn = f[0]\n        for f in file_list('cfg', t):\n                b = os.path.basename(f)\n                if os.path.splitext(b)[0] == fn:\n                        return f\n        return None\n\ndef mk_tuple(lst, s):\n        ''' Convert a string to a tuple if needed '''\n        t = {}\n\n        if type(lst[s]) != tuple:\n                if verbose:\n                        print('Not a Tuple', type(lst[s]), lst[s])\n                t[s] = tuple([lst[s],])\n        else:\n                if verbose:\n                        print('A tuple', type(lst[s]), lst[s])\n                t[s] = lst[s]\n\n        if verbose:\n                print('New t[s]', type(t[s]), t[s])\n\n        return t[s]\n\ndef add_ld_options(s, arg_list):\n        ''' Append LD_LIBRARY_PATH option to arg list '''\n        if s in cfg.run:\n            str = 'LD_LIBRARY_PATH=.'\n            for a in mk_tuple(cfg.run, s):\n                _p = a % globals()\n                str = str + ':' + _p\n            arg_list.append(str)\n\ndef add_run_options(s, arg_list, p):\n        ''' Append options to arg list '''\n        if s in cfg.run:\n            for a in mk_tuple(cfg.run, s):\n                if p is not None:\n                    arg_list.append(p)\n\n                _p = a % globals()\n                arg_list.append(_p)\n\ndef add_setup_options(s, arg_list):\n        ''' Append options to arg list '''\n        if s in cfg.setup:\n                for a in mk_tuple(cfg.setup, s):\n                        arg_list.extend(a.split(' '))\n\ndef file_list(directory, file_extension):\n        ''' Return list of configuration files '''\n        fileiter = (os.path.join(root, f)\n                for root, _, files in os.walk(directory)\n                        for f in files)\n        return (f for f in fileiter if os.path.splitext(f)[1] == file_extension)\n\ndef load_cfg(fname):\n        ''' Load the configuration or .cfg file as a python data file '''\n\n        if not os.path.exists(fname):\n                err_exit(\"Config file %s does not exists\\n\" % fname)\n\n        try:\n                configuration_file = open(fname)\n        except:\n                err_exit(\"Error: unable to open file %s\\n\" % fname)\n\n        global cfg\n        loader = importlib.machinery.SourceFileLoader('cfg', fname)\n        spec = importlib.util.spec_from_loader(loader.name, loader)\n        cfg = importlib.util.module_from_spec(spec)\n        loader.exec_module(cfg)\n        print(cfg)\n\n        configuration_file.close()\n        shutil.rmtree('cfg/__pycache__')\n\n        return cfg\n\ndef show_configs():\n        ''' Show configuration files '''\n\n        print(\"Configurations:\")\n        print(\"   %-16s - %s\" % (\"Name\", \"Description\"))\n        print(\"   %-16s   %s\" % (\"----\", \"-----------\"))\n\n        for fname in file_list('cfg', '.cfg'):\n                base = os.path.splitext(os.path.basename(fname))[0]\n\n                try:\n                        cfg = load_cfg(fname)\n\n                        if not cfg.description:\n                                cfg.description = \"\"\n                        print(\"   %-16s - %s\" % (base, cfg.description))\n                except NameError:\n                        sys.stderr.write(\"We were unable to load the module \" + fname + \\\n                        \" If you do not plan to use this module you can safely ignore this \" \\\n                        \"message.\\n\")\n                finally:\n                        # reset the description to empty, for next loop/file\n                        cfg.description = \"\"\n\n        sys.exit(0)\n\ndef run_cfg(cfg_file):\n        ''' Run the configuration in the .cfg file '''\n\n        cfg = load_cfg(cfg_file)\n\n        args = []\n\n        add_run_options('exec', args, None)\n\n        add_ld_options('ld_path', args)\n\n        if not 'app_path' in cfg.run:\n                err_exit(\"'app_path' variable is missing from cfg.run in config file\")\n\n        if not 'app_name' in cfg.run:\n                err_exit(\"'app_name' variable is missing from cfg.run in config file\")\n\n        # convert the cfg.run['app_name'] into a global variable used in\n        # the creation of the application/path. app_name must be a global variable.\n        global app_name\n        app_name = cfg.run['app_name']\n\n        # Try all of the different path versions till we find one.\n        fname = None\n        for app in cfg.run['app_path']:\n                fn = app % globals()\n                print(\"   Trying %s\" % fn)\n                if os.path.exists(fn):\n                        fname = fn\n                        if verbose:\n                                print(\"Found %s\" % fn)\n                        break\n\n        if not fname:\n                err_exit(\"Error: Unable to locate application %s\" % cfg.run['app_name'])\n\n        args.extend([fname])\n\n        add_run_options('cores', args, '-l')\n        add_run_options('nrank', args, '-n')\n        add_run_options('proc', args, '--proc-type')\n        add_run_options('log', args, '--log-level')\n        add_run_options('prefix', args, '--file-prefix')\n        add_run_options('shared', args, '-d')\n        add_run_options('blocklist', args, '-b')\n        add_run_options('allowlist', args, '-a')\n        add_run_options('vdev', args, '--vdev')\n        add_run_options('plugin', args, '-d')\n        args.extend([\"--\"])\n        add_run_options('opts', args, None)\n        add_run_options('map', args, '-m')\n        add_run_options('pcap', args, '-s')\n        add_run_options('theme', args, '-f')\n        add_run_options('loadfile', args, '-f')\n        add_run_options('logfile', args, '-l')\n\n        # Convert the args list to a single string with spaces.\n        str = \"\"\n        for a in args:\n                str = str + \"%s \" % a\n\n        # Output the command line\n        print(str)\n        if norun:\n                return\n\n        if verbose:\n                print(\"Command line:\")\n                print(args)\n\n        subprocess.call(args)\n\n        subprocess.call(['stty', 'sane'])\n\ndef num_sockets(hpath):\n        ''' Count the number of sockets in the system '''\n\n        sockets = 0\n        for i in range(0, 8):\n                if os.path.exists(hpath % i):\n                        sockets = sockets + 1\n\n        return sockets\n\ndef setup_cfg(cfg_file):\n        ''' Setup the system by adding modules and ports to dpdk control '''\n\n        cfg = load_cfg(cfg_file)\n\n        print(\"Setup DPDK to run '%s' application from %s file\" %\n                   (cfg.run['app_name'], cfg_file))\n\n        sys_node = '/sys/devices/system/node/node%d/hugepages'\n        hugepage_path = sys_node + '/hugepages-2048kB/nr_hugepages'\n\n        # calculate the number of sockets in the system.\n        nb_sockets = int(num_sockets(hugepage_path))\n        if nb_sockets == 0:\n                nb_sockets = 1\n\n        p = subprocess.Popen(['sysctl', '-n', 'vm.nr_hugepages'],\n                                                 stdout=subprocess.PIPE)\n\n        # split the number of hugepages between the sockets\n        nb_hugepages = int(p.communicate()[0]) / nb_sockets\n\n        if verbose:\n                print(\"  Hugepages per socket %d\" % nb_hugepages)\n\n        if verbose:\n                print(\"  modprobe the 'uio' required module\")\n\n        if 'uio' in cfg.setup:\n            u = cfg.setup['uio']\n\n            if u == 'igb_uio':\n                subprocess.call(['sudo', 'modprobe', \"uio\"])\n\n                if verbose:\n                        print(\"  Remove %s if already installed\" % u)\n\n                ret = subprocess.call(['sudo', 'rmmod', u])\n                if ret > 0:\n                        print(\"  Remove of %s, displayed an error ignore it\" % u)\n\n                uio = (\"%s/%s/kmod/%s.ko\" % (sdk, target, u))\n                if verbose:\n                        print(\"  insmode the %s module\" % uio)\n                subprocess.call(['sudo', 'insmod', uio])\n\n            if u == 'vfio-pci':\n                ret = subprocess.call(['sudo', 'rmmod', u])\n                if ret > 0:\n                        print(\"  Remove of %s, displayed an error ignore it\" % u)\n\n                if verbose:\n                        print(\"  modprobe the %s module\" % u)\n                subprocess.call(['sudo', 'modprobe', u])\n\n            if u == 'uio_pci_generic':\n                ret = subprocess.call(['sudo', 'rmmod', u])\n                if ret > 0:\n                        print(\"  Remove of %s, displayed an error ignore it\" % u)\n\n                if verbose:\n                        print(\"  insmode the %s module\" % u)\n                subprocess.call(['sudo', 'modprobe', u])\n        else:\n            if u == 'vfio-pci':\n                ret = subprocess.call(['sudo', 'rmmod', u])\n                if ret > 0:\n                        print(\"  Remove of %s, displayed an error ignore it\" % u)\n\n                if verbose:\n                        print(\"  modprobe the %s module\" % u)\n                subprocess.call(['sudo', 'modprobe', u])\n\n        for i in range(0, nb_sockets):\n                fn = (hugepage_path % i)\n                if verbose:\n                        print(\"  Set %d socket to %d hugepages\" % (i, nb_hugepages))\n                subprocess.call(['sudo', 'sh', '-c', 'eval',\n                                         'echo %s > %s' % (nb_hugepages, fn)])\n\n        # locate the binding tool\n        if os.path.exists(\"/usr/local/bin/dpdk-devbind.py\"):\n                script =\"/usr/local/bin/dpdk-devbind.py\"\n        else:\n                if os.path.exists(\"%s/bin/dpdk-devbind.py\" % sdk):\n                        script = \"%s/bin/dpdk-devbind.py\" % sdk\n                else:\n                        if os.path.exists(\"%s/usertools/dpdk-devbind.py\" % sdk):\n                                script = \"%s/usertools/dpdk-devbind.py\" % sdk\n                        else:\n                                err_exit(\"Error: Failed to find dpdk-devbind.py or dpdk_nic_bind.py\")\n\n        # build up the system command line to be executed\n        args = []\n        add_setup_options('exec', args)\n\n        args.extend([script])\n\n        args.append('-b')\n        args.append(cfg.setup['uio'])\n\n        add_setup_options('devices', args)\n\n        if verbose:\n                print(\"  Bind following devices to DPDK:\")\n                for a in cfg.setup['devices']:\n                        print(\"         %s\" % a)\n                print(args)\n\n        subprocess.call(args)\n\ndef parse_args():\n        ''' Parse the command arguments '''\n\n        global run_flag, verbose, norun\n\n        run_flag = True\n        verbose = False\n        norun = False\n\n        cfg_file = \"./cfg/default.cfg\"\n\n        if len(sys.argv) <= 1:\n                print(\"*** Pick one of the following config files\\n\")\n                show_configs()\n\n        try:\n                opts, args = getopt.getopt(sys.argv[1:], \"hulsvn\",\n                                [\"help\", \"usage\", \"list\", \"setup\", \"verbose\", \"no-run\", ])\n\n        except getopt.GetoptError as error:\n                print(str(error))\n                usage()\n\n        for opt, _ in opts:\n                if opt == \"--help\" or opt == \"-h\":\n                        usage()\n                if opt == \"--usage\" or opt == \"-u\":\n                        usage()\n                if opt == \"--list\" or opt == \"-l\":\n                        show_configs()\n                if opt == \"--setup\" or opt == \"-s\":\n                        run_flag = False\n                if opt == \"--verbose\" or opt == \"-v\":\n                        verbose = True\n                if opt == \"--no-run\" or opt == \"-n\":\n                        norun = True\n\n        if not args or len(args) > 1:\n                usage()\n\n        fn = find_file(args[0], '.cfg')\n        if not fn:\n                f = args[0]\n                if os.path.splitext(args[0])[1] != '.cfg':\n                    f = args[0] + '.cfg'\n                print(\"*** Config file '%s' not found\" % f)\n                print(\"    Make sure you are running this command in pktgen top directory\")\n                print(\"    e.g. cd Pktgen-DPDK; ./tools/run.py default\")\n                show_configs()\n        else:\n                cfg_file = fn\n\n        return cfg_file\n\ndef main():\n        '''program main function'''\n\n        global sdk, target\n\n        sdk = os.getenv(\"RTE_SDK\")\n\n        target = os.getenv(\"RTE_TARGET\")\n\n        print(\">>> sdk '%s', target '%s'\" % (sdk, target))\n\n        cfg_file = parse_args()\n\n        if run_flag:\n                run_cfg(cfg_file)\n        else:\n                setup_cfg(cfg_file)\n\nif __name__ == \"__main__\":\n        main()\n"
  },
  {
    "path": "tools/sudoGDB",
    "content": "#\n# Wrapper for running a CNDP application with GDB. Normally used with vscode debugging.\n#   CNDP needs to run in sudo or privileged mode, use this wrapper\n#   to run a application if needing to debug the application.\ndir=`pwd`\nabs_path=${dir}/usr/local/lib/x86_64-linux-gnu\nrel_path=${dir}/../lib/x86_64-linux-gnu\nld_paths=${abs_path}:${rel_path}\n\nsudo LD_LIBRARY_PATH=${ld_paths} /usr/bin/gdb \"$@\"\n"
  },
  {
    "path": "tools/vfio-setup.sh",
    "content": "echo 0 > /sys/bus/pci/devices/0000\\:82\\:00.0/sriov_numvfs\nsleep 2\necho 2 > /sys/bus/pci/devices/0000\\:82\\:00.0/sriov_numvfs\n\nifconfig ens260f0 down\nip link set dev ens260f0 vf 0 mac 00:11:22:33:44:01\nip link set dev ens260f0 vf 1 mac 00:11:22:33:44:02\n\n../dpdk/usertools/dpdk-devbind.py -b vfio-pci 82:02.0 82:02.1\n"
  }
]