[
  {
    "path": ".github/dependabot.yml",
    "content": "# Copyright Contributors to the L3AF Project.\n# SPDX-License-Identifier: Apache-2.0\n#\n# For documentation on the format of this file, see\n# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file\n\nversion: 2\nupdates:\n\n  - package-ecosystem: \"github-actions\"\n    # Workflow files are stored in the default location of `.github/workflows`\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n      day: \"saturday\"\n\n  - package-ecosystem: \"gomod\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n      day: \"saturday\"\n"
  },
  {
    "path": ".github/release.yml",
    "content": "# .github/release.yml\n---\nchangelog:\n  exclude:\n    labels:\n      - ignore-for-release\n  categories:\n    - title: Breaking Changes 💥\n      labels:\n        - breaking-change\n        - breaking\n    - title: New Features 🎉\n      labels:\n        - feat\n        - enhancement\n    - title: Bug Fixes 🐛\n      labels:\n        - fix\n        - bugfix\n        - bug\n    - title: Other Changes\n      labels:\n        - \"*\"\n"
  },
  {
    "path": ".github/workflows/ci-build-windows.yaml",
    "content": "# Copyright Contributors to the L3AF Project.\n# SPDX-License-Identifier: Apache-2.0\n#\n# For documentation on the github environment, see\n# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners\n#\n# For documentation on the syntax of this file, see\n# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions\nname: CI Windows build\non:\n  pull_request: {}\n  push:\n    branches:\n      - main\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    runs-on: windows-latest\n\n    steps:\n      - name: Setup Go 1.26.0\n        uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c\n        with:\n          go-version: '1.26.0'\n\n      - name: Harden Runner\n        uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df \n        with:\n          egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs\n\n      - name: Set up git env\n        run: |\n          git config --global core.autocrlf false\n          $gopath = (go env GOPATH)\n          echo \"GOPATH=$gopath\" >> $env:GITHUB_ENV\n\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd \n\n      - name: Format\n        run: |\n          go install golang.org/x/tools/cmd/goimports@latest\n          $goimp = (Join-path -Path (go env GOPATH) -ChildPath \"\\bin\\goimports\")\n          $res = (&$goimp -l .) -replace \"$_\"      \n          if ($res -ne \"\") {\n            echo \"Unformatted source code:\" \n            echo $res\n            exit 1\n          }\n\n      - name: Vet\n        run: |\n          go vet -tags WINDOWS ./...\n\n      - name: Test\n        run: |\n          go test -tags WINDOWS ./...\n\n      - uses: dominikh/staticcheck-action@9716614d4101e79b4340dd97b10e54d68234e431 \n        with:\n          version: \"2025.1.1\"\n          install-go: false\n          cache-key: \"1.24.x\"\n          build-tags: WINDOWS\n\n      - name: Build\n        env:\n          GOPATH: ${{env.GOPATH}}\n        run: |\n          cmake -B build\n          cmake --build build\n"
  },
  {
    "path": ".github/workflows/ci-build.yaml",
    "content": "# Copyright Contributors to the L3AF Project.\n# SPDX-License-Identifier: Apache-2.0\n#\n# For documentation on the github environment, see\n# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners\n#\n# For documentation on the syntax of this file, see\n# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions\n\nname: CI Ubuntu build\non:\n  pull_request: {}\n  push:\n    branches:\n      - main\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    strategy:\n      matrix:\n        os:\n          - ubuntu-24.04\n          - ubuntu-22.04\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: Setup Go 1.26.0\n        uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c\n        with:\n          go-version: '1.26.0'\n\n      - name: Harden Runner\n        uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df\n        with:\n          egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs\n\n      - name: Set up environment\n        run: |\n          sudo apt-get update\n          sudo apt-get remove -y containerd.io docker docker.io moby-engine moby-cli || true  # Remove any existing Docker-related packages\n          sudo apt-get install -y \\\n            apt-transport-https \\\n            ca-certificates \\\n            curl \\\n            software-properties-common\n          # docker src \n          curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg\n          echo \"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null\n          sudo apt-get update\n          sudo apt-get install -y docker-ce docker-ce-cli containerd.io\n          sudo apt-get install -y gcc libc-dev bash perl curl make\n\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd \n\n      - name: Format\n        run: |\n          go install golang.org/x/tools/cmd/goimports@latest\n          res=\"$(goimports -l .)\"\n          if [[ \"$(printf '%s' \"$res\")\" != '' ]]; then\n            echo \"Unformatted source code:\"\n            echo \"$res\"\n            exit 1\n          fi\n\n      - name: Vet\n        run: |\n          go vet ./...\n\n      - name: Test\n        run: |\n          go test ./...\n          go clean -modcache\n\n      - uses: dominikh/staticcheck-action@9716614d4101e79b4340dd97b10e54d68234e431\n        with:\n          version: \"2025.1.1\"\n          install-go: false\n          cache-key: \"1.24.x\"\n\n      - name: Build\n        run: |\n          make\n\n      - name: Copy files\n        if: github.ref == 'refs/heads/main'\n        run: |\n          sudo cp ./config/l3afd.cfg ./build-docker\n          sudo cp l3afd ./build-docker\n\n      - name: login to docker registry\n        if: github.ref == 'refs/heads/main'\n        uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121\n        with:\n          username: ${{secrets.DOCKER_USERNAME}}\n          password: ${{secrets.DOCKER_TOKEN}}\n\n      - name: build and push docker image to registry\n        if: github.ref == 'refs/heads/main'\n        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f\n        with:\n          context: ./build-docker\n          push: true\n          tags: linuxfoundationl3af/l3afd:latest\n\n      - name: upload l3afd binary  \n        if: github.ref == 'refs/heads/main'\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a\n        with:\n          name: l3afd-latest-linux-x86_64-${{ matrix.os }}\n          path: l3afd\n\n"
  },
  {
    "path": ".github/workflows/ci-e2e.yaml",
    "content": "# Copyright Contributors to the L3AF Project.\n# SPDX-License-Identifier: Apache-2.0\n#\n# For documentation on the github environment, see\n# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners\n#\n# For documentation on the syntax of this file, see\n# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions\n\nname: CI E2E build\non:\n  pull_request: {}\n  push:\n    branches:\n      - main\n\npermissions:\n  contents: read\n\njobs:\n  build:\n    strategy:\n      matrix:\n        os:\n          - ubuntu-24.04\n          - ubuntu-22.04\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: Update and firewall stop\n        run: |\n          sudo apt update\n          sudo systemctl stop ufw\n          sudo apt install -y iproute2\n          sudo apt install git curl hey\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd\n      \n      - name: Prep\n        run: |\n          sudo cp -r /home/runner/work/l3afd/l3afd /root\n          sudo git clone https://github.com/l3af-project/l3af-arch.git /root/l3af-arch\n          sudo bash /root/l3af-arch/dev_environment/e2e_test/prep_env.sh\n          sudo bash /root/l3af-arch/dev_environment/setup_linux_dev_env.sh --ci-build\n          hm=$(hostname)\n          sudo find /root/l3af-arch/dev_environment/e2e_test -type f -name \"*.json\" -exec sed -i \"s/l3af-test-host/$hm/g\" {} +\n          \n      - name: Run Tests\n        run: |\n         sudo bash /root/l3af-arch/dev_environment/e2e_test/test_suite.sh\n"
  },
  {
    "path": ".github/workflows/codeql.yaml",
    "content": "# Copyright Contributors to the L3AF Project.\n# SPDX-License-Identifier: Apache-2.0\n#\n# For documentation on the github environment, see\n# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners\n#\n# For documentation on the syntax of this file, see\n# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions\n\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\n# Declare default permissions as read only.\npermissions: read-all\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'go' ]\n\n    steps:\n    - name: Harden Runner\n      uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df\n      with:\n        egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs\n\n    - name: Checkout repository\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd\n\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225\n      with:\n        languages: ${{ matrix.language }}\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225\n"
  },
  {
    "path": ".github/workflows/scorecards-analysis.yml",
    "content": "# Copyright Contributors to the L3AF Project.\n# SPDX-License-Identifier: Apache-2.0\n#\n# For documentation on the github environment, see\n# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners\n#\n# For documentation on the syntax of this file, see\n# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions\n\nname: Scorecards\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\nconcurrency:\n  # Cancel any Scorecards workflow currently in progress for the same PR.\n  # Allow running concurrently with any other commits.\n  group: scorecards-${{ github.event.pull_request.number || github.sha }}\n  cancel-in-progress: true\n\n# Declare default permissions as read only.\npermissions: read-all\n\njobs:\n  analysis:\n    name: Scorecards analysis\n    runs-on: ubuntu-latest\n    permissions:\n      # Needed to upload the results to code-scanning dashboard.\n      security-events: write\n      id-token: write\n      actions: read\n      contents: read\n\n    steps:\n      - name: \"Checkout code\"\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd\n        with:\n          persist-credentials: false\n\n      - name: \"Run analysis\"\n        uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3\n        with:\n          results_file: results.sarif\n          results_format: sarif\n          # (Optional) \"write\" PAT token. Uncomment the `repo_token` line below if:\n          # - you want to enable the Branch-Protection check on a *public* repository, or\n          # - you are installing Scorecard on a *private* repository\n          # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.\n          # repo_token: ${{ secrets.SCORECARD_TOKEN }}\n\n          # Public repositories:\n          #   - Publish results to OpenSSF REST API for easy access by consumers\n          #   - Allows the repository to include the Scorecard badge.\n          #   - See https://github.com/ossf/scorecard-action#publishing-results.\n          # For private repositories:\n          #   - `publish_results` will always be set to `false`, regardless\n          #     of the value entered here.\n          publish_results: ${{ github.event_name != 'pull_request' }}\n\n\n      # Upload the results as artifacts (optional).\n      - name: \"Upload artifact\"\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a\n        with:\n          name: SARIF file\n          path: results.sarif\n          retention-days: 5\n\n      # Upload the results to GitHub's code scanning dashboard so it will be visible\n      # at https://github.com/l3af-project/l3afd/security/code-scanning.\n      - name: \"Upload to code-scanning\"\n        uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225\n        with:\n          sarif_file: results.sarif\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\nvendor/\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\nproject(l3afd)\n\nadd_custom_target(swagger ALL\n                  DEPENDS ${CMAKE_SOURCE_DIR}/docs/docs.go\n                          ${CMAKE_SOURCE_DIR}/docs/swagger.json\n                          ${CMAKE_SOURCE_DIR}/docs/swagger.yaml)\n\nadd_custom_command(OUTPUT $ENV{GOPATH}/bin/swag.exe\n                   COMMAND go install github.com/swaggo/swag/cmd/swag@latest\n                   COMMAND go get -u github.com/swaggo/http-swagger\n                   COMMAND go get -u github.com/alecthomas/template)\n\nadd_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/docs/docs.go\n                          ${CMAKE_SOURCE_DIR}/docs/swagger.json\n                          ${CMAKE_SOURCE_DIR}/docs/swagger.yaml\n                   DEPENDS ${CMAKE_SOURCE_DIR}/apis/configwatch.go\n                           $ENV{GOPATH}/bin/swag.exe\n                   WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n                   COMMAND \"$ENV{GOPATH}/bin/swag.exe\" init -d \"./\" -g \"apis/configwatch.go\")\n\nadd_custom_target(build ALL\n                  DEPENDS ${CMAKE_SOURCE_DIR}/l3afd.exe)\n\nif (${WIN32})\n  add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/l3afd.exe\n                     DEPENDS ${CMAKE_SOURCE_DIR}/main.go\n                     WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n                     COMMAND go build -tags WINDOWS .)\nelse ()\n  add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/l3afd.exe\n                     DEPENDS ${CMAKE_SOURCE_DIR}/main.go\n                     WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n                     COMMAND go build .)\nendif ()\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "# Default Code Owners\n\n* @sanfern @charleskbliu0 @jniesz @dalalkaran @pmoroney\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: all\n\nexport GOPATH := $(HOME)/go\nall: swagger build\n\nswagger:\n\t@mkdir $(GOPATH) || true \n\t@go install github.com/swaggo/swag/cmd/swag@latest\n\t@$(GOPATH)/bin/swag init -d \"./\" -g \"apis/configwatch.go\"\n\nbuild:\n\t@CGO_ENABLED=0 go build -ldflags \\\n\t\t\"-X main.Version=v2.1.0 \\\n\t\t -X main.VersionSHA=`git rev-parse HEAD`\"\ninstall: swagger\n\t@go mod tidy\n\t@CGO_ENABLED=0 go install -ldflags \\\n\t\t\"-X main.Version=v2.1.0 \\\n\t\t -X main.VersionSHA=`git rev-parse HEAD`\"\ncibuild: swagger\n\t@go mod tidy\n\t@CGO_ENABLED=0 go install -cover -ldflags \\\n\t\t\"-X main.Version=v2.1.0 \\\n\t\t -X main.VersionSHA=`git rev-parse HEAD`\"\n"
  },
  {
    "path": "README.md",
    "content": "# L3AFD: Lightweight eBPF Daemon\n![L3AF_Logo](https://github.com/l3af-project/l3af-arch/blob/main/images/logos/Color/L3AF_logo.svg)\n\n[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/6075/badge)](https://bestpractices.coreinfrastructure.org/projects/6075)\n[![Go Report Card](https://goreportcard.com/badge/github.com/l3af-project/l3afd)](https://goreportcard.com/report/github.com/l3af-project/l3afd)\n[![GoDoc](https://godoc.org/github.com/l3af-project/l3afd?status.svg)](https://pkg.go.dev/github.com/l3af-project/l3afd)\n[![Apache licensed](https://img.shields.io/badge/license-Apache-blue.svg)](LICENSE)\n[![L3AF Slack](https://img.shields.io/badge/slack-L3AF-brightgreen.svg?logo=slack)](http://l3afworkspace.slack.com/)\n\nL3AFD is a crucial part of the L3AF ecosystem. For more information on L3AF see\nhttps://l3af.io/\n\n# Overview\nL3AFD is the primary component of the L3AF control plane. L3AFD is a daemon\nthat orchestrates and manages multiple eBPF programs. L3AFD runs on each node\nwhere the user wishes to run eBPF programs. L3AFD reads configuration data and\nmanages the execution and monitoring of eBPF programs running on the node.\n\nL3AFD downloads pre-built eBPF programs from a user-configured repository.\nHowever, we envision the creation of a community-driven eBPF package marketplace\nwhere L3AF users can obtain a variety of eBPF programs developed by multiple\nsources.\n\n![L3AF Platform](https://github.com/l3af-project/l3af-arch/blob/main/images/L3AF_platform.png)\n\n# Try it out\nSee our [L3AF Development Environment](https://github.com/l3af-project/l3af-arch/tree/main/dev_environment)\nfor a quick and easy way to try out L3AF on your local machine.\n\n# Installing\nTry [a binary release](https://github.com/l3af-project/l3afd/releases/latest).\n\n# Building\nTo build on your local machine, including swagger docs do the following.\n\nFor Linux:\n```\nmake\n```\n\nFor Windows:\n```\ncmake -B build\ncmake --build build\n```\n# Docker build\n- L3AFD binary & configuration that is required in the Docker image needs to be built locally and copied to build-docker directory\n- Execute below command to build the docker image\n```\ndocker build -t l3afd:<version> -f Dockerfile .\n```\nRequirements to run L3AFD as a Container\n- BPF, debugfs & shared-memory filesystems mount points should be available in the container\n- L3AFD container needs privileged access as it needs to manage eBPF programs\n- eBPF programs should be attached to the host interface so that it will apply to all the containers in the host\n\nIn order to satisfy the above requirements L3afd docker container needs to be run using the below command\n```\ndocker run -d -v /sys/fs/bpf:/sys/fs/bpf -v /sys/kernel/debug/:/sys/kernel/debug/ -v /dev/shm:/dev/shm --privileged --net=host l3afd:<version>\n```\n# Testing\nTo test on your local machine, do the following.\n\nFor Linux:\n```\ngo test ./...\n```\n\nFor Windows:\n```\ngo test -tags WINDOWS ./...\n```\n\n# Generate Swagger Docs\nSee our [Swaggo setup](docs/swagger.md)\n\n# Contributing\nContributing to L3afd is fun. To get started:\n- [Contributing guide](docs/CONTRIBUTING.md)\n"
  },
  {
    "path": "apis/configwatch.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !configs\n// +build !configs\n\npackage apis\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/signal\"\n\t\"path\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/utf8\"\n\n\thttpSwagger \"github.com/swaggo/http-swagger\"\n\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n\t\"github.com/l3af-project/l3afd/v2/config\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/l3af-project/l3afd/v2/routes\"\n\t\"github.com/l3af-project/l3afd/v2/signals\"\n\n\t_ \"github.com/l3af-project/l3afd/v2/docs\"\n\n\t\"github.com/rs/zerolog/log\"\n)\n\ntype Server struct {\n\tBPFRTConfigs  *bpfprogs.NFConfigs\n\tHostName      string\n\tl3afdServer   *http.Server\n\tCaCertPool    *x509.CertPool\n\tSANMatchRules []string\n}\n\n// @title L3AFD APIs\n// @version 1.0\n// @description Configuration APIs to deploy and get the details of the eBPF Programs on the node\n// @host\n// @BasePath /\nfunc StartConfigWatcher(ctx context.Context, hostname, daemonName string, conf *config.Config, bpfrtconfg *bpfprogs.NFConfigs) error {\n\tlog.Info().Msgf(\"%s config server setup started on host %s\", daemonName, hostname)\n\n\ts := &Server{\n\t\tBPFRTConfigs: bpfrtconfg,\n\t\tHostName:     hostname,\n\t\tl3afdServer: &http.Server{\n\t\t\tAddr: conf.L3afConfigsRestAPIAddr,\n\t\t},\n\t\tSANMatchRules: conf.MTLSSANMatchRules,\n\t}\n\tif _, ok := models.AllNetListeners.Load(\"main_http\"); !ok {\n\t\ttcpAddr, err := net.ResolveTCPAddr(\"tcp\", conf.L3afConfigsRestAPIAddr)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error resolving TCP address:%w\", err)\n\t\t}\n\t\tlistener, err := net.ListenTCP(\"tcp\", tcpAddr)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"creating tcp listner failed with %w\", err)\n\t\t}\n\t\tmodels.AllNetListeners.Store(\"main_http\", listener)\n\t}\n\tterm := make(chan os.Signal, 1)\n\tsignal.Notify(term, signals.ShutdownSignals...)\n\tgo func() {\n\t\t<-term\n\t\ts.GracefulStop(conf.ShutdownTimeout)\n\t\tctx.Done()\n\t\tlog.Info().Msg(\"L3afd gracefulStop completed\")\n\t}()\n\n\tgo func() {\n\t\tr := routes.NewRouter(apiRoutes(ctx, bpfrtconfg))\n\t\tif conf.SwaggerApiEnabled {\n\t\t\tr.Mount(\"/swagger\", httpSwagger.WrapHandler)\n\t\t}\n\t\ts.l3afdServer.Handler = r\n\n\t\t// As per design discussion when mTLS flag is not set and not listening on loopback or localhost\n\t\tif !conf.MTLSEnabled && !isLoopback(conf.L3afConfigsRestAPIAddr) && conf.Environment == config.ENV_PROD {\n\t\t\tconf.MTLSEnabled = true\n\t\t}\n\t\tval, _ := models.AllNetListeners.Load(\"main_http\")\n\t\tl, _ := val.(*net.TCPListener)\n\t\tif conf.MTLSEnabled {\n\t\t\tlog.Info().Msgf(\"l3afd server listening with mTLS - %s \", conf.L3afConfigsRestAPIAddr)\n\t\t\t// Create a CA certificate pool and add client ca's to it\n\t\t\tcaCert, err := os.ReadFile(path.Join(conf.MTLSCertDir, conf.MTLSCACertFilename))\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal().Err(err).Msgf(\"client CA %s file not found\", conf.MTLSCACertFilename)\n\t\t\t}\n\n\t\t\ts.CaCertPool, _ = x509.SystemCertPool()\n\t\t\tif s.CaCertPool == nil {\n\t\t\t\ts.CaCertPool = x509.NewCertPool()\n\t\t\t}\n\t\t\tif ok := s.CaCertPool.AppendCertsFromPEM(caCert); !ok {\n\t\t\t\tlog.Warn().Msgf(\"No client certs appended for mTLS\")\n\t\t\t}\n\t\t\tserverCertFile := path.Join(conf.MTLSCertDir, conf.MTLSServerCertFilename)\n\t\t\tserverKeyFile := path.Join(conf.MTLSCertDir, conf.MTLSServerKeyFilename)\n\t\t\tserverCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal().Err(err).Msgf(\"failure loading certs\")\n\t\t\t}\n\t\t\t// build server config\n\t\t\ts.l3afdServer.TLSConfig = &tls.Config{\n\t\t\t\tCertificates: []tls.Certificate{serverCert},\n\t\t\t\tGetConfigForClient: func(hi *tls.ClientHelloInfo) (*tls.Config, error) {\n\t\t\t\t\tserverConf := &tls.Config{\n\t\t\t\t\t\tCertificates:          []tls.Certificate{serverCert},\n\t\t\t\t\t\tMinVersion:            tls.VersionTLS12,\n\t\t\t\t\t\tClientAuth:            tls.RequireAndVerifyClientCert,\n\t\t\t\t\t\tClientCAs:             s.CaCertPool,\n\t\t\t\t\t\tVerifyPeerCertificate: s.getClientValidator(hi),\n\t\t\t\t\t}\n\t\t\t\t\treturn serverConf, nil\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tcpb, _ := pem.Decode(caCert)\n\t\t\tcert, err := x509.ParseCertificate(cpb.Bytes)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal().Err(err).Msgf(\"error in parsing tls certificate : %v\", conf.MTLSCACertFilename)\n\t\t\t}\n\t\t\texpiry := cert.NotAfter\n\t\t\tstart := cert.NotBefore\n\t\t\tgo func() {\n\t\t\t\tperiod := time.Hour * 24\n\t\t\t\tticker := time.NewTicker(period)\n\t\t\t\tdefer ticker.Stop()\n\t\t\t\tfor {\n\t\t\t\t\tselect {\n\t\t\t\t\tcase <-ticker.C:\n\t\t\t\t\t\tMonitorTLS(start, expiry, conf)\n\t\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\t\t\tif err := s.l3afdServer.ServeTLS(l, serverCertFile, serverKeyFile); !errors.Is(err, http.ErrServerClosed) {\n\t\t\t\tlog.Fatal().Err(err).Msgf(\"failed to start L3AFD server with mTLS enabled\")\n\t\t\t}\n\t\t} else {\n\t\t\tlog.Info().Msgf(\"l3afd server listening - %s \", conf.L3afConfigsRestAPIAddr)\n\t\t\tif err := s.l3afdServer.Serve(l); !errors.Is(err, http.ErrServerClosed) {\n\t\t\t\tlog.Fatal().Err(err).Msgf(\"failed to start L3AFD server\")\n\t\t\t}\n\t\t}\n\t}()\n\treturn nil\n}\n\nfunc (s *Server) GracefulStop(shutdownTimeout time.Duration) error {\n\tlog.Info().Msg(\"L3afd graceful stop initiated\")\n\texitCode := 0\n\tif len(s.BPFRTConfigs.IngressXDPBpfs) > 0 || len(s.BPFRTConfigs.IngressTCBpfs) > 0 || len(s.BPFRTConfigs.EgressTCBpfs) > 0 || s.BPFRTConfigs.ProbesBpfs.Len() > 0 {\n\t\tctx, cancelfunc := context.WithTimeout(context.Background(), shutdownTimeout)\n\t\tdefer cancelfunc()\n\t\tif err := s.BPFRTConfigs.Close(ctx); err != nil {\n\t\t\tlog.Error().Err(err).Msg(\"stopping all network functions failed\")\n\t\t\texitCode = 1\n\t\t}\n\t}\n\tos.Exit(exitCode)\n\treturn nil\n}\n\n// isLoopback - Check for localhost or loopback address\nfunc isLoopback(addr string) bool {\n\n\tif strings.Contains(addr, \"localhost:\") {\n\t\treturn true\n\t}\n\tif id := strings.LastIndex(addr, \":\"); id > -1 {\n\t\taddr = addr[:id]\n\t}\n\tif ipAddr := net.ParseIP(addr); ipAddr != nil {\n\t\treturn ipAddr.IsLoopback()\n\t}\n\t// :port scenario\n\treturn true\n}\n\nfunc MonitorTLS(start time.Time, expiry time.Time, conf *config.Config) {\n\ttodayDate := time.Now()\n\texpiryDate := expiry\n\tstartDate := start\n\tdiff := expiryDate.Sub(todayDate)\n\tremainingHoursToStart := todayDate.Sub(startDate)\n\tlimit := conf.MTLSCertExpiryWarningDays * 24\n\tremainingHoursToExpire := int(diff.Hours())\n\tif remainingHoursToStart > 0 {\n\t\tlog.Fatal().Msgf(\"tls certificate start from : %v\", startDate)\n\t}\n\tif remainingHoursToExpire <= limit {\n\t\tif remainingHoursToExpire < 0 {\n\t\t\tlog.Fatal().Msgf(\"tls certificate is expired on : %v\", expiryDate)\n\t\t} else {\n\t\t\tlog.Warn().Msgf(\"tls certificate will expire in %v days\", int64(remainingHoursToExpire/24))\n\t\t}\n\t}\n}\n\nfunc (s *Server) getClientValidator(helloInfo *tls.ClientHelloInfo) func([][]byte, [][]*x509.Certificate) error {\n\n\tlog.Debug().Msgf(\"Inside get client validator - %v\", helloInfo.Conn.RemoteAddr())\n\treturn func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {\n\t\t// Verifying client certs with root ca\n\t\topts := x509.VerifyOptions{\n\t\t\tRoots:         s.CaCertPool,\n\t\t\tCurrentTime:   time.Now(),\n\t\t\tIntermediates: x509.NewCertPool(),\n\t\t\tKeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},\n\t\t}\n\t\t_, err := verifiedChains[0][0].Verify(opts)\n\t\tif err != nil {\n\t\t\tlog.Error().Err(err).Msgf(\"certs verification failed\")\n\t\t\treturn err\n\t\t}\n\n\t\tlog.Debug().Msgf(\"validating with SAN match rules - %s\", s.SANMatchRules)\n\t\tif len(s.SANMatchRules) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\tfor _, dnsName := range verifiedChains[0][0].DNSNames {\n\t\t\tif !validHostname(dnsName, true) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tdnsName = toLowerCaseASCII(dnsName)\n\t\t\tfor _, sanMatchRule := range s.SANMatchRules {\n\t\t\t\tsanMatchRule = toLowerCaseASCII(sanMatchRule)\n\t\t\t\tif matchExactly(dnsName, sanMatchRule) {\n\t\t\t\t\tlog.Debug().Msgf(\"Successfully matched matchExactly cert dns %s SANMatchRule %s\", dnsName, sanMatchRule)\n\t\t\t\t\treturn nil\n\t\t\t\t} else if matchHostnamesWithRegexp(dnsName, sanMatchRule) {\n\t\t\t\t\tlog.Debug().Msgf(\"Successfully matched matchHostnamesWithRegexp cert dns %s SANMatchRule %s\", dnsName, sanMatchRule)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\terr = errors.New(\"certs verification with SAN match not found\")\n\t\tlog.Error().Err(err).Msgf(\"SAN match rules %s\", s.SANMatchRules)\n\t\treturn err\n\t}\n}\n\n// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use\n// an explicitly ASCII function to avoid any sharp corners resulting from\n// performing Unicode operations on DNS labels.\nfunc toLowerCaseASCII(in string) string {\n\t// If the string is already lower-case then there's nothing to do.\n\tisAlreadyLowerCase := true\n\tfor _, c := range in {\n\t\tif c == utf8.RuneError {\n\t\t\t// If we get a UTF-8 error then there might be\n\t\t\t// upper-case ASCII bytes in the invalid sequence.\n\t\t\tisAlreadyLowerCase = false\n\t\t\tbreak\n\t\t}\n\t\tif 'A' <= c && c <= 'Z' {\n\t\t\tisAlreadyLowerCase = false\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif isAlreadyLowerCase {\n\t\treturn in\n\t}\n\n\tout := []byte(in)\n\tfor i, c := range out {\n\t\tif 'A' <= c && c <= 'Z' {\n\t\t\tout[i] += 'a' - 'A'\n\t\t}\n\t}\n\treturn string(out)\n}\n\n// validHostname reports whether host is a valid hostname that can be matched or\n// matched against according to RFC 6125 2.2, with some leniency to accommodate\n// legacy values.\nfunc validHostname(host string, isPattern bool) bool {\n\tif !isPattern {\n\t\thost = strings.TrimSuffix(host, \".\")\n\t}\n\tif len(host) == 0 {\n\t\treturn false\n\t}\n\n\tfor i, part := range strings.Split(host, \".\") {\n\t\tif part == \"\" {\n\t\t\t// Empty label.\n\t\t\treturn false\n\t\t}\n\t\tif isPattern && i == 0 && part == \"*\" {\n\t\t\t// Only allow full left-most wildcards, as those are the only ones\n\t\t\t// we match, and matching literal '*' characters is probably never\n\t\t\t// the expected behavior.\n\t\t\tcontinue\n\t\t}\n\t\tfor j, c := range part {\n\t\t\tif 'a' <= c && c <= 'z' {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif '0' <= c && c <= '9' {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif 'A' <= c && c <= 'Z' {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif c == '-' && j != 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif c == '_' {\n\t\t\t\t// Not a valid character in hostnames, but commonly\n\t\t\t\t// found in deployments outside the WebPKI.\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\n// matchExactly - match hostnames\nfunc matchExactly(hostA, hostB string) bool {\n\t// Here checking hostB (i.e. sanMatchRule) is valid hostname and not regex/pattern\n\tif !validHostname(hostB, false) {\n\t\treturn false\n\t}\n\treturn hostA == hostB\n}\n\n// matchHostnamesWithRegexp - To match the san rules with regexp\nfunc matchHostnamesWithRegexp(dnsName, sanMatchRule string) bool {\n\tdefer func() bool {\n\t\tif err := recover(); err != nil {\n\t\t\tlog.Warn().Msgf(\"panic occurred: %v\", err)\n\t\t}\n\t\treturn false\n\t}()\n\tif len(dnsName) == 0 || len(sanMatchRule) == 0 {\n\t\treturn false\n\t}\n\tre := regexp.MustCompile(sanMatchRule)\n\n\treturn re.MatchString(dnsName)\n}\n"
  },
  {
    "path": "apis/configwatch_test.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage apis\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestMatchHostnamesWithRegexp(t *testing.T) {\n\ttype args struct {\n\t\tdnsName      string\n\t\tsanMatchRule string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    bool\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"EmptyCheck\",\n\t\t\targs:    args{dnsName: \"\", sanMatchRule: \"\"},\n\t\t\twant:    false,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"LengthMissMatchCheck\",\n\t\t\targs:    args{dnsName: \"l3afd-lfn.us.l3af.io\", sanMatchRule: \"l3afd-lfn.l3af.io\"},\n\t\t\twant:    false,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"LengthMatchCheck\",\n\t\t\targs:    args{dnsName: \"l3afd-lfn.l3af.io\", sanMatchRule: \"l3afd-lfn.l3af.io\"},\n\t\t\twant:    true,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"LengthMatchPatternMissCheck\",\n\t\t\targs:    args{dnsName: \"l3afd-us.l3af.io\", sanMatchRule: \"l3afd-lf.l3af.io\"},\n\t\t\twant:    false,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"PatternMatchCheck\",\n\t\t\targs:    args{dnsName: \"l3afd-*.l3af.io\", sanMatchRule: \"l3afd-lfn.l3af.io\"},\n\t\t\twant:    false,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"PatternMissMatchCheck\",\n\t\t\targs:    args{dnsName: \"*l3afd-.l3af.io\", sanMatchRule: \"l3afd-lfn.l3af.io\"},\n\t\t\twant:    false,\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname:    \"PatternRegExMatchCheck\",\n\t\t\targs:    args{dnsName: \"asnl3afd-lfn.l3af.io\", sanMatchRule: \".*l3afd-lfn.l3af.io\"},\n\t\t\twant:    true,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"PatternRegExExactMatchCheck\",\n\t\t\targs:    args{dnsName: \"l3afd-dev.l3af.io\", sanMatchRule: \"^dev.l3af.io$\"},\n\t\t\twant:    false,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"PatternRegExFindMatch\",\n\t\t\targs:    args{dnsName: \"l3afd-dev.l3af.io\", sanMatchRule: \"dev.l3af.io\"},\n\t\t\twant:    true,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"PatternRegExFindMatchPattern\",\n\t\t\targs:    args{dnsName: \"l3afd-dev-10.l3af.io\", sanMatchRule: \"^l3afd-dev-[0-9][0-9]\\\\.l3af\\\\.io$\"},\n\t\t\twant:    true,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"PatternRegExLowerCaseMatch\",\n\t\t\targs:    args{dnsName: \"l3afd-dev-a0.l3af.io\", sanMatchRule: \"^l3afd-dev-[a-z][0-9]\\\\.l3af\\\\.io$\"},\n\t\t\twant:    true,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"PatternRegExUpperCaseMatch\",\n\t\t\targs:    args{dnsName: \"l3afd-dev-A0.l3af.io\", sanMatchRule: \"^l3afd-dev-[A-Z][0-9]\\\\.l3af\\\\.io$\"},\n\t\t\twant:    true,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"PatternRegExPanicCheck\",\n\t\t\targs:    args{dnsName: \"l3afd-dev-A0.l3af.io\", sanMatchRule: \"*l3afd-dev.l3af.io\"},\n\t\t\twant:    false,\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := matchHostnamesWithRegexp(tt.args.dnsName, tt.args.sanMatchRule)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"matchHostnamesWithRegexp() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMatchExactly(t *testing.T) {\n\ttype args struct {\n\t\thostA string\n\t\thostB string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    bool\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"EmptyCheck\",\n\t\t\targs:    args{hostA: \"\", hostB: \"\"},\n\t\t\twant:    false,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"ExactMatchCheck\",\n\t\t\targs:    args{hostA: \"l3afd-lfn.l3af.io\", hostB: \"l3afd-lfn.l3af.io\"},\n\t\t\twant:    true,\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := matchExactly(tt.args.hostA, tt.args.hostB)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"matchHostnames() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestToLowerCaseASCII(t *testing.T) {\n\ttype args struct {\n\t\tin string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    string\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"EmptyCheck\",\n\t\t\targs:    args{in: \"\"},\n\t\t\twant:    \"\",\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"RegexLowerCheck\",\n\t\t\targs:    args{in: \"^l3afd-dev-[0-9][0-9]\\\\.l3af\\\\.io$\"},\n\t\t\twant:    \"^l3afd-dev-[0-9][0-9]\\\\.l3af\\\\.io$\",\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"RegexUpperValueCheck\",\n\t\t\targs:    args{in: \"^L3AFd-dev-[0-9][0-9]\\\\.l3af\\\\.io$\"},\n\t\t\twant:    \"^l3afd-dev-[0-9][0-9]\\\\.l3af\\\\.io$\",\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"RegexLowerCheckRuneError\",\n\t\t\targs:    args{in: \"^�l3afd-dev-[0-9][0-9]\\\\.l3af\\\\.io$\"},\n\t\t\twant:    \"^�l3afd-dev-[0-9][0-9]\\\\.l3af\\\\.io$\",\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := toLowerCaseASCII(tt.args.in)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"matchHostnames() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "apis/handlers/addprog.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage handlers\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"net/http\"\n\n\t\"github.com/rs/zerolog/log\"\n\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n)\n\n// AddEbpfPrograms add new eBPF programs on node\n// @Summary Adds new eBPF Programs on node\n// @Description Adds new eBPF Programs on node\n// @Accept  json\n// @Produce  json\n// @Param cfgs body []models.L3afBPFPrograms true \"BPF programs\"\n// @Success 200\n// @Router /l3af/configs/v1/add [post]\nfunc AddEbpfPrograms(ctx context.Context, bpfcfg *bpfprogs.NFConfigs) http.HandlerFunc {\n\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tmesg := \"\"\n\t\tstatusCode := http.StatusOK\n\n\t\tw.Header().Add(\"Content-Type\", \"application/json\")\n\n\t\tdefer func(mesg *string, statusCode *int) {\n\t\t\tw.WriteHeader(*statusCode)\n\t\t\t_, err := w.Write([]byte(*mesg))\n\t\t\tif err != nil {\n\t\t\t\tlog.Warn().Msgf(\"Failed to write response bytes: %v\", err)\n\t\t\t}\n\t\t}(&mesg, &statusCode)\n\t\tif models.IsReadOnly {\n\t\t\tlog.Warn().Msgf(\"We are in between restart please try after some time\")\n\t\t\tmesg = \"We are currently in the middle of a restart. Please attempt again after a while.\"\n\t\t\treturn\n\t\t}\n\t\tdefer DecWriteReq()\n\t\tIncWriteReq()\n\t\tif r.Body == nil {\n\t\t\tlog.Warn().Msgf(\"Empty request body\")\n\t\t\treturn\n\t\t}\n\t\tbodyBuffer, err := io.ReadAll(r.Body)\n\t\tif err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed to read request body: %v\", err)\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\n\t\tvar t []models.L3afBPFPrograms\n\t\tif err := json.Unmarshal(bodyBuffer, &t); err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed to unmarshal payload: %v\", err)\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\t\tif err := bpfcfg.AddeBPFPrograms(t); err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed to AddEbpfPrograms : %v\", err)\n\t\t\tlog.Error().Msg(mesg)\n\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc IncWriteReq() {\n\tmodels.StateLock.Lock()\n\tmodels.CurrentWriteReq++\n\tmodels.StateLock.Unlock()\n}\nfunc DecWriteReq() {\n\tmodels.StateLock.Lock()\n\tmodels.CurrentWriteReq--\n\tmodels.StateLock.Unlock()\n}\n"
  },
  {
    "path": "apis/handlers/addprog_test.go",
    "content": "package handlers\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n\t\"github.com/l3af-project/l3afd/v2/config\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n)\n\nconst dummypayload string = `[\n\t{\n\t  \"host_name\" : \"l3af-local-test\",\n\t  \"iface\" : \"fakeif0\",\n\t  \"bpf_programs\" : {\n\t\t\"xdp_ingress\" : [\n\t\t],\n\t\t\"tc_egress\": [\n\t\t],\n\t\t\"tc_ingress\": [\n\t\t]\n\t  }\n\t}\n  ]\n  `\n\nfunc Test_addprog(t *testing.T) {\n\n\ttests := []struct {\n\t\tname       string\n\t\tBody       *strings.Reader\n\t\theader     map[string]string\n\t\tstatus     int\n\t\tcfg        *bpfprogs.NFConfigs\n\t\tisreadonly bool\n\t}{\n\t\t{\n\t\t\tname:       \"NilBody\",\n\t\t\tBody:       nil,\n\t\t\tstatus:     http.StatusOK,\n\t\t\tisreadonly: false,\n\t\t\tcfg: &bpfprogs.NFConfigs{\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"FailedToUnmarshal\",\n\t\t\tBody:       strings.NewReader(\"Something\"),\n\t\t\tstatus:     http.StatusInternalServerError,\n\t\t\theader:     map[string]string{},\n\t\t\tisreadonly: false,\n\t\t\tcfg: &bpfprogs.NFConfigs{\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"EmptyInput\",\n\t\t\tBody: strings.NewReader(\"[]\"),\n\t\t\theader: map[string]string{\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tisreadonly: false,\n\t\t\tcfg: &bpfprogs.NFConfigs{\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tstatus: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:   \"UnknownHostName\",\n\t\t\tBody:   strings.NewReader(dummypayload),\n\t\t\tstatus: http.StatusInternalServerError,\n\t\t\theader: map[string]string{},\n\t\t\tcfg: &bpfprogs.NFConfigs{\n\t\t\t\tHostName: \"dummy\",\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tisreadonly: false,\n\t\t},\n\t\t{\n\t\t\tname: \"InReadonly\",\n\t\t\tBody: nil,\n\t\t\theader: map[string]string{\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tisreadonly: true,\n\t\t\tcfg:        nil,\n\t\t\tstatus:     http.StatusOK,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tvar req *http.Request\n\t\tif tt.Body == nil {\n\t\t\treq, _ = http.NewRequest(\"POST\", \"/l3af/configs/v1/add\", nil)\n\t\t} else {\n\t\t\treq, _ = http.NewRequest(\"POST\", \"/l3af/configs/v1/add\", tt.Body)\n\t\t}\n\t\tfor key, val := range tt.header {\n\t\t\treq.Header.Set(key, val)\n\t\t}\n\t\tmodels.IsReadOnly = tt.isreadonly\n\t\trr := httptest.NewRecorder()\n\t\thandler := AddEbpfPrograms(context.Background(), tt.cfg)\n\t\thandler.ServeHTTP(rr, req)\n\t\tif rr.Code != tt.status {\n\t\t\tmodels.IsReadOnly = false\n\t\t\tt.Error(\"AddEbpfPrograms Failed\")\n\t\t}\n\t\tmodels.IsReadOnly = false\n\t}\n}\n"
  },
  {
    "path": "apis/handlers/deleteprog.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage handlers\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"net/http\"\n\n\t\"github.com/rs/zerolog/log\"\n\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n)\n\n// DeleteEbpfPrograms   remove eBPF programs on node\n// @Summary Removes eBPF Programs on node\n// @Description Removes eBPF Programs on node\n// @Accept  json\n// @Produce  json\n// @Param cfgs body []models.L3afBPFProgramNames true \"BPF program names\"\n// @Success 200\n// @Router /l3af/configs/v1/delete [post]\nfunc DeleteEbpfPrograms(ctx context.Context, bpfcfg *bpfprogs.NFConfigs) http.HandlerFunc {\n\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tmesg := \"\"\n\t\tstatusCode := http.StatusOK\n\n\t\tw.Header().Add(\"Content-Type\", \"application/json\")\n\n\t\tdefer func(mesg *string, statusCode *int) {\n\t\t\tw.WriteHeader(*statusCode)\n\t\t\t_, err := w.Write([]byte(*mesg))\n\t\t\tif err != nil {\n\t\t\t\tlog.Warn().Msgf(\"Failed to write response bytes: %v\", err)\n\t\t\t}\n\t\t}(&mesg, &statusCode)\n\t\tif models.IsReadOnly {\n\t\t\tlog.Warn().Msgf(\"We are in between restart please try after some time\")\n\t\t\tmesg = \"We are currently in the middle of a restart. Please attempt again after a while.\"\n\t\t\treturn\n\t\t}\n\t\tdefer DecWriteReq()\n\t\tIncWriteReq()\n\t\tif r.Body == nil {\n\t\t\tlog.Warn().Msgf(\"Empty request body\")\n\t\t\treturn\n\t\t}\n\t\tbodyBuffer, err := io.ReadAll(r.Body)\n\t\tif err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed to read request body: %v\", err)\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\n\t\tvar t []models.L3afBPFProgramNames\n\t\tif err := json.Unmarshal(bodyBuffer, &t); err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed to unmarshal payload: %v\", err)\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\n\t\tif err := bpfcfg.DeleteEbpfPrograms(t); err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed to DeleteEbpfPrograms : %v\", err)\n\t\t\tlog.Error().Msg(mesg)\n\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "apis/handlers/deleteprog_test.go",
    "content": "package handlers\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n\t\"github.com/l3af-project/l3afd/v2/config\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n)\n\nconst payloadfordelete string = `[\n    {\n        \"host_name\": \"l3af-local-test\",\n        \"iface\": \"fakeif0\",\n        \"bpf_programs\": {\n            \"xdp_ingress\": [\n                \"ratelimiting\",\n                \"connection-limit\"\n            ]\n        }\n    }\n]\n`\n\nfunc Test_DeleteEbpfPrograms(t *testing.T) {\n\n\ttests := []struct {\n\t\tname       string\n\t\tBody       *strings.Reader\n\t\theader     map[string]string\n\t\tstatus     int\n\t\tcfg        *bpfprogs.NFConfigs\n\t\tisreadonly bool\n\t}{\n\t\t{\n\t\t\tname:       \"NilBody\",\n\t\t\tBody:       nil,\n\t\t\tstatus:     http.StatusOK,\n\t\t\tisreadonly: false,\n\t\t\tcfg: &bpfprogs.NFConfigs{\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"FailedToUnmarshal\",\n\t\t\tBody:       strings.NewReader(\"Something\"),\n\t\t\tstatus:     http.StatusInternalServerError,\n\t\t\theader:     map[string]string{},\n\t\t\tisreadonly: false,\n\t\t\tcfg: &bpfprogs.NFConfigs{\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"EmptyInput\",\n\t\t\tBody: strings.NewReader(`[]`),\n\t\t\theader: map[string]string{\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tisreadonly: false,\n\t\t\tcfg: &bpfprogs.NFConfigs{\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tstatus: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:       \"UnknownHostName\",\n\t\t\tBody:       strings.NewReader(payloadfordelete),\n\t\t\tstatus:     http.StatusInternalServerError,\n\t\t\theader:     map[string]string{},\n\t\t\tisreadonly: false,\n\t\t\tcfg: &bpfprogs.NFConfigs{\n\t\t\t\tHostName: \"dummy\",\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:   \"InReadonly\",\n\t\t\tBody:   nil,\n\t\t\tstatus: http.StatusOK,\n\t\t\theader: map[string]string{\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tisreadonly: true,\n\t\t\tcfg:        nil,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tvar req *http.Request\n\t\tif tt.Body == nil {\n\t\t\treq, _ = http.NewRequest(\"POST\", \"/l3af/configs/v1/delete\", nil)\n\t\t} else {\n\t\t\treq, _ = http.NewRequest(\"POST\", \"/l3af/configs/v1/delete\", tt.Body)\n\t\t}\n\t\tfor key, val := range tt.header {\n\t\t\treq.Header.Set(key, val)\n\t\t}\n\t\tmodels.IsReadOnly = tt.isreadonly\n\t\trr := httptest.NewRecorder()\n\t\thandler := DeleteEbpfPrograms(context.Background(), tt.cfg)\n\t\thandler.ServeHTTP(rr, req)\n\t\tif rr.Code != tt.status {\n\t\t\tmodels.IsReadOnly = false\n\t\t\tt.Error(\"DeleteEbpfPrograms Failed\")\n\t\t}\n\t\tmodels.IsReadOnly = false\n\t}\n}\n"
  },
  {
    "path": "apis/handlers/getconfig.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage handlers\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\n\tchi \"github.com/go-chi/chi/v5\"\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n\t\"github.com/rs/zerolog/log\"\n)\n\nvar bpfcfgs *bpfprogs.NFConfigs\n\nfunc InitConfigs(cfgs *bpfprogs.NFConfigs) error {\n\tbpfcfgs = cfgs\n\treturn nil\n}\n\n// GetConfig Returns details of the configuration of eBPF Programs for a given interface\n// @Summary Returns details of the configuration of eBPF Programs for a given interface\n// @Description Returns details of the configuration of eBPF Programs for a given interface\n// @Accept  json\n// @Produce  json\n// @Param iface path string true \"interface name\"\n// @Success 200\n// @Router /l3af/configs/v1/{iface} [get]\nfunc GetConfig(w http.ResponseWriter, r *http.Request) {\n\tmesg := \"\"\n\tstatusCode := http.StatusOK\n\n\tw.Header().Add(\"Content-Type\", \"application/json\")\n\n\tdefer func(mesg *string, statusCode *int) {\n\t\tw.WriteHeader(*statusCode)\n\t\t_, err := w.Write([]byte(*mesg))\n\t\tif err != nil {\n\t\t\tlog.Warn().Msgf(\"Failed to write response bytes: %v\", err)\n\t\t}\n\t}(&mesg, &statusCode)\n\n\tiface := chi.URLParam(r, \"iface\")\n\tif len(iface) == 0 {\n\t\tmesg = \"iface value is empty\"\n\t\tlog.Error().Msg(mesg)\n\t\tstatusCode = http.StatusBadRequest\n\t\treturn\n\t}\n\n\tresp, err := json.MarshalIndent(bpfcfgs.EBPFPrograms(iface), \"\", \"  \")\n\tif err != nil {\n\t\tmesg = \"internal server error\"\n\t\tlog.Error().Msgf(\"failed to marshal response: %v\", err)\n\t\tstatusCode = http.StatusInternalServerError\n\t\treturn\n\t}\n\tmesg = string(resp)\n}\n\n// GetConfigAll Returns details of the configuration of eBPF Programs for all interfaces on a node\n// @Summary Returns details of the configuration of eBPF Programs for all interfaces on a node\n// @Description Returns details of the configuration of eBPF Programs for all interfaces on a node\n// @Accept  json\n// @Produce  json\n// @Success 200\n// @Router /l3af/configs/v1 [get]\nfunc GetConfigAll(w http.ResponseWriter, r *http.Request) {\n\tmesg := \"\"\n\tstatusCode := http.StatusOK\n\n\tw.Header().Add(\"Content-Type\", \"application/json\")\n\n\tdefer func(mesg *string, statusCode *int) {\n\t\tw.WriteHeader(*statusCode)\n\t\t_, err := w.Write([]byte(*mesg))\n\t\tif err != nil {\n\t\t\tlog.Warn().Msgf(\"Failed to write response bytes: %v\", err)\n\t\t}\n\t}(&mesg, &statusCode)\n\n\tresp, err := json.MarshalIndent(bpfcfgs.EBPFProgramsAll(), \"\", \"  \")\n\tif err != nil {\n\t\tmesg = \"internal server error\"\n\t\tlog.Error().Msgf(\"failed to marshal response: %v\", err)\n\t\tstatusCode = http.StatusInternalServerError\n\t\treturn\n\t}\n\tmesg = string(resp)\n}\n"
  },
  {
    "path": "apis/handlers/getconfig_test.go",
    "content": "package handlers\n\nimport (\n\t\"container/list\"\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\tchi \"github.com/go-chi/chi/v5\"\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n)\n\nfunc Test_GetConfig(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tiface  string\n\t\tstatus int\n\t\tcfg    *bpfprogs.NFConfigs\n\t}{\n\t\t{\n\t\t\tname:   \"EmptyInterfaceInRequest\",\n\t\t\tiface:  \"\",\n\t\t\tstatus: http.StatusBadRequest,\n\t\t\tcfg:    &bpfprogs.NFConfigs{},\n\t\t},\n\t\t{\n\t\t\tname:   \"GoodInput\",\n\t\t\tiface:  \"fakeif0\",\n\t\t\tstatus: http.StatusOK,\n\t\t\tcfg: &bpfprogs.NFConfigs{\n\t\t\t\tIngressXDPBpfs: map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\tIngressTCBpfs:  map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\tEgressTCBpfs:   map[string]*list.List{\"fakeif0\": nil},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\treq, _ := http.NewRequest(\"GET\", \"l3af/configs/v1/\"+tt.iface, nil)\n\t\trctx := chi.NewRouteContext()\n\t\treq = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx))\n\t\trctx.URLParams.Add(\"iface\", tt.iface)\n\t\trr := httptest.NewRecorder()\n\t\thandler := http.HandlerFunc(GetConfig)\n\t\tInitConfigs(tt.cfg)\n\t\thandler.ServeHTTP(rr, req)\n\t\tif rr.Code != tt.status {\n\t\t\tt.Errorf(\"GetConfig Failed\")\n\t\t}\n\t}\n}\n\nfunc Test_GetConfigAll(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tstatus int\n\t\tcfg    *bpfprogs.NFConfigs\n\t}{\n\t\t{\n\t\t\tname:   \"GoodInput\",\n\t\t\tstatus: http.StatusOK,\n\t\t\tcfg:    &bpfprogs.NFConfigs{},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\treq, _ := http.NewRequest(\"GET\", \"l3af/configs/v1\", nil)\n\t\trr := httptest.NewRecorder()\n\t\thandler := http.HandlerFunc(GetConfigAll)\n\t\tInitConfigs(tt.cfg)\n\t\thandler.ServeHTTP(rr, req)\n\t\tif rr.Code != tt.status {\n\t\t\tt.Errorf(\"GetConfigAll Failed\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "apis/handlers/restart_linux.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage handlers\n\nimport (\n\t\"encoding/gob\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"net/http\"\n\n\t\"github.com/rs/zerolog/log\"\n\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/l3af-project/l3afd/v2/pidfile\"\n\t\"github.com/l3af-project/l3afd/v2/restart\"\n)\n\n// HandleRestart will start new instance of l3afd provided by payload\n// @Summary this api will start new instance of l3afd provided by payload\n// @Description this api will start new instance of l3afd provided by payload\n// @Accept  json\n// @Produce  json\n// @Param cfgs body []models.L3afBPFPrograms true \"BPF programs\"\n// @Success 200\n// @Router /l3af/configs/v1/restart [put]\nfunc HandleRestart(bpfcfg *bpfprogs.NFConfigs) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tmesg := \"\"\n\t\tstatusCode := http.StatusOK\n\t\tw.Header().Add(\"Content-Type\", \"application/json\")\n\t\tdefer func(mesg *string, statusCode *int) {\n\t\t\tw.WriteHeader(*statusCode)\n\t\t\t_, err := w.Write([]byte(*mesg))\n\t\t\tif err != nil {\n\t\t\t\tlog.Warn().Msgf(\"Failed to write response bytes: %v\", err)\n\t\t\t}\n\t\t}(&mesg, &statusCode)\n\t\tif models.IsReadOnly {\n\t\t\tlog.Warn().Msgf(\"We are in between restart please try after some time\")\n\t\t\tmesg = \"We are currently in the middle of a restart. Please attempt again after a while.\"\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\n\t\tif r.Body == nil {\n\t\t\tmesg = \"nil request body\"\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\tlog.Warn().Msgf(\"Empty request body\")\n\t\t\treturn\n\t\t}\n\t\tbodyBuffer, err := io.ReadAll(r.Body)\n\t\tif err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed to read request body: %v\", err)\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\n\t\tvar t models.RestartConfig\n\t\tif err := json.Unmarshal(bodyBuffer, &t); err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed to unmarshal payload: %v\", err)\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\n\t\tmachineHostname, err := os.Hostname()\n\t\tif err != nil {\n\t\t\tmesg = \"failed to get os hostname\"\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\t\tif machineHostname != t.HostName {\n\t\t\tmesg = \"this api request is not for provided host\"\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\n\t\tdefer func() {\n\t\t\tmodels.IsReadOnly = false\n\t\t}()\n\t\tmodels.IsReadOnly = true\n\n\t\t// complete active requests\n\t\tfor {\n\t\t\tmodels.StateLock.Lock()\n\t\t\tif models.CurrentWriteReq == 0 {\n\t\t\t\tmodels.StateLock.Unlock()\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tmodels.StateLock.Unlock()\n\t\t\ttime.Sleep(5 * time.Millisecond)\n\t\t}\n\t\t// Now our system is in Readonly state\n\n\t\toldCfgPath, err := restart.ReadSymlink(filepath.Join(bpfcfg.HostConfig.BasePath, \"latest/l3afd.cfg\"))\n\t\tif err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed read symlink %v with error: %v\", filepath.Join(bpfcfg.HostConfig.BasePath, \"latest/l3afd.cfg\"), err)\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\t\toldBinPath, err := restart.ReadSymlink(filepath.Join(bpfcfg.HostConfig.BasePath, \"latest/l3afd\"))\n\t\tif err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed read symlink %v with error: %v\", filepath.Join(bpfcfg.HostConfig.BasePath, \"latest/l3afd\"), err)\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\n\t\t// /usr/local/l3afd/v2.0.0/l3afd/l3afd --> v2.0.0/l3afd/l3afd --> v2.0.0\n\t\toldVersion := strings.Split(strings.Trim(oldBinPath, bpfcfg.HostConfig.BasePath+\"/\"), \"/\")[0]\n\t\tif _, ok := models.AvailableVersions[t.Version]; !ok {\n\t\t\tmesg = \"invalid version to upgrade\"\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\t\terr = restart.GetNewVersion(models.L3AFDRestartArtifactName, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)\n\t\tif err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed to get new version: %v\", err)\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\terr = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)\n\t\t\tmesg = mesg + fmt.Sprintf(\"rollback of symlinks failed: %v\", err)\n\t\t\treturn\n\t\t}\n\n\t\tbpfProgs := bpfcfg.GetL3AFHOSTDATA()\n\t\tln, err := net.Listen(\"unix\", models.HostSock)\n\t\tif err != nil {\n\t\t\tlog.Err(err)\n\t\t\terr = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)\n\t\t\tmesg = mesg + fmt.Sprintf(\"rollback of symlinks failed: %v\", err)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\t\tsrvError := make(chan error, 1)\n\t\tgo func() {\n\t\t\tdefer ln.Close()\n\t\t\tconn, err := ln.Accept()\n\t\t\tif err != nil {\n\t\t\t\tlog.Err(err)\n\t\t\t\tsrvError <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer conn.Close()\n\t\t\tencoder := gob.NewEncoder(conn)\n\t\t\terr = encoder.Encode(bpfProgs)\n\t\t\tif err != nil {\n\t\t\t\tlog.Err(err)\n\t\t\t\tsrvError <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsrvError <- nil\n\t\t}()\n\n\t\tfiles := make([]*os.File, 3)\n\t\tsrvToIndex := make(map[string]int)\n\t\tsrvToIndex[\"stat_http\"] = 0\n\t\tsrvToIndex[\"main_http\"] = 1\n\t\tsrvToIndex[\"debug_http\"] = 2\n\t\tisErr := false\n\t\tmodels.AllNetListeners.Range(func(srvr, listr interface{}) bool { // iterate over the map\n\t\t\tsrv, _ := srvr.(string)\n\t\t\tlis, _ := listr.(*net.TCPListener)\n\t\t\tidx := srvToIndex[srv]\n\t\t\tlf, err := lis.File()\n\t\t\tif err != nil {\n\t\t\t\tlog.Error().Msgf(\"%v\", err)\n\t\t\t\terr = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)\n\t\t\t\tmesg = mesg + fmt.Sprintf(\"rollback of symlinks failed: %v\", err)\n\t\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\t\tisErr = true\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tnewFile := os.NewFile(uintptr(lf.Fd()), \"dupFdlistner\"+strconv.Itoa(idx))\n\t\t\tfiles[idx] = newFile\n\t\t\treturn true\n\t\t})\n\t\tif isErr {\n\t\t\treturn\n\t\t}\n\n\t\tcmd := exec.Command(filepath.Join(bpfcfg.HostConfig.BasePath, \"latest/l3afd\"), \"--config\", filepath.Join(bpfcfg.HostConfig.BasePath, \"latest/l3afd.cfg\"))\n\t\tcmd.SysProcAttr = &syscall.SysProcAttr{\n\t\t\tSetsid: true,\n\t\t}\n\t\tcmd.Stdout = os.Stdout\n\t\tcmd.Stderr = os.Stderr\n\t\tcmd.ExtraFiles = files\n\n\t\terr = bpfcfg.StopAllProbesAndUserPrograms()\n\t\tif err != nil {\n\t\t\tlog.Err(err)\n\t\t\terr = bpfcfg.StartAllUserProgramsAndProbes()\n\t\t\tif err != nil {\n\t\t\t\tlog.Error().Msgf(\"%v\", err)\n\t\t\t\tmesg = mesg + fmt.Sprintf(\"unable to start all userprograms and probes: %v\", err)\n\t\t\t}\n\t\t\terr = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)\n\t\t\tmesg = mesg + fmt.Sprintf(\"rollback of symlinks failed: %v\", err)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\n\t\tlog.Info().Msg(\"Starting child Process\")\n\t\terr = cmd.Start()\n\t\tif err != nil {\n\t\t\tlog.Error().Msgf(\"%v\", err)\n\t\t\tmesg = mesg + fmt.Sprintf(\"unable to start new instance %v\", err)\n\t\t\terr = cmd.Process.Kill()\n\t\t\tif err != nil {\n\t\t\t\tlog.Error().Msgf(\"%v\", err)\n\t\t\t\tmesg = mesg + fmt.Sprintf(\"unable to kill the new instance %v\", err)\n\t\t\t}\n\t\t\terr = bpfcfg.StartAllUserProgramsAndProbes()\n\t\t\tif err != nil {\n\t\t\t\tlog.Error().Msgf(\"%v\", err)\n\t\t\t\tmesg = mesg + fmt.Sprintf(\"unable to start all userprograms and probes: %v\", err)\n\t\t\t}\n\t\t\terr = pidfile.CreatePID(bpfcfg.HostConfig.PIDFilename)\n\t\t\tif err != nil {\n\t\t\t\tlog.Error().Msgf(\"%v\", err)\n\t\t\t\tmesg = mesg + fmt.Sprintf(\"unable to create pid file: %v\", err)\n\t\t\t}\n\t\t\terr = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)\n\t\t\tif err != nil {\n\t\t\t\tmesg = mesg + fmt.Sprintf(\"rollback of symlink failed: %v\", err)\n\t\t\t}\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\t\tNewProcessStatus := make(chan string)\n\t\tgo func() {\n\t\t\t// I need to write client code for reading the state of new process\n\t\t\tvar err error\n\t\t\tvar conn net.Conn\n\t\t\tf := false\n\t\t\tfor i := 1; i <= bpfcfg.HostConfig.TimetoRestart; i++ {\n\t\t\t\tconn, err = net.Dial(\"unix\", models.StateSock)\n\t\t\t\tif err == nil {\n\t\t\t\t\tf = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tlog.Info().Msgf(\"Waiting for socket to be up...\")\n\t\t\t\ttime.Sleep(time.Second) // sleep for a second before trying again\n\t\t\t}\n\t\t\tif !f {\n\t\t\t\tconn.Close()\n\t\t\t\tNewProcessStatus <- models.StatusFailed\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer conn.Close()\n\t\t\tdecoder := gob.NewDecoder(conn)\n\t\t\tvar data string\n\t\t\terr = decoder.Decode(&data)\n\t\t\tif err != nil {\n\t\t\t\tNewProcessStatus <- models.StatusFailed\n\t\t\t\treturn\n\t\t\t}\n\t\t\tNewProcessStatus <- data\n\t\t}()\n\n\t\t// time to bootup\n\t\tselect {\n\t\tcase terr := <-srvError:\n\t\t\tif terr != nil {\n\t\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\t\terr = cmd.Process.Kill()\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Error().Msgf(\"%v\", err)\n\t\t\t\t\tmesg = mesg + fmt.Sprintf(\"unable to kill the new instance %v\", err)\n\t\t\t\t}\n\t\t\t\terr = bpfcfg.StartAllUserProgramsAndProbes()\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Error().Msgf(\"%v\", err)\n\t\t\t\t\tmesg = mesg + fmt.Sprintf(\"unable to start all userprograms and probes: %v\", err)\n\t\t\t\t}\n\t\t\t\terr = pidfile.CreatePID(bpfcfg.HostConfig.PIDFilename)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Error().Msgf(\"%v\", err)\n\t\t\t\t\tmesg = mesg + fmt.Sprintf(\"unable to create pid file: %v\", err)\n\t\t\t\t}\n\t\t\t\terr = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)\n\t\t\t\tif err != nil {\n\t\t\t\t\tmesg = mesg + fmt.Sprintf(\"rollback of symlink failed: %v\", err)\n\t\t\t\t}\n\t\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\t\tlog.Err(terr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tbreak\n\t\tdefault:\n\t\t\ttime.Sleep(time.Second)\n\t\t}\n\n\t\tst := <-NewProcessStatus\n\t\tif st == models.StatusFailed {\n\t\t\terr = cmd.Process.Kill()\n\t\t\tif err != nil {\n\t\t\t\tlog.Error().Msgf(\"%v\", err)\n\t\t\t\tmesg = mesg + fmt.Sprintf(\"unable to kill the new instance %v\", err)\n\t\t\t}\n\t\t\terr = bpfcfg.StartAllUserProgramsAndProbes()\n\t\t\tif err != nil {\n\t\t\t\tlog.Error().Msgf(\"%v\", err)\n\t\t\t\tmesg = mesg + fmt.Sprintf(\"unable to start all userprograms and probes: %v\", err)\n\t\t\t}\n\t\t\terr = pidfile.CreatePID(bpfcfg.HostConfig.PIDFilename)\n\t\t\tif err != nil {\n\t\t\t\tlog.Error().Msgf(\"%v\", err)\n\t\t\t\tmesg = mesg + fmt.Sprintf(\"unable to create pid file: %v\", err)\n\t\t\t}\n\t\t\terr = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)\n\t\t\tif err != nil {\n\t\t\t\tmesg = mesg + fmt.Sprintf(\"rollback of symlink failed: %v\", err)\n\t\t\t}\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t} else {\n\t\t\tlog.Info().Msgf(\"doing exiting old process\")\n\t\t\tmodels.CloseForRestart <- struct{}{}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "apis/handlers/restart_linux_test.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\npackage handlers\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/l3af-project/l3afd/v2/models\"\n)\n\nfunc Test_HandleRestart(t *testing.T) {\n\tvar req *http.Request\n\treq, _ = http.NewRequest(\"PUT\", \"/l3af/configs/v1/restart\", nil)\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tmodels.IsReadOnly = true\n\trr := httptest.NewRecorder()\n\thandler := HandleRestart(nil)\n\thandler.ServeHTTP(rr, req)\n\tif rr.Code != http.StatusInternalServerError {\n\t\tmodels.IsReadOnly = false\n\t\tt.Error(\"Handle restart Failed\")\n\t}\n\tmodels.IsReadOnly = false\n}\n"
  },
  {
    "path": "apis/handlers/restart_windows.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage handlers\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/rs/zerolog/log\"\n\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n)\n\n// HandleRestart Store meta data about ebpf programs and exit\n// @Summary Store meta data about ebpf programs and exit\n// @Description Store meta data about ebpf programs and exit\n// @Accept  json\n// @Produce  json\n// @Param cfgs body []models.L3afBPFPrograms true \"BPF programs\"\n// @Success 200\n// @Router /l3af/configs/v1/restart [put]\nfunc HandleRestart(bpfcfg *bpfprogs.NFConfigs) http.HandlerFunc {\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tmesg := \"\"\n\t\tstatusCode := http.StatusOK\n\t\tw.Header().Add(\"Content-Type\", \"application/json\")\n\t\tdefer func(mesg *string, statusCode *int) {\n\t\t\tw.WriteHeader(*statusCode)\n\t\t\t_, err := w.Write([]byte(*mesg))\n\t\t\tif err != nil {\n\t\t\t\tlog.Warn().Msgf(\"Failed to write response bytes: %v\", err)\n\t\t\t}\n\t\t}(&mesg, &statusCode)\n\t\tmesg = \"Graceful restart is only supported for linux as of now\"\n\t}\n}\n"
  },
  {
    "path": "apis/handlers/restart_windows_test.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\npackage handlers\n\nimport (\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc Test_HandleRestart(t *testing.T) {\n\tvar req *http.Request\n\treq, _ = http.NewRequest(\"PUT\", \"/l3af/configs/v1/restart\", nil)\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\trr := httptest.NewRecorder()\n\thandler := HandleRestart(nil)\n\thandler.ServeHTTP(rr, req)\n\tif rr.Code != http.StatusOK {\n\t\tt.Error(\"Handle restart Failed\")\n\t}\n}\n"
  },
  {
    "path": "apis/handlers/updateconfig.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage handlers\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"net/http\"\n\n\t\"github.com/rs/zerolog/log\"\n\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n)\n\n// UpdateConfig Update eBPF Programs configuration\n// @Summary Update eBPF Programs configuration\n// @Description Update eBPF Programs configuration\n// @Accept  json\n// @Produce  json\n// @Param cfgs body []models.L3afBPFPrograms true \"BPF programs\"\n// @Success 200\n// @Router /l3af/configs/v1/update [post]\nfunc UpdateConfig(ctx context.Context, bpfcfg *bpfprogs.NFConfigs) http.HandlerFunc {\n\n\treturn func(w http.ResponseWriter, r *http.Request) {\n\t\tmesg := \"\"\n\t\tstatusCode := http.StatusOK\n\n\t\tw.Header().Add(\"Content-Type\", \"application/json\")\n\n\t\tdefer func(mesg *string, statusCode *int) {\n\t\t\tw.WriteHeader(*statusCode)\n\t\t\t_, err := w.Write([]byte(*mesg))\n\t\t\tif err != nil {\n\t\t\t\tlog.Warn().Msgf(\"Failed to write response bytes: %v\", err)\n\t\t\t}\n\t\t}(&mesg, &statusCode)\n\t\tif models.IsReadOnly {\n\t\t\tlog.Warn().Msgf(\"We are in between restart please try after some time\")\n\t\t\tmesg = \"We are currently in the middle of a restart. Please attempt again after a while.\"\n\t\t\treturn\n\t\t}\n\t\tdefer DecWriteReq()\n\t\tIncWriteReq()\n\t\tif r.Body == nil {\n\t\t\tlog.Warn().Msgf(\"Empty request body\")\n\t\t\treturn\n\t\t}\n\t\tbodyBuffer, err := io.ReadAll(r.Body)\n\t\tif err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed to read request body: %v\", err)\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\n\t\tvar t []models.L3afBPFPrograms\n\t\tif err := json.Unmarshal(bodyBuffer, &t); err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed to unmarshal payload: %v\", err)\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\n\t\tif err := bpfcfg.DeployeBPFPrograms(t); err != nil {\n\t\t\tmesg = fmt.Sprintf(\"failed to deploy ebpf programs: %v\", err)\n\t\t\tlog.Error().Msg(mesg)\n\t\t\tstatusCode = http.StatusInternalServerError\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "apis/handlers/updateconfig_test.go",
    "content": "package handlers\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n\t\"github.com/l3af-project/l3afd/v2/config\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n)\n\nfunc Test_UpdateConfig(t *testing.T) {\n\n\ttests := []struct {\n\t\tname       string\n\t\tBody       *strings.Reader\n\t\theader     map[string]string\n\t\tstatus     int\n\t\tcfg        *bpfprogs.NFConfigs\n\t\tisreadonly bool\n\t}{\n\t\t{\n\t\t\tname:       \"NilBody\",\n\t\t\tBody:       nil,\n\t\t\tstatus:     http.StatusOK,\n\t\t\tisreadonly: false,\n\t\t\tcfg: &bpfprogs.NFConfigs{\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"FailedToUnmarshal\",\n\t\t\tBody:       strings.NewReader(\"Something\"),\n\t\t\tstatus:     http.StatusInternalServerError,\n\t\t\theader:     map[string]string{},\n\t\t\tisreadonly: false,\n\t\t\tcfg: &bpfprogs.NFConfigs{\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:       \"UnknownHostName\",\n\t\t\tBody:       strings.NewReader(dummypayload),\n\t\t\tstatus:     http.StatusInternalServerError,\n\t\t\theader:     map[string]string{},\n\t\t\tisreadonly: false,\n\t\t\tcfg: &bpfprogs.NFConfigs{\n\t\t\t\tHostName: \"dummy\",\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:   \"InReadonly\",\n\t\t\tBody:   nil,\n\t\t\tstatus: http.StatusOK,\n\t\t\theader: map[string]string{\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tisreadonly: true,\n\t\t\tcfg:        nil,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tvar req *http.Request\n\t\tif tt.Body == nil {\n\t\t\treq, _ = http.NewRequest(\"POST\", \"/l3af/configs/v1/update\", nil)\n\t\t} else {\n\t\t\treq, _ = http.NewRequest(\"POST\", \"/l3af/configs/v1/update\", tt.Body)\n\t\t}\n\t\tfor key, val := range tt.header {\n\t\t\treq.Header.Set(key, val)\n\t\t}\n\t\tmodels.IsReadOnly = tt.isreadonly\n\t\trr := httptest.NewRecorder()\n\t\thandler := UpdateConfig(context.Background(), tt.cfg)\n\t\thandler.ServeHTTP(rr, req)\n\t\tif rr.Code != tt.status {\n\t\t\tmodels.IsReadOnly = false\n\t\t\tt.Error(\"UpdateConfig Failed\")\n\t\t}\n\t\tmodels.IsReadOnly = false\n\t}\n}\n"
  },
  {
    "path": "apis/routes.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage apis\n\nimport (\n\t\"context\"\n\n\t\"github.com/l3af-project/l3afd/v2/apis/handlers\"\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n\t\"github.com/l3af-project/l3afd/v2/routes\"\n)\n\nfunc apiRoutes(ctx context.Context, bpfcfg *bpfprogs.NFConfigs) []routes.Route {\n\n\tr := []routes.Route{\n\t\t{\n\t\t\tMethod:      \"POST\",\n\t\t\tPath:        \"/l3af/configs/{version}/update\",\n\t\t\tHandlerFunc: handlers.UpdateConfig(ctx, bpfcfg),\n\t\t},\n\t\t{\n\t\t\tMethod:      \"GET\",\n\t\t\tPath:        \"/l3af/configs/{version}/{iface}\",\n\t\t\tHandlerFunc: handlers.GetConfig,\n\t\t},\n\t\t{\n\t\t\tMethod:      \"GET\",\n\t\t\tPath:        \"/l3af/configs/{version}\",\n\t\t\tHandlerFunc: handlers.GetConfigAll,\n\t\t},\n\t\t{\n\t\t\tMethod:      \"POST\",\n\t\t\tPath:        \"/l3af/configs/{version}/add\",\n\t\t\tHandlerFunc: handlers.AddEbpfPrograms(ctx, bpfcfg),\n\t\t},\n\t\t{\n\t\t\tMethod:      \"POST\",\n\t\t\tPath:        \"/l3af/configs/{version}/delete\",\n\t\t\tHandlerFunc: handlers.DeleteEbpfPrograms(ctx, bpfcfg),\n\t\t},\n\t\t{\n\t\t\tMethod:      \"PUT\",\n\t\t\tPath:        \"/l3af/configs/{version}/restart\",\n\t\t\tHandlerFunc: handlers.HandleRestart(bpfcfg),\n\t\t},\n\t}\n\n\treturn r\n}\n"
  },
  {
    "path": "bpfprogs/bpf.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package bpfprogs provides primitives for BPF programs / Network Functions.\npackage bpfprogs\n\nimport (\n\t\"archive/tar\"\n\t\"archive/zip\"\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"container/ring\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/l3af-project/l3afd/v2/config\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/l3af-project/l3afd/v2/stats\"\n\t\"github.com/l3af-project/l3afd/v2/utils\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/cilium/ebpf/rlimit\"\n\tps \"github.com/mitchellh/go-ps\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/rs/zerolog/log\"\n)\n\nvar (\n\texecCommand           = exec.Command\n\tcopyBufPool sync.Pool = sync.Pool{New: func() interface{} { return new(bytes.Buffer) }}\n)\n\n//lint:ignore U1000 avoid false linter error on windows, since this variable is only used in linux code\nconst executePerm uint32 = 0111\nconst bpfStatus string = \"RUNNING\"\n\n// BPF defines run time details for BPFProgram.\ntype BPF struct {\n\tProgram           models.BPFProgram\n\tCmd               *exec.Cmd                 `json:\"-\"`\n\tFilePath          string                    // Binary file path\n\tRestartCount      int                       // To track restart count\n\tPrevMapNamePath   string                    // Previous Map name with path to link\n\tMapNamePath       string                    // Map name with path\n\tProgID            ebpf.ProgramID            // eBPF Program ID\n\tBpfMaps           map[string]BPFMap         // Config maps passed as map-args, Map name is Key\n\tMetricsBpfMaps    map[string]*MetricsBPFMap // Metrics map name+key+aggregator is key\n\tCtx               context.Context           `json:\"-\"`\n\tDone              chan bool                 `json:\"-\"`\n\tProgMapCollection *ebpf.Collection          `json:\"_\"` // eBPF Collection reference\n\tProgMapID         ebpf.MapID                // Prog map id\n\tPrevProgMapID     ebpf.MapID                // Prev prog map id\n\tHostConfig        *config.Config\n\tLink              link.Link `json:\"-\"` // handle link object\n\tProbeLinks        []*link.Link\n}\n\nfunc NewBpfProgram(ctx context.Context, program models.BPFProgram, conf *config.Config, ifaceName string) *BPF {\n\n\tvar progMapFilePath string\n\tversion := utils.ReplaceDotsWithUnderscores(program.Version)\n\tif len(program.MapName) > 0 {\n\t\tif program.ProgType == models.XDPType {\n\t\t\tprogMapFilePath = filepath.Join(conf.BpfMapDefaultPath, ifaceName, program.Name, version, program.MapName)\n\t\t} else if program.ProgType == models.TCType {\n\t\t\tprogMapFilePath = filepath.Join(conf.BpfMapDefaultPath, models.TCMapPinPath, ifaceName, program.Name, version, program.MapName)\n\t\t}\n\t\tif strings.Contains(progMapFilePath, \"..\") {\n\t\t\tlog.Error().Msgf(\"program map file contains relative path %s\", progMapFilePath)\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tbpf := &BPF{\n\t\tProgram:        program,\n\t\tRestartCount:   0,\n\t\tCmd:            nil,\n\t\tFilePath:       \"\",\n\t\tBpfMaps:        make(map[string]BPFMap, 0),\n\t\tMetricsBpfMaps: make(map[string]*MetricsBPFMap, 0),\n\t\tCtx:            ctx,\n\t\tDone:           nil,\n\t\tHostConfig:     conf,\n\t\tMapNamePath:    progMapFilePath,\n\t}\n\n\treturn bpf\n}\n\n// LoadRootProgram - Loading the Root Program for a given interface.\nfunc LoadRootProgram(ifaceName string, direction string, progType string, conf *config.Config) (*BPF, error) {\n\n\tlog.Info().Msgf(\"LoadRootProgram iface %s direction %s progType %s\", ifaceName, direction, progType)\n\tvar rootProgBPF *BPF\n\tswitch progType {\n\tcase models.XDPType:\n\t\tversion := utils.ReplaceDotsWithUnderscores(conf.XDPRootVersion)\n\t\trootProgBPF = &BPF{\n\t\t\tProgram: models.BPFProgram{\n\t\t\t\tName:              conf.XDPRootPackageName,\n\t\t\t\tArtifact:          conf.XDPRootArtifact,\n\t\t\t\tMapName:           conf.XDPRootMapName,\n\t\t\t\tVersion:           conf.XDPRootVersion,\n\t\t\t\tUserProgramDaemon: false,\n\t\t\t\tAdminStatus:       models.Enabled,\n\t\t\t\tProgType:          models.XDPType,\n\t\t\t\tSeqID:             0,\n\t\t\t\tStartArgs:         map[string]interface{}{},\n\t\t\t\tStopArgs:          map[string]interface{}{},\n\t\t\t\tStatusArgs:        map[string]interface{}{},\n\t\t\t\tObjectFile:        conf.XDPRootObjectFile,\n\t\t\t\tEntryFunctionName: conf.XDPRootEntryFunctionName,\n\t\t\t},\n\t\t\tRestartCount:    0,\n\t\t\tCmd:             nil,\n\t\t\tFilePath:        \"\",\n\t\t\tPrevMapNamePath: \"\",\n\t\t\tHostConfig:      conf,\n\t\t\tMapNamePath:     filepath.Join(conf.BpfMapDefaultPath, ifaceName, conf.XDPRootPackageName, version, conf.XDPRootMapName),\n\t\t\tLink:            nil,\n\t\t}\n\tcase models.TCType:\n\t\trootProgBPF = &BPF{\n\t\t\tProgram: models.BPFProgram{\n\t\t\t\tName:              conf.TCRootPackageName,\n\t\t\t\tArtifact:          conf.TCRootArtifact,\n\t\t\t\tVersion:           conf.TCRootVersion,\n\t\t\t\tUserProgramDaemon: false,\n\t\t\t\tAdminStatus:       models.Enabled,\n\t\t\t\tProgType:          models.TCType,\n\t\t\t\tStartArgs:         map[string]interface{}{},\n\t\t\t\tStopArgs:          map[string]interface{}{},\n\t\t\t\tStatusArgs:        map[string]interface{}{},\n\t\t\t},\n\t\t\tRestartCount:    0,\n\t\t\tCmd:             nil,\n\t\t\tFilePath:        \"\",\n\t\t\tPrevMapNamePath: \"\",\n\t\t\tHostConfig:      conf,\n\t\t}\n\t\tif direction == models.IngressType {\n\t\t\trootProgBPF.Program.MapName = conf.TCRootIngressMapName\n\t\t\trootProgBPF.Program.ObjectFile = conf.TCRootIngressObjectFile\n\t\t\trootProgBPF.Program.EntryFunctionName = conf.TCRootIngressEntryFunctionName\n\t\t} else if direction == models.EgressType {\n\t\t\trootProgBPF.Program.MapName = conf.TCRootEgressMapName\n\t\t\trootProgBPF.Program.ObjectFile = conf.TCRootEgressObjectFile\n\t\t\trootProgBPF.Program.EntryFunctionName = conf.TCRootEgressEntryFunctionName\n\t\t}\n\t\tversion := utils.ReplaceDotsWithUnderscores(rootProgBPF.Program.Version)\n\t\trootProgBPF.MapNamePath = filepath.Join(conf.BpfMapDefaultPath, models.TCMapPinPath, ifaceName, rootProgBPF.Program.Name, version, rootProgBPF.Program.MapName)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown direction %s for root program in iface %s\", direction, ifaceName)\n\t}\n\n\tif err := rootProgBPF.VerifyAndGetArtifacts(conf); err != nil {\n\t\tlog.Error().Err(err).Msg(\"failed to get root artifacts\")\n\t\treturn nil, err\n\t}\n\n\t// On l3afd crashing scenario verify root program are unloaded properly by checking existence of persisted maps\n\t// if map file exists then root program didn't clean up pinned map files\n\tif fileExists(rootProgBPF.MapNamePath) {\n\t\tlog.Warn().Msgf(\"previous instance of root program %s persisted map %s file exists\", rootProgBPF.Program.Name, rootProgBPF.MapNamePath)\n\t\tif err := rootProgBPF.RemoveRootProgMapFile(ifaceName); err != nil {\n\t\t\tlog.Warn().Err(err).Msgf(\"previous instance of root program %s map file not removed successfully - %s \", rootProgBPF.Program.Name, rootProgBPF.MapNamePath)\n\t\t}\n\t}\n\n\tversion := utils.ReplaceDotsWithUnderscores(rootProgBPF.Program.Version)\n\tif progType == models.XDPType {\n\t\trlimit.RemoveMemlock()\n\t\tif err := rootProgBPF.LoadXDPAttachProgram(ifaceName); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to load xdp root program on iface %s name %s direction %s with err %w\", ifaceName, rootProgBPF.Program.Name, direction, err)\n\t\t}\n\t\t// pin the program also\n\t\tprogPinPath := utils.ProgPinPath(rootProgBPF.HostConfig.BpfMapDefaultPath, ifaceName, rootProgBPF.Program.Name, version, rootProgBPF.Program.EntryFunctionName, rootProgBPF.Program.ProgType)\n\t\tif err := rootProgBPF.ProgMapCollection.Programs[rootProgBPF.Program.EntryFunctionName].Pin(progPinPath); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tif utils.CheckTCXSupport() {\n\t\t\tif err := rootProgBPF.LoadTCXAttachProgram(ifaceName, direction); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to load tcx root program on iface %s name %s direction %s with err %w\", ifaceName, rootProgBPF.Program.Name, direction, err)\n\t\t\t}\n\t\t} else {\n\t\t\tif err := rootProgBPF.LoadTCAttachProgram(ifaceName, direction); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to load tc root program on iface %s name %s direction %s with err %w\", ifaceName, rootProgBPF.Program.Name, direction, err)\n\t\t\t}\n\t\t}\n\t\t// pin the program also\n\t\tprogPinPath := utils.ProgPinPath(rootProgBPF.HostConfig.BpfMapDefaultPath, ifaceName, rootProgBPF.Program.Name, version, rootProgBPF.Program.EntryFunctionName, rootProgBPF.Program.ProgType)\n\t\tif err := rootProgBPF.ProgMapCollection.Programs[rootProgBPF.Program.EntryFunctionName].Pin(progPinPath); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn rootProgBPF, nil\n}\n\n// Stop the NF process if running outside l3afd\nfunc StopExternalRunningProcess(processName string) error {\n\t// validate process name\n\tif len(processName) < 1 {\n\t\treturn fmt.Errorf(\"process name can not be empty\")\n\t}\n\n\t// process names are truncated to 15 chars\n\tpsName := processName\n\tif len(processName) > 15 {\n\t\tpsName = processName[:15]\n\t}\n\n\tmyPid := os.Getpid()\n\tprocessList, err := ps.Processes()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to fetch processes list with err %w\", err)\n\t}\n\tlog.Info().Msgf(\"Searching for process %s and not ppid %d\", processName, myPid)\n\tfor _, process := range processList {\n\t\tif strings.Contains(process.Executable(), psName) {\n\t\t\tif process.PPid() != myPid {\n\t\t\t\tlog.Warn().Msgf(\"found process id %d name %s ppid %d, stopping it\", process.Pid(), process.Executable(), process.PPid())\n\t\t\t\tosProcess, err := os.FindProcess(process.Pid())\n\t\t\t\tif err == nil {\n\t\t\t\t\terr = osProcess.Kill()\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"external BPFProgram stop failed with error: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// Stop returns the last error seen, but stops bpf program.\n// Stops the user programs if any, and unloads the BPF program.\n// Clean up all map handles.\n// Verify next program pinned map file is removed\nfunc (b *BPF) Stop(ifaceName, ipv4_address, direction string, chain bool) error {\n\tif b.Program.UserProgramDaemon && b.Cmd == nil {\n\t\treturn fmt.Errorf(\"BPFProgram is not running %s\", b.Program.Name)\n\t}\n\n\tlog.Info().Msgf(\"Stopping BPF Program - %s\", b.Program.Name)\n\n\t// Removing maps\n\tfor key, val := range b.BpfMaps {\n\t\tlog.Debug().Msgf(\"removing BPF maps %s value map %#v\", key, val)\n\t\tdelete(b.BpfMaps, key)\n\t}\n\n\t// Removing Metrics maps\n\tfor key, val := range b.MetricsBpfMaps {\n\t\tlog.Debug().Msgf(\"removing metric bpf maps %s value %#v\", key, val)\n\t\tdelete(b.MetricsBpfMaps, key)\n\t}\n\n\t// Stop BPF configs\n\tif len(b.Program.CmdConfig) > 0 && len(b.Program.ConfigFilePath) > 0 {\n\t\tlog.Info().Msgf(\"Stopping BPF configs %s \", b.Program.Name)\n\t\tb.Done <- true\n\t}\n\n\t// Reset ProgID\n\tb.ProgID = 0\n\n\tstats.Add(1, stats.BPFStopCount, b.Program.Name, direction, ifaceName, ipv4_address)\n\n\t// Deleting stale metrics indicates that the ebpf program is not running.\n\tstats.BPFRunning.Delete(prometheus.Labels{\"ebpf_program\": b.Program.Name, \"version\": b.Program.Version, \"direction\": direction, \"interface_name\": ifaceName, \"ipv4_address\": ipv4_address})\n\tstats.BPFStartTime.Delete(prometheus.Labels{\"ebpf_program\": b.Program.Name, \"direction\": direction, \"interface_name\": ifaceName, \"ipv4_address\": ipv4_address})\n\t// Stop User Programs if any\n\tif len(b.Program.CmdStop) < 1 && b.Program.UserProgramDaemon {\n\t\t// Loaded using user program\n\t\tif err := b.ProcessTerminate(); err != nil {\n\t\t\treturn fmt.Errorf(\"BPFProgram %s process terminate failed with error: %w\", b.Program.Name, err)\n\t\t}\n\t\tif b.Cmd != nil {\n\t\t\tif err := b.Cmd.Wait(); err != nil {\n\t\t\t\tlog.Error().Err(err).Msgf(\"cmd wait at stopping bpf program %s errored\", b.Program.Name)\n\t\t\t}\n\t\t\tb.Cmd = nil\n\t\t}\n\t} else if len(b.Program.CmdStop) > 0 && b.Program.UserProgramDaemon {\n\t\tcmd := filepath.Join(b.FilePath, b.Program.CmdStop)\n\n\t\tif err := assertExecutable(cmd); err != nil {\n\t\t\treturn fmt.Errorf(\"no executable permissions on %s - error %w\", b.Program.CmdStop, err)\n\t\t}\n\n\t\targs := make([]string, 0, len(b.Program.StopArgs)<<1)\n\t\tif len(ifaceName) > 0 {\n\t\t\targs = append(args, \"--iface=\"+ifaceName) // detaching from iface\n\t\t}\n\t\tif len(direction) > 0 {\n\t\t\targs = append(args, \"--direction=\"+direction) // xdpingress or ingress or egress\n\t\t}\n\t\tfor k, val := range b.Program.StopArgs {\n\t\t\tif v, ok := val.(string); !ok {\n\t\t\t\terr := fmt.Errorf(\"stop args is not a string for the bpf program %s\", b.Program.Name)\n\t\t\t\tlog.Error().Err(err).Msgf(\"failed to convert stop args value into string for program %s\", b.Program.Name)\n\t\t\t\treturn err\n\t\t\t} else {\n\t\t\t\targs = append(args, \"--\"+k+\" =\"+v)\n\t\t\t}\n\t\t}\n\n\t\tlog.Info().Msgf(\"bpf program stop command : %s %v\", cmd, args)\n\t\tprog := execCommand(cmd, args...)\n\t\tif err := prog.Run(); err != nil {\n\t\t\tlog.Warn().Err(err).Msgf(\"l3afd : Failed to stop the program %s\", b.Program.CmdStop)\n\t\t}\n\t\tb.Cmd = nil\n\t}\n\n\t// unload the BPF programs\n\tif allInterfaces, err := getHostInterfaces(); err != nil {\n\t\terrOut := fmt.Errorf(\"failed get interfaces in Stop Function: %v\", err)\n\t\tlog.Error().Err(errOut)\n\t\treturn errOut\n\t} else {\n\t\tif _, ok := allInterfaces[ifaceName]; ok {\n\t\t\tif b.ProgMapCollection != nil {\n\t\t\t\tif err := b.UnloadProgram(ifaceName, direction); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"BPFProgram %s unload failed on interface %s with error: %w\", b.Program.Name, ifaceName, err)\n\t\t\t\t}\n\t\t\t\tlog.Info().Msgf(\"%s => %s direction => %s - program is unloaded/detached successfully\", ifaceName, b.Program.Name, direction)\n\t\t\t}\n\t\t} else {\n\t\t\tif err := b.RemovePinnedFiles(ifaceName); err != nil {\n\t\t\t\tlog.Error().Err(err).Msgf(\"stop user program - failed to remove map files %s\", b.Program.Name)\n\t\t\t\treturn fmt.Errorf(\"stop user program - failed to remove map files %s\", b.Program.Name)\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := b.VerifyCleanupMaps(chain); err != nil {\n\t\tlog.Error().Err(err).Msgf(\"stop user program - failed to remove map files %s\", b.Program.Name)\n\t\treturn fmt.Errorf(\"stop user program - failed to remove map files %s\", b.Program.Name)\n\t}\n\n\treturn nil\n}\n\n// Start returns the last error seen, but starts bpf program.\n// Here initially prevprogmap entry is removed and passed to the bpf program\n// After starting the user program, will update the kernel progam fd into prevprogram map.\n// This method waits till prog fd entry is updated, else returns error assuming kernel program is not loaded.\n// It also verifies the next program pinned map is created or not.\nfunc (b *BPF) Start(ifaceName, ipv4_address, direction string, chain bool) error {\n\tif b.FilePath == \"\" {\n\t\treturn errors.New(\"no program binary path found\")\n\t}\n\n\tif len(b.Program.CmdStart) > 0 {\n\t\t// Verify other instance is running\n\t\tif err := StopExternalRunningProcess(b.Program.CmdStart); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to stop external instance of the program %s with error : %w\", b.Program.CmdStart, err)\n\t\t}\n\t}\n\n\t// Making sure old map entry is removed before passing the prog fd map to the program.\n\tif len(b.PrevMapNamePath) > 0 {\n\t\tif err := b.RemovePrevProgFD(); err != nil {\n\t\t\tlog.Error().Err(err).Msgf(\"ProgramMap %s entry removal failed\", b.PrevMapNamePath)\n\t\t}\n\t}\n\n\t// Both xdp and tc are loaded using the same mechanism.\n\tif len(b.Program.ObjectFile) > 0 {\n\t\tif chain {\n\t\t\tif err := b.LoadBPFProgramChain(ifaceName, direction); err != nil {\n\t\t\t\treturn fmt.Errorf(\"loading bpf program %s - error %w\", b.Program.Name, err)\n\t\t\t}\n\t\t} else {\n\t\t\tif err := b.AttachBPFProgram(ifaceName, direction); err != nil {\n\t\t\t\treturn fmt.Errorf(\"attaching bpf program %s - error %w\", b.Program.Name, err)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tlog.Info().Msgf(\"bpf program object file is not defined - %s\", b.Program.Name)\n\t}\n\n\tlog.Info().Msgf(\"successfully Loaded & Attached %s in direction %s on interface %s\", b.Program.Name, direction, ifaceName)\n\t// Start user program before loading\n\tif len(b.Program.CmdStart) > 0 {\n\t\tif err := b.StartUserProgram(ifaceName, direction, chain); err != nil {\n\t\t\treturn fmt.Errorf(\"user program startup failed %s - error %w\", b.Program.CmdStart, err)\n\t\t}\n\t}\n\n\t// making sure program fd map pinned file is created\n\tif err := b.VerifyPinnedProgMap(chain, true); err != nil {\n\t\treturn fmt.Errorf(\"failed to find pinned file %s  %w\", b.MapNamePath, err)\n\t}\n\n\t// BPF map config values\n\tif len(b.Program.MapArgs) > 0 {\n\t\tif err := b.UpdateBPFMaps(ifaceName, ipv4_address, direction); err != nil {\n\t\t\tlog.Error().Err(err).Msg(\"failed to update ebpf program BPF maps\")\n\t\t\treturn fmt.Errorf(\"failed to update ebpf program BPF maps %w\", err)\n\t\t}\n\t}\n\n\t// Update args config values\n\tif len(b.Program.UpdateArgs) > 0 {\n\t\tif err := b.UpdateArgs(ifaceName, ipv4_address, direction); err != nil {\n\t\t\tlog.Error().Err(err).Msg(\"failed to update ebpf program config update\")\n\t\t\treturn fmt.Errorf(\"failed to update ebpf program config update %w\", err)\n\t\t}\n\t}\n\n\t// Fetch when prev program map is updated only when loaded using user program\n\tif len(b.PrevMapNamePath) > 0 && b.ProgMapCollection == nil {\n\t\tvar err error\n\t\t// retry 10 times to verify entry is created\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tb.ProgID, err = b.GetProgID()\n\t\t\tif err == nil {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tlog.Warn().Msg(\"failed to fetch the program ID, retrying after a second ... \")\n\t\t\ttime.Sleep(1 * time.Second)\n\t\t}\n\n\t\tif err != nil {\n\t\t\tlog.Error().Err(err).Msg(\"failed to fetch ebpf program FD\")\n\t\t\treturn fmt.Errorf(\"failed to fetch ebpf program FD %w\", err)\n\t\t}\n\t}\n\n\t// BPFconfigs\n\tif len(b.Program.CmdConfig) > 0 && len(b.Program.ConfigFilePath) > 0 {\n\t\tlog.Info().Msgf(\"eBPF program specific config monitoring - %s\", b.Program.ConfigFilePath)\n\t\tb.Done = make(chan bool)\n\t\tgo b.RunBPFConfigs()\n\t}\n\n\tstats.Set(float64(time.Now().Unix()), stats.BPFStartTime, b.Program.Name, direction, ifaceName, ipv4_address)\n\n\tuserProgram, bpfProgram, err := b.isRunning()\n\tif !userProgram && !bpfProgram {\n\t\tlog.Error().Err(err).Msg(\"eBPF program failed to start\")\n\t\treturn fmt.Errorf(\"bpf program %s failed to start %w\", b.Program.Name, err)\n\t}\n\n\tstats.Add(1, stats.BPFStartCount, b.Program.Name, direction, ifaceName, ipv4_address)\n\n\tlog.Info().Msgf(\"BPF program - %s started Program ID %d\", b.Program.Name, uint32(b.ProgID))\n\treturn nil\n}\n\n// UpdateBPFMaps - Update the config ebpf maps via map arguments\nfunc (b *BPF) UpdateBPFMaps(ifaceName, ipv4_address, direction string) error {\n\tfor _, val := range b.Program.MapArgs {\n\t\tbpfMap, ok := b.BpfMaps[val.Name]\n\t\tif !ok {\n\t\t\tif err := b.AddBPFMap(val.Name); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tbpfMap = b.BpfMaps[val.Name]\n\t\t}\n\t\tfor _, v := range val.Args {\n\t\t\tlog.Info().Msgf(\"Update map args key %v val %v\", v.Key, v.Value)\n\t\t\tbpfMap.Update(v.Key, v.Value)\n\t\t}\n\t\tif err := bpfMap.RemoveMissingKeys(val.Args); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to remove missing entries of map %s with err %w\", val.Name, err)\n\t\t}\n\t}\n\tstats.Add(1, stats.BPFUpdateCount, b.Program.Name, direction, ifaceName, ipv4_address)\n\treturn nil\n}\n\n// Update config arguments using user program\nfunc (b *BPF) UpdateArgs(ifaceName, ipv4_address, direction string) error {\n\tif b.FilePath == \"\" {\n\t\treturn errors.New(\"update - no program binary path found\")\n\t}\n\n\tversion := utils.ReplaceDotsWithUnderscores(b.Program.Version)\n\tcmd := filepath.Join(b.FilePath, b.Program.CmdUpdate)\n\t// Validate\n\tif err := assertExecutable(cmd); err != nil {\n\t\treturn fmt.Errorf(\"no executable permissions on %s - error %w\", b.Program.CmdUpdate, err)\n\t}\n\n\targs := make([]string, 0, len(b.Program.UpdateArgs)<<1)\n\targs = append(args, \"--iface=\"+ifaceName)       // attaching to interface\n\targs = append(args, \"--direction=\"+direction)   // direction xdpingress or ingress or egress\n\targs = append(args, \"--cmd=\"+models.UpdateType) // argument cmd to update configs\n\targs = append(args, \"--version=\"+version)       // argument version of ebpf program\n\n\tif len(b.HostConfig.BPFLogDir) > 1 {\n\t\targs = append(args, \"--log-dir=\"+b.HostConfig.BPFLogDir)\n\t}\n\n\tfor k, val := range b.Program.UpdateArgs {\n\t\tif v, ok := val.(string); !ok {\n\t\t\terr := fmt.Errorf(\"update args is not a string for the ebpf program %s\", b.Program.Name)\n\t\t\tlog.Error().Err(err).Msgf(\"failed to convert update args value into string for program %s\", b.Program.Name)\n\t\t\treturn err\n\t\t} else {\n\t\t\targs = append(args, \"--\"+k+\"=\"+v)\n\t\t}\n\t}\n\n\tlog.Info().Msgf(\"BPF Program update command : %s %v\", cmd, args)\n\tUpdateCmd := execCommand(cmd, args...)\n\tif err := UpdateCmd.Start(); err != nil {\n\t\tstats.Add(1, stats.BPFUpdateFailedCount, b.Program.Name, direction, ifaceName, ipv4_address)\n\t\tcustomerr := fmt.Errorf(\"failed to start : %s %v %w\", cmd, args, err)\n\t\tlog.Warn().Err(customerr).Msgf(\"user mode BPF program failed - %s\", b.Program.Name)\n\t\treturn customerr\n\t}\n\n\tif err := UpdateCmd.Wait(); err != nil {\n\t\tstats.Add(1, stats.BPFUpdateFailedCount, b.Program.Name, direction, ifaceName, ipv4_address)\n\t\treturn fmt.Errorf(\"cmd wait at starting of bpf program returned with error %w\", err)\n\t}\n\n\tstats.Add(1, stats.BPFUpdateCount, b.Program.Name, direction, ifaceName, ipv4_address)\n\tlog.Info().Msgf(\"BPF program - %s config updated\", b.Program.Name)\n\treturn nil\n}\n\n// Status of user program is running\nfunc (b *BPF) isRunning() (bool, bool, error) {\n\tuserProgram := true\n\tvar err error\n\n\t// CmdStatus should check for user and BPF program\n\tif len(b.Program.CmdStatus) > 1 {\n\t\tcmd := filepath.Join(b.FilePath, b.Program.CmdStatus)\n\n\t\tif err := assertExecutable(cmd); err != nil {\n\t\t\tuserProgram = false\n\t\t} else {\n\t\t\targs := make([]string, 0, len(b.Program.StatusArgs)<<1)\n\n\t\t\tfor k, val := range b.Program.StatusArgs {\n\t\t\t\tif v, ok := val.(string); !ok {\n\t\t\t\t\terr = fmt.Errorf(\"status args is not a string for the ebpf program %s\", b.Program.Name)\n\t\t\t\t\tlog.Warn().Err(err).Msgf(\"failed to convert status args value into string for program %s\", b.Program.Name)\n\t\t\t\t} else {\n\t\t\t\t\targs = append(args, \"--\"+k+\" =\"+v)\n\t\t\t\t}\n\t\t\t}\n\t\t\tprog := execCommand(cmd, args...)\n\t\t\tvar out bytes.Buffer\n\t\t\tprog.Stdout = &out\n\t\t\tprog.Stderr = &out\n\t\t\tif err = prog.Run(); err != nil {\n\t\t\t\tlog.Warn().Err(err).Msgf(\"l3afd : Failed to execute %s\", b.Program.CmdStatus)\n\t\t\t}\n\t\t\toutStr, errStr := out.String(), out.String()\n\t\t\tif strings.EqualFold(outStr, bpfStatus) {\n\t\t\t\tuserProgram = true\n\t\t\t} else {\n\t\t\t\tuserProgram = false\n\t\t\t\tlog.Warn().Msgf(\"bpf program not running error - %s\", errStr)\n\t\t\t}\n\t\t}\n\t\treturn userProgram, b.IsLoaded(), err\n\t}\n\tif len(b.Program.CmdStart) > 1 && b.Program.UserProgramDaemon {\n\t\tif err := b.VerifyProcessObject(); err != nil {\n\t\t\tuserProgram = false\n\t\t\tlog.Warn().Err(err).Msgf(\"process object is not created for command start - %s\", b.Program.CmdStart)\n\t\t} else {\n\t\t\tuserProgram, err = IsProcessRunning(b.Cmd.Process.Pid, b.Program.Name)\n\t\t\tif err != nil {\n\t\t\t\tlog.Warn().Err(err).Msgf(\"failed to execute command status - %s user program %v\", b.Program.CmdStart, userProgram)\n\t\t\t} else {\n\t\t\t\tuserProgram = true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn userProgram, b.IsLoaded(), err\n}\n\n// VerifyAndGetArtifacts -Check binary already exists\nfunc (b *BPF) VerifyAndGetArtifacts(conf *config.Config) error {\n\n\tfPath := filepath.Join(conf.BPFDir, b.Program.Name, b.Program.Version, strings.Split(b.Program.Artifact, \".\")[0])\n\tif _, err := os.Stat(fPath); os.IsNotExist(err) {\n\t\treturn b.GetArtifacts(conf)\n\t}\n\n\tb.FilePath = fPath\n\treturn nil\n}\n\n// GetArtifacts downloads artifacts from the specified eBPF repo\nfunc (b *BPF) GetArtifacts(conf *config.Config) error {\n\n\tbuf := &bytes.Buffer{}\n\tisDefaultURLUsed := false\n\tplatform, err := GetPlatform()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to identify platform type: %w\", err)\n\t}\n\n\tRepoURL := b.Program.EPRURL\n\tif len(b.Program.EPRURL) == 0 {\n\t\tRepoURL = conf.EBPFRepoURL\n\t\tisDefaultURLUsed = true\n\t}\n\n\t_, err = url.Parse(RepoURL)\n\tif err != nil {\n\t\tif isDefaultURLUsed {\n\t\t\treturn fmt.Errorf(\"unknown ebpf-repo format : %w\", err)\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"unknown ebpf_package_repo_url format : %w\", err)\n\t\t}\n\t}\n\n\turlpath, err := url.JoinPath(RepoURL, b.Program.Name, b.Program.Version, platform, b.Program.Artifact)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"not able to join the artifact path %w\", err)\n\t}\n\tlog.Info().Msgf(\"Retrieving artifact - %s\", urlpath)\n\terr = DownloadArtifact(urlpath, conf.HttpClientTimeout, buf)\n\tif err != nil {\n\t\treturn err\n\t}\n\ttempDir := filepath.Join(conf.BPFDir, b.Program.Name, b.Program.Version)\n\terr = ExtractArtifact(b.Program.Artifact, buf, tempDir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to extract artifact %w\", err)\n\t}\n\tnewDir := strings.Split(b.Program.Artifact, \".\")\n\tb.FilePath = filepath.Join(tempDir, newDir[0])\n\treturn nil\n}\n\n// create rules file\nfunc (b *BPF) createUpdateRulesFile(direction string) (string, error) {\n\n\tif len(b.Program.RulesFile) < 1 {\n\t\treturn \"\", fmt.Errorf(\"RulesFile name is empty\")\n\t}\n\n\tfileName := path.Join(b.FilePath, direction, b.Program.RulesFile)\n\n\tif err := os.WriteFile(fileName, []byte(b.Program.Rules), 0644); err != nil {\n\t\treturn \"\", fmt.Errorf(\"create or Update Rules File failed with error %w\", err)\n\t}\n\n\treturn fileName, nil\n\n}\n\n// fileExists checks if a file exists or not\nfunc fileExists(filename string) bool {\n\tinfo, err := os.Stat(filename)\n\tif os.IsNotExist(err) {\n\t\treturn false\n\t}\n\treturn err == nil && !info.IsDir()\n}\n\n// Add eBPF map into BPFMaps list\nfunc (b *BPF) AddBPFMap(mapName string) error {\n\tbpfMap, err := b.GetBPFMap(mapName)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tb.BpfMaps[mapName] = *bpfMap\n\treturn nil\n}\n\nfunc (b *BPF) GetBPFMap(mapName string) (*BPFMap, error) {\n\n\tif b.ProgMapCollection == nil {\n\t\treturn nil, fmt.Errorf(\"no handle to prog map collection\")\n\t}\n\tebpfMap, ok := b.ProgMapCollection.Maps[mapName]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"not found\")\n\t}\n\n\tebpfInfo, err := ebpfMap.Info()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fetching map info failed %w\", err)\n\t}\n\n\ttempMapID, ok := ebpfInfo.ID()\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"fetching map id failed %w\", err)\n\t}\n\n\tnewBPFMap := BPFMap{\n\t\tName:    mapName,\n\t\tMapID:   tempMapID,\n\t\tType:    ebpfInfo.Type,\n\t\tBPFProg: b,\n\t}\n\n\tlog.Info().Msgf(\"added mapID %d Name %s Type %s\", newBPFMap.MapID, newBPFMap.Name, newBPFMap.Type)\n\treturn &newBPFMap, nil\n}\n\n// Add eBPF map into BPFMaps list\nfunc (b *BPF) AddMetricsBPFMap(mapName, aggregator string, key, samplesLength int) error {\n\tvar tmpMetricsBPFMap MetricsBPFMap\n\tbpfMap, err := b.GetBPFMap(mapName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"program %s metrics map %s not found : %w\", b.Program.Name, mapName, err)\n\t}\n\n\ttmpMetricsBPFMap.BPFMap = *bpfMap\n\ttmpMetricsBPFMap.Key = key\n\ttmpMetricsBPFMap.Aggregator = aggregator\n\ttmpMetricsBPFMap.Values = ring.New(samplesLength)\n\n\tlog.Info().Msgf(\"added Metrics map ID %d Name %s Type %s Key %d Aggregator %s\", tmpMetricsBPFMap.MapID, tmpMetricsBPFMap.Name, tmpMetricsBPFMap.Type, tmpMetricsBPFMap.Key, tmpMetricsBPFMap.Aggregator)\n\tmap_key := mapName + strconv.Itoa(key) + aggregator\n\tb.MetricsBpfMaps[map_key] = &tmpMetricsBPFMap\n\n\treturn nil\n}\n\n// This method to fetch values from bpf maps and publish to metrics\nfunc (b *BPF) MonitorMaps(ifaceName, ipv4_address string, intervals int) error {\n\tfor _, element := range b.Program.MonitorMaps {\n\t\tlog.Debug().Msgf(\"monitor maps element %s key %d aggregator %s\", element.Name, element.Key, element.Aggregator)\n\t\tmapKey := element.Name + strconv.Itoa(element.Key) + element.Aggregator\n\t\t_, ok := b.MetricsBpfMaps[mapKey]\n\t\tif !ok {\n\t\t\tif err := b.AddMetricsBPFMap(element.Name, element.Aggregator, element.Key, intervals); err != nil {\n\t\t\t\treturn fmt.Errorf(\"unable to fetch map %s key %d aggregator %s : %w\", element.Name, element.Key, element.Aggregator, err)\n\t\t\t}\n\t\t}\n\t\tbpfMap := b.MetricsBpfMaps[mapKey]\n\t\tMetricName := element.Name + \"_\" + strconv.Itoa(element.Key) + \"_\" + element.Aggregator\n\t\tstats.SetValue(bpfMap.GetValue(), stats.BPFMonitorMap, b.Program.Name, MetricName, ifaceName, ipv4_address)\n\t}\n\treturn nil\n}\n\n// Updating next program FD from program ID\nfunc (b *BPF) PutNextProgFDFromID(progID int) error {\n\tif len(b.Program.MapName) == 0 {\n\t\t// no chaining map\n\t\treturn nil\n\t}\n\n\tlog.Info().Msgf(\"PutNextProgFDFromID : Map Name %s ID %d\", b.Program.MapName, progID)\n\tebpfMap, err := ebpf.NewMapFromID(b.ProgMapID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to access pinned next prog map %s %w\", b.Program.MapName, err)\n\t}\n\tdefer ebpfMap.Close()\n\n\tbpfProg, err := ebpf.NewProgramFromID(ebpf.ProgramID(progID))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get next prog FD from ID for program %s %w\", b.Program.Name, err)\n\t}\n\tkey := 0\n\tfd := bpfProg.FD()\n\tlog.Info().Msgf(\"PutNextProgFDFromID : Map Name %s FD %d\", b.Program.MapName, fd)\n\tif err = ebpfMap.Update(unsafe.Pointer(&key), unsafe.Pointer(&fd), 0); err != nil {\n\t\treturn fmt.Errorf(\"unable to update prog next map %s %w\", b.Program.MapName, err)\n\t}\n\treturn nil\n}\n\n// GetProgID - This returns ID of the bpf program\nfunc (b *BPF) GetProgID() (ebpf.ProgramID, error) {\n\n\tebpfMap, err := ebpf.LoadPinnedMap(b.PrevMapNamePath, &ebpf.LoadPinOptions{ReadOnly: true})\n\tif err != nil {\n\t\tlog.Error().Err(err).Msgf(\"unable to access pinned prog map %s\", b.PrevMapNamePath)\n\t\treturn 0, fmt.Errorf(\"unable to access pinned prog map %s %w\", b.PrevMapNamePath, err)\n\t}\n\tdefer ebpfMap.Close()\n\tvar value ebpf.ProgramID\n\tkey := 0\n\n\tif err = ebpfMap.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil {\n\t\tlog.Warn().Err(err).Msgf(\"unable to look up prog map %s\", b.PrevMapNamePath)\n\t\treturn 0, fmt.Errorf(\"unable to look up prog map %w\", err)\n\t}\n\n\t// verify progID before storing in locally.\n\t_, err = ebpf.NewProgramFromID(ebpf.ProgramID(value))\n\tif err != nil {\n\t\tlog.Warn().Err(err).Msgf(\"failed to verify program ID %s\", b.PrevMapNamePath)\n\t\treturn 0, fmt.Errorf(\"failed to verify program ID %s %w\", b.Program.Name, err)\n\t}\n\n\tlog.Info().Msgf(\"GetProgID - Name %s PrevMapName %s ID %d\", b.Program.Name, b.PrevMapNamePath, value)\n\treturn value, nil\n}\n\n// RemoveNextProgFD Delete the entry if its last program in the chain.\n// This method is called when sequence of the program changed to last in the chain\nfunc (b *BPF) RemoveNextProgFD() error {\n\tif len(b.Program.MapName) == 0 {\n\t\t// no chaining map in case of root programs\n\t\treturn nil\n\t}\n\n\tebpfMap, err := ebpf.NewMapFromID(b.PrevProgMapID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to access pinned next prog map %s %w\", b.Program.MapName, err)\n\t}\n\tdefer ebpfMap.Close()\n\tkey := 0\n\n\tif err := ebpfMap.Delete(unsafe.Pointer(&key)); err != nil {\n\t\treturn fmt.Errorf(\"failed to delete prog fd entry : %w\", err)\n\t}\n\treturn nil\n}\n\n// RemovePrevProgFD Delete the entry if the last element\nfunc (b *BPF) RemovePrevProgFD() error {\n\tebpfMap, err := ebpf.NewMapFromID(b.PrevProgMapID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to access pinned prev prog map %s %w\", b.PrevMapNamePath, err)\n\t}\n\tdefer ebpfMap.Close()\n\tkey := 0\n\n\tif err := ebpfMap.Delete(unsafe.Pointer(&key)); err != nil {\n\t\t// Some cases map may be empty ignore it.\n\t\tlog.Debug().Err(err).Msg(\"RemovePrevProgFD failed\")\n\t}\n\treturn nil\n}\n\n// VerifyPinnedProgMap - making sure program fd map's pinned file is created if exists flag is true\nfunc (b *BPF) VerifyPinnedProgMap(chain, exists bool) error {\n\tif !chain {\n\t\treturn nil\n\t}\n\tvar err error\n\tif len(b.Program.MapName) > 0 {\n\t\tlog.Debug().Msgf(\"VerifyPinnedMap : Program %s MapName %s Exists %t\", b.Program.Name, b.Program.MapName, exists)\n\n\t\tfor i := 0; i < 10; i++ {\n\t\t\t_, err = os.Stat(b.MapNamePath)\n\t\t\tif err == nil && exists {\n\t\t\t\tlog.Info().Msgf(\"VerifyPinnedProgMap creation : map file created %s\", b.MapNamePath)\n\t\t\t\treturn nil\n\t\t\t} else if err != nil && !exists {\n\t\t\t\tif _, err = os.Stat(b.MapNamePath); os.IsNotExist(err) {\n\t\t\t\t\tlog.Info().Msgf(\"VerifyPinnedProgMap removal : map file removed successfully - %s \", b.MapNamePath)\n\t\t\t\t\treturn nil\n\t\t\t\t} else if err != nil {\n\t\t\t\t\tlog.Warn().Err(err).Msg(\"VerifyPinnedProgMap removal : Error checking for map file\")\n\t\t\t\t} else {\n\t\t\t\t\tlog.Warn().Msg(\"VerifyPinnedProgMap removal : program pinned file still exists, checking again after a second\")\n\t\t\t\t}\n\t\t\t}\n\t\t\ttime.Sleep(1 * time.Second)\n\t\t}\n\n\t\tif err != nil && exists {\n\t\t\terr = fmt.Errorf(\"VerifyPinnedProgMap creation : failed to find pinned file %s err %w\", b.MapNamePath, err)\n\t\t\tlog.Error().Err(err).Msg(\"\")\n\t\t} else if err != nil {\n\t\t\terr = fmt.Errorf(\"VerifyPinnedProgMap removal : %s map file was never removed by BPF program %s err %w\", b.MapNamePath, b.Program.Name, err)\n\t\t\tlog.Error().Err(err).Msg(\"\")\n\t\t}\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// VerifyProcessObject - This method to verify cmd and process object is populated or not\nfunc (b *BPF) VerifyProcessObject() error {\n\n\tif b.Cmd == nil {\n\t\terr := fmt.Errorf(\"command object is nil - %s\", b.Program.Name)\n\t\tlog.Error().Err(err).Msg(\"command object is nil -\")\n\t\treturn err\n\t}\n\n\tfor i := 0; i < 10; i++ {\n\t\tif b.Cmd.Process != nil {\n\t\t\treturn nil\n\t\t}\n\t\tlog.Warn().Msgf(\"VerifyProcessObject: process object not found, checking again after a second\")\n\t\ttime.Sleep(1 * time.Second)\n\t}\n\terr := fmt.Errorf(\"process object is nil - %s\", b.Program.Name)\n\tlog.Error().Err(err).Msg(\"\")\n\treturn err\n}\n\n// VerifyMetricsMapsVanish - checks for all metrics maps references are removed from the kernel\nfunc (b *BPF) VerifyMetricsMapsVanish() error {\n\n\tfor i := 0; i < 10; i++ {\n\t\tmapExists := false\n\t\tfor _, v := range b.BpfMaps {\n\t\t\t_, err := ebpf.NewMapFromID(v.MapID)\n\t\t\tif err == nil {\n\t\t\t\tlog.Warn().Msgf(\"VerifyMetricsMapsVanish: bpf map reference still exists - %s\", v.Name)\n\t\t\t\tmapExists = true\n\t\t\t}\n\t\t}\n\t\tif !mapExists {\n\t\t\treturn nil\n\t\t}\n\n\t\tlog.Warn().Msgf(\"VerifyMetricsMapsVanish: bpf map reference still exists - %s, checking again after a second\", b.Program.Name)\n\t\ttime.Sleep(1 * time.Second)\n\t}\n\n\terr := fmt.Errorf(\"metrics maps are never removed by Kernel %s\", b.Program.Name)\n\tlog.Error().Err(err).Msg(\"\")\n\treturn err\n}\n\n// UnloadProgram - Unload or detach the program from the interface and close all the program resources\nfunc (b *BPF) UnloadProgram(ifaceName, direction string) error {\n\t// remove pinned files\n\tif err := b.RemovePinnedFiles(ifaceName); err != nil {\n\t\tlog.Error().Err(err).Msgf(\"failed to remove map file for program %s => %s\", ifaceName, b.Program.Name)\n\t}\n\t// Verifying program attached to the interface.\n\t// SeqID will be 0 for root program or any other program without chaining\n\tif b.Program.SeqID == 0 || !b.HostConfig.BpfChainingEnabled {\n\t\tif b.Program.ProgType == models.TCType {\n\t\t\tif utils.CheckTCXSupport() {\n\t\t\t\tif b.Link != nil {\n\t\t\t\t\tif err := b.Link.Close(); err != nil {\n\t\t\t\t\t\tlog.Warn().Msgf(\"removing tc attached program %s failed iface %q direction %s error - %v\", b.Program.Name, ifaceName, direction, err)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tlog.Warn().Msgf(\"attach program %s link file is missing iface %q direction %s\", b.Program.Name, ifaceName, direction)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif err := b.UnloadTCProgram(ifaceName, direction); err != nil {\n\t\t\t\t\tlog.Warn().Msgf(\"removing tc filter failed iface %q direction %s error - %v\", ifaceName, direction, err)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if b.Program.ProgType == models.XDPType {\n\t\t\tif err := b.Link.Close(); err != nil {\n\t\t\t\tlog.Warn().Msgf(\"removing xdp attached program failed iface %q direction %s error - %v\", ifaceName, direction, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, LinkObject := range b.ProbeLinks {\n\t\t(*LinkObject).Close()\n\t}\n\n\t// Release all the resources of the epbf program\n\tif b.ProgMapCollection != nil {\n\t\tb.ProgMapCollection.Close()\n\t}\n\treturn nil\n}\n\n// RemovePinnedFiles - removes all the pinned files\nfunc (b *BPF) RemovePinnedFiles(ifaceName string) error {\n\tif b.ProgMapCollection != nil {\n\t\tfor k, v := range b.ProgMapCollection.Maps {\n\t\t\tvar mapFilename string\n\t\t\tversion := utils.ReplaceDotsWithUnderscores(b.Program.Version)\n\t\t\tif b.Program.ProgType == models.TCType {\n\t\t\t\tmapFilename = filepath.Join(b.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, ifaceName, b.Program.Name, version, k)\n\t\t\t} else {\n\t\t\t\tmapFilename = filepath.Join(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, k)\n\t\t\t}\n\t\t\tif err := v.Unpin(); err != nil {\n\t\t\t\treturn fmt.Errorf(\"BPF program %s prog type %s ifacename %s map %s:failed to pin the map err - %w\",\n\t\t\t\t\tb.Program.Name, b.Program.ProgType, ifaceName, mapFilename, err)\n\t\t\t}\n\t\t}\n\t}\n\t// remove pinned links\n\tif b.Link != nil {\n\t\tif err := b.Link.Unpin(); err != nil {\n\t\t\treturn fmt.Errorf(\"unable to unpin the xdp link for %s with err : %w\", b.Program.Name, err)\n\t\t}\n\t}\n\n\t// remove programs pins\n\tif b.ProgMapCollection != nil {\n\t\tfor _, v := range b.ProgMapCollection.Programs {\n\t\t\tif err := v.Unpin(); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// RemoveRootProgMapFile - removes root pinned prog map file\n// This is invoked if any stale map file persists for root map\nfunc (b *BPF) RemoveRootProgMapFile(ifacename string) error {\n\tvar mapFilename string\n\tversion := utils.ReplaceDotsWithUnderscores(b.Program.Version)\n\tswitch b.Program.ProgType {\n\tcase models.TCType:\n\t\tmapFilename = filepath.Join(b.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, ifacename, b.Program.Name, version, b.Program.MapName)\n\tcase models.XDPType:\n\t\tmapFilename = filepath.Join(b.HostConfig.BpfMapDefaultPath, ifacename, b.Program.Name, version, b.Program.MapName)\n\tdefault:\n\t\tlog.Warn().Msgf(\"RemoveRootProgMapFile: program %s map file %s - unknown type\", b.Program.Name, b.MapNamePath)\n\t\treturn fmt.Errorf(\"removeMapFile: program %s unknown type %s\", b.Program.Name, b.Program.ProgType)\n\t}\n\n\t// codeQL Check\n\tif strings.Contains(mapFilename, \"..\") {\n\t\treturn fmt.Errorf(\"%s contains relative path is not supported - %s\", mapFilename, b.Program.Name)\n\t}\n\n\tif err := os.Remove(mapFilename); err != nil {\n\t\tif !os.IsNotExist(err) {\n\t\t\tlog.Warn().Msgf(\"RemoveRootProgMapFile: %s program type %s map file remove unsuccessfully - %s err - %#v\", b.Program.ProgType, b.Program.Name, mapFilename, err)\n\t\t\treturn fmt.Errorf(\"%s - remove failed with error %w\", mapFilename, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// VerifyCleanupMaps - This method verifies map entries in the fs is removed\nfunc (b *BPF) VerifyCleanupMaps(chain bool) error {\n\t// verify pinned file is removed.\n\tif err := b.VerifyPinnedProgMap(chain, false); err != nil {\n\t\tlog.Error().Err(err).Msgf(\"stop user program - failed to remove pinned file %s\", b.Program.Name)\n\t\treturn fmt.Errorf(\"stop user program - failed to remove pinned file %s : %w\", b.Program.Name, err)\n\t}\n\n\t// Verify all metrics map references are removed from kernel\n\tif err := b.VerifyMetricsMapsVanish(); err != nil {\n\t\tlog.Error().Err(err).Msgf(\"stop user program - failed to remove metric map references %s\", b.Program.Name)\n\t\treturn fmt.Errorf(\"stop user program - failed to remove metric map references %s : %w\", b.Program.Name, err)\n\t}\n\n\treturn nil\n}\n\n// LoadBPFProgram - This method loads the eBPF program natively.\nfunc (b *BPF) LoadBPFProgram(ifaceName string) error {\n\tObjectFile := filepath.Join(b.FilePath, b.Program.ObjectFile)\n\tif _, err := os.Stat(ObjectFile); os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"%s: file doesn't exist\", ObjectFile)\n\t}\n\n\t// Allow the current process to lock memory for eBPF resources.\n\tif err := rlimit.RemoveMemlock(); err != nil {\n\t\tlog.Error().Msgf(\"failed to remove memory lock limits  %#v\", err)\n\t\treturn fmt.Errorf(\"%s: remove rlimit lock failed : %w\", b.Program.Name, err)\n\t}\n\n\tobjSpec, err := ebpf.LoadCollectionSpec(ObjectFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%s: loading collection spec failed - %w\", ObjectFile, err)\n\t}\n\n\tversion := utils.ReplaceDotsWithUnderscores(b.Program.Version)\n\tif err := b.CreatePinDirectories(ifaceName, b.Program.Name, version); err != nil {\n\t\treturn err\n\t}\n\tvar mapPinPath string\n\tif b.Program.ProgType == models.TCType {\n\t\tmapPinPath = filepath.Join(b.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, ifaceName, b.Program.Name, version)\n\t} else if b.Program.ProgType == models.XDPType {\n\t\tmapPinPath = filepath.Join(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version)\n\t}\n\tcollOptions := ebpf.CollectionOptions{\n\t\tMaps: ebpf.MapOptions{\n\t\t\tPinPath: mapPinPath,\n\t\t},\n\t}\n\n\t// Load the BPF program with the updated map options\n\tprg, err := ebpf.NewCollectionWithOptions(objSpec, collOptions)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%s: loading of bpf program failed - %w\", b.Program.Name, err)\n\t}\n\n\t// Persist program handle\n\tb.ProgMapCollection = prg\n\n\tif err := b.LoadBPFProgramProbeTypes(objSpec); err != nil {\n\t\treturn fmt.Errorf(\"LoadBPFProgramProbeTypes failed with error %v \", err)\n\t}\n\n\t//var bpfProg *ebpf.Program\n\tif len(b.Program.EntryFunctionName) > 0 {\n\t\tbpfProg := prg.Programs[b.Program.EntryFunctionName]\n\t\tif bpfProg == nil {\n\t\t\treturn fmt.Errorf(\"%s entry function is not found in the loaded object file of the program %s\", b.Program.EntryFunctionName, b.Program.Name)\n\t\t}\n\n\t\tprogInfo, err := bpfProg.Info()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s: information of bpf program failed : %w\", b.Program.Name, err)\n\t\t}\n\n\t\tok := false\n\t\tb.ProgID, ok = progInfo.ID()\n\t\tif !ok {\n\t\t\tlog.Warn().Msgf(\"Program ID fetch failed: %s\", b.Program.Name)\n\t\t}\n\n\t\t// Initialise metric maps\n\t\tif err := b.InitialiseMetricMaps(); err != nil {\n\t\t\treturn fmt.Errorf(\"initialising metric maps failed %w\", err)\n\t\t}\n\n\t\tif err := b.PinBpfMaps(ifaceName); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// InitialiseMetricMaps - This method initialises all the monitor maps\nfunc (b *BPF) InitialiseMetricMaps() error {\n\tif b.ProgMapCollection == nil {\n\t\tlog.Warn().Msgf(\"prog is not loaded by l3afd\")\n\t\treturn nil\n\t}\n\tfor _, tmpMap := range b.Program.MonitorMaps {\n\t\ttmpMetricsMap := b.ProgMapCollection.Maps[tmpMap.Name]\n\t\tif tmpMetricsMap == nil {\n\t\t\tlog.Error().Msgf(\"%s map is not loaded\", tmpMap.Name)\n\t\t\tcontinue\n\t\t}\n\n\t\tvar err error\n\t\tlog.Debug().Msgf(\"Program - %s map name %s key size %d value size %d\\n\", b.Program.Name, tmpMap.Name, tmpMetricsMap.KeySize(), tmpMetricsMap.ValueSize())\n\t\tif tmpMetricsMap.KeySize() == 1 {\n\t\t\tvar k int8\n\t\t\tswitch tmpMetricsMap.ValueSize() {\n\t\t\tcase 1:\n\t\t\t\tvar v int8\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tcase 2:\n\t\t\t\tvar v int16\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tcase 4:\n\t\t\t\tvar v int32\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tcase 8:\n\t\t\t\tvar v int64\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tdefault:\n\t\t\t\tlog.Error().Msgf(\"unsupported key type int8 and value size - %d\", tmpMetricsMap.ValueSize())\n\t\t\t}\n\t\t} else if tmpMetricsMap.KeySize() == 2 {\n\t\t\tvar k int16\n\t\t\tswitch tmpMetricsMap.ValueSize() {\n\t\t\tcase 1:\n\t\t\t\tvar v int8\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tcase 2:\n\t\t\t\tvar v int16\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tcase 4:\n\t\t\t\tvar v int32\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tcase 8:\n\t\t\t\tvar v int64\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tdefault:\n\t\t\t\tlog.Error().Msgf(\"unsupported map key type int16 and value size - %d\", tmpMetricsMap.ValueSize())\n\t\t\t}\n\t\t} else if tmpMetricsMap.KeySize() == 4 {\n\t\t\tvar k int32\n\t\t\tswitch tmpMetricsMap.ValueSize() {\n\t\t\tcase 1:\n\t\t\t\tvar v int8\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tcase 2:\n\t\t\t\tvar v int16\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tcase 4:\n\t\t\t\tvar v int32\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tcase 8:\n\t\t\t\tvar v int64\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tdefault:\n\t\t\t\tlog.Error().Msgf(\"unsupported map key type int32 and value size - %d\", tmpMetricsMap.ValueSize())\n\t\t\t}\n\t\t} else if tmpMetricsMap.KeySize() == 8 {\n\t\t\tvar k int64\n\t\t\tswitch tmpMetricsMap.ValueSize() {\n\t\t\tcase 1:\n\t\t\t\tvar v int8\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tcase 2:\n\t\t\t\tvar v int16\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tcase 4:\n\t\t\t\tvar v int32\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tcase 8:\n\t\t\t\tvar v int64\n\t\t\t\terr = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)\n\t\t\tdefault:\n\t\t\t\tlog.Error().Msgf(\"unsupported key type int64 and value size - %d\", tmpMetricsMap.ValueSize())\n\t\t\t}\n\t\t}\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"update hash map element failed for map name %s error %v\", tmpMap.Name, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// IsLoaded - Method verifies whether bpf program is loaded or not\n// Here it checks whether prog ID is valid and active\nfunc (b *BPF) IsLoaded() bool {\n\tif b.ProgID == 0 {\n\t\treturn false\n\t}\n\tebpfProg, err := ebpf.NewProgramFromID(b.ProgID)\n\tif err != nil {\n\t\tlog.Debug().Msgf(\"IsLoaded - %s is not loaded or invalid program id %d\", b.Program.Name, uint32(b.ProgID))\n\t\treturn false\n\t}\n\tdefer ebpfProg.Close()\n\treturn true\n}\n\nfunc (b *BPF) StartUserProgram(ifaceName, direction string, chain bool) error {\n\tcmd := filepath.Join(b.FilePath, b.Program.CmdStart)\n\t// Validate\n\tif err := assertExecutable(cmd); err != nil {\n\t\treturn fmt.Errorf(\"no executable permissions on %s - error %w\", b.Program.CmdStart, err)\n\t}\n\n\targs := make([]string, 0, len(b.Program.StartArgs)<<1)\n\tif len(ifaceName) > 0 {\n\t\targs = append(args, \"--iface=\"+ifaceName) // attaching to interface\n\t}\n\tif len(direction) > 0 {\n\t\targs = append(args, \"--direction=\"+direction) // direction xdpingress or ingress or egress\n\t}\n\n\tversion := utils.ReplaceDotsWithUnderscores(b.Program.Version)\n\tif len(b.Program.Version) > 0 {\n\t\targs = append(args, \"--version=\"+version) // version xdpingress or ingress or egress\n\t}\n\n\tif chain && b.ProgMapCollection == nil {\n\t\t// chaining from user program\n\t\tif len(b.PrevMapNamePath) > 1 {\n\t\t\targs = append(args, \"--map-name=\"+b.PrevMapNamePath)\n\t\t}\n\t}\n\n\tif len(b.HostConfig.BPFLogDir) > 1 {\n\t\targs = append(args, \"--log-dir=\"+b.HostConfig.BPFLogDir)\n\t}\n\n\tif len(b.Program.RulesFile) > 1 && len(b.Program.Rules) > 1 {\n\t\tfileName, err := b.createUpdateRulesFile(direction)\n\t\tif err == nil {\n\t\t\targs = append(args, \"--rules-file=\"+fileName)\n\t\t}\n\t}\n\n\tfor k, val := range b.Program.StartArgs {\n\t\tif v, ok := val.(string); !ok {\n\t\t\terr := fmt.Errorf(\"start args is not a string for the bpf program %s\", b.Program.Name)\n\t\t\tlog.Error().Err(err).Msgf(\"failed to convert start args value into string for program %s\", b.Program.Name)\n\t\t\treturn err\n\t\t} else {\n\t\t\targs = append(args, \"--\"+k+\"=\"+v)\n\t\t}\n\t}\n\n\tlog.Info().Msgf(\"BPF Program start command : %s %v\", cmd, args)\n\tb.Cmd = execCommand(cmd, args...)\n\tif err := b.Cmd.Start(); err != nil {\n\t\tlog.Info().Err(err).Msgf(\"user program failed - %s\", b.Program.Name)\n\t\treturn fmt.Errorf(\"failed to start : %s %v with err: %w\", cmd, args, err)\n\t}\n\tif !b.Program.UserProgramDaemon {\n\t\tlog.Info().Msgf(\"no user program - %s No Pid\", b.Program.Name)\n\t\tif err := b.Cmd.Wait(); err != nil {\n\t\t\tlog.Warn().Msgf(\"failed at wait - %s err %s\", b.Program.Name, err.Error())\n\t\t}\n\t\tb.Cmd = nil\n\t} else {\n\t\tif err := b.SetPrLimits(); err != nil {\n\t\t\tlog.Warn().Err(err).Msg(\"failed to set resource limits\")\n\t\t}\n\t\tlog.Info().Msgf(\"BPF program - %s User program Process id %d started\", b.Program.Name, b.Cmd.Process.Pid)\n\t}\n\n\treturn nil\n}\n\n// CreatePinDirectories - This method creates directory for ebpf objects\n// TC maps are pinned to directory /sys/fs/bpf/tc/globals/<ifaceName>\n// XDP maps are pinned to directory /sys/fs/bpf/<ifaceName>\n// links are pinned to directory /sys/fs/bpf/links/<ifaceName>/<progName>/<progVersion>\n// Program are pinned to directory  /sys/fs/bpf/progs/<ifaceName>/<progName>/<progVersion>\nfunc (b *BPF) CreatePinDirectories(ifaceName, progName, progVersion string) error {\n\tvar mapPathDir string\n\tif b.Program.ProgType == models.XDPType {\n\t\tmapPathDir = filepath.Join(b.HostConfig.BpfMapDefaultPath, ifaceName, progName, progVersion)\n\t} else if b.Program.ProgType == models.TCType {\n\t\tmapPathDir = filepath.Join(b.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, ifaceName, progName, progVersion)\n\t}\n\t// Create map dir for XDP and TC programs only\n\tif len(mapPathDir) > 0 {\n\t\t// codeQL Check\n\t\tif strings.Contains(mapPathDir, \"..\") {\n\t\t\treturn fmt.Errorf(\"%s contains relative path is not supported - %s\", mapPathDir, b.Program.Name)\n\t\t}\n\t\tif err := os.MkdirAll(mapPathDir, 0750); err != nil {\n\t\t\treturn fmt.Errorf(\"%s failed to create map dir path of %s program %s with err : %w\", mapPathDir, b.Program.ProgType, b.Program.Name, err)\n\t\t}\n\t}\n\n\tlinksPathDir := filepath.Join(b.HostConfig.BpfMapDefaultPath, \"links\", ifaceName, progName, progVersion)\n\tif strings.Contains(linksPathDir, \"..\") {\n\t\treturn fmt.Errorf(\"%s contains relative path is not supported - %s\", linksPathDir, b.Program.Name)\n\t}\n\tif err := os.MkdirAll(linksPathDir, 0750); err != nil {\n\t\treturn fmt.Errorf(\"%s failed to create map dir path of %s program %s with err : %w\", linksPathDir, b.Program.ProgType, b.Program.Name, err)\n\t}\n\n\tProgPathDir := filepath.Join(b.HostConfig.BpfMapDefaultPath, \"progs\", ifaceName, progName, progVersion)\n\tif strings.Contains(ProgPathDir, \"..\") {\n\t\treturn fmt.Errorf(\"%s contains relative path is not supported - %s\", ProgPathDir, b.Program.Name)\n\t}\n\tif err := os.MkdirAll(ProgPathDir, 0750); err != nil {\n\t\treturn fmt.Errorf(\"%s failed to create map dir path of %s program %s with err : %w\", ProgPathDir, b.Program.ProgType, b.Program.Name, err)\n\t}\n\treturn nil\n}\n\n// AttachBPFProgram - method to attach bpf program to interface\nfunc (b *BPF) AttachBPFProgram(ifaceName, direction string) error {\n\tversion := utils.ReplaceDotsWithUnderscores(b.Program.Version)\n\tif b.Program.ProgType == models.XDPType {\n\t\tif err := b.LoadXDPAttachProgram(ifaceName); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to attach xdp program %s to inferface %s with err: %w\", b.Program.Name, ifaceName, err)\n\t\t}\n\t\t// pin the program also\n\t\tprogPinPath := utils.ProgPinPath(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, b.Program.EntryFunctionName, b.Program.ProgType)\n\t\tif err := b.ProgMapCollection.Programs[b.Program.EntryFunctionName].Pin(progPinPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else if b.Program.ProgType == models.TCType {\n\t\tif utils.CheckTCXSupport() {\n\t\t\tif err := b.LoadTCXAttachProgram(ifaceName, direction); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to attach tcx program %s to interface %s direction %s with err %w\", b.Program.Name, ifaceName, direction, err)\n\t\t\t}\n\t\t} else {\n\t\t\tif err := b.LoadTCAttachProgram(ifaceName, direction); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to attach tc program %s to interface %s direction %s with err %w\", b.Program.Name, ifaceName, direction, err)\n\t\t\t}\n\t\t}\n\n\t\t// pin the program also\n\t\tprogPinPath := utils.ProgPinPath(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, b.Program.EntryFunctionName, b.Program.ProgType)\n\t\tif err := b.ProgMapCollection.Programs[b.Program.EntryFunctionName].Pin(progPinPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// PinBpfMaps - Pinning tc and xdp maps\nfunc (b *BPF) PinBpfMaps(ifaceName string) error {\n\tversion := utils.ReplaceDotsWithUnderscores(b.Program.Version)\n\tfor k, v := range b.ProgMapCollection.Maps {\n\t\tvar mapFilename string\n\t\t// ebpf programs temporary storage created by eBPF program skip it\n\t\tif k == \".bss\" {\n\t\t\tcontinue\n\t\t}\n\t\tif b.Program.ProgType == models.TCType {\n\t\t\tmapFilename = filepath.Join(b.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, ifaceName, b.Program.Name, version, k)\n\t\t} else {\n\t\t\tmapFilename = filepath.Join(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, k)\n\t\t}\n\t\t// In case one of the program pins the map then other program will skip\n\t\tif !fileExists(mapFilename) {\n\t\t\tif err := v.Pin(mapFilename); err != nil {\n\t\t\t\treturn fmt.Errorf(\"eBPF program %s map %s:failed to pin the map err - %w\", b.Program.Name, mapFilename, err)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// UpdateProgramMap - Store the program map reference\nfunc (b *BPF) UpdateProgramMap(ifaceName string) error {\n\t// Verify chaining map is provided\n\tif len(b.Program.MapName) == 0 {\n\t\treturn fmt.Errorf(\"program map name is missing for %s program %s\", b.Program.ProgType, b.Program.Name)\n\t}\n\n\tbpfRootMap := b.ProgMapCollection.Maps[b.Program.MapName]\n\n\tebpfInfo, err := bpfRootMap.Info()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"fetching map info failed for %s program %s to interface %s : %w\", b.Program.ProgType, b.Program.Name, ifaceName, err)\n\t}\n\n\tvar ok bool\n\tb.ProgMapID, ok = ebpfInfo.ID()\n\tif !ok {\n\t\treturn fmt.Errorf(\"fetching map id failed for %s program %s to interface %s : %w\", b.Program.ProgType, b.Program.Name, ifaceName, err)\n\t}\n\n\treturn nil\n}\n\n// LoadBPFProgramChain - Load the BPF program and chain it.\nfunc (b *BPF) LoadBPFProgramChain(ifaceName, direction string) error {\n\n\tif err := b.LoadBPFProgram(ifaceName); err != nil {\n\t\treturn err\n\t}\n\n\tversion := utils.ReplaceDotsWithUnderscores(b.Program.Version)\n\t// pin the program also\n\tprogPinPath := utils.ProgPinPath(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, b.Program.EntryFunctionName, b.Program.ProgType)\n\tif err := b.ProgMapCollection.Programs[b.Program.EntryFunctionName].Pin(progPinPath); err != nil {\n\t\treturn err\n\t}\n\n\t// Update program map id\n\tif err := b.UpdateProgramMap(ifaceName); err != nil {\n\t\treturn err\n\t}\n\n\t// Link this program into previous program map\n\tebpfMap, err := ebpf.NewMapFromID(b.PrevProgMapID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to access pinned previous prog map %s %w\", b.PrevMapNamePath, err)\n\t}\n\tdefer ebpfMap.Close()\n\n\tbpfProg := b.ProgMapCollection.Programs[b.Program.EntryFunctionName]\n\tif bpfProg == nil {\n\t\treturn fmt.Errorf(\"%s entry function is not found in the loaded object file of the program %s\", b.Program.EntryFunctionName, b.Program.Name)\n\t}\n\n\tkey := 0\n\tfd := bpfProg.FD()\n\tlog.Info().Msgf(\"previous program map path %s FD %d\", b.PrevMapNamePath, fd)\n\tif err = ebpfMap.Update(unsafe.Pointer(&key), unsafe.Pointer(&fd), 0); err != nil {\n\t\treturn fmt.Errorf(\"unable to update prog next map %s %v\", b.Program.MapName, err)\n\t}\n\tlog.Info().Msgf(\"eBPF program %s loaded on interface %s direction %s successfully\", b.Program.Name, ifaceName, direction)\n\treturn nil\n}\n\nfunc (b *BPF) StopUserProgram(ifaceName, direction string) error {\n\t// Stop User Programs if any\n\tif len(b.Program.CmdStop) < 1 && b.Program.UserProgramDaemon {\n\t\t// Loaded using user program\n\t\tif err := b.ProcessTerminate(); err != nil {\n\t\t\treturn fmt.Errorf(\"BPFProgram %s process terminate failed with error: %w\", b.Program.Name, err)\n\t\t}\n\t\tif b.Cmd != nil {\n\t\t\tif err := b.Cmd.Wait(); err != nil {\n\t\t\t\tlog.Error().Err(err).Msgf(\"cmd wait at stopping bpf program %s errored\", b.Program.Name)\n\t\t\t}\n\t\t\tb.Cmd = nil\n\t\t}\n\t} else if len(b.Program.CmdStop) > 0 && b.Program.UserProgramDaemon {\n\t\tcmd := filepath.Join(b.FilePath, b.Program.CmdStop)\n\n\t\tif err := assertExecutable(cmd); err != nil {\n\t\t\treturn fmt.Errorf(\"no executable permissions on %s - error %w\", b.Program.CmdStop, err)\n\t\t}\n\n\t\targs := make([]string, 0, len(b.Program.StopArgs)<<1)\n\t\tif len(ifaceName) > 0 {\n\t\t\targs = append(args, \"--iface=\"+ifaceName) // detaching from iface\n\t\t}\n\t\tif len(direction) > 0 {\n\t\t\targs = append(args, \"--direction=\"+direction) // xdpingress or ingress or egress\n\t\t}\n\t\tfor k, val := range b.Program.StopArgs {\n\t\t\tif v, ok := val.(string); !ok {\n\t\t\t\terr := fmt.Errorf(\"stop args is not a string for the bpf program %s\", b.Program.Name)\n\t\t\t\tlog.Error().Err(err).Msgf(\"failed to convert stop args value into string for program %s\", b.Program.Name)\n\t\t\t\treturn err\n\t\t\t} else {\n\t\t\t\targs = append(args, \"--\"+k+\" =\"+v)\n\t\t\t}\n\t\t}\n\t\tlog.Info().Msgf(\"bpf user program stop command : %s %v\", cmd, args)\n\t\tprog := execCommand(cmd, args...)\n\t\tif err := prog.Run(); err != nil {\n\t\t\tlog.Warn().Err(err).Msgf(\"l3afd : Failed to stop the user program %s\", b.Program.CmdStop)\n\t\t}\n\t\tb.Cmd = nil\n\t}\n\treturn nil\n}\n\n// DownloadArtifact will download artifact from provided urlpath and store it in buffer\nfunc DownloadArtifact(urlpath string, timeout time.Duration, buf *bytes.Buffer) error {\n\tURL, err := url.Parse(urlpath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unknown url format : %w\", err)\n\t}\n\tswitch URL.Scheme {\n\tcase models.HttpScheme, models.HttpsScheme:\n\t\t{\n\t\t\ttimeOut := time.Duration(timeout) * time.Second\n\t\t\tvar netTransport = &http.Transport{\n\t\t\t\tResponseHeaderTimeout: timeOut,\n\t\t\t}\n\t\t\tclient := http.Client{Transport: netTransport, Timeout: timeOut}\n\t\t\t// Get the data\n\t\t\tresp, err := client.Get(URL.String())\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"download failed: %w\", err)\n\t\t\t}\n\t\t\tdefer resp.Body.Close()\n\n\t\t\tif resp.StatusCode != http.StatusOK {\n\t\t\t\treturn fmt.Errorf(\"get request returned unexpected status code: %d (%s), %d was expected\\n\\tResponse Body: %s\", resp.StatusCode, http.StatusText(resp.StatusCode), http.StatusOK, buf.Bytes())\n\t\t\t}\n\t\t\tbuf.ReadFrom(resp.Body)\n\t\t\treturn nil\n\t\t}\n\tcase models.FileScheme:\n\t\t{\n\t\t\tif fileExists(URL.Path) {\n\t\t\t\tf, err := os.Open(URL.Path)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"opening err : %w\", err)\n\t\t\t\t}\n\t\t\t\tbuf.ReadFrom(f)\n\t\t\t\tf.Close()\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"artifact is not found\")\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown url scheme\")\n\t}\n}\n\n// ExtractArtifact will extract artifact to given tempDir\nfunc ExtractArtifact(artifactName string, buf *bytes.Buffer, tempDir string) error {\n\tswitch artifact := artifactName; {\n\tcase strings.HasSuffix(artifact, \".zip\"):\n\t\t{\n\t\t\tc := bytes.NewReader(buf.Bytes())\n\t\t\tzipReader, err := zip.NewReader(c, int64(c.Len()))\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to create zip reader: %w\", err)\n\t\t\t}\n\t\t\tfor _, file := range zipReader.File {\n\n\t\t\t\tzippedFile, err := file.Open()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"unzip failed: %w\", err)\n\t\t\t\t}\n\t\t\t\tdefer zippedFile.Close()\n\n\t\t\t\textractedFilePath, err := ValidatePath(file.Name, tempDir)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tif file.FileInfo().IsDir() {\n\t\t\t\t\tos.MkdirAll(extractedFilePath, file.Mode())\n\t\t\t\t} else {\n\t\t\t\t\toutputFile, err := os.OpenFile(\n\t\t\t\t\t\textractedFilePath,\n\t\t\t\t\t\tos.O_WRONLY|os.O_CREATE|os.O_TRUNC,\n\t\t\t\t\t\tfile.Mode(),\n\t\t\t\t\t)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"unzip failed to create file: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t\tdefer outputFile.Close()\n\n\t\t\t\t\tbuf := copyBufPool.Get().(*bytes.Buffer)\n\t\t\t\t\t_, err = io.CopyBuffer(outputFile, zippedFile, buf.Bytes())\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"GetArtifacts failed to copy files: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t\tcopyBufPool.Put(buf)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\tcase strings.HasSuffix(artifact, \".tar.gz\"):\n\t\t{\n\t\t\tarchive, err := gzip.NewReader(buf)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to create Gzip reader: %w\", err)\n\t\t\t}\n\t\t\tdefer archive.Close()\n\t\t\ttarReader := tar.NewReader(archive)\n\n\t\t\tfor {\n\t\t\t\theader, err := tarReader.Next()\n\n\t\t\t\tif err == io.EOF {\n\t\t\t\t\tbreak\n\t\t\t\t} else if err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"untar failed: %w\", err)\n\t\t\t\t}\n\n\t\t\t\tfPath, err := ValidatePath(header.Name, tempDir)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tinfo := header.FileInfo()\n\t\t\t\tif info.IsDir() {\n\t\t\t\t\tif err = os.MkdirAll(fPath, info.Mode()); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"untar failed to create directories: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tfile, err := os.OpenFile(fPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"untar failed to create file: %w\", err)\n\t\t\t\t}\n\t\t\t\tdefer file.Close()\n\n\t\t\t\tbuf := copyBufPool.Get().(*bytes.Buffer)\n\t\t\t\t_, err = io.CopyBuffer(file, tarReader, buf.Bytes())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"GetArtifacts failed to copy files: %w\", err)\n\t\t\t\t}\n\t\t\t\tcopyBufPool.Put(buf)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown artifact format\")\n\t}\n}\n\n// ValidatePath will validate any illegal file path\nfunc ValidatePath(filePath string, destination string) (string, error) {\n\tdestpath := filepath.Join(destination, filePath)\n\tif strings.Contains(filePath, \"..\") {\n\t\treturn \"\", fmt.Errorf(\" file contains filepath (%s) that includes (..)\", filePath)\n\t}\n\tif !strings.HasPrefix(destpath, filepath.Clean(destination)+string(os.PathSeparator)) {\n\t\treturn \"\", fmt.Errorf(\"%s: illegal file path\", filePath)\n\t}\n\treturn destpath, nil\n}\n"
  },
  {
    "path": "bpfprogs/bpfCfgs_internal.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !configs\n// +build !configs\n\n// This file is used for walmart internal to run BPF specific configs.\n// We will be removing this file in future.\n\npackage bpfprogs\n\nimport (\n\t\"github.com/rs/zerolog/log\"\n)\n\nfunc (b *BPF) RunBPFConfigs() error {\n\tlog.Warn().Msg(\"Implement custom BPF specific configs\")\n\treturn nil\n}\n"
  },
  {
    "path": "bpfprogs/bpf_test.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"archive/tar\"\n\t\"archive/zip\"\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/l3af-project/l3afd/v2/config\"\n\t\"github.com/l3af-project/l3afd/v2/mocks\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/l3af-project/l3afd/v2/stats\"\n\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/rs/zerolog/log\"\n\t\"go.uber.org/mock/gomock\"\n)\n\nvar mockedExitStatus = 1\nvar mockPid = 77\n\nfunc fakeExecCommand(command string, args ...string) *exec.Cmd {\n\tcs := []string{\"-test.run=TestHelperProcess\", \"--\", command}\n\tcs = append(cs, args...)\n\tcmd := exec.Command(os.Args[0], cs...)\n\tcmd.Env = []string{\"GO_WANT_HELPER_PROCESS=1\"}\n\tcmd.Process = &os.Process{Pid: mockPid}\n\treturn cmd\n}\n\nfunc TestHelperProcess(t *testing.T) {\n\tif os.Getenv(\"GO_WANT_HELPER_PROCESS\") != \"1\" {\n\t\treturn\n\t}\n\tos.Exit(mockedExitStatus)\n}\n\nfunc TestNewBpfProgram(t *testing.T) {\n\ttype args struct {\n\t\tprogram    models.BPFProgram\n\t\tlogDir     string\n\t\tchain      bool\n\t\tdirection  string\n\t\tctx        context.Context\n\t\tdatacenter string\n\t\thostConfig *config.Config\n\t}\n\texecCommand = fakeExecCommand\n\tdefer func() { execCommand = exec.Command }()\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant *BPF\n\t}{\n\t\t{name: \"GoodInput\",\n\t\t\targs: args{\n\t\t\t\tprogram: models.BPFProgram{\n\t\t\t\t\tName:              \"nfprogram\",\n\t\t\t\t\tArtifact:          \"foo.tar.gz\",\n\t\t\t\t\tCmdStart:          \"foo\",\n\t\t\t\t\tCmdStop:           \"\",\n\t\t\t\t\tCmdConfig:         \"\",\n\t\t\t\t\tVersion:           \"1.0\",\n\t\t\t\t\tUserProgramDaemon: true,\n\t\t\t\t\tIsPlugin:          false,\n\t\t\t\t\tAdminStatus:       \"enabled\",\n\t\t\t\t\tProgType:          \"tc\",\n\t\t\t\t},\n\t\t\t\tlogDir:     \"\",\n\t\t\t\tchain:      false,\n\t\t\t\tdirection:  \"ingress\",\n\t\t\t\tdatacenter: \"localdc\",\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBPFLogDir:  \"\",\n\t\t\t\t\tDataCenter: \"localdc\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: &BPF{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tName:              \"nfprogram\",\n\t\t\t\t\tArtifact:          \"foo.tar.gz\",\n\t\t\t\t\tCmdStart:          \"foo\",\n\t\t\t\t\tCmdStop:           \"\",\n\t\t\t\t\tCmdConfig:         \"\",\n\t\t\t\t\tCmdUpdate:         \"\",\n\t\t\t\t\tVersion:           \"1.0\",\n\t\t\t\t\tUserProgramDaemon: true,\n\t\t\t\t\tIsPlugin:          false,\n\t\t\t\t\tAdminStatus:       \"enabled\",\n\t\t\t\t\tProgType:          \"tc\",\n\t\t\t\t},\n\t\t\t\tCmd:            nil,\n\t\t\t\tFilePath:       \"\",\n\t\t\t\tBpfMaps:        make(map[string]BPFMap, 0),\n\t\t\t\tMetricsBpfMaps: make(map[string]*MetricsBPFMap, 0),\n\t\t\t\tCtx:            nil,\n\t\t\t\tDone:           nil,\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tBPFLogDir:  \"\",\n\t\t\t\t\tDataCenter: \"localdc\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{name: \"EmptyBPFProgram\",\n\t\t\targs: args{\n\t\t\t\tprogram:    models.BPFProgram{},\n\t\t\t\thostConfig: &config.Config{},\n\t\t\t},\n\t\t\twant: &BPF{\n\t\t\t\tProgram:        models.BPFProgram{},\n\t\t\t\tCmd:            nil,\n\t\t\t\tFilePath:       \"\",\n\t\t\t\tBpfMaps:        make(map[string]BPFMap, 0),\n\t\t\t\tMetricsBpfMaps: make(map[string]*MetricsBPFMap, 0),\n\t\t\t\tHostConfig:     &config.Config{},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := NewBpfProgram(tt.args.ctx, tt.args.program, tt.args.hostConfig, ifaceName); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewBpfProgram() = %#v, want %#v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBPF_Stop(t *testing.T) {\n\ttype fields struct {\n\t\tProgram      models.BPFProgram\n\t\tCmd          *exec.Cmd\n\t\tFilePath     string\n\t\tRestartCount int\n\t\tDirection    string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\twantErr bool\n\t}{\n\t\t{name: \"NilCmd\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tName:              \"nfprogram\",\n\t\t\t\t\tArtifact:          \"foo.tar.gz\",\n\t\t\t\t\tCmdStart:          \"foo\",\n\t\t\t\t\tCmdStop:           \"\",\n\t\t\t\t\tVersion:           \"1.0\",\n\t\t\t\t\tUserProgramDaemon: true,\n\t\t\t\t\tAdminStatus:       \"enabled\",\n\t\t\t\t},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"/tmp/dummy/dummy\",\n\t\t\t\tRestartCount: 3,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{name: \"WithStopCmd\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tName:              \"nfprogram\",\n\t\t\t\t\tArtifact:          \"foo.tar.gz\",\n\t\t\t\t\tCmdStart:          \"foo\",\n\t\t\t\t\tCmdStop:           \"foo\",\n\t\t\t\t\tVersion:           \"1.0\",\n\t\t\t\t\tUserProgramDaemon: true,\n\t\t\t\t\tAdminStatus:       \"enabled\",\n\t\t\t\t},\n\t\t\t\tCmd:          fakeExecCommand(\"/tmp/dummy/foo\"),\n\t\t\t\tFilePath:     \"/tmp/dummy/dummy\",\n\t\t\t\tRestartCount: 3,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{name: \"AnyBinaryFile\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tName:              \"nfprogram\",\n\t\t\t\t\tArtifact:          \"data.tar.gz\",\n\t\t\t\t\tCmdStart:          GetTestExecutableName(),\n\t\t\t\t\tCmdStop:           GetTestExecutableName(),\n\t\t\t\t\tUserProgramDaemon: false,\n\t\t\t\t\tAdminStatus:       \"enabled\",\n\t\t\t\t},\n\t\t\t\tCmd:          fakeExecCommand(GetTestExecutablePathName()),\n\t\t\t\tFilePath:     GetTestExecutablePath(),\n\t\t\t\tRestartCount: 3,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := &BPF{\n\t\t\t\tProgram:      tt.fields.Program,\n\t\t\t\tCmd:          tt.fields.Cmd,\n\t\t\t\tFilePath:     tt.fields.FilePath,\n\t\t\t\tRestartCount: tt.fields.RestartCount,\n\t\t\t}\n\n\t\t\t// Create the metric with expected labels\n\t\t\tstats.BPFRunning = prometheus.NewGaugeVec(\n\t\t\t\tprometheus.GaugeOpts{\n\t\t\t\t\tName: \"l3afd_BPFRunning\",\n\t\t\t\t\tHelp: \"Indicates whether eBPF program is running\",\n\t\t\t\t},\n\t\t\t\t[]string{\"program\", \"version\", \"direction\", \"iface\"},\n\t\t\t)\n\n\t\t\tif err := prometheus.Register(stats.BPFRunning); err != nil {\n\t\t\t\tlog.Warn().Err(err).Msg(\"Failed to register BPFRunning metrics\")\n\t\t\t}\n\n\t\t\t// Simulate program load\n\t\t\tstats.BPFRunning.With(prometheus.Labels{\n\t\t\t\t\"program\":   b.Program.Name,\n\t\t\t\t\"version\":   b.Program.Version,\n\t\t\t\t\"direction\": \"ingress\",\n\t\t\t\t\"iface\":     \"eth0\",\n\t\t\t}).Set(1)\n\n\t\t\tstats.BPFStartTime = prometheus.NewGaugeVec(\n\t\t\t\tprometheus.GaugeOpts{\n\t\t\t\t\tNamespace: \"l3afd\",\n\t\t\t\t\tName:      \"BPFStartTime\",\n\t\t\t\t\tHelp:      \"This value indicates start time of the BPF program since unix epoch in seconds\",\n\t\t\t\t},\n\t\t\t\t[]string{\"host\", \"ebpf_program\", \"direction\", \"interface_name\"},\n\t\t\t)\n\n\t\t\tif err := prometheus.Register(stats.BPFStartTime); err != nil {\n\t\t\t\tlog.Warn().Err(err).Msg(\"Failed to register BPFStartTime metrics\")\n\t\t\t}\n\n\t\t\tif err := b.Stop(ifaceName, \"\", models.IngressType, false); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"BPF.Stop() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBPF_Start(t *testing.T) {\n\ttype fields struct {\n\t\tProgram      models.BPFProgram\n\t\tCmd          *exec.Cmd\n\t\tFilePath     string\n\t\tRestartCount int\n\t\tifaceName    string\n\t\thostConfig   *config.Config\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\twantErr bool\n\t}{\n\t\t{name: \"NoFilePath\",\n\t\t\tfields: fields{\n\t\t\t\tProgram:      models.BPFProgram{},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBPFLogDir: \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{name: \"AnyBinary\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tName:              \"nfprogram\",\n\t\t\t\t\tArtifact:          \"data.tar.gz\",\n\t\t\t\t\tCmdStart:          GetTestExecutableName(),\n\t\t\t\t\tCmdStop:           GetTestExecutableName(),\n\t\t\t\t\tUserProgramDaemon: true,\n\t\t\t\t\tAdminStatus:       \"enabled\",\n\t\t\t\t},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     GetTestExecutablePath(),\n\t\t\t\tRestartCount: 0,\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBPFLogDir: \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{name: \"UserProgramFalse\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tName:              \"nfprogram\",\n\t\t\t\t\tArtifact:          \"data.tar.gz\",\n\t\t\t\t\tCmdStart:          GetTestExecutableName(),\n\t\t\t\t\tCmdStop:           GetTestExecutableName(),\n\t\t\t\t\tUserProgramDaemon: false,\n\t\t\t\t\tAdminStatus:       \"enabled\",\n\t\t\t\t},\n\t\t\t\tCmd:          fakeExecCommand(GetTestExecutablePathName()),\n\t\t\t\tFilePath:     GetTestExecutablePath(),\n\t\t\t\tRestartCount: 0,\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBPFLogDir: \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := &BPF{\n\t\t\t\tProgram:      tt.fields.Program,\n\t\t\t\tCmd:          tt.fields.Cmd,\n\t\t\t\tFilePath:     tt.fields.FilePath,\n\t\t\t\tRestartCount: tt.fields.RestartCount,\n\t\t\t\tHostConfig:   tt.fields.hostConfig,\n\t\t\t}\n\t\t\tif err := b.Start(tt.fields.ifaceName, \"\", models.IngressType, true); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"BPF.Start() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBPF_isRunning(t *testing.T) {\n\ttype fields struct {\n\t\tProgram      models.BPFProgram\n\t\tCmd          *exec.Cmd\n\t\tFilePath     string\n\t\tRestartCount int\n\t\tCmdStatus    string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\twant    bool\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"NoPID\",\n\t\t\tfields: fields{\n\t\t\t\tProgram:      models.BPFProgram{},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t},\n\t\t\twant:    false,\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := &BPF{\n\t\t\t\tProgram:      tt.fields.Program,\n\t\t\t\tCmd:          tt.fields.Cmd,\n\t\t\t\tFilePath:     tt.fields.FilePath,\n\t\t\t\tRestartCount: tt.fields.RestartCount,\n\t\t\t}\n\t\t\tuserProg, bpfProg, err := b.isRunning()\n\t\t\tif (err != nil) != tt.wantErr && (userProg == tt.want || bpfProg != tt.want) {\n\t\t\t\tt.Errorf(\"BPF.isRunning() user prog = %v, bpf prog = %v, error = %v, wantErr %v \", userProg, bpfProg, err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// RoundTripFunc .\ntype RoundTripFunc func(req *http.Request) *http.Response\n\n// RoundTrip .\nfunc (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {\n\treturn f(req), nil\n}\n\n// NewTestClient returns *http.Client with Transport replaced to avoid making real calls\nfunc NewTestClient(fn RoundTripFunc) *http.Client {\n\treturn &http.Client{\n\t\tTransport: RoundTripFunc(fn),\n\t}\n}\nfunc TestBPF_GetArtifacts(t *testing.T) {\n\n\ttype fields struct {\n\t\tProgram      models.BPFProgram\n\t\tCmd          *exec.Cmd\n\t\tFilePath     string\n\t\tRestartCount int\n\t\tClient       *http.Client\n\t}\n\ttype args struct {\n\t\tconf *config.Config\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\targs    args\n\t\twantErr bool\n\t}{\n\t\t{name: \"EmptyArtifact\",\n\t\t\tfields: fields{\n\t\t\t\tProgram:      models.BPFProgram{},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t},\n\t\t\targs:    args{conf: &config.Config{BPFDir: \"/tmp\"}},\n\t\t\twantErr: true,\n\t\t},\n\t\t{name: \"DummyArtifact\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tName:     \"dummy\",\n\t\t\t\t\tVersion:  \"1\",\n\t\t\t\t\tArtifact: \"dummy.tar.gz\",\n\t\t\t\t},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t\tClient: NewTestClient(func(r *http.Request) *http.Response {\n\t\t\t\t\tbuf := new(bytes.Buffer)\n\t\t\t\t\twriter := gzip.NewWriter(buf)\n\t\t\t\t\tdefer writer.Close()\n\t\t\t\t\ttarWriter := tar.NewWriter(writer)\n\t\t\t\t\tdefer tarWriter.Close()\n\t\t\t\t\theader := new(tar.Header)\n\t\t\t\t\theader.Name = \"random\"\n\t\t\t\t\theader.Mode = 0777\n\t\t\t\t\ttarWriter.WriteHeader(header)\n\t\t\t\t\ttarWriter.Write([]byte(\"random things\"))\n\t\t\t\t\treturn &http.Response{\n\t\t\t\t\t\tStatusCode: 200,\n\t\t\t\t\t\tBody:       io.NopCloser(buf),\n\t\t\t\t\t\tHeader:     make(http.Header),\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t},\n\t\t\targs: args{conf: &config.Config{BPFDir: \"/tmp\",\n\t\t\t\tEBPFRepoURL: \"https://l3af.io/\"}},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"Unknown_url_with_http_scheme\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tEPRURL: \"http://www.example.com\",\n\t\t\t\t},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tconf: &config.Config{\n\t\t\t\t\tBPFDir:      \"/tmp\",\n\t\t\t\t\tEBPFRepoURL: \"https://l3af.io/\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"Unknown_url_with_file_scheme\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tEPRURL: \"file:///Users/random/dummy.tar.gz\",\n\t\t\t\t},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tconf: &config.Config{\n\t\t\t\t\tBPFDir:      \"/tmp\",\n\t\t\t\t\tEBPFRepoURL: \"https://l3af.io/\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"Unknown_scheme\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tEPRURL: \"ftp://ftp.foo.org/dummy.tar.gz\",\n\t\t\t\t},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tconf: &config.Config{\n\t\t\t\t\tBPFDir:      \"/tmp\",\n\t\t\t\t\tEBPFRepoURL: \"https://l3af.io/\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"ZipReaderFail\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tName:     \"testebpfprogram\",\n\t\t\t\t\tVersion:  \"1.0\",\n\t\t\t\t\tArtifact: \"testebpfprogram.zip\",\n\t\t\t\t},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t\tClient: NewTestClient(func(r *http.Request) *http.Response {\n\t\t\t\t\tbuf := new(bytes.Buffer)\n\t\t\t\t\twriter := zip.NewWriter(buf)\n\t\t\t\t\tf, _ := writer.Create(\"testebpfprogram\")\n\t\t\t\t\tdata := strings.NewReader(\"this is just a test ebpf program\")\n\t\t\t\t\tio.Copy(f, data)\n\t\t\t\t\twriter.Close()\n\t\t\t\t\treturn &http.Response{\n\t\t\t\t\t\tStatusCode: 200,\n\t\t\t\t\t\tBody:       io.NopCloser(buf),\n\t\t\t\t\t\tHeader:     make(http.Header),\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tconf: &config.Config{\n\t\t\t\t\tBPFDir: \"/tmp\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := &BPF{\n\t\t\t\tProgram:      tt.fields.Program,\n\t\t\t\tCmd:          tt.fields.Cmd,\n\t\t\t\tFilePath:     tt.fields.FilePath,\n\t\t\t\tRestartCount: tt.fields.RestartCount,\n\t\t\t}\n\t\t\tctrl := gomock.NewController(t)\n\t\t\tdefer ctrl.Finish()\n\t\t\tm := mocks.NewMockplatformInterface(ctrl)\n\t\t\tif runtime.GOOS == \"windows\" {\n\t\t\t\tm.EXPECT().GetPlatform().Return(\"windows\", nil).AnyTimes()\n\t\t\t} else {\n\t\t\t\tm.EXPECT().GetPlatform().Return(\"focal\", nil).AnyTimes()\n\t\t\t}\n\t\t\terr := b.GetArtifacts(tt.args.conf)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"BPF.download() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBPF_SetPrLimits(t *testing.T) {\n\ttype fields struct {\n\t\tProgram models.BPFProgram\n\t\tCmd     *exec.Cmd\n\t\t//\t\tPid          int\n\t\tFilePath     string\n\t\tRestartCount int\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\twantErr bool\n\t}{\n\t\t{name: \"DefaultLimitsWithNoCmd\",\n\t\t\tfields: fields{\n\t\t\t\tProgram:      models.BPFProgram{},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{name: \"ValidLimitsWithNoCmd\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tCPU:    100,\n\t\t\t\t\tMemory: 1024,\n\t\t\t\t},\n\t\t\t\tCmd:          fakeExecCommand(\"/foo/foo\"),\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := &BPF{\n\t\t\t\tProgram: tt.fields.Program,\n\t\t\t\tCmd:     tt.fields.Cmd,\n\t\t\t\t//\t\t\t\tPid:          tt.fields.Pid,\n\t\t\t\tFilePath:     tt.fields.FilePath,\n\t\t\t\tRestartCount: tt.fields.RestartCount,\n\t\t\t}\n\t\t\tif err := b.SetPrLimits(); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"BPF.SetPrLimits() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_assertExecute(t *testing.T) {\n\ttype args struct {\n\t\tfilepath string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"InvalidFilepath\",\n\t\t\targs: args{\n\t\t\t\tfilepath: \"/tmp/dummyfile\",\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"ValidFilepath\",\n\t\t\targs: args{\n\t\t\t\tfilepath: GetTestExecutablePathName(),\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"ValidFilepathWihoutExecute\",\n\t\t\targs: args{\n\t\t\t\tfilepath: GetTestNonexecutablePathName(),\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif err := assertExecutable(tt.args.filepath); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"assertExecute() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_fileExists(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tfileName string\n\t\texist    bool\n\t}{\n\t\t{\n\t\t\tname:     \"invalidfilename\",\n\t\t\tfileName: \"blahblah\",\n\t\t\texist:    false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tif fileExists(tt.fileName) != tt.exist {\n\t\t\tt.Errorf(\"Invalid filename\")\n\t\t}\n\t}\n}\n\nfunc Test_StopExternalRunningProcess(t *testing.T) {\n\ttests := []struct {\n\t\tname        string\n\t\tprocessName string\n\t\twantErr     bool\n\t}{\n\t\t{\n\t\t\tname:        \"emptyProcessName\",\n\t\t\tprocessName: \"\",\n\t\t\twantErr:     true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\terr := StopExternalRunningProcess(tt.processName)\n\t\tif (err != nil) != tt.wantErr {\n\t\t\tt.Errorf(\"Error During execution StopExternalRunningProcess : %v\", err)\n\t\t}\n\t}\n}\n\nfunc Test_createUpdateRulesFile(t *testing.T) {\n\ttype fields struct {\n\t\tProgram models.BPFProgram\n\t\tCmd     *exec.Cmd\n\t\t//\t\tPid          int\n\t\tFilePath     string\n\t\tRestartCount int\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"emptyRuleFileName\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tRulesFile: \"\",\n\t\t\t\t},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalidPath\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tRulesFile: \"bad\",\n\t\t\t\t},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"/dummy/fpp\",\n\t\t\t\tRestartCount: 0,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := &BPF{\n\t\t\t\tProgram:      tt.fields.Program,\n\t\t\t\tCmd:          tt.fields.Cmd,\n\t\t\t\tFilePath:     tt.fields.FilePath,\n\t\t\t\tRestartCount: tt.fields.RestartCount,\n\t\t\t}\n\t\t\t_, err := b.createUpdateRulesFile(\"ingress\")\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"createUpdateRulesFile() error : %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_PutNextProgFDFromID(t *testing.T) {\n\ttype fields struct {\n\t\tProgram      models.BPFProgram\n\t\tCmd          *exec.Cmd\n\t\tFilePath     string\n\t\tRestartCount int\n\t\thostConfig   *config.Config\n\t}\n\ttests := []struct {\n\t\tname       string\n\t\tfields     fields\n\t\twantErr    bool\n\t\tprogId     int\n\t\thostConfig *config.Config\n\t}{\n\t\t{\n\t\t\tname: \"emptyMapName\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tMapName: \"\",\n\t\t\t\t},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBpfMapDefaultPath: \"/sys/fs/bpf\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t\tprogId:  1,\n\t\t},\n\t\t{\n\t\t\tname: \"invalidMapName\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tMapName: \"invalidname\",\n\t\t\t\t},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBpfMapDefaultPath: \"/sys/fs/bpf\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t\tprogId:  1,\n\t\t},\n\t\t{\n\t\t\tname: \"invalidProgID\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tName:              \"ratelimiting\",\n\t\t\t\t\tSeqID:             1,\n\t\t\t\t\tArtifact:          \"l3af_ratelimiting.tar.gz\",\n\t\t\t\t\tMapName:           \"xdp_rl_ingress_next_prog\",\n\t\t\t\t\tCmdStart:          \"ratelimiting\",\n\t\t\t\t\tVersion:           \"latest\",\n\t\t\t\t\tUserProgramDaemon: true,\n\t\t\t\t\tAdminStatus:       \"enabled\",\n\t\t\t\t\tProgType:          \"xdp\",\n\t\t\t\t\tCfgVersion:        1,\n\t\t\t\t},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBpfMapDefaultPath: \"/sys/fs/bpf\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tprogId:  -1,\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := &BPF{\n\t\t\t\tProgram:      tt.fields.Program,\n\t\t\t\tCmd:          tt.fields.Cmd,\n\t\t\t\tFilePath:     tt.fields.FilePath,\n\t\t\t\tRestartCount: tt.fields.RestartCount,\n\t\t\t\tHostConfig:   tt.fields.hostConfig,\n\t\t\t}\n\t\t\terr := b.PutNextProgFDFromID(tt.progId)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"PutNextProgFDFromID() error : %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_VerifyPinnedProgMapExists(t *testing.T) {\n\ttype fields struct {\n\t\tProgram      models.BPFProgram\n\t\tCmd          *exec.Cmd\n\t\tFilePath     string\n\t\tRestartCount int\n\t\thostConfig   *config.Config\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"invalidMapName\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tMapName: \"invalid\",\n\t\t\t\t},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBpfMapDefaultPath: \"/sys/fs/bpf\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := &BPF{\n\t\t\t\tProgram:      tt.fields.Program,\n\t\t\t\tCmd:          tt.fields.Cmd,\n\t\t\t\tFilePath:     tt.fields.FilePath,\n\t\t\t\tRestartCount: tt.fields.RestartCount,\n\t\t\t\tHostConfig:   tt.fields.hostConfig,\n\t\t\t}\n\t\t\terr := b.VerifyPinnedProgMap(true, true)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"VerifyPinnedMapExists() error : %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\nfunc Test_VerifyProcessObject(t *testing.T) {\n\ttype fields struct {\n\t\tProgram      models.BPFProgram\n\t\tCmd          *exec.Cmd\n\t\tFilePath     string\n\t\tRestartCount int\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"nilCmd\",\n\t\t\tfields: fields{\n\t\t\t\tProgram:      models.BPFProgram{},\n\t\t\t\tCmd:          nil,\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"nillCmdProcess\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{},\n\t\t\t\tCmd: &exec.Cmd{\n\t\t\t\t\tProcess: nil,\n\t\t\t\t},\n\t\t\t\tFilePath:     \"\",\n\t\t\t\tRestartCount: 0,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := &BPF{\n\t\t\t\tProgram:      tt.fields.Program,\n\t\t\t\tCmd:          tt.fields.Cmd,\n\t\t\t\tFilePath:     tt.fields.FilePath,\n\t\t\t\tRestartCount: tt.fields.RestartCount,\n\t\t\t}\n\t\t\terr := b.VerifyProcessObject()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"VerifyProcessObject() error : %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_VerifyPinnedProgMapVanish(t *testing.T) {\n\ttype fields struct {\n\t\tProgram      models.BPFProgram\n\t\tCmd          *exec.Cmd\n\t\tFilePath     string\n\t\tRestartCount int\n\t\thostConfig   *config.Config\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"emptyMapName\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tMapName: \"\",\n\t\t\t\t},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBpfMapDefaultPath: \"/sys/fs/bpf\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"invalidProgType\",\n\t\t\tfields: fields{\n\t\t\t\tProgram: models.BPFProgram{\n\t\t\t\t\tMapName:  \"tc/globals/something\",\n\t\t\t\t\tProgType: models.TCType,\n\t\t\t\t},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBpfMapDefaultPath: \"/sys/fs/bpf\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tb := &BPF{\n\t\t\t\tProgram:      tt.fields.Program,\n\t\t\t\tCmd:          tt.fields.Cmd,\n\t\t\t\tFilePath:     tt.fields.FilePath,\n\t\t\t\tRestartCount: tt.fields.RestartCount,\n\t\t\t\tHostConfig: &config.Config{\n\t\t\t\t\tBpfMapDefaultPath: tt.fields.hostConfig.BpfMapDefaultPath,\n\t\t\t\t},\n\t\t\t}\n\t\t\terr := b.VerifyPinnedProgMap(true, false)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"VerifyPinnedMapVanish() error : %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bpfprogs/bpf_test_unix.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !WINDOWS\n// +build !WINDOWS\n\npackage bpfprogs\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc GetTestNonexecutablePathName() string {\n\treturn \"/var/log/syslog\"\n}\n\nfunc GetTestExecutablePathName() string {\n\treturn \"/bin/date\"\n}\n\nfunc GetTestExecutablePath() string {\n\treturn \"/bin\"\n}\n\nfunc GetTestExecutableName() string {\n\treturn \"date\"\n}\n\n// assertExecutable checks for executable permissions\nfunc assertExecutable(fPath string) error {\n\tinfo, err := os.Stat(fPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not stat file: %s with error: %w\", fPath, err)\n\t}\n\n\tif (info.Mode()&os.ModePerm)&os.FileMode(executePerm) == 0 {\n\t\treturn fmt.Errorf(\"file: %s, is not executable\", fPath)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "bpfprogs/bpf_test_windows.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build WINDOWS\n// +build WINDOWS\n\npackage bpfprogs\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc GetTestNonexecutablePathName() string {\n\treturn \"c:/windows/system32/drivers/etc/host\"\n}\n\nfunc GetTestExecutablePathName() string {\n\treturn \"c:/windows/system32/net.exe\"\n}\n\nfunc GetTestExecutablePath() string {\n\treturn \"c:/windows/system32\"\n}\n\nfunc GetTestExecutableName() string {\n\treturn \"net.exe\"\n}\n\n// assertExecutable checks for executable permissions\nfunc assertExecutable(fPath string) error {\n\t_, err := os.Stat(fPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not stat file: %s with error: %v\", fPath, err)\n\t}\n\n\t// info.Mode() does not return the correct permissions on Windows,\n\t// it always has the 'x' permissions clear, so instead use the file suffix.\n\tif !strings.HasSuffix(fPath, \".exe\") {\n\t\treturn fmt.Errorf(\"file: %s, is not executable\", fPath)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "bpfprogs/bpf_unix.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !WINDOWS\n// +build !WINDOWS\n\n// Package bpfprogs provides primitives for l3afd's network function configs.\npackage bpfprogs\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/l3af-project/l3afd/v2/utils\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/florianl/go-tc\"\n\t\"github.com/florianl/go-tc/core\"\n\t\"github.com/rs/zerolog/log\"\n\t\"github.com/safchain/ethtool\"\n\t\"golang.org/x/sys/unix\"\n)\n\n// DisableLRO - XDP programs are failing when LRO is enabled, to fix this we use to manually disable.\n// # ethtool -K ens7 lro off\n// # ethtool -k ens7 | grep large-receive-offload\n// large-receive-offload: off\nfunc DisableLRO(ifaceName string) error {\n\tethHandle, err := ethtool.NewEthtool()\n\tif err != nil {\n\t\terr = fmt.Errorf(\"ethtool failed to get the handle %w\", err)\n\t\tlog.Error().Err(err).Msg(\"\")\n\t\treturn err\n\t}\n\tdefer ethHandle.Close()\n\n\tconfig := make(map[string]bool, 1)\n\tconfig[\"rx-lro\"] = false\n\tif err := ethHandle.Change(ifaceName, config); err != nil {\n\t\terr = fmt.Errorf(\"ethtool failed to disable LRO on %s with err %w\", ifaceName, err)\n\t\tlog.Error().Err(err).Msg(\"\")\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// prLimit set the memory and cpu limits for the bpf program\nfunc prLimit(pid int, limit uintptr, rlimit *unix.Rlimit) error {\n\t_, _, errno := unix.RawSyscall6(unix.SYS_PRLIMIT64,\n\t\tuintptr(pid),\n\t\tlimit,\n\t\tuintptr(unsafe.Pointer(rlimit)),\n\t\t0, 0, 0)\n\n\tif errno != 0 {\n\t\tlog.Error().Msgf(\"Failed to set prlimit for process %d and errorno %d\", pid, errno)\n\t\treturn errors.New(\"failed to set prlimit\")\n\t}\n\n\treturn nil\n}\n\n// Set process resource limits only non-zero value\nfunc (b *BPF) SetPrLimits() error {\n\tvar rlimit unix.Rlimit\n\n\tif b.Cmd == nil {\n\t\treturn errors.New(\"no Process to set limits\")\n\t}\n\n\tif b.Program.Memory != 0 {\n\t\trlimit.Cur = uint64(b.Program.Memory)\n\t\trlimit.Max = uint64(b.Program.Memory)\n\n\t\tif err := prLimit(b.Cmd.Process.Pid, unix.RLIMIT_AS, &rlimit); err != nil {\n\t\t\tlog.Error().Err(err).Msgf(\"Failed to set Memory limits - %s\", b.Program.Name)\n\t\t}\n\t}\n\n\tif b.Program.CPU != 0 {\n\t\trlimit.Cur = uint64(b.Program.CPU)\n\t\trlimit.Max = uint64(b.Program.CPU)\n\t\tif err := prLimit(b.Cmd.Process.Pid, unix.RLIMIT_CPU, &rlimit); err != nil {\n\t\t\tlog.Error().Err(err).Msgf(\"Failed to set CPU limits - %s\", b.Program.Name)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// ProcessTerminate - Send sigterm to the process\nfunc (b *BPF) ProcessTerminate() error {\n\tif err := b.Cmd.Process.Signal(syscall.SIGTERM); err != nil {\n\t\treturn fmt.Errorf(\"BPFProgram %s SIGTERM failed with error: %w\", b.Program.Name, err)\n\t}\n\treturn nil\n}\n\n// VerifyNMountBPFFS - Mounting bpf filesystem\nfunc VerifyNMountBPFFS() error {\n\tdstPath := \"/sys/fs/bpf\"\n\tsrcPath := \"bpffs\"\n\tfstype := \"bpf\"\n\tflags := 0\n\n\tmnts, err := os.ReadFile(\"/proc/mounts\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read procfs: %w\", err)\n\t}\n\n\tif !strings.Contains(string(mnts), dstPath) {\n\t\tlog.Warn().Msg(\"bpf filesystem is not mounted going to mount\")\n\t\tif err = syscall.Mount(srcPath, dstPath, fstype, uintptr(flags), \"\"); err != nil {\n\t\t\treturn fmt.Errorf(\"unable to mount %s at %s: %w\", srcPath, dstPath, err)\n\t\t}\n\t}\n\n\treturn VerifyNMountTraceFS()\n}\n\n// VerifyNMounTraceFS - Mounting trace filesystem\nfunc VerifyNMountTraceFS() error {\n\tdstPath := \"/sys/kernel/debug/tracing\"\n\tsrcPath := \"tracefs\"\n\tfstype := \"tracefs\"\n\tflags := syscall.MS_NODEV | syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_RELATIME\n\n\tmnts, err := os.ReadFile(\"/proc/self/mounts\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read procfs: %w\", err)\n\t}\n\n\tif !strings.Contains(string(mnts), dstPath) {\n\t\tlog.Warn().Msgf(\" %s filesystem is not mounted going to mount\", dstPath)\n\t\tif _, err = os.Stat(dstPath); err != nil {\n\t\t\tlog.Warn().Msgf(\" %s directory doesn't exists, creating\", dstPath)\n\t\t\tif err := os.Mkdir(dstPath, 0700); err != nil {\n\t\t\t\treturn fmt.Errorf(\"unable to create mount point %s : %w\", dstPath, err)\n\t\t\t}\n\t\t}\n\t\tif err = syscall.Mount(srcPath, dstPath, fstype, uintptr(flags), \"\"); err != nil {\n\t\t\treturn fmt.Errorf(\"unable to mount %s at %s: %w\", srcPath, dstPath, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// This method get the Linux distribution Codename. This logic works on ubuntu\n// Here assumption is all edge nodes are running with lsb modules.\n// It returns empty string in case of error\nfunc GetPlatform() (string, error) {\n\n\tlinuxDistrib := execCommand(\"lsb_release\", \"-cs\")\n\tvar out bytes.Buffer\n\tlinuxDistrib.Stdout = &out\n\n\tif err := linuxDistrib.Run(); err != nil {\n\t\treturn \"\", fmt.Errorf(\"l3afd/nf : Failed to run command with error: %w\", err)\n\t}\n\n\treturn strings.TrimSpace(out.String()), nil\n}\n\nfunc IsProcessRunning(pid int, name string) (bool, error) {\n\tprocState, err := os.ReadFile(fmt.Sprintf(\"/proc/%d/stat\", pid))\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"BPF Program not running %s because of error: %w\", name, err)\n\t}\n\tvar u1, u2, state string\n\t_, err = fmt.Sscanf(string(procState), \"%s %s %s\", &u1, &u2, &state)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"failed to scan proc state with error: %w\", err)\n\t}\n\tif state == \"Z\" {\n\t\treturn false, fmt.Errorf(\"process %d in Zombie state\", pid)\n\t}\n\n\treturn true, nil\n}\n\n// VerifyNCreateTCDirs - Creating BPF sudo FS for pinning TC maps\nfunc VerifyNCreateTCDirs() error {\n\tpath := \"/sys/fs/bpf/tc/globals\"\n\tif _, err := os.Stat(path); err == nil {\n\t\tlog.Debug().Msgf(\" %s tc directory exists\", path)\n\t\treturn nil\n\t}\n\tlog.Info().Msgf(\" %s tc directory doesn't exists, creating\", path)\n\n\tif err := os.MkdirAll(path, 0700); err != nil {\n\t\treturn fmt.Errorf(\"unable to create directories to pin tc maps %s : %w\", path, err)\n\t}\n\treturn nil\n}\n\n// LoadTCAttachProgram - Load and attach tc root program filters or any tc program when chaining is disabled\nfunc (b *BPF) LoadTCAttachProgram(ifaceName, direction string) error {\n\tiface, err := net.InterfaceByName(ifaceName)\n\tif err != nil {\n\t\tlog.Error().Err(err).Msgf(\"LoadTCAttachProgram - look up network iface %q\", ifaceName)\n\t\treturn err\n\t}\n\tif err := b.LoadBPFProgram(ifaceName); err != nil {\n\t\treturn err\n\t}\n\t// verify and add attribute clsact\n\ttcgo, err := tc.Open(&tc.Config{})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not open rtnetlink socket for interface %s : %w\", ifaceName, err)\n\t}\n\tclsactFound := false\n\thtbFound := false\n\tingressFound := false\n\tvar htbHandle uint32\n\tvar ingressHandle uint32\n\tvar parentHandle uint32\n\t// get all the qdiscs from all interfaces\n\tqdiscs, err := tcgo.Qdisc().Get()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not get qdiscs for interface %s : %w\", ifaceName, err)\n\t}\n\n\tfor _, qdisc := range qdiscs {\n\t\tiface, err := net.InterfaceByIndex(int(qdisc.Ifindex))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not get interface %s from id %d: %w\", ifaceName, qdisc.Ifindex, err)\n\t\t}\n\t\tif iface.Name == ifaceName {\n\t\t\tswitch qdisc.Kind {\n\t\t\tcase \"clsact\":\n\t\t\t\tclsactFound = true\n\t\t\tcase \"htb\":\n\t\t\t\thtbFound = true\n\t\t\t\thtbHandle = qdisc.Msg.Handle\n\t\t\tcase \"ingress\":\n\t\t\t\tingressFound = true\n\t\t\t\tingressHandle = qdisc.Msg.Handle\n\t\t\tdefault:\n\t\t\t\tlog.Info().Msgf(\"Un-supported qdisc kind for interface %s : %s \", ifaceName, qdisc.Kind)\n\t\t\t}\n\t\t}\n\t}\n\n\tbpfRootProg := b.ProgMapCollection.Programs[b.Program.EntryFunctionName]\n\n\tvar parent uint32\n\tvar filter tc.Object\n\n\tprogFD := uint32(bpfRootProg.FD())\n\t// Netlink attribute used in the Linux kernel\n\tbpfFlag := uint32(tc.BpfActDirect)\n\tif clsactFound {\n\t\tif direction == models.IngressType {\n\t\t\tparent = tc.HandleMinIngress\n\t\t} else if direction == models.EgressType {\n\t\t\tparent = tc.HandleMinEgress\n\t\t}\n\n\t\tfilter = tc.Object{\n\t\t\tMsg: tc.Msg{\n\t\t\t\tFamily:  unix.AF_UNSPEC,\n\t\t\t\tIfindex: uint32(iface.Index),\n\t\t\t\tHandle:  0,\n\t\t\t\tParent:  core.BuildHandle(tc.HandleRoot, parent),\n\t\t\t\tInfo:    0x300,\n\t\t\t},\n\t\t\tAttribute: tc.Attribute{\n\t\t\t\tKind: \"bpf\",\n\t\t\t\tBPF: &tc.Bpf{\n\t\t\t\t\tFD:    &progFD,\n\t\t\t\t\tFlags: &bpfFlag,\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t} else if !clsactFound && !ingressFound && !htbFound {\n\t\tqdisc := tc.Object{\n\t\t\tMsg: tc.Msg{\n\t\t\t\tFamily:  unix.AF_UNSPEC,\n\t\t\t\tIfindex: uint32(iface.Index),\n\t\t\t\tHandle:  core.BuildHandle(tc.HandleRoot, 0x0000),\n\t\t\t\tParent:  tc.HandleIngress,\n\t\t\t\tInfo:    0,\n\t\t\t},\n\t\t\tAttribute: tc.Attribute{\n\t\t\t\tKind: \"clsact\",\n\t\t\t},\n\t\t}\n\t\tif err := tcgo.Qdisc().Add(&qdisc); err != nil {\n\t\t\tlog.Info().Msgf(\"could not assign clsact to %s : %v, its already exists\", ifaceName, err)\n\t\t}\n\n\t\tif direction == models.IngressType {\n\t\t\tparent = tc.HandleMinIngress\n\t\t} else if direction == models.EgressType {\n\t\t\tparent = tc.HandleMinEgress\n\t\t}\n\n\t\tfilter = tc.Object{\n\t\t\tMsg: tc.Msg{\n\t\t\t\tFamily:  unix.AF_UNSPEC,\n\t\t\t\tIfindex: uint32(iface.Index),\n\t\t\t\tHandle:  0,\n\t\t\t\tParent:  core.BuildHandle(tc.HandleRoot, parent),\n\t\t\t\tInfo:    0x300,\n\t\t\t},\n\t\t\tAttribute: tc.Attribute{\n\t\t\t\tKind: \"bpf\",\n\t\t\t\tBPF: &tc.Bpf{\n\t\t\t\t\tFD:    &progFD,\n\t\t\t\t\tFlags: &bpfFlag,\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t} else if !clsactFound && ingressFound && htbFound {\n\t\tif direction == models.IngressType {\n\t\t\tparentHandle = ingressHandle\n\t\t} else if direction == models.EgressType {\n\t\t\tparentHandle = htbHandle\n\t\t}\n\n\t\t// parentNew needs to handle of HTB and ingress 1:, and ffff:\n\t\tfilter = tc.Object{\n\t\t\tMsg: tc.Msg{\n\t\t\t\tFamily:  unix.AF_UNSPEC,\n\t\t\t\tIfindex: uint32(iface.Index),\n\t\t\t\tHandle:  0,\n\t\t\t\tParent:  parentHandle,\n\t\t\t\tInfo:    0x300,\n\t\t\t},\n\t\t\tAttribute: tc.Attribute{\n\t\t\t\tKind: \"bpf\",\n\t\t\t\tBPF: &tc.Bpf{\n\t\t\t\t\tFD:    &progFD,\n\t\t\t\t\tFlags: &bpfFlag,\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t} else {\n\t\tlog.Info().Msgf(\"Unable to create qdisc object for interface %s\", ifaceName)\n\t}\n\n\t// Storing Filter handle\n\tfilterHandle := tcgo.Filter()\n\t// Attaching / Adding as filter\n\tif err := filterHandle.Add(&filter); err != nil {\n\t\treturn fmt.Errorf(\"could not attach filter to interface %s for eBPF program %s : %w\", ifaceName, b.Program.Name, err)\n\t}\n\n\tif b.HostConfig.BpfChainingEnabled {\n\t\tif err = b.UpdateProgramMap(ifaceName); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// UnloadTCProgram - Remove TC filters\nfunc (b *BPF) UnloadTCProgram(ifaceName, direction string) error {\n\tiface, err := net.InterfaceByName(ifaceName)\n\tif err != nil {\n\t\tlog.Error().Err(err).Msgf(\"UnloadTCProgram - look up network iface %q\", ifaceName)\n\t\treturn err\n\t}\n\n\ttcgo, err := tc.Open(&tc.Config{})\n\tif err != nil {\n\t\tlog.Error().Err(err).Msgf(\"UnloadTCProgram - Unable to tc.Open(&tc.Config{}):  %q\", ifaceName)\n\t\treturn err\n\t}\n\n\tclsactFound := false\n\thtbFound := false\n\tingressFound := false\n\tvar htbHandle uint32\n\tvar ingressHandle uint32\n\tvar parentHandle uint32\n\t// get all the qdiscs from all interfaces\n\tqdiscs, err := tcgo.Qdisc().Get()\n\tif err != nil {\n\t\tlog.Warn().Msgf(\"Could not get filters for interface \\\"%s\\\" direction %s \", ifaceName, direction)\n\t\treturn fmt.Errorf(\"could not get filters for interface %s : %w\", ifaceName, err)\n\t}\n\tfor _, qdisc := range qdiscs {\n\t\tiface, err := net.InterfaceByIndex(int(qdisc.Ifindex))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not get interface %s from id %d: %v\", ifaceName, qdisc.Ifindex, err)\n\t\t}\n\t\tif iface.Name == ifaceName {\n\t\t\tswitch qdisc.Kind {\n\t\t\tcase \"clsact\":\n\t\t\t\tclsactFound = true\n\t\t\tcase \"htb\":\n\t\t\t\thtbFound = true\n\t\t\t\thtbHandle = qdisc.Msg.Handle\n\t\t\tcase \"ingress\":\n\t\t\t\tingressFound = true\n\t\t\t\tingressHandle = qdisc.Msg.Handle\n\t\t\tdefault:\n\t\t\t\tlog.Info().Msgf(\"qdisc kind for %s : %v\", ifaceName, qdisc.Kind)\n\t\t\t}\n\t\t}\n\t}\n\n\tbpfRootProg := b.ProgMapCollection.Programs[b.Program.EntryFunctionName]\n\t// Storing Filter handle\n\tfilterHandle := tcgo.Filter()\n\tvar parent uint32\n\tvar filter tc.Object\n\n\tif clsactFound && !ingressFound && !htbFound {\n\t\tif direction == models.IngressType {\n\t\t\tparent = tc.HandleMinIngress\n\t\t} else if direction == models.EgressType {\n\t\t\tparent = tc.HandleMinEgress\n\t\t}\n\n\t\ttcfilts, err := filterHandle.Get(&tc.Msg{\n\t\t\tFamily:  unix.AF_UNSPEC,\n\t\t\tIfindex: uint32(iface.Index),\n\t\t\tHandle:  0x0,\n\t\t\tParent:  core.BuildHandle(tc.HandleRoot, parent),\n\t\t})\n\n\t\tif err != nil {\n\t\t\tlog.Warn().Msgf(\"Could not get filters for interface \\\"%s\\\" direction %s \", ifaceName, direction)\n\t\t\treturn fmt.Errorf(\"could not get filters for interface %s : %v\", ifaceName, err)\n\t\t}\n\n\t\tprogFD := uint32(bpfRootProg.FD())\n\t\t// Netlink attribute used in the Linux kernel\n\t\tbpfFlag := uint32(tc.BpfActDirect)\n\n\t\tif len(tcfilts) > 0 {\n\t\t\tfilter = tc.Object{\n\t\t\t\tMsg: tc.Msg{\n\t\t\t\t\tFamily:  unix.AF_UNSPEC,\n\t\t\t\t\tIfindex: uint32(iface.Index),\n\t\t\t\t\tHandle:  0,\n\t\t\t\t\tParent:  core.BuildHandle(tc.HandleRoot, parent),\n\t\t\t\t\tInfo:    tcfilts[0].Msg.Info,\n\t\t\t\t},\n\t\t\t\tAttribute: tc.Attribute{\n\t\t\t\t\tKind: \"bpf\",\n\t\t\t\t\tBPF: &tc.Bpf{\n\t\t\t\t\t\tFD:    &progFD,\n\t\t\t\t\t\tFlags: &bpfFlag,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t} else {\n\t\t\tlog.Warn().Msgf(\"unload TC program clasct filters not found  for interface %s direction %s\", ifaceName, direction)\n\t\t}\n\t} else if !clsactFound && ingressFound && htbFound {\n\t\tif direction == models.EgressType {\n\t\t\tparentHandle = htbHandle\n\t\t\t// _ = pa(\"parentNew...1 \", parentNew)\n\t\t} else if direction == models.IngressType {\n\t\t\tparentHandle = ingressHandle\n\t\t}\n\t\ttcfilts, err := filterHandle.Get(&tc.Msg{\n\t\t\tFamily:  unix.AF_UNSPEC,\n\t\t\tIfindex: uint32(iface.Index),\n\t\t\tHandle:  0x0,\n\t\t\tParent:  parentHandle,\n\t\t})\n\n\t\tif err != nil {\n\t\t\tlog.Warn().Msgf(\"Could not get filters for interface \\\"%s\\\" direction %s \", ifaceName, direction)\n\t\t\treturn fmt.Errorf(\"could not get filters for interface %s : %v\", ifaceName, err)\n\t\t}\n\n\t\tprogFD := uint32(bpfRootProg.FD())\n\t\t// Netlink attribute used in the Linux kernel\n\t\tbpfFlag := uint32(tc.BpfActDirect)\n\n\t\tvar tcFilterIndex int\n\t\tfor i, tcfilt := range tcfilts {\n\t\t\t// finding the Info field of the relevant BPF filter among all set filters for that qdisc\n\t\t\tif tcfilt.Attribute.Kind == \"bpf\" {\n\t\t\t\ttcFilterIndex = i\n\t\t\t}\n\t\t}\n\t\t// Add a check for if tcFilterIndex out of bounds\n\t\tif len(tcfilts) > 0 {\n\t\t\tfilter = tc.Object{\n\t\t\t\tMsg: tc.Msg{\n\t\t\t\t\tFamily:  unix.AF_UNSPEC,\n\t\t\t\t\tIfindex: uint32(iface.Index),\n\t\t\t\t\tHandle:  0,\n\t\t\t\t\tParent:  parentHandle,\n\t\t\t\t\tInfo:    tcfilts[tcFilterIndex].Msg.Info,\n\t\t\t\t},\n\t\t\t\tAttribute: tc.Attribute{\n\t\t\t\t\tKind: \"bpf\",\n\t\t\t\t\tBPF: &tc.Bpf{\n\t\t\t\t\t\tFD:    &progFD,\n\t\t\t\t\t\tFlags: &bpfFlag,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t} else {\n\t\t\tlog.Warn().Msgf(\"unload TC program ingress or htp filters not found for interface %s direction %s\", ifaceName, direction)\n\t\t}\n\t}\n\n\t// Detaching / Deleting filter\n\tif err := filterHandle.Delete(&filter); err != nil {\n\t\treturn fmt.Errorf(\"could not dettach tc filter for interface %s : Direction: %v, parentHandle: %v, Error:%w\", ifaceName, direction, parentHandle, err)\n\t}\n\n\treturn nil\n}\n\n// LoadTCXAttachProgram - Load and attach xdp root program or any xdp program when chaining is disabled\nfunc (b *BPF) LoadTCXAttachProgram(ifaceName, direction string) error {\n\n\tiface, err := net.InterfaceByName(ifaceName)\n\tif err != nil {\n\t\tlog.Error().Err(err).Msgf(\"LoadXDPAttachProgram -look up network iface %q\", ifaceName)\n\t\treturn err\n\t}\n\n\tif err := b.LoadBPFProgram(ifaceName); err != nil {\n\t\treturn err\n\t}\n\n\tvar attachType ebpf.AttachType\n\tif direction == models.IngressType {\n\t\tattachType = ebpf.AttachTCXIngress\n\t} else {\n\t\tattachType = ebpf.AttachTCXEgress\n\t}\n\n\tb.Link, err = link.AttachTCX(link.TCXOptions{\n\t\tProgram:   b.ProgMapCollection.Programs[b.Program.EntryFunctionName],\n\t\tInterface: iface.Index,\n\t\tAttach:    attachType,\n\t})\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not attach tc program %s to interface %s direction %s : %w\", b.Program.Name, ifaceName, direction, err)\n\t}\n\n\tversion := utils.ReplaceDotsWithUnderscores(b.Program.Version)\n\t// Pin the Link\n\tlinkPinPath := utils.TCLinkPinPath(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, b.Program.ProgType, direction)\n\tif err := b.Link.Pin(linkPinPath); err != nil {\n\t\treturn fmt.Errorf(\"tcx program pinning failed program %s direction %s interface %s : %w\", b.Program.Name, direction, ifaceName, err)\n\t}\n\n\tif b.HostConfig.BpfChainingEnabled {\n\t\tif err = b.UpdateProgramMap(ifaceName); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// LoadXDPAttachProgram - Load and attach xdp root program or any xdp program when chaining is disabled\n// This method has been moved to linux specific till cilium windows library implements link.AttachXDP\nfunc (b *BPF) LoadXDPAttachProgram(ifaceName string) error {\n\tiface, err := net.InterfaceByName(ifaceName)\n\tif err != nil {\n\t\tlog.Error().Err(err).Msgf(\"LoadXDPAttachProgram -look up network iface %q\", ifaceName)\n\t\treturn err\n\t}\n\n\tif err := b.LoadBPFProgram(ifaceName); err != nil {\n\t\treturn err\n\t}\n\tb.Link, err = link.AttachXDP(link.XDPOptions{\n\t\tProgram:   b.ProgMapCollection.Programs[b.Program.EntryFunctionName],\n\t\tInterface: iface.Index,\n\t})\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not attach xdp program %s to interface %s : %w\", b.Program.Name, ifaceName, err)\n\t}\n\n\tversion := utils.ReplaceDotsWithUnderscores(b.Program.Version)\n\t// Pin the Link\n\tlinkPinPath := utils.LinkPinPath(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, b.Program.ProgType)\n\tif err := b.Link.Pin(linkPinPath); err != nil {\n\t\treturn fmt.Errorf(\"xdp program pinning failed program %s interface %s : %w\", b.Program.Name, ifaceName, err)\n\t}\n\n\tif b.HostConfig.BpfChainingEnabled {\n\t\tif err = b.UpdateProgramMap(ifaceName); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "bpfprogs/bpf_windows.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build WINDOWS\n// +build WINDOWS\n\n// Package bpfprogs provides primitives for l3afd's network function configs.\npackage bpfprogs\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/cilium/ebpf\"\n)\n\n// DisableLRO - XDP programs are failing when Large Receive Offload is enabled, to fix this we use to manually disable.\nfunc DisableLRO(ifaceName string) error {\n\treturn nil\n}\n\n// Set process resource limits only non-zero value\nfunc (b *BPF) SetPrLimits() error {\n\tif b.Cmd == nil {\n\t\treturn errors.New(\"no Process to set limits\")\n\t}\n\treturn nil\n}\n\n// VerifyNMountBPFFS - Mounting bpf filesystem\nfunc VerifyNMountBPFFS() error {\n\treturn nil\n}\n\nfunc GetPlatform() (string, error) {\n\treturn \"Windows\", nil\n}\n\nfunc IsProcessRunning(pid int, name string) (bool, error) {\n\t_, err := os.FindProcess(pid)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"BPF Program not running %s because of error: %w\", name, err)\n\t}\n\treturn true, nil\n}\n\n// ProcessTerminate - Kills the process\nfunc (b *BPF) ProcessTerminate() error {\n\tif err := b.Cmd.Process.Kill(); err != nil {\n\t\treturn fmt.Errorf(\"BPFProgram %s kill failed with error: %w\", b.Program.Name, err)\n\t}\n\treturn nil\n}\n\n// VerifyNCreateTCDirs - Creating BPF sudo FS for pinning TC maps\nfunc VerifyNCreateTCDirs() error {\n\treturn nil\n}\n\n// LoadTCAttachProgram - not implemented in windows\nfunc (b *BPF) LoadTCAttachProgram(ifaceName, direction string) error {\n\t// not implement nothing todo\n\treturn fmt.Errorf(\"LoadTCAttachProgram - TC programs Unsupported on windows\")\n}\n\n// LoadTCXAttachProgram - not implemented in windows\nfunc (b *BPF) LoadTCXAttachProgram(ifaceName, direction string) error {\n\t// not implement nothing todo\n\treturn fmt.Errorf(\"LoadTCXAttachProgram - TC programs Unsupported on windows\")\n}\n\n// UnloadTCProgram - Remove TC filters\nfunc (b *BPF) UnloadTCProgram(ifaceName, direction string) error {\n\t// not implement nothing todo\n\treturn fmt.Errorf(\"UnloadTCProgram - TC programs Unsupported on windows\")\n}\n\n// LoadXDPAttachProgram - Attaches XDP program to interface\nfunc (b *BPF) LoadXDPAttachProgram(ifaceName string) error {\n\t// not implement nothing todo\n\treturn fmt.Errorf(\"LoadXDPAttachProgram - AttachXDP method is Unsupported on windows\")\n}\n\n// LoadBPFProgramProbeType - Loads Probe type bpf program\nfunc (b *BPF) LoadBPFProgramProbeTypes(objSpec *ebpf.CollectionSpec) error {\n\t// not implement nothing todo\n\treturn fmt.Errorf(\"LoadBPFProgramProbeTypes - Probes are Unsupported on windows\")\n}\n"
  },
  {
    "path": "bpfprogs/bpfdebug.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/rs/zerolog/log\"\n)\n\nvar bpfcfgs *NFConfigs\n\nfunc SetupBPFDebug(ebpfChainDebugAddr string, BPFConfigs *NFConfigs) {\n\tbpfcfgs = BPFConfigs\n\tgo func() {\n\t\tif _, ok := models.AllNetListeners.Load(\"debug_http\"); !ok {\n\t\t\ttcpAddr, err := net.ResolveTCPAddr(\"tcp\", ebpfChainDebugAddr)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal().Err(err).Msgf(\"unable to resolve tcpaddr %v \", ebpfChainDebugAddr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlistener, err := net.ListenTCP(\"tcp\", tcpAddr)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal().Err(err).Msgf(\"unable to create tcp listener\")\n\t\t\t}\n\t\t\tmodels.AllNetListeners.Store(\"debug_http\", listener)\n\t\t}\n\t\thttp.HandleFunc(\"/bpfs/\", ViewHandler)\n\t\t// We just need to start a server.\n\t\tlog.Info().Msg(\"Starting BPF debug server\")\n\t\tval, _ := models.AllNetListeners.Load(\"debug_http\")\n\t\tl, _ := val.(*net.TCPListener)\n\t\tif err := http.Serve(l, nil); !errors.Is(err, http.ErrServerClosed) {\n\t\t\tlog.Fatal().Err(err).Msg(\"failed to start BPF chain debug server\")\n\t\t}\n\t}()\n}\n\nfunc ViewHandler(w http.ResponseWriter, r *http.Request) {\n\n\tiface := strings.TrimPrefix(r.URL.Path, \"/bpfs/\")\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusOK)\n\tif err := json.NewEncoder(w).Encode(bpfcfgs.BPFDetails(iface)); err != nil {\n\t\tlog.Err(err).Msgf(\"unable to serialize json\")\n\t}\n}\n"
  },
  {
    "path": "bpfprogs/bpfmap.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"container/ring\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"unsafe\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/rs/zerolog/log\"\n)\n\ntype BPFMap struct {\n\tName  string\n\tMapID ebpf.MapID\n\tType  ebpf.MapType\n\n\t// BPFProg reference in case of stale map id\n\tBPFProg *BPF `json:\"-\"`\n}\n\n// This stores Metrics map details.\ntype MetricsBPFMap struct {\n\tBPFMap\n\tKey        int\n\tValues     *ring.Ring\n\tAggregator string\n\tLastValue  float64\n}\n\n// The RemoveMissingKeys function is used to delete missing entries of eBPF maps, which are used by eBPF Programs.\nfunc (b *BPFMap) RemoveMissingKeys(args []models.KeyValue) error {\n\tebpfMap, err := ebpf.NewMapFromID(b.MapID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"access new map from ID failed %w\", err)\n\t}\n\tdefer ebpfMap.Close()\n\tKeyValueMap := make(map[int]bool, len(args))\n\tfor _, k := range args {\n\t\tKeyValueMap[k.Key] = true\n\t}\n\tvar key, nextKey int\n\tfor {\n\t\terr := ebpfMap.NextKey(unsafe.Pointer(&key), unsafe.Pointer(&nextKey))\n\t\tif err != nil {\n\t\t\tif errors.Is(err, ebpf.ErrKeyNotExist) {\n\t\t\t\tbreak\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"get next key failed with error %w, mapid %d\", err, b.MapID)\n\t\t\t}\n\t\t}\n\t\tkey = nextKey\n\t\t_, IsKeyExists := KeyValueMap[key]\n\t\tif !IsKeyExists {\n\t\t\tlog.Info().Msgf(\"removing key %v because it is missing\\n\", key)\n\t\t\tif err := ebpfMap.Delete(unsafe.Pointer(&key)); err != nil {\n\t\t\t\treturn fmt.Errorf(\"delete key failed with error %w, mapid %d\", err, b.MapID)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// The update function is used to update eBPF maps, which are used by eBPF programs.\nfunc (b *BPFMap) Update(key, value int) error {\n\n\tlog.Debug().Msgf(\"update map name %s ID %d\", b.Name, b.MapID)\n\tebpfMap, err := ebpf.NewMapFromID(b.MapID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"access new map from ID failed %w\", err)\n\t}\n\tdefer ebpfMap.Close()\n\tlog.Info().Msgf(\"updating map %s key %d mapid %d\", b.Name, key, b.MapID)\n\tif err := ebpfMap.Update(unsafe.Pointer(&key), unsafe.Pointer(&value), 0); err != nil {\n\t\treturn fmt.Errorf(\"update hash map element failed for key %d error %w\", key, err)\n\t}\n\treturn nil\n}\n\n// Get value of the map for given key\n// There are 2 aggregators are supported here\n// max-rate - this calculates delta requests / sec and stores absolute value.\n// avg - stores the values in the circular queue\n// We can implement more aggregate function as needed.\nfunc (b *MetricsBPFMap) GetValue() float64 {\n\tebpfMap, err := ebpf.NewMapFromID(b.MapID)\n\tif err != nil {\n\t\t// We have observed in smaller configuration VM's, if we restart BPF's\n\t\t// Stale mapID's are reported, in such cases re-checking map id\n\t\tlog.Warn().Err(err).Msgf(\"GetValue : NewMapFromID failed ID %d, re-looking up of map id\", b.MapID)\n\t\ttmpBPF, err := b.BPFProg.GetBPFMap(b.Name)\n\t\tif err != nil {\n\t\t\tlog.Warn().Err(err).Msgf(\"GetValue: Update new map ID %d\", tmpBPF.MapID)\n\t\t\treturn 0\n\t\t}\n\t\tlog.Info().Msgf(\"GetValue: Update new map ID %d\", tmpBPF.MapID)\n\t\tb.MapID = tmpBPF.MapID\n\t\tebpfMap, err = ebpf.NewMapFromID(b.MapID)\n\t\tif err != nil {\n\t\t\tlog.Warn().Err(err).Msgf(\"GetValue : retry of NewMapFromID failed ID %d\", b.MapID)\n\t\t\treturn 0\n\t\t}\n\t}\n\tdefer ebpfMap.Close()\n\n\tvar value int64\n\tif err = ebpfMap.Lookup(unsafe.Pointer(&b.Key), unsafe.Pointer(&value)); err != nil {\n\t\tlog.Warn().Err(err).Msgf(\"GetValue Lookup failed : Name %s ID %d\", b.Name, b.MapID)\n\t\treturn 0\n\t}\n\n\tvar retVal float64\n\tswitch b.Aggregator {\n\tcase \"scalar\":\n\t\tretVal = float64(value)\n\tcase \"max-rate\":\n\t\tb.Values = b.Values.Next()\n\t\tb.Values.Value = math.Abs(float64(float64(value) - b.LastValue))\n\t\tb.LastValue = float64(value)\n\t\tretVal = b.MaxValue()\n\tcase \"avg\":\n\t\tb.Values.Value = value\n\t\tb.Values = b.Values.Next()\n\t\tretVal = b.AvgValue()\n\tdefault:\n\t\tlog.Warn().Msgf(\"unsupported aggregator %s and value %d\", b.Aggregator, value)\n\t}\n\treturn retVal\n}\n\n// This method  finds the max value in the circular list\nfunc (b *MetricsBPFMap) MaxValue() float64 {\n\ttmp := b.Values\n\tvar max float64\n\tfor i := 0; i < b.Values.Len(); i++ {\n\t\tif tmp.Value != nil {\n\t\t\tval := tmp.Value.(float64)\n\t\t\tif max < val {\n\t\t\t\tmax = val\n\t\t\t}\n\t\t}\n\t\ttmp = tmp.Next()\n\t}\n\treturn max\n}\n\n// This method calculates the average\nfunc (b *MetricsBPFMap) AvgValue() float64 {\n\ttmp := b.Values.Next()\n\tvar sum float64\n\tvar n float64 = 0.0\n\tfor i := 0; i < b.Values.Len(); i++ {\n\t\tif tmp.Value != nil {\n\t\t\tsum = sum + tmp.Value.(float64)\n\t\t\tn = n + 1\n\t\t}\n\t\ttmp = tmp.Next()\n\t}\n\treturn sum / n\n}\n"
  },
  {
    "path": "bpfprogs/bpfmap_test.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"container/ring\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nvar TestValues *ring.Ring = ring.New(10)\n\nfunc SetupTestValues() {\n\ta := [10]float64{8, 10, 6, 23, 4, 53, 32, 8, 2, 7}\n\tv := TestValues\n\tfor i := 0; i < TestValues.Len(); i++ {\n\t\tv.Value = a[i]\n\t\tv = v.Next()\n\t}\n}\nfunc TestMetricsBPFMapMaxValue(t *testing.T) {\n\ttype args struct {\n\t\tkey        int\n\t\tValues     *ring.Ring\n\t\taggregator string\n\t}\n\tSetupTestValues()\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    float64\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"max-rate\",\n\t\t\targs:    args{key: 0, Values: TestValues, aggregator: \"max-rate\"},\n\t\t\twant:    53,\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tmetricsMap := &MetricsBPFMap{\n\t\t\t\tValues:     TestValues,\n\t\t\t\tKey:        0,\n\t\t\t\tAggregator: tt.args.aggregator,\n\t\t\t\tLastValue:  0,\n\t\t\t}\n\t\t\tgot := (metricsMap.MaxValue())\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"MaxValue() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMetricsBPFMapAvgValue(t *testing.T) {\n\ttype args struct {\n\t\tkey        int\n\t\tValues     *ring.Ring\n\t\taggregator string\n\t}\n\tSetupTestValues()\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    float64\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"avg\",\n\t\t\targs:    args{key: 0, Values: TestValues, aggregator: \"avg\"},\n\t\t\twant:    15.3,\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tmetricsMap := &MetricsBPFMap{\n\t\t\t\tValues:     TestValues,\n\t\t\t\tKey:        0,\n\t\t\t\tAggregator: tt.args.aggregator,\n\t\t\t\tLastValue:  0,\n\t\t\t}\n\t\t\tgot := (metricsMap.AvgValue())\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"AvgValue() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bpfprogs/bpfmetrics.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package bpfprogs provides primitives for NF process monitoring.\npackage bpfprogs\n\nimport (\n\t\"container/list\"\n\t\"time\"\n\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\n\t\"github.com/rs/zerolog/log\"\n)\n\ntype BpfMetrics struct {\n\tChain     bool\n\tIntervals int\n}\n\nfunc NewpBpfMetrics(chain bool, interval int) *BpfMetrics {\n\tm := &BpfMetrics{\n\t\tChain:     chain,\n\t\tIntervals: interval,\n\t}\n\treturn m\n}\n\nfunc (c *BpfMetrics) BpfMetricsStart(xdpProgs, ingressTCProgs, egressTCProgs map[string]*list.List, probes *list.List, ifaces *map[string]string) {\n\tgo c.BpfMetricsWorker(xdpProgs, ifaces)\n\tgo c.BpfMetricsWorker(ingressTCProgs, ifaces)\n\tgo c.BpfMetricsWorker(egressTCProgs, ifaces)\n\tgo c.BpfMetricsProbeWorker(probes)\n}\n\nfunc (c *BpfMetrics) BpfMetricsWorker(bpfProgs map[string]*list.List, ifaces *map[string]string) {\n\tfor range time.NewTicker(1 * time.Second).C {\n\t\tfor ifaceName, bpfList := range bpfProgs {\n\t\t\tif bpfList == nil { // no bpf programs are running\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor e := bpfList.Front(); e != nil; e = e.Next() {\n\t\t\t\tbpf := e.Value.(*BPF)\n\t\t\t\tif c.Chain && bpf.Program.SeqID == 0 { // do not monitor root program\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif bpf.Program.AdminStatus == models.Disabled {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif err := bpf.MonitorMaps(ifaceName, (*ifaces)[ifaceName], c.Intervals); err != nil {\n\t\t\t\t\tlog.Debug().Err(err).Msgf(\"pMonitor monitor maps failed - %s\", bpf.Program.Name)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (c *BpfMetrics) BpfMetricsProbeWorker(bpfProgs *list.List) {\n\tfor range time.NewTicker(1 * time.Second).C {\n\t\tif bpfProgs == nil {\n\t\t\ttime.Sleep(time.Second)\n\t\t\tcontinue\n\t\t}\n\t\tfor e := bpfProgs.Front(); e != nil; e = e.Next() {\n\t\t\tbpf := e.Value.(*BPF)\n\t\t\tif bpf.Program.AdminStatus == models.Disabled {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err := bpf.MonitorMaps(\"\", \"\", c.Intervals); err != nil {\n\t\t\t\tlog.Debug().Err(err).Msgf(\"pMonitor probe monitor maps failed - %s\", bpf.Program.Name)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "bpfprogs/bpfmetrics_test.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"container/list\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestNewpKFMetrics(t *testing.T) {\n\ttype args struct {\n\t\tchain    bool\n\t\tinterval int\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    *BpfMetrics\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"EmptypCheck\",\n\t\t\targs:    args{chain: false, interval: 0},\n\t\t\twant:    &BpfMetrics{Chain: false, Intervals: 0},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"ValidpCheck\",\n\t\t\targs:    args{chain: true, interval: 10},\n\t\t\twant:    &BpfMetrics{Chain: true, Intervals: 10},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := NewpBpfMetrics(tt.args.chain, tt.args.interval)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewKFMetrics() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_BPFMetrics_Start(t *testing.T) {\n\ttype fields struct {\n\t\tChain    bool\n\t\tInterval int\n\t}\n\ttype args struct {\n\t\tIngressXDPbpfProgs map[string]*list.List\n\t\tIngressTCbpfProgs  map[string]*list.List\n\t\tEgressTCbpfProgs   map[string]*list.List\n\t\tProbes             *list.List\n\t\tIfaces             map[string]string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\targs    args\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:   \"EmptyBPF\",\n\t\t\tfields: fields{Chain: true, Interval: 10},\n\t\t\targs: args{IngressXDPbpfProgs: make(map[string]*list.List),\n\t\t\t\tIngressTCbpfProgs: make(map[string]*list.List),\n\t\t\t\tEgressTCbpfProgs:  make(map[string]*list.List),\n\t\t\t\tIfaces:            map[string]string{},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tc := &BpfMetrics{\n\t\t\t\tChain:     tt.fields.Chain,\n\t\t\t\tIntervals: tt.fields.Interval,\n\t\t\t}\n\t\t\tc.BpfMetricsStart(tt.args.IngressXDPbpfProgs, tt.args.IngressTCbpfProgs, tt.args.EgressTCbpfProgs, tt.args.Probes, &tt.args.Ifaces)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bpfprogs/nfconfig.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package bpfprogs provides primitives for l3afd's network function configs.\npackage bpfprogs\n\nimport (\n\t\"container/list\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"reflect\"\n\t\"slices\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/link\"\n\n\t\"github.com/l3af-project/l3afd/v2/config\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/l3af-project/l3afd/v2/stats\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\n\t\"github.com/rs/zerolog/log\"\n)\n\ntype NFConfigs struct {\n\tCtx            context.Context\n\tHostName       string\n\tHostInterfaces map[string]bool\n\t//\tconfigs        sync.Map // key: string, val: *models.L3afDNFConfigDetail\n\t// These holds bpf programs in the list\n\t// map keys are network iface names index's are seq_id, position in the chain\n\t// root element will be root program\n\tIngressXDPBpfs map[string]*list.List\n\tIngressTCBpfs  map[string]*list.List\n\tEgressTCBpfs   map[string]*list.List\n\tProbesBpfs     list.List\n\n\tHostConfig    *config.Config\n\tProcessMon    *PCheck\n\tBpfMetricsMon *BpfMetrics\n\n\t// keep track of interfaces\n\tIfaces map[string]string\n\n\tMu *sync.Mutex\n}\n\nfunc NewNFConfigs(ctx context.Context, host string, hostConf *config.Config, pMon *PCheck, metricsMon *BpfMetrics) (*NFConfigs, error) {\n\tnfConfigs := &NFConfigs{\n\t\tCtx:            ctx,\n\t\tHostName:       host,\n\t\tHostConfig:     hostConf,\n\t\tIngressXDPBpfs: make(map[string]*list.List),\n\t\tIngressTCBpfs:  make(map[string]*list.List),\n\t\tEgressTCBpfs:   make(map[string]*list.List),\n\t\tMu:             new(sync.Mutex),\n\t\tIfaces:         make(map[string]string),\n\t}\n\n\tvar err error\n\tif nfConfigs.HostInterfaces, err = getHostInterfaces(); err != nil {\n\t\terrOut := fmt.Errorf(\"%s failed to get network interfaces %w\", host, err)\n\t\tlog.Error().Err(errOut)\n\t\treturn nil, errOut\n\t}\n\n\tnfConfigs.ProcessMon = pMon\n\tnfConfigs.ProcessMon.PCheckStart(nfConfigs.IngressXDPBpfs, nfConfigs.IngressTCBpfs, nfConfigs.EgressTCBpfs, &nfConfigs.ProbesBpfs, &nfConfigs.Ifaces)\n\tnfConfigs.BpfMetricsMon = metricsMon\n\tnfConfigs.BpfMetricsMon.BpfMetricsStart(nfConfigs.IngressXDPBpfs, nfConfigs.IngressTCBpfs, nfConfigs.EgressTCBpfs, &nfConfigs.ProbesBpfs, &nfConfigs.Ifaces)\n\treturn nfConfigs, nil\n}\n\n// Close stop all the eBPF Programs and delete elements in the list\nfunc (c *NFConfigs) Close(ctx context.Context) error {\n\tticker := time.NewTicker(c.HostConfig.ShutdownTimeout)\n\tdefer ticker.Stop()\n\tdoneCh := make(chan struct{})\n\tvar wg sync.WaitGroup\n\n\t// wait for waitGroup to shut down\n\tgo func() {\n\t\twg.Wait()\n\t\tclose(doneCh)\n\t}()\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor ifaceName := range c.IngressXDPBpfs {\n\t\t\tif err := c.StopNRemoveAllBPFPrograms(ifaceName, models.XDPIngressType); err != nil {\n\t\t\t\tlog.Warn().Err(err).Msg(\"failed to Close Ingress XDP BPF Program\")\n\t\t\t}\n\t\t\tdelete(c.IngressXDPBpfs, ifaceName)\n\t\t}\n\t}()\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor ifaceName := range c.IngressTCBpfs {\n\t\t\tif err := c.StopNRemoveAllBPFPrograms(ifaceName, models.IngressType); err != nil {\n\t\t\t\tlog.Warn().Err(err).Msg(\"failed to Close Ingress TC BPF Program\")\n\t\t\t}\n\t\t\tdelete(c.IngressTCBpfs, ifaceName)\n\t\t}\n\t}()\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor ifaceName := range c.EgressTCBpfs {\n\t\t\tif err := c.StopNRemoveAllBPFPrograms(ifaceName, models.EgressType); err != nil {\n\t\t\t\tlog.Warn().Err(err).Msg(\"failed to Close Egress TC BPF Program\")\n\t\t\t}\n\t\t\tdelete(c.EgressTCBpfs, ifaceName)\n\t\t}\n\t}()\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tif err := c.StopNRemoveAllBPFProbePrograms(); err != nil {\n\t\t\tlog.Warn().Err(err).Msg(\"failed to Close Probe BPF Programs\")\n\t\t}\n\n\t}()\n\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\tcase <-ticker.C:\n\t\t// didn't close successfully\n\t\treturn fmt.Errorf(\"nfconfig close didn't got processed in shutdownInterval ms %v\", c.HostConfig.ShutdownTimeout)\n\tcase <-doneCh:\n\t\t// we deleted successfully\n\t}\n\n\treturn nil\n}\n\n// Check for XDP programs are not loaded then initialise the array\n// Check for XDP root program is running for a interface. if not loaded it\nfunc (c *NFConfigs) VerifyAndStartXDPRootProgram(ifaceName, direction string) error {\n\n\tif err := DisableLRO(ifaceName); err != nil {\n\t\treturn fmt.Errorf(\"failed to disable lro %w\", err)\n\t}\n\tif err := VerifyNMountBPFFS(); err != nil {\n\t\treturn fmt.Errorf(\"failed to mount bpf file system with err %w\", err)\n\t}\n\n\t// chaining is disabled nothing to do\n\tif !c.HostConfig.BpfChainingEnabled {\n\t\treturn nil\n\t}\n\n\tif c.IngressXDPBpfs[ifaceName].Len() == 0 {\n\t\trootBpf, err := LoadRootProgram(ifaceName, direction, models.XDPType, c.HostConfig)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to load %s xdp root program: %w\", direction, err)\n\t\t}\n\t\tlog.Info().Msg(\"ingress xdp root program attached\")\n\t\tc.IngressXDPBpfs[ifaceName].PushFront(rootBpf)\n\t}\n\n\treturn nil\n}\n\n// Check for TC root program is running for a interface. If not start it\nfunc (c *NFConfigs) VerifyAndStartTCRootProgram(ifaceName, direction string) error {\n\n\tif err := VerifyNMountBPFFS(); err != nil {\n\t\treturn fmt.Errorf(\"failed to mount bpf file system with err : %w\", err)\n\t}\n\tif err := VerifyNCreateTCDirs(); err != nil {\n\t\treturn fmt.Errorf(\"failed to create tc/global diretories with err: %w\", err)\n\t}\n\t// Check for chaining flag\n\tif !c.HostConfig.BpfChainingEnabled {\n\t\treturn nil\n\t}\n\n\tif direction == models.IngressType {\n\t\tif c.IngressTCBpfs[ifaceName].Len() == 0 { //Root program is not running start then\n\t\t\trootBpf, err := LoadRootProgram(ifaceName, direction, models.TCType, c.HostConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to load %s tc root program: %w\", direction, err)\n\t\t\t}\n\t\t\tlog.Info().Msg(\"ingress tc root program attached\")\n\t\t\tc.IngressTCBpfs[ifaceName].PushFront(rootBpf)\n\t\t}\n\t} else {\n\t\tif c.EgressTCBpfs[ifaceName].Len() == 0 { //Root program is not running start then\n\t\t\trootBpf, err := LoadRootProgram(ifaceName, direction, models.TCType, c.HostConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to load %s tc root program: %w\", direction, err)\n\t\t\t}\n\t\t\tlog.Info().Msg(\"egress tc root program attached\")\n\t\t\tc.EgressTCBpfs[ifaceName].PushFront(rootBpf)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// This method inserts the element at the end of the list\nfunc (c *NFConfigs) PushBackAndStartBPF(bpfProg *models.BPFProgram, ifaceName, direction string) error {\n\n\tlog.Info().Msgf(\"PushBackAndStartBPF : iface %s, direction %s\", ifaceName, direction)\n\tbpf := NewBpfProgram(c.Ctx, *bpfProg, c.HostConfig, ifaceName)\n\tvar bpfList *list.List\n\n\tswitch direction {\n\tcase models.XDPIngressType:\n\t\tbpfList = c.IngressXDPBpfs[ifaceName]\n\tcase models.IngressType:\n\t\tbpfList = c.IngressTCBpfs[ifaceName]\n\tcase models.EgressType:\n\t\tbpfList = c.EgressTCBpfs[ifaceName]\n\tdefault: // we should never reach here\n\t\treturn fmt.Errorf(\"unknown direction type\")\n\t}\n\n\tif err := c.DownloadAndStartBPFProgram(bpfList.PushBack(bpf), ifaceName, direction); err != nil {\n\t\treturn fmt.Errorf(\"failed to download and start the BPF %s iface %s direction %s with err: %w\", bpfProg.Name, ifaceName, direction, err)\n\t}\n\n\treturn nil\n}\n\nfunc (c *NFConfigs) DownloadAndStartBPFProgram(element *list.Element, ifaceName, direction string) error {\n\tif element == nil {\n\t\treturn fmt.Errorf(\"element is nil pointer\")\n\t}\n\n\tbpf := element.Value.(*BPF)\n\n\tif element.Prev() != nil {\n\t\tprevBPF := element.Prev().Value.(*BPF)\n\t\tbpf.PrevMapNamePath = prevBPF.MapNamePath\n\t\tbpf.PrevProgMapID = prevBPF.ProgMapID\n\t\tlog.Info().Msgf(\"DownloadAndStartBPFProgram : program name %s previous program map name: %s\", bpf.Program.Name, bpf.PrevMapNamePath)\n\t}\n\n\tif err := bpf.VerifyAndGetArtifacts(c.HostConfig); err != nil {\n\t\treturn fmt.Errorf(\"failed to get artifacts %s with error: %w\", bpf.Program.Artifact, err)\n\t}\n\n\tif err := bpf.Start(ifaceName, c.Ifaces[ifaceName], direction, c.HostConfig.BpfChainingEnabled); err != nil {\n\t\treturn fmt.Errorf(\"failed to start bpf program %s with error: %w\", bpf.Program.Name, err)\n\t}\n\n\treturn nil\n}\n\n// Stopping all programs in order\nfunc (c *NFConfigs) StopNRemoveAllBPFPrograms(ifaceName, direction string) error {\n\n\tvar bpfList *list.List\n\n\tswitch direction {\n\tcase models.XDPIngressType:\n\t\tbpfList = c.IngressXDPBpfs[ifaceName]\n\t\tc.IngressXDPBpfs[ifaceName] = nil\n\tcase models.IngressType:\n\t\tbpfList = c.IngressTCBpfs[ifaceName]\n\t\tc.IngressTCBpfs[ifaceName] = nil\n\tcase models.EgressType:\n\t\tbpfList = c.EgressTCBpfs[ifaceName]\n\t\tc.EgressTCBpfs[ifaceName] = nil\n\tdefault: // we should never reach here\n\t\treturn fmt.Errorf(\"unknown direction type %s\", direction)\n\t}\n\n\tif bpfList == nil {\n\t\tlog.Warn().Msgf(\"no %s ebpf programs to stop\", direction)\n\t\treturn nil\n\t}\n\n\tfor e := bpfList.Front(); e != nil; {\n\t\tdata := e.Value.(*BPF)\n\t\tif err := data.Stop(ifaceName, c.Ifaces[ifaceName], direction, c.HostConfig.BpfChainingEnabled); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to stop program %s direction %s with err :%w\", data.Program.Name, direction, err)\n\t\t}\n\t\tnextBPF := e.Next()\n\t\tbpfList.Remove(e)\n\t\te = nextBPF\n\t}\n\n\treturn nil\n}\n\n// StopNRemoveAllBPFProbePrograms Stopping all probe programs in order\nfunc (c *NFConfigs) StopNRemoveAllBPFProbePrograms() error {\n\n\tfor e := c.ProbesBpfs.Front(); e != nil; {\n\t\tdata := e.Value.(*BPF)\n\t\tif err := data.Stop(\"\", \"\", \"\", false); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to stop probe program %s with err :%w\", data.Program.Name, err)\n\t\t}\n\t\tnextBPF := e.Next()\n\t\tc.ProbesBpfs.Remove(e)\n\t\te = nextBPF\n\t}\n\treturn nil\n}\n\n// VerifyNUpdateBPFProgram - This method checks the following conditions\n// 1. BPF Program already running with no change\n// 2. BPF Program running but needs to stop (admin_status == disabled)\n// 3. BPF Program running but needs version update\n// 4. BPF Program running but position change (seq_id change)\n// 5. BPF Program not running but needs to start.\n// 6. BPF Program running but map args change, will update the map values (i.e. Array and Hash maps only)\n// 7. BPF Program running but update args change, will invoke cmd_update with additional option --cmd=update\nfunc (c *NFConfigs) VerifyNUpdateBPFProgram(bpfProg *models.BPFProgram, ifaceName, direction string) error {\n\n\tvar bpfList *list.List\n\tif bpfProg == nil {\n\t\treturn nil\n\t}\n\n\tswitch direction {\n\tcase models.XDPIngressType:\n\t\tbpfList = c.IngressXDPBpfs[ifaceName]\n\tcase models.IngressType:\n\t\tbpfList = c.IngressTCBpfs[ifaceName]\n\tcase models.EgressType:\n\t\tbpfList = c.EgressTCBpfs[ifaceName]\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown direction type\")\n\t}\n\n\tfor e := bpfList.Front(); e != nil; e = e.Next() {\n\t\tdata := e.Value.(*BPF)\n\t\tif strings.Compare(data.Program.Name, bpfProg.Name) != 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif reflect.DeepEqual(data.Program, *bpfProg) {\n\t\t\t// Nothing to do\n\t\t\treturn nil\n\t\t}\n\n\t\t// Admin status change - disabled\n\t\tif data.Program.AdminStatus != bpfProg.AdminStatus {\n\t\t\tlog.Info().Msgf(\"verifyNUpdateBPFProgram :admin_status change detected - disabling the program %s\", data.Program.Name)\n\t\t\tdata.Program.AdminStatus = bpfProg.AdminStatus\n\t\t\tif err := data.Stop(ifaceName, c.Ifaces[ifaceName], direction, c.HostConfig.BpfChainingEnabled); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to stop to on admin_status change BPF %s iface %s direction %s admin_status %s with err %w\", bpfProg.Name, ifaceName, direction, bpfProg.AdminStatus, err)\n\t\t\t}\n\t\t\ttmpNextBPF := e.Next()\n\t\t\ttmpPreviousBPF := e.Prev()\n\t\t\tbpfList.Remove(e)\n\t\t\tif tmpNextBPF != nil && tmpNextBPF.Prev() != nil { // relink the next element\n\t\t\t\tif err := c.LinkBPFPrograms(tmpNextBPF.Prev().Value.(*BPF), tmpNextBPF.Value.(*BPF)); err != nil {\n\t\t\t\t\tlog.Error().Err(err).Msg(\"admin status disabled - failed LinkBPFPrograms\")\n\t\t\t\t\treturn fmt.Errorf(\"admin status disabled - failed LinkBPFPrograms %w\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// if chaining is disabled prev will be nil\n\t\t\tif tmpPreviousBPF == nil && tmpNextBPF == nil {\n\t\t\t\tswitch direction {\n\t\t\t\tcase models.XDPIngressType:\n\t\t\t\t\tc.IngressXDPBpfs[ifaceName] = nil\n\t\t\t\tcase models.IngressType:\n\t\t\t\t\tc.IngressTCBpfs[ifaceName] = nil\n\t\t\t\tcase models.EgressType:\n\t\t\t\t\tc.EgressTCBpfs[ifaceName] = nil\n\t\t\t\tdefault:\n\t\t\t\t\treturn fmt.Errorf(\"unknown direction type %s\", direction)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\t// Check if list contains root program only then stop the root program.\n\t\t\tif tmpPreviousBPF.Prev() == nil && tmpPreviousBPF.Next() == nil {\n\t\t\t\tlog.Info().Msg(\"no eBPF Programs are running, stopping root program\")\n\t\t\t\tif c.HostConfig.BpfChainingEnabled {\n\t\t\t\t\tif err := c.StopRootProgram(ifaceName, direction); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to stop to root program  %s iface %s direction %s with err: %w\", bpfProg.Name, ifaceName, direction, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\n\t\t// Version Change\n\t\tif data.Program.Version != bpfProg.Version || !reflect.DeepEqual(data.Program.StartArgs, bpfProg.StartArgs) {\n\t\t\tlog.Info().Msgf(\"VerifyNUpdateBPFProgram : version update initiated - current version %s new version %s\", data.Program.Version, bpfProg.Version)\n\t\t\tif err := data.Stop(ifaceName, c.Ifaces[ifaceName], direction, c.HostConfig.BpfChainingEnabled); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to stop older version of network function BPF %s iface %s direction %s version %s with err: %w\", bpfProg.Name, ifaceName, direction, bpfProg.Version, err)\n\t\t\t}\n\n\t\t\tdata.Program = *bpfProg\n\n\t\t\tif err := c.DownloadAndStartBPFProgram(e, ifaceName, direction); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to download and start newer version of network function BPF %s version %s iface %s direction %s with err: %w\", bpfProg.Name, bpfProg.Version, ifaceName, direction, err)\n\t\t\t}\n\n\t\t\t// update if not a last program\n\t\t\tif e.Next() != nil {\n\t\t\t\tdata.PutNextProgFDFromID(int(e.Next().Value.(*BPF).ProgID))\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}\n\n\t\t// monitor maps change\n\t\tif !reflect.DeepEqual(data.Program.MonitorMaps, bpfProg.MonitorMaps) {\n\t\t\tlog.Info().Msgf(\"monitor map list is mismatch - updated\")\n\t\t\tdata.Program.MonitorMaps = bpfProg.MonitorMaps\n\t\t}\n\n\t\t// Update CfgVersion\n\t\tdata.Program.CfgVersion = bpfProg.CfgVersion\n\n\t\t// Seq ID Change\n\t\tif data.Program.SeqID != bpfProg.SeqID {\n\t\t\tlog.Info().Msgf(\"VerifyNUpdateBPFProgram : seq id change detected %s current seq id %d new seq id %d\", data.Program.Name, data.Program.SeqID, bpfProg.SeqID)\n\n\t\t\t// Update seq id\n\t\t\tdata.Program.SeqID = bpfProg.SeqID\n\n\t\t\tif err := c.MoveToLocation(e, bpfList); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to move to new position in the chain BPF %s version %s iface %s direction %s with err: %w\", bpfProg.Name, bpfProg.Version, ifaceName, direction, err)\n\t\t\t}\n\t\t}\n\n\t\t// map arguments change - basically any config change to ebpf program updating config maps\n\t\tif !reflect.DeepEqual(data.Program.MapArgs, bpfProg.MapArgs) {\n\t\t\tlog.Info().Msg(\"maps_args are mismatched\")\n\t\t\tdata.Program.MapArgs = bpfProg.MapArgs\n\t\t\tdata.UpdateBPFMaps(ifaceName, c.Ifaces[ifaceName], direction)\n\t\t}\n\n\t\t// update arguments change - basically any config change to ebpf program config maps using user program\n\t\tif !reflect.DeepEqual(data.Program.UpdateArgs, bpfProg.UpdateArgs) {\n\t\t\tlog.Info().Msg(\"update_args are mismatched\")\n\t\t\tdata.Program.UpdateArgs = bpfProg.UpdateArgs\n\t\t\tdata.UpdateArgs(ifaceName, c.Ifaces[ifaceName], direction)\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tlog.Debug().Msgf(\"Program is not found in the list name %s\", bpfProg.Name)\n\t// if not found in the list.\n\tif err := c.InsertAndStartBPFProgram(bpfProg, ifaceName, direction); err != nil {\n\t\treturn fmt.Errorf(\"failed to insert and start BPFProgram to new location BPF %s version %s iface %s direction %s with err: %w\", bpfProg.Name, bpfProg.Version, ifaceName, direction, err)\n\t}\n\n\treturn nil\n}\n\nfunc (c *NFConfigs) MoveToLocation(element *list.Element, bpfList *list.List) error {\n\n\tif element == nil {\n\t\treturn fmt.Errorf(\"MoveToLocation - element is nil\")\n\t}\n\tbpf := element.Value.(*BPF)\n\n\tif bpfList == nil {\n\t\tlog.Warn().Msg(\"ebpf program list is empty\")\n\t\treturn nil\n\t}\n\n\tfor e := bpfList.Front(); e != nil; e = e.Next() {\n\t\tdata := e.Value.(*BPF)\n\n\t\tif data.Program.SeqID >= bpf.Program.SeqID && data.Program.Name != bpf.Program.Name {\n\t\t\tif element.Next() != nil && element.Prev() != nil {\n\t\t\t\tif err := c.LinkBPFPrograms(element.Prev().Value.(*BPF), element.Next().Value.(*BPF)); err != nil {\n\t\t\t\t\tlog.Error().Err(err).Msg(\"MoveToLocation - failed LinkBPFPrograms before move\")\n\t\t\t\t\treturn fmt.Errorf(\"MoveToLocation - failed LinkBPFPrograms before move %w\", err)\n\t\t\t\t}\n\t\t\t} else if element.Next() == nil && element.Prev() != nil {\n\t\t\t\tif err := element.Prev().Value.(*BPF).RemoveNextProgFD(); err != nil {\n\t\t\t\t\tlog.Error().Err(err).Msg(\"failed to remove program fd in map\")\n\t\t\t\t\treturn fmt.Errorf(\"failed to remove program fd in map %w\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbpfList.MoveBefore(element, e)\n\n\t\t\tif err := c.LinkBPFPrograms(element.Prev().Value.(*BPF), element.Value.(*BPF)); err != nil {\n\t\t\t\tlog.Error().Err(err).Msg(\"MoveToLocation - failed LinkBPFPrograms after move element to with prev prog\")\n\t\t\t\treturn fmt.Errorf(\"MoveToLocation - failed LinkBPFPrograms after move element to with prev prog %w\", err)\n\t\t\t}\n\n\t\t\tif element.Next() != nil {\n\t\t\t\tif err := c.LinkBPFPrograms(element.Value.(*BPF), element.Next().Value.(*BPF)); err != nil {\n\t\t\t\t\tlog.Error().Err(err).Msg(\"MoveToLocation - failed LinkBPFPrograms after move element to with next prog\")\n\t\t\t\t\treturn fmt.Errorf(\"MoveToLocation - failed LinkBPFPrograms after move element to with next prog %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tlog.Info().Msgf(\"MoveToLocation : Moved - %s\", element.Value.(*BPF).Program.Name)\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tlog.Info().Msg(\"element seq id greater than last element in the list move to back of the list\")\n\tif element.Next() != nil && element.Prev() != nil {\n\t\tif err := c.LinkBPFPrograms(element.Prev().Value.(*BPF), element.Next().Value.(*BPF)); err != nil {\n\t\t\tlog.Error().Err(err).Msg(\"MoveToLocation - failed LinkBPFPrograms before MoveToBack element to with prev prog\")\n\t\t\treturn fmt.Errorf(\"MoveToLocation - failed LinkBPFPrograms before MoveToBack element to with prev prog %w\", err)\n\t\t}\n\t}\n\n\tbpfList.MoveToBack(element)\n\tif element.Prev() != nil {\n\t\tif err := c.LinkBPFPrograms(element.Prev().Value.(*BPF), element.Value.(*BPF)); err != nil {\n\t\t\tlog.Error().Err(err).Msg(\"MoveToLocation - failed LinkBPFPrograms after MoveToBack element to with prev prog\")\n\t\t\treturn fmt.Errorf(\"MoveToLocation - failed LinkBPFPrograms after MoveToBack element to with prev prog %w\", err)\n\t\t}\n\t}\n\n\tif element.Next() == nil {\n\t\tif err := element.Value.(*BPF).RemoveNextProgFD(); err != nil {\n\t\t\tlog.Error().Err(err).Msg(\"failed to remove MoveToBack program fd in map\")\n\t\t\treturn fmt.Errorf(\"failed to remove MoveToBack program fd in map %w\", err)\n\t\t}\n\t}\n\n\tlog.Info().Msgf(\"MoveToLocation : MoveToBack Moved - %s\", element.Value.(*BPF).Program.Name)\n\treturn nil\n}\n\n// InsertAndStartBPFProgram method for tc programs\nfunc (c *NFConfigs) InsertAndStartBPFProgram(bpfProg *models.BPFProgram, ifaceName, direction string) error {\n\n\tvar bpfList *list.List\n\tif bpfProg == nil {\n\t\treturn fmt.Errorf(\"InsertAndStartBPFProgram - bpf program is nil\")\n\t}\n\n\tif bpfProg.AdminStatus == models.Disabled {\n\t\treturn nil\n\t}\n\n\tbpf := NewBpfProgram(c.Ctx, *bpfProg, c.HostConfig, ifaceName)\n\n\tswitch direction {\n\tcase models.XDPIngressType:\n\t\tbpfList = c.IngressXDPBpfs[ifaceName]\n\tcase models.IngressType:\n\t\tbpfList = c.IngressTCBpfs[ifaceName]\n\tcase models.EgressType:\n\t\tbpfList = c.EgressTCBpfs[ifaceName]\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown direction type\")\n\t}\n\n\tif bpfList == nil {\n\t\tlog.Warn().Msgf(\"%s program list is empty\", direction)\n\t\treturn nil\n\t}\n\n\tfor e := bpfList.Front(); e != nil; e = e.Next() {\n\t\tdata := e.Value.(*BPF)\n\t\tif data.Program.SeqID >= bpfProg.SeqID {\n\t\t\ttmpBPF := bpfList.InsertBefore(bpf, e)\n\t\t\tif err := c.DownloadAndStartBPFProgram(tmpBPF, ifaceName, direction); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to download and start network function %s version %s iface %s direction %s with err %w\", bpfProg.Name, bpfProg.Version, ifaceName, direction, err)\n\t\t\t}\n\n\t\t\tif tmpBPF.Next() != nil {\n\t\t\t\tif err := c.LinkBPFPrograms(tmpBPF.Value.(*BPF), tmpBPF.Next().Value.(*BPF)); err != nil {\n\t\t\t\t\tlog.Error().Err(err).Msg(\"InsertAndStartBPFProgram - failed LinkBPFPrograms after InsertBefore element to with next prog\")\n\t\t\t\t\treturn fmt.Errorf(\"InsertAndStartBPFProgram - failed LinkBPFPrograms after InsertBefore element to with next prog %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// insert at the end\n\tif err := c.PushBackAndStartBPF(bpfProg, ifaceName, direction); err != nil {\n\t\treturn fmt.Errorf(\"failed to push back and start network function %s version %s iface %s direction %s with err: %w\", bpfProg.Name, bpfProg.Version, ifaceName, direction, err)\n\t}\n\n\treturn nil\n}\n\n// StopRootProgram -This method stops the root program, removes the root node from the list and reset the list to nil\nfunc (c *NFConfigs) StopRootProgram(ifaceName, direction string) error {\n\n\tswitch direction {\n\tcase models.XDPIngressType:\n\t\tif c.IngressXDPBpfs[ifaceName] == nil {\n\t\t\tlog.Warn().Msg(\"xdp root program is not running\")\n\t\t\treturn nil\n\t\t}\n\n\t\tif err := c.IngressXDPBpfs[ifaceName].Front().Value.(*BPF).Stop(ifaceName, c.Ifaces[ifaceName], direction, c.HostConfig.BpfChainingEnabled); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to stop xdp root program iface %s with err %w\", ifaceName, err)\n\t\t}\n\t\tc.IngressXDPBpfs[ifaceName].Remove(c.IngressXDPBpfs[ifaceName].Front())\n\t\tc.IngressXDPBpfs[ifaceName] = nil\n\tcase models.IngressType:\n\t\tif c.IngressTCBpfs[ifaceName] == nil {\n\t\t\tlog.Warn().Msgf(\"tc root program %s not running\", direction)\n\t\t\treturn nil\n\t\t}\n\t\tif err := c.IngressTCBpfs[ifaceName].Front().Value.(*BPF).Stop(ifaceName, c.Ifaces[ifaceName], direction, c.HostConfig.BpfChainingEnabled); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to stop ingress tc root program on interface %s with err %w\", ifaceName, err)\n\t\t}\n\t\tc.IngressTCBpfs[ifaceName].Remove(c.IngressTCBpfs[ifaceName].Front())\n\t\tc.IngressTCBpfs[ifaceName] = nil\n\tcase models.EgressType:\n\t\tif c.EgressTCBpfs[ifaceName] == nil {\n\t\t\tlog.Warn().Msgf(\"tc root program %s not running\", direction)\n\t\t\treturn nil\n\t\t}\n\t\tif err := c.EgressTCBpfs[ifaceName].Front().Value.(*BPF).Stop(ifaceName, c.Ifaces[ifaceName], direction, c.HostConfig.BpfChainingEnabled); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to stop egress tc root program on interface %s with err %w\", ifaceName, err)\n\t\t}\n\t\tc.EgressTCBpfs[ifaceName].Remove(c.EgressTCBpfs[ifaceName].Front())\n\t\tc.EgressTCBpfs[ifaceName] = nil\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown direction type\")\n\t}\n\n\treturn nil\n}\n\n// Link BPF programs\nfunc (c *NFConfigs) LinkBPFPrograms(leftBPF, rightBPF *BPF) error {\n\tlog.Info().Msgf(\"LinkBPFPrograms : left BPF Prog %s right BPF Prog %s\", leftBPF.Program.Name, rightBPF.Program.Name)\n\trightBPF.PrevMapNamePath = leftBPF.MapNamePath\n\trightBPF.PrevProgMapID = leftBPF.PrevProgMapID\n\tif err := leftBPF.PutNextProgFDFromID(int(rightBPF.ProgID)); err != nil {\n\t\tlog.Error().Err(err).Msgf(\"LinkBPFPrograms - failed to update program fd in prev prog map before move\")\n\t\treturn fmt.Errorf(\"LinkBPFPrograms - failed to update program fd in prev prog prog map before move %w\", err)\n\t}\n\treturn nil\n}\n\n// BPFDetails - Method provides dump of BPFs for debug purpose\nfunc (c *NFConfigs) BPFDetails(iface string) []*BPF {\n\tarrBPFDetails := make([]*BPF, 0)\n\tbpfList := c.IngressXDPBpfs[iface]\n\tif bpfList != nil {\n\t\tfor e := bpfList.Front(); e != nil; e = e.Next() {\n\t\t\tarrBPFDetails = append(arrBPFDetails, e.Value.(*BPF))\n\t\t}\n\t}\n\tbpfList = c.IngressTCBpfs[iface]\n\tif bpfList != nil {\n\t\tfor e := bpfList.Front(); e != nil; e = e.Next() {\n\t\t\tarrBPFDetails = append(arrBPFDetails, e.Value.(*BPF))\n\t\t}\n\t}\n\tbpfList = c.EgressTCBpfs[iface]\n\tif bpfList != nil {\n\t\tfor e := bpfList.Front(); e != nil; e = e.Next() {\n\t\t\tarrBPFDetails = append(arrBPFDetails, e.Value.(*BPF))\n\t\t}\n\t}\n\n\tfor e := c.ProbesBpfs.Front(); e != nil; e = e.Next() {\n\t\tarrBPFDetails = append(arrBPFDetails, e.Value.(*BPF))\n\t}\n\n\treturn arrBPFDetails\n}\n\nfunc (c *NFConfigs) Deploy(ifaceName, HostName string, bpfProgs *models.BPFPrograms) error {\n\n\tif HostName != c.HostName {\n\t\terrOut := fmt.Errorf(\"provided bpf programs do not belong to this host\")\n\t\tlog.Error().Err(errOut)\n\t\tstats.Add(1, stats.BPFDeployFailedCount, \"\", \"\", ifaceName, c.Ifaces[ifaceName])\n\t\treturn errOut\n\t}\n\n\tif ifaceName == \"\" || bpfProgs == nil {\n\t\terrOut := fmt.Errorf(\"iface name or bpf programs are empty\")\n\t\tlog.Error().Err(errOut)\n\t\tstats.Add(1, stats.BPFDeployFailedCount, \"\", \"\", ifaceName, c.Ifaces[ifaceName])\n\t\treturn errOut\n\t}\n\n\tc.HostInterfaces, _ = getHostInterfaces()\n\tif _, ok := c.HostInterfaces[ifaceName]; !ok {\n\t\tc.CleanupProgramsOnInterface(ifaceName)\n\t\terrOut := fmt.Errorf(\"%s interface name not found in the host Stop called\", ifaceName)\n\t\tstats.Add(1, stats.BPFDeployFailedCount, \"\", \"\", ifaceName, c.Ifaces[ifaceName])\n\t\tlog.Error().Err(errOut)\n\t\treturn errOut\n\t}\n\tc.Mu.Lock()\n\tdefer c.Mu.Unlock()\n\n\tfor _, bpfProg := range bpfProgs.XDPIngress {\n\t\tif c.IngressXDPBpfs[ifaceName] == nil {\n\t\t\tif bpfProg.AdminStatus == models.Enabled {\n\t\t\t\tc.IngressXDPBpfs[ifaceName] = list.New()\n\t\t\t\tif err := c.VerifyAndStartXDPRootProgram(ifaceName, models.XDPIngressType); err != nil {\n\t\t\t\t\tc.IngressXDPBpfs[ifaceName] = nil\n\t\t\t\t\tstats.Add(1, stats.BPFDeployFailedCount, bpfProg.Name, models.XDPType, ifaceName, c.Ifaces[ifaceName])\n\t\t\t\t\treturn fmt.Errorf(\"failed to chain XDP BPF programs: %w\", err)\n\t\t\t\t}\n\t\t\t\tlog.Info().Msgf(\"Push Back and Start XDP program : %s seq_id : %d\", bpfProg.Name, bpfProg.SeqID)\n\t\t\t\tif err := c.PushBackAndStartBPF(bpfProg, ifaceName, models.XDPIngressType); err != nil {\n\t\t\t\t\tstats.Add(1, stats.BPFDeployFailedCount, bpfProg.Name, models.XDPType, ifaceName, c.Ifaces[ifaceName])\n\t\t\t\t\treturn fmt.Errorf(\"failed to update BPF Program: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if err := c.VerifyNUpdateBPFProgram(bpfProg, ifaceName, models.XDPIngressType); err != nil {\n\t\t\tstats.Add(1, stats.BPFDeployFailedCount, bpfProg.Name, models.XDPType, ifaceName, c.Ifaces[ifaceName])\n\t\t\treturn fmt.Errorf(\"failed to update xdp BPF Program: %w\", err)\n\t\t}\n\t}\n\n\tfor _, bpfProg := range bpfProgs.TCIngress {\n\t\tif c.IngressTCBpfs[ifaceName] == nil {\n\t\t\tif bpfProg.AdminStatus == models.Enabled {\n\t\t\t\tc.IngressTCBpfs[ifaceName] = list.New()\n\t\t\t\tif err := c.VerifyAndStartTCRootProgram(ifaceName, models.IngressType); err != nil {\n\t\t\t\t\tc.IngressTCBpfs[ifaceName] = nil\n\t\t\t\t\tstats.Add(1, stats.BPFDeployFailedCount, bpfProg.Name, models.IngressType, ifaceName, c.Ifaces[ifaceName])\n\t\t\t\t\treturn fmt.Errorf(\"failed to chain ingress tc bpf programs: %w\", err)\n\t\t\t\t}\n\t\t\t\tif err := c.PushBackAndStartBPF(bpfProg, ifaceName, models.IngressType); err != nil {\n\t\t\t\t\tstats.Add(1, stats.BPFDeployFailedCount, bpfProg.Name, models.IngressType, ifaceName, c.Ifaces[ifaceName])\n\t\t\t\t\treturn fmt.Errorf(\"failed to update BPF Program: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if err := c.VerifyNUpdateBPFProgram(bpfProg, ifaceName, models.IngressType); err != nil {\n\t\t\tstats.Add(1, stats.BPFDeployFailedCount, bpfProg.Name, models.IngressType, ifaceName, c.Ifaces[ifaceName])\n\t\t\treturn fmt.Errorf(\"failed to update BPF Program: %w\", err)\n\t\t}\n\t}\n\n\tfor _, bpfProg := range bpfProgs.TCEgress {\n\t\tif c.EgressTCBpfs[ifaceName] == nil {\n\t\t\tif bpfProg.AdminStatus == models.Enabled {\n\t\t\t\tc.EgressTCBpfs[ifaceName] = list.New()\n\t\t\t\tif err := c.VerifyAndStartTCRootProgram(ifaceName, models.EgressType); err != nil {\n\t\t\t\t\tc.EgressTCBpfs[ifaceName] = nil\n\t\t\t\t\tstats.Add(1, stats.BPFDeployFailedCount, bpfProg.Name, models.EgressType, ifaceName, c.Ifaces[ifaceName])\n\t\t\t\t\treturn fmt.Errorf(\"failed to chain ingress tc bpf programs: %w\", err)\n\t\t\t\t}\n\t\t\t\tif err := c.PushBackAndStartBPF(bpfProg, ifaceName, models.EgressType); err != nil {\n\t\t\t\t\tstats.Add(1, stats.BPFDeployFailedCount, bpfProg.Name, models.EgressType, ifaceName, c.Ifaces[ifaceName])\n\t\t\t\t\treturn fmt.Errorf(\"failed to update BPF Program: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if err := c.VerifyNUpdateBPFProgram(bpfProg, ifaceName, models.EgressType); err != nil {\n\t\t\tstats.Add(1, stats.BPFDeployFailedCount, bpfProg.Name, models.EgressType, ifaceName, c.Ifaces[ifaceName])\n\t\t\treturn fmt.Errorf(\"failed to update BPF Program: %w\", err)\n\t\t}\n\t}\n\n\tfor _, bpfProg := range bpfProgs.Probes {\n\t\tif err := c.PushBackAndStartProbe(bpfProg); err != nil {\n\t\t\tstats.Add(1, stats.BPFDeployFailedCount, bpfProg.Name, bpfProg.ProgType, ifaceName, c.Ifaces[ifaceName])\n\t\t\treturn fmt.Errorf(\"failed to update Probe BPF Program: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// DeployeBPFPrograms - Starts eBPF programs on the node if they are not running\nfunc (c *NFConfigs) DeployeBPFPrograms(bpfProgs []models.L3afBPFPrograms) error {\n\tvar combinedError error\n\n\tfor _, bpfProg := range bpfProgs {\n\t\tif len(c.Ifaces) == 0 {\n\t\t\tc.Ifaces = map[string]string{bpfProg.Iface: bpfProg.IPv4Address}\n\t\t} else {\n\t\t\tc.Ifaces[bpfProg.Iface] = bpfProg.IPv4Address\n\t\t}\n\t\tif err := c.Deploy(bpfProg.Iface, bpfProg.HostName, bpfProg.BpfPrograms); err != nil {\n\t\t\tcombinedError = errors.Join(combinedError, fmt.Errorf(\"failed to deploy BPF program on iface %s with error: %w\", bpfProg.Iface, err))\n\t\t}\n\t}\n\n\tif err := c.RemoveMissingNetIfacesNBPFProgsInConfig(bpfProgs); err != nil {\n\t\tlog.Warn().Err(err).Msgf(\"Remove missing interfaces and BPF programs in the config failed with error \")\n\t}\n\tif err := c.SaveConfigsToConfigStore(); err != nil {\n\t\tcombinedError = errors.Join(combinedError, fmt.Errorf(\"deploy eBPF Programs failed to save configs %w\", err))\n\t}\n\treturn combinedError\n}\n\n// SaveConfigsToConfigStore - Writes configs to persistent store\nfunc (c *NFConfigs) SaveConfigsToConfigStore() error {\n\n\t// StoreFile name is not defined then skip it\n\tif len(c.HostConfig.L3afConfigStoreFileName) < 1 {\n\t\treturn nil\n\t}\n\n\tvar bpfProgs []models.L3afBPFPrograms\n\n\tc.HostInterfaces, _ = getHostInterfaces()\n\tfor iface := range c.Ifaces {\n\t\tif _, interfaceFound := c.HostInterfaces[iface]; interfaceFound {\n\t\t\tlog.Info().Msgf(\"SaveConfigsToConfigStore - %s\", iface)\n\t\t\tbpfPrograms := c.EBPFPrograms(iface)\n\t\t\tbpfProgs = append(bpfProgs, bpfPrograms)\n\t\t}\n\t}\n\n\tfile, err := json.MarshalIndent(bpfProgs, \"\", \" \")\n\tif err != nil {\n\t\tlog.Error().Err(err).Msgf(\"failed to marshal configs to save\")\n\t\treturn fmt.Errorf(\"failed to marshal configs %w\", err)\n\t}\n\tif err = os.WriteFile(c.HostConfig.L3afConfigStoreFileName, file, 0644); err != nil {\n\t\tlog.Error().Err(err).Msgf(\"failed write to file operation\")\n\t\treturn fmt.Errorf(\"failed to save configs %w\", err)\n\t}\n\n\treturn nil\n}\n\n// EBPFPrograms - Method provides list of eBPF Programs running on iface\nfunc (c *NFConfigs) EBPFPrograms(iface string) models.L3afBPFPrograms {\n\tBPFProgram := models.L3afBPFPrograms{\n\t\tHostName:    c.HostName,\n\t\tIface:       iface,\n\t\tIPv4Address: c.Ifaces[iface],\n\t\tBpfPrograms: &models.BPFPrograms{},\n\t}\n\n\tbpfList := c.IngressXDPBpfs[iface]\n\tif bpfList != nil {\n\t\te := bpfList.Front()\n\t\tif c.HostConfig.BpfChainingEnabled && e.Value.(*BPF).Program.Name == c.HostConfig.XDPRootPackageName {\n\t\t\te = e.Next()\n\t\t}\n\t\tfor ; e != nil; e = e.Next() {\n\t\t\tBPFProgram.BpfPrograms.XDPIngress = append(BPFProgram.BpfPrograms.XDPIngress, &e.Value.(*BPF).Program)\n\t\t}\n\t}\n\tbpfList = c.IngressTCBpfs[iface]\n\tif bpfList != nil {\n\t\te := bpfList.Front()\n\t\tif c.HostConfig.BpfChainingEnabled && e.Value.(*BPF).Program.Name == c.HostConfig.TCRootPackageName {\n\t\t\te = e.Next()\n\t\t}\n\t\tfor ; e != nil; e = e.Next() {\n\t\t\tBPFProgram.BpfPrograms.TCIngress = append(BPFProgram.BpfPrograms.TCIngress, &e.Value.(*BPF).Program)\n\t\t}\n\t}\n\tbpfList = c.EgressTCBpfs[iface]\n\tif bpfList != nil {\n\t\te := bpfList.Front()\n\t\tif c.HostConfig.BpfChainingEnabled && e.Value.(*BPF).Program.Name == c.HostConfig.TCRootPackageName {\n\t\t\te = e.Next()\n\t\t}\n\t\tfor ; e != nil; e = e.Next() {\n\t\t\tBPFProgram.BpfPrograms.TCEgress = append(BPFProgram.BpfPrograms.TCEgress, &e.Value.(*BPF).Program)\n\t\t}\n\t}\n\n\te := c.ProbesBpfs.Front()\n\tfor ; e != nil; e = e.Next() {\n\t\tBPFProgram.BpfPrograms.Probes = append(BPFProgram.BpfPrograms.Probes, &e.Value.(*BPF).Program)\n\t}\n\n\treturn BPFProgram\n}\n\n// EBPFProgramsAll - Method provides list of eBPF Programs running on all ifaces on the host\nfunc (c *NFConfigs) EBPFProgramsAll() []models.L3afBPFPrograms {\n\n\tBPFPrograms := make([]models.L3afBPFPrograms, 0)\n\tfor iface := range c.Ifaces {\n\t\tBPFProgram := c.EBPFPrograms(iface)\n\t\tBPFPrograms = append(BPFPrograms, BPFProgram)\n\t}\n\n\treturn BPFPrograms\n}\n\n// RemoveMissingNetIfacesNBPFProgsInConfig - Stops running eBPF programs which are missing in the config\nfunc (c *NFConfigs) RemoveMissingNetIfacesNBPFProgsInConfig(bpfProgCfgs []models.L3afBPFPrograms) error {\n\ttempIfaces := map[string]bool{}\n\twg := sync.WaitGroup{}\n\tfor _, bpfProg := range bpfProgCfgs {\n\t\ttempIfaces[bpfProg.Iface] = true\n\t\tif ifaceName, ok := c.Ifaces[bpfProg.Iface]; ok {\n\t\t\t_, ok := c.IngressXDPBpfs[ifaceName]\n\t\t\tif ok {\n\t\t\t\twg.Add(1)\n\t\t\t\tgo func(bpfProg models.L3afBPFPrograms) {\n\t\t\t\t\tdefer wg.Done()\n\t\t\t\t\tif err := c.RemoveMissingBPFProgramsInConfig(bpfProg, ifaceName, models.XDPIngressType); err != nil {\n\t\t\t\t\t\tlog.Error().Err(err).Msgf(\"Failed to stop missing program for network interface %s direction Ingress\", ifaceName)\n\t\t\t\t\t}\n\t\t\t\t}(bpfProg)\n\t\t\t}\n\t\t\t_, ok = c.IngressTCBpfs[ifaceName]\n\t\t\tif ok {\n\t\t\t\twg.Add(1)\n\t\t\t\tgo func(bpfProg models.L3afBPFPrograms) {\n\t\t\t\t\tdefer wg.Done()\n\t\t\t\t\tif err := c.RemoveMissingBPFProgramsInConfig(bpfProg, ifaceName, models.IngressType); err != nil {\n\t\t\t\t\t\tlog.Error().Err(err).Msgf(\"Failed to stop missing program for network interface %s direction Ingress\", ifaceName)\n\t\t\t\t\t}\n\t\t\t\t}(bpfProg)\n\t\t\t}\n\t\t\t_, ok = c.EgressTCBpfs[ifaceName]\n\t\t\tif ok {\n\t\t\t\twg.Add(1)\n\t\t\t\tgo func(bpfProg models.L3afBPFPrograms) {\n\t\t\t\t\tdefer wg.Done()\n\t\t\t\t\tif err := c.RemoveMissingBPFProgramsInConfig(bpfProg, ifaceName, models.EgressType); err != nil {\n\t\t\t\t\t\tlog.Error().Err(err).Msgf(\"Failed to stop missing program for network interface %s direction Ingress\", ifaceName)\n\t\t\t\t\t}\n\t\t\t\t}(bpfProg)\n\t\t\t}\n\t\t}\n\t}\n\twg.Wait()\n\n\tfor ifaceName := range c.Ifaces {\n\t\tif _, ok := tempIfaces[ifaceName]; !ok {\n\t\t\tlog.Info().Msgf(\"Missing Network Interface %s in the configs, stopping\", ifaceName)\n\t\t\tif err := c.StopNRemoveAllBPFPrograms(ifaceName, models.XDPIngressType); err != nil {\n\t\t\t\tlog.Error().Err(err).Msgf(\"Failed to stop all the program in the direction xdp ingress for interface %s\", ifaceName)\n\t\t\t}\n\t\t\tif err := c.StopNRemoveAllBPFPrograms(ifaceName, models.IngressType); err != nil {\n\t\t\t\tlog.Error().Err(err).Msgf(\"Failed to stop all the program in the direction tc ingress for interface %s\", ifaceName)\n\t\t\t}\n\t\t\tif err := c.StopNRemoveAllBPFPrograms(ifaceName, models.EgressType); err != nil {\n\t\t\t\tlog.Error().Err(err).Msgf(\"Failed to stop all the program in the direction tc egress for interface %s\", ifaceName)\n\t\t\t}\n\t\t\tdelete(c.Ifaces, ifaceName)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// RemoveMissingBPFProgramsInConfig - This method to stop the eBPF programs which are not listed in the config.\nfunc (c *NFConfigs) RemoveMissingBPFProgramsInConfig(bpfProg models.L3afBPFPrograms, ifaceName, direction string) error {\n\tvar bpfProgArr []*models.BPFProgram\n\tvar bpfList *list.List\n\tswitch direction {\n\tcase models.XDPIngressType:\n\t\tbpfProgArr = bpfProg.BpfPrograms.XDPIngress\n\t\tbpfList = c.IngressXDPBpfs[ifaceName]\n\tcase models.IngressType:\n\t\tbpfProgArr = bpfProg.BpfPrograms.TCIngress\n\t\tbpfList = c.IngressTCBpfs[ifaceName]\n\tcase models.EgressType:\n\t\tbpfProgArr = bpfProg.BpfPrograms.TCEgress\n\t\tbpfList = c.EgressTCBpfs[ifaceName]\n\tdefault: // we should never reach here\n\t\treturn fmt.Errorf(\"unknown direction type %s\", direction)\n\t}\n\n\tif bpfList == nil {\n\t\t// Empty list, Nothing to check return\n\t\treturn nil\n\t}\n\n\te := bpfList.Front()\n\tif e != nil && c.HostConfig.BpfChainingEnabled {\n\t\te = e.Next()\n\t}\n\tfor ; e != nil; e = e.Next() {\n\t\tprog := e.Value.(*BPF)\n\t\tFound := false\n\t\tfor _, bpfConfigProg := range bpfProgArr {\n\t\t\tif bpfConfigProg.Name == prog.Program.Name {\n\t\t\t\tFound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !Found {\n\t\t\tlog.Info().Msgf(\"eBPF Program not found in config stopping - %s direction %s\", prog.Program.Name, direction)\n\t\t\tprog.Program.AdminStatus = models.Disabled\n\t\t\tif err := prog.Stop(ifaceName, c.Ifaces[ifaceName], direction, c.HostConfig.BpfChainingEnabled); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to stop to on removed config BPF %s iface %s direction %s with err %w\", prog.Program.Name, ifaceName, models.XDPIngressType, err)\n\t\t\t}\n\t\t\ttmpNextBPF := e.Next()\n\t\t\ttmpPreviousBPF := e.Prev()\n\t\t\tbpfList.Remove(e)\n\t\t\tif tmpNextBPF != nil && tmpNextBPF.Prev() != nil { // relink the next element\n\t\t\t\tif err := c.LinkBPFPrograms(tmpNextBPF.Prev().Value.(*BPF), tmpNextBPF.Value.(*BPF)); err != nil {\n\t\t\t\t\tlog.Error().Err(err).Msgf(\"missing config - failed LinkBPFPrograms\")\n\t\t\t\t\treturn fmt.Errorf(\"missing config - failed LinkBPFPrograms %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Check if list contains root program only then stop the root program.\n\t\t\tif tmpPreviousBPF.Prev() == nil && tmpPreviousBPF.Next() == nil {\n\t\t\t\tlog.Info().Msgf(\"no eBPF Programs are running, stopping root program\")\n\n\t\t\t\tif err := c.StopRootProgram(ifaceName, direction); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to stop to root program of iface %s direction XDP Ingress with err %w\", ifaceName, err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// getHostInterfaces - return host network interfaces\nfunc getHostInterfaces() (map[string]bool, error) {\n\tvar hostIfaces = make(map[string]bool, 0)\n\tifaces, err := net.Interfaces()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get net interfaces: %w\", err)\n\t}\n\n\tfor _, iface := range ifaces {\n\t\tif iface.Flags&net.FlagLoopback != 0 {\n\t\t\tcontinue // loopback interface\n\t\t}\n\t\thostIfaces[iface.Name] = true\n\t}\n\treturn hostIfaces, nil\n}\n\nfunc (c *NFConfigs) AddAndStartBPF(bpfProg *models.BPFProgram, ifaceName string, direction string) error {\n\tvar bpfList *list.List\n\tif bpfProg == nil {\n\t\treturn fmt.Errorf(\"AddAndStartBPF - bpf program is nil\")\n\t}\n\n\tif bpfProg.AdminStatus == models.Disabled {\n\t\treturn nil\n\t}\n\n\tswitch direction {\n\tcase models.XDPIngressType:\n\t\tbpfList = c.IngressXDPBpfs[ifaceName]\n\tcase models.IngressType:\n\t\tbpfList = c.IngressTCBpfs[ifaceName]\n\tcase models.EgressType:\n\t\tbpfList = c.EgressTCBpfs[ifaceName]\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown direction type\")\n\t}\n\n\tfor e := bpfList.Front(); e != nil; e = e.Next() {\n\t\tdata := e.Value.(*BPF)\n\t\tif data.Program.Name == bpfProg.Name {\n\t\t\tlog.Warn().Msgf(\"%v is already running on %v iface and in %v direction \", data.Program.Name, ifaceName, direction)\n\t\t\treturn nil\n\t\t}\n\n\t\tif data.Program.SeqID == bpfProg.SeqID {\n\t\t\tlog.Warn().Msgf(\"duplicate seq Id detected for %v in direction %v\", data.Program.Name, direction)\n\t\t\treturn nil\n\t\t}\n\t}\n\tfor e := bpfList.Front(); e != nil; e = e.Next() {\n\t\tdata := e.Value.(*BPF)\n\t\tif data.Program.SeqID > bpfProg.SeqID {\n\t\t\tbpf := NewBpfProgram(c.Ctx, *bpfProg, c.HostConfig, ifaceName)\n\t\t\ttmpBPF := bpfList.InsertBefore(bpf, e)\n\t\t\tif err := c.DownloadAndStartBPFProgram(tmpBPF, ifaceName, direction); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to download and start eBPF program %s version %s iface %s direction %s with err %w\", bpfProg.Name, bpfProg.Version, ifaceName, direction, err)\n\t\t\t}\n\n\t\t\tif tmpBPF.Next() != nil {\n\t\t\t\tif err := c.LinkBPFPrograms(tmpBPF.Value.(*BPF), tmpBPF.Next().Value.(*BPF)); err != nil {\n\t\t\t\t\tlog.Error().Err(err).Msg(\"AddAndStartBPF - failed LinkBPFPrograms after InsertBefore element to with next prog\")\n\t\t\t\t\treturn fmt.Errorf(\"AddAndStartBPFProg - failed LinkBPFPrograms after InsertBefore element to with next prog %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t// insert at the end\n\tif err := c.PushBackAndStartBPF(bpfProg, ifaceName, direction); err != nil {\n\t\treturn fmt.Errorf(\"failed to push back and start eBPF Program %s version %s iface %s direction %s with err %w\", bpfProg.Name, bpfProg.Version, ifaceName, direction, err)\n\t}\n\n\treturn nil\n}\n\n// AddProgramWithoutChaining : add eBPF program on given interface when chaining is not enabled\nfunc (c *NFConfigs) AddProgramWithoutChaining(ifaceName string, bpfProgs *models.BPFPrograms) error {\n\tif c.HostConfig.BpfChainingEnabled {\n\t\treturn nil\n\t}\n\n\tif len(bpfProgs.XDPIngress) > 1 || len(bpfProgs.TCIngress) > 1 || len(bpfProgs.TCEgress) > 1 {\n\t\treturn fmt.Errorf(\"failed to add multiple programs because chaining is disabled\")\n\t}\n\n\tif len(bpfProgs.XDPIngress) == 1 {\n\t\tbpfProg := bpfProgs.XDPIngress[0]\n\t\tif bpfProg.AdminStatus == models.Enabled {\n\t\t\tif c.IngressXDPBpfs[ifaceName] == nil {\n\t\t\t\tc.IngressXDPBpfs[ifaceName] = list.New()\n\t\t\t\tif err := c.PushBackAndStartBPF(bpfProg, ifaceName, models.XDPIngressType); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to PushBackAndStartBPF BPF Program: %w\", err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tprog := c.IngressXDPBpfs[ifaceName].Front().Value.(*BPF)\n\t\t\t\treturn fmt.Errorf(\"failed to add %v due to existing program %v on iface %v direction %v\", bpfProg.Name, prog.Program.Name, ifaceName, models.XDPIngressType)\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(bpfProgs.TCIngress) == 1 {\n\t\tbpfProg := bpfProgs.TCIngress[0]\n\t\tif bpfProg.AdminStatus == models.Enabled {\n\t\t\tif c.IngressTCBpfs[ifaceName] == nil {\n\t\t\t\tc.IngressTCBpfs[ifaceName] = list.New()\n\t\t\t\tif err := c.PushBackAndStartBPF(bpfProg, ifaceName, models.IngressType); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to PushBackAndStartBPF BPF Program: %w\", err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tprog := c.IngressTCBpfs[ifaceName].Front().Value.(*BPF)\n\t\t\t\treturn fmt.Errorf(\"failed to add %v due to existing program %v on iface %v direction %v\", bpfProg.Name, prog.Program.Name, ifaceName, models.IngressType)\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(bpfProgs.TCEgress) == 1 {\n\t\tbpfProg := bpfProgs.TCEgress[0]\n\t\tif bpfProg.AdminStatus == models.Enabled {\n\t\t\tif c.EgressTCBpfs[ifaceName] == nil {\n\t\t\t\tc.EgressTCBpfs[ifaceName] = list.New()\n\t\t\t\tif err := c.PushBackAndStartBPF(bpfProg, ifaceName, models.EgressType); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to PushBackAndStartBPF BPF Program: %w\", err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tprog := c.EgressTCBpfs[ifaceName].Front().Value.(*BPF)\n\t\t\t\treturn fmt.Errorf(\"failed to add %v due to existing program %v on iface %v direction %v\", bpfProg.Name, prog.Program.Name, ifaceName, models.EgressType)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// AddProgramsOnInterface will add given ebpf programs on given interface\nfunc (c *NFConfigs) AddProgramsOnInterface(ifaceName, HostName string, bpfProgs *models.BPFPrograms) error {\n\n\tif HostName != c.HostName {\n\t\terrOut := fmt.Errorf(\"provided bpf programs do not belong to this host\")\n\t\tlog.Error().Err(errOut)\n\t\treturn errOut\n\t}\n\n\tif ifaceName == \"\" || bpfProgs == nil {\n\t\terrOut := fmt.Errorf(\"iface name or bpf programs are empty\")\n\t\tlog.Error().Err(errOut)\n\t\treturn errOut\n\t}\n\n\tc.HostInterfaces, _ = getHostInterfaces()\n\tif _, ok := c.HostInterfaces[ifaceName]; !ok {\n\t\terrOut := fmt.Errorf(\"%s interface name not found in the host\", ifaceName)\n\t\tlog.Error().Err(errOut)\n\t\treturn errOut\n\t}\n\n\tc.Mu.Lock()\n\tdefer c.Mu.Unlock()\n\n\tif !c.HostConfig.BpfChainingEnabled {\n\t\terrout := c.AddProgramWithoutChaining(ifaceName, bpfProgs)\n\t\tif errout != nil {\n\t\t\treturn errout\n\t\t}\n\t\treturn nil\n\t}\n\n\tfor _, bpfProg := range bpfProgs.XDPIngress {\n\t\tif c.IngressXDPBpfs[ifaceName] == nil {\n\t\t\tif bpfProg.AdminStatus == models.Enabled {\n\t\t\t\tc.IngressXDPBpfs[ifaceName] = list.New()\n\t\t\t\tif err := c.VerifyAndStartXDPRootProgram(ifaceName, models.XDPIngressType); err != nil {\n\t\t\t\t\tc.IngressXDPBpfs[ifaceName] = nil\n\t\t\t\t\treturn fmt.Errorf(\"failed to chain XDP BPF programs: %w\", err)\n\t\t\t\t}\n\n\t\t\t\tlog.Info().Msgf(\"Push Back and Start XDP program : %s seq_id : %d\", bpfProg.Name, bpfProg.SeqID)\n\t\t\t\tif err := c.PushBackAndStartBPF(bpfProg, ifaceName, models.XDPIngressType); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to PushBackAndStartBPF BPF Program: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if err := c.AddAndStartBPF(bpfProg, ifaceName, models.XDPIngressType); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to AddAndStartBPF xdp BPF Program: %w\", err)\n\t\t}\n\t}\n\n\tfor _, bpfProg := range bpfProgs.TCIngress {\n\t\tif c.IngressTCBpfs[ifaceName] == nil {\n\t\t\tif bpfProg.AdminStatus == models.Enabled {\n\t\t\t\tc.IngressTCBpfs[ifaceName] = list.New()\n\t\t\t\tif err := c.VerifyAndStartTCRootProgram(ifaceName, models.IngressType); err != nil {\n\t\t\t\t\tc.IngressTCBpfs[ifaceName] = nil\n\t\t\t\t\treturn fmt.Errorf(\"failed to chain ingress tc bpf programs: %w\", err)\n\t\t\t\t}\n\n\t\t\t\tif err := c.PushBackAndStartBPF(bpfProg, ifaceName, models.IngressType); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to PushBackAndStartBPF BPF Program: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if err := c.AddAndStartBPF(bpfProg, ifaceName, models.IngressType); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to AddAndStartBPF tcingress BPF Program: %w\", err)\n\t\t}\n\t}\n\n\tfor _, bpfProg := range bpfProgs.TCEgress {\n\t\tif c.EgressTCBpfs[ifaceName] == nil {\n\t\t\tif bpfProg.AdminStatus == models.Enabled {\n\t\t\t\tc.EgressTCBpfs[ifaceName] = list.New()\n\t\t\t\tif err := c.VerifyAndStartTCRootProgram(ifaceName, models.EgressType); err != nil {\n\t\t\t\t\tc.EgressTCBpfs[ifaceName] = nil\n\t\t\t\t\treturn fmt.Errorf(\"failed to chain ingress tc bpf programs: %w\", err)\n\t\t\t\t}\n\t\t\t\tif err := c.PushBackAndStartBPF(bpfProg, ifaceName, models.EgressType); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to PushBackAndStartBPF BPF Program: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if err := c.AddAndStartBPF(bpfProg, ifaceName, models.EgressType); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to AddAndStartBPF tcegress BPF Program: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// AddeBPFPrograms - Starts eBPF programs on the node if they are not running\nfunc (c *NFConfigs) AddeBPFPrograms(bpfProgs []models.L3afBPFPrograms) error {\n\tvar combinedError error\n\tfor _, bpfProg := range bpfProgs {\n\t\tc.Ifaces = map[string]string{bpfProg.Iface: bpfProg.IPv4Address}\n\t\tif len(c.Ifaces) == 0 {\n\t\t\tc.Ifaces = map[string]string{bpfProg.Iface: bpfProg.IPv4Address}\n\t\t} else {\n\t\t\tc.Ifaces[bpfProg.Iface] = bpfProg.IPv4Address\n\t\t}\n\t\tif err := c.AddProgramsOnInterface(bpfProg.Iface, bpfProg.HostName, bpfProg.BpfPrograms); err != nil {\n\t\t\tif err := c.SaveConfigsToConfigStore(); err != nil {\n\t\t\t\tcombinedError = errors.Join(combinedError, fmt.Errorf(\"add eBPF Programs failed to save configs %w\", err))\n\t\t\t}\n\t\t\tcombinedError = errors.Join(combinedError, fmt.Errorf(\"failed to Add BPF program on iface %s with error: %w\", bpfProg.Iface, err))\n\t\t}\n\t\tif err := c.AddProbePrograms(bpfProg.HostName, bpfProg.BpfPrograms.Probes); err != nil {\n\t\t\tif err := c.SaveConfigsToConfigStore(); err != nil {\n\t\t\t\tcombinedError = errors.Join(combinedError, fmt.Errorf(\"add eBPF Programs of type probes failed to save configs %w\", err))\n\t\t\t}\n\t\t\tcombinedError = errors.Join(combinedError, fmt.Errorf(\"failed to Add eBPF program of type probe with error: %w\", err))\n\t\t}\n\t}\n\tif err := c.SaveConfigsToConfigStore(); err != nil {\n\t\tcombinedError = errors.Join(combinedError, fmt.Errorf(\"AddeBPFPrograms failed to save configs %w\", err))\n\t}\n\treturn combinedError\n}\n\n// CleanupProgramsOnInterface removes all EBPF program and its metadata, on the network interface provided\nfunc (c *NFConfigs) CleanupProgramsOnInterface(ifaceName string) {\n\tif c.IngressXDPBpfs[ifaceName] != nil {\n\t\tif err := c.StopNRemoveAllBPFPrograms(ifaceName, models.XDPIngressType); err != nil {\n\t\t\tlog.Warn().Err(err).Msg(\"failed to Close Ingress XDP BPF Program\")\n\t\t}\n\t}\n\tif c.IngressTCBpfs[ifaceName] != nil {\n\t\tif err := c.StopNRemoveAllBPFPrograms(ifaceName, models.IngressType); err != nil {\n\t\t\tlog.Warn().Err(err).Msg(\"failed to Close Ingress XDP BPF Program\")\n\t\t}\n\t}\n\tif c.EgressTCBpfs[ifaceName] != nil {\n\t\tif err := c.StopNRemoveAllBPFPrograms(ifaceName, models.EgressType); err != nil {\n\t\t\tlog.Warn().Err(err).Msg(\"failed to Close Ingress XDP BPF Program\")\n\t\t}\n\t}\n}\n\n// DeleteProgramsOnInterface : It will delete ebpf Programs on the given interface\nfunc (c *NFConfigs) DeleteProgramsOnInterface(ifaceName, HostName string, bpfProgs *models.BPFProgramNames) error {\n\tvar err error\n\tif c.HostInterfaces, err = getHostInterfaces(); err != nil {\n\t\terrOut := fmt.Errorf(\"failed get interfaces in DeleteProgramsOnInterface Function: %v\", err)\n\t\tlog.Error().Err(errOut)\n\t\treturn errOut\n\t}\n\n\tif HostName != c.HostName {\n\t\terrOut := fmt.Errorf(\"provided bpf programs do not belong to this host\")\n\t\tlog.Error().Err(errOut)\n\t\treturn errOut\n\t}\n\n\tif ifaceName == \"\" || bpfProgs == nil {\n\t\terrOut := fmt.Errorf(\"iface name or bpf programs are empty\")\n\t\tlog.Error().Err(errOut)\n\t\treturn errOut\n\t}\n\n\tif _, ok := c.HostInterfaces[ifaceName]; !ok {\n\t\tc.CleanupProgramsOnInterface(ifaceName)\n\t\terrOut := fmt.Errorf(\"%s interface name not found in the host, Stop called, %w\", ifaceName, err)\n\t\tlog.Error().Err(errOut)\n\t\treturn errOut\n\t}\n\tc.Mu.Lock()\n\tdefer c.Mu.Unlock()\n\n\tsort.Strings(bpfProgs.XDPIngress)\n\tif c.IngressXDPBpfs[ifaceName] != nil {\n\t\tbpfList := c.IngressXDPBpfs[ifaceName]\n\t\tfor e := bpfList.Front(); e != nil; {\n\t\t\tnext := e.Next()\n\t\t\tdata := e.Value.(*BPF)\n\t\t\tif BinarySearch(bpfProgs.XDPIngress, data.Program.Name) {\n\t\t\t\terr := c.DeleteProgramsOnInterfaceHelper(e, ifaceName, models.XDPIngressType, bpfList)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"DeleteProgramsOnInterfaceHelper function failed : %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\te = next\n\t\t}\n\t\tif bpfList.Len() == 0 {\n\t\t\tc.IngressXDPBpfs[ifaceName] = nil\n\t\t}\n\t}\n\tsort.Strings(bpfProgs.TCIngress)\n\tif c.IngressTCBpfs[ifaceName] != nil {\n\t\tbpfList := c.IngressTCBpfs[ifaceName]\n\t\tfor e := bpfList.Front(); e != nil; {\n\t\t\tnext := e.Next()\n\t\t\tdata := e.Value.(*BPF)\n\t\t\tif BinarySearch(bpfProgs.TCIngress, data.Program.Name) {\n\t\t\t\terr := c.DeleteProgramsOnInterfaceHelper(e, ifaceName, models.IngressType, bpfList)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"DeleteProgramsOnInterfaceHelper function failed : %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\te = next\n\t\t}\n\t\tif bpfList.Len() == 0 {\n\t\t\tc.IngressTCBpfs[ifaceName] = nil\n\t\t}\n\t}\n\n\tsort.Strings(bpfProgs.TCEgress)\n\tif c.EgressTCBpfs[ifaceName] != nil {\n\t\tbpfList := c.EgressTCBpfs[ifaceName]\n\t\tfor e := bpfList.Front(); e != nil; {\n\t\t\tnext := e.Next()\n\t\t\tdata := e.Value.(*BPF)\n\t\t\tif BinarySearch(bpfProgs.TCEgress, data.Program.Name) {\n\t\t\t\terr := c.DeleteProgramsOnInterfaceHelper(e, ifaceName, models.EgressType, bpfList)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"DeleteProgramsOnInterfaceHelper function failed : %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\te = next\n\t\t}\n\t\tif bpfList.Len() == 0 {\n\t\t\tc.EgressTCBpfs[ifaceName] = nil\n\t\t}\n\t}\n\n\tsort.Strings(bpfProgs.Probes)\n\tfor e := c.ProbesBpfs.Front(); e != nil; {\n\t\tnext := e.Next()\n\t\tdata := e.Value.(*BPF)\n\t\tif BinarySearch(bpfProgs.Probes, data.Program.Name) {\n\t\t\terr := c.DeleteProgramsOnInterfaceHelper(e, ifaceName, \"\", &c.ProbesBpfs)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"DeleteProgramsOnInterfaceHelper function failed : %w\", err)\n\t\t\t}\n\t\t}\n\t\te = next\n\t}\n\treturn nil\n}\n\n// DeleteProgramsOnInterfaceHelper : helper function for DeleteProgramsOnInterface function\nfunc (c *NFConfigs) DeleteProgramsOnInterfaceHelper(e *list.Element, ifaceName string, direction string, bpfList *list.List) error {\n\tif e == nil {\n\t\treturn nil\n\t}\n\tprog := e.Value.(*BPF)\n\tprog.Program.AdminStatus = models.Disabled\n\tif err := prog.Stop(ifaceName, c.Ifaces[ifaceName], direction, c.HostConfig.BpfChainingEnabled); err != nil {\n\t\treturn fmt.Errorf(\"failed to stop %s iface %s direction %s with err %w\", prog.Program.Name, ifaceName, direction, err)\n\t}\n\ttmpNextBPF := e.Next()\n\ttmpPreviousBPF := e.Prev()\n\tbpfList.Remove(e)\n\n\tif !c.HostConfig.BpfChainingEnabled {\n\t\treturn nil\n\t}\n\tif tmpNextBPF != nil && tmpNextBPF.Prev() != nil { // relink the next element\n\t\tif err := c.LinkBPFPrograms(tmpNextBPF.Prev().Value.(*BPF), tmpNextBPF.Value.(*BPF)); err != nil {\n\t\t\tlog.Error().Err(err).Msgf(\"DeleteProgramsOnInterfaceHelper - failed LinkBPFPrograms\")\n\t\t\treturn fmt.Errorf(\"DeleteProgramsOnInterfaceHelper - failed LinkBPFPrograms %w\", err)\n\t\t}\n\t}\n\t// check if the program is not probes\n\tif len(direction) > 1 {\n\t\t// Check if list contains root program only then stop the root program.\n\t\tif tmpPreviousBPF.Prev() == nil && tmpPreviousBPF.Next() == nil {\n\t\t\tlog.Info().Msgf(\"no ebpf programs are running, stopping root program\")\n\n\t\t\tif err := c.StopRootProgram(ifaceName, direction); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to stop to root program of iface %s direction %v with err %w\", ifaceName, direction, err)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// DeleteEbpfPrograms - Delete eBPF programs on the node if they are running\nfunc (c *NFConfigs) DeleteEbpfPrograms(bpfProgs []models.L3afBPFProgramNames) error {\n\tvar combinedError error\n\tfor _, bpfProg := range bpfProgs {\n\t\tif err := c.DeleteProgramsOnInterface(bpfProg.Iface, bpfProg.HostName, bpfProg.BpfProgramNames); err != nil {\n\t\t\tif err := c.SaveConfigsToConfigStore(); err != nil {\n\t\t\t\tcombinedError = errors.Join(combinedError, fmt.Errorf(\"SaveConfigsToConfigStore failed to save configs %w\", err))\n\t\t\t}\n\t\t\tcombinedError = errors.Join(combinedError, fmt.Errorf(\"failed to Remove eBPF program on iface %s with error: %w\", bpfProg.Iface, err))\n\t\t}\n\t}\n\tif err := c.SaveConfigsToConfigStore(); err != nil {\n\t\tcombinedError = errors.Join(combinedError, fmt.Errorf(\"DeleteEbpfPrograms failed to save configs %w\", err))\n\t}\n\treturn combinedError\n}\n\n// BinarySearch: It is checking a target string exists in sorted slice of strings\nfunc BinarySearch(names []string, target string) bool {\n\tleft := 0\n\tright := len(names) - 1\n\n\tfor right >= left {\n\t\tmid := (left + right) >> 1\n\t\tif names[mid] == target {\n\t\t\treturn true\n\t\t} else if names[mid] > target {\n\t\t\tright = mid - 1\n\t\t} else {\n\t\t\tleft = mid + 1\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (c *NFConfigs) AddProbePrograms(HostName string, bpfProgs []*models.BPFProgram) error {\n\tif HostName != c.HostName {\n\t\terrOut := fmt.Errorf(\"provided bpf programs do not belong to this host\")\n\t\tlog.Error().Err(errOut)\n\t\treturn errOut\n\t}\n\n\tif len(bpfProgs) == 0 {\n\t\treturn nil\n\t}\n\n\tfor _, bpfProg := range bpfProgs {\n\t\tif err := c.PushBackAndStartProbe(bpfProg); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to PushBackAndStartBPF BPF Program: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// PushBackAndStartProbe method inserts the element at the end of the list\nfunc (c *NFConfigs) PushBackAndStartProbe(bpfProg *models.BPFProgram) error {\n\tlog.Info().Msgf(\"PushBackAndStartProbe: %s\", bpfProg.Name)\n\tbpf := NewBpfProgram(c.Ctx, *bpfProg, c.HostConfig, \"\")\n\n\tif err := c.DownloadAndStartProbes(c.ProbesBpfs.PushBack(bpf)); err != nil {\n\t\treturn fmt.Errorf(\"failed to download and start the BPF %s  with err: %w\", bpfProg.Name, err)\n\t}\n\n\treturn nil\n}\n\nfunc (c *NFConfigs) DownloadAndStartProbes(element *list.Element) error {\n\tif element == nil {\n\t\treturn fmt.Errorf(\"element is nil pointer\")\n\t}\n\n\tbpf := element.Value.(*BPF)\n\n\tif err := bpf.VerifyAndGetArtifacts(c.HostConfig); err != nil {\n\t\treturn fmt.Errorf(\"failed to get artifacts %s with error: %w\", bpf.Program.Artifact, err)\n\t}\n\n\tif err := bpf.LoadBPFProgram(\"\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to load bpf program %s with error: %w\", bpf.Program.Name, err)\n\t}\n\n\treturn nil\n}\n\n// SerialzeProgram this function wil serialize the program\nfunc SerialzeProgram(e *list.Element) *models.L3AFMetaData {\n\ttmp := &models.L3AFMetaData{}\n\tbpf := e.Value.(*BPF)\n\ttmp.BpfMaps = make([]string, 0)\n\tif bpf.BpfMaps != nil {\n\t\tfor _, v := range bpf.BpfMaps {\n\t\t\ttmp.BpfMaps = append(tmp.BpfMaps, v.Name)\n\t\t}\n\t}\n\ttmp.FilePath = bpf.FilePath\n\ttmp.MapNamePath = bpf.MapNamePath\n\ttmp.PrevMapNamePath = bpf.PrevMapNamePath\n\ttmp.PrevProgMapID = uint32(bpf.PrevProgMapID)\n\ttmp.ProgID = uint32(bpf.ProgID)\n\ttmp.Program = bpf.Program\n\ttmp.ProgMapID = uint32(bpf.ProgMapID)\n\ttmp.RestartCount = bpf.RestartCount\n\ttmp.ProgMapCollection = models.MetaColl{\n\t\tPrograms: make([]string, 0),\n\t\tMaps:     make([]string, 0),\n\t}\n\tif bpf.ProgMapCollection != nil {\n\t\tfor k, v := range bpf.ProgMapCollection.Programs {\n\t\t\tif v.Type() == ebpf.XDP || v.Type() == ebpf.SchedACT || v.Type() == ebpf.SchedCLS {\n\t\t\t\ttmp.ProgMapCollection.Programs = append(tmp.ProgMapCollection.Programs, k)\n\t\t\t}\n\t\t}\n\t\tfor k := range bpf.ProgMapCollection.Maps {\n\t\t\ttmp.ProgMapCollection.Maps = append(tmp.ProgMapCollection.Maps, k)\n\t\t}\n\t}\n\ttmp.MetricsBpfMaps = make(map[string]models.MetaMetricsBPFMap)\n\tif bpf.MetricsBpfMaps != nil {\n\t\tfor k1, v1 := range bpf.MetricsBpfMaps {\n\t\t\tvalues := make([]float64, 0)\n\t\t\ttmpval := v1.Values\n\t\t\tfor i := 0; i < v1.Values.Len(); i++ {\n\t\t\t\tif tmpval.Value != nil {\n\t\t\t\t\tvalues = append(values, tmpval.Value.(float64))\n\t\t\t\t}\n\t\t\t\ttmpval = tmpval.Next()\n\t\t\t}\n\t\t\ttmp.MetricsBpfMaps[k1] = models.MetaMetricsBPFMap{\n\t\t\t\tMapName:    v1.Name,\n\t\t\t\tKey:        v1.Key,\n\t\t\t\tValues:     values,\n\t\t\t\tAggregator: v1.Aggregator,\n\t\t\t\tLastValue:  float64(v1.LastValue),\n\t\t\t}\n\t\t}\n\t}\n\ttmp.Link = false\n\tif bpf.Link != nil {\n\t\ttmp.Link = true\n\t}\n\treturn tmp\n}\n\n// GetL3AFHOSTDATA this function will give serialize form of current l3afd state\nfunc (c *NFConfigs) GetL3AFHOSTDATA() models.L3AFALLHOSTDATA {\n\tresult := models.L3AFALLHOSTDATA{}\n\tresult.HostName = c.HostName\n\tresult.Ifaces = c.Ifaces\n\tresult.HostInterfaces = c.HostInterfaces\n\tresult.IngressXDPBpfs = make(map[string][]*models.L3AFMetaData)\n\tresult.IngressTCBpfs = make(map[string][]*models.L3AFMetaData)\n\tresult.EgressTCBpfs = make(map[string][]*models.L3AFMetaData)\n\tresult.ProbesBpfs = make([]models.L3AFMetaData, 0)\n\tif c.IngressXDPBpfs != nil {\n\t\tfor k, v := range c.IngressXDPBpfs {\n\t\t\tls := make([]*models.L3AFMetaData, 0)\n\t\t\tfor e := v.Front(); e != nil; e = e.Next() {\n\t\t\t\tls = append(ls, SerialzeProgram(e))\n\t\t\t}\n\t\t\tresult.IngressXDPBpfs[k] = ls\n\t\t}\n\t}\n\tif c.IngressTCBpfs != nil {\n\t\tfor k, v := range c.IngressTCBpfs {\n\t\t\tls := make([]*models.L3AFMetaData, 0)\n\t\t\tfor e := v.Front(); e != nil; e = e.Next() {\n\t\t\t\tls = append(ls, SerialzeProgram(e))\n\t\t\t}\n\t\t\tresult.IngressTCBpfs[k] = ls\n\t\t}\n\t}\n\tif c.EgressTCBpfs != nil {\n\t\tfor k, v := range c.EgressTCBpfs {\n\t\t\tls := make([]*models.L3AFMetaData, 0)\n\t\t\tfor e := v.Front(); e != nil; e = e.Next() {\n\t\t\t\tls = append(ls, SerialzeProgram(e))\n\t\t\t}\n\t\t\tresult.EgressTCBpfs[k] = ls\n\t\t}\n\t}\n\tfor e := c.ProbesBpfs.Front(); e != nil; e = e.Next() {\n\t\tresult.ProbesBpfs = append(result.ProbesBpfs, *SerialzeProgram(e))\n\t}\n\tmetrics, _ := prometheus.DefaultGatherer.Gather()\n\tresult.AllStats = make([]models.MetricVec, 0)\n\tlistofMetrics := []string{\"l3afd_BPFStartCount\", \"l3afd_BPFStopCount\", \"l3afd_BPFUpdateCount\", \"l3afd_BPFUpdateFailedCount\", \"l3afd_BPFRunning\", \"l3afd_BPFStartTime\", \"l3afd_BPFMonitorMap\"}\n\tfor _, metric := range metrics {\n\t\tname := *metric.Name\n\t\ttp := metric.Type.Number()\n\t\tif slices.Index(listofMetrics, name) != -1 {\n\t\t\tfor _, m := range metric.Metric {\n\t\t\t\tr := models.MetricVec{}\n\t\t\t\tlt := m.GetLabel()\n\t\t\t\tfor _, y := range lt {\n\t\t\t\t\tr.Labels = append(r.Labels, models.Label{\n\t\t\t\t\t\tName:  *y.Name,\n\t\t\t\t\t\tValue: *y.Value,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tr.MetricName = name\n\t\t\t\tr.Type = int32(tp)\n\t\t\t\tif r.Type == 0 {\n\t\t\t\t\tr.Value = m.GetCounter().GetValue()\n\t\t\t\t} else {\n\t\t\t\t\tr.Value = m.Gauge.GetValue()\n\t\t\t\t}\n\t\t\t\tresult.AllStats = append(result.AllStats, r)\n\t\t\t}\n\t\t}\n\t}\n\treturn result\n}\n\n// StartAllUserProgramsAndProbes this function will restart all the User Programs and probes\nfunc (c *NFConfigs) StartAllUserProgramsAndProbes() error {\n\tif c.IngressXDPBpfs != nil {\n\t\tfor iface, v := range c.IngressXDPBpfs {\n\t\t\tfor e := v.Front(); e != nil; e = e.Next() {\n\t\t\t\t// Starting Probes\n\t\t\t\tb := e.Value.(*BPF)\n\t\t\t\tef := b.Program.EntryFunctionName\n\t\t\t\tb.Program.EntryFunctionName = \"\"\n\t\t\t\tprg := b.ProgMapCollection\n\t\t\t\tb.ProgMapCollection = nil\n\t\t\t\tif err := b.LoadBPFProgram(iface); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"unable to load probes %w\", err)\n\t\t\t\t}\n\t\t\t\tb.Program.EntryFunctionName = ef\n\t\t\t\tif b.ProgMapCollection != nil {\n\t\t\t\t\tfor fk, vf := range b.ProgMapCollection.Programs {\n\t\t\t\t\t\tif _, ok := prg.Programs[fk]; !ok {\n\t\t\t\t\t\t\tprg.Programs[fk] = vf\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvf.Close()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor fk, vf := range b.ProgMapCollection.Maps {\n\t\t\t\t\t\tif _, ok := prg.Maps[fk]; !ok {\n\t\t\t\t\t\t\tprg.Maps[fk] = vf\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvf.Close()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tb.ProgMapCollection = prg\n\t\t\t\tif len(b.Program.CmdStart) > 0 {\n\t\t\t\t\t// Verify other instance is running\n\t\t\t\t\tif err := StopExternalRunningProcess(b.Program.CmdStart); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to stop external instance of the program %s with error : %w\", b.Program.CmdStart, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif b.Program.UserProgramDaemon {\n\t\t\t\t\t// Starting User Program\n\t\t\t\t\tif err := b.StartUserProgram(iface, models.XDPIngressType, c.HostConfig.BpfChainingEnabled); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif c.IngressTCBpfs != nil {\n\t\tfor iface, v := range c.IngressTCBpfs {\n\t\t\tfor e := v.Front(); e != nil; e = e.Next() {\n\t\t\t\tb := e.Value.(*BPF)\n\t\t\t\tef := b.Program.EntryFunctionName\n\t\t\t\tb.Program.EntryFunctionName = \"\"\n\t\t\t\tprg := b.ProgMapCollection\n\t\t\t\tb.ProgMapCollection = nil\n\t\t\t\tif err := b.LoadBPFProgram(iface); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"unable to load probes %w\", err)\n\t\t\t\t}\n\t\t\t\tb.Program.EntryFunctionName = ef\n\t\t\t\tif b.ProgMapCollection != nil {\n\t\t\t\t\tfor fk, vf := range b.ProgMapCollection.Programs {\n\t\t\t\t\t\tif _, ok := prg.Programs[fk]; !ok {\n\t\t\t\t\t\t\tprg.Programs[fk] = vf\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvf.Close()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor fk, vf := range b.ProgMapCollection.Maps {\n\t\t\t\t\t\tif _, ok := prg.Maps[fk]; !ok {\n\t\t\t\t\t\t\tprg.Maps[fk] = vf\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvf.Close()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tb.ProgMapCollection = prg\n\t\t\t\tif len(b.Program.CmdStart) > 0 {\n\t\t\t\t\t// Verify other instance is running\n\t\t\t\t\tif err := StopExternalRunningProcess(b.Program.CmdStart); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to stop external instance of the program %s with error : %w\", b.Program.CmdStart, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif b.Program.UserProgramDaemon {\n\t\t\t\t\t// Starting User Program\n\t\t\t\t\tif err := b.StartUserProgram(iface, models.XDPIngressType, c.HostConfig.BpfChainingEnabled); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif c.EgressTCBpfs != nil {\n\t\tfor iface, v := range c.EgressTCBpfs {\n\t\t\tfor e := v.Front(); e != nil; e = e.Next() {\n\t\t\t\tb := e.Value.(*BPF)\n\t\t\t\tef := b.Program.EntryFunctionName\n\t\t\t\tb.Program.EntryFunctionName = \"\"\n\t\t\t\tprg := b.ProgMapCollection\n\t\t\t\tb.ProgMapCollection = nil\n\t\t\t\tif err := b.LoadBPFProgram(iface); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"unable to load probes %w\", err)\n\t\t\t\t}\n\t\t\t\tb.Program.EntryFunctionName = ef\n\t\t\t\tif b.ProgMapCollection != nil {\n\t\t\t\t\tfor fk, vf := range b.ProgMapCollection.Programs {\n\t\t\t\t\t\tif _, ok := prg.Programs[fk]; !ok {\n\t\t\t\t\t\t\tprg.Programs[fk] = vf\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvf.Close()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor fk, vf := range b.ProgMapCollection.Maps {\n\t\t\t\t\t\tif _, ok := prg.Maps[fk]; !ok {\n\t\t\t\t\t\t\tprg.Maps[fk] = vf\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvf.Close()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tb.ProgMapCollection = prg\n\t\t\t\tif len(b.Program.CmdStart) > 0 {\n\t\t\t\t\t// Verify other instance is running\n\t\t\t\t\tif err := StopExternalRunningProcess(b.Program.CmdStart); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to stop external instance of the program %s with error : %w\", b.Program.CmdStart, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif b.Program.UserProgramDaemon {\n\t\t\t\t\t// Starting User Program\n\t\t\t\t\tif err := b.StartUserProgram(iface, models.XDPIngressType, c.HostConfig.BpfChainingEnabled); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// StopAllProbesAndUserPrograms this function will stop all the probes & stops user programs\nfunc (c *NFConfigs) StopAllProbesAndUserPrograms() error {\n\tif c.IngressXDPBpfs != nil {\n\t\tfor iface, v := range c.IngressXDPBpfs {\n\t\t\tfor e := v.Front(); e != nil; e = e.Next() {\n\t\t\t\t// Stopping Probes\n\t\t\t\tb := e.Value.(*BPF)\n\t\t\t\tif b.ProbeLinks != nil {\n\t\t\t\t\tfor _, pb := range b.ProbeLinks {\n\t\t\t\t\t\t(*pb).Close()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tb.ProbeLinks = make([]*link.Link, 0)\n\t\t\t\t// Stopping UserPrograms\n\t\t\t\tif err := b.StopUserProgram(iface, models.XDPIngressType); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to stop user program : %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif c.IngressTCBpfs != nil {\n\t\tfor iface, v := range c.IngressTCBpfs {\n\t\t\tfor e := v.Front(); e != nil; e = e.Next() {\n\t\t\t\t// Stopping Probes\n\t\t\t\tb := e.Value.(*BPF)\n\t\t\t\tif b.ProbeLinks != nil {\n\t\t\t\t\tfor _, pb := range b.ProbeLinks {\n\t\t\t\t\t\t(*pb).Close()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tb.ProbeLinks = make([]*link.Link, 0)\n\t\t\t\t// Stopping UserPrograms\n\t\t\t\tif err := b.StopUserProgram(iface, models.XDPIngressType); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to stop user program : %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif c.EgressTCBpfs != nil {\n\t\tfor iface, v := range c.EgressTCBpfs {\n\t\t\tfor e := v.Front(); e != nil; e = e.Next() {\n\t\t\t\t// Stopping Probes\n\t\t\t\tb := e.Value.(*BPF)\n\t\t\t\tif b.ProbeLinks != nil {\n\t\t\t\t\tfor _, pb := range b.ProbeLinks {\n\t\t\t\t\t\t(*pb).Close()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tb.ProbeLinks = make([]*link.Link, 0)\n\t\t\t\t// Stopping UserPrograms\n\t\t\t\tif err := b.StopUserProgram(iface, models.XDPIngressType); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"failed to stop user program : %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "bpfprogs/nfconfig_test.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"container/list\"\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/l3af-project/l3afd/v2/config\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\n\t\"github.com/rs/zerolog/log\"\n)\n\nvar (\n\tmachineHostname string\n\tHostInterfaces  map[string]bool\n\tpMon            *PCheck\n\tmMon            *BpfMetrics\n\tvalVerChange    *models.BPFPrograms\n\tvalStatusChange *models.BPFPrograms\n\tingressXDPBpfs  map[string]*list.List\n\tingressTCBpfs   map[string]*list.List\n\tegressTCBpfs    map[string]*list.List\n\tifaceName       string\n\tseqID           int\n\tbpfProgs        *models.BPFPrograms\n)\n\nfunc setupDBTest() {\n\tmachineHostname, _ = os.Hostname()\n\tHostInterfaces = make(map[string]bool)\n\tHostInterfaces[\"fakeif0\"] = true\n\tpMon = NewPCheck(3, true, 10)\n\tmMon = NewpBpfMetrics(true, 30)\n\n\tingressXDPBpfs = make(map[string]*list.List)\n\tingressTCBpfs = make(map[string]*list.List)\n\tegressTCBpfs = make(map[string]*list.List)\n}\n\nfunc setupValidBPF() {\n\tbpf := BPF{\n\t\tProgram: models.BPFProgram{\n\t\t\tID:                1,\n\t\t\tName:              \"foo\",\n\t\t\tArtifact:          \"foo.tar.gz\",\n\t\t\tCmdStart:          \"foo\",\n\t\t\tCmdStop:           \"\",\n\t\t\tVersion:           \"1.0\",\n\t\t\tUserProgramDaemon: true,\n\t\t\tAdminStatus:       \"DISABLED\",\n\t\t},\n\t\tCmd:          nil,\n\t\tFilePath:     \"\",\n\t\tRestartCount: 0,\n\t}\n\tifaceName = \"dummy\"\n\tseqID = 1\n\tlog.Info().Msg(bpf.Program.Name)\n}\n\nfunc setupBPFProgramData() {\n\tbpfProgsTmp := &models.BPFPrograms{}\n\tifaceName = \"dummy\"\n\tseqID = 1\n\n\tbpfProg := &models.BPFProgram{\n\t\tID:                1,\n\t\tName:              \"foo\",\n\t\tArtifact:          \"foo.tar.gz\",\n\t\tCmdStart:          \"foo\",\n\t\tCmdStop:           \"\",\n\t\tVersion:           \"1.0\",\n\t\tUserProgramDaemon: true,\n\t\tAdminStatus:       \"ENABLED\",\n\t\tSeqID:             1,\n\t}\n\tbpfProgsTmp.XDPIngress = append(bpfProgsTmp.XDPIngress, bpfProg)\n\n\tbpfProgs = bpfProgsTmp\n}\n\nfunc setupBPFProgramVersionChange() {\n\tbpfProgsTmp := &models.BPFPrograms{}\n\tifaceName = \"dummy\"\n\tseqID = 1\n\n\tbpfProg := &models.BPFProgram{\n\t\tID:                1,\n\t\tName:              \"foo\",\n\t\tArtifact:          \"foo.tar.gz\",\n\t\tCmdStart:          \"foo\",\n\t\tCmdStop:           \"\",\n\t\tVersion:           \"2.0\",\n\t\tUserProgramDaemon: true,\n\t\tAdminStatus:       \"ENABLED\",\n\t}\n\tbpfProgsTmp.XDPIngress = append(bpfProgsTmp.XDPIngress, bpfProg)\n\tvalVerChange = bpfProgsTmp\n}\n\nfunc setupBPFProgramStatusChange() {\n\n\tbpfProgsTmp := &models.BPFPrograms{}\n\t//cfg := make(map[string][]*models.BPFProgram)\n\tifaceName = \"dummy\"\n\tseqID = 1\n\n\tbpfProg := &models.BPFProgram{\n\t\tID:                1,\n\t\tName:              \"foo\",\n\t\tArtifact:          \"foo.tar.gz\",\n\t\tCmdStart:          \"foo\",\n\t\tCmdStop:           \"\",\n\t\tVersion:           \"2.0\",\n\t\tUserProgramDaemon: true,\n\t\tAdminStatus:       \"DISABLED\",\n\t}\n\tbpfProgsTmp.XDPIngress = append(bpfProgsTmp.XDPIngress, bpfProg)\n\tvalStatusChange = bpfProgsTmp\n}\n\nfunc TestNewNFConfigs(t *testing.T) {\n\ttype args struct {\n\t\thost     string\n\t\thostConf *config.Config\n\t\tpMon     *PCheck\n\t\tmMon     *BpfMetrics\n\t\tctx      context.Context\n\t}\n\tsetupDBTest()\n\thostIfaces, _ := getHostInterfaces()\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    *NFConfigs\n\t\twantErr bool\n\t}{\n\t\t{name: \"EmptyConfig\",\n\t\t\targs: args{\n\t\t\t\thost:     machineHostname,\n\t\t\t\thostConf: nil,\n\t\t\t\tpMon:     pMon,\n\t\t\t\tmMon:     mMon},\n\t\t\twant: &NFConfigs{HostName: machineHostname,\n\t\t\t\tHostInterfaces: hostIfaces,\n\t\t\t\tIngressXDPBpfs: ingressXDPBpfs,\n\t\t\t\tIngressTCBpfs:  ingressTCBpfs,\n\t\t\t\tEgressTCBpfs:   egressTCBpfs,\n\t\t\t\tHostConfig:     nil,\n\t\t\t\tProcessMon:     pMon,\n\t\t\t\tBpfMetricsMon:  mMon,\n\t\t\t\tMu:             new(sync.Mutex),\n\t\t\t\tIfaces:         make(map[string]string),\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := NewNFConfigs(tt.args.ctx, tt.args.host, tt.args.hostConf, tt.args.pMon, tt.args.mMon)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"NewNFConfigs() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewNFConfigs() = %#v, want %#v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNFConfigs_Deploy(t *testing.T) {\n\ttype fields struct {\n\t\thostName       string\n\t\tHostInterfaces map[string]bool\n\t\tingressXDPBpfs map[string]*list.List\n\t\tingressTCBpfs  map[string]*list.List\n\t\tegressTCBpfs   map[string]*list.List\n\t\thostConfig     *config.Config\n\t\tProcessMon     *PCheck\n\t\tmetricsMon     *BpfMetrics\n\t}\n\ttype args struct {\n\t\tiface    string\n\t\thostName string\n\t\tbpfProgs *models.BPFPrograms\n\t}\n\n\tsetupDBTest()\n\tsetupValidBPF()\n\tsetupBPFProgramData()\n\tsetupBPFProgramVersionChange()\n\tsetupBPFProgramStatusChange()\n\n\tHostInterfaces, err := getHostInterfaces()\n\tif err != nil {\n\t\tlog.Info().Msg(\"getHostInterfaces returned and error\")\n\t}\n\tvar HostInterfacesKey string\n\tvar HostInterfacesValue bool\n\tfor HostInterfacesKey, HostInterfacesValue = range HostInterfaces {\n\t\tlog.Debug().Msgf(\"HostInterfacesKey: %v, HostInterfacesValue: %v\", HostInterfacesKey, HostInterfacesValue)\n\t\tbreak\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\targs    args\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"EmptyBPFs\",\n\t\t\tfields: fields{\n\t\t\t\thostName:       machineHostname,\n\t\t\t\tingressXDPBpfs: make(map[string]*list.List),\n\t\t\t\tingressTCBpfs:  make(map[string]*list.List),\n\t\t\t\tegressTCBpfs:   make(map[string]*list.List),\n\t\t\t\thostConfig:     nil,\n\t\t\t\tProcessMon:     pMon,\n\t\t\t\tmetricsMon:     mMon,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tiface:    \"\",\n\t\t\t\thostName: machineHostname,\n\t\t\t\tbpfProgs: nil,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"InvalidHostName\",\n\t\t\tfields: fields{\n\t\t\t\thostName:       machineHostname,\n\t\t\t\tingressXDPBpfs: make(map[string]*list.List),\n\t\t\t\tingressTCBpfs:  make(map[string]*list.List),\n\t\t\t\tegressTCBpfs:   make(map[string]*list.List),\n\t\t\t\thostConfig:     nil,\n\t\t\t\tProcessMon:     pMon,\n\t\t\t\tmetricsMon:     mMon,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tiface:    \"dummy\",\n\t\t\t\thostName: \"dummy\",\n\t\t\t\tbpfProgs: bpfProgs,\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"ValidHostNameInvalidIfaceName\",\n\t\t\tfields: fields{\n\t\t\t\thostName:       machineHostname,\n\t\t\t\tingressXDPBpfs: make(map[string]*list.List),\n\t\t\t\tingressTCBpfs:  make(map[string]*list.List),\n\t\t\t\tegressTCBpfs:   make(map[string]*list.List),\n\t\t\t\thostConfig:     nil,\n\t\t\t\tProcessMon:     pMon,\n\t\t\t\tmetricsMon:     mMon,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tiface:    \"dummy\",\n\t\t\t\thostName: machineHostname,\n\t\t\t\tbpfProgs: &models.BPFPrograms{},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"ValidHostNameValidIfaceName\",\n\t\t\tfields: fields{\n\t\t\t\thostName:       machineHostname,\n\t\t\t\tHostInterfaces: HostInterfaces,\n\t\t\t\tingressXDPBpfs: make(map[string]*list.List),\n\t\t\t\tingressTCBpfs:  make(map[string]*list.List),\n\t\t\t\tegressTCBpfs:   make(map[string]*list.List),\n\t\t\t\thostConfig:     nil,\n\t\t\t\tProcessMon:     pMon,\n\t\t\t\tmetricsMon:     mMon,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tiface:    HostInterfacesKey,\n\t\t\t\thostName: machineHostname,\n\t\t\t\tbpfProgs: &models.BPFPrograms{},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"TestEBPFRepoDownload\",\n\t\t\tfields: fields{\n\t\t\t\thostName:       machineHostname,\n\t\t\t\tHostInterfaces: HostInterfaces,\n\t\t\t\tingressXDPBpfs: make(map[string]*list.List),\n\t\t\t\tingressTCBpfs:  make(map[string]*list.List),\n\t\t\t\tegressTCBpfs:   make(map[string]*list.List),\n\t\t\t\thostConfig:     &config.Config{BPFDir: \"/tmp\", EBPFRepoURL: \"http://www.example.com\"},\n\t\t\t\tProcessMon:     pMon,\n\t\t\t\tmetricsMon:     mMon,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tiface:    HostInterfacesKey,\n\t\t\t\thostName: machineHostname,\n\t\t\t\tbpfProgs: bpfProgs,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"NewBPFWithVersionChange\",\n\t\t\tfields: fields{\n\t\t\t\thostName:       machineHostname,\n\t\t\t\tHostInterfaces: HostInterfaces,\n\t\t\t\tingressXDPBpfs: make(map[string]*list.List),\n\t\t\t\tingressTCBpfs:  make(map[string]*list.List),\n\t\t\t\tegressTCBpfs:   make(map[string]*list.List),\n\t\t\t\thostConfig:     &config.Config{BPFDir: \"/tmp\", EBPFRepoURL: \"http://www.example.com\"},\n\t\t\t\tProcessMon:     pMon,\n\t\t\t\tmetricsMon:     mMon,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tiface:    HostInterfacesKey,\n\t\t\t\thostName: machineHostname,\n\t\t\t\tbpfProgs: valVerChange,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"NewBPFWithStatusChange\",\n\t\t\tfields: fields{\n\t\t\t\thostName:       machineHostname,\n\t\t\t\tHostInterfaces: HostInterfaces,\n\t\t\t\tingressXDPBpfs: make(map[string]*list.List),\n\t\t\t\tingressTCBpfs:  make(map[string]*list.List),\n\t\t\t\tegressTCBpfs:   make(map[string]*list.List),\n\t\t\t\thostConfig:     &config.Config{BPFDir: \"/tmp\", EBPFRepoURL: \"http://www.example.com\"},\n\t\t\t\tProcessMon:     pMon,\n\t\t\t\tmetricsMon:     mMon,\n\t\t\t},\n\t\t\targs: args{\n\t\t\t\tiface:    HostInterfacesKey,\n\t\t\t\thostName: machineHostname,\n\t\t\t\tbpfProgs: valStatusChange,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcfg := &NFConfigs{\n\t\t\t\tHostName: tt.fields.hostName,\n\t\t\t\t//\t\t\t\tconfigs:    tt.fields.configs,\n\t\t\t\tHostInterfaces: tt.fields.HostInterfaces,\n\t\t\t\tIngressXDPBpfs: tt.fields.ingressXDPBpfs,\n\t\t\t\tIngressTCBpfs:  tt.fields.ingressTCBpfs,\n\t\t\t\tEgressTCBpfs:   tt.fields.egressTCBpfs,\n\t\t\t\tHostConfig:     tt.fields.hostConfig,\n\t\t\t\tProcessMon:     tt.fields.ProcessMon,\n\t\t\t\tMu:             new(sync.Mutex),\n\t\t\t}\n\t\t\tif err := cfg.Deploy(tt.args.iface, tt.args.hostName, tt.args.bpfProgs); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"NFConfigs.Deploy() error = %#v, wantErr %#v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNFConfigs_Close(t *testing.T) {\n\ttype fields struct {\n\t\thostName       string\n\t\tingressXDPBpfs map[string]*list.List\n\t\tingressTCBpfs  map[string]*list.List\n\t\tegressTCBpfs   map[string]*list.List\n\t\thostConfig     *config.Config\n\t\tProcessMon     *PCheck\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"EmptyMap\",\n\t\t\tfields: fields{\n\t\t\t\thostName:       machineHostname,\n\t\t\t\tingressXDPBpfs: make(map[string]*list.List),\n\t\t\t\tingressTCBpfs:  make(map[string]*list.List),\n\t\t\t\tegressTCBpfs:   make(map[string]*list.List),\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBpfMapDefaultPath: \"/sys/fs/bpf\",\n\t\t\t\t\tShutdownTimeout:   30 * time.Second,\n\t\t\t\t},\n\t\t\t\tProcessMon: pMon,\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcfg := &NFConfigs{\n\t\t\t\tHostName:       tt.fields.hostName,\n\t\t\t\tIngressXDPBpfs: tt.fields.ingressXDPBpfs,\n\t\t\t\tIngressTCBpfs:  tt.fields.ingressTCBpfs,\n\t\t\t\tEgressTCBpfs:   tt.fields.egressTCBpfs,\n\t\t\t\tHostConfig:     tt.fields.hostConfig,\n\t\t\t\tProcessMon:     tt.fields.ProcessMon,\n\t\t\t}\n\t\t\tctx, cancelfunc := context.WithTimeout(context.Background(), 30*time.Second)\n\t\t\tdefer cancelfunc()\n\t\t\tif err := cfg.Close(ctx); (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"NFConfigs.Close() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_getHostInterfaces(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"GoodInput\",\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t_, err := getHostInterfaces()\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"getHostInterfaces() error : %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\nfunc Test_BinarySearch(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tvals   []string\n\t\ttarget string\n\t\tresult bool\n\t}{\n\t\t{\n\t\t\tname:   \"FoundTheTarget\",\n\t\t\tvals:   []string{\"connection-limit\", \"ipfix-flow-exporter\", \"ratelimiting\"},\n\t\t\ttarget: \"ratelimiting\",\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname:   \"DidNotFindTheTarget\",\n\t\t\tvals:   []string{\"connection-limit\", \"ipfix-flow-exporter\", \"ratelimiting\"},\n\t\t\ttarget: \"zsdf\",\n\t\t\tresult: false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tif BinarySearch(tt.vals, tt.target) != tt.result {\n\t\t\tt.Errorf(\"BinarySearch is not producing expected output\")\n\t\t}\n\t}\n}\n\nfunc Test_AddProgramsOnInterface(t *testing.T) {\n\tHostInterfaces, err := getHostInterfaces()\n\tif err != nil {\n\t\tlog.Info().Msg(\"getHostInterfaces returned and error\")\n\t}\n\tvar HostInterfacesKey string\n\tvar HostInterfacesValue bool\n\tfor HostInterfacesKey, HostInterfacesValue = range HostInterfaces {\n\t\tlog.Debug().Msgf(\"HostInterfacesKey: %v, HostInterfacesValue: %v\", HostInterfacesKey, HostInterfacesValue)\n\t\tbreak\n\t}\n\ttype fields struct {\n\t\thostName       string\n\t\tHostInterfaces map[string]bool\n\t\tingressXDPBpfs map[string]*list.List\n\t\tingressTCBpfs  map[string]*list.List\n\t\tegressTCBpfs   map[string]*list.List\n\t\thostConfig     *config.Config\n\t\tProcessMon     *PCheck\n\t\tmu             *sync.Mutex\n\t}\n\ttype args struct {\n\t\tiface    string\n\t\thostName string\n\t\tbpfProgs *models.BPFPrograms\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfield   fields\n\t\targ     args\n\t\twanterr bool\n\t}{\n\t\t{\n\t\t\tname:    \"UnknownHostName\",\n\t\t\tfield:   fields{},\n\t\t\targ:     args{},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"NilInterface\",\n\t\t\tfield: fields{\n\t\t\t\thostName: \"l3af-local-test\",\n\t\t\t},\n\t\t\targ: args{\n\t\t\t\thostName: \"fakeif0\",\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"UnknownInterface\",\n\t\t\tfield: fields{\n\t\t\t\thostName: \"l3af-local-test\",\n\t\t\t},\n\t\t\targ: args{\n\t\t\t\thostName: \"l3af-local-test\",\n\t\t\t\tiface:    \"dummyinterface\",\n\t\t\t\tbpfProgs: &models.BPFPrograms{\n\t\t\t\t\tXDPIngress: []*models.BPFProgram{\n\t\t\t\t\t\t&models.BPFProgram{\n\t\t\t\t\t\t\tName:              \"dummy_name\",\n\t\t\t\t\t\t\tSeqID:             1,\n\t\t\t\t\t\t\tArtifact:          \"dummy_artifact.tar.gz\",\n\t\t\t\t\t\t\tMapName:           \"xdp_rl_ingress_next_prog\",\n\t\t\t\t\t\t\tCmdStart:          \"dummy_command\",\n\t\t\t\t\t\t\tVersion:           \"latest\",\n\t\t\t\t\t\t\tUserProgramDaemon: true,\n\t\t\t\t\t\t\tAdminStatus:       \"enabled\",\n\t\t\t\t\t\t\tProgType:          \"xdp\",\n\t\t\t\t\t\t\tCfgVersion:        1,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"GoodInput\",\n\t\t\tfield: fields{\n\t\t\t\thostName:       \"l3af-local-test\",\n\t\t\t\tHostInterfaces: HostInterfaces,\n\t\t\t\tmu:             new(sync.Mutex),\n\t\t\t\tingressXDPBpfs: map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\tingressTCBpfs:  map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\tegressTCBpfs:   map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBpfChainingEnabled: true,\n\t\t\t\t},\n\t\t\t},\n\t\t\targ: args{\n\t\t\t\thostName: \"l3af-local-test\",\n\t\t\t\tiface:    HostInterfacesKey,\n\t\t\t\tbpfProgs: &models.BPFPrograms{\n\t\t\t\t\tXDPIngress: []*models.BPFProgram{},\n\t\t\t\t\tTCEgress:   []*models.BPFProgram{},\n\t\t\t\t\tTCIngress:  []*models.BPFProgram{},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcfg := &NFConfigs{\n\t\t\t\tHostName:       tt.field.hostName,\n\t\t\t\tIngressXDPBpfs: tt.field.ingressXDPBpfs,\n\t\t\t\tIngressTCBpfs:  tt.field.ingressTCBpfs,\n\t\t\t\tEgressTCBpfs:   tt.field.egressTCBpfs,\n\t\t\t\tHostConfig:     tt.field.hostConfig,\n\t\t\t\tProcessMon:     tt.field.ProcessMon,\n\t\t\t\tHostInterfaces: tt.field.HostInterfaces,\n\t\t\t\tMu:             tt.field.mu,\n\t\t\t}\n\t\t\terr := cfg.AddProgramsOnInterface(tt.arg.iface, tt.arg.hostName, tt.arg.bpfProgs)\n\t\t\tif (err != nil) != tt.wanterr {\n\t\t\t\tt.Errorf(\"AddProgramsOnInterface: %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAddeBPFPrograms(t *testing.T) {\n\tHostInterfaces, err := getHostInterfaces()\n\tif err != nil {\n\t\tlog.Info().Msg(\"getHostInterfaces returned and error\")\n\t}\n\tvar HostInterfacesKey string\n\tvar HostInterfacesValue bool\n\tfor HostInterfacesKey, HostInterfacesValue = range HostInterfaces {\n\t\tlog.Debug().Msgf(\"HostInterfacesKey: %v, HostInterfacesValue: %v\", HostInterfacesKey, HostInterfacesValue)\n\t\tbreak\n\t}\n\n\ttype fields struct {\n\t\thostName       string\n\t\tHostInterfaces map[string]bool\n\t\tingressXDPBpfs map[string]*list.List\n\t\tingressTCBpfs  map[string]*list.List\n\t\tegressTCBpfs   map[string]*list.List\n\t\thostConfig     *config.Config\n\t\tProcessMon     *PCheck\n\t\tmu             *sync.Mutex\n\t\tifaces         map[string]string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfield   fields\n\t\targ     []models.L3afBPFPrograms\n\t\twanterr bool\n\t}{\n\t\t{\n\t\t\tname: \"UnknownHostName\",\n\t\t\tfield: fields{\n\t\t\t\thostName: \"l3af-prod\",\n\t\t\t\tifaces:   map[string]string{},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\targ: []models.L3afBPFPrograms{\n\t\t\t\t{\n\t\t\t\t\tHostName: \"l3af-test\",\n\t\t\t\t\tIface:    \"fakeif0\",\n\t\t\t\t\tBpfPrograms: &models.BPFPrograms{\n\t\t\t\t\t\tXDPIngress: []*models.BPFProgram{},\n\t\t\t\t\t\tTCIngress:  []*models.BPFProgram{},\n\t\t\t\t\t\tTCEgress:   []*models.BPFProgram{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"NilInterface\",\n\t\t\tfield: fields{\n\t\t\t\thostName: \"l3af-local-test\",\n\t\t\t\tifaces:   map[string]string{},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\targ: []models.L3afBPFPrograms{\n\t\t\t\t{\n\t\t\t\t\tHostName: \"l3af-local-test\",\n\t\t\t\t\tBpfPrograms: &models.BPFPrograms{\n\t\t\t\t\t\tXDPIngress: []*models.BPFProgram{},\n\t\t\t\t\t\tTCIngress:  []*models.BPFProgram{},\n\t\t\t\t\t\tTCEgress:   []*models.BPFProgram{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"UnknownInterface\",\n\t\t\tfield: fields{\n\t\t\t\thostName: \"l3af-local-test\",\n\t\t\t\tifaces:   map[string]string{},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\targ: []models.L3afBPFPrograms{\n\t\t\t\t{\n\t\t\t\t\tHostName: \"l3af-local-test\",\n\t\t\t\t\tIface:    \"dummyinterface\",\n\t\t\t\t\tBpfPrograms: &models.BPFPrograms{\n\t\t\t\t\t\tXDPIngress: []*models.BPFProgram{\n\t\t\t\t\t\t\t&models.BPFProgram{\n\t\t\t\t\t\t\t\tName:              \"dummy_name\",\n\t\t\t\t\t\t\t\tSeqID:             1,\n\t\t\t\t\t\t\t\tArtifact:          \"dummy_artifact_name\",\n\t\t\t\t\t\t\t\tMapName:           \"xdp_rl_ingress_next_prog\",\n\t\t\t\t\t\t\t\tCmdStart:          \"dummy_command\",\n\t\t\t\t\t\t\t\tVersion:           \"latest\",\n\t\t\t\t\t\t\t\tUserProgramDaemon: true,\n\t\t\t\t\t\t\t\tAdminStatus:       \"enabled\",\n\t\t\t\t\t\t\t\tProgType:          \"xdp\",\n\t\t\t\t\t\t\t\tCfgVersion:        1,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"GoodInput\",\n\t\t\tfield: fields{\n\t\t\t\thostName:       \"l3af-local-test\",\n\t\t\t\tHostInterfaces: HostInterfaces,\n\t\t\t\t// fakeif0 is a fake interface\n\t\t\t\tmu:             new(sync.Mutex),\n\t\t\t\tingressXDPBpfs: map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\tingressTCBpfs:  map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\tegressTCBpfs:   map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\tifaces:         map[string]string{},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\targ: []models.L3afBPFPrograms{\n\t\t\t\t{\n\t\t\t\t\tHostName: \"l3af-local-test\",\n\t\t\t\t\tIface:    HostInterfacesKey,\n\t\t\t\t\tBpfPrograms: &models.BPFPrograms{\n\t\t\t\t\t\tXDPIngress: []*models.BPFProgram{},\n\t\t\t\t\t\tTCIngress:  []*models.BPFProgram{},\n\t\t\t\t\t\tTCEgress:   []*models.BPFProgram{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcfg := &NFConfigs{\n\t\t\t\tHostName:       tt.field.hostName,\n\t\t\t\tIngressXDPBpfs: tt.field.ingressXDPBpfs,\n\t\t\t\tIngressTCBpfs:  tt.field.ingressTCBpfs,\n\t\t\t\tEgressTCBpfs:   tt.field.egressTCBpfs,\n\t\t\t\tHostConfig:     tt.field.hostConfig,\n\t\t\t\tProcessMon:     tt.field.ProcessMon,\n\t\t\t\tHostInterfaces: tt.field.HostInterfaces,\n\t\t\t\tMu:             tt.field.mu,\n\t\t\t}\n\t\t\terr := cfg.AddeBPFPrograms(tt.arg)\n\t\t\tif (err != nil) != tt.wanterr {\n\t\t\t\tt.Errorf(\"AddeBPFPrograms failed: %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDeleteProgramsOnInterface(t *testing.T) {\n\ttype fields struct {\n\t\thostName       string\n\t\tHostInterfaces map[string]bool\n\t\tingressXDPBpfs map[string]*list.List\n\t\tingressTCBpfs  map[string]*list.List\n\t\tegressTCBpfs   map[string]*list.List\n\t\thostConfig     *config.Config\n\t\tProcessMon     *PCheck\n\t\tmu             *sync.Mutex\n\t}\n\ttype args struct {\n\t\tiface    string\n\t\thostName string\n\t\tbpfProgs *models.BPFProgramNames\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfield   fields\n\t\targ     args\n\t\twanterr bool\n\t}{\n\t\t{\n\t\t\tname:    \"UnknownHostName\",\n\t\t\tfield:   fields{},\n\t\t\targ:     args{},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"NilInterface\",\n\t\t\tfield: fields{\n\t\t\t\thostName: \"l3af-local-test\",\n\t\t\t},\n\t\t\targ: args{\n\t\t\t\thostName: \"fakeif0\",\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"UnknownInterface\",\n\t\t\tfield: fields{\n\t\t\t\thostName: \"l3af-local-test\",\n\t\t\t},\n\t\t\targ: args{\n\t\t\t\thostName: \"l3af-local-test\",\n\t\t\t\tiface:    \"dummyinterface\",\n\t\t\t\tbpfProgs: &models.BPFProgramNames{\n\t\t\t\t\tXDPIngress: []string{},\n\t\t\t\t\tTCIngress:  []string{},\n\t\t\t\t\tTCEgress:   []string{},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"GoodInputButNotRealInterface\",\n\t\t\tfield: fields{\n\t\t\t\thostName:       \"l3af-local-test\",\n\t\t\t\tHostInterfaces: map[string]bool{\"fakeif0\": true},\n\t\t\t\tmu:             new(sync.Mutex),\n\t\t\t\tingressXDPBpfs: map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\tingressTCBpfs:  map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\tegressTCBpfs:   map[string]*list.List{\"fakeif0\": nil},\n\t\t\t},\n\t\t\targ: args{\n\t\t\t\thostName: \"l3af-local-test\",\n\t\t\t\tiface:    \"fakeif0\",\n\t\t\t\tbpfProgs: &models.BPFProgramNames{\n\t\t\t\t\tXDPIngress: []string{},\n\t\t\t\t\tTCIngress:  []string{},\n\t\t\t\t\tTCEgress:   []string{},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcfg := &NFConfigs{\n\t\t\t\tHostName:       tt.field.hostName,\n\t\t\t\tIngressXDPBpfs: tt.field.ingressXDPBpfs,\n\t\t\t\tIngressTCBpfs:  tt.field.ingressTCBpfs,\n\t\t\t\tEgressTCBpfs:   tt.field.egressTCBpfs,\n\t\t\t\tHostConfig:     tt.field.hostConfig,\n\t\t\t\tProcessMon:     tt.field.ProcessMon,\n\t\t\t\tHostInterfaces: tt.field.HostInterfaces,\n\t\t\t\tMu:             tt.field.mu,\n\t\t\t}\n\t\t\terr := cfg.DeleteProgramsOnInterface(tt.arg.iface, tt.arg.hostName, tt.arg.bpfProgs)\n\t\t\tif (err != nil) != tt.wanterr {\n\t\t\t\tt.Errorf(\"DeleteProgramsOnInterface failed: %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDeleteEbpfPrograms(t *testing.T) {\n\ttype fields struct {\n\t\thostName       string\n\t\tHostInterfaces map[string]bool\n\t\tingressXDPBpfs map[string]*list.List\n\t\tingressTCBpfs  map[string]*list.List\n\t\tegressTCBpfs   map[string]*list.List\n\t\thostConfig     *config.Config\n\t\tProcessMon     *PCheck\n\t\tmu             *sync.Mutex\n\t\tifaces         map[string]string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfield   fields\n\t\targ     []models.L3afBPFProgramNames\n\t\twanterr bool\n\t}{\n\t\t{\n\t\t\tname: \"UnknowhostName\",\n\t\t\tfield: fields{\n\t\t\t\thostName: \"l3af-prod\",\n\t\t\t\tifaces:   map[string]string{},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\targ: []models.L3afBPFProgramNames{\n\t\t\t\t{\n\t\t\t\t\tHostName: \"l3af-local-test\",\n\t\t\t\t\tIface:    \"fakeif0\",\n\t\t\t\t\tBpfProgramNames: &models.BPFProgramNames{\n\t\t\t\t\t\tXDPIngress: []string{},\n\t\t\t\t\t\tTCIngress:  []string{},\n\t\t\t\t\t\tTCEgress:   []string{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"NilInterface\",\n\t\t\tfield: fields{\n\t\t\t\thostName: \"l3af-local-test\",\n\t\t\t\tifaces:   map[string]string{},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\targ: []models.L3afBPFProgramNames{\n\t\t\t\t{\n\t\t\t\t\tHostName: \"l3af-local-test\",\n\t\t\t\t\tIface:    \"fakeif0\",\n\t\t\t\t\tBpfProgramNames: &models.BPFProgramNames{\n\t\t\t\t\t\tXDPIngress: []string{},\n\t\t\t\t\t\tTCIngress:  []string{},\n\t\t\t\t\t\tTCEgress:   []string{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"UnknownInterface\",\n\t\t\tfield: fields{\n\t\t\t\thostName: \"l3af-local-test\",\n\t\t\t\tifaces:   map[string]string{},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\targ: []models.L3afBPFProgramNames{\n\t\t\t\t{\n\t\t\t\t\tHostName: \"l3af-local-test\",\n\t\t\t\t\tIface:    \"fakeif0\",\n\t\t\t\t\tBpfProgramNames: &models.BPFProgramNames{\n\t\t\t\t\t\tXDPIngress: []string{},\n\t\t\t\t\t\tTCIngress:  []string{},\n\t\t\t\t\t\tTCEgress:   []string{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"GoodInputButNotRealInterface\",\n\t\t\tfield: fields{\n\t\t\t\thostName:       \"l3af-local-test\",\n\t\t\t\tHostInterfaces: map[string]bool{\"fakeif0\": true},\n\t\t\t\tmu:             new(sync.Mutex),\n\t\t\t\tingressXDPBpfs: map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\tingressTCBpfs:  map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\tegressTCBpfs:   map[string]*list.List{\"fakeif0\": nil},\n\t\t\t\tifaces:         map[string]string{},\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tL3afConfigStoreFileName: filepath.FromSlash(\"../testdata/Test_l3af-config.json\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\targ: []models.L3afBPFProgramNames{\n\t\t\t\t{\n\t\t\t\t\tHostName: \"l3af-local-test\",\n\t\t\t\t\tIface:    \"fakeif0\",\n\t\t\t\t\tBpfProgramNames: &models.BPFProgramNames{\n\t\t\t\t\t\tXDPIngress: []string{},\n\t\t\t\t\t\tTCIngress:  []string{},\n\t\t\t\t\t\tTCEgress:   []string{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcfg := &NFConfigs{\n\t\t\t\tHostName:       tt.field.hostName,\n\t\t\t\tIngressXDPBpfs: tt.field.ingressXDPBpfs,\n\t\t\t\tIngressTCBpfs:  tt.field.ingressTCBpfs,\n\t\t\t\tEgressTCBpfs:   tt.field.egressTCBpfs,\n\t\t\t\tHostConfig:     tt.field.hostConfig,\n\t\t\t\tProcessMon:     tt.field.ProcessMon,\n\t\t\t\tHostInterfaces: tt.field.HostInterfaces,\n\t\t\t\tMu:             tt.field.mu,\n\t\t\t}\n\t\t\terr := cfg.DeleteEbpfPrograms(tt.arg)\n\t\t\tif (err != nil) != tt.wanterr {\n\t\t\t\tt.Errorf(\"DeleteEbpfPrograms failed: %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAddAndStartBPF(t *testing.T) {\n\ttype field struct {\n\t\tctx        context.Context\n\t\thostConfig *config.Config\n\t}\n\ttype arg struct {\n\t\tbpfProg   *models.BPFProgram\n\t\tdirection string\n\t\tiface     string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  field\n\t\targs    arg\n\t\twanterr bool\n\t}{\n\t\t{\n\t\t\tname:   \"NilProgram\",\n\t\t\tfields: field{},\n\t\t\targs: arg{\n\t\t\t\tbpfProg:   nil,\n\t\t\t\tdirection: \"fakedirection\",\n\t\t\t\tiface:     \"fakeif0\",\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t\t{\n\t\t\tname:   \"AdminStatusDisabled\",\n\t\t\tfields: field{},\n\t\t\targs: arg{\n\t\t\t\tbpfProg: &models.BPFProgram{\n\t\t\t\t\tName:        \"dummy\",\n\t\t\t\t\tAdminStatus: \"disabled\",\n\t\t\t\t},\n\t\t\t\tdirection: \"fakedirection\",\n\t\t\t\tiface:     \"fakeif0\",\n\t\t\t},\n\t\t\twanterr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcfg := &NFConfigs{\n\t\t\t\tCtx:        tt.fields.ctx,\n\t\t\t\tHostConfig: tt.fields.hostConfig,\n\t\t\t}\n\t\t\te := cfg.AddAndStartBPF(tt.args.bpfProg, tt.args.iface, tt.args.direction)\n\t\t\tif (e != nil) != tt.wanterr {\n\t\t\t\tt.Errorf(\"AddAndStartBPF failed : %v\", e)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAddProgramWithoutChaining(t *testing.T) {\n\tprogList := list.New()\n\tprogList.PushBack(&BPF{\n\t\tProgram: models.BPFProgram{\n\t\t\tName: \"dummyProgram\",\n\t\t},\n\t})\n\ttype fields struct {\n\t\tingressXDPBpfs map[string]*list.List\n\t\tingressTCBpfs  map[string]*list.List\n\t\tegressTCBpfs   map[string]*list.List\n\t\thostConfig     *config.Config\n\t}\n\ttype args struct {\n\t\tiface    string\n\t\thostName string\n\t\tbpfProgs *models.BPFPrograms\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfield   fields\n\t\targ     args\n\t\twanterr bool\n\t}{\n\t\t{\n\t\t\tname: \"chainingEnabled\",\n\t\t\tfield: fields{\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBpfChainingEnabled: true,\n\t\t\t\t},\n\t\t\t},\n\t\t\targ: args{\n\t\t\t\tiface:    \"fakeif0\",\n\t\t\t\thostName: \"fakehost\",\n\t\t\t},\n\t\t\twanterr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"badInput\",\n\t\t\tfield: fields{\n\t\t\t\thostConfig: &config.Config{\n\t\t\t\t\tBpfChainingEnabled: false,\n\t\t\t\t},\n\t\t\t\tingressXDPBpfs: map[string]*list.List{\"fakeif0\": progList},\n\t\t\t\tegressTCBpfs:   map[string]*list.List{\"fakeif0\": progList},\n\t\t\t\tingressTCBpfs:  map[string]*list.List{\"fakeif0\": progList},\n\t\t\t},\n\t\t\targ: args{\n\t\t\t\tiface: \"fakeif0\",\n\t\t\t\tbpfProgs: &models.BPFPrograms{\n\t\t\t\t\tXDPIngress: []*models.BPFProgram{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:        \"dummyProgram\",\n\t\t\t\t\t\t\tAdminStatus: models.Enabled,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tTCIngress: []*models.BPFProgram{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:        \"dummyProgram\",\n\t\t\t\t\t\t\tAdminStatus: models.Enabled,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tTCEgress: []*models.BPFProgram{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:        \"dummyProgram\",\n\t\t\t\t\t\t\tAdminStatus: models.Enabled,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\twanterr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcfg := &NFConfigs{\n\t\t\t\tHostConfig:     tt.field.hostConfig,\n\t\t\t\tIngressXDPBpfs: tt.field.ingressXDPBpfs,\n\t\t\t\tEgressTCBpfs:   tt.field.egressTCBpfs,\n\t\t\t\tIngressTCBpfs:  tt.field.ingressTCBpfs,\n\t\t\t}\n\t\t\te := cfg.AddProgramWithoutChaining(tt.arg.iface, tt.arg.bpfProgs)\n\t\t\tif (e != nil) != tt.wanterr {\n\t\t\t\tt.Errorf(\" AddProgramWithoutChaining failed : %v\", e)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bpfprogs/probes.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !WINDOWS\n// +build !WINDOWS\n\npackage bpfprogs\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/l3af-project/l3afd/v2/stats\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/rs/zerolog/log\"\n)\n\nfunc (b *BPF) LoadBPFProgramProbeType(prog *ebpf.Program, sectionName string) error {\n\tvar progType, hookName, subType string\n\n\tswitch prog.Type() {\n\tcase ebpf.TracePoint:\n\t\tprogType, hookName, subType = GetProgramSectionDetails(sectionName)\n\t\ttp, err := link.Tracepoint(hookName, subType, prog, nil)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to link tracepoint sec name %s error %v\", sectionName, err)\n\t\t}\n\t\tb.ProbeLinks = append(b.ProbeLinks, &tp)\n\tcase ebpf.Kprobe:\n\t\tprogType, hookName, _ = GetProgramSectionDetails(sectionName)\n\t\tkp, err := b.AttachProbePerfEvent(hookName, progType, prog)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to attach perf event error %v\", err)\n\t\t}\n\t\tb.ProbeLinks = append(b.ProbeLinks, &kp)\n\tdefault:\n\t\treturn fmt.Errorf(\"un-supported probe type %s \", prog.Type())\n\t}\n\tebpfProgName := b.Program.Name + \"_\" + progType + \"_\" + hookName\n\tstats.Add(1, stats.BPFStartCount, ebpfProgName, \"\", \"\", \"\")\n\treturn nil\n}\n\n// LoadBPFProgramProbeTypes - Load the BPF programs of probe types - TracePoint\nfunc (b *BPF) LoadBPFProgramProbeTypes(objSpec *ebpf.CollectionSpec) error {\n\tfor i, prog := range b.ProgMapCollection.Programs {\n\t\tif prog.Type() == ebpf.XDP || prog.Type() == ebpf.SchedACT || prog.Type() == ebpf.SchedCLS {\n\t\t\t// skipping XDP/TC programs\n\t\t\tcontinue\n\t\t}\n\t\tif err := b.LoadBPFProgramProbeType(prog, objSpec.Programs[i].SectionName); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, tmpMap := range b.Program.MonitorMaps {\n\t\t\ttmpMetricsMap := b.ProgMapCollection.Maps[tmpMap.Name]\n\t\t\tif tmpMetricsMap == nil {\n\t\t\t\tlog.Error().Msgf(\"%s map is not loaded\", tmpMap.Name)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// GetProgramSectionDetails returns group and name details\n// Section name format prog-type/hook/subtype\n// ret : prog-type, hook, subtype\n// e.g.: tracepoint/sock/inet_sock_set_state\n// e.g.: kprobe/sys_execve\n// e.g.: uprobe/<path>:<provider>:<name>\nfunc GetProgramSectionDetails(sectionName string) (string, string, string) {\n\tsections := strings.Split(sectionName, \"/\")\n\n\tswitch strings.ToLower(sections[0]) {\n\tcase models.TracePoint:\n\t\treturn sections[0], sections[1], sections[2]\n\tcase models.KProbe, models.KRetProbe:\n\t\treturn sections[0], sections[1], \"\"\n\tcase models.UProbe, models.URetProbe:\n\t\tvar funcName string\n\t\tif len(sections) > 2 {\n\t\t\tfuncName = strings.Join(sections[1:], \"/\")\n\t\t}\n\t\treturn sections[0], funcName, \"\"\n\tdefault:\n\t\treturn \"\", \"\", \"\"\n\t}\n}\n\nfunc (b *BPF) AttachProbePerfEvent(hookName, progType string, prog *ebpf.Program) (link.Link, error) {\n\tvar kp link.Link\n\tvar err error\n\tswitch strings.ToLower(progType) {\n\tcase models.KProbe:\n\t\tkp, err = link.Kprobe(hookName, prog, nil)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to link kprobe hook name %s error %v\", hookName, err)\n\t\t}\n\tcase models.KRetProbe:\n\t\tkp, err = link.Kretprobe(hookName, prog, nil)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to link kretprobe hook name %s error %v\", hookName, err)\n\t\t}\n\tcase models.UProbe:\n\t\tkp, err = b.AttachUProbePerfEvent(hookName, prog)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to attach uprobe program %v\", err)\n\t\t}\n\tcase models.URetProbe:\n\t\tkp, err = b.AttachURetProbePerfEvent(hookName, prog)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to attach uretprobe program %v\", err)\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported perf event progType: %s\", progType)\n\t}\n\treturn kp, nil\n}\n\nfunc (b *BPF) AttachUProbePerfEvent(hookName string, prog *ebpf.Program) (link.Link, error) {\n\tvar kp link.Link\n\tfuncNames := strings.Split(hookName, \":\")\n\tex, err := link.OpenExecutable(funcNames[0])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"uprobe failed to openExecutable binary file %s error %v\", hookName, err)\n\t}\n\n\tkp, err = ex.Uprobe(getSymbolName(funcNames), prog, nil)\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to link uprobe symbol %s - %v\", getSymbolName(funcNames), err)\n\t}\n\n\treturn kp, nil\n}\n\nfunc (b *BPF) AttachURetProbePerfEvent(hookName string, prog *ebpf.Program) (link.Link, error) {\n\tvar kp link.Link\n\tfuncNames := strings.Split(hookName, \":\")\n\tex, err := link.OpenExecutable(funcNames[0])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"uretprobe failed to openExecutable binary file %s error %v\", hookName, err)\n\t}\n\n\tkp, err = ex.Uretprobe(getSymbolName(funcNames), prog, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to link uretprobe symbol %s - %v\", getSymbolName(funcNames), err)\n\t}\n\n\treturn kp, nil\n}\n\nfunc getSymbolName(funcNames []string) string {\n\tvar symbol string\n\tif len(funcNames) == 1 {\n\t\tsymbol = funcNames[0]\n\t} else {\n\t\tsymbol = funcNames[len(funcNames)-1]\n\t}\n\treturn symbol\n}\n"
  },
  {
    "path": "bpfprogs/probes_test.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !WINDOWS\n// +build !WINDOWS\n\npackage bpfprogs\n\nimport (\n\t\"testing\"\n)\n\nfunc TestGetProgramSectionDetails(t *testing.T) {\n\n\ttests := []struct {\n\t\tdesc        string\n\t\tsectionName string\n\t\twant1       string\n\t\twant2       string\n\t\twant3       string\n\t}{\n\t\t{\n\t\t\tdesc:        \"empty section name\",\n\t\t\tsectionName: \"\",\n\t\t\twant1:       \"\",\n\t\t\twant2:       \"\",\n\t\t},\n\t\t{\n\t\t\tdesc:        \"section name contains group\",\n\t\t\tsectionName: \"kprobe/perf_event\",\n\t\t\twant1:       \"kprobe\",\n\t\t\twant2:       \"perf_event\",\n\t\t\twant3:       \"\",\n\t\t},\n\t\t{\n\t\t\tdesc:        \"section name contains all details\",\n\t\t\tsectionName: \"tracepoint/sock/inet_sock_set_state\",\n\t\t\twant1:       \"tracepoint\",\n\t\t\twant2:       \"sock\",\n\t\t\twant3:       \"inet_sock_set_state\",\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test\n\t\tt.Run(test.desc, func(t *testing.T) {\n\t\t\tgot1, got2, got3 := GetProgramSectionDetails(test.sectionName)\n\t\t\tif test.want1 != got1 && test.want2 != got2 && test.want3 != got3 {\n\t\t\t\tt.Errorf(\"want1 %v => got1 %v, want2 %v => got2 %v, want3 %v => got3 %v\", test.want1, got1, test.want2, got2, test.want3, got3)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bpfprogs/processCheck.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package bpfprogs provides primitives for BPF process monitoring.\npackage bpfprogs\n\nimport (\n\t\"container/list\"\n\t\"time\"\n\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/l3af-project/l3afd/v2/stats\"\n\t\"github.com/rs/zerolog/log\"\n)\n\ntype PCheck struct {\n\tMaxRetryCount     int\n\tChain             bool\n\tRetryMonitorDelay time.Duration\n}\n\nfunc NewPCheck(rc int, chain bool, interval time.Duration) *PCheck {\n\tc := &PCheck{\n\t\tMaxRetryCount:     rc,\n\t\tChain:             chain,\n\t\tRetryMonitorDelay: interval,\n\t}\n\treturn c\n}\n\nfunc (c *PCheck) PCheckStart(xdpProgs, ingressTCProgs, egressTCProgs map[string]*list.List, probes *list.List, ifaces *map[string]string) {\n\tgo c.pMonitorWorker(xdpProgs, models.XDPIngressType, ifaces)\n\tgo c.pMonitorWorker(ingressTCProgs, models.IngressType, ifaces)\n\tgo c.pMonitorWorker(egressTCProgs, models.EgressType, ifaces)\n\tgo c.pMonitorProbeWorker(probes)\n}\n\nfunc (c *PCheck) pMonitorWorker(bpfProgs map[string]*list.List, direction string, ifaces *map[string]string) {\n\tfor range time.NewTicker(c.RetryMonitorDelay).C {\n\t\tif models.IsReadOnly {\n\t\t\tlog.Info().Msgf(\"Not monitoring because we are in readonly state\")\n\t\t\treturn\n\t\t}\n\t\tfor ifaceName, bpfList := range bpfProgs {\n\t\t\tif bpfList == nil { // no bpf programs are running\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor e := bpfList.Front(); e != nil; e = e.Next() {\n\t\t\t\tbpf := e.Value.(*BPF)\n\t\t\t\tif c.Chain && bpf.Program.SeqID == 0 { // do not monitor root program\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif bpf.Program.AdminStatus == models.Disabled {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tuserProgram, bpfProgram, _ := bpf.isRunning()\n\t\t\t\tif userProgram && bpfProgram {\n\t\t\t\t\tstats.SetWithVersion(1.0, stats.BPFRunning, bpf.Program.Name, bpf.Program.Version, direction, ifaceName, (*ifaces)[ifaceName])\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// Not running trying to restart\n\t\t\t\tif bpf.RestartCount < c.MaxRetryCount && bpf.Program.AdminStatus == models.Enabled {\n\t\t\t\t\tbpf.RestartCount++\n\t\t\t\t\tlog.Warn().Msgf(\"pMonitor BPF Program is not running. Restart attempt: %d, program name: %s, iface: %s\",\n\t\t\t\t\t\tbpf.RestartCount, bpf.Program.Name, ifaceName)\n\t\t\t\t\t//  User program is a daemon and not running, but the BPF program is loaded\n\t\t\t\t\tif !userProgram && bpfProgram {\n\t\t\t\t\t\tif err := bpf.StartUserProgram(ifaceName, direction, c.Chain); err != nil {\n\t\t\t\t\t\t\tlog.Error().Err(err).Msgf(\"pMonitorWorker: BPF Program start user program failed for program %s\", bpf.Program.Name)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// BPF program is not loaded.\n\t\t\t\t\t// if user program is daemon then stop it and restart both the programs\n\t\t\t\t\tif !bpfProgram {\n\t\t\t\t\t\tlog.Warn().Msgf(\"%s BPF program is not loaded, %s program reloading ...\", bpf.Program.EntryFunctionName, bpf.Program.Name)\n\t\t\t\t\t\t// User program is a daemon and running, stop before reloading the BPF program\n\t\t\t\t\t\tif bpf.Program.UserProgramDaemon && userProgram {\n\t\t\t\t\t\t\tif err := bpf.Stop(ifaceName, (*ifaces)[ifaceName], direction, c.Chain); err != nil {\n\t\t\t\t\t\t\t\tlog.Error().Err(err).Msgf(\"pMonitorWorker: BPF Program stop failed for program %s\", bpf.Program.Name)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif err := bpf.Start(ifaceName, (*ifaces)[ifaceName], direction, c.Chain); err != nil {\n\t\t\t\t\t\t\tlog.Error().Err(err).Msgf(\"pMonitorWorker: BPF Program start failed for program %s\", bpf.Program.Name)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstats.SetWithVersion(0.0, stats.BPFRunning, bpf.Program.Name, bpf.Program.Version, direction, ifaceName, (*ifaces)[ifaceName])\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (c *PCheck) pMonitorProbeWorker(bpfProgs *list.List) {\n\tfor range time.NewTicker(c.RetryMonitorDelay).C {\n\t\tif bpfProgs == nil {\n\t\t\ttime.Sleep(time.Second)\n\t\t\tcontinue\n\t\t}\n\t\tfor e := bpfProgs.Front(); e != nil; e = e.Next() {\n\t\t\tbpf := e.Value.(*BPF)\n\t\t\tif bpf.Program.AdminStatus == models.Disabled {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tuserProgram, bpfProgram, _ := bpf.isRunning()\n\t\t\tif userProgram && bpfProgram {\n\t\t\t\tstats.SetWithVersion(1.0, stats.BPFRunning, bpf.Program.Name, bpf.Program.Version, \"\", \"\", \"\")\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Not running trying to restart\n\t\t\tif bpf.RestartCount < c.MaxRetryCount && bpf.Program.AdminStatus == models.Enabled {\n\t\t\t\tbpf.RestartCount++\n\t\t\t\tlog.Warn().Msgf(\"pMonitorProbeWorker: BPF Program is not running. Restart attempt: %d, program name: %s, iface: %s\",\n\t\t\t\t\tbpf.RestartCount, bpf.Program.Name, \"\")\n\t\t\t\t//  User program is a daemon and not running, but the BPF program is loaded\n\t\t\t\tif !userProgram && bpfProgram {\n\t\t\t\t\tif err := bpf.StartUserProgram(\"\", \"\", c.Chain); err != nil {\n\t\t\t\t\t\tlog.Error().Err(err).Msgf(\"pMonitorProbeWorker: BPF Program start user program failed for program %s\", bpf.Program.Name)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// BPF program is not loaded.\n\t\t\t\t// if user program is daemon then stop it and restart both the programs\n\t\t\t\tif !bpfProgram {\n\t\t\t\t\tlog.Warn().Msgf(\"%s BPF program is not loaded, %s program reloading ...\", bpf.Program.EntryFunctionName, bpf.Program.Name)\n\t\t\t\t\t// User program is a daemon and running, stop before reloading the BPF program\n\t\t\t\t\tif bpf.Program.UserProgramDaemon && userProgram {\n\t\t\t\t\t\tif err := bpf.Stop(\"\", \"\", \"\", c.Chain); err != nil {\n\t\t\t\t\t\t\tlog.Error().Err(err).Msgf(\"pMonitorProbeWorker: BPF Program stop failed for program %s\", bpf.Program.Name)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif err := bpf.Start(\"\", \"\", \"\", c.Chain); err != nil {\n\t\t\t\t\t\tlog.Error().Err(err).Msgf(\"pMonitorProbeWorker: BPF Program start failed for program %s\", bpf.Program.Name)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tstats.SetWithVersion(0.0, stats.BPFRunning, bpf.Program.Name, bpf.Program.Version, \"\", \"\", \"\")\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "bpfprogs/processCheck_test.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"container/list\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestNewpCheck(t *testing.T) {\n\ttype args struct {\n\t\trc       int\n\t\tchain    bool\n\t\tinterval time.Duration\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    *PCheck\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"EmptypCheck\",\n\t\t\targs:    args{rc: 0, chain: false, interval: 0},\n\t\t\twant:    &PCheck{MaxRetryCount: 0},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"ValidpCheck\",\n\t\t\targs:    args{rc: 3, chain: true, interval: 10},\n\t\t\twant:    &PCheck{MaxRetryCount: 3},\n\t\t\twantErr: false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := NewPCheck(tt.args.rc, false, 0)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NewpCheck() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_pCheck_pCheckStart(t *testing.T) {\n\ttype fields struct {\n\t\tMaxRetryCount     int\n\t\tchain             bool\n\t\tretryMonitorDelay time.Duration\n\t}\n\ttype args struct {\n\t\tIngressXDPbpfProgs map[string]*list.List\n\t\tIngressTCbpfProgs  map[string]*list.List\n\t\tEgressTCbpfProgs   map[string]*list.List\n\t\tProbebpfs          list.List\n\t\tIfaces             map[string]string\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\tfields  fields\n\t\targs    args\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:   \"EmptyBPF\",\n\t\t\tfields: fields{MaxRetryCount: 3, chain: true, retryMonitorDelay: 10},\n\t\t\targs: args{IngressXDPbpfProgs: make(map[string]*list.List),\n\t\t\t\tIngressTCbpfProgs: make(map[string]*list.List),\n\t\t\t\tEgressTCbpfProgs:  make(map[string]*list.List),\n\t\t\t\tIfaces:            make(map[string]string),\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tc := &PCheck{\n\t\t\t\tMaxRetryCount:     tt.fields.MaxRetryCount,\n\t\t\t\tChain:             tt.fields.chain,\n\t\t\t\tRetryMonitorDelay: tt.fields.retryMonitorDelay,\n\t\t\t}\n\t\t\tc.PCheckStart(tt.args.IngressXDPbpfProgs, tt.args.IngressTCbpfProgs, tt.args.EgressTCbpfProgs, &tt.args.Probebpfs, &tt.args.Ifaces)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "build-docker/Dockerfile",
    "content": "FROM ubuntu:jammy@sha256:6d7b5d3317a71adb5e175640150e44b8b9a9401a7dd394f44840626aff9fa94d\n\nARG DEBIAN_FRONTEND=noninteractive\n\nUSER root\n\n# Install necessary dependencies\nRUN apt-get update && \\\n    apt-get install -y \\\n    clang \\\n    llvm \\\n    libelf-dev \\\n    linux-headers-generic \\\n    linux-tools-common linux-tools-generic \\\n    libbpf-dev \\\n    tzdata \\\n    cmake \\\n    zlib1g-dev \\\n    libevent-dev \\\n    vim \\\n    wget \\\n    curl \\\n    linux-tools-generic \\\n    net-tools \\\n    iproute2 \\\n    elfutils \\\n    libjson-c-dev \\\n    curl \\\n    && rm -rf /var/lib/apt/lists/*\n\nRUN mkdir -p /srv/l3afd/\nRUN mkdir -p /var/l3afd\nRUN mkdir -p /var/log/l3af\nRUN mkdir -p /usr/local/l3afd/latest \nRUN mkdir -p /usr/local/l3afd/v2.1.0/l3afd \n\nCOPY l3afd /usr/local/l3afd/v2.1.0/l3afd/l3afd\nCOPY l3afd.cfg /usr/local/l3afd/v2.1.0/l3afd/l3afd.cfg\nCOPY start.sh /usr/local/l3afd/start.sh\n\nRUN chmod +x /usr/local/l3afd/start.sh\nRUN ln -s /usr/local/l3afd/v2.1.0/l3afd/l3afd /usr/local/l3afd/latest/l3afd \nRUN ln -s /usr/local/l3afd/v2.1.0/l3afd/l3afd.cfg /usr/local/l3afd/latest/l3afd.cfg\n\nENTRYPOINT [\"/bin/bash\",\"/usr/local/l3afd/start.sh\"]\n"
  },
  {
    "path": "build-docker/start.sh",
    "content": "#!/bin/bash\n\n# Start 'l3afd' in the background\n/usr/local/l3afd/latest/l3afd --config /usr/local/l3afd/latest/l3afd.cfg &\nsleep 5;\n\nfunction check_and_wait() {\n    sleep_time=$1\n    while true; do\n    if ps -p $(cat /var/run/l3afd.pid) > /dev/null; then\n        sleep $sleep_time\n    else\n        break\n    fi\n    done\n}\n\nfunction on_term {\n    echo \"Signal $1 received\"\n    kill -SIGTERM  $(cat /var/run/l3afd.pid)\n    check_and_wait 1\n    echo \"L3AFd process has terminated\"\n}\n\ntrap 'on_term SIGHUP' SIGHUP\ntrap 'on_term SIGINT' SIGINT\ntrap 'on_term SIGQUIT' SIGQUIT\ntrap 'on_term SIGTERM' SIGTERM\ntrap 'on_term SIGSTOP' SIGSTOP\ntrap 'on_term SIGSEGV' SIGSEGV\ntrap 'on_term SIGILL' SIGILL\ntrap 'on_term SIGKILL' SIGKILL\ntrap 'on_term SIGABRT' SIGABRT\ntrap 'on_term SIGBUS' SIGBUS\n\ncheck_and_wait 5\n"
  },
  {
    "path": "config/config.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package config provides primitives for l3afd configuration ( i.e. l3afd.cfg) file.\npackage config\n\nimport (\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/robfig/config\"\n\t\"github.com/rs/zerolog/log\"\n)\n\nconst (\n\tENV_PROD = \"PROD\"\n)\n\ntype Config struct {\n\tPIDFilename         string\n\tDataCenter          string\n\tBPFDir              string\n\tBPFLogDir           string\n\tMinKernelMajorVer   int\n\tMinKernelMinorVer   int\n\tEBPFRepoURL         string\n\tHttpClientTimeout   time.Duration\n\tMaxEBPFReStartCount int\n\tEnvironment         string\n\tBpfMapDefaultPath   string\n\t// Flag to enable chaining with root program\n\tBpfChainingEnabled bool\n\n\tFileLogLocation   string\n\tFileLogMaxSize    int\n\tFileLogMaxBackups int\n\tFileLogMaxAge     int\n\tJSONFormatLogs    bool\n\n\t// stats\n\t// Prometheus endpoint for pull/scrape the metrics.\n\tMetricsAddr      string\n\tEBPFPollInterval time.Duration\n\tNMetricSamples   int\n\n\tShutdownTimeout time.Duration\n\n\tSwaggerApiEnabled bool\n\n\t// XDP Root program details.\n\tXDPRootPackageName       string\n\tXDPRootArtifact          string\n\tXDPRootMapName           string\n\tXDPRootCommand           string\n\tXDPRootVersion           string\n\tXDPRootObjectFile        string\n\tXDPRootEntryFunctionName string\n\n\t// TC Root program details.\n\tTCRootPackageName              string\n\tTCRootArtifact                 string\n\tTCRootIngressMapName           string\n\tTCRootEgressMapName            string\n\tTCRootCommand                  string\n\tTCRootVersion                  string\n\tTCRootIngressObjectFile        string\n\tTCRootEgressObjectFile         string\n\tTCRootIngressEntryFunctionName string\n\tTCRootEgressEntryFunctionName  string\n\n\t// ebpf chain details\n\tEBPFChainDebugAddr    string\n\tEBPFChainDebugEnabled bool\n\n\t// l3af configs to listen addrs\n\tL3afConfigsRestAPIAddr string\n\n\t// l3af config store\n\tL3afConfigStoreFileName string\n\n\t// mTLS\n\tMTLSEnabled               bool\n\tMTLSMinVersion            uint16\n\tMTLSCertDir               string\n\tMTLSCACertFilename        string\n\tMTLSServerCertFilename    string\n\tMTLSServerKeyFilename     string\n\tMTLSCertExpiryWarningDays int\n\tMTLSSANMatchRules         []string\n\n\t// graceful-restart\n\tTimetoRestart      int\n\tBasePath           string\n\tRestartArtifactURL string\n\tVersionLimit       int\n}\n\n// ReadConfig - Initializes configuration from file\nfunc ReadConfig(configPath string) (*Config, error) {\n\n\tlog.Info().Msgf(\"Reading configuration from: %s\", configPath)\n\tconfReader, configErr := config.ReadDefault(configPath)\n\tif configErr != nil {\n\t\tlog.Fatal().Err(configErr).Msgf(\"Could not open config file %q\", configPath)\n\t}\n\tminTLSVersion, err := loadTLSVersion(confReader, \"min-tls-version\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &Config{\n\t\tPIDFilename:                    LoadConfigString(confReader, \"l3afd\", \"pid-file\"),\n\t\tDataCenter:                     LoadConfigString(confReader, \"l3afd\", \"datacenter\"),\n\t\tBPFDir:                         LoadConfigString(confReader, \"l3afd\", \"bpf-dir\"),\n\t\tBPFLogDir:                      LoadOptionalConfigString(confReader, \"l3afd\", \"bpf-log-dir\", \"\"),\n\t\tMinKernelMajorVer:              LoadOptionalConfigInt(confReader, \"l3afd\", \"kernel-major-version\", 5),\n\t\tMinKernelMinorVer:              LoadOptionalConfigInt(confReader, \"l3afd\", \"kernel-minor-version\", 15),\n\t\tFileLogLocation:                LoadOptionalConfigString(confReader, \"l3afd\", \"file-log-location\", \"\"),\n\t\tFileLogMaxSize:                 LoadOptionalConfigInt(confReader, \"l3afd\", \"file-log-max-size\", 100),\n\t\tFileLogMaxBackups:              LoadOptionalConfigInt(confReader, \"l3afd\", \"file-log-max-backups\", 10),\n\t\tFileLogMaxAge:                  LoadOptionalConfigInt(confReader, \"l3afd\", \"file-log-max-age\", 60),\n\t\tJSONFormatLogs:                 LoadOptionalConfigBool(confReader, \"l3afd\", \"json-format-logs\", false),\n\t\tEBPFRepoURL:                    LoadConfigString(confReader, \"ebpf-repo\", \"url\"),\n\t\tHttpClientTimeout:              LoadOptionalConfigDuration(confReader, \"l3afd\", \"http-client-timeout\", 30*time.Second),\n\t\tMaxEBPFReStartCount:            LoadOptionalConfigInt(confReader, \"l3afd\", \"max-ebpf-restart-count\", 3),\n\t\tBpfChainingEnabled:             LoadConfigBool(confReader, \"l3afd\", \"bpf-chaining-enabled\"),\n\t\tMetricsAddr:                    LoadOptionalConfigString(confReader, \"web\", \"metrics-addr\", \"localhost:8898\"),\n\t\tEBPFPollInterval:               LoadOptionalConfigDuration(confReader, \"web\", \"ebpf-poll-interval\", 30*time.Second),\n\t\tNMetricSamples:                 LoadOptionalConfigInt(confReader, \"web\", \"n-metric-samples\", 20),\n\t\tShutdownTimeout:                LoadOptionalConfigDuration(confReader, \"l3afd\", \"shutdown-timeout\", 25*time.Second),\n\t\tSwaggerApiEnabled:              LoadOptionalConfigBool(confReader, \"l3afd\", \"swagger-api-enabled\", false),\n\t\tEnvironment:                    LoadOptionalConfigString(confReader, \"l3afd\", \"environment\", ENV_PROD),\n\t\tBpfMapDefaultPath:              LoadConfigString(confReader, \"l3afd\", \"BpfMapDefaultPath\"),\n\t\tXDPRootPackageName:             loadXDPRootPackageName(confReader),\n\t\tXDPRootArtifact:                loadXDPRootArtifact(confReader),\n\t\tXDPRootMapName:                 loadXDPRootIngressMapName(confReader),\n\t\tXDPRootCommand:                 loadXDPRootCommand(confReader),\n\t\tXDPRootVersion:                 loadXDPRootVersion(confReader),\n\t\tXDPRootObjectFile:              LoadOptionalConfigString(confReader, \"xdp-root\", \"object-file\", \"xdp_root.bpf.o\"),\n\t\tXDPRootEntryFunctionName:       LoadOptionalConfigString(confReader, \"xdp-root\", \"entry-function-name\", \"xdp_root\"),\n\t\tTCRootPackageName:              loadTCRootPackageName(confReader),\n\t\tTCRootArtifact:                 loadTCRootArtifact(confReader),\n\t\tTCRootIngressMapName:           loadTCRootIngressMapName(confReader),\n\t\tTCRootEgressMapName:            loadTCRootEgressMapName(confReader),\n\t\tTCRootCommand:                  loadTCRootCommand(confReader),\n\t\tTCRootVersion:                  loadTCRootVersion(confReader),\n\t\tTCRootIngressObjectFile:        LoadOptionalConfigString(confReader, \"tc-root\", \"ingress-object-file\", \"tc_root_ingress.bpf.o\"),\n\t\tTCRootEgressObjectFile:         LoadOptionalConfigString(confReader, \"tc-root\", \"egress-object-file\", \"tc_root_egress.bpf.o\"),\n\t\tTCRootIngressEntryFunctionName: LoadOptionalConfigString(confReader, \"tc-root\", \"ingress-entry-function-name\", \"tc_ingress_root\"),\n\t\tTCRootEgressEntryFunctionName:  LoadOptionalConfigString(confReader, \"tc-root\", \"egress-entry-function-name\", \"tc_egress_root\"),\n\t\tEBPFChainDebugAddr:             LoadOptionalConfigString(confReader, \"ebpf-chain-debug\", \"addr\", \"localhost:8899\"),\n\t\tEBPFChainDebugEnabled:          LoadOptionalConfigBool(confReader, \"ebpf-chain-debug\", \"enabled\", false),\n\t\tL3afConfigsRestAPIAddr:         LoadOptionalConfigString(confReader, \"l3af-configs\", \"restapi-addr\", \"localhost:53000\"),\n\t\tL3afConfigStoreFileName:        LoadConfigString(confReader, \"l3af-config-store\", \"filename\"),\n\t\tMTLSEnabled:                    LoadOptionalConfigBool(confReader, \"mtls\", \"enabled\", true),\n\t\tMTLSMinVersion:                 minTLSVersion,\n\t\tMTLSCertDir:                    LoadOptionalConfigString(confReader, \"mtls\", \"cert-dir\", \"\"),\n\t\tMTLSCACertFilename:             LoadOptionalConfigString(confReader, \"mtls\", \"cacert-filename\", \"ca.pem\"),\n\t\tMTLSServerCertFilename:         LoadOptionalConfigString(confReader, \"mtls\", \"server-cert-filename\", \"server.crt\"),\n\t\tMTLSServerKeyFilename:          LoadOptionalConfigString(confReader, \"mtls\", \"server-key-filename\", \"server.key\"),\n\t\tMTLSCertExpiryWarningDays:      LoadOptionalConfigInt(confReader, \"mtls\", \"cert-expiry-warning-days\", 30),\n\t\tMTLSSANMatchRules:              strings.Split(LoadOptionalConfigString(confReader, \"mtls\", \"san-match-rules\", \"\"), \",\"),\n\t\tBasePath:                       LoadOptionalConfigString(confReader, \"graceful-restart\", \"basepath\", \"/usr/local/l3afd\"),\n\t\tTimetoRestart:                  LoadOptionalConfigInt(confReader, \"graceful-restart\", \"time-to-restart\", 7),\n\t\tVersionLimit:                   LoadOptionalConfigInt(confReader, \"graceful-restart\", \"version-limit\", 100),\n\t\tRestartArtifactURL:             LoadOptionalConfigString(confReader, \"graceful-restart\", \"restart-artifacts-url\", \"file:///srv/l3afd\"),\n\t}, nil\n}\n\nfunc loadTLSVersion(cfgRdr *config.Config, fieldName string) (uint16, error) {\n\tver := strings.TrimSpace(LoadOptionalConfigString(cfgRdr, \"mTLS\", fieldName, \"TLS_1.3\"))\n\tswitch ver {\n\tcase \"\", \"Default\", \"default\":\n\t\treturn tls.VersionTLS13, nil\n\tcase \"TLS_1.2\":\n\t\treturn tls.VersionTLS12, nil\n\tcase \"TLS_1.3\":\n\t\treturn tls.VersionTLS13, nil\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"unsupported TLS version: %s; use: TLS_1.{2,3}\", ver)\n\t}\n}\n\nfunc loadXDPRootPackageName(cfgRdr *config.Config) string {\n\txdpRootPackageName := LoadOptionalConfigString(cfgRdr, \"xdp-root-program\", \"name\", \"\")\n\tif xdpRootPackageName == \"\" {\n\t\txdpRootPackageName = LoadOptionalConfigString(cfgRdr, \"xdp-root\", \"package-name\", \"xdp-root\")\n\t}\n\treturn xdpRootPackageName\n}\n\nfunc loadXDPRootArtifact(cfgRdr *config.Config) string {\n\txdpRootArtifactName := LoadOptionalConfigString(cfgRdr, \"xdp-root-program\", \"artifact\", \"\")\n\tif xdpRootArtifactName == \"\" {\n\t\txdpRootArtifactName = LoadOptionalConfigString(cfgRdr, \"xdp-root\", \"artifact\", \"l3af_xdp_root.tar.gz\")\n\t}\n\treturn xdpRootArtifactName\n}\n\nfunc loadXDPRootIngressMapName(cfgRdr *config.Config) string {\n\txdpRootIngressMapName := LoadOptionalConfigString(cfgRdr, \"xdp-root-program\", \"ingress-map-name\", \"\")\n\tif xdpRootIngressMapName == \"\" {\n\t\txdpRootIngressMapName = LoadOptionalConfigString(cfgRdr, \"xdp-root\", \"ingress-map-name\", \"xdp_root_array\")\n\t}\n\treturn xdpRootIngressMapName\n}\n\nfunc loadXDPRootCommand(cfgRdr *config.Config) string {\n\txdpRootCommand := LoadOptionalConfigString(cfgRdr, \"xdp-root-program\", \"command\", \"\")\n\tif xdpRootCommand == \"\" {\n\t\txdpRootCommand = LoadOptionalConfigString(cfgRdr, \"xdp-root\", \"command\", \"xdp_root\")\n\t}\n\treturn xdpRootCommand\n}\n\nfunc loadXDPRootVersion(cfgRdr *config.Config) string {\n\txdpRootVersion := LoadOptionalConfigString(cfgRdr, \"xdp-root-program\", \"version\", \"\")\n\tif xdpRootVersion == \"\" {\n\t\txdpRootVersion = LoadOptionalConfigString(cfgRdr, \"xdp-root\", \"version\", \"latest\")\n\t}\n\treturn xdpRootVersion\n}\n\nfunc loadTCRootPackageName(cfgRdr *config.Config) string {\n\ttcRootPackageName := LoadOptionalConfigString(cfgRdr, \"tc-root-program\", \"name\", \"\")\n\tif tcRootPackageName == \"\" {\n\t\ttcRootPackageName = LoadOptionalConfigString(cfgRdr, \"tc-root\", \"package-name\", \"tc-root\")\n\t}\n\treturn tcRootPackageName\n}\n\nfunc loadTCRootArtifact(cfgRdr *config.Config) string {\n\ttcRootArtifactName := LoadOptionalConfigString(cfgRdr, \"tc-root-program\", \"artifact\", \"\")\n\tif tcRootArtifactName == \"\" {\n\t\ttcRootArtifactName = LoadOptionalConfigString(cfgRdr, \"tc-root\", \"artifact\", \"l3af_tc_root.tar.gz\")\n\t}\n\treturn tcRootArtifactName\n}\n\nfunc loadTCRootIngressMapName(cfgRdr *config.Config) string {\n\ttcRootIngressMapName := LoadOptionalConfigString(cfgRdr, \"tc-root-program\", \"ingress-map-name\", \"\")\n\tif tcRootIngressMapName == \"\" {\n\t\ttcRootIngressMapName = LoadOptionalConfigString(cfgRdr, \"tc-root\", \"ingress-map-name\", \"tc_ingress_root_array\")\n\t}\n\treturn tcRootIngressMapName\n}\n\nfunc loadTCRootEgressMapName(cfgRdr *config.Config) string {\n\ttcRootEgressMapName := LoadOptionalConfigString(cfgRdr, \"tc-root-program\", \"egress-map-name\", \"\")\n\tif tcRootEgressMapName == \"\" {\n\t\ttcRootEgressMapName = LoadOptionalConfigString(cfgRdr, \"tc-root\", \"egress-map-name\", \"tc_egress_root_array\")\n\t}\n\treturn tcRootEgressMapName\n}\n\nfunc loadTCRootCommand(cfgRdr *config.Config) string {\n\ttcRootCommand := LoadOptionalConfigString(cfgRdr, \"tc-root-program\", \"command\", \"\")\n\tif tcRootCommand == \"\" {\n\t\ttcRootCommand = LoadOptionalConfigString(cfgRdr, \"tc-root\", \"command\", \"tc_root\")\n\t}\n\treturn tcRootCommand\n}\n\nfunc loadTCRootVersion(cfgRdr *config.Config) string {\n\ttcRootVersion := LoadOptionalConfigString(cfgRdr, \"tc-root-program\", \"version\", \"\")\n\tif tcRootVersion == \"\" {\n\t\ttcRootVersion = LoadOptionalConfigString(cfgRdr, \"tc-root\", \"version\", \"latest\")\n\t}\n\treturn tcRootVersion\n}\n"
  },
  {
    "path": "config/config_loader.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage config\n\nimport (\n\t\"encoding/csv\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/robfig/config\"\n\t\"github.com/rs/zerolog/log\"\n)\n\nconst (\n\tcfgFatalMsg    = \"Could not read %s value %q from group %q in config file\"\n\tcfgOptionalMsg = \"Using default value %v after failure to read group:%s; field:%s\"\n)\n\n//Config Shortcuts--------------------------------------------------------------\n// Note: For all the LoadXXX functions, we now have an equivalent LoadOptionalXXX variant.\n// The LoadOptionalXXX variant will accept a default value as a parameter and if it fails to\n// read in a value from the cfg, will return the default value as opposed to terminating odnd.\n// For any new parameters that are not essential and/or have reasonable defaults - it will be\n// a good idea to use the LoadOptionalXXX function.\n\n// LoadConfigString gets the value (as a string) for a field belonging to a group.\n// If the group and field are present - it returns the value\n// If the group or field are absent - it aborts the process\n// Note: Values that are encrypted are decrypted using a global key\nfunc LoadConfigString(confReader *config.Config, group, field string) string {\n\treturn LoadConfigStringEncKey(confReader, group, field)\n}\n\n// LoadOptionalConfigString gets the value (as a string) for a field belonging to a group.\n// If the group and field are present - it returns the value\n// If the group or field are absent - it returns the supplied default value\n// Note: Values that are encrypted are decrypted using a global key\nfunc LoadOptionalConfigString(confReader *config.Config, group, field, defaultValue string) string {\n\treturn LoadOptionalConfigStringEncKey(confReader, group, field, defaultValue)\n}\n\n// LoadOptionalConfigStringEncKey is similar to LoadOptionalConfigString, except that it accepts an optional decryption key.\nfunc LoadOptionalConfigStringEncKey(confReader *config.Config, group, field, defaultValue string) string {\n\tval, err := loadConfigStringEncKey(confReader, group, field)\n\tif err != nil {\n\t\tlog.Info().Err(err).Msgf(cfgOptionalMsg, defaultValue, group, field)\n\t\treturn defaultValue\n\t}\n\treturn val\n}\n\n// LoadConfigStringEncKey is similar to LoadConfigString, except that it accepts an optional decryption key.\nfunc LoadConfigStringEncKey(confReader *config.Config, group, field string) string {\n\tval, err := loadConfigStringEncKey(confReader, group, field)\n\tif err != nil {\n\t\tlog.Fatal().Err(err).Msgf(cfgFatalMsg, \"text\", field, group)\n\t}\n\treturn val\n}\n\n// loadConfigStringEncKey attempts to read the value for a given group and field in a config object.\n// If the value is absent - an error is returned to the caller, who can then abort execution or return a default.\n// If the value is present - it is returned to the caller, and optionally decrypted if the value starts with `ENC:`\n// Note: Decryption is done with the supplied key. If nil - a global key is used for decryption.\nfunc loadConfigStringEncKey(confReader *config.Config, group, field string) (string, error) {\n\t//Read value from config reader\n\tvalue, err := confReader.String(group, field)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn value, nil\n}\n\nfunc LoadConfigBool(confReader *config.Config, group, field string) bool {\n\tvalue, err := confReader.Bool(group, field)\n\tif err != nil {\n\t\tlog.Fatal().Err(err).Msgf(cfgFatalMsg, \"logical\", field, group)\n\t}\n\treturn value\n}\n\nfunc LoadOptionalConfigBool(confReader *config.Config, group, field string, defaultValue bool) bool {\n\tvalue, err := confReader.Bool(group, field)\n\tif err != nil {\n\t\tlog.Info().Err(err).Msgf(cfgOptionalMsg, defaultValue, group, field)\n\t\treturn defaultValue\n\t}\n\treturn value\n}\n\nfunc LoadConfigInt(confReader *config.Config, group, field string) int {\n\tvalue, err := confReader.Int(group, field)\n\tif err != nil {\n\t\tlog.Fatal().Err(err).Msgf(cfgFatalMsg, \"integer\", field, group)\n\t}\n\treturn value\n}\n\nfunc LoadOptionalConfigInt(confReader *config.Config, group, field string, defaultValue int) int {\n\tvalue, err := confReader.Int(group, field)\n\tif err != nil {\n\t\tlog.Info().Err(err).Msgf(cfgOptionalMsg, defaultValue, group, field)\n\t\treturn defaultValue\n\t}\n\treturn value\n}\n\nfunc LoadConfigFloat(confReader *config.Config, group, field string) float64 {\n\tvalue, err := confReader.Float(group, field)\n\tif err != nil {\n\t\tlog.Fatal().Err(err).Msgf(cfgFatalMsg, \"decimal point\", field, group)\n\t}\n\treturn value\n}\n\nfunc LoadOptionalConfigFloat(confReader *config.Config, group, field string, defaultValue float64) float64 {\n\tvalue, err := confReader.Float(group, field)\n\tif err != nil {\n\t\tlog.Info().Err(err).Msgf(cfgOptionalMsg, defaultValue, group, field)\n\t\treturn defaultValue\n\t}\n\treturn value\n}\n\nfunc LoadConfigDuration(confReader *config.Config, group, field string) time.Duration {\n\tvalue, err := time.ParseDuration(LoadConfigString(confReader, group, field))\n\tif err != nil {\n\t\tlog.Fatal().Err(err).Msgf(cfgFatalMsg, \"duration\", field, group)\n\t}\n\treturn value\n}\n\nfunc LoadOptionalConfigDuration(confReader *config.Config, group, field string, defaultValue time.Duration) time.Duration {\n\tstringValue := LoadOptionalConfigString(confReader, group, field, \"\")\n\tif len(stringValue) == 0 {\n\t\treturn defaultValue\n\t}\n\n\tvalue, err := time.ParseDuration(stringValue)\n\tif err != nil {\n\t\tlog.Info().Err(err).Msgf(cfgOptionalMsg, defaultValue, group, field)\n\t\treturn defaultValue\n\t}\n\treturn value\n}\n\nfunc LoadConfigURL(confReader *config.Config, group, field string) *url.URL {\n\tvalue, err := url.Parse(LoadConfigString(confReader, group, field))\n\tif err != nil {\n\t\tlog.Fatal().Err(err).Msgf(cfgFatalMsg, \"url\", field, group)\n\t}\n\treturn value\n}\n\nfunc LoadOptionalConfigURL(confReader *config.Config, group, field string, defaultValue *url.URL) *url.URL {\n\tstringValue := LoadOptionalConfigString(confReader, group, field, \"\")\n\tif len(stringValue) == 0 {\n\t\treturn defaultValue\n\t}\n\n\tvalue, err := url.Parse(stringValue)\n\tif err != nil {\n\t\tlog.Info().Err(err).Msgf(cfgOptionalMsg, defaultValue, group, field)\n\t\treturn defaultValue\n\t}\n\treturn value\n}\n\n// LoadConfigStringCSV splits a CSV config string value and returns the\n// resulting slice of strings. An emptyDefault []string is returned if the config\n// field is emptyDefault (as opposed to []string{\"\"}, which strings.Split() would\n// return).\nfunc LoadConfigStringCSV(confReader *config.Config, group, field string) []string {\n\tCSVStr := strings.TrimSpace(LoadConfigString(confReader, group, field))\n\tif CSVStr == \"\" {\n\t\treturn []string{}\n\t}\n\tvals, err := csv.NewReader(strings.NewReader(CSVStr)).Read()\n\tif err != nil {\n\t\tlog.Fatal().Err(err).Msgf(cfgFatalMsg, \"CSV string\", field, group)\n\t}\n\treturn vals\n}\n\nfunc LoadOptionalConfigStringCSV(confReader *config.Config, group, field string, defaultValue []string) []string {\n\tval, err := loadConfigStringEncKey(confReader, group, field)\n\tif err != nil {\n\t\tlog.Info().Err(err).Msgf(cfgOptionalMsg, defaultValue, group, field)\n\t\treturn defaultValue\n\t}\n\tCSVStr := strings.TrimSpace(val)\n\tif CSVStr == \"\" {\n\t\treturn []string{}\n\t}\n\tvals, err := csv.NewReader(strings.NewReader(CSVStr)).Read()\n\tif err != nil {\n\t\tlog.Fatal().Err(err).Msgf(cfgFatalMsg, \"CSV string\", field, group)\n\t}\n\treturn vals\n}\n"
  },
  {
    "path": "config/l3afd.cfg",
    "content": "[DEFAULT]\n\n[l3afd]\npid-file: /var/run/l3afd.pid\ndatacenter: dummy\nbpf-dir: /dev/shm\nbpf-log-dir:\nkernel-major-version: 5\nkernel-minor-version: 15\nshutdown-timeout: 25s\nhttp-client-timeout: 10s\nmax-ebpf-restart-count: 3\nbpf-chaining-enabled: true\nswagger-api-enabled: true\nenvironment: DEV\nBpfMapDefaultPath: /sys/fs/bpf\n#file-log-location: /var/log/l3afd.log\n#file-log-max-size: 100\n#file-log-max-backups: 20\n#file-log-max-age: 60\n#json-format-logs: true\n\n[ebpf-repo]\nurl: file:///srv/l3afd\n\n\n[web]\nmetrics-addr: localhost:8898\nebpf-poll-interval: 30s\nn-metric-samples: 20\n\n[xdp-root]\npackage-name: xdp-root\nartifact: l3af_xdp_root.tar.gz\ncommand: xdp_root\ningress-map-name: xdp_root_array\nversion: latest\nobject-file: xdp_root.bpf.o\nentry-function-name: xdp_root\n\n[tc-root]\npackage-name: tc-root\nartifact: l3af_tc_root.tar.gz\ningress-map-name: tc_ingress_root_array\negress-map-name: tc_egress_root_array\ncommand: tc_root\nversion: latest\ningress-object-file: tc_root_ingress.bpf.o\negress-object-file: tc_root_egress.bpf.o\ningress-entry-function-name: tc_ingress_root\negress-entry-function-name: tc_egress_root\n\n[ebpf-chain-debug]\naddr: localhost:8899\nenabled: true\n\n[l3af-configs]\nrestapi-addr: localhost:7080\n\n[l3af-config-store]\nfilename: /var/l3afd/l3af-config.json\n\n[mtls]\nenabled: false\n# TLS_1_2 or TLS_1_3\n# min-tls-version: TLS_1_3\n# cert-dir: /etc/l3af/certs\n# cacert-filename: ca.pem\n# server-crt-filename: server.crt\n# server-key-filename: server.key\n# how many days before expiry you want warning\n# cert-expiry-warning-days: 30\n# multiple domains seperated by comma\n# literal and regex are validated in lowercase\n# san-match-rules: .+l3afd.l3af.io,.*l3af.l3af.io,^l3afd.l3af.io$\n\n[l3af-config-store]\nfilename: /var/l3afd/l3af-config.json\n\n[graceful-restart]\nrestart-artifacts-url: file:///srv/l3afd\ntime-to-restart: 7\nbasepath: /usr/local/l3afd\nversion-limit: 100\n"
  },
  {
    "path": "config.yml",
    "content": "register_admind.go\nbpfprogs/cdbhelpers.go\nbpfprogs/bpfCfgs.go\n"
  },
  {
    "path": "docs/CONTRIBUTING.md",
    "content": "# How To Contribute\n\n## Some Ways to Contribute\n- [Getting started with L3AF](#getting-started-with-l3af)\n- [Report potential bugs](#report-potential-bugs)\n- [New features or product enhancements](#new-features-or-product-enhancements)\n- [Submitting a patch that fixes a bug](#submitting-a-patch-that-fixes-a-bug)\n- [Coding style](#coding-style)\n- [Guidelines for pull requests](#guidelines-for-pull-requests)\n- [Improve our guides and documentation](#improve-our-guides-and-documentation)\n- [Increase our test coverage](#increase-our-test-coverage)\n\n### Getting started with L3AF\n\nSee [L3AF](https://wiki.lfnetworking.org/display/L3AF/Getting+Started+with+L3AF)\n\n### Report potential bugs\n\nFirst, **ensure the bug was not already reported** by searching on GitHub under\n[Issues](https://github.com/l3af-project/l3afd/issues).\n\nIf you did not find a related bug, you can help us by\n[submitting a GitHub Issue](https://github.com/l3af-project/l3afd/issues/new).\nA good bug report provides a detailed description of the issue and step-by-step instructions\nfor reliably reproducing the issue.\n\nWe will aim to triage issues in [weekly TSC meetings](https://wiki.lfnetworking.org/display/L3AF/Community+Meetings).\nIn case we are unable to repro the issue, we will request more information from you. There will be a waiting period of\n2 weeks for the requested information and if there is no response, the issue will be closed. If this happens,\nplease reopen the issue if you do get a repro and provide the requested information.\n\nIf you found a security issue, please do not open a GitHub Issue, and instead [email](mailto:security@lists.l3af.io) it in detail.\n\n### New features or product enhancements\n\nYou can request or implement a new feature by [submitting a GitHub Issue](https://github.com/l3af-project/l3afd/issues/new).\nand communicate your proposal so that the L3AF community can review and provide feedback. Getting\nearly feedback will help ensure your implementation work is accepted by the community.\nThis will also allow us to better coordinate our efforts and minimize duplicated effort.\n\n### Submitting a patch that fixes a bug\n\nFork the repo and make your changes. Then open a new GitHub pull request with the patch.\n\n* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number\n  if applicable.\n\n### Guidelines for pull requests\n\nGreat, you want to directly contribute to the l3af project and submit a pull request.\nIt is recommended prior to working on a PR to submit an issue in github for the change you want\nto make describing the change and context around it. This gives the l3af maintainers a chance to review\nthe issue and provide feedback and work with you on the change. If you have any questions, please\nfeel free to reach out to the l3af maintainers via [Slack](https://app.slack.com/client/T02GD9YQJUT/C02GRTC0SAD) or\n[mail](main@lists.l3af.io) group. Below are some general guidelines to help ensure a successful PR approval.\n\n- Provide background why you are making the change and the issue it addresses\n- List what is changing and provide a high-level summary of the change\n- List any relevant dependencies linked to this change\n- Describe the tests that you ran to verify your changes and add/update test cases\n- Update relevant docs, especially if you've changed APIs\n- Ensure all GitHub CI/CD checks pass\n\n### Coding Style\n\nSee [uber-go](https://github.com/uber-go/guide/blob/master/style.md)\n\n### Improve our guides and documentation\n\nWe look forward to contributions improving our guides and documentation.\nDocumentation should be written in an inclusive style. The [Google developer documentation](https://developers.google.com/style/inclusive-documentation)\ncontains an excellent reference on this topic.\n\n### Increase our test coverage\n\nIncrease the code coverage by adding tests. PRs are expected to have 100% test coverage for added code. This can be\nverified with a coverage build. If your PR cannot have 100% coverage for some reason please clearly explain why when\nyou open it. Run your tests and get coverage locally\n\n```bash\ngo test -race -covermode=atomic -coverprofile=coverage.out\n```\n\n## Developer Certificate of Origin (DCO)\n\nThe [Developer Certificate of Origin](https://developercertificate.org/) is a lightweight way for contributors\nto certify that they wrote or otherwise have the right to submit the code they are contributing to the project.\nThis App enforces the Developer Certificate of Origin on Pull Requests. It requires all commit messages to contain\nthe ```Signed-off-by``` line with an email address that matches the commit author.\n\n## Governance\n\nPlease refer to the [governance repo](https://github.com/l3af-project/governance) for Project Charter, Code of Conduct,\nRelease Process, and Committee Members.\n"
  },
  {
    "path": "docs/api/README.md",
    "content": "# L3AFD API Documentation\n\n# Update API\n\nSee [payload.json](https://github.com/l3af-project/l3af-arch/blob/main/dev_environment/cfg/payload.json) for a full example payload.\n\nThe payload will look more like this standard JSON:\n\n```\n[\n  {\n    \"host_name\" : \"l3af-local-test\",\n    \"iface\" : \"enp0s3\",\n    \"bpf_programs\" : {\n      \"xdp_ingress\" : [\n        {\n          \"name\": \"ratelimiting\",\n          \"seq_id\": 1,\n          \"artifact\": \"l3af_ratelimiting.tar.gz\",\n          \"ebpf_package_repo_url\": \"https://l3af.io\"\n          \"map_name\": \"xdp_rl_ingress_next_prog\",\n          \"cmd_start\": \"\",\n          \"version\": \"latest\",\n          \"user_program_daemon\": true,\n          \"admin_status\": \"enabled\",\n          \"prog_type\": \"xdp\",\n          \"cfg_version\": 1,\n          \"map_args\": { \"rl_ports_map\": \"8080,8081\", \"rl_config_map\": \"2\" },\n          \"monitor_maps\": [\n            { \"name\": \"rl_drop_count_map\", \"key\": 0, \"aggregator\": \"scalar\"},\n            { \"name\": \"rl_recv_count_map\", \"key\": 0, \"aggregator\": \"max-rate\"}\n          ],\n          \"object_file\": \"ratelimiting.bpf.o\",\n          \"entry_function_name\": \"_xdp_ratelimiting\"\n        }\n        ],\n      \"tc_ingress\":[\n        {\"...\": \"...\"}\n        ],\n      \"tc_egress\": [\n        {\"...\":  \"...\"}\n      ]\n    }\n  }\n]\n```\n\n### Below is the detailed documentation for each field\n\n| Key                   | Type                                           | Example                                                        | Description                                                                                                                                                                                                                                                                                                          |\n|-----------------------|------------------------------------------------|----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| name                  | string                                         | ratelimiting                                                   | Name of the BPF Program                                                                                                                                                                                                                                                                                              |\n| seq_id                | number                                         | `1`                                                            | Position of the BPF program in the chain. Count starts at 1.                                                                                                                                                                                                                                                         |\n| artifact              | string                                         | `\"l3af_ratelimiting.tar.gz\"`                                   | Userspace BPF program binary and kernel BPF program byte code in tar.gz format                                                                                                                                                                                                                                       |\n| ebpf_package_repo_url | string                                         | `\"https://l3af.io/\"`                                           | eBPF package repository URL.  If it is not provided default URL is used.                                                                                                                                                                                                                                             |\n| map_name              | string                                         | `\"ep1_next_prog_array\"`                                        | Chaining program map to pin to. This should match the BPF program code.                                                                                                                                                                                                                                              |\n| cmd_start             | string                                         | `\"ratelimiting\"`                                               | The command used to start the userspace program. Usually the userspace program binary name. This userspace program can load bpf program (i.e. not loaded by l3afd) and execute custom logic. If the BPF program is loaded by userspace program then initial linking of BPF program should be handled by this program. |\n| cmd_stop              | string                                         |                                                                | The command used stop the userspace program. This program should unlink the program and cleanup the BPF maps                                                                                                                                                                                                         |\n| cmd_status            | string                                         |                                                                | The command used to get the status of the BPF program.                                                                                                                                                                                                                                                               |\n| cmd_update            | string                                         |                                                                | The command used to start the program to update BPF maps dynamically. Usually the userspace program binary name.                                                                                                                                                                                                     |\n| version               | string                                         | `\"latest\"`                                                     | The version of the BPF Program                                                                                                                                                                                                                                                                                       |\n| user_program_daemon   | boolean                                        | `true` or `false`                                              | Whether the userspace program continues running after the BPF program is started                                                                                                                                                                                                                                     |\n| admin_status          | string                                         | `\"enabled\"` or `\"disabled\"`                                    | This represents the program status. `\"enabled\"` means to be started if not running.  `\"disabled\"` means to be stopped if running                                                                                                                                                                                     |\n| prog_type             | string                                         | `\"xdp\"` or `\"tc\"`                                              | Type of BPF program. Currently only XDP and TC network programs are supported.                                                                                                                                                                                                                                       |\n| cfg_version           | number                                         | `1`                                                            | Payload version number                                                                                                                                                                                                                                                                                               |\n| start_args            | map                                            | `{\"collector_ip\": \"10.10.10.2\", \"verbose\":\"2\"}`                | Argument list passed while starting the userspace program using cmd_start.                                                                                                                                                                                                                                           |\n| stop_args             | map                                            |                                                                | Argument list passed while stopping the userspace program using cmd_stop.                                                                                                                                                                                                                                            |\n| status_args           | map                                            |                                                                | Argument list passed while checking the running status of the user program using cmd_status.                                                                                                                                                                                                                         |\n| map_args              | map                                            | `{\"rl_config_map\": \"2\", \"rl_ports_map\":\"80,443\"}`              | BPF map to be updated with the value provided in the config. This option can only be utilized when object file provided to load by l3afd.                                                                                                                                                                            |\n| update_args           | map                                            |                                                                | Argument list passed while calling cmd_update to update the configuration BPF maps.  A program must have logic to parse the map argument and update the appropriate configuration maps for the BPF program.                                                                                                  |\n| monitor_maps          | array of [monitor_maps](#monitor_maps) objects | `[{\"name\":\"cl_drop_count_map\",\"key\":0,\"aggregator\":\"scalar\"}]` | The BPF maps to monitor for metrics and how to aggregate metrics information at each interval metrics are sampled. This option can only be utilized when object file provided to load by l3afd.                                                                                                                      |\n| object_file           | string                                         | `ratelimiting.bpf.o`                                          | The Object file containing BPF programs and maps, this option is needed to load BPF program from l3afd                                                                                                                                                                                                               |\n| entry_function_name   | string                                         | `_xdp_ratelimiting`                                            | The BPF program entry function name, this option is needed to load BPF program from l3afd                                                                                                                                                                                                                            |\n\nNote: `name`, `version`, the Linux distribution name, and `artifact` are\ncombined with the configured ebpf-repo URL into the path that is used to download\nthe artifact containing the BPF program. For example, if\n`name=\"ratelimiting\"`, `version=\"latest\"`, and\n`artifact=\"l3af_ratelimiting.tar.gz\"` and L3AFD is running on Ubuntu 20.04.3\nLTS (Focal Fossa), then we would look for the artifact at:\n\n`http://{ebpf-repo configured in l3afd.cfg}/ratelimiting/latest/focal/l3af_ratelimiting.tar.gz`\n\n## monitor_maps\n\n|Key|Type|Example|Description|\n|--- |--- |--- |--- |\n|name|string|`\"rl_drop_count_map\"`|The name of the map where metrics are stored|\n|key|number|0|The index in the map specified by `name` where metrics are stored|\n|aggregator|string|scalar|The type of metrics aggregation to use for the configured metric sampling interval. Supported values are `\"scalar\"`, `\"max-rate\"`, and `\"avg\"`.|\n\n\n\n\n# Add API \nThe JSON is the same as for the Update API. Refer to above documentation.\n\n\n# Delete API \n\nSee [delete_payload.json](https://github.com/l3af-project/l3af-arch/blob/main/dev_environment/cfg/delete_payload.json) for a full example payload.\n\nThe payload will look more like this standard JSON:\n\n```\n[\n    {\n        \"host_name\": \"l3af-local-test\",\n        \"iface\": \"fakeif0\",\n        \"bpf_programs\": {\n            \"xdp_ingress\": [\n                \"ratelimiting\",\n                \"connection-limit\"\n            ],\n            \"tc_ingress\": [\n              \"...\",\n              \"...\"\n            ],\n            \"tc_egress\": [\n              \"...\", \n              \"...\"\n            ]\n        }\n    }\n]\n\n```\n\n### Below is the detailed documentation for each field\n\n| FieldName     | Example       | Description                           |\n| ------------- | ------------- |---------------------------------------|\n| host_name | `\"l3af-local-test\"` | The host's name                       |\n| iface | `\"fakeif0\"` | Interface name                        |\n| bpf_programs | `\"\"` | List of BPF program names             |\n| xdp_ingress | `\"\"` | Names of xdp ingress type BPF programs |\n| tc_ingress | `\"\"` | Names of tc ingress type BPF programs |\n| tc_egress | `\"\"` | Names of tc egress type BPF programs  |\n\n"
  },
  {
    "path": "docs/configdoc.md",
    "content": "# L3AFD Config Options Documentation\n\nSee [l3afd.cfg](https://github.com/l3af-project/l3afd/blob/main/config/l3afd.cfg) for a full example configuration.\n\n\n```\n[DEFAULT]\n\n[l3afd]\npid-file: ./l3afd.pid\ndatacenter: dc\nbpf-dir: /dev/shm\nbpf-log-dir:\nshutdown-timeout: 1s\nhttp-client-timeout: 10s\nmax-ebpf-restart-count: 3\nbpf-chaining-enabled: true\nswagger-api-enabled: false\n# PROD | DEV\nenvironment: PROD\n....\n```\n\n### Below is the detailed documentation for each field\n\n\n## [l3afd]\n\n| FieldName     | Default                  | Description                                                                                                                                              | Required        |\n| ------------- |--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| --------------- |\n|pid-file| `\"/var/l3afd/l3afd.pid\"` | The path to the l3afd.pid file which contains process id of L3afd                                                                                        | Yes |\n|datacenter| `\"dc\"`                   | Name of Datacenter                                                                                                                                       | Yes |\n|bpf-dir| `\"/dev/shm\"`             | Absolute Path where eBPF packages are to be extracted                                                                                                    | Yes |\n|bpf-log-dir| `\"\"`                     | Absolute Path for log files, which is passed to applications on the command line. L3afd does not store any logs itself.                                  | No |\n|kernel-major-version| `\"5\"`                    | Major version of the kernel required to run eBPF programs (Linux Only)                                                                                   | No |\n|kernel-minor-version| `\"1\"`                    | Minor version of the kernel required to run eBPF programs (Linux Only)                                                                                   | No |\n|shutdown-timeout| `\"1s\"`                   | Maximum amount of time allowed for l3afd to gracefully stop. After shutdown-timeout, l3afd will exit even if it could not stop applications.             | No |\n|http-client-timeout| `\"10s\"`                  | Maximum amount of time allowed to get HTTP response headers when fetching a package from a repository                                                    | No |\n|max-nf-restart-count| `\"3\"`                    | Maximum number of tries to restart eBPF applications if they are not running                                                                             | No |\n|bpf-chaining-enabled| `\"true\"`                 | Boolean to set bpf-chaining. For more info about bpf chaining check [L3AF_KFaaS.pdf](https://github.com/l3af-project/l3af-arch/blob/main/L3AF_KFaaS.pdf) | Yes |\n|swagger-api-enabled| `\"false\"`                | Whether the swagger API is enabled or not.  For more info see [swagger.md](https://github.com/l3af-project/l3afd/blob/main/docs/swagger.md)              | No |\n|environment| `\"PROD\"`                 | If set to anything other than \"PROD\", mTLS security will not be checked                                                                                  | Yes |\n|BpfMapDefaultPath| `\"/sys/fs/bpf\"`          | The base pin path for eBPF maps                                                                                                                          | Yes |\n| file-log-location | `\"/var/log/l3afd.log\"`   | Location of the log file                                                                                                                                 | No |\n| file-log-max-size | `\"100\"`                  | Max size in megabytes for Log file rotation                                                                                                              | No |\n| file-log-max-backups | `\"20\"`                   | Max size in megabytes for Log file rotation                                                                                                              | No |\n| file-log-max-age | `\"60\"`                   | Max number of days to keep Log files                                                                                                                     | No |\n| json-format-logs | `\"false\"`                | Logs to be printed as a JSON object                                                                                                                      | No |\n\n## [ebpf-repo]\n| FieldName     | Default                    | Description     | Required |\n| ------------- |----------------------------| --------------- |----------|\n|url| `\"file:///var/l3afd/repo\"` |Default repository from which to download eBPF packages| Yes      |\n\n## [web]\n\n| FieldName          | Default       | Description     | Required |\n|--------------------| ------------- | --------------- |----------|\n| metrics-addr       |`\"0.0.0.0:8898\"`|Prometheus endpoint for pulling/scraping the metrics.  For more info about Prometheus see [prometheus.io](https://prometheus.io/) | Yes      |\n| ebpf-poll-interval |`\"30s\"`|Periodic interval at which to scrape metrics using Prometheus| No       |\n| n-metric-samples   |`\"20\"`|Number of Metric Samples| No       |\n\n\n## [xdp-root]\nThis section is needed when bpf-chaining-enabled is set to true.\n\n| FieldName           | Default                  | Description                                                              | Required        |\n|---------------------|--------------------------|--------------------------------------------------------------------------| --------------- |\n| package-name        | `\"xdp-root\"`             | Name of subdirectory in which to extract artifact                        | Yes |\n| artifact            | `\"l3af_xdp_root.tar.gz\"` | Filename of xdp-root package. Only tar.gz and .zip formats are supported | Yes |\n| ingress-map-name    | `\"xdp_root_array\"`       | Ingress map name of xdp-root program                                     | Yes |\n| command             | `\"xdp_root\"`             | Command to run xdp-root program                                          | Yes |\n| version             | `\"latest\"`               | Version of xdp-root program                                              | Yes |\n| object-file         | `\"xdp_root.bpf.o\"`      | File containing the object code for xdp-root program                     | Yes |\n| entry-function-name | `\"xdp_root\"`             | Name of the function that begins the XDP-root program                    | Yes |\n\n\n## [tc-root]\nThis section is needed when bpf-chaining-enabled is set to true.\n\n| FieldName                   | Default                    | Description                                                                                                                               | Required        |\n|-----------------------------|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| --------------- |\n| pakage-name                 | `\"tc-root\"`                | Name of subdirectory in which to extract artifact                                                                                         | Yes |\n| artifact                    | `\"l3af_tc_root.tar.gz\"`    | Filename of tc_root package                                                                                                               | Yes |\n| ingress-map-name            | `\"tc_ingress_root_array\"`  | Ingress map name of tc_root program                                                                                                       | Yes |\n| egress-map-name             | `\"tc_egress_root_array\"`   | Egress map name of tc_root program,for more info about ingress/egress check [cilium](https://docs.cilium.io/en/v1.9/concepts/ebpf/intro/) | Yes |\n| command                     | `\"tc_root\"`                | Command to run tc_root program                                                                                                            | Yes |\n| version                     | `\"latest\"`                 | Version of tc_root program                                                                                                                | Yes |\n| ingress-object-file         | `\"tc_root_ingress.bpf.o\"` | File containing the object code for tc-root ingress program                                                                               | Yes |\n| egress-object-file          | `\"tc_root_egress.bpf.o\"`  | File containing the object code for tc-root egress program                                                                                | Yes |\n| ingress-entry-function-name | `\"tc_ingress_root\"`        | Name of the function that begins the tc-root ingress program                                                                              | Yes |\n| egress-entry-function-name  | `\"tc_egress_root\"`         | Name of the function that begins the tc-root egress program                                                                               | Yes |\n\n\n## [ebpf-chain-debug]\n| FieldName | Default            | Description                                                    | Required |\n|-----------|--------------------|----------------------------------------------------------------|----------|\n| addr      | `\"localhost:8899\"` | Hostname and Port of chaining debug REST API                   | No       |\n| enabled   | `\"false\"`          | Boolean to check ebpf chaining debug details is enabled or not | No       |\n\n## [l3af-configs]\n| FieldName     | Default       | Description     | Required |\n| ------------- | ------------- | --------------- |----------|\n|restapi-addr|`\"localhost:53000\"`| Hostname and Port of l3af-configs REST API | No       |\n\n## [l3af-config-store]\n| FieldName     | Default       | Description     | Required        |\n| ------------- | ------------- | --------------- | --------------- |\n|filename|`\"/etc/l3afd/l3af-config.json\"`|Absolute path of persistent config file where we are storing L3afBPFPrograms objects. For more info see [models](https://github.com/l3af-project/l3afd/blob/main/models/l3afd.go)| Yes |\n\n## [mtls]\n| FieldName     | Default                            | Description                                                                                                                                                                                                                  | Required |\n| ------------- |------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|\n|enabled| `\"true\"`                           | Boolean controlling whether mTLS is enabled or not on the REST API exposed by l3afd                                                                                                                                          | No       |\n|min-tls-version| `\"1.3\"`                            | Minimum tls version allowed                                                                                                                                                                                                  | No       |\n|cert-dir| `\"/etc/l3afd/certs\"`               | Absolute path of CA certificates. On Linux this points to a filesystem directory, but on Windows it can point to a [certificate store](https://docs.microsoft.com/en-us/windows-hardware/drivers/install/certificate-stores) | No       |\n|server-crt-filename| `\"server.crt\"`                     | Server's ca certificate filename                                                                                                                                                                                             | No       |\n|server-key-filename| `\"server.key\"`                     | Server's mtls key filename                                                                                                                                                                                                   | No       |\n|cert-expiry-warning-days| `\"30\"`                             | How many days before expiry you want warning                                                                                                                                                                                 | No       |\n|san-match-rules| `\".*l3af.l3af.io,^l3afd.l3af.io$\"` | List of domain names (exact match) or regular expressions to validate client SAN DNS Names against                                                                                                                                                                  | No      |\n"
  },
  {
    "path": "docs/docs.go",
    "content": "// Package docs Code generated by swaggo/swag. DO NOT EDIT\npackage docs\n\nimport \"github.com/swaggo/swag\"\n\nconst docTemplate = `{\n    \"schemes\": {{ marshal .Schemes }},\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"{{escape .Description}}\",\n        \"title\": \"{{.Title}}\",\n        \"contact\": {},\n        \"version\": \"{{.Version}}\"\n    },\n    \"host\": \"{{.Host}}\",\n    \"basePath\": \"{{.BasePath}}\",\n    \"paths\": {\n        \"/l3af/configs/v1\": {\n            \"get\": {\n                \"description\": \"Returns details of the configuration of eBPF Programs for all interfaces on a node\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"summary\": \"Returns details of the configuration of eBPF Programs for all interfaces on a node\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\"\n                    }\n                }\n            }\n        },\n        \"/l3af/configs/v1/add\": {\n            \"post\": {\n                \"description\": \"Adds new eBPF Programs on node\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"summary\": \"Adds new eBPF Programs on node\",\n                \"parameters\": [\n                    {\n                        \"description\": \"BPF programs\",\n                        \"name\": \"cfgs\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/definitions/models.L3afBPFPrograms\"\n                            }\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\"\n                    }\n                }\n            }\n        },\n        \"/l3af/configs/v1/delete\": {\n            \"post\": {\n                \"description\": \"Removes eBPF Programs on node\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"summary\": \"Removes eBPF Programs on node\",\n                \"parameters\": [\n                    {\n                        \"description\": \"BPF program names\",\n                        \"name\": \"cfgs\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/definitions/models.L3afBPFProgramNames\"\n                            }\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\"\n                    }\n                }\n            }\n        },\n        \"/l3af/configs/v1/restart\": {\n            \"put\": {\n                \"description\": \"Store meta data about ebpf programs and exit\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"summary\": \"Store meta data about ebpf programs and exit\",\n                \"parameters\": [\n                    {\n                        \"description\": \"BPF programs\",\n                        \"name\": \"cfgs\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/definitions/models.L3afBPFPrograms\"\n                            }\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\"\n                    }\n                }\n            }\n        },\n        \"/l3af/configs/v1/update\": {\n            \"post\": {\n                \"description\": \"Update eBPF Programs configuration\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"summary\": \"Update eBPF Programs configuration\",\n                \"parameters\": [\n                    {\n                        \"description\": \"BPF programs\",\n                        \"name\": \"cfgs\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/definitions/models.L3afBPFPrograms\"\n                            }\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\"\n                    }\n                }\n            }\n        },\n        \"/l3af/configs/v1/{iface}\": {\n            \"get\": {\n                \"description\": \"Returns details of the configuration of eBPF Programs for a given interface\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"summary\": \"Returns details of the configuration of eBPF Programs for a given interface\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"interface name\",\n                        \"name\": \"iface\",\n                        \"in\": \"path\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\"\n                    }\n                }\n            }\n        }\n    },\n    \"definitions\": {\n        \"models.BPFProgram\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"admin_status\": {\n                    \"description\": \"Program admin status enabled or disabled\",\n                    \"type\": \"string\"\n                },\n                \"artifact\": {\n                    \"description\": \"Artifact file name\",\n                    \"type\": \"string\"\n                },\n                \"cfg_version\": {\n                    \"description\": \"Config version\",\n                    \"type\": \"integer\"\n                },\n                \"cmd_config\": {\n                    \"description\": \"Program config providing command\",\n                    \"type\": \"string\"\n                },\n                \"cmd_start\": {\n                    \"description\": \"Program start command\",\n                    \"type\": \"string\"\n                },\n                \"cmd_status\": {\n                    \"description\": \"Program status command\",\n                    \"type\": \"string\"\n                },\n                \"cmd_stop\": {\n                    \"description\": \"Program stop command\",\n                    \"type\": \"string\"\n                },\n                \"cmd_update\": {\n                    \"description\": \"Program update config command\",\n                    \"type\": \"string\"\n                },\n                \"config_args\": {\n                    \"description\": \"Map of arguments to config command\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.L3afDNFArgs\"\n                        }\n                    ]\n                },\n                \"config_file_path\": {\n                    \"description\": \"Config file location\",\n                    \"type\": \"string\"\n                },\n                \"cpu\": {\n                    \"description\": \"User program cpu limits\",\n                    \"type\": \"integer\"\n                },\n                \"ebpf_package_repo_url\": {\n                    \"description\": \"Download url for Program\",\n                    \"type\": \"string\"\n                },\n                \"entry_function_name\": {\n                    \"description\": \"BPF entry function name to load\",\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"description\": \"Program id\",\n                    \"type\": \"integer\"\n                },\n                \"is_plugin\": {\n                    \"description\": \"User program is plugin or not\",\n                    \"type\": \"boolean\"\n                },\n                \"map_args\": {\n                    \"description\": \"Config BPF Map of arguments\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.L3afDMapArg\"\n                    }\n                },\n                \"map_name\": {\n                    \"description\": \"BPF map to store next program fd\",\n                    \"type\": \"string\"\n                },\n                \"memory\": {\n                    \"description\": \"User program memory limits\",\n                    \"type\": \"integer\"\n                },\n                \"monitor_maps\": {\n                    \"description\": \"Metrics BPF maps\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.L3afDNFMetricsMap\"\n                    }\n                },\n                \"name\": {\n                    \"description\": \"Name of the BPF program package\",\n                    \"type\": \"string\"\n                },\n                \"object_file\": {\n                    \"description\": \"Object file contains BPF code\",\n                    \"type\": \"string\"\n                },\n                \"prog_type\": {\n                    \"description\": \"Program type XDP or TC\",\n                    \"type\": \"string\"\n                },\n                \"rules\": {\n                    \"description\": \"Config rules\",\n                    \"type\": \"string\"\n                },\n                \"rules_file\": {\n                    \"description\": \"Config rules file name\",\n                    \"type\": \"string\"\n                },\n                \"seq_id\": {\n                    \"description\": \"Sequence position in the chain\",\n                    \"type\": \"integer\"\n                },\n                \"start_args\": {\n                    \"description\": \"Map of arguments to start command\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.L3afDNFArgs\"\n                        }\n                    ]\n                },\n                \"status_args\": {\n                    \"description\": \"Map of arguments to status command\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.L3afDNFArgs\"\n                        }\n                    ]\n                },\n                \"stop_args\": {\n                    \"description\": \"Map of arguments to stop command\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.L3afDNFArgs\"\n                        }\n                    ]\n                },\n                \"update_args\": {\n                    \"description\": \"Map of arguments to update command\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.L3afDNFArgs\"\n                        }\n                    ]\n                },\n                \"user_program_daemon\": {\n                    \"description\": \"User program daemon or not\",\n                    \"type\": \"boolean\"\n                },\n                \"version\": {\n                    \"description\": \"Program version\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.BPFProgramNames\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"probes\": {\n                    \"description\": \"names of the probe eBPF programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"string\"\n                    }\n                },\n                \"tc_egress\": {\n                    \"description\": \"names of the TC egress eBPF programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"string\"\n                    }\n                },\n                \"tc_ingress\": {\n                    \"description\": \"names of the TC ingress eBPF programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"string\"\n                    }\n                },\n                \"xdp_ingress\": {\n                    \"description\": \"names of the XDP ingress eBPF programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"string\"\n                    }\n                }\n            }\n        },\n        \"models.BPFPrograms\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"probes\": {\n                    \"description\": \"list of probe bpf programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.BPFProgram\"\n                    }\n                },\n                \"tc_egress\": {\n                    \"description\": \"list of tc egress bpf programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.BPFProgram\"\n                    }\n                },\n                \"tc_ingress\": {\n                    \"description\": \"list of tc ingress bpf programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.BPFProgram\"\n                    }\n                },\n                \"xdp_ingress\": {\n                    \"description\": \"list of xdp ingress bpf programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.BPFProgram\"\n                    }\n                }\n            }\n        },\n        \"models.KeyValue\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"key\": {\n                    \"description\": \"Key\",\n                    \"type\": \"integer\"\n                },\n                \"value\": {\n                    \"description\": \"Value\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"models.L3afBPFProgramNames\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"bpf_programs\": {\n                    \"description\": \"List of eBPF program names to remove\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.BPFProgramNames\"\n                        }\n                    ]\n                },\n                \"host_name\": {\n                    \"description\": \"Host name or pod name\",\n                    \"type\": \"string\"\n                },\n                \"iface\": {\n                    \"description\": \"Interface name\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.L3afBPFPrograms\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"bpf_programs\": {\n                    \"description\": \"List of bpf programs\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.BPFPrograms\"\n                        }\n                    ]\n                },\n                \"host_name\": {\n                    \"description\": \"Host name or pod name\",\n                    \"type\": \"string\"\n                },\n                \"iface\": {\n                    \"description\": \"Interface name\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.L3afDMapArg\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"args\": {\n                    \"description\": \"BPF map arguments\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.KeyValue\"\n                    }\n                },\n                \"name\": {\n                    \"description\": \"BPF map name\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.L3afDNFArgs\": {\n            \"type\": \"object\",\n            \"additionalProperties\": true\n        },\n        \"models.L3afDNFMetricsMap\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"aggregator\": {\n                    \"description\": \"Aggregation function names\",\n                    \"type\": \"string\"\n                },\n                \"key\": {\n                    \"description\": \"Index of the bpf map\",\n                    \"type\": \"integer\"\n                },\n                \"name\": {\n                    \"description\": \"BPF map name\",\n                    \"type\": \"string\"\n                }\n            }\n        }\n    }\n}`\n\n// SwaggerInfo holds exported Swagger Info so clients can modify it\nvar SwaggerInfo = &swag.Spec{\n\tVersion:          \"1.0\",\n\tHost:             \"\",\n\tBasePath:         \"/\",\n\tSchemes:          []string{},\n\tTitle:            \"L3AFD APIs\",\n\tDescription:      \"Configuration APIs to deploy and get the details of the eBPF Programs on the node\",\n\tInfoInstanceName: \"swagger\",\n\tSwaggerTemplate:  docTemplate,\n\tLeftDelim:        \"{{\",\n\tRightDelim:       \"}}\",\n}\n\nfunc init() {\n\tswag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)\n}\n"
  },
  {
    "path": "docs/graceful-restart-guide.md",
    "content": "# Guide to L3AFD Graceful Restart\n\n## Prerequisites\nTo begin, ensure that you have a specific folder where the `l3afd` binary and `l3afd.cfg` files are present. By default, this should be in `/usr/local/l3afd/`.\n\n## Directory Structure\n\nFirstly, create a directory structure as shown below:\n\n```\n/usr/local/l3afd# tree\n.\n├── latest\n│   ├── l3afd -> /usr/local/l3afd/v2.0.0/l3afd/l3afd\n│   └── l3afd.cfg -> /usr/local/l3afd/v2.0.0/l3afd/l3afd.cfg\n├── start.sh\n└── v2.0.0\n    └── l3afd\n        ├── l3afd\n        └── l3afd.cfg\n```\nL3afd runs a certain version (in this case, v2.0.0), and in the 'latest' folder, it is symlinked.\n\n## Starting the l3afd Service\nTo start the service, run the start.sh script:\n\n```\n/usr/local/l3afd# cat start.sh\n#!/bin/bash\n/usr/local/l3afd/latest/l3afd --config /usr/local/l3afd/latest/l3afd.cfg &\n```\nEnsure that the PIDFile in the service file matches the path used in `l3afd.cfg`. This is crucial for systemd to monitor the l3afd PID.\n\n## Upgrading to a New Version\n\nTo upgrade from v2.0.0 to v2.x.x, follow these steps. L3afd supports HTTP, HTTPS, and file protocols for downloading artifacts and tar.gz or .zip compression formats.\n\nHere's an example of how to create an artifact:\n\n1. Create a folder at /srv/l3afd/pkg/.\n2. Inside that, create /srv/l3afd/pkg/l3afd.\n3. Copy your v2.x.0 binary and cfg file to the above folder.\n4. Create a tar gunzip file: `tar -czvf l3afd.tar.gz l3afd` or create a zip file.\n\nThe artifact can be served from a remote server, local server, or local folder.\n\nFor a local start, your payload for the restart would look like this:\n\n```\n{\n\t\"hostname\": \"l3af-test-host\",\n\t\"version\": \"v2.x.x\",\n}\n```\n## Restarting the Service\n\nTo restart the service, use the following API call:\n\n```\ncurl -X PUT http://localhost:7080/l3af/configs/v1/restart -d \"@restart.json\"\n```\nDuring this graceful restart, eBPF Programs of type probes and all user programs are restarted.\n\nNote: During a restart, the HTTP endpoint will always be active, meaning you can make HTTP requests to that endpoint. However, all write operations (add, remove, modify program configurations) are blocked. If there are any dependent services on user_programs, you should restart them manually after restarting eBPF Programs. Expect minor metric discrepancies during the restart process."
  },
  {
    "path": "docs/prod-deploy-guide.md",
    "content": "# Guide to use L3AF in production environments\n\n## Installing l3afd\n\nDownload the latest build artifacts for the last stable release on the l3afd [repo page](../../../)\n\n## Configuring l3afd\n\nThis guide lists recommendations on how to run l3afd in a production environment.  Please see [l3afd.cfg](../config/l3afd.cfg) for a sample configuration.\n\nThe only secure configuration for production deployments at this time is with mTLS enabled.  mTLS is necessary to properly protect the REST API when running in production mode.  To securely run l3afd in a production environment please follow the configuration guidelines below.\n\n* Make sure `environment: PROD` is set to prevent l3afd starting up in an insecure configuration.\n\n* Ensure mTLS is set to  `enabled: true` in the configuration.\n\n* It is recommended to use TLS version `1.3`.\n\n* Do not use self-signed certificates.  It is always encouraged to use well-known root certificates to create server certificates and client certificates.\n\n* The debug log API should only be enabled and set to listen on localhost when it is required to debug issues with program chaining. The debug log should normally be disabled by setting `enable: false` in the `ebpf-chain-debug` section.\n\n* For security reasons, it is not recommended configuring l3afd to point to a public eBPF repository.  Instead, configure l3afd to point to a private mirror or local file repository once you have validated and ensured the eBPF programs are safe to run in production. \n  * eBPF repository artifacts are retrieved by joining the following elements to build the complete path: `https://<ebpf-repo-url>/<ebpf-program>/<version>/<platform>/<artifact>` or `file:///<repo-dir>/<ebpf-program>/<version>/<platform>/<artifact>`.\n\n## Running l3afd\n\n* l3afd on Linux needs to run with the `CAP_SYS_ADMIN` or with the `CAP_BPF`, `CAP_NET_ADMIN`, and `CAP_PERFMON` privileges (newer kernels). Unprivileged users will not have the necessary permissions to load eBPF programs.\n\n* l3afd only supports handling the following signals `SIGINT`, `SIGTERM`, which will cause l3afd to perform a clean shut down.\n\n* l3afd can be configured through a system manager to start on boot, such as systemd.\n"
  },
  {
    "path": "docs/swagger.json",
    "content": "{\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"Configuration APIs to deploy and get the details of the eBPF Programs on the node\",\n        \"title\": \"L3AFD APIs\",\n        \"contact\": {},\n        \"version\": \"1.0\"\n    },\n    \"basePath\": \"/\",\n    \"paths\": {\n        \"/l3af/configs/v1\": {\n            \"get\": {\n                \"description\": \"Returns details of the configuration of eBPF Programs for all interfaces on a node\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"summary\": \"Returns details of the configuration of eBPF Programs for all interfaces on a node\",\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\"\n                    }\n                }\n            }\n        },\n        \"/l3af/configs/v1/add\": {\n            \"post\": {\n                \"description\": \"Adds new eBPF Programs on node\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"summary\": \"Adds new eBPF Programs on node\",\n                \"parameters\": [\n                    {\n                        \"description\": \"BPF programs\",\n                        \"name\": \"cfgs\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/definitions/models.L3afBPFPrograms\"\n                            }\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\"\n                    }\n                }\n            }\n        },\n        \"/l3af/configs/v1/delete\": {\n            \"post\": {\n                \"description\": \"Removes eBPF Programs on node\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"summary\": \"Removes eBPF Programs on node\",\n                \"parameters\": [\n                    {\n                        \"description\": \"BPF program names\",\n                        \"name\": \"cfgs\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/definitions/models.L3afBPFProgramNames\"\n                            }\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\"\n                    }\n                }\n            }\n        },\n        \"/l3af/configs/v1/restart\": {\n            \"put\": {\n                \"description\": \"Store meta data about ebpf programs and exit\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"summary\": \"Store meta data about ebpf programs and exit\",\n                \"parameters\": [\n                    {\n                        \"description\": \"BPF programs\",\n                        \"name\": \"cfgs\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/definitions/models.L3afBPFPrograms\"\n                            }\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\"\n                    }\n                }\n            }\n        },\n        \"/l3af/configs/v1/update\": {\n            \"post\": {\n                \"description\": \"Update eBPF Programs configuration\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"summary\": \"Update eBPF Programs configuration\",\n                \"parameters\": [\n                    {\n                        \"description\": \"BPF programs\",\n                        \"name\": \"cfgs\",\n                        \"in\": \"body\",\n                        \"required\": true,\n                        \"schema\": {\n                            \"type\": \"array\",\n                            \"items\": {\n                                \"$ref\": \"#/definitions/models.L3afBPFPrograms\"\n                            }\n                        }\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\"\n                    }\n                }\n            }\n        },\n        \"/l3af/configs/v1/{iface}\": {\n            \"get\": {\n                \"description\": \"Returns details of the configuration of eBPF Programs for a given interface\",\n                \"consumes\": [\n                    \"application/json\"\n                ],\n                \"produces\": [\n                    \"application/json\"\n                ],\n                \"summary\": \"Returns details of the configuration of eBPF Programs for a given interface\",\n                \"parameters\": [\n                    {\n                        \"type\": \"string\",\n                        \"description\": \"interface name\",\n                        \"name\": \"iface\",\n                        \"in\": \"path\",\n                        \"required\": true\n                    }\n                ],\n                \"responses\": {\n                    \"200\": {\n                        \"description\": \"OK\"\n                    }\n                }\n            }\n        }\n    },\n    \"definitions\": {\n        \"models.BPFProgram\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"admin_status\": {\n                    \"description\": \"Program admin status enabled or disabled\",\n                    \"type\": \"string\"\n                },\n                \"artifact\": {\n                    \"description\": \"Artifact file name\",\n                    \"type\": \"string\"\n                },\n                \"cfg_version\": {\n                    \"description\": \"Config version\",\n                    \"type\": \"integer\"\n                },\n                \"cmd_config\": {\n                    \"description\": \"Program config providing command\",\n                    \"type\": \"string\"\n                },\n                \"cmd_start\": {\n                    \"description\": \"Program start command\",\n                    \"type\": \"string\"\n                },\n                \"cmd_status\": {\n                    \"description\": \"Program status command\",\n                    \"type\": \"string\"\n                },\n                \"cmd_stop\": {\n                    \"description\": \"Program stop command\",\n                    \"type\": \"string\"\n                },\n                \"cmd_update\": {\n                    \"description\": \"Program update config command\",\n                    \"type\": \"string\"\n                },\n                \"config_args\": {\n                    \"description\": \"Map of arguments to config command\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.L3afDNFArgs\"\n                        }\n                    ]\n                },\n                \"config_file_path\": {\n                    \"description\": \"Config file location\",\n                    \"type\": \"string\"\n                },\n                \"cpu\": {\n                    \"description\": \"User program cpu limits\",\n                    \"type\": \"integer\"\n                },\n                \"ebpf_package_repo_url\": {\n                    \"description\": \"Download url for Program\",\n                    \"type\": \"string\"\n                },\n                \"entry_function_name\": {\n                    \"description\": \"BPF entry function name to load\",\n                    \"type\": \"string\"\n                },\n                \"id\": {\n                    \"description\": \"Program id\",\n                    \"type\": \"integer\"\n                },\n                \"is_plugin\": {\n                    \"description\": \"User program is plugin or not\",\n                    \"type\": \"boolean\"\n                },\n                \"map_args\": {\n                    \"description\": \"Config BPF Map of arguments\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.L3afDMapArg\"\n                    }\n                },\n                \"map_name\": {\n                    \"description\": \"BPF map to store next program fd\",\n                    \"type\": \"string\"\n                },\n                \"memory\": {\n                    \"description\": \"User program memory limits\",\n                    \"type\": \"integer\"\n                },\n                \"monitor_maps\": {\n                    \"description\": \"Metrics BPF maps\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.L3afDNFMetricsMap\"\n                    }\n                },\n                \"name\": {\n                    \"description\": \"Name of the BPF program package\",\n                    \"type\": \"string\"\n                },\n                \"object_file\": {\n                    \"description\": \"Object file contains BPF code\",\n                    \"type\": \"string\"\n                },\n                \"prog_type\": {\n                    \"description\": \"Program type XDP or TC\",\n                    \"type\": \"string\"\n                },\n                \"rules\": {\n                    \"description\": \"Config rules\",\n                    \"type\": \"string\"\n                },\n                \"rules_file\": {\n                    \"description\": \"Config rules file name\",\n                    \"type\": \"string\"\n                },\n                \"seq_id\": {\n                    \"description\": \"Sequence position in the chain\",\n                    \"type\": \"integer\"\n                },\n                \"start_args\": {\n                    \"description\": \"Map of arguments to start command\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.L3afDNFArgs\"\n                        }\n                    ]\n                },\n                \"status_args\": {\n                    \"description\": \"Map of arguments to status command\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.L3afDNFArgs\"\n                        }\n                    ]\n                },\n                \"stop_args\": {\n                    \"description\": \"Map of arguments to stop command\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.L3afDNFArgs\"\n                        }\n                    ]\n                },\n                \"update_args\": {\n                    \"description\": \"Map of arguments to update command\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.L3afDNFArgs\"\n                        }\n                    ]\n                },\n                \"user_program_daemon\": {\n                    \"description\": \"User program daemon or not\",\n                    \"type\": \"boolean\"\n                },\n                \"version\": {\n                    \"description\": \"Program version\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.BPFProgramNames\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"probes\": {\n                    \"description\": \"names of the probe eBPF programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"string\"\n                    }\n                },\n                \"tc_egress\": {\n                    \"description\": \"names of the TC egress eBPF programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"string\"\n                    }\n                },\n                \"tc_ingress\": {\n                    \"description\": \"names of the TC ingress eBPF programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"string\"\n                    }\n                },\n                \"xdp_ingress\": {\n                    \"description\": \"names of the XDP ingress eBPF programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"string\"\n                    }\n                }\n            }\n        },\n        \"models.BPFPrograms\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"probes\": {\n                    \"description\": \"list of probe bpf programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.BPFProgram\"\n                    }\n                },\n                \"tc_egress\": {\n                    \"description\": \"list of tc egress bpf programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.BPFProgram\"\n                    }\n                },\n                \"tc_ingress\": {\n                    \"description\": \"list of tc ingress bpf programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.BPFProgram\"\n                    }\n                },\n                \"xdp_ingress\": {\n                    \"description\": \"list of xdp ingress bpf programs\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.BPFProgram\"\n                    }\n                }\n            }\n        },\n        \"models.KeyValue\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"key\": {\n                    \"description\": \"Key\",\n                    \"type\": \"integer\"\n                },\n                \"value\": {\n                    \"description\": \"Value\",\n                    \"type\": \"integer\"\n                }\n            }\n        },\n        \"models.L3afBPFProgramNames\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"bpf_programs\": {\n                    \"description\": \"List of eBPF program names to remove\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.BPFProgramNames\"\n                        }\n                    ]\n                },\n                \"host_name\": {\n                    \"description\": \"Host name or pod name\",\n                    \"type\": \"string\"\n                },\n                \"iface\": {\n                    \"description\": \"Interface name\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.L3afBPFPrograms\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"bpf_programs\": {\n                    \"description\": \"List of bpf programs\",\n                    \"allOf\": [\n                        {\n                            \"$ref\": \"#/definitions/models.BPFPrograms\"\n                        }\n                    ]\n                },\n                \"host_name\": {\n                    \"description\": \"Host name or pod name\",\n                    \"type\": \"string\"\n                },\n                \"iface\": {\n                    \"description\": \"Interface name\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.L3afDMapArg\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"args\": {\n                    \"description\": \"BPF map arguments\",\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"$ref\": \"#/definitions/models.KeyValue\"\n                    }\n                },\n                \"name\": {\n                    \"description\": \"BPF map name\",\n                    \"type\": \"string\"\n                }\n            }\n        },\n        \"models.L3afDNFArgs\": {\n            \"type\": \"object\",\n            \"additionalProperties\": true\n        },\n        \"models.L3afDNFMetricsMap\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"aggregator\": {\n                    \"description\": \"Aggregation function names\",\n                    \"type\": \"string\"\n                },\n                \"key\": {\n                    \"description\": \"Index of the bpf map\",\n                    \"type\": \"integer\"\n                },\n                \"name\": {\n                    \"description\": \"BPF map name\",\n                    \"type\": \"string\"\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "docs/swagger.md",
    "content": "## Swaggo setup\n\nOur first task is to install the libraries we are dependent on. Run the following commands from the commandline:\n```\ngo get -u github.com/swaggo/swag/cmd/swag\ngo get -u github.com/swaggo/http-swagger\ngo get -u github.com/alecthomas/template\n\n```\nThe first two commands install swag and http-swagger respectively:\n\n#### swag\n\nThis library converts Go annotations to Swagger 2.0 docs (swagger.json/swagger.yaml), which are later used by\nhttp-swagger to serve the Swagger UI.\n\n#### http-swagger\n\nThis library helps to serve the Swagger UI using the docs generated by swag.\n\nThe third command is to install template, a fork of Go’s text/template package. This dependency is required in the docs.go\nfile generated by swag, and we’ll see an error while running the application without it.\n\n## Generate Swagger documentation\n\n#### Adding annotations in code\n\nIf you have not added the annotations, follow these docs\n\n* https://www.soberkoder.com/swagger-go-api-swaggo/\n* https://github.com/swaggo/swag#declarative-comments-format\n\n#### Generating Swagger specs (swagger.json and swagger.yaml)\n\nFrom the root folder of `l3afd` run following command\n```\nswag init -d \"./\" -g \"apis/configwatch.go\"\n```\n"
  },
  {
    "path": "docs/swagger.yaml",
    "content": "basePath: /\ndefinitions:\n  models.BPFProgram:\n    properties:\n      admin_status:\n        description: Program admin status enabled or disabled\n        type: string\n      artifact:\n        description: Artifact file name\n        type: string\n      cfg_version:\n        description: Config version\n        type: integer\n      cmd_config:\n        description: Program config providing command\n        type: string\n      cmd_start:\n        description: Program start command\n        type: string\n      cmd_status:\n        description: Program status command\n        type: string\n      cmd_stop:\n        description: Program stop command\n        type: string\n      cmd_update:\n        description: Program update config command\n        type: string\n      config_args:\n        allOf:\n        - $ref: '#/definitions/models.L3afDNFArgs'\n        description: Map of arguments to config command\n      config_file_path:\n        description: Config file location\n        type: string\n      cpu:\n        description: User program cpu limits\n        type: integer\n      ebpf_package_repo_url:\n        description: Download url for Program\n        type: string\n      entry_function_name:\n        description: BPF entry function name to load\n        type: string\n      id:\n        description: Program id\n        type: integer\n      is_plugin:\n        description: User program is plugin or not\n        type: boolean\n      map_args:\n        description: Config BPF Map of arguments\n        items:\n          $ref: '#/definitions/models.L3afDMapArg'\n        type: array\n      map_name:\n        description: BPF map to store next program fd\n        type: string\n      memory:\n        description: User program memory limits\n        type: integer\n      monitor_maps:\n        description: Metrics BPF maps\n        items:\n          $ref: '#/definitions/models.L3afDNFMetricsMap'\n        type: array\n      name:\n        description: Name of the BPF program package\n        type: string\n      object_file:\n        description: Object file contains BPF code\n        type: string\n      prog_type:\n        description: Program type XDP or TC\n        type: string\n      rules:\n        description: Config rules\n        type: string\n      rules_file:\n        description: Config rules file name\n        type: string\n      seq_id:\n        description: Sequence position in the chain\n        type: integer\n      start_args:\n        allOf:\n        - $ref: '#/definitions/models.L3afDNFArgs'\n        description: Map of arguments to start command\n      status_args:\n        allOf:\n        - $ref: '#/definitions/models.L3afDNFArgs'\n        description: Map of arguments to status command\n      stop_args:\n        allOf:\n        - $ref: '#/definitions/models.L3afDNFArgs'\n        description: Map of arguments to stop command\n      update_args:\n        allOf:\n        - $ref: '#/definitions/models.L3afDNFArgs'\n        description: Map of arguments to update command\n      user_program_daemon:\n        description: User program daemon or not\n        type: boolean\n      version:\n        description: Program version\n        type: string\n    type: object\n  models.BPFProgramNames:\n    properties:\n      probes:\n        description: names of the probe eBPF programs\n        items:\n          type: string\n        type: array\n      tc_egress:\n        description: names of the TC egress eBPF programs\n        items:\n          type: string\n        type: array\n      tc_ingress:\n        description: names of the TC ingress eBPF programs\n        items:\n          type: string\n        type: array\n      xdp_ingress:\n        description: names of the XDP ingress eBPF programs\n        items:\n          type: string\n        type: array\n    type: object\n  models.BPFPrograms:\n    properties:\n      probes:\n        description: list of probe bpf programs\n        items:\n          $ref: '#/definitions/models.BPFProgram'\n        type: array\n      tc_egress:\n        description: list of tc egress bpf programs\n        items:\n          $ref: '#/definitions/models.BPFProgram'\n        type: array\n      tc_ingress:\n        description: list of tc ingress bpf programs\n        items:\n          $ref: '#/definitions/models.BPFProgram'\n        type: array\n      xdp_ingress:\n        description: list of xdp ingress bpf programs\n        items:\n          $ref: '#/definitions/models.BPFProgram'\n        type: array\n    type: object\n  models.KeyValue:\n    properties:\n      key:\n        description: Key\n        type: integer\n      value:\n        description: Value\n        type: integer\n    type: object\n  models.L3afBPFProgramNames:\n    properties:\n      bpf_programs:\n        allOf:\n        - $ref: '#/definitions/models.BPFProgramNames'\n        description: List of eBPF program names to remove\n      host_name:\n        description: Host name or pod name\n        type: string\n      iface:\n        description: Interface name\n        type: string\n    type: object\n  models.L3afBPFPrograms:\n    properties:\n      bpf_programs:\n        allOf:\n        - $ref: '#/definitions/models.BPFPrograms'\n        description: List of bpf programs\n      host_name:\n        description: Host name or pod name\n        type: string\n      iface:\n        description: Interface name\n        type: string\n    type: object\n  models.L3afDMapArg:\n    properties:\n      args:\n        description: BPF map arguments\n        items:\n          $ref: '#/definitions/models.KeyValue'\n        type: array\n      name:\n        description: BPF map name\n        type: string\n    type: object\n  models.L3afDNFArgs:\n    additionalProperties: true\n    type: object\n  models.L3afDNFMetricsMap:\n    properties:\n      aggregator:\n        description: Aggregation function names\n        type: string\n      key:\n        description: Index of the bpf map\n        type: integer\n      name:\n        description: BPF map name\n        type: string\n    type: object\ninfo:\n  contact: {}\n  description: Configuration APIs to deploy and get the details of the eBPF Programs\n    on the node\n  title: L3AFD APIs\n  version: \"1.0\"\npaths:\n  /l3af/configs/v1:\n    get:\n      consumes:\n      - application/json\n      description: Returns details of the configuration of eBPF Programs for all interfaces\n        on a node\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: OK\n      summary: Returns details of the configuration of eBPF Programs for all interfaces\n        on a node\n  /l3af/configs/v1/{iface}:\n    get:\n      consumes:\n      - application/json\n      description: Returns details of the configuration of eBPF Programs for a given\n        interface\n      parameters:\n      - description: interface name\n        in: path\n        name: iface\n        required: true\n        type: string\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: OK\n      summary: Returns details of the configuration of eBPF Programs for a given interface\n  /l3af/configs/v1/add:\n    post:\n      consumes:\n      - application/json\n      description: Adds new eBPF Programs on node\n      parameters:\n      - description: BPF programs\n        in: body\n        name: cfgs\n        required: true\n        schema:\n          items:\n            $ref: '#/definitions/models.L3afBPFPrograms'\n          type: array\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: OK\n      summary: Adds new eBPF Programs on node\n  /l3af/configs/v1/delete:\n    post:\n      consumes:\n      - application/json\n      description: Removes eBPF Programs on node\n      parameters:\n      - description: BPF program names\n        in: body\n        name: cfgs\n        required: true\n        schema:\n          items:\n            $ref: '#/definitions/models.L3afBPFProgramNames'\n          type: array\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: OK\n      summary: Removes eBPF Programs on node\n  /l3af/configs/v1/restart:\n    put:\n      consumes:\n      - application/json\n      description: Store meta data about ebpf programs and exit\n      parameters:\n      - description: BPF programs\n        in: body\n        name: cfgs\n        required: true\n        schema:\n          items:\n            $ref: '#/definitions/models.L3afBPFPrograms'\n          type: array\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: OK\n      summary: Store meta data about ebpf programs and exit\n  /l3af/configs/v1/update:\n    post:\n      consumes:\n      - application/json\n      description: Update eBPF Programs configuration\n      parameters:\n      - description: BPF programs\n        in: body\n        name: cfgs\n        required: true\n        schema:\n          items:\n            $ref: '#/definitions/models.L3afBPFPrograms'\n          type: array\n      produces:\n      - application/json\n      responses:\n        \"200\":\n          description: OK\n      summary: Update eBPF Programs configuration\nswagger: \"2.0\"\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/l3af-project/l3afd/v2\n\ngo 1.26.0\n\nrequire (\n\tgithub.com/cilium/ebpf v0.21.0\n\tgithub.com/go-chi/chi/v5 v5.2.5\n\tgithub.com/mitchellh/go-ps v1.0.0\n\tgithub.com/prometheus/client_golang v1.23.2\n\tgithub.com/robfig/config v0.0.0-20141207224736-0f78529c8c7e\n\tgithub.com/rs/zerolog v1.35.0\n\tgithub.com/safchain/ethtool v0.7.0\n\tgithub.com/swaggo/http-swagger v1.3.4\n\tgithub.com/swaggo/swag v1.16.4\n\tgolang.org/x/sys v0.43.0 // exclude\n)\n\nrequire (\n\tgithub.com/florianl/go-tc v0.4.8\n\tgo.uber.org/mock v0.6.0\n\tgopkg.in/natefinch/lumberjack.v2 v2.2.1\n)\n\nrequire (\n\tgithub.com/KyleBanks/depth v1.2.1 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.20.0 // indirect\n\tgithub.com/go-openapi/jsonreference v0.20.2 // indirect\n\tgithub.com/go-openapi/spec v0.20.9 // indirect\n\tgithub.com/go-openapi/swag v0.22.4 // indirect\n\tgithub.com/google/go-cmp v0.7.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/josharian/native v1.1.0 // indirect\n\tgithub.com/mailru/easyjson v0.7.7 // indirect\n\tgithub.com/mattn/go-colorable v0.1.14 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/mdlayher/netlink v1.7.2 // indirect\n\tgithub.com/mdlayher/socket v0.5.1 // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/prometheus/client_model v0.6.2 // indirect\n\tgithub.com/prometheus/common v0.66.1 // indirect\n\tgithub.com/prometheus/procfs v0.16.1 // indirect\n\tgithub.com/swaggo/files v1.0.1 // indirect\n\tgo.yaml.in/yaml/v2 v2.4.2 // indirect\n\tgolang.org/x/net v0.46.0 // indirect\n\tgolang.org/x/sync v0.17.0 // indirect\n\tgolang.org/x/tools v0.38.0 // indirect\n\tgoogle.golang.org/protobuf v1.36.8 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=\ngithub.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=\ngithub.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=\ngithub.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=\ngithub.com/cilium/ebpf v0.21.0 h1:4dpx1J/B/1apeTmWBH5BkVLayHTkFrMovVPnHEk+l3k=\ngithub.com/cilium/ebpf v0.21.0/go.mod h1:1kHKv6Kvh5a6TePP5vvvoMa1bclRyzUXELSs272fmIQ=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/florianl/go-tc v0.4.8 h1:hgmakUX1Nm0Ba1I0ZkbUl9CH6HbRwqSiwipnpmYp3Es=\ngithub.com/florianl/go-tc v0.4.8/go.mod h1:B8GeOEnmrbOnxZtaCvsYJcgIzzmM8c/AIhtfCZsDj3Q=\ngithub.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=\ngithub.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=\ngithub.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=\ngithub.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=\ngithub.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=\ngithub.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=\ngithub.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=\ngithub.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=\ngithub.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=\ngithub.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=\ngithub.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=\ngithub.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=\ngithub.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=\ngithub.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=\ngithub.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s=\ngithub.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=\ngithub.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786 h1:N527AHMa793TP5z5GNAn/VLPzlc0ewzWdeP/25gDfgQ=\ngithub.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs=\ngithub.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=\ngithub.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=\ngithub.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=\ngithub.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=\ngithub.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=\ngithub.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=\ngithub.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=\ngithub.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=\ngithub.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=\ngithub.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=\ngithub.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=\ngithub.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=\ngithub.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=\ngithub.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=\ngithub.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=\ngithub.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q=\ngithub.com/mdlayher/netlink v1.6.2/go.mod h1:O1HXX2sIWSMJ3Qn1BYZk1yZM+7iMki/uYGGiwGyq/iU=\ngithub.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=\ngithub.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=\ngithub.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc=\ngithub.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY=\ngithub.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=\ngithub.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=\ngithub.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=\ngithub.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=\ngithub.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=\ngithub.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=\ngithub.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=\ngithub.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=\ngithub.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=\ngithub.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=\ngithub.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=\ngithub.com/robfig/config v0.0.0-20141207224736-0f78529c8c7e h1:3/9k/etUfgykjM3Rx8X0echJzo7gNNeND/ubPkqYw1k=\ngithub.com/robfig/config v0.0.0-20141207224736-0f78529c8c7e/go.mod h1:Zerq1qYbCKtIIU9QgPydffGlpYfZ8KI/si49wuTLY/Q=\ngithub.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=\ngithub.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=\ngithub.com/rs/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI=\ngithub.com/rs/zerolog v1.35.0/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=\ngithub.com/safchain/ethtool v0.7.0 h1:rlJzfDetsVvT61uz8x1YIcFn12akMfuPulHtZjtb7Is=\ngithub.com/safchain/ethtool v0.7.0/go.mod h1:MenQKEjXdfkjD3mp2QdCk8B/hwvkrlOTm/FD4gTpFxQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=\ngithub.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=\ngithub.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww=\ngithub.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ=\ngithub.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=\ngithub.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=\ngo.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=\ngo.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=\ngo.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=\ngolang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=\ngolang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=\ngolang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=\ngolang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=\ngolang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=\ngolang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=\ngoogle.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=\ngopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "main.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\t\"encoding/gob\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"gopkg.in/natefinch/lumberjack.v2\"\n\n\t\"github.com/l3af-project/l3afd/v2/apis\"\n\t\"github.com/l3af-project/l3afd/v2/apis/handlers\"\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n\t\"github.com/l3af-project/l3afd/v2/config\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/l3af-project/l3afd/v2/pidfile\"\n\t\"github.com/l3af-project/l3afd/v2/restart\"\n\t\"github.com/l3af-project/l3afd/v2/stats\"\n\t\"github.com/l3af-project/l3afd/v2/utils\"\n\n\t\"github.com/rs/zerolog\"\n\t\"github.com/rs/zerolog/log\"\n)\n\nconst daemonName = \"l3afd\"\n\nvar stateSockPath string\n\nfunc setupLogging(conf *config.Config) {\n\t// ConsoleWriter formats the logs for user-readability\n\tif conf.JSONFormatLogs {\n\t\tlog.Logger = zerolog.New(os.Stderr).With().Timestamp().Logger()\n\t}\n\n\t// Set the default Log level\n\tzerolog.SetGlobalLevel(zerolog.InfoLevel)\n\tlog.Info().Msgf(\"Log level set to %q\", zerolog.InfoLevel)\n\n\tif logLevelStr := os.Getenv(\"L3AF_LOG_LEVEL\"); logLevelStr != \"\" {\n\t\tif logLevel, err := zerolog.ParseLevel(logLevelStr); err != nil {\n\t\t\tlog.Error().Err(err).Msg(\"Invalid environment-specified log level. Defaulting to INFO.\")\n\t\t} else {\n\t\t\tzerolog.SetGlobalLevel(logLevel)\n\t\t\tlog.Info().Msgf(\"Log level set to %q via environment variable\", logLevel)\n\t\t}\n\t}\n\n\tif conf.FileLogLocation != \"\" {\n\t\tlog.Info().Msgf(\"Saving logs to file: %s\", conf.FileLogLocation)\n\t\tsaveLogsToFile(conf)\n\t\treturn\n\t}\n\n}\n\nfunc saveLogsToFile(conf *config.Config) {\n\tlogFileWithRotation := &lumberjack.Logger{\n\t\tFilename:   conf.FileLogLocation,\n\t\tMaxSize:    conf.FileLogMaxSize,    // Max size in megabytes\n\t\tMaxBackups: conf.FileLogMaxBackups, // Max number of old log files to keep\n\t\tMaxAge:     conf.FileLogMaxAge,     // Max number of days to keep log files\n\t}\n\t// Create a multi-writer for stdout and the file\n\tmultiWriter := zerolog.MultiLevelWriter(os.Stdout, logFileWithRotation)\n\n\tif conf.JSONFormatLogs {\n\t\tlog.Logger = log.Output(zerolog.ConsoleWriter{\n\t\t\tOut: multiWriter})\n\n\t} else {\n\t\tlog.Logger = zerolog.New(multiWriter).With().Timestamp().Logger()\n\t}\n}\n\nfunc main() {\n\tmodels.CloseForRestart = make(chan struct{})\n\tmodels.IsReadOnly = false\n\tmodels.CurrentWriteReq = 0\n\tmodels.StateLock = sync.Mutex{}\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\t//Default Logger (uses user-friendly colored log statements in RFC3339Nano (e.g., 2006-01-02T15:04:05.999999999Z07:00) format)\n\tzerolog.TimeFieldFormat = time.RFC3339Nano\n\tlog.Logger = log.Output(zerolog.ConsoleWriter{\n\t\tOut: os.Stderr})\n\n\tlog.Info().Msgf(\"%s started.\", daemonName)\n\n\tvar confPath string\n\tflag.StringVar(&confPath, \"config\", \"config/l3afd.cfg\", \"config path\")\n\n\tflag.Parse()\n\tinitVersion()\n\tconf, err := config.ReadConfig(confPath)\n\tif err != nil {\n\t\tlog.Fatal().Err(err).Msgf(\"Unable to parse config %q\", confPath)\n\t}\n\n\t// Setup logging according to the configuration provided\n\tsetupLogging(conf)\n\tpopulateVersions(conf)\n\tif err = pidfile.CheckPIDConflict(conf.PIDFilename); err != nil {\n\t\tif err = setupForRestartOuter(ctx, conf); err != nil {\n\t\t\tlog.Warn().Msg(\"Doing Normal Startup\")\n\t\t} else {\n\t\t\tlog.Fatal().Err(err).Msgf(\"The PID file: %s, is in an unacceptable state\", conf.PIDFilename)\n\t\t}\n\t}\n\tif err = pidfile.CreatePID(conf.PIDFilename); err != nil {\n\t\tlog.Fatal().Err(err).Msgf(\"The PID file: %s, could not be created\", conf.PIDFilename)\n\t}\n\n\tif runtime.GOOS == \"linux\" {\n\t\tif err = checkKernelVersion(conf); err != nil {\n\t\t\tlog.Fatal().Err(err).Msg(\"The unsupported kernel version please upgrade\")\n\t\t}\n\t}\n\n\tif err = registerL3afD(conf); err != nil {\n\t\tlog.Error().Err(err).Msg(\"L3afd registration failed\")\n\t}\n\n\tebpfConfigs, err := SetupNFConfigs(ctx, conf)\n\tif err != nil {\n\t\tlog.Fatal().Err(err).Msg(\"L3afd failed to start\")\n\t}\n\n\tt, err := ReadConfigsFromConfigStore(conf)\n\tif err != nil {\n\t\tlog.Error().Err(err).Msg(\"L3afd failed to read configs from store\")\n\t}\n\n\tif t != nil {\n\t\tif err := ebpfConfigs.DeployeBPFPrograms(t); err != nil {\n\t\t\tlog.Error().Err(err).Msg(\"L3afd failed to deploy persistent configs from store\")\n\t\t}\n\t}\n\n\tif err := handlers.InitConfigs(ebpfConfigs); err != nil {\n\t\tlog.Fatal().Err(err).Msg(\"L3afd failed to initialise configs\")\n\t}\n\n\tif conf.EBPFChainDebugEnabled {\n\t\tbpfprogs.SetupBPFDebug(conf.EBPFChainDebugAddr, ebpfConfigs)\n\t}\n\t<-models.CloseForRestart\n\tos.Exit(0)\n}\n\nfunc SetupNFConfigs(ctx context.Context, conf *config.Config) (*bpfprogs.NFConfigs, error) {\n\t// Get Hostname\n\tmachineHostname, err := os.Hostname()\n\tif err != nil {\n\t\tlog.Error().Err(err).Msg(\"Could not get hostname from OS\")\n\t}\n\t// setup Metrics endpoint\n\tstats.SetupMetrics(machineHostname, daemonName, conf.MetricsAddr)\n\n\tpMon := bpfprogs.NewPCheck(conf.MaxEBPFReStartCount, conf.BpfChainingEnabled, conf.EBPFPollInterval)\n\tbpfM := bpfprogs.NewpBpfMetrics(conf.BpfChainingEnabled, conf.NMetricSamples)\n\tnfConfigs, err := bpfprogs.NewNFConfigs(ctx, machineHostname, conf, pMon, bpfM)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error in NewNFConfigs setup: %v\", err)\n\t}\n\n\tif err := apis.StartConfigWatcher(ctx, machineHostname, daemonName, conf, nfConfigs); err != nil {\n\t\treturn nil, fmt.Errorf(\"error in version announcer: %v\", err)\n\t}\n\treturn nfConfigs, nil\n}\n\nfunc checkKernelVersion(conf *config.Config) error {\n\tconst minVerLen = 2\n\n\tkernelVersion, err := utils.GetKernelVersion()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to find kernel version: %v\", err)\n\t}\n\n\t//validate version\n\tver := strings.Split(kernelVersion, \".\")\n\tif len(ver) < minVerLen {\n\t\treturn fmt.Errorf(\"expected minimum kernel version length %d and got %d, ver %+q\", minVerLen, len(ver), ver)\n\t}\n\tmajor_ver, err := strconv.Atoi(ver[0])\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to find kernel major version: %v\", err)\n\t}\n\tminor_ver, err := strconv.Atoi(ver[1])\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to find kernel minor version: %v\", err)\n\t}\n\n\tif major_ver > conf.MinKernelMajorVer {\n\t\treturn nil\n\t}\n\tif major_ver == conf.MinKernelMajorVer && minor_ver >= conf.MinKernelMinorVer {\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"expected Kernel version >=  %d.%d\", conf.MinKernelMajorVer, conf.MinKernelMinorVer)\n}\n\nfunc ReadConfigsFromConfigStore(conf *config.Config) ([]models.L3afBPFPrograms, error) {\n\n\t// check for persistent file\n\tif _, err := os.Stat(conf.L3afConfigStoreFileName); errors.Is(err, os.ErrNotExist) {\n\t\tlog.Warn().Msgf(\"no persistent config exists\")\n\t\treturn nil, nil\n\t}\n\n\tfile, err := os.OpenFile(conf.L3afConfigStoreFileName, os.O_RDONLY, os.ModePerm)\n\tdefer func() {\n\t\t_ = file.Close()\n\t}()\n\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to open persistent file (%s): %v\", conf.L3afConfigStoreFileName, err)\n\t}\n\n\tbyteValue, err := io.ReadAll(file)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read persistent file (%s): %v\", conf.L3afConfigStoreFileName, err)\n\t}\n\n\tvar t []models.L3afBPFPrograms\n\tif err = json.Unmarshal(byteValue, &t); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to unmarshal persistent config json: %v\", err)\n\t}\n\treturn t, nil\n}\n\n// setupForRestartOuter is a wrapper for setupForRestart, written for better error handling\nfunc setupForRestartOuter(ctx context.Context, conf *config.Config) error {\n\tif _, err := os.Stat(models.HostSock); os.IsNotExist(err) {\n\t\treturn err\n\t}\n\tstateSockPath = models.StateSock\n\tmodels.IsReadOnly = true\n\terr := setupForRestart(ctx, conf)\n\tif err != nil {\n\t\tsendState(\"Failed\")\n\t\tlog.Fatal().Err(err).Msg(\"unable to restart the l3afd\")\n\t}\n\tsendState(\"Ready\")\n\tmodels.IsReadOnly = false\n\t<-models.CloseForRestart\n\tos.Exit(0)\n\treturn nil\n}\n\n// setupForRestart will start the l3afd with state provided by other l3afd instance\nfunc setupForRestart(ctx context.Context, conf *config.Config) error {\n\tconn, err := net.Dial(\"unix\", models.HostSock)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to dial unix domain socket : %w\", err)\n\t}\n\tdecoder := gob.NewDecoder(conn)\n\tvar t models.L3AFALLHOSTDATA\n\terr = decoder.Decode(&t)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn fmt.Errorf(\"unable to decode\")\n\t}\n\tconn.Close()\n\tmachineHostname, err := os.Hostname()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to fetch the hostname\")\n\t}\n\n\tl, err := restart.GetNetListener(3, \"stat_server\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"getting stat_server listener failed\")\n\t}\n\tmodels.AllNetListeners.Store(\"stat_http\", l)\n\n\tl, err = restart.GetNetListener(4, \"main_server\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"getting main_server listener failed\")\n\t}\n\tmodels.AllNetListeners.Store(\"main_http\", l)\n\n\tif conf.EBPFChainDebugEnabled {\n\t\tl, err = restart.GetNetListener(5, \"debug_server\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"getting main_server listener failed\")\n\t\t}\n\t\tmodels.AllNetListeners.Store(\"debug_http\", l)\n\t}\n\t// setup Metrics endpoint\n\tstats.SetupMetrics(machineHostname, daemonName, conf.MetricsAddr)\n\trestart.SetMetrics(t)\n\tpMon := bpfprogs.NewPCheck(conf.MaxEBPFReStartCount, conf.BpfChainingEnabled, conf.EBPFPollInterval)\n\tbpfM := bpfprogs.NewpBpfMetrics(conf.BpfChainingEnabled, conf.NMetricSamples)\n\tlog.Info().Msgf(\"Restoring Previous State Graceful Restart\")\n\tebpfConfigs, err := restart.Convert(ctx, t, conf)\n\tebpfConfigs.BpfMetricsMon = bpfM\n\tebpfConfigs.ProcessMon = pMon\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to convert deserilaze the state\")\n\t}\n\terr = ebpfConfigs.StartAllUserProgramsAndProbes()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to start all the user programs and probes\")\n\t}\n\terr = apis.StartConfigWatcher(ctx, machineHostname, daemonName, conf, ebpfConfigs)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"starting config Watcher failed\")\n\t}\n\terr = handlers.InitConfigs(ebpfConfigs)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"l3afd failed to initialise configs\")\n\t}\n\tif conf.EBPFChainDebugEnabled {\n\t\tbpfprogs.SetupBPFDebug(conf.EBPFChainDebugAddr, ebpfConfigs)\n\t}\n\tebpfConfigs.ProcessMon.PCheckStart(ebpfConfigs.IngressXDPBpfs, ebpfConfigs.IngressTCBpfs, ebpfConfigs.EgressTCBpfs, &ebpfConfigs.ProbesBpfs, &ebpfConfigs.Ifaces)\n\tebpfConfigs.BpfMetricsMon.BpfMetricsStart(ebpfConfigs.IngressXDPBpfs, ebpfConfigs.IngressTCBpfs, ebpfConfigs.EgressTCBpfs, &ebpfConfigs.ProbesBpfs, &ebpfConfigs.Ifaces)\n\terr = pidfile.CreatePID(conf.PIDFilename)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"the PID file: %s, could not be created\", conf.PIDFilename)\n\t}\n\treturn nil\n}\n\nfunc sendState(s string) {\n\tln, err := net.Listen(\"unix\", stateSockPath)\n\tif err != nil {\n\t\tlog.Err(err)\n\t\tos.Exit(0)\n\t\treturn\n\t}\n\tconn, err := ln.Accept()\n\tif err != nil {\n\t\tlog.Err(err)\n\t\tln.Close()\n\t\tos.Exit(0)\n\t\treturn\n\t}\n\tencoder := gob.NewEncoder(conn)\n\terr = encoder.Encode(s)\n\tif err != nil {\n\t\tlog.Err(err)\n\t\tconn.Close()\n\t\tln.Close()\n\t\tos.Exit(0)\n\t\treturn\n\t}\n\tconn.Close()\n\tln.Close()\n}\n\n// populateVersions is to suppress codeql warning - Uncontrolled data used in network request\nfunc populateVersions(conf *config.Config) {\n\tmodels.AvailableVersions = make(map[string]string)\n\tfor i := 0; i <= conf.VersionLimit; i++ {\n\t\tfor j := 0; j <= conf.VersionLimit; j++ {\n\t\t\tfor k := 0; k <= conf.VersionLimit; k++ {\n\t\t\t\tversion := fmt.Sprintf(\"v%d.%d.%d\", i, j, k)\n\t\t\t\tmodels.AvailableVersions[version] = version\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "main_test.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/l3af-project/l3afd/v2/config\"\n)\n\nfunc TestTestConfigValid(t *testing.T) {\n\t// skip if file DNE\n\tf, err := os.Open(\"config/l3afd.cfg\")\n\tif err != nil {\n\t\tt.Skip(\"l3afd.cfg not found\")\n\t}\n\tf.Close()\n\tif _, err := config.ReadConfig(\"config/l3afd.cfg\"); err != nil {\n\t\tt.Errorf(\"Unable to read l3afd config: %s\", err)\n\t}\n}\n"
  },
  {
    "path": "mocks/mocked_interfaces.go",
    "content": "// Code generated by MockGen. DO NOT EDIT.\n// Source: mock_interfaces.go\n\n// Package mocks is a generated GoMock package.\npackage mocks\n\nimport (\n\treflect \"reflect\"\n\n\tgomock \"go.uber.org/mock/gomock\"\n)\n\n// MockplatformInterface is a mock of platformInterface interface.\ntype MockplatformInterface struct {\n\tctrl     *gomock.Controller\n\trecorder *MockplatformInterfaceMockRecorder\n}\n\n// MockplatformInterfaceMockRecorder is the mock recorder for MockplatformInterface.\ntype MockplatformInterfaceMockRecorder struct {\n\tmock *MockplatformInterface\n}\n\n// NewMockplatformInterface creates a new mock instance.\nfunc NewMockplatformInterface(ctrl *gomock.Controller) *MockplatformInterface {\n\tmock := &MockplatformInterface{ctrl: ctrl}\n\tmock.recorder = &MockplatformInterfaceMockRecorder{mock}\n\treturn mock\n}\n\n// EXPECT returns an object that allows the caller to indicate expected use.\nfunc (m *MockplatformInterface) EXPECT() *MockplatformInterfaceMockRecorder {\n\treturn m.recorder\n}\n\n// GetPlatform mocks base method.\nfunc (m *MockplatformInterface) GetPlatform() (string, error) {\n\tm.ctrl.T.Helper()\n\tret := m.ctrl.Call(m, \"GetPlatform\")\n\tret0, _ := ret[0].(string)\n\tret1, _ := ret[1].(error)\n\treturn ret0, ret1\n}\n\n// GetPlatform indicates an expected call of GetPlatform.\nfunc (mr *MockplatformInterfaceMockRecorder) GetPlatform() *gomock.Call {\n\tmr.mock.ctrl.T.Helper()\n\treturn mr.mock.ctrl.RecordCallWithMethodType(mr.mock, \"GetPlatform\", reflect.TypeOf((*MockplatformInterface)(nil).GetPlatform))\n}\n"
  },
  {
    "path": "models/l3afd.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage models\n\nimport (\n\t\"sync\"\n)\n\n// l3afd constants\nconst (\n\tEnabled  = \"enabled\"\n\tDisabled = \"disabled\"\n\n\tStartType  = \"start\"\n\tStopType   = \"stop\"\n\tUpdateType = \"update\"\n\n\tXDPType = \"xdp\"\n\tTCType  = \"tc\"\n\n\tIngressType    = \"ingress\"\n\tEgressType     = \"egress\"\n\tXDPIngressType = \"xdpingress\"\n\tTCMapPinPath   = \"tc/globals\"\n\n\tKProbe     = \"kprobe\"\n\tTracePoint = \"tracepoint\"\n\tKRetProbe  = \"kretprobe\"\n\tUProbe     = \"uprobe\"\n\tURetProbe  = \"uretprobe\"\n)\n\ntype L3afDNFArgs map[string]interface{}\n\n// BPFProgram defines BPF Program for specific host\ntype BPFProgram struct {\n\tID                int                 `json:\"id\"`                    // Program id\n\tName              string              `json:\"name\"`                  // Name of the BPF program package\n\tSeqID             int                 `json:\"seq_id\"`                // Sequence position in the chain\n\tArtifact          string              `json:\"artifact\"`              // Artifact file name\n\tMapName           string              `json:\"map_name\"`              // BPF map to store next program fd\n\tCmdStart          string              `json:\"cmd_start\"`             // Program start command\n\tCmdStop           string              `json:\"cmd_stop\"`              // Program stop command\n\tCmdStatus         string              `json:\"cmd_status\"`            // Program status command\n\tCmdConfig         string              `json:\"cmd_config\"`            // Program config providing command\n\tCmdUpdate         string              `json:\"cmd_update\"`            // Program update config command\n\tVersion           string              `json:\"version\"`               // Program version\n\tUserProgramDaemon bool                `json:\"user_program_daemon\"`   // User program daemon or not\n\tIsPlugin          bool                `json:\"is_plugin\"`             // User program is plugin or not\n\tCPU               int                 `json:\"cpu\"`                   // User program cpu limits\n\tMemory            int                 `json:\"memory\"`                // User program memory limits\n\tAdminStatus       string              `json:\"admin_status\"`          // Program admin status enabled or disabled\n\tProgType          string              `json:\"prog_type\"`             // Program type XDP or TC\n\tRulesFile         string              `json:\"rules_file\"`            // Config rules file name\n\tRules             string              `json:\"rules\"`                 // Config rules\n\tConfigFilePath    string              `json:\"config_file_path\"`      // Config file location\n\tCfgVersion        int                 `json:\"cfg_version\"`           // Config version\n\tStartArgs         L3afDNFArgs         `json:\"start_args\"`            // Map of arguments to start command\n\tStopArgs          L3afDNFArgs         `json:\"stop_args\"`             // Map of arguments to stop command\n\tStatusArgs        L3afDNFArgs         `json:\"status_args\"`           // Map of arguments to status command\n\tUpdateArgs        L3afDNFArgs         `json:\"update_args\"`           // Map of arguments to update command\n\tMapArgs           []L3afDMapArg       `json:\"map_args\"`              // Config BPF Map of arguments\n\tConfigArgs        L3afDNFArgs         `json:\"config_args\"`           // Map of arguments to config command\n\tMonitorMaps       []L3afDNFMetricsMap `json:\"monitor_maps\"`          // Metrics BPF maps\n\tEPRURL            string              `json:\"ebpf_package_repo_url\"` // Download url for Program\n\tObjectFile        string              `json:\"object_file\"`           // Object file contains BPF code\n\tEntryFunctionName string              `json:\"entry_function_name\"`   // BPF entry function name to load\n}\n\n// L3afDNFMetricsMap defines BPF map\ntype L3afDNFMetricsMap struct {\n\tName       string `json:\"name\"`       // BPF map name\n\tKey        int    `json:\"key\"`        // Index of the bpf map\n\tAggregator string `json:\"aggregator\"` // Aggregation function names\n}\n\n// KeyValue defines struct for key and value\ntype KeyValue struct {\n\tKey   int `json:\"key\"`   // Key\n\tValue int `json:\"value\"` // Value\n}\n\n// L3afDMapArg defines map arg\ntype L3afDMapArg struct {\n\tName string     `json:\"name\"` // BPF map name\n\tArgs []KeyValue `json:\"args\"` // BPF map arguments\n}\n\n// L3afBPFPrograms defines configs for a node\ntype L3afBPFPrograms struct {\n\tHostName    string       `json:\"host_name\"`    // Host name or pod name\n\tIface       string       `json:\"iface\"`        // Interface name\n\tIPv4Address string       `json:\"ipv4_address\"` // Ipv4 address of network interface\n\tBpfPrograms *BPFPrograms `json:\"bpf_programs\"` // List of bpf programs\n}\n\n// BPFPrograms for a node\ntype BPFPrograms struct {\n\tXDPIngress []*BPFProgram `json:\"xdp_ingress\"` // list of xdp ingress bpf programs\n\tTCIngress  []*BPFProgram `json:\"tc_ingress\"`  // list of tc ingress bpf programs\n\tTCEgress   []*BPFProgram `json:\"tc_egress\"`   // list of tc egress bpf programs\n\tProbes     []*BPFProgram `json:\"probes\"`      // list of probe bpf programs\n}\n\n// L3afBPFProgramNames defines names of Bpf programs on interface\ntype L3afBPFProgramNames struct {\n\tHostName        string           `json:\"host_name\"`    // Host name or pod name\n\tIface           string           `json:\"iface\"`        // Interface name\n\tIPv4Address     string           `json:\"ipv4_address\"` // Ipv4 address of network interface\n\tBpfProgramNames *BPFProgramNames `json:\"bpf_programs\"` // List of eBPF program names to remove\n}\n\n// BPFProgramNames defines names of eBPF programs on node\ntype BPFProgramNames struct {\n\tXDPIngress []string `json:\"xdp_ingress\"` // names of the XDP ingress eBPF programs\n\tTCIngress  []string `json:\"tc_ingress\"`  // names of the TC ingress eBPF programs\n\tTCEgress   []string `json:\"tc_egress\"`   // names of the TC egress eBPF programs\n\tProbes     []string `json:\"probes\"`      // names of the probe eBPF programs\n}\n\ntype MetaColl struct {\n\tPrograms []string\n\tMaps     []string\n}\n\ntype MetaMetricsBPFMap struct {\n\tMapName    string\n\tKey        int\n\tValues     []float64\n\tAggregator string\n\tLastValue  float64\n}\n\ntype Label struct {\n\tName  string\n\tValue string\n}\n\ntype MetricVec struct {\n\tMetricName string\n\tLabels     []Label\n\tValue      float64\n\tType       int32\n}\n\ntype L3AFMetaData struct {\n\tProgram           BPFProgram\n\tFilePath          string\n\tRestartCount      int\n\tPrevMapNamePath   string\n\tMapNamePath       string\n\tProgID            uint32\n\tBpfMaps           []string\n\tMetricsBpfMaps    map[string]MetaMetricsBPFMap\n\tProgMapCollection MetaColl\n\tProgMapID         uint32\n\tPrevProgMapID     uint32\n\tLink              bool\n}\n\ntype L3AFALLHOSTDATA struct {\n\tHostName       string\n\tHostInterfaces map[string]bool\n\tIngressXDPBpfs map[string][]*L3AFMetaData\n\tIngressTCBpfs  map[string][]*L3AFMetaData\n\tEgressTCBpfs   map[string][]*L3AFMetaData\n\tProbesBpfs     []L3AFMetaData\n\tIfaces         map[string]string\n\tAllStats       []MetricVec\n}\n\ntype RestartConfig struct {\n\tHostName string `json:\"hostname\"`\n\tVersion  string `json:\"version\"`\n}\n\nvar CloseForRestart chan struct{}\nvar AllNetListeners sync.Map\nvar CurrentWriteReq int\nvar StateLock sync.Mutex\nvar IsReadOnly bool\nvar AvailableVersions map[string]string\n\nconst HttpScheme string = \"http\"\nconst HttpsScheme string = \"https\"\nconst FileScheme string = \"file\"\nconst StatusFailed string = \"Failed\"\nconst StatusReady string = \"Ready\"\n\n// Please Do not make changes in socketpaths because they are means of communication between graceful restarts\nconst HostSock string = \"/tmp/l3afd.sock\"\nconst StateSock string = \"/tmp/l3afstate.sock\"\nconst L3AFDRestartArtifactName string = \"l3afd.tar.gz\"\n"
  },
  {
    "path": "pidfile/pidfile.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage pidfile\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/signal\"\n\t\"strconv\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/rs/zerolog/log\"\n)\n\nfunc CheckPIDConflict(pidFilename string) error {\n\tlog.Info().Msgf(\"Checking for another already running instance (using PID file \\\"%s\\\")...\", pidFilename)\n\tpidFileContent, err := os.ReadFile(pidFilename)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\tlog.Info().Msgf(\"OK, no PID file already exists at %s.\", pidFilename)\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"could not open PID file: %s, please manually remove; error: %v\", pidFilename, err)\n\t}\n\tif len(pidFileContent) < 1 {\n\t\tlog.Warn().Msgf(\"PID file already exists at %s, but it is empty... ignoring.\", pidFilename)\n\t\treturn nil\n\t}\n\toldPIDString := string(pidFileContent)\n\toldPID, err := strconv.Atoi(oldPIDString)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"PID file: %s, contained value: %s, which could not be parsed; error: %v\", pidFilename, oldPIDString, err)\n\t}\n\n\tlog.Info().Msgf(\"Found PID file with PID: %d; checking if it is this process: PID: %d\", oldPID, os.Getpid())\n\tif oldPID == os.Getpid() {\n\t\tlog.Warn().Msgf(\"PID file already exists at %s, but it contains the current PID(%d)... ignoring.\", pidFilename, oldPID)\n\t\treturn nil\n\t}\n\n\tlog.Info().Msgf(\"Found PID file with PID: %s; checking if process is running...\", oldPIDString)\n\tprocess, err := os.FindProcess(oldPID)\n\tif err == nil {\n\t\t//On Linux, if sig is 0, then no signal is sent, but error checking is still performed;\n\t\t//this can be used to check for the existence of a process ID or process  group ID.\n\t\t//See: man 2 kill\n\t\terr = process.Signal(syscall.Signal(0))\n\t}\n\tif err != nil {\n\t\tlog.Info().Msgf(\"Process was not running, removing PID file.\")\n\t\tif err = RemovePID(pidFilename); err != nil {\n\t\t\treturn fmt.Errorf(\"removal failed, please manually remove; err: %v\", err)\n\t\t}\n\t\treturn nil\n\t}\n\n\tlog.Info().Msgf(\"Process with PID: %s; is running. Comparing process names to ensure it is a true conflict.\", oldPIDString)\n\tselfProcName, err := os.ReadFile(\"/proc/self/comm\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not read this processes command name from the proc filesystem; err: %v\", err)\n\t}\n\tconflictProcName, err := os.ReadFile(fmt.Sprintf(\"/proc/%d/comm\", oldPID))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not read old processes (PID: %s) command name from the proc filesystem; error: %v\", oldPIDString, err)\n\t}\n\tif string(selfProcName) != string(conflictProcName) {\n\t\tlog.Info().Msgf(\"Old process had command name: %q, not %q, removing PID file.\", conflictProcName, selfProcName)\n\t\tif err = RemovePID(pidFilename); err != nil {\n\t\t\treturn fmt.Errorf(\"removal failed, please manually remove; error: %v\", err)\n\t\t}\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"a previous instance of this process (%s) is running with ID %s; please shutdown this process before running\", selfProcName, oldPIDString)\n}\n\nfunc CreatePID(pidFilename string) error {\n\tPID := os.Getpid()\n\tlog.Info().Msgf(\"Writing process ID %d to %s...\", PID, pidFilename)\n\tif err := os.WriteFile(pidFilename, []byte(strconv.Itoa(PID)), 0640); err != nil {\n\t\treturn fmt.Errorf(\"could not write process ID to file: \\\"%s\\\"; error: %v\", pidFilename, err)\n\t}\n\treturn nil\n}\n\nfunc RemovePID(pidFilename string) error {\n\terr := os.RemoveAll(pidFilename)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"could not remove PID file: %s; error: %v\", pidFilename, err)\n\t}\n\treturn err\n}\n\nfunc SetupGracefulShutdown(shutdownHandler func() error, shutdownHandlerTimeout time.Duration, pidFilename string) {\n\tconst defaultShutdownTO = time.Second * 10\n\tif shutdownHandlerTimeout < 1 {\n\t\tlog.Warn().Msgf(\"GracefulShutdown: No shutdown timeout was provided! Using %s.\", defaultShutdownTO)\n\t\tshutdownHandlerTimeout = defaultShutdownTO\n\t}\n\n\t//We must use a buffered channel or risk missing the signal if we're not\n\t//ready to receive when the signal is sent.\n\tinterruptCh := make(chan os.Signal, 1)\n\tsignal.Notify(interruptCh, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT)\n\n\t//Start worker that listens for shutdown signal and handles it\n\tgo func() {\n\t\tinterrupt := <-interruptCh\n\t\texitCode := 0\n\n\t\tif shutdownHandler != nil { //Run shutdown handler\n\t\t\tlog.Info().Msgf(\"GracefulShutdown: Received shutdown signal: %s, waiting for shutdown handler to execute (will timeout after %s)...\", interrupt, shutdownHandlerTimeout)\n\t\t\thandlerDoneCh := make(chan struct{})\n\t\t\tgo func() {\n\t\t\t\tif err := shutdownHandler(); err != nil {\n\t\t\t\t\tlog.Error().Err(err).Msgf(\"GracefulShutdown: Shutdown handler returned error\")\n\t\t\t\t\texitCode = 1\n\t\t\t\t}\n\t\t\t\thandlerDoneCh <- struct{}{}\n\t\t\t}()\n\t\t\tselect {\n\t\t\tcase <-handlerDoneCh:\n\t\t\t\tlog.Info().Msgf(\"GracefulShutdown: Shutdown handler execution complete. Shutting down...\")\n\t\t\tcase <-time.After(shutdownHandlerTimeout):\n\t\t\t\tlog.Error().Msgf(\"GracefulShutdown: Shutdown handler execution timed-out after %s! Shutting down...\", shutdownHandlerTimeout)\n\t\t\t\texitCode = 1\n\t\t\t}\n\t\t} else {\n\t\t\tlog.Info().Msgf(\"GracefulShutdown: Received shutdown signal: %s, shutting down...\", interrupt)\n\t\t}\n\n\t\tif pidFilename != \"\" {\n\t\t\tif err := RemovePID(pidFilename); err != nil {\n\t\t\t\tlog.Warn().Err(err).Msgf(\"Could not cleanup PID file\")\n\t\t\t}\n\t\t}\n\t\tlog.Info().Msgf(\"Shutdown now.\")\n\t\tos.Exit(exitCode)\n\t}()\n}\n"
  },
  {
    "path": "register_internal.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !admind\n// +build !admind\n\n// This file is used for walmart internal to register with management server.\n// We will be removing this file in future.\n\npackage main\n\nimport (\n\t\"github.com/l3af-project/l3afd/v2/config\"\n\n\t\"github.com/rs/zerolog/log\"\n)\n\nfunc registerL3afD(conf *config.Config) error {\n\tlog.Warn().Msg(\"Implement custom registration with management server\")\n\treturn nil\n}\n"
  },
  {
    "path": "restart/restart.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package restart provides primitives for gracefully restarting l3afd\npackage restart\n\nimport (\n\t\"bytes\"\n\t\"container/list\"\n\t\"container/ring\"\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/l3af-project/l3afd/v2/bpfprogs\"\n\t\"github.com/l3af-project/l3afd/v2/config\"\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/l3af-project/l3afd/v2/stats\"\n\t\"github.com/l3af-project/l3afd/v2/utils\"\n\n\t\"github.com/cilium/ebpf\"\n\t\"github.com/cilium/ebpf/link\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/rs/zerolog/log\"\n)\n\n// convertBPFMap will  populate bpf maps from deserialized map names\nfunc convertBPFMap(in []string, g *bpfprogs.BPF, output *map[string]bpfprogs.BPFMap, iface string) error {\n\tversion := utils.ReplaceDotsWithUnderscores(g.Program.Version)\n\tfor _, v := range in {\n\t\tvar pinnedPath string\n\t\tif g.Program.ProgType == models.XDPType {\n\t\t\tpinnedPath = filepath.Join(g.HostConfig.BpfMapDefaultPath, iface, g.Program.Name, version, v)\n\t\t} else {\n\t\t\tpinnedPath = filepath.Join(g.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, iface, g.Program.Name, version, v)\n\t\t}\n\t\tm, err := ebpf.LoadPinnedMap(pinnedPath, nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tinfo, err := m.Info()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tid, _ := info.ID()\n\t\t(*output)[v] = bpfprogs.BPFMap{\n\t\t\tName:    v,\n\t\t\tMapID:   id,\n\t\t\tType:    info.Type,\n\t\t\tBPFProg: g,\n\t\t}\n\t\tm.Close()\n\t}\n\treturn nil\n}\n\n// getCollection will populate ebpf Collection from deserialized meta collection object\nfunc getCollection(input models.MetaColl, output **ebpf.Collection, b *bpfprogs.BPF, iface string) error {\n\tversion := utils.ReplaceDotsWithUnderscores(b.Program.Version)\n\tfor _, v := range input.Programs {\n\t\tprogPinPath := utils.ProgPinPath(b.HostConfig.BpfMapDefaultPath, iface, b.Program.Name, version, b.Program.EntryFunctionName, b.Program.ProgType)\n\t\tprog, err := ebpf.LoadPinnedProgram(progPinPath, nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t(*output).Programs[v] = prog\n\t}\n\tfor _, v := range input.Maps {\n\t\tvar mapPinPath string\n\t\tif b.Program.ProgType == models.TCType {\n\t\t\tmapPinPath = filepath.Join(b.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, iface, b.Program.Name, version, v)\n\t\t} else if b.Program.ProgType == models.XDPType {\n\t\t\tmapPinPath = filepath.Join(b.HostConfig.BpfMapDefaultPath, iface, b.Program.Name, version, v)\n\t\t}\n\t\tm, err := ebpf.LoadPinnedMap(mapPinPath, nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t(*output).Maps[v] = m\n\t}\n\treturn nil\n}\n\n// getMetricsMaps will populate MetricsBPFMap from deserialized meta metric object\nfunc getMetricsMaps(input map[string]models.MetaMetricsBPFMap, b *bpfprogs.BPF, conf *config.Config, output *map[string]*bpfprogs.MetricsBPFMap, iface string) error {\n\tif input == nil {\n\t\treturn nil\n\t}\n\tversion := utils.ReplaceDotsWithUnderscores(b.Program.Version)\n\tfor k, v := range input {\n\t\tfg := &bpfprogs.MetricsBPFMap{}\n\t\tvar pinnedPath string\n\t\tif b.Program.ProgType == models.XDPType {\n\t\t\tpinnedPath = filepath.Join(b.HostConfig.BpfMapDefaultPath, iface, b.Program.Name, version, v.MapName)\n\t\t} else {\n\t\t\tpinnedPath = filepath.Join(b.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, iface, b.Program.Name, version, v.MapName)\n\t\t}\n\t\tm, err := ebpf.LoadPinnedMap(pinnedPath, nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tinfo, err := m.Info()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tid, _ := info.ID()\n\t\tfg.BPFMap = bpfprogs.BPFMap{\n\t\t\tName:    v.MapName,\n\t\t\tMapID:   id,\n\t\t\tType:    info.Type,\n\t\t\tBPFProg: b,\n\t\t}\n\t\tfg.Values = ring.New(conf.NMetricSamples)\n\t\tfor _, a := range v.Values {\n\t\t\tfg.Values.Value = a\n\t\t\tfg.Values = fg.Values.Next()\n\t\t}\n\t\tfg.Aggregator = v.Aggregator\n\t\tfg.Key = v.Key\n\t\tfg.LastValue = v.LastValue\n\t\t(*output)[k] = fg\n\t\tm.Close()\n\t}\n\treturn nil\n}\n\n// deserializeProgram will deserialize individual program from given *models.L3AFMetaData\nfunc deserializeProgram(ctx context.Context, r *models.L3AFMetaData, hostconfig *config.Config, iface, direction string) (*bpfprogs.BPF, error) {\n\tg := &bpfprogs.BPF{}\n\tg.Program = r.Program\n\tg.FilePath = r.FilePath\n\tg.RestartCount = r.RestartCount\n\tg.MapNamePath = r.MapNamePath\n\tg.PrevMapNamePath = r.PrevMapNamePath\n\tg.PrevProgMapID = ebpf.MapID(r.PrevProgMapID)\n\tg.ProgMapID = ebpf.MapID(r.ProgMapID)\n\tg.ProgID = ebpf.ProgramID(r.ProgID)\n\tg.Ctx = ctx\n\tg.HostConfig = hostconfig\n\tg.Done = nil\n\tg.BpfMaps = make(map[string]bpfprogs.BPFMap)\n\tif err := convertBPFMap(r.BpfMaps, g, &g.BpfMaps, iface); err != nil {\n\t\treturn nil, err\n\t}\n\tg.MetricsBpfMaps = make(map[string]*bpfprogs.MetricsBPFMap)\n\tif err := getMetricsMaps(r.MetricsBpfMaps, g, hostconfig, &g.MetricsBpfMaps, iface); err != nil {\n\t\treturn nil, fmt.Errorf(\"metrics maps conversion failed\")\n\t}\n\tg.ProgMapCollection = &ebpf.Collection{\n\t\tPrograms: make(map[string]*ebpf.Program),\n\t\tMaps:     make(map[string]*ebpf.Map),\n\t}\n\n\tif r.Link {\n\t\tversion := utils.ReplaceDotsWithUnderscores(g.Program.Version)\n\t\tvar linkPinPath string\n\t\tif g.Program.ProgType == models.XDPType {\n\t\t\tlinkPinPath = utils.LinkPinPath(hostconfig.BpfMapDefaultPath, iface, g.Program.Name, version, g.Program.ProgType)\n\t\t} else {\n\t\t\tlinkPinPath = utils.TCLinkPinPath(hostconfig.BpfMapDefaultPath, iface, g.Program.Name, version, g.Program.ProgType, direction)\n\t\t}\n\t\tvar err error\n\t\tg.Link, err = link.LoadPinnedLink(linkPinPath, nil)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif err := getCollection(r.ProgMapCollection, &g.ProgMapCollection, g, iface); err != nil {\n\t\treturn nil, err\n\t}\n\treturn g, nil\n}\n\n// GetValueofLabel will query label value from given label array\nfunc getValueofLabel(l string, t []models.Label) string {\n\tfor _, f := range t {\n\t\tif f.Name == l {\n\t\t\treturn f.Value\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// GetGaugeVecByMetricName will provide CounterVec metrics pointer from metric name\nfunc getCountVecByMetricName(name string) *prometheus.CounterVec {\n\tswitch name {\n\tcase \"l3afd_BPFUpdateCount\":\n\t\treturn stats.BPFUpdateCount\n\tcase \"l3afd_BPFStartCount\":\n\t\treturn stats.BPFStartCount\n\tcase \"l3afd_BPFStopCount\":\n\t\treturn stats.BPFStopCount\n\tcase \"l3afd_BPFUpdateFailedCount\":\n\t\treturn stats.BPFUpdateFailedCount\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// GetGaugeVecByMetricName will provide GaugeVec metrics pointer from metric name\nfunc getGaugeVecByMetricName(name string) *prometheus.GaugeVec {\n\tswitch name {\n\tcase \"l3afd_BPFRunning\":\n\t\treturn stats.BPFRunning\n\tcase \"l3afd_BPFStartTime\":\n\t\treturn stats.BPFStartTime\n\tcase \"l3afd_BPFMonitorMap\":\n\t\treturn stats.BPFMonitorMap\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// Convert will produce *bpfprogs.NFConfigs from deserilzed l3afd state\nfunc Convert(ctx context.Context, t models.L3AFALLHOSTDATA, hostconfig *config.Config) (*bpfprogs.NFConfigs, error) {\n\tD := &bpfprogs.NFConfigs{}\n\tD.Ctx = ctx\n\tD.HostName = t.HostName\n\tD.HostInterfaces = t.HostInterfaces\n\tD.Ifaces = t.Ifaces\n\tD.IngressXDPBpfs = make(map[string]*list.List)\n\tD.IngressTCBpfs = make(map[string]*list.List)\n\tD.EgressTCBpfs = make(map[string]*list.List)\n\tD.ProbesBpfs = *list.New()\n\tD.HostConfig = hostconfig\n\tD.Mu = new(sync.Mutex)\n\tif t.IngressXDPBpfs != nil {\n\t\tfor k, v := range t.IngressXDPBpfs {\n\t\t\tl := list.New()\n\t\t\tfor _, r := range v {\n\t\t\t\tf, err := deserializeProgram(ctx, r, hostconfig, k, models.XDPIngressType)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Err(err).Msg(\"Deserialization failed for xdp ingress programs\")\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tl.PushBack(f)\n\t\t\t}\n\t\t\tD.IngressXDPBpfs[k] = l\n\t\t}\n\t}\n\tif t.IngressTCBpfs != nil {\n\t\tfor k, v := range t.IngressTCBpfs {\n\t\t\tl := list.New()\n\t\t\tfor _, r := range v {\n\t\t\t\tf, err := deserializeProgram(ctx, r, hostconfig, k, models.IngressType)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Err(err).Msg(\"Deserialization failed for tc ingress programs\")\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tl.PushBack(f)\n\t\t\t}\n\t\t\tD.IngressTCBpfs[k] = l\n\t\t}\n\t}\n\tif t.EgressTCBpfs != nil {\n\t\tfor k, v := range t.EgressTCBpfs {\n\t\t\tl := list.New()\n\t\t\tfor _, r := range v {\n\t\t\t\tf, err := deserializeProgram(ctx, r, hostconfig, k, models.EgressType)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Err(err).Msg(\"Deserialization failed for tc egress programs\")\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tl.PushBack(f)\n\t\t\t}\n\t\t\tD.EgressTCBpfs[k] = l\n\t\t}\n\t}\n\treturn D, nil\n}\n\n// SetMetrics will populate all stats from models.L3AFALLHOSTDATA\nfunc SetMetrics(t models.L3AFALLHOSTDATA) {\n\tfor _, f := range t.AllStats {\n\t\tif f.Type == 0 {\n\t\t\tstats.Add(f.Value, getCountVecByMetricName(f.MetricName), getValueofLabel(\"ebpf_program\", f.Labels),\n\t\t\t\tgetValueofLabel(\"direction\", f.Labels), getValueofLabel(\"interface_name\", f.Labels), getValueofLabel(\"ipv4_address\", f.Labels))\n\t\t} else {\n\t\t\tif len(getValueofLabel(\"version\", f.Labels)) > 0 {\n\t\t\t\tstats.SetWithVersion(f.Value, getGaugeVecByMetricName(f.MetricName), getValueofLabel(\"ebpf_program\", f.Labels),\n\t\t\t\t\tgetValueofLabel(\"version\", f.Labels), getValueofLabel(\"direction\", f.Labels), getValueofLabel(\"interface_name\", f.Labels), getValueofLabel(\"ipv4_address\", f.Labels))\n\t\t\t} else if len(getValueofLabel(\"map_name\", f.Labels)) > 0 {\n\t\t\t\tstats.SetValue(f.Value, getGaugeVecByMetricName(f.MetricName), getValueofLabel(\"ebpf_program\", f.Labels),\n\t\t\t\t\tgetValueofLabel(\"map_name\", f.Labels), getValueofLabel(\"interface_name\", f.Labels), getValueofLabel(\"ipv4_address\", f.Labels))\n\t\t\t} else {\n\t\t\t\tstats.Set(f.Value, getGaugeVecByMetricName(f.MetricName), getValueofLabel(\"ebpf_program\", f.Labels),\n\t\t\t\t\tgetValueofLabel(\"direction\", f.Labels), getValueofLabel(\"interface_name\", f.Labels), getValueofLabel(\"ipv4_address\", f.Labels))\n\t\t\t}\n\t\t}\n\t}\n}\n\n// GetNetListener will get tcp listener from provided file descriptor\nfunc GetNetListener(fd int, fname string) (*net.TCPListener, error) {\n\tfile := os.NewFile(uintptr(fd), \"DupFd\"+fname)\n\tl, err := net.FileListener(file)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlf, e := l.(*net.TCPListener)\n\tif !e {\n\t\treturn nil, fmt.Errorf(\"unable to convert to tcp listener\")\n\t}\n\tfile.Close()\n\treturn lf, nil\n}\n\n// AddSymlink to add symlink\nfunc AddSymlink(sPath, symlink string) error {\n\terr := os.Symlink(sPath, symlink)\n\treturn err\n}\n\n// RemoveSymlink to remove symlink\nfunc RemoveSymlink(symlink string) error {\n\terr := os.Remove(symlink)\n\treturn err\n}\n\n// ReadSymlink to read symlink\nfunc ReadSymlink(symlink string) (string, error) {\n\toriginalPath, err := os.Readlink(symlink)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn originalPath, nil\n}\n\n// GetNewVersion will download new version make it ready to execute\nfunc GetNewVersion(artifactName, oldVersion, newVersion string, conf *config.Config) error {\n\tif oldVersion == newVersion {\n\t\treturn nil\n\t}\n\n\tnewVersionPath := filepath.Clean(filepath.Join(conf.BasePath, newVersion))\n\terr := os.RemoveAll(newVersionPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error while deleting directory: %w\", err)\n\t}\n\terr = os.MkdirAll(newVersionPath, 0750)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error while creating directory: %w\", err)\n\t}\n\n\t// now I need to download artifacts\n\tbuf := &bytes.Buffer{}\n\turlpath, err := url.JoinPath(conf.RestartArtifactURL, newVersion, artifactName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error while joining artifact path %w\", err)\n\t}\n\terr = bpfprogs.DownloadArtifact(urlpath, conf.HttpClientTimeout, buf)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to download artifacts %w\", err)\n\t}\n\terr = bpfprogs.ExtractArtifact(artifactName, buf, newVersionPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to extract artifacts %w\", err)\n\t}\n\n\t// removing symlinks for old version\n\terr = RemoveSymlink(filepath.Join(conf.BasePath, \"latest/l3afd\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to remove symlink %w\", err)\n\t}\n\terr = RemoveSymlink(filepath.Join(conf.BasePath, \"latest/l3afd.cfg\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to remove symlink %w\", err)\n\t}\n\n\t// adding new version symlinks\n\terr = AddSymlink(filepath.Join(newVersionPath, \"l3afd\", \"l3afd\"), filepath.Join(conf.BasePath, \"latest/l3afd\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to add symlink %w\", err)\n\t}\n\n\terr = AddSymlink(filepath.Join(newVersionPath, \"l3afd\", \"l3afd.cfg\"), filepath.Join(conf.BasePath, \"latest/l3afd.cfg\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to add symlink %w\", err)\n\t}\n\treturn nil\n}\n\n// RollBackSymlink will rollback binary & cfgfile symlinks to old version\nfunc RollBackSymlink(oldCfgPath, oldBinPath string, oldVersion, newVersion string, conf *config.Config) error {\n\tif oldVersion == newVersion {\n\t\treturn nil\n\t}\n\n\terr := RemoveSymlink(filepath.Join(conf.BasePath, \"latest/l3afd\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to remove symlink %w\", err)\n\t}\n\terr = RemoveSymlink(filepath.Join(conf.BasePath, \"latest/l3afd.cfg\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to remove symlink %w\", err)\n\t}\n\n\t// add new symlink\n\terr = AddSymlink(oldBinPath, filepath.Join(conf.BasePath, \"latest/l3afd\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to add symlink %w\", err)\n\t}\n\n\terr = AddSymlink(oldCfgPath, filepath.Join(conf.BasePath, \"latest/l3afd.cfg\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to add symlink %w\", err)\n\t}\n\n\tnewVersionPath := filepath.Join(conf.BasePath, newVersion)\n\tif strings.Contains(newVersionPath, \"..\") {\n\t\treturn fmt.Errorf(\"malicious path\")\n\t}\n\terr = os.RemoveAll(newVersionPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error while deleting directory: %w\", err)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "restart/restart_test.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage restart\n\nimport (\n\t\"testing\"\n\n\t\"github.com/l3af-project/l3afd/v2/models\"\n)\n\nfunc TestGetValueofLabel(t *testing.T) {\n\tb := []models.Label{\n\t\t{\n\t\t\tName:  \"iface\",\n\t\t\tValue: \"fakeif0\",\n\t\t},\n\t}\n\tq := \"iface\"\n\tans := \"fakeif0\"\n\tt.Run(\"goodtest\", func(t *testing.T) {\n\t\tif ans != getValueofLabel(q, b) {\n\t\t\tt.Errorf(\"GetValueofLabel failed\")\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "routes/route.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage routes\n\nimport \"net/http\"\n\n// Route defines a valid endpoint with the type of action supported on it\ntype Route struct {\n\tMethod      string\n\tPath        string\n\tHandlerFunc http.HandlerFunc\n}\n"
  },
  {
    "path": "routes/router.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage routes\n\nimport (\n\tchi \"github.com/go-chi/chi/v5\"\n\t\"github.com/rs/zerolog/log\"\n)\n\n// NewRouter returns a router handle loaded with all the supported routes\nfunc NewRouter(routes []Route) *chi.Mux {\n\tr := chi.NewRouter()\n\n\tfor _, route := range routes {\n\t\tr.Method(route.Method, route.Path, route.HandlerFunc)\n\t\tlog.Info().Msgf(\"Route added:%+v\\n\", route)\n\t}\n\n\treturn r\n}\n"
  },
  {
    "path": "signals/signal_unix.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !WINDOWS\n// +build !WINDOWS\n\npackage signals\n\nimport (\n\t\"os\"\n\t\"syscall\"\n)\n\nvar ShutdownSignals = []os.Signal{os.Interrupt,\n\tsyscall.SIGHUP,\n\tsyscall.SIGINT,\n\tsyscall.SIGTERM,\n\tsyscall.SIGQUIT}\n"
  },
  {
    "path": "signals/signal_windows.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build WINDOWS\n// +build WINDOWS\n\npackage signals\n\nimport (\n\t\"os\"\n)\n\nvar ShutdownSignals = []os.Signal{os.Interrupt}\n"
  },
  {
    "path": "stats/metrics.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage stats\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"net/http\"\n\n\t\"github.com/l3af-project/l3afd/v2/models\"\n\t\"github.com/prometheus/client_golang/prometheus\"\n\t\"github.com/prometheus/client_golang/prometheus/promauto\"\n\t\"github.com/prometheus/client_golang/prometheus/promhttp\"\n\t\"github.com/rs/zerolog/log\"\n)\n\nvar (\n\tBPFStartCount        *prometheus.CounterVec\n\tBPFStopCount         *prometheus.CounterVec\n\tBPFUpdateCount       *prometheus.CounterVec\n\tBPFUpdateFailedCount *prometheus.CounterVec\n\tBPFRunning           *prometheus.GaugeVec\n\tBPFStartTime         *prometheus.GaugeVec\n\tBPFMonitorMap        *prometheus.GaugeVec\n\tBPFDeployFailedCount *prometheus.CounterVec\n)\n\nfunc SetupMetrics(hostname, daemonName, metricsAddr string) {\n\n\tbpfStartCountVec := promauto.NewCounterVec(\n\t\tprometheus.CounterOpts{\n\t\t\tNamespace: daemonName,\n\t\t\tName:      \"BPFStartCount\",\n\t\t\tHelp:      \"The count of BPF program started\",\n\t\t},\n\t\t[]string{\"host\", \"ebpf_program\", \"direction\", \"interface_name\", \"ipv4_address\"},\n\t)\n\n\tBPFStartCount = bpfStartCountVec.MustCurryWith(prometheus.Labels{\"host\": hostname})\n\n\tbpfStopCountVec := promauto.NewCounterVec(\n\t\tprometheus.CounterOpts{\n\t\t\tNamespace: daemonName,\n\t\t\tName:      \"BPFStopCount\",\n\t\t\tHelp:      \"The count of BPF program stopped\",\n\t\t},\n\t\t[]string{\"host\", \"ebpf_program\", \"direction\", \"interface_name\", \"ipv4_address\"},\n\t)\n\n\tBPFStopCount = bpfStopCountVec.MustCurryWith(prometheus.Labels{\"host\": hostname})\n\n\tbpfUpdateCountVec := promauto.NewCounterVec(\n\t\tprometheus.CounterOpts{\n\t\t\tNamespace: daemonName,\n\t\t\tName:      \"BPFUpdateCount\",\n\t\t\tHelp:      \"The count of BPF programs updated\",\n\t\t},\n\t\t[]string{\"host\", \"ebpf_program\", \"direction\", \"interface_name\", \"ipv4_address\"},\n\t)\n\n\tBPFUpdateCount = bpfUpdateCountVec.MustCurryWith(prometheus.Labels{\"host\": hostname})\n\n\tbpfUpdateFailedCountVec := promauto.NewCounterVec(\n\t\tprometheus.CounterOpts{\n\t\t\tNamespace: daemonName,\n\t\t\tName:      \"BPFUpdateFailedCount\",\n\t\t\tHelp:      \"The count of Failed BPF program update args\",\n\t\t},\n\t\t[]string{\"host\", \"bpf_program\", \"direction\", \"interface_name\", \"ipv4_address\"},\n\t)\n\n\tBPFUpdateFailedCount = bpfUpdateFailedCountVec.MustCurryWith(prometheus.Labels{\"host\": hostname})\n\n\tbpfRunningVec := prometheus.NewGaugeVec(\n\t\tprometheus.GaugeOpts{\n\t\t\tNamespace: daemonName,\n\t\t\tName:      \"BPFRunning\",\n\t\t\tHelp:      \"This value indicates BPF program is running or not\",\n\t\t},\n\t\t[]string{\"host\", \"ebpf_program\", \"version\", \"direction\", \"interface_name\", \"ipv4_address\"},\n\t)\n\n\tif err := prometheus.Register(bpfRunningVec); err != nil {\n\t\tlog.Warn().Err(err).Msg(\"Failed to register BPFRunning metrics\")\n\t}\n\n\tBPFRunning = bpfRunningVec.MustCurryWith(prometheus.Labels{\"host\": hostname})\n\n\tbpfStartTimeVec := prometheus.NewGaugeVec(\n\t\tprometheus.GaugeOpts{\n\t\t\tNamespace: daemonName,\n\t\t\tName:      \"BPFStartTime\",\n\t\t\tHelp:      \"This value indicates start time of the BPF program since unix epoch in seconds\",\n\t\t},\n\t\t[]string{\"host\", \"ebpf_program\", \"direction\", \"interface_name\", \"ipv4_address\"},\n\t)\n\n\tif err := prometheus.Register(bpfStartTimeVec); err != nil {\n\t\tlog.Warn().Err(err).Msg(\"Failed to register BPFStartTime metrics\")\n\t}\n\n\tBPFStartTime = bpfStartTimeVec.MustCurryWith(prometheus.Labels{\"host\": hostname})\n\n\tbpfMonitorMapVec := prometheus.NewGaugeVec(\n\t\tprometheus.GaugeOpts{\n\t\t\tNamespace: daemonName,\n\t\t\tName:      \"BPFMonitorMap\",\n\t\t\tHelp:      \"This value indicates BPF program monitor counters\",\n\t\t},\n\t\t[]string{\"host\", \"ebpf_program\", \"map_name\", \"interface_name\", \"ipv4_address\"},\n\t)\n\n\tif err := prometheus.Register(bpfMonitorMapVec); err != nil {\n\t\tlog.Warn().Err(err).Msg(\"Failed to register BPFMonitorMap metrics\")\n\t}\n\n\tBPFMonitorMap = bpfMonitorMapVec.MustCurryWith(prometheus.Labels{\"host\": hostname})\n\n\tBPFDeployFailedCountVec := promauto.NewCounterVec(\n\t\tprometheus.CounterOpts{\n\t\t\tNamespace: daemonName,\n\t\t\tName:      \"BPFDeployFailedCount\",\n\t\t\tHelp:      \"The count of BPF program failed to start or update\",\n\t\t},\n\t\t[]string{\"host\", \"ebpf_program\", \"direction\", \"interface_name\", \"ipv4_address\"},\n\t)\n\tBPFDeployFailedCount = BPFDeployFailedCountVec.MustCurryWith(prometheus.Labels{\"host\": hostname})\n\n\tBPFStartCount = bpfStartCountVec.MustCurryWith(prometheus.Labels{\"host\": hostname})\n\t// Prometheus handler\n\tmetricsHandler := promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{})\n\t// Adding web endpoint\n\tgo func() {\n\t\t// Expose the registered metrics via HTTP.\n\t\tif _, ok := models.AllNetListeners.Load(\"stat_http\"); !ok {\n\t\t\ttcpAddr, err := net.ResolveTCPAddr(\"tcp\", metricsAddr)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal().Err(err).Msg(\"Error resolving TCP address\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlistener, err := net.ListenTCP(\"tcp\", tcpAddr)\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatal().Err(err).Msgf(\"unable to create net Listen\")\n\t\t\t}\n\t\t\tmodels.AllNetListeners.Store(\"stat_http\", listener)\n\t\t}\n\t\thttp.Handle(\"/metrics\", metricsHandler)\n\t\tval, _ := models.AllNetListeners.Load(\"stat_http\")\n\t\tl, _ := val.(*net.TCPListener)\n\t\tif err := http.Serve(l, nil); !errors.Is(err, http.ErrServerClosed) {\n\t\t\tlog.Fatal().Err(err).Msgf(\"Failed to launch prometheus metrics endpoint\")\n\t\t}\n\t}()\n}\n\nfunc Add(value float64, counterVec *prometheus.CounterVec, ebpfProgram, direction, ifaceName string, ipv4Address string) {\n\n\tif counterVec == nil {\n\t\tlog.Warn().Msg(\"Metrics: counter vector is nil and needs to be initialized before Incr\")\n\t\treturn\n\t}\n\tbpfCounter, err := counterVec.GetMetricWith(\n\t\tprometheus.Labels(map[string]string{\n\t\t\t\"ebpf_program\":   ebpfProgram,\n\t\t\t\"direction\":      direction,\n\t\t\t\"interface_name\": ifaceName,\n\t\t\t\"ipv4_address\":   ipv4Address,\n\t\t}),\n\t)\n\tif err != nil {\n\t\tlog.Warn().Msgf(\"Metrics: unable to fetch counter with fields: ebpf_program: %s, direction: %s, interface_name: %s\",\n\t\t\tebpfProgram, direction, ifaceName)\n\t\treturn\n\t}\n\tbpfCounter.Add(value)\n}\n\nfunc Set(value float64, gaugeVec *prometheus.GaugeVec, ebpfProgram, direction, ifaceName string, ipv4Address string) {\n\n\tif gaugeVec == nil {\n\t\tlog.Warn().Msg(\"Metrics: gauge vector is nil and needs to be initialized before Set\")\n\t\treturn\n\t}\n\tbpfGauge, err := gaugeVec.GetMetricWith(\n\t\tprometheus.Labels(map[string]string{\n\t\t\t\"ebpf_program\":   ebpfProgram,\n\t\t\t\"direction\":      direction,\n\t\t\t\"interface_name\": ifaceName,\n\t\t\t\"ipv4_address\":   ipv4Address,\n\t\t}),\n\t)\n\tif err != nil {\n\t\tlog.Warn().Msgf(\"Metrics: unable to fetch gauge with fields: ebpf_program: %s, direction: %s, interface_name: %s\",\n\t\t\tebpfProgram, direction, ifaceName)\n\t\treturn\n\t}\n\tbpfGauge.Set(value)\n}\n\n// Set gaugevec metrics value with given mapName and other fields\nfunc SetValue(value float64, gaugeVec *prometheus.GaugeVec, ebpfProgram, mapName, ifaceName string, ipv4Address string) {\n\n\tif gaugeVec == nil {\n\t\tlog.Warn().Msg(\"Metrics: gauge vector is nil and needs to be initialized before SetValue\")\n\t\treturn\n\t}\n\tbpfGauge, err := gaugeVec.GetMetricWith(\n\t\tprometheus.Labels(map[string]string{\n\t\t\t\"ebpf_program\":   ebpfProgram,\n\t\t\t\"map_name\":       mapName,\n\t\t\t\"interface_name\": ifaceName,\n\t\t\t\"ipv4_address\":   ipv4Address,\n\t\t}),\n\t)\n\tif err != nil {\n\t\tlog.Warn().Msgf(\"Metrics: unable to fetch gauge with fields: ebpf_program: %s, map_name: %s, interface_name: %s\",\n\t\t\tebpfProgram, mapName, ifaceName)\n\t\treturn\n\t}\n\tbpfGauge.Set(value)\n}\n\nfunc SetWithVersion(value float64, gaugeVec *prometheus.GaugeVec, ebpfProgram, version, direction, ifaceName string, ipv4Address string) {\n\n\tif gaugeVec == nil {\n\t\tlog.Warn().Msg(\"Metrics: gauge vector is nil and needs to be initialized before Set\")\n\t\treturn\n\t}\n\tbpfGauge, err := gaugeVec.GetMetricWith(\n\t\tprometheus.Labels(map[string]string{\n\t\t\t\"ebpf_program\":   ebpfProgram,\n\t\t\t\"version\":        version,\n\t\t\t\"direction\":      direction,\n\t\t\t\"interface_name\": ifaceName,\n\t\t\t\"ipv4_address\":   ipv4Address,\n\t\t}),\n\t)\n\tif err != nil {\n\t\tlog.Warn().Msgf(\"Metrics: unable to fetch gauge with fields: ebpf_program: %s, version: %s, direction: %s, interface_name: %s\",\n\t\t\tebpfProgram, version, direction, ifaceName)\n\t\treturn\n\t}\n\tbpfGauge.Set(value)\n}\n"
  },
  {
    "path": "testdata/Test_l3af-config.json",
    "content": "null\n"
  },
  {
    "path": "utils/utils.go",
    "content": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package utils provides helper functions for l3afd\npackage utils\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// GetKernelVersion - reads the kernel version from /proc/version\nfunc GetKernelVersion() (string, error) {\n\tosVersion, err := os.ReadFile(\"/proc/version\")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to read procfs: %v\", err)\n\t}\n\tvar u1, u2, kernelVersion string\n\t_, err = fmt.Sscanf(string(osVersion), \"%s %s %s\", &u1, &u2, &kernelVersion)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to scan procfs version: %v\", err)\n\t}\n\n\treturn kernelVersion, nil\n}\n\n// CheckTCXSupport - verifies kernel version is 6.8 or above\nfunc CheckTCXSupport() bool {\n\tconst minVerLen = 2\n\tconst minMajorKernelVer = 6\n\tconst minMinorKernelVer = 8\n\n\tkernelVersion, err := GetKernelVersion()\n\tif err != nil {\n\t\treturn false\n\t}\n\n\t// validate version\n\tver := strings.Split(kernelVersion, \".\")\n\tif len(ver) < minVerLen {\n\t\treturn false\n\t}\n\tmajorVer, err := strconv.Atoi(ver[0])\n\tif err != nil {\n\t\treturn false\n\t}\n\tminorVer, err := strconv.Atoi(ver[1])\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tif majorVer > minMajorKernelVer {\n\t\treturn true\n\t}\n\tif majorVer == minMajorKernelVer && minorVer >= minMinorKernelVer {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// ReplaceDotsWithUnderscores replaces all dots in the given string with underscores.\nfunc ReplaceDotsWithUnderscores(version string) string {\n\treturn strings.ReplaceAll(version, \".\", \"_\")\n}\n\n// LinkPinPath builds the formatted link pin path string.\nfunc LinkPinPath(bpfMapDefaultPath, ifaceName, programName, version, progType string) string {\n\treturn filepath.Join(bpfMapDefaultPath, \"links\", ifaceName, programName, version, fmt.Sprintf(\"%s_%s\", programName, progType))\n}\n\n// TCLinkPinPath builds the formatted link pin path string.\nfunc TCLinkPinPath(bpfMapDefaultPath, ifaceName, programName, version, progType, direction string) string {\n\treturn filepath.Join(bpfMapDefaultPath, \"links\", ifaceName, programName, version, fmt.Sprintf(\"%s_%s_%s\", programName, progType, direction))\n}\n\n// ProgPinPath builds the formatted program pin path string.\nfunc ProgPinPath(bpfMapDefaultPath, ifaceName, programName, version, entryFunctionName, progType string) string {\n\treturn filepath.Join(bpfMapDefaultPath, \"progs\", ifaceName, programName, version, fmt.Sprintf(\"%s_%s\", entryFunctionName, progType))\n}\n"
  },
  {
    "path": "version.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\t\"time\"\n)\n\nvar (\n\t// Version binary version number\n\tVersion string = \"2.1.0\"\n\t// SuffixTag binary tag (e.g. beta, dev, devel, production).\n\tSuffixTag string\n\t// VersionDate build timestamp (e.g. ).\n\tVersionDate string\n\t// VersionSHA git commit SHA.\n\tVersionSHA string\n\n\tversionFlag = flag.Bool(\"version\", false, \"display version information\")\n)\n\n// Init prints version information and exits, if --version option passed.\nfunc initVersion() {\n\tif *versionFlag {\n\t\tfmt.Println(VersionInfo())\n\t\tos.Exit(0)\n\t}\n}\n\n// VersionInfo returns a formatted string of version data used in the version flag.\nfunc VersionInfo() string {\n\tvar sha, buildDate string\n\tif btime, err := time.Parse(\"20060102150405\", VersionDate); err == nil {\n\t\tbuildDate = \"built \" + btime.Format(time.RFC3339)\n\t} else {\n\t\tcurrentFilePath, err := os.Executable()\n\t\tif err == nil {\n\t\t\tinfo, err := os.Stat(currentFilePath)\n\t\t\tif err == nil {\n\t\t\t\tbuildDate = info.ModTime().Format(time.RFC3339)\n\t\t\t}\n\t\t}\n\t}\n\n\tif VersionSHA != \"\" {\n\t\tsha = \"\\nBuild SHA: \" + VersionSHA\n\t}\n\n\treturn fmt.Sprintf(\"Version: %s\\nGo Version: %s\\nBuild Date: %s%s\", ShortVersion(), runtime.Version(), buildDate, sha)\n}\n\n// ShortVersion returns a formatted string containing only the Version and VersionTag\nfunc ShortVersion() string {\n\tif Version == \"0.0.0\" {\n\t\tSuffixTag = \"dev\"\n\t}\n\tif SuffixTag != \"\" {\n\t\treturn fmt.Sprintf(\"%s-%s\", Version, SuffixTag)\n\t}\n\treturn Version\n}\n"
  }
]