Full Code of l3af-project/l3afd for AI

main 4ce859684188 cached
80 files
472.1 KB
138.2k tokens
351 symbols
1 requests
Download .txt
Showing preview only (497K chars total). Download the full file or copy to clipboard to get everything.
Repository: l3af-project/l3afd
Branch: main
Commit: 4ce859684188
Files: 80
Total size: 472.1 KB

Directory structure:
gitextract_j3_x9dxv/

├── .github/
│   ├── dependabot.yml
│   ├── release.yml
│   └── workflows/
│       ├── ci-build-windows.yaml
│       ├── ci-build.yaml
│       ├── ci-e2e.yaml
│       ├── codeql.yaml
│       └── scorecards-analysis.yml
├── .gitignore
├── CMakeLists.txt
├── CODEOWNERS
├── LICENSE
├── Makefile
├── README.md
├── apis/
│   ├── configwatch.go
│   ├── configwatch_test.go
│   ├── handlers/
│   │   ├── addprog.go
│   │   ├── addprog_test.go
│   │   ├── deleteprog.go
│   │   ├── deleteprog_test.go
│   │   ├── getconfig.go
│   │   ├── getconfig_test.go
│   │   ├── restart_linux.go
│   │   ├── restart_linux_test.go
│   │   ├── restart_windows.go
│   │   ├── restart_windows_test.go
│   │   ├── updateconfig.go
│   │   └── updateconfig_test.go
│   └── routes.go
├── bpfprogs/
│   ├── bpf.go
│   ├── bpfCfgs_internal.go
│   ├── bpf_test.go
│   ├── bpf_test_unix.go
│   ├── bpf_test_windows.go
│   ├── bpf_unix.go
│   ├── bpf_windows.go
│   ├── bpfdebug.go
│   ├── bpfmap.go
│   ├── bpfmap_test.go
│   ├── bpfmetrics.go
│   ├── bpfmetrics_test.go
│   ├── nfconfig.go
│   ├── nfconfig_test.go
│   ├── probes.go
│   ├── probes_test.go
│   ├── processCheck.go
│   └── processCheck_test.go
├── build-docker/
│   ├── Dockerfile
│   └── start.sh
├── config/
│   ├── config.go
│   ├── config_loader.go
│   └── l3afd.cfg
├── config.yml
├── docs/
│   ├── CONTRIBUTING.md
│   ├── api/
│   │   └── README.md
│   ├── configdoc.md
│   ├── docs.go
│   ├── graceful-restart-guide.md
│   ├── prod-deploy-guide.md
│   ├── swagger.json
│   ├── swagger.md
│   └── swagger.yaml
├── go.mod
├── go.sum
├── main.go
├── main_test.go
├── mocks/
│   └── mocked_interfaces.go
├── models/
│   └── l3afd.go
├── pidfile/
│   └── pidfile.go
├── register_internal.go
├── restart/
│   ├── restart.go
│   └── restart_test.go
├── routes/
│   ├── route.go
│   └── router.go
├── signals/
│   ├── signal_unix.go
│   └── signal_windows.go
├── stats/
│   └── metrics.go
├── testdata/
│   ├── Test_l3af-config.json
│   └── l3afd.cdb
├── utils/
│   └── utils.go
└── version.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/dependabot.yml
================================================
# Copyright Contributors to the L3AF Project.
# SPDX-License-Identifier: Apache-2.0
#
# For documentation on the format of this file, see
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:

  - package-ecosystem: "github-actions"
    # Workflow files are stored in the default location of `.github/workflows`
    directory: "/"
    schedule:
      interval: "weekly"
      day: "saturday"

  - package-ecosystem: "gomod"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "saturday"


================================================
FILE: .github/release.yml
================================================
# .github/release.yml
---
changelog:
  exclude:
    labels:
      - ignore-for-release
  categories:
    - title: Breaking Changes 💥
      labels:
        - breaking-change
        - breaking
    - title: New Features 🎉
      labels:
        - feat
        - enhancement
    - title: Bug Fixes 🐛
      labels:
        - fix
        - bugfix
        - bug
    - title: Other Changes
      labels:
        - "*"


================================================
FILE: .github/workflows/ci-build-windows.yaml
================================================
# Copyright Contributors to the L3AF Project.
# SPDX-License-Identifier: Apache-2.0
#
# For documentation on the github environment, see
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners
#
# For documentation on the syntax of this file, see
# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions
name: CI Windows build
on:
  pull_request: {}
  push:
    branches:
      - main

permissions:
  contents: read

jobs:
  build:
    runs-on: windows-latest

    steps:
      - name: Setup Go 1.26.0
        uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
        with:
          go-version: '1.26.0'

      - name: Harden Runner
        uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df 
        with:
          egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs

      - name: Set up git env
        run: |
          git config --global core.autocrlf false
          $gopath = (go env GOPATH)
          echo "GOPATH=$gopath" >> $env:GITHUB_ENV

      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd 

      - name: Format
        run: |
          go install golang.org/x/tools/cmd/goimports@latest
          $goimp = (Join-path -Path (go env GOPATH) -ChildPath "\bin\goimports")
          $res = (&$goimp -l .) -replace "$_"      
          if ($res -ne "") {
            echo "Unformatted source code:" 
            echo $res
            exit 1
          }

      - name: Vet
        run: |
          go vet -tags WINDOWS ./...

      - name: Test
        run: |
          go test -tags WINDOWS ./...

      - uses: dominikh/staticcheck-action@9716614d4101e79b4340dd97b10e54d68234e431 
        with:
          version: "2025.1.1"
          install-go: false
          cache-key: "1.24.x"
          build-tags: WINDOWS

      - name: Build
        env:
          GOPATH: ${{env.GOPATH}}
        run: |
          cmake -B build
          cmake --build build


================================================
FILE: .github/workflows/ci-build.yaml
================================================
# Copyright Contributors to the L3AF Project.
# SPDX-License-Identifier: Apache-2.0
#
# For documentation on the github environment, see
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners
#
# For documentation on the syntax of this file, see
# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions

name: CI Ubuntu build
on:
  pull_request: {}
  push:
    branches:
      - main

permissions:
  contents: read

jobs:
  build:
    strategy:
      matrix:
        os:
          - ubuntu-24.04
          - ubuntu-22.04
    runs-on: ${{ matrix.os }}
    steps:
      - name: Setup Go 1.26.0
        uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
        with:
          go-version: '1.26.0'

      - name: Harden Runner
        uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df
        with:
          egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs

      - name: Set up environment
        run: |
          sudo apt-get update
          sudo apt-get remove -y containerd.io docker docker.io moby-engine moby-cli || true  # Remove any existing Docker-related packages
          sudo apt-get install -y \
            apt-transport-https \
            ca-certificates \
            curl \
            software-properties-common
          # docker src 
          curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
          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
          sudo apt-get update
          sudo apt-get install -y docker-ce docker-ce-cli containerd.io
          sudo apt-get install -y gcc libc-dev bash perl curl make

      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd 

      - name: Format
        run: |
          go install golang.org/x/tools/cmd/goimports@latest
          res="$(goimports -l .)"
          if [[ "$(printf '%s' "$res")" != '' ]]; then
            echo "Unformatted source code:"
            echo "$res"
            exit 1
          fi

      - name: Vet
        run: |
          go vet ./...

      - name: Test
        run: |
          go test ./...
          go clean -modcache

      - uses: dominikh/staticcheck-action@9716614d4101e79b4340dd97b10e54d68234e431
        with:
          version: "2025.1.1"
          install-go: false
          cache-key: "1.24.x"

      - name: Build
        run: |
          make

      - name: Copy files
        if: github.ref == 'refs/heads/main'
        run: |
          sudo cp ./config/l3afd.cfg ./build-docker
          sudo cp l3afd ./build-docker

      - name: login to docker registry
        if: github.ref == 'refs/heads/main'
        uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121
        with:
          username: ${{secrets.DOCKER_USERNAME}}
          password: ${{secrets.DOCKER_TOKEN}}

      - name: build and push docker image to registry
        if: github.ref == 'refs/heads/main'
        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f
        with:
          context: ./build-docker
          push: true
          tags: linuxfoundationl3af/l3afd:latest

      - name: upload l3afd binary  
        if: github.ref == 'refs/heads/main'
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
        with:
          name: l3afd-latest-linux-x86_64-${{ matrix.os }}
          path: l3afd



================================================
FILE: .github/workflows/ci-e2e.yaml
================================================
# Copyright Contributors to the L3AF Project.
# SPDX-License-Identifier: Apache-2.0
#
# For documentation on the github environment, see
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners
#
# For documentation on the syntax of this file, see
# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions

name: CI E2E build
on:
  pull_request: {}
  push:
    branches:
      - main

permissions:
  contents: read

jobs:
  build:
    strategy:
      matrix:
        os:
          - ubuntu-24.04
          - ubuntu-22.04
    runs-on: ${{ matrix.os }}
    steps:
      - name: Update and firewall stop
        run: |
          sudo apt update
          sudo systemctl stop ufw
          sudo apt install -y iproute2
          sudo apt install git curl hey
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
      
      - name: Prep
        run: |
          sudo cp -r /home/runner/work/l3afd/l3afd /root
          sudo git clone https://github.com/l3af-project/l3af-arch.git /root/l3af-arch
          sudo bash /root/l3af-arch/dev_environment/e2e_test/prep_env.sh
          sudo bash /root/l3af-arch/dev_environment/setup_linux_dev_env.sh --ci-build
          hm=$(hostname)
          sudo find /root/l3af-arch/dev_environment/e2e_test -type f -name "*.json" -exec sed -i "s/l3af-test-host/$hm/g" {} +
          
      - name: Run Tests
        run: |
         sudo bash /root/l3af-arch/dev_environment/e2e_test/test_suite.sh


================================================
FILE: .github/workflows/codeql.yaml
================================================
# Copyright Contributors to the L3AF Project.
# SPDX-License-Identifier: Apache-2.0
#
# For documentation on the github environment, see
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners
#
# For documentation on the syntax of this file, see
# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions

name: "CodeQL"

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

# Declare default permissions as read only.
permissions: read-all

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'go' ]

    steps:
    - name: Harden Runner
      uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df
      with:
        egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs

    - name: Checkout repository
      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd

    - name: Initialize CodeQL
      uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225
      with:
        languages: ${{ matrix.language }}

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225


================================================
FILE: .github/workflows/scorecards-analysis.yml
================================================
# Copyright Contributors to the L3AF Project.
# SPDX-License-Identifier: Apache-2.0
#
# For documentation on the github environment, see
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners
#
# For documentation on the syntax of this file, see
# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions

name: Scorecards

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

concurrency:
  # Cancel any Scorecards workflow currently in progress for the same PR.
  # Allow running concurrently with any other commits.
  group: scorecards-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true

# Declare default permissions as read only.
permissions: read-all

jobs:
  analysis:
    name: Scorecards analysis
    runs-on: ubuntu-latest
    permissions:
      # Needed to upload the results to code-scanning dashboard.
      security-events: write
      id-token: write
      actions: read
      contents: read

    steps:
      - name: "Checkout code"
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
        with:
          persist-credentials: false

      - name: "Run analysis"
        uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
        with:
          results_file: results.sarif
          results_format: sarif
          # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
          # - you want to enable the Branch-Protection check on a *public* repository, or
          # - you are installing Scorecard on a *private* repository
          # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
          # repo_token: ${{ secrets.SCORECARD_TOKEN }}

          # Public repositories:
          #   - Publish results to OpenSSF REST API for easy access by consumers
          #   - Allows the repository to include the Scorecard badge.
          #   - See https://github.com/ossf/scorecard-action#publishing-results.
          # For private repositories:
          #   - `publish_results` will always be set to `false`, regardless
          #     of the value entered here.
          publish_results: ${{ github.event_name != 'pull_request' }}


      # Upload the results as artifacts (optional).
      - name: "Upload artifact"
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
        with:
          name: SARIF file
          path: results.sarif
          retention-days: 5

      # Upload the results to GitHub's code scanning dashboard so it will be visible
      # at https://github.com/l3af-project/l3afd/security/code-scanning.
      - name: "Upload to code-scanning"
        uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225
        with:
          sarif_file: results.sarif


================================================
FILE: .gitignore
================================================
.idea/
vendor/


================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.10)
project(l3afd)

add_custom_target(swagger ALL
                  DEPENDS ${CMAKE_SOURCE_DIR}/docs/docs.go
                          ${CMAKE_SOURCE_DIR}/docs/swagger.json
                          ${CMAKE_SOURCE_DIR}/docs/swagger.yaml)

add_custom_command(OUTPUT $ENV{GOPATH}/bin/swag.exe
                   COMMAND go install github.com/swaggo/swag/cmd/swag@latest
                   COMMAND go get -u github.com/swaggo/http-swagger
                   COMMAND go get -u github.com/alecthomas/template)

add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/docs/docs.go
                          ${CMAKE_SOURCE_DIR}/docs/swagger.json
                          ${CMAKE_SOURCE_DIR}/docs/swagger.yaml
                   DEPENDS ${CMAKE_SOURCE_DIR}/apis/configwatch.go
                           $ENV{GOPATH}/bin/swag.exe
                   WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
                   COMMAND "$ENV{GOPATH}/bin/swag.exe" init -d "./" -g "apis/configwatch.go")

add_custom_target(build ALL
                  DEPENDS ${CMAKE_SOURCE_DIR}/l3afd.exe)

if (${WIN32})
  add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/l3afd.exe
                     DEPENDS ${CMAKE_SOURCE_DIR}/main.go
                     WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
                     COMMAND go build -tags WINDOWS .)
else ()
  add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/l3afd.exe
                     DEPENDS ${CMAKE_SOURCE_DIR}/main.go
                     WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
                     COMMAND go build .)
endif ()


================================================
FILE: CODEOWNERS
================================================
# Default Code Owners

* @sanfern @charleskbliu0 @jniesz @dalalkaran @pmoroney


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: Makefile
================================================
.PHONY: all

export GOPATH := $(HOME)/go
all: swagger build

swagger:
	@mkdir $(GOPATH) || true 
	@go install github.com/swaggo/swag/cmd/swag@latest
	@$(GOPATH)/bin/swag init -d "./" -g "apis/configwatch.go"

build:
	@CGO_ENABLED=0 go build -ldflags \
		"-X main.Version=v2.1.0 \
		 -X main.VersionSHA=`git rev-parse HEAD`"
install: swagger
	@go mod tidy
	@CGO_ENABLED=0 go install -ldflags \
		"-X main.Version=v2.1.0 \
		 -X main.VersionSHA=`git rev-parse HEAD`"
cibuild: swagger
	@go mod tidy
	@CGO_ENABLED=0 go install -cover -ldflags \
		"-X main.Version=v2.1.0 \
		 -X main.VersionSHA=`git rev-parse HEAD`"


================================================
FILE: README.md
================================================
# L3AFD: Lightweight eBPF Daemon
![L3AF_Logo](https://github.com/l3af-project/l3af-arch/blob/main/images/logos/Color/L3AF_logo.svg)

[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/6075/badge)](https://bestpractices.coreinfrastructure.org/projects/6075)
[![Go Report Card](https://goreportcard.com/badge/github.com/l3af-project/l3afd)](https://goreportcard.com/report/github.com/l3af-project/l3afd)
[![GoDoc](https://godoc.org/github.com/l3af-project/l3afd?status.svg)](https://pkg.go.dev/github.com/l3af-project/l3afd)
[![Apache licensed](https://img.shields.io/badge/license-Apache-blue.svg)](LICENSE)
[![L3AF Slack](https://img.shields.io/badge/slack-L3AF-brightgreen.svg?logo=slack)](http://l3afworkspace.slack.com/)

L3AFD is a crucial part of the L3AF ecosystem. For more information on L3AF see
https://l3af.io/

# Overview
L3AFD is the primary component of the L3AF control plane. L3AFD is a daemon
that orchestrates and manages multiple eBPF programs. L3AFD runs on each node
where the user wishes to run eBPF programs. L3AFD reads configuration data and
manages the execution and monitoring of eBPF programs running on the node.

L3AFD downloads pre-built eBPF programs from a user-configured repository.
However, we envision the creation of a community-driven eBPF package marketplace
where L3AF users can obtain a variety of eBPF programs developed by multiple
sources.

![L3AF Platform](https://github.com/l3af-project/l3af-arch/blob/main/images/L3AF_platform.png)

# Try it out
See our [L3AF Development Environment](https://github.com/l3af-project/l3af-arch/tree/main/dev_environment)
for a quick and easy way to try out L3AF on your local machine.

# Installing
Try [a binary release](https://github.com/l3af-project/l3afd/releases/latest).

# Building
To build on your local machine, including swagger docs do the following.

For Linux:
```
make
```

For Windows:
```
cmake -B build
cmake --build build
```
# Docker build
- L3AFD binary & configuration that is required in the Docker image needs to be built locally and copied to build-docker directory
- Execute below command to build the docker image
```
docker build -t l3afd:<version> -f Dockerfile .
```
Requirements to run L3AFD as a Container
- BPF, debugfs & shared-memory filesystems mount points should be available in the container
- L3AFD container needs privileged access as it needs to manage eBPF programs
- eBPF programs should be attached to the host interface so that it will apply to all the containers in the host

In order to satisfy the above requirements L3afd docker container needs to be run using the below command
```
docker 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>
```
# Testing
To test on your local machine, do the following.

For Linux:
```
go test ./...
```

For Windows:
```
go test -tags WINDOWS ./...
```

# Generate Swagger Docs
See our [Swaggo setup](docs/swagger.md)

# Contributing
Contributing to L3afd is fun. To get started:
- [Contributing guide](docs/CONTRIBUTING.md)


================================================
FILE: apis/configwatch.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0
//
//go:build !configs
// +build !configs

package apis

import (
	"context"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path"
	"regexp"
	"strings"
	"time"
	"unicode/utf8"

	httpSwagger "github.com/swaggo/http-swagger"

	"github.com/l3af-project/l3afd/v2/bpfprogs"
	"github.com/l3af-project/l3afd/v2/config"
	"github.com/l3af-project/l3afd/v2/models"
	"github.com/l3af-project/l3afd/v2/routes"
	"github.com/l3af-project/l3afd/v2/signals"

	_ "github.com/l3af-project/l3afd/v2/docs"

	"github.com/rs/zerolog/log"
)

type Server struct {
	BPFRTConfigs  *bpfprogs.NFConfigs
	HostName      string
	l3afdServer   *http.Server
	CaCertPool    *x509.CertPool
	SANMatchRules []string
}

// @title L3AFD APIs
// @version 1.0
// @description Configuration APIs to deploy and get the details of the eBPF Programs on the node
// @host
// @BasePath /
func StartConfigWatcher(ctx context.Context, hostname, daemonName string, conf *config.Config, bpfrtconfg *bpfprogs.NFConfigs) error {
	log.Info().Msgf("%s config server setup started on host %s", daemonName, hostname)

	s := &Server{
		BPFRTConfigs: bpfrtconfg,
		HostName:     hostname,
		l3afdServer: &http.Server{
			Addr: conf.L3afConfigsRestAPIAddr,
		},
		SANMatchRules: conf.MTLSSANMatchRules,
	}
	if _, ok := models.AllNetListeners.Load("main_http"); !ok {
		tcpAddr, err := net.ResolveTCPAddr("tcp", conf.L3afConfigsRestAPIAddr)
		if err != nil {
			return fmt.Errorf("error resolving TCP address:%w", err)
		}
		listener, err := net.ListenTCP("tcp", tcpAddr)
		if err != nil {
			return fmt.Errorf("creating tcp listner failed with %w", err)
		}
		models.AllNetListeners.Store("main_http", listener)
	}
	term := make(chan os.Signal, 1)
	signal.Notify(term, signals.ShutdownSignals...)
	go func() {
		<-term
		s.GracefulStop(conf.ShutdownTimeout)
		ctx.Done()
		log.Info().Msg("L3afd gracefulStop completed")
	}()

	go func() {
		r := routes.NewRouter(apiRoutes(ctx, bpfrtconfg))
		if conf.SwaggerApiEnabled {
			r.Mount("/swagger", httpSwagger.WrapHandler)
		}
		s.l3afdServer.Handler = r

		// As per design discussion when mTLS flag is not set and not listening on loopback or localhost
		if !conf.MTLSEnabled && !isLoopback(conf.L3afConfigsRestAPIAddr) && conf.Environment == config.ENV_PROD {
			conf.MTLSEnabled = true
		}
		val, _ := models.AllNetListeners.Load("main_http")
		l, _ := val.(*net.TCPListener)
		if conf.MTLSEnabled {
			log.Info().Msgf("l3afd server listening with mTLS - %s ", conf.L3afConfigsRestAPIAddr)
			// Create a CA certificate pool and add client ca's to it
			caCert, err := os.ReadFile(path.Join(conf.MTLSCertDir, conf.MTLSCACertFilename))
			if err != nil {
				log.Fatal().Err(err).Msgf("client CA %s file not found", conf.MTLSCACertFilename)
			}

			s.CaCertPool, _ = x509.SystemCertPool()
			if s.CaCertPool == nil {
				s.CaCertPool = x509.NewCertPool()
			}
			if ok := s.CaCertPool.AppendCertsFromPEM(caCert); !ok {
				log.Warn().Msgf("No client certs appended for mTLS")
			}
			serverCertFile := path.Join(conf.MTLSCertDir, conf.MTLSServerCertFilename)
			serverKeyFile := path.Join(conf.MTLSCertDir, conf.MTLSServerKeyFilename)
			serverCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)
			if err != nil {
				log.Fatal().Err(err).Msgf("failure loading certs")
			}
			// build server config
			s.l3afdServer.TLSConfig = &tls.Config{
				Certificates: []tls.Certificate{serverCert},
				GetConfigForClient: func(hi *tls.ClientHelloInfo) (*tls.Config, error) {
					serverConf := &tls.Config{
						Certificates:          []tls.Certificate{serverCert},
						MinVersion:            tls.VersionTLS12,
						ClientAuth:            tls.RequireAndVerifyClientCert,
						ClientCAs:             s.CaCertPool,
						VerifyPeerCertificate: s.getClientValidator(hi),
					}
					return serverConf, nil
				},
			}

			cpb, _ := pem.Decode(caCert)
			cert, err := x509.ParseCertificate(cpb.Bytes)
			if err != nil {
				log.Fatal().Err(err).Msgf("error in parsing tls certificate : %v", conf.MTLSCACertFilename)
			}
			expiry := cert.NotAfter
			start := cert.NotBefore
			go func() {
				period := time.Hour * 24
				ticker := time.NewTicker(period)
				defer ticker.Stop()
				for {
					select {
					case <-ticker.C:
						MonitorTLS(start, expiry, conf)
					case <-ctx.Done():
						return
					}
				}
			}()
			if err := s.l3afdServer.ServeTLS(l, serverCertFile, serverKeyFile); !errors.Is(err, http.ErrServerClosed) {
				log.Fatal().Err(err).Msgf("failed to start L3AFD server with mTLS enabled")
			}
		} else {
			log.Info().Msgf("l3afd server listening - %s ", conf.L3afConfigsRestAPIAddr)
			if err := s.l3afdServer.Serve(l); !errors.Is(err, http.ErrServerClosed) {
				log.Fatal().Err(err).Msgf("failed to start L3AFD server")
			}
		}
	}()
	return nil
}

func (s *Server) GracefulStop(shutdownTimeout time.Duration) error {
	log.Info().Msg("L3afd graceful stop initiated")
	exitCode := 0
	if len(s.BPFRTConfigs.IngressXDPBpfs) > 0 || len(s.BPFRTConfigs.IngressTCBpfs) > 0 || len(s.BPFRTConfigs.EgressTCBpfs) > 0 || s.BPFRTConfigs.ProbesBpfs.Len() > 0 {
		ctx, cancelfunc := context.WithTimeout(context.Background(), shutdownTimeout)
		defer cancelfunc()
		if err := s.BPFRTConfigs.Close(ctx); err != nil {
			log.Error().Err(err).Msg("stopping all network functions failed")
			exitCode = 1
		}
	}
	os.Exit(exitCode)
	return nil
}

// isLoopback - Check for localhost or loopback address
func isLoopback(addr string) bool {

	if strings.Contains(addr, "localhost:") {
		return true
	}
	if id := strings.LastIndex(addr, ":"); id > -1 {
		addr = addr[:id]
	}
	if ipAddr := net.ParseIP(addr); ipAddr != nil {
		return ipAddr.IsLoopback()
	}
	// :port scenario
	return true
}

func MonitorTLS(start time.Time, expiry time.Time, conf *config.Config) {
	todayDate := time.Now()
	expiryDate := expiry
	startDate := start
	diff := expiryDate.Sub(todayDate)
	remainingHoursToStart := todayDate.Sub(startDate)
	limit := conf.MTLSCertExpiryWarningDays * 24
	remainingHoursToExpire := int(diff.Hours())
	if remainingHoursToStart > 0 {
		log.Fatal().Msgf("tls certificate start from : %v", startDate)
	}
	if remainingHoursToExpire <= limit {
		if remainingHoursToExpire < 0 {
			log.Fatal().Msgf("tls certificate is expired on : %v", expiryDate)
		} else {
			log.Warn().Msgf("tls certificate will expire in %v days", int64(remainingHoursToExpire/24))
		}
	}
}

func (s *Server) getClientValidator(helloInfo *tls.ClientHelloInfo) func([][]byte, [][]*x509.Certificate) error {

	log.Debug().Msgf("Inside get client validator - %v", helloInfo.Conn.RemoteAddr())
	return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
		// Verifying client certs with root ca
		opts := x509.VerifyOptions{
			Roots:         s.CaCertPool,
			CurrentTime:   time.Now(),
			Intermediates: x509.NewCertPool(),
			KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
		}
		_, err := verifiedChains[0][0].Verify(opts)
		if err != nil {
			log.Error().Err(err).Msgf("certs verification failed")
			return err
		}

		log.Debug().Msgf("validating with SAN match rules - %s", s.SANMatchRules)
		if len(s.SANMatchRules) == 0 {
			return nil
		}
		for _, dnsName := range verifiedChains[0][0].DNSNames {
			if !validHostname(dnsName, true) {
				continue
			}
			dnsName = toLowerCaseASCII(dnsName)
			for _, sanMatchRule := range s.SANMatchRules {
				sanMatchRule = toLowerCaseASCII(sanMatchRule)
				if matchExactly(dnsName, sanMatchRule) {
					log.Debug().Msgf("Successfully matched matchExactly cert dns %s SANMatchRule %s", dnsName, sanMatchRule)
					return nil
				} else if matchHostnamesWithRegexp(dnsName, sanMatchRule) {
					log.Debug().Msgf("Successfully matched matchHostnamesWithRegexp cert dns %s SANMatchRule %s", dnsName, sanMatchRule)
					return nil
				}
			}
		}

		err = errors.New("certs verification with SAN match not found")
		log.Error().Err(err).Msgf("SAN match rules %s", s.SANMatchRules)
		return err
	}
}

// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use
// an explicitly ASCII function to avoid any sharp corners resulting from
// performing Unicode operations on DNS labels.
func toLowerCaseASCII(in string) string {
	// If the string is already lower-case then there's nothing to do.
	isAlreadyLowerCase := true
	for _, c := range in {
		if c == utf8.RuneError {
			// If we get a UTF-8 error then there might be
			// upper-case ASCII bytes in the invalid sequence.
			isAlreadyLowerCase = false
			break
		}
		if 'A' <= c && c <= 'Z' {
			isAlreadyLowerCase = false
			break
		}
	}

	if isAlreadyLowerCase {
		return in
	}

	out := []byte(in)
	for i, c := range out {
		if 'A' <= c && c <= 'Z' {
			out[i] += 'a' - 'A'
		}
	}
	return string(out)
}

// validHostname reports whether host is a valid hostname that can be matched or
// matched against according to RFC 6125 2.2, with some leniency to accommodate
// legacy values.
func validHostname(host string, isPattern bool) bool {
	if !isPattern {
		host = strings.TrimSuffix(host, ".")
	}
	if len(host) == 0 {
		return false
	}

	for i, part := range strings.Split(host, ".") {
		if part == "" {
			// Empty label.
			return false
		}
		if isPattern && i == 0 && part == "*" {
			// Only allow full left-most wildcards, as those are the only ones
			// we match, and matching literal '*' characters is probably never
			// the expected behavior.
			continue
		}
		for j, c := range part {
			if 'a' <= c && c <= 'z' {
				continue
			}
			if '0' <= c && c <= '9' {
				continue
			}
			if 'A' <= c && c <= 'Z' {
				continue
			}
			if c == '-' && j != 0 {
				continue
			}
			if c == '_' {
				// Not a valid character in hostnames, but commonly
				// found in deployments outside the WebPKI.
				continue
			}
			return false
		}
	}

	return true
}

// matchExactly - match hostnames
func matchExactly(hostA, hostB string) bool {
	// Here checking hostB (i.e. sanMatchRule) is valid hostname and not regex/pattern
	if !validHostname(hostB, false) {
		return false
	}
	return hostA == hostB
}

// matchHostnamesWithRegexp - To match the san rules with regexp
func matchHostnamesWithRegexp(dnsName, sanMatchRule string) bool {
	defer func() bool {
		if err := recover(); err != nil {
			log.Warn().Msgf("panic occurred: %v", err)
		}
		return false
	}()
	if len(dnsName) == 0 || len(sanMatchRule) == 0 {
		return false
	}
	re := regexp.MustCompile(sanMatchRule)

	return re.MatchString(dnsName)
}


================================================
FILE: apis/configwatch_test.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package apis

import (
	"reflect"
	"testing"
)

func TestMatchHostnamesWithRegexp(t *testing.T) {
	type args struct {
		dnsName      string
		sanMatchRule string
	}
	tests := []struct {
		name    string
		args    args
		want    bool
		wantErr bool
	}{
		{
			name:    "EmptyCheck",
			args:    args{dnsName: "", sanMatchRule: ""},
			want:    false,
			wantErr: false,
		},
		{
			name:    "LengthMissMatchCheck",
			args:    args{dnsName: "l3afd-lfn.us.l3af.io", sanMatchRule: "l3afd-lfn.l3af.io"},
			want:    false,
			wantErr: false,
		},
		{
			name:    "LengthMatchCheck",
			args:    args{dnsName: "l3afd-lfn.l3af.io", sanMatchRule: "l3afd-lfn.l3af.io"},
			want:    true,
			wantErr: false,
		},
		{
			name:    "LengthMatchPatternMissCheck",
			args:    args{dnsName: "l3afd-us.l3af.io", sanMatchRule: "l3afd-lf.l3af.io"},
			want:    false,
			wantErr: false,
		},
		{
			name:    "PatternMatchCheck",
			args:    args{dnsName: "l3afd-*.l3af.io", sanMatchRule: "l3afd-lfn.l3af.io"},
			want:    false,
			wantErr: false,
		},
		{
			name:    "PatternMissMatchCheck",
			args:    args{dnsName: "*l3afd-.l3af.io", sanMatchRule: "l3afd-lfn.l3af.io"},
			want:    false,
			wantErr: true,
		},
		{
			name:    "PatternRegExMatchCheck",
			args:    args{dnsName: "asnl3afd-lfn.l3af.io", sanMatchRule: ".*l3afd-lfn.l3af.io"},
			want:    true,
			wantErr: false,
		},
		{
			name:    "PatternRegExExactMatchCheck",
			args:    args{dnsName: "l3afd-dev.l3af.io", sanMatchRule: "^dev.l3af.io$"},
			want:    false,
			wantErr: false,
		},
		{
			name:    "PatternRegExFindMatch",
			args:    args{dnsName: "l3afd-dev.l3af.io", sanMatchRule: "dev.l3af.io"},
			want:    true,
			wantErr: false,
		},
		{
			name:    "PatternRegExFindMatchPattern",
			args:    args{dnsName: "l3afd-dev-10.l3af.io", sanMatchRule: "^l3afd-dev-[0-9][0-9]\\.l3af\\.io$"},
			want:    true,
			wantErr: false,
		},
		{
			name:    "PatternRegExLowerCaseMatch",
			args:    args{dnsName: "l3afd-dev-a0.l3af.io", sanMatchRule: "^l3afd-dev-[a-z][0-9]\\.l3af\\.io$"},
			want:    true,
			wantErr: false,
		},
		{
			name:    "PatternRegExUpperCaseMatch",
			args:    args{dnsName: "l3afd-dev-A0.l3af.io", sanMatchRule: "^l3afd-dev-[A-Z][0-9]\\.l3af\\.io$"},
			want:    true,
			wantErr: false,
		},
		{
			name:    "PatternRegExPanicCheck",
			args:    args{dnsName: "l3afd-dev-A0.l3af.io", sanMatchRule: "*l3afd-dev.l3af.io"},
			want:    false,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := matchHostnamesWithRegexp(tt.args.dnsName, tt.args.sanMatchRule)
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("matchHostnamesWithRegexp() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestMatchExactly(t *testing.T) {
	type args struct {
		hostA string
		hostB string
	}
	tests := []struct {
		name    string
		args    args
		want    bool
		wantErr bool
	}{
		{
			name:    "EmptyCheck",
			args:    args{hostA: "", hostB: ""},
			want:    false,
			wantErr: false,
		},
		{
			name:    "ExactMatchCheck",
			args:    args{hostA: "l3afd-lfn.l3af.io", hostB: "l3afd-lfn.l3af.io"},
			want:    true,
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := matchExactly(tt.args.hostA, tt.args.hostB)
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("matchHostnames() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestToLowerCaseASCII(t *testing.T) {
	type args struct {
		in string
	}
	tests := []struct {
		name    string
		args    args
		want    string
		wantErr bool
	}{
		{
			name:    "EmptyCheck",
			args:    args{in: ""},
			want:    "",
			wantErr: false,
		},
		{
			name:    "RegexLowerCheck",
			args:    args{in: "^l3afd-dev-[0-9][0-9]\\.l3af\\.io$"},
			want:    "^l3afd-dev-[0-9][0-9]\\.l3af\\.io$",
			wantErr: false,
		},
		{
			name:    "RegexUpperValueCheck",
			args:    args{in: "^L3AFd-dev-[0-9][0-9]\\.l3af\\.io$"},
			want:    "^l3afd-dev-[0-9][0-9]\\.l3af\\.io$",
			wantErr: false,
		},
		{
			name:    "RegexLowerCheckRuneError",
			args:    args{in: "^�l3afd-dev-[0-9][0-9]\\.l3af\\.io$"},
			want:    "^�l3afd-dev-[0-9][0-9]\\.l3af\\.io$",
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := toLowerCaseASCII(tt.args.in)
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("matchHostnames() = %v, want %v", got, tt.want)
			}
		})
	}
}


================================================
FILE: apis/handlers/addprog.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package handlers

import (
	"context"
	"encoding/json"
	"fmt"
	"io"

	"net/http"

	"github.com/rs/zerolog/log"

	"github.com/l3af-project/l3afd/v2/bpfprogs"
	"github.com/l3af-project/l3afd/v2/models"
)

// AddEbpfPrograms add new eBPF programs on node
// @Summary Adds new eBPF Programs on node
// @Description Adds new eBPF Programs on node
// @Accept  json
// @Produce  json
// @Param cfgs body []models.L3afBPFPrograms true "BPF programs"
// @Success 200
// @Router /l3af/configs/v1/add [post]
func AddEbpfPrograms(ctx context.Context, bpfcfg *bpfprogs.NFConfigs) http.HandlerFunc {

	return func(w http.ResponseWriter, r *http.Request) {
		mesg := ""
		statusCode := http.StatusOK

		w.Header().Add("Content-Type", "application/json")

		defer func(mesg *string, statusCode *int) {
			w.WriteHeader(*statusCode)
			_, err := w.Write([]byte(*mesg))
			if err != nil {
				log.Warn().Msgf("Failed to write response bytes: %v", err)
			}
		}(&mesg, &statusCode)
		if models.IsReadOnly {
			log.Warn().Msgf("We are in between restart please try after some time")
			mesg = "We are currently in the middle of a restart. Please attempt again after a while."
			return
		}
		defer DecWriteReq()
		IncWriteReq()
		if r.Body == nil {
			log.Warn().Msgf("Empty request body")
			return
		}
		bodyBuffer, err := io.ReadAll(r.Body)
		if err != nil {
			mesg = fmt.Sprintf("failed to read request body: %v", err)
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}

		var t []models.L3afBPFPrograms
		if err := json.Unmarshal(bodyBuffer, &t); err != nil {
			mesg = fmt.Sprintf("failed to unmarshal payload: %v", err)
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}
		if err := bpfcfg.AddeBPFPrograms(t); err != nil {
			mesg = fmt.Sprintf("failed to AddEbpfPrograms : %v", err)
			log.Error().Msg(mesg)

			statusCode = http.StatusInternalServerError
			return
		}
	}
}

func IncWriteReq() {
	models.StateLock.Lock()
	models.CurrentWriteReq++
	models.StateLock.Unlock()
}
func DecWriteReq() {
	models.StateLock.Lock()
	models.CurrentWriteReq--
	models.StateLock.Unlock()
}


================================================
FILE: apis/handlers/addprog_test.go
================================================
package handlers

import (
	"context"
	"net/http"
	"net/http/httptest"
	"path/filepath"
	"strings"
	"testing"

	"github.com/l3af-project/l3afd/v2/bpfprogs"
	"github.com/l3af-project/l3afd/v2/config"
	"github.com/l3af-project/l3afd/v2/models"
)

const dummypayload string = `[
	{
	  "host_name" : "l3af-local-test",
	  "iface" : "fakeif0",
	  "bpf_programs" : {
		"xdp_ingress" : [
		],
		"tc_egress": [
		],
		"tc_ingress": [
		]
	  }
	}
  ]
  `

func Test_addprog(t *testing.T) {

	tests := []struct {
		name       string
		Body       *strings.Reader
		header     map[string]string
		status     int
		cfg        *bpfprogs.NFConfigs
		isreadonly bool
	}{
		{
			name:       "NilBody",
			Body:       nil,
			status:     http.StatusOK,
			isreadonly: false,
			cfg: &bpfprogs.NFConfigs{
				HostConfig: &config.Config{
					L3afConfigStoreFileName: filepath.FromSlash("../../testdata/Test_l3af-config.json"),
				},
			},
		},
		{
			name:       "FailedToUnmarshal",
			Body:       strings.NewReader("Something"),
			status:     http.StatusInternalServerError,
			header:     map[string]string{},
			isreadonly: false,
			cfg: &bpfprogs.NFConfigs{
				HostConfig: &config.Config{
					L3afConfigStoreFileName: filepath.FromSlash("../../testdata/Test_l3af-config.json"),
				},
			},
		},
		{
			name: "EmptyInput",
			Body: strings.NewReader("[]"),
			header: map[string]string{
				"Content-Type": "application/json",
			},
			isreadonly: false,
			cfg: &bpfprogs.NFConfigs{
				HostConfig: &config.Config{
					L3afConfigStoreFileName: filepath.FromSlash("../../testdata/Test_l3af-config.json"),
				},
			},
			status: http.StatusOK,
		},
		{
			name:   "UnknownHostName",
			Body:   strings.NewReader(dummypayload),
			status: http.StatusInternalServerError,
			header: map[string]string{},
			cfg: &bpfprogs.NFConfigs{
				HostName: "dummy",
				HostConfig: &config.Config{
					L3afConfigStoreFileName: filepath.FromSlash("../../testdata/Test_l3af-config.json"),
				},
			},
			isreadonly: false,
		},
		{
			name: "InReadonly",
			Body: nil,
			header: map[string]string{
				"Content-Type": "application/json",
			},
			isreadonly: true,
			cfg:        nil,
			status:     http.StatusOK,
		},
	}
	for _, tt := range tests {
		var req *http.Request
		if tt.Body == nil {
			req, _ = http.NewRequest("POST", "/l3af/configs/v1/add", nil)
		} else {
			req, _ = http.NewRequest("POST", "/l3af/configs/v1/add", tt.Body)
		}
		for key, val := range tt.header {
			req.Header.Set(key, val)
		}
		models.IsReadOnly = tt.isreadonly
		rr := httptest.NewRecorder()
		handler := AddEbpfPrograms(context.Background(), tt.cfg)
		handler.ServeHTTP(rr, req)
		if rr.Code != tt.status {
			models.IsReadOnly = false
			t.Error("AddEbpfPrograms Failed")
		}
		models.IsReadOnly = false
	}
}


================================================
FILE: apis/handlers/deleteprog.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package handlers

import (
	"context"
	"encoding/json"
	"fmt"
	"io"

	"net/http"

	"github.com/rs/zerolog/log"

	"github.com/l3af-project/l3afd/v2/bpfprogs"
	"github.com/l3af-project/l3afd/v2/models"
)

// DeleteEbpfPrograms   remove eBPF programs on node
// @Summary Removes eBPF Programs on node
// @Description Removes eBPF Programs on node
// @Accept  json
// @Produce  json
// @Param cfgs body []models.L3afBPFProgramNames true "BPF program names"
// @Success 200
// @Router /l3af/configs/v1/delete [post]
func DeleteEbpfPrograms(ctx context.Context, bpfcfg *bpfprogs.NFConfigs) http.HandlerFunc {

	return func(w http.ResponseWriter, r *http.Request) {
		mesg := ""
		statusCode := http.StatusOK

		w.Header().Add("Content-Type", "application/json")

		defer func(mesg *string, statusCode *int) {
			w.WriteHeader(*statusCode)
			_, err := w.Write([]byte(*mesg))
			if err != nil {
				log.Warn().Msgf("Failed to write response bytes: %v", err)
			}
		}(&mesg, &statusCode)
		if models.IsReadOnly {
			log.Warn().Msgf("We are in between restart please try after some time")
			mesg = "We are currently in the middle of a restart. Please attempt again after a while."
			return
		}
		defer DecWriteReq()
		IncWriteReq()
		if r.Body == nil {
			log.Warn().Msgf("Empty request body")
			return
		}
		bodyBuffer, err := io.ReadAll(r.Body)
		if err != nil {
			mesg = fmt.Sprintf("failed to read request body: %v", err)
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}

		var t []models.L3afBPFProgramNames
		if err := json.Unmarshal(bodyBuffer, &t); err != nil {
			mesg = fmt.Sprintf("failed to unmarshal payload: %v", err)
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}

		if err := bpfcfg.DeleteEbpfPrograms(t); err != nil {
			mesg = fmt.Sprintf("failed to DeleteEbpfPrograms : %v", err)
			log.Error().Msg(mesg)

			statusCode = http.StatusInternalServerError
			return
		}
	}
}


================================================
FILE: apis/handlers/deleteprog_test.go
================================================
package handlers

import (
	"context"
	"net/http"
	"net/http/httptest"
	"path/filepath"
	"strings"
	"testing"

	"github.com/l3af-project/l3afd/v2/bpfprogs"
	"github.com/l3af-project/l3afd/v2/config"
	"github.com/l3af-project/l3afd/v2/models"
)

const payloadfordelete string = `[
    {
        "host_name": "l3af-local-test",
        "iface": "fakeif0",
        "bpf_programs": {
            "xdp_ingress": [
                "ratelimiting",
                "connection-limit"
            ]
        }
    }
]
`

func Test_DeleteEbpfPrograms(t *testing.T) {

	tests := []struct {
		name       string
		Body       *strings.Reader
		header     map[string]string
		status     int
		cfg        *bpfprogs.NFConfigs
		isreadonly bool
	}{
		{
			name:       "NilBody",
			Body:       nil,
			status:     http.StatusOK,
			isreadonly: false,
			cfg: &bpfprogs.NFConfigs{
				HostConfig: &config.Config{
					L3afConfigStoreFileName: filepath.FromSlash("../../testdata/Test_l3af-config.json"),
				},
			},
		},
		{
			name:       "FailedToUnmarshal",
			Body:       strings.NewReader("Something"),
			status:     http.StatusInternalServerError,
			header:     map[string]string{},
			isreadonly: false,
			cfg: &bpfprogs.NFConfigs{
				HostConfig: &config.Config{
					L3afConfigStoreFileName: filepath.FromSlash("../../testdata/Test_l3af-config.json"),
				},
			},
		},
		{
			name: "EmptyInput",
			Body: strings.NewReader(`[]`),
			header: map[string]string{
				"Content-Type": "application/json",
			},
			isreadonly: false,
			cfg: &bpfprogs.NFConfigs{
				HostConfig: &config.Config{
					L3afConfigStoreFileName: filepath.FromSlash("../../testdata/Test_l3af-config.json"),
				},
			},
			status: http.StatusOK,
		},
		{
			name:       "UnknownHostName",
			Body:       strings.NewReader(payloadfordelete),
			status:     http.StatusInternalServerError,
			header:     map[string]string{},
			isreadonly: false,
			cfg: &bpfprogs.NFConfigs{
				HostName: "dummy",
				HostConfig: &config.Config{
					L3afConfigStoreFileName: filepath.FromSlash("../../testdata/Test_l3af-config.json"),
				},
			},
		},
		{
			name:   "InReadonly",
			Body:   nil,
			status: http.StatusOK,
			header: map[string]string{
				"Content-Type": "application/json",
			},
			isreadonly: true,
			cfg:        nil,
		},
	}
	for _, tt := range tests {
		var req *http.Request
		if tt.Body == nil {
			req, _ = http.NewRequest("POST", "/l3af/configs/v1/delete", nil)
		} else {
			req, _ = http.NewRequest("POST", "/l3af/configs/v1/delete", tt.Body)
		}
		for key, val := range tt.header {
			req.Header.Set(key, val)
		}
		models.IsReadOnly = tt.isreadonly
		rr := httptest.NewRecorder()
		handler := DeleteEbpfPrograms(context.Background(), tt.cfg)
		handler.ServeHTTP(rr, req)
		if rr.Code != tt.status {
			models.IsReadOnly = false
			t.Error("DeleteEbpfPrograms Failed")
		}
		models.IsReadOnly = false
	}
}


================================================
FILE: apis/handlers/getconfig.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package handlers

import (
	"encoding/json"
	"net/http"

	chi "github.com/go-chi/chi/v5"
	"github.com/l3af-project/l3afd/v2/bpfprogs"
	"github.com/rs/zerolog/log"
)

var bpfcfgs *bpfprogs.NFConfigs

func InitConfigs(cfgs *bpfprogs.NFConfigs) error {
	bpfcfgs = cfgs
	return nil
}

// GetConfig Returns details of the configuration of eBPF Programs for a given interface
// @Summary Returns details of the configuration of eBPF Programs for a given interface
// @Description Returns details of the configuration of eBPF Programs for a given interface
// @Accept  json
// @Produce  json
// @Param iface path string true "interface name"
// @Success 200
// @Router /l3af/configs/v1/{iface} [get]
func GetConfig(w http.ResponseWriter, r *http.Request) {
	mesg := ""
	statusCode := http.StatusOK

	w.Header().Add("Content-Type", "application/json")

	defer func(mesg *string, statusCode *int) {
		w.WriteHeader(*statusCode)
		_, err := w.Write([]byte(*mesg))
		if err != nil {
			log.Warn().Msgf("Failed to write response bytes: %v", err)
		}
	}(&mesg, &statusCode)

	iface := chi.URLParam(r, "iface")
	if len(iface) == 0 {
		mesg = "iface value is empty"
		log.Error().Msg(mesg)
		statusCode = http.StatusBadRequest
		return
	}

	resp, err := json.MarshalIndent(bpfcfgs.EBPFPrograms(iface), "", "  ")
	if err != nil {
		mesg = "internal server error"
		log.Error().Msgf("failed to marshal response: %v", err)
		statusCode = http.StatusInternalServerError
		return
	}
	mesg = string(resp)
}

// GetConfigAll Returns details of the configuration of eBPF Programs for all interfaces on a node
// @Summary Returns details of the configuration of eBPF Programs for all interfaces on a node
// @Description Returns details of the configuration of eBPF Programs for all interfaces on a node
// @Accept  json
// @Produce  json
// @Success 200
// @Router /l3af/configs/v1 [get]
func GetConfigAll(w http.ResponseWriter, r *http.Request) {
	mesg := ""
	statusCode := http.StatusOK

	w.Header().Add("Content-Type", "application/json")

	defer func(mesg *string, statusCode *int) {
		w.WriteHeader(*statusCode)
		_, err := w.Write([]byte(*mesg))
		if err != nil {
			log.Warn().Msgf("Failed to write response bytes: %v", err)
		}
	}(&mesg, &statusCode)

	resp, err := json.MarshalIndent(bpfcfgs.EBPFProgramsAll(), "", "  ")
	if err != nil {
		mesg = "internal server error"
		log.Error().Msgf("failed to marshal response: %v", err)
		statusCode = http.StatusInternalServerError
		return
	}
	mesg = string(resp)
}


================================================
FILE: apis/handlers/getconfig_test.go
================================================
package handlers

import (
	"container/list"
	"context"
	"net/http"
	"net/http/httptest"
	"testing"

	chi "github.com/go-chi/chi/v5"
	"github.com/l3af-project/l3afd/v2/bpfprogs"
)

func Test_GetConfig(t *testing.T) {
	tests := []struct {
		name   string
		iface  string
		status int
		cfg    *bpfprogs.NFConfigs
	}{
		{
			name:   "EmptyInterfaceInRequest",
			iface:  "",
			status: http.StatusBadRequest,
			cfg:    &bpfprogs.NFConfigs{},
		},
		{
			name:   "GoodInput",
			iface:  "fakeif0",
			status: http.StatusOK,
			cfg: &bpfprogs.NFConfigs{
				IngressXDPBpfs: map[string]*list.List{"fakeif0": nil},
				IngressTCBpfs:  map[string]*list.List{"fakeif0": nil},
				EgressTCBpfs:   map[string]*list.List{"fakeif0": nil},
			},
		},
	}
	for _, tt := range tests {
		req, _ := http.NewRequest("GET", "l3af/configs/v1/"+tt.iface, nil)
		rctx := chi.NewRouteContext()
		req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx))
		rctx.URLParams.Add("iface", tt.iface)
		rr := httptest.NewRecorder()
		handler := http.HandlerFunc(GetConfig)
		InitConfigs(tt.cfg)
		handler.ServeHTTP(rr, req)
		if rr.Code != tt.status {
			t.Errorf("GetConfig Failed")
		}
	}
}

func Test_GetConfigAll(t *testing.T) {
	tests := []struct {
		name   string
		status int
		cfg    *bpfprogs.NFConfigs
	}{
		{
			name:   "GoodInput",
			status: http.StatusOK,
			cfg:    &bpfprogs.NFConfigs{},
		},
	}
	for _, tt := range tests {
		req, _ := http.NewRequest("GET", "l3af/configs/v1", nil)
		rr := httptest.NewRecorder()
		handler := http.HandlerFunc(GetConfigAll)
		InitConfigs(tt.cfg)
		handler.ServeHTTP(rr, req)
		if rr.Code != tt.status {
			t.Errorf("GetConfigAll Failed")
		}
	}
}


================================================
FILE: apis/handlers/restart_linux.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package handlers

import (
	"encoding/gob"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	"net/http"

	"github.com/rs/zerolog/log"

	"github.com/l3af-project/l3afd/v2/bpfprogs"
	"github.com/l3af-project/l3afd/v2/models"
	"github.com/l3af-project/l3afd/v2/pidfile"
	"github.com/l3af-project/l3afd/v2/restart"
)

// HandleRestart will start new instance of l3afd provided by payload
// @Summary this api will start new instance of l3afd provided by payload
// @Description this api will start new instance of l3afd provided by payload
// @Accept  json
// @Produce  json
// @Param cfgs body []models.L3afBPFPrograms true "BPF programs"
// @Success 200
// @Router /l3af/configs/v1/restart [put]
func HandleRestart(bpfcfg *bpfprogs.NFConfigs) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		mesg := ""
		statusCode := http.StatusOK
		w.Header().Add("Content-Type", "application/json")
		defer func(mesg *string, statusCode *int) {
			w.WriteHeader(*statusCode)
			_, err := w.Write([]byte(*mesg))
			if err != nil {
				log.Warn().Msgf("Failed to write response bytes: %v", err)
			}
		}(&mesg, &statusCode)
		if models.IsReadOnly {
			log.Warn().Msgf("We are in between restart please try after some time")
			mesg = "We are currently in the middle of a restart. Please attempt again after a while."
			statusCode = http.StatusInternalServerError
			return
		}

		if r.Body == nil {
			mesg = "nil request body"
			statusCode = http.StatusInternalServerError
			log.Warn().Msgf("Empty request body")
			return
		}
		bodyBuffer, err := io.ReadAll(r.Body)
		if err != nil {
			mesg = fmt.Sprintf("failed to read request body: %v", err)
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}

		var t models.RestartConfig
		if err := json.Unmarshal(bodyBuffer, &t); err != nil {
			mesg = fmt.Sprintf("failed to unmarshal payload: %v", err)
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}

		machineHostname, err := os.Hostname()
		if err != nil {
			mesg = "failed to get os hostname"
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}
		if machineHostname != t.HostName {
			mesg = "this api request is not for provided host"
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}

		defer func() {
			models.IsReadOnly = false
		}()
		models.IsReadOnly = true

		// complete active requests
		for {
			models.StateLock.Lock()
			if models.CurrentWriteReq == 0 {
				models.StateLock.Unlock()
				break
			}
			models.StateLock.Unlock()
			time.Sleep(5 * time.Millisecond)
		}
		// Now our system is in Readonly state

		oldCfgPath, err := restart.ReadSymlink(filepath.Join(bpfcfg.HostConfig.BasePath, "latest/l3afd.cfg"))
		if err != nil {
			mesg = fmt.Sprintf("failed read symlink %v with error: %v", filepath.Join(bpfcfg.HostConfig.BasePath, "latest/l3afd.cfg"), err)
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}
		oldBinPath, err := restart.ReadSymlink(filepath.Join(bpfcfg.HostConfig.BasePath, "latest/l3afd"))
		if err != nil {
			mesg = fmt.Sprintf("failed read symlink %v with error: %v", filepath.Join(bpfcfg.HostConfig.BasePath, "latest/l3afd"), err)
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}

		// /usr/local/l3afd/v2.0.0/l3afd/l3afd --> v2.0.0/l3afd/l3afd --> v2.0.0
		oldVersion := strings.Split(strings.Trim(oldBinPath, bpfcfg.HostConfig.BasePath+"/"), "/")[0]
		if _, ok := models.AvailableVersions[t.Version]; !ok {
			mesg = "invalid version to upgrade"
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}
		err = restart.GetNewVersion(models.L3AFDRestartArtifactName, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)
		if err != nil {
			mesg = fmt.Sprintf("failed to get new version: %v", err)
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			err = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)
			mesg = mesg + fmt.Sprintf("rollback of symlinks failed: %v", err)
			return
		}

		bpfProgs := bpfcfg.GetL3AFHOSTDATA()
		ln, err := net.Listen("unix", models.HostSock)
		if err != nil {
			log.Err(err)
			err = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)
			mesg = mesg + fmt.Sprintf("rollback of symlinks failed: %v", err)
			statusCode = http.StatusInternalServerError
			return
		}
		srvError := make(chan error, 1)
		go func() {
			defer ln.Close()
			conn, err := ln.Accept()
			if err != nil {
				log.Err(err)
				srvError <- err
				return
			}
			defer conn.Close()
			encoder := gob.NewEncoder(conn)
			err = encoder.Encode(bpfProgs)
			if err != nil {
				log.Err(err)
				srvError <- err
				return
			}
			srvError <- nil
		}()

		files := make([]*os.File, 3)
		srvToIndex := make(map[string]int)
		srvToIndex["stat_http"] = 0
		srvToIndex["main_http"] = 1
		srvToIndex["debug_http"] = 2
		isErr := false
		models.AllNetListeners.Range(func(srvr, listr interface{}) bool { // iterate over the map
			srv, _ := srvr.(string)
			lis, _ := listr.(*net.TCPListener)
			idx := srvToIndex[srv]
			lf, err := lis.File()
			if err != nil {
				log.Error().Msgf("%v", err)
				err = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)
				mesg = mesg + fmt.Sprintf("rollback of symlinks failed: %v", err)
				statusCode = http.StatusInternalServerError
				isErr = true
				return false
			}
			newFile := os.NewFile(uintptr(lf.Fd()), "dupFdlistner"+strconv.Itoa(idx))
			files[idx] = newFile
			return true
		})
		if isErr {
			return
		}

		cmd := exec.Command(filepath.Join(bpfcfg.HostConfig.BasePath, "latest/l3afd"), "--config", filepath.Join(bpfcfg.HostConfig.BasePath, "latest/l3afd.cfg"))
		cmd.SysProcAttr = &syscall.SysProcAttr{
			Setsid: true,
		}
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.ExtraFiles = files

		err = bpfcfg.StopAllProbesAndUserPrograms()
		if err != nil {
			log.Err(err)
			err = bpfcfg.StartAllUserProgramsAndProbes()
			if err != nil {
				log.Error().Msgf("%v", err)
				mesg = mesg + fmt.Sprintf("unable to start all userprograms and probes: %v", err)
			}
			err = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)
			mesg = mesg + fmt.Sprintf("rollback of symlinks failed: %v", err)
			statusCode = http.StatusInternalServerError
			return
		}

		log.Info().Msg("Starting child Process")
		err = cmd.Start()
		if err != nil {
			log.Error().Msgf("%v", err)
			mesg = mesg + fmt.Sprintf("unable to start new instance %v", err)
			err = cmd.Process.Kill()
			if err != nil {
				log.Error().Msgf("%v", err)
				mesg = mesg + fmt.Sprintf("unable to kill the new instance %v", err)
			}
			err = bpfcfg.StartAllUserProgramsAndProbes()
			if err != nil {
				log.Error().Msgf("%v", err)
				mesg = mesg + fmt.Sprintf("unable to start all userprograms and probes: %v", err)
			}
			err = pidfile.CreatePID(bpfcfg.HostConfig.PIDFilename)
			if err != nil {
				log.Error().Msgf("%v", err)
				mesg = mesg + fmt.Sprintf("unable to create pid file: %v", err)
			}
			err = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)
			if err != nil {
				mesg = mesg + fmt.Sprintf("rollback of symlink failed: %v", err)
			}
			statusCode = http.StatusInternalServerError
			return
		}
		NewProcessStatus := make(chan string)
		go func() {
			// I need to write client code for reading the state of new process
			var err error
			var conn net.Conn
			f := false
			for i := 1; i <= bpfcfg.HostConfig.TimetoRestart; i++ {
				conn, err = net.Dial("unix", models.StateSock)
				if err == nil {
					f = true
					break
				}
				log.Info().Msgf("Waiting for socket to be up...")
				time.Sleep(time.Second) // sleep for a second before trying again
			}
			if !f {
				conn.Close()
				NewProcessStatus <- models.StatusFailed
				return
			}
			defer conn.Close()
			decoder := gob.NewDecoder(conn)
			var data string
			err = decoder.Decode(&data)
			if err != nil {
				NewProcessStatus <- models.StatusFailed
				return
			}
			NewProcessStatus <- data
		}()

		// time to bootup
		select {
		case terr := <-srvError:
			if terr != nil {
				statusCode = http.StatusInternalServerError
				err = cmd.Process.Kill()
				if err != nil {
					log.Error().Msgf("%v", err)
					mesg = mesg + fmt.Sprintf("unable to kill the new instance %v", err)
				}
				err = bpfcfg.StartAllUserProgramsAndProbes()
				if err != nil {
					log.Error().Msgf("%v", err)
					mesg = mesg + fmt.Sprintf("unable to start all userprograms and probes: %v", err)
				}
				err = pidfile.CreatePID(bpfcfg.HostConfig.PIDFilename)
				if err != nil {
					log.Error().Msgf("%v", err)
					mesg = mesg + fmt.Sprintf("unable to create pid file: %v", err)
				}
				err = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)
				if err != nil {
					mesg = mesg + fmt.Sprintf("rollback of symlink failed: %v", err)
				}
				statusCode = http.StatusInternalServerError
				log.Err(terr)
				return
			}
			break
		default:
			time.Sleep(time.Second)
		}

		st := <-NewProcessStatus
		if st == models.StatusFailed {
			err = cmd.Process.Kill()
			if err != nil {
				log.Error().Msgf("%v", err)
				mesg = mesg + fmt.Sprintf("unable to kill the new instance %v", err)
			}
			err = bpfcfg.StartAllUserProgramsAndProbes()
			if err != nil {
				log.Error().Msgf("%v", err)
				mesg = mesg + fmt.Sprintf("unable to start all userprograms and probes: %v", err)
			}
			err = pidfile.CreatePID(bpfcfg.HostConfig.PIDFilename)
			if err != nil {
				log.Error().Msgf("%v", err)
				mesg = mesg + fmt.Sprintf("unable to create pid file: %v", err)
			}
			err = restart.RollBackSymlink(oldCfgPath, oldBinPath, oldVersion, models.AvailableVersions[t.Version], bpfcfg.HostConfig)
			if err != nil {
				mesg = mesg + fmt.Sprintf("rollback of symlink failed: %v", err)
			}
			statusCode = http.StatusInternalServerError
			return
		} else {
			log.Info().Msgf("doing exiting old process")
			models.CloseForRestart <- struct{}{}
		}
	}
}


================================================
FILE: apis/handlers/restart_linux_test.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0
package handlers

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/l3af-project/l3afd/v2/models"
)

func Test_HandleRestart(t *testing.T) {
	var req *http.Request
	req, _ = http.NewRequest("PUT", "/l3af/configs/v1/restart", nil)
	req.Header.Set("Content-Type", "application/json")
	models.IsReadOnly = true
	rr := httptest.NewRecorder()
	handler := HandleRestart(nil)
	handler.ServeHTTP(rr, req)
	if rr.Code != http.StatusInternalServerError {
		models.IsReadOnly = false
		t.Error("Handle restart Failed")
	}
	models.IsReadOnly = false
}


================================================
FILE: apis/handlers/restart_windows.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package handlers

import (
	"net/http"

	"github.com/rs/zerolog/log"

	"github.com/l3af-project/l3afd/v2/bpfprogs"
)

// HandleRestart Store meta data about ebpf programs and exit
// @Summary Store meta data about ebpf programs and exit
// @Description Store meta data about ebpf programs and exit
// @Accept  json
// @Produce  json
// @Param cfgs body []models.L3afBPFPrograms true "BPF programs"
// @Success 200
// @Router /l3af/configs/v1/restart [put]
func HandleRestart(bpfcfg *bpfprogs.NFConfigs) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		mesg := ""
		statusCode := http.StatusOK
		w.Header().Add("Content-Type", "application/json")
		defer func(mesg *string, statusCode *int) {
			w.WriteHeader(*statusCode)
			_, err := w.Write([]byte(*mesg))
			if err != nil {
				log.Warn().Msgf("Failed to write response bytes: %v", err)
			}
		}(&mesg, &statusCode)
		mesg = "Graceful restart is only supported for linux as of now"
	}
}


================================================
FILE: apis/handlers/restart_windows_test.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0
package handlers

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

func Test_HandleRestart(t *testing.T) {
	var req *http.Request
	req, _ = http.NewRequest("PUT", "/l3af/configs/v1/restart", nil)
	req.Header.Set("Content-Type", "application/json")
	rr := httptest.NewRecorder()
	handler := HandleRestart(nil)
	handler.ServeHTTP(rr, req)
	if rr.Code != http.StatusOK {
		t.Error("Handle restart Failed")
	}
}


================================================
FILE: apis/handlers/updateconfig.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package handlers

import (
	"context"
	"encoding/json"
	"fmt"
	"io"

	"net/http"

	"github.com/rs/zerolog/log"

	"github.com/l3af-project/l3afd/v2/bpfprogs"
	"github.com/l3af-project/l3afd/v2/models"
)

// UpdateConfig Update eBPF Programs configuration
// @Summary Update eBPF Programs configuration
// @Description Update eBPF Programs configuration
// @Accept  json
// @Produce  json
// @Param cfgs body []models.L3afBPFPrograms true "BPF programs"
// @Success 200
// @Router /l3af/configs/v1/update [post]
func UpdateConfig(ctx context.Context, bpfcfg *bpfprogs.NFConfigs) http.HandlerFunc {

	return func(w http.ResponseWriter, r *http.Request) {
		mesg := ""
		statusCode := http.StatusOK

		w.Header().Add("Content-Type", "application/json")

		defer func(mesg *string, statusCode *int) {
			w.WriteHeader(*statusCode)
			_, err := w.Write([]byte(*mesg))
			if err != nil {
				log.Warn().Msgf("Failed to write response bytes: %v", err)
			}
		}(&mesg, &statusCode)
		if models.IsReadOnly {
			log.Warn().Msgf("We are in between restart please try after some time")
			mesg = "We are currently in the middle of a restart. Please attempt again after a while."
			return
		}
		defer DecWriteReq()
		IncWriteReq()
		if r.Body == nil {
			log.Warn().Msgf("Empty request body")
			return
		}
		bodyBuffer, err := io.ReadAll(r.Body)
		if err != nil {
			mesg = fmt.Sprintf("failed to read request body: %v", err)
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}

		var t []models.L3afBPFPrograms
		if err := json.Unmarshal(bodyBuffer, &t); err != nil {
			mesg = fmt.Sprintf("failed to unmarshal payload: %v", err)
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}

		if err := bpfcfg.DeployeBPFPrograms(t); err != nil {
			mesg = fmt.Sprintf("failed to deploy ebpf programs: %v", err)
			log.Error().Msg(mesg)
			statusCode = http.StatusInternalServerError
			return
		}
	}
}


================================================
FILE: apis/handlers/updateconfig_test.go
================================================
package handlers

import (
	"context"
	"net/http"
	"net/http/httptest"
	"path/filepath"
	"strings"
	"testing"

	"github.com/l3af-project/l3afd/v2/bpfprogs"
	"github.com/l3af-project/l3afd/v2/config"
	"github.com/l3af-project/l3afd/v2/models"
)

func Test_UpdateConfig(t *testing.T) {

	tests := []struct {
		name       string
		Body       *strings.Reader
		header     map[string]string
		status     int
		cfg        *bpfprogs.NFConfigs
		isreadonly bool
	}{
		{
			name:       "NilBody",
			Body:       nil,
			status:     http.StatusOK,
			isreadonly: false,
			cfg: &bpfprogs.NFConfigs{
				HostConfig: &config.Config{
					L3afConfigStoreFileName: filepath.FromSlash("../../testdata/Test_l3af-config.json"),
				},
			},
		},
		{
			name:       "FailedToUnmarshal",
			Body:       strings.NewReader("Something"),
			status:     http.StatusInternalServerError,
			header:     map[string]string{},
			isreadonly: false,
			cfg: &bpfprogs.NFConfigs{
				HostConfig: &config.Config{
					L3afConfigStoreFileName: filepath.FromSlash("../../testdata/Test_l3af-config.json"),
				},
			},
		},
		{
			name:       "UnknownHostName",
			Body:       strings.NewReader(dummypayload),
			status:     http.StatusInternalServerError,
			header:     map[string]string{},
			isreadonly: false,
			cfg: &bpfprogs.NFConfigs{
				HostName: "dummy",
				HostConfig: &config.Config{
					L3afConfigStoreFileName: filepath.FromSlash("../../testdata/Test_l3af-config.json"),
				},
			},
		},
		{
			name:   "InReadonly",
			Body:   nil,
			status: http.StatusOK,
			header: map[string]string{
				"Content-Type": "application/json",
			},
			isreadonly: true,
			cfg:        nil,
		},
	}
	for _, tt := range tests {
		var req *http.Request
		if tt.Body == nil {
			req, _ = http.NewRequest("POST", "/l3af/configs/v1/update", nil)
		} else {
			req, _ = http.NewRequest("POST", "/l3af/configs/v1/update", tt.Body)
		}
		for key, val := range tt.header {
			req.Header.Set(key, val)
		}
		models.IsReadOnly = tt.isreadonly
		rr := httptest.NewRecorder()
		handler := UpdateConfig(context.Background(), tt.cfg)
		handler.ServeHTTP(rr, req)
		if rr.Code != tt.status {
			models.IsReadOnly = false
			t.Error("UpdateConfig Failed")
		}
		models.IsReadOnly = false
	}
}


================================================
FILE: apis/routes.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package apis

import (
	"context"

	"github.com/l3af-project/l3afd/v2/apis/handlers"
	"github.com/l3af-project/l3afd/v2/bpfprogs"
	"github.com/l3af-project/l3afd/v2/routes"
)

func apiRoutes(ctx context.Context, bpfcfg *bpfprogs.NFConfigs) []routes.Route {

	r := []routes.Route{
		{
			Method:      "POST",
			Path:        "/l3af/configs/{version}/update",
			HandlerFunc: handlers.UpdateConfig(ctx, bpfcfg),
		},
		{
			Method:      "GET",
			Path:        "/l3af/configs/{version}/{iface}",
			HandlerFunc: handlers.GetConfig,
		},
		{
			Method:      "GET",
			Path:        "/l3af/configs/{version}",
			HandlerFunc: handlers.GetConfigAll,
		},
		{
			Method:      "POST",
			Path:        "/l3af/configs/{version}/add",
			HandlerFunc: handlers.AddEbpfPrograms(ctx, bpfcfg),
		},
		{
			Method:      "POST",
			Path:        "/l3af/configs/{version}/delete",
			HandlerFunc: handlers.DeleteEbpfPrograms(ctx, bpfcfg),
		},
		{
			Method:      "PUT",
			Path:        "/l3af/configs/{version}/restart",
			HandlerFunc: handlers.HandleRestart(bpfcfg),
		},
	}

	return r
}


================================================
FILE: bpfprogs/bpf.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

// Package bpfprogs provides primitives for BPF programs / Network Functions.
package bpfprogs

import (
	"archive/tar"
	"archive/zip"
	"bytes"
	"compress/gzip"
	"container/ring"
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
	"unsafe"

	"github.com/l3af-project/l3afd/v2/config"
	"github.com/l3af-project/l3afd/v2/models"
	"github.com/l3af-project/l3afd/v2/stats"
	"github.com/l3af-project/l3afd/v2/utils"

	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/link"
	"github.com/cilium/ebpf/rlimit"
	ps "github.com/mitchellh/go-ps"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/rs/zerolog/log"
)

var (
	execCommand           = exec.Command
	copyBufPool sync.Pool = sync.Pool{New: func() interface{} { return new(bytes.Buffer) }}
)

//lint:ignore U1000 avoid false linter error on windows, since this variable is only used in linux code
const executePerm uint32 = 0111
const bpfStatus string = "RUNNING"

// BPF defines run time details for BPFProgram.
type BPF struct {
	Program           models.BPFProgram
	Cmd               *exec.Cmd                 `json:"-"`
	FilePath          string                    // Binary file path
	RestartCount      int                       // To track restart count
	PrevMapNamePath   string                    // Previous Map name with path to link
	MapNamePath       string                    // Map name with path
	ProgID            ebpf.ProgramID            // eBPF Program ID
	BpfMaps           map[string]BPFMap         // Config maps passed as map-args, Map name is Key
	MetricsBpfMaps    map[string]*MetricsBPFMap // Metrics map name+key+aggregator is key
	Ctx               context.Context           `json:"-"`
	Done              chan bool                 `json:"-"`
	ProgMapCollection *ebpf.Collection          `json:"_"` // eBPF Collection reference
	ProgMapID         ebpf.MapID                // Prog map id
	PrevProgMapID     ebpf.MapID                // Prev prog map id
	HostConfig        *config.Config
	Link              link.Link `json:"-"` // handle link object
	ProbeLinks        []*link.Link
}

func NewBpfProgram(ctx context.Context, program models.BPFProgram, conf *config.Config, ifaceName string) *BPF {

	var progMapFilePath string
	version := utils.ReplaceDotsWithUnderscores(program.Version)
	if len(program.MapName) > 0 {
		if program.ProgType == models.XDPType {
			progMapFilePath = filepath.Join(conf.BpfMapDefaultPath, ifaceName, program.Name, version, program.MapName)
		} else if program.ProgType == models.TCType {
			progMapFilePath = filepath.Join(conf.BpfMapDefaultPath, models.TCMapPinPath, ifaceName, program.Name, version, program.MapName)
		}
		if strings.Contains(progMapFilePath, "..") {
			log.Error().Msgf("program map file contains relative path %s", progMapFilePath)
			return nil
		}
	}

	bpf := &BPF{
		Program:        program,
		RestartCount:   0,
		Cmd:            nil,
		FilePath:       "",
		BpfMaps:        make(map[string]BPFMap, 0),
		MetricsBpfMaps: make(map[string]*MetricsBPFMap, 0),
		Ctx:            ctx,
		Done:           nil,
		HostConfig:     conf,
		MapNamePath:    progMapFilePath,
	}

	return bpf
}

// LoadRootProgram - Loading the Root Program for a given interface.
func LoadRootProgram(ifaceName string, direction string, progType string, conf *config.Config) (*BPF, error) {

	log.Info().Msgf("LoadRootProgram iface %s direction %s progType %s", ifaceName, direction, progType)
	var rootProgBPF *BPF
	switch progType {
	case models.XDPType:
		version := utils.ReplaceDotsWithUnderscores(conf.XDPRootVersion)
		rootProgBPF = &BPF{
			Program: models.BPFProgram{
				Name:              conf.XDPRootPackageName,
				Artifact:          conf.XDPRootArtifact,
				MapName:           conf.XDPRootMapName,
				Version:           conf.XDPRootVersion,
				UserProgramDaemon: false,
				AdminStatus:       models.Enabled,
				ProgType:          models.XDPType,
				SeqID:             0,
				StartArgs:         map[string]interface{}{},
				StopArgs:          map[string]interface{}{},
				StatusArgs:        map[string]interface{}{},
				ObjectFile:        conf.XDPRootObjectFile,
				EntryFunctionName: conf.XDPRootEntryFunctionName,
			},
			RestartCount:    0,
			Cmd:             nil,
			FilePath:        "",
			PrevMapNamePath: "",
			HostConfig:      conf,
			MapNamePath:     filepath.Join(conf.BpfMapDefaultPath, ifaceName, conf.XDPRootPackageName, version, conf.XDPRootMapName),
			Link:            nil,
		}
	case models.TCType:
		rootProgBPF = &BPF{
			Program: models.BPFProgram{
				Name:              conf.TCRootPackageName,
				Artifact:          conf.TCRootArtifact,
				Version:           conf.TCRootVersion,
				UserProgramDaemon: false,
				AdminStatus:       models.Enabled,
				ProgType:          models.TCType,
				StartArgs:         map[string]interface{}{},
				StopArgs:          map[string]interface{}{},
				StatusArgs:        map[string]interface{}{},
			},
			RestartCount:    0,
			Cmd:             nil,
			FilePath:        "",
			PrevMapNamePath: "",
			HostConfig:      conf,
		}
		if direction == models.IngressType {
			rootProgBPF.Program.MapName = conf.TCRootIngressMapName
			rootProgBPF.Program.ObjectFile = conf.TCRootIngressObjectFile
			rootProgBPF.Program.EntryFunctionName = conf.TCRootIngressEntryFunctionName
		} else if direction == models.EgressType {
			rootProgBPF.Program.MapName = conf.TCRootEgressMapName
			rootProgBPF.Program.ObjectFile = conf.TCRootEgressObjectFile
			rootProgBPF.Program.EntryFunctionName = conf.TCRootEgressEntryFunctionName
		}
		version := utils.ReplaceDotsWithUnderscores(rootProgBPF.Program.Version)
		rootProgBPF.MapNamePath = filepath.Join(conf.BpfMapDefaultPath, models.TCMapPinPath, ifaceName, rootProgBPF.Program.Name, version, rootProgBPF.Program.MapName)
	default:
		return nil, fmt.Errorf("unknown direction %s for root program in iface %s", direction, ifaceName)
	}

	if err := rootProgBPF.VerifyAndGetArtifacts(conf); err != nil {
		log.Error().Err(err).Msg("failed to get root artifacts")
		return nil, err
	}

	// On l3afd crashing scenario verify root program are unloaded properly by checking existence of persisted maps
	// if map file exists then root program didn't clean up pinned map files
	if fileExists(rootProgBPF.MapNamePath) {
		log.Warn().Msgf("previous instance of root program %s persisted map %s file exists", rootProgBPF.Program.Name, rootProgBPF.MapNamePath)
		if err := rootProgBPF.RemoveRootProgMapFile(ifaceName); err != nil {
			log.Warn().Err(err).Msgf("previous instance of root program %s map file not removed successfully - %s ", rootProgBPF.Program.Name, rootProgBPF.MapNamePath)
		}
	}

	version := utils.ReplaceDotsWithUnderscores(rootProgBPF.Program.Version)
	if progType == models.XDPType {
		rlimit.RemoveMemlock()
		if err := rootProgBPF.LoadXDPAttachProgram(ifaceName); err != nil {
			return 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)
		}
		// pin the program also
		progPinPath := utils.ProgPinPath(rootProgBPF.HostConfig.BpfMapDefaultPath, ifaceName, rootProgBPF.Program.Name, version, rootProgBPF.Program.EntryFunctionName, rootProgBPF.Program.ProgType)
		if err := rootProgBPF.ProgMapCollection.Programs[rootProgBPF.Program.EntryFunctionName].Pin(progPinPath); err != nil {
			return nil, err
		}
	} else {
		if utils.CheckTCXSupport() {
			if err := rootProgBPF.LoadTCXAttachProgram(ifaceName, direction); err != nil {
				return 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)
			}
		} else {
			if err := rootProgBPF.LoadTCAttachProgram(ifaceName, direction); err != nil {
				return 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)
			}
		}
		// pin the program also
		progPinPath := utils.ProgPinPath(rootProgBPF.HostConfig.BpfMapDefaultPath, ifaceName, rootProgBPF.Program.Name, version, rootProgBPF.Program.EntryFunctionName, rootProgBPF.Program.ProgType)
		if err := rootProgBPF.ProgMapCollection.Programs[rootProgBPF.Program.EntryFunctionName].Pin(progPinPath); err != nil {
			return nil, err
		}
	}

	return rootProgBPF, nil
}

// Stop the NF process if running outside l3afd
func StopExternalRunningProcess(processName string) error {
	// validate process name
	if len(processName) < 1 {
		return fmt.Errorf("process name can not be empty")
	}

	// process names are truncated to 15 chars
	psName := processName
	if len(processName) > 15 {
		psName = processName[:15]
	}

	myPid := os.Getpid()
	processList, err := ps.Processes()
	if err != nil {
		return fmt.Errorf("failed to fetch processes list with err %w", err)
	}
	log.Info().Msgf("Searching for process %s and not ppid %d", processName, myPid)
	for _, process := range processList {
		if strings.Contains(process.Executable(), psName) {
			if process.PPid() != myPid {
				log.Warn().Msgf("found process id %d name %s ppid %d, stopping it", process.Pid(), process.Executable(), process.PPid())
				osProcess, err := os.FindProcess(process.Pid())
				if err == nil {
					err = osProcess.Kill()
				}
				if err != nil {
					return fmt.Errorf("external BPFProgram stop failed with error: %w", err)
				}
			}
		}
	}
	return nil
}

// Stop returns the last error seen, but stops bpf program.
// Stops the user programs if any, and unloads the BPF program.
// Clean up all map handles.
// Verify next program pinned map file is removed
func (b *BPF) Stop(ifaceName, ipv4_address, direction string, chain bool) error {
	if b.Program.UserProgramDaemon && b.Cmd == nil {
		return fmt.Errorf("BPFProgram is not running %s", b.Program.Name)
	}

	log.Info().Msgf("Stopping BPF Program - %s", b.Program.Name)

	// Removing maps
	for key, val := range b.BpfMaps {
		log.Debug().Msgf("removing BPF maps %s value map %#v", key, val)
		delete(b.BpfMaps, key)
	}

	// Removing Metrics maps
	for key, val := range b.MetricsBpfMaps {
		log.Debug().Msgf("removing metric bpf maps %s value %#v", key, val)
		delete(b.MetricsBpfMaps, key)
	}

	// Stop BPF configs
	if len(b.Program.CmdConfig) > 0 && len(b.Program.ConfigFilePath) > 0 {
		log.Info().Msgf("Stopping BPF configs %s ", b.Program.Name)
		b.Done <- true
	}

	// Reset ProgID
	b.ProgID = 0

	stats.Add(1, stats.BPFStopCount, b.Program.Name, direction, ifaceName, ipv4_address)

	// Deleting stale metrics indicates that the ebpf program is not running.
	stats.BPFRunning.Delete(prometheus.Labels{"ebpf_program": b.Program.Name, "version": b.Program.Version, "direction": direction, "interface_name": ifaceName, "ipv4_address": ipv4_address})
	stats.BPFStartTime.Delete(prometheus.Labels{"ebpf_program": b.Program.Name, "direction": direction, "interface_name": ifaceName, "ipv4_address": ipv4_address})
	// Stop User Programs if any
	if len(b.Program.CmdStop) < 1 && b.Program.UserProgramDaemon {
		// Loaded using user program
		if err := b.ProcessTerminate(); err != nil {
			return fmt.Errorf("BPFProgram %s process terminate failed with error: %w", b.Program.Name, err)
		}
		if b.Cmd != nil {
			if err := b.Cmd.Wait(); err != nil {
				log.Error().Err(err).Msgf("cmd wait at stopping bpf program %s errored", b.Program.Name)
			}
			b.Cmd = nil
		}
	} else if len(b.Program.CmdStop) > 0 && b.Program.UserProgramDaemon {
		cmd := filepath.Join(b.FilePath, b.Program.CmdStop)

		if err := assertExecutable(cmd); err != nil {
			return fmt.Errorf("no executable permissions on %s - error %w", b.Program.CmdStop, err)
		}

		args := make([]string, 0, len(b.Program.StopArgs)<<1)
		if len(ifaceName) > 0 {
			args = append(args, "--iface="+ifaceName) // detaching from iface
		}
		if len(direction) > 0 {
			args = append(args, "--direction="+direction) // xdpingress or ingress or egress
		}
		for k, val := range b.Program.StopArgs {
			if v, ok := val.(string); !ok {
				err := fmt.Errorf("stop args is not a string for the bpf program %s", b.Program.Name)
				log.Error().Err(err).Msgf("failed to convert stop args value into string for program %s", b.Program.Name)
				return err
			} else {
				args = append(args, "--"+k+" ="+v)
			}
		}

		log.Info().Msgf("bpf program stop command : %s %v", cmd, args)
		prog := execCommand(cmd, args...)
		if err := prog.Run(); err != nil {
			log.Warn().Err(err).Msgf("l3afd : Failed to stop the program %s", b.Program.CmdStop)
		}
		b.Cmd = nil
	}

	// unload the BPF programs
	if allInterfaces, err := getHostInterfaces(); err != nil {
		errOut := fmt.Errorf("failed get interfaces in Stop Function: %v", err)
		log.Error().Err(errOut)
		return errOut
	} else {
		if _, ok := allInterfaces[ifaceName]; ok {
			if b.ProgMapCollection != nil {
				if err := b.UnloadProgram(ifaceName, direction); err != nil {
					return fmt.Errorf("BPFProgram %s unload failed on interface %s with error: %w", b.Program.Name, ifaceName, err)
				}
				log.Info().Msgf("%s => %s direction => %s - program is unloaded/detached successfully", ifaceName, b.Program.Name, direction)
			}
		} else {
			if err := b.RemovePinnedFiles(ifaceName); err != nil {
				log.Error().Err(err).Msgf("stop user program - failed to remove map files %s", b.Program.Name)
				return fmt.Errorf("stop user program - failed to remove map files %s", b.Program.Name)
			}
		}
	}

	if err := b.VerifyCleanupMaps(chain); err != nil {
		log.Error().Err(err).Msgf("stop user program - failed to remove map files %s", b.Program.Name)
		return fmt.Errorf("stop user program - failed to remove map files %s", b.Program.Name)
	}

	return nil
}

// Start returns the last error seen, but starts bpf program.
// Here initially prevprogmap entry is removed and passed to the bpf program
// After starting the user program, will update the kernel progam fd into prevprogram map.
// This method waits till prog fd entry is updated, else returns error assuming kernel program is not loaded.
// It also verifies the next program pinned map is created or not.
func (b *BPF) Start(ifaceName, ipv4_address, direction string, chain bool) error {
	if b.FilePath == "" {
		return errors.New("no program binary path found")
	}

	if len(b.Program.CmdStart) > 0 {
		// Verify other instance is running
		if err := StopExternalRunningProcess(b.Program.CmdStart); err != nil {
			return fmt.Errorf("failed to stop external instance of the program %s with error : %w", b.Program.CmdStart, err)
		}
	}

	// Making sure old map entry is removed before passing the prog fd map to the program.
	if len(b.PrevMapNamePath) > 0 {
		if err := b.RemovePrevProgFD(); err != nil {
			log.Error().Err(err).Msgf("ProgramMap %s entry removal failed", b.PrevMapNamePath)
		}
	}

	// Both xdp and tc are loaded using the same mechanism.
	if len(b.Program.ObjectFile) > 0 {
		if chain {
			if err := b.LoadBPFProgramChain(ifaceName, direction); err != nil {
				return fmt.Errorf("loading bpf program %s - error %w", b.Program.Name, err)
			}
		} else {
			if err := b.AttachBPFProgram(ifaceName, direction); err != nil {
				return fmt.Errorf("attaching bpf program %s - error %w", b.Program.Name, err)
			}
		}
	} else {
		log.Info().Msgf("bpf program object file is not defined - %s", b.Program.Name)
	}

	log.Info().Msgf("successfully Loaded & Attached %s in direction %s on interface %s", b.Program.Name, direction, ifaceName)
	// Start user program before loading
	if len(b.Program.CmdStart) > 0 {
		if err := b.StartUserProgram(ifaceName, direction, chain); err != nil {
			return fmt.Errorf("user program startup failed %s - error %w", b.Program.CmdStart, err)
		}
	}

	// making sure program fd map pinned file is created
	if err := b.VerifyPinnedProgMap(chain, true); err != nil {
		return fmt.Errorf("failed to find pinned file %s  %w", b.MapNamePath, err)
	}

	// BPF map config values
	if len(b.Program.MapArgs) > 0 {
		if err := b.UpdateBPFMaps(ifaceName, ipv4_address, direction); err != nil {
			log.Error().Err(err).Msg("failed to update ebpf program BPF maps")
			return fmt.Errorf("failed to update ebpf program BPF maps %w", err)
		}
	}

	// Update args config values
	if len(b.Program.UpdateArgs) > 0 {
		if err := b.UpdateArgs(ifaceName, ipv4_address, direction); err != nil {
			log.Error().Err(err).Msg("failed to update ebpf program config update")
			return fmt.Errorf("failed to update ebpf program config update %w", err)
		}
	}

	// Fetch when prev program map is updated only when loaded using user program
	if len(b.PrevMapNamePath) > 0 && b.ProgMapCollection == nil {
		var err error
		// retry 10 times to verify entry is created
		for i := 0; i < 10; i++ {
			b.ProgID, err = b.GetProgID()
			if err == nil {
				break
			}

			log.Warn().Msg("failed to fetch the program ID, retrying after a second ... ")
			time.Sleep(1 * time.Second)
		}

		if err != nil {
			log.Error().Err(err).Msg("failed to fetch ebpf program FD")
			return fmt.Errorf("failed to fetch ebpf program FD %w", err)
		}
	}

	// BPFconfigs
	if len(b.Program.CmdConfig) > 0 && len(b.Program.ConfigFilePath) > 0 {
		log.Info().Msgf("eBPF program specific config monitoring - %s", b.Program.ConfigFilePath)
		b.Done = make(chan bool)
		go b.RunBPFConfigs()
	}

	stats.Set(float64(time.Now().Unix()), stats.BPFStartTime, b.Program.Name, direction, ifaceName, ipv4_address)

	userProgram, bpfProgram, err := b.isRunning()
	if !userProgram && !bpfProgram {
		log.Error().Err(err).Msg("eBPF program failed to start")
		return fmt.Errorf("bpf program %s failed to start %w", b.Program.Name, err)
	}

	stats.Add(1, stats.BPFStartCount, b.Program.Name, direction, ifaceName, ipv4_address)

	log.Info().Msgf("BPF program - %s started Program ID %d", b.Program.Name, uint32(b.ProgID))
	return nil
}

// UpdateBPFMaps - Update the config ebpf maps via map arguments
func (b *BPF) UpdateBPFMaps(ifaceName, ipv4_address, direction string) error {
	for _, val := range b.Program.MapArgs {
		bpfMap, ok := b.BpfMaps[val.Name]
		if !ok {
			if err := b.AddBPFMap(val.Name); err != nil {
				return err
			}
			bpfMap = b.BpfMaps[val.Name]
		}
		for _, v := range val.Args {
			log.Info().Msgf("Update map args key %v val %v", v.Key, v.Value)
			bpfMap.Update(v.Key, v.Value)
		}
		if err := bpfMap.RemoveMissingKeys(val.Args); err != nil {
			return fmt.Errorf("failed to remove missing entries of map %s with err %w", val.Name, err)
		}
	}
	stats.Add(1, stats.BPFUpdateCount, b.Program.Name, direction, ifaceName, ipv4_address)
	return nil
}

// Update config arguments using user program
func (b *BPF) UpdateArgs(ifaceName, ipv4_address, direction string) error {
	if b.FilePath == "" {
		return errors.New("update - no program binary path found")
	}

	version := utils.ReplaceDotsWithUnderscores(b.Program.Version)
	cmd := filepath.Join(b.FilePath, b.Program.CmdUpdate)
	// Validate
	if err := assertExecutable(cmd); err != nil {
		return fmt.Errorf("no executable permissions on %s - error %w", b.Program.CmdUpdate, err)
	}

	args := make([]string, 0, len(b.Program.UpdateArgs)<<1)
	args = append(args, "--iface="+ifaceName)       // attaching to interface
	args = append(args, "--direction="+direction)   // direction xdpingress or ingress or egress
	args = append(args, "--cmd="+models.UpdateType) // argument cmd to update configs
	args = append(args, "--version="+version)       // argument version of ebpf program

	if len(b.HostConfig.BPFLogDir) > 1 {
		args = append(args, "--log-dir="+b.HostConfig.BPFLogDir)
	}

	for k, val := range b.Program.UpdateArgs {
		if v, ok := val.(string); !ok {
			err := fmt.Errorf("update args is not a string for the ebpf program %s", b.Program.Name)
			log.Error().Err(err).Msgf("failed to convert update args value into string for program %s", b.Program.Name)
			return err
		} else {
			args = append(args, "--"+k+"="+v)
		}
	}

	log.Info().Msgf("BPF Program update command : %s %v", cmd, args)
	UpdateCmd := execCommand(cmd, args...)
	if err := UpdateCmd.Start(); err != nil {
		stats.Add(1, stats.BPFUpdateFailedCount, b.Program.Name, direction, ifaceName, ipv4_address)
		customerr := fmt.Errorf("failed to start : %s %v %w", cmd, args, err)
		log.Warn().Err(customerr).Msgf("user mode BPF program failed - %s", b.Program.Name)
		return customerr
	}

	if err := UpdateCmd.Wait(); err != nil {
		stats.Add(1, stats.BPFUpdateFailedCount, b.Program.Name, direction, ifaceName, ipv4_address)
		return fmt.Errorf("cmd wait at starting of bpf program returned with error %w", err)
	}

	stats.Add(1, stats.BPFUpdateCount, b.Program.Name, direction, ifaceName, ipv4_address)
	log.Info().Msgf("BPF program - %s config updated", b.Program.Name)
	return nil
}

// Status of user program is running
func (b *BPF) isRunning() (bool, bool, error) {
	userProgram := true
	var err error

	// CmdStatus should check for user and BPF program
	if len(b.Program.CmdStatus) > 1 {
		cmd := filepath.Join(b.FilePath, b.Program.CmdStatus)

		if err := assertExecutable(cmd); err != nil {
			userProgram = false
		} else {
			args := make([]string, 0, len(b.Program.StatusArgs)<<1)

			for k, val := range b.Program.StatusArgs {
				if v, ok := val.(string); !ok {
					err = fmt.Errorf("status args is not a string for the ebpf program %s", b.Program.Name)
					log.Warn().Err(err).Msgf("failed to convert status args value into string for program %s", b.Program.Name)
				} else {
					args = append(args, "--"+k+" ="+v)
				}
			}
			prog := execCommand(cmd, args...)
			var out bytes.Buffer
			prog.Stdout = &out
			prog.Stderr = &out
			if err = prog.Run(); err != nil {
				log.Warn().Err(err).Msgf("l3afd : Failed to execute %s", b.Program.CmdStatus)
			}
			outStr, errStr := out.String(), out.String()
			if strings.EqualFold(outStr, bpfStatus) {
				userProgram = true
			} else {
				userProgram = false
				log.Warn().Msgf("bpf program not running error - %s", errStr)
			}
		}
		return userProgram, b.IsLoaded(), err
	}
	if len(b.Program.CmdStart) > 1 && b.Program.UserProgramDaemon {
		if err := b.VerifyProcessObject(); err != nil {
			userProgram = false
			log.Warn().Err(err).Msgf("process object is not created for command start - %s", b.Program.CmdStart)
		} else {
			userProgram, err = IsProcessRunning(b.Cmd.Process.Pid, b.Program.Name)
			if err != nil {
				log.Warn().Err(err).Msgf("failed to execute command status - %s user program %v", b.Program.CmdStart, userProgram)
			} else {
				userProgram = true
			}
		}
	}

	return userProgram, b.IsLoaded(), err
}

// VerifyAndGetArtifacts -Check binary already exists
func (b *BPF) VerifyAndGetArtifacts(conf *config.Config) error {

	fPath := filepath.Join(conf.BPFDir, b.Program.Name, b.Program.Version, strings.Split(b.Program.Artifact, ".")[0])
	if _, err := os.Stat(fPath); os.IsNotExist(err) {
		return b.GetArtifacts(conf)
	}

	b.FilePath = fPath
	return nil
}

// GetArtifacts downloads artifacts from the specified eBPF repo
func (b *BPF) GetArtifacts(conf *config.Config) error {

	buf := &bytes.Buffer{}
	isDefaultURLUsed := false
	platform, err := GetPlatform()
	if err != nil {
		return fmt.Errorf("failed to identify platform type: %w", err)
	}

	RepoURL := b.Program.EPRURL
	if len(b.Program.EPRURL) == 0 {
		RepoURL = conf.EBPFRepoURL
		isDefaultURLUsed = true
	}

	_, err = url.Parse(RepoURL)
	if err != nil {
		if isDefaultURLUsed {
			return fmt.Errorf("unknown ebpf-repo format : %w", err)
		} else {
			return fmt.Errorf("unknown ebpf_package_repo_url format : %w", err)
		}
	}

	urlpath, err := url.JoinPath(RepoURL, b.Program.Name, b.Program.Version, platform, b.Program.Artifact)
	if err != nil {
		return fmt.Errorf("not able to join the artifact path %w", err)
	}
	log.Info().Msgf("Retrieving artifact - %s", urlpath)
	err = DownloadArtifact(urlpath, conf.HttpClientTimeout, buf)
	if err != nil {
		return err
	}
	tempDir := filepath.Join(conf.BPFDir, b.Program.Name, b.Program.Version)
	err = ExtractArtifact(b.Program.Artifact, buf, tempDir)
	if err != nil {
		return fmt.Errorf("unable to extract artifact %w", err)
	}
	newDir := strings.Split(b.Program.Artifact, ".")
	b.FilePath = filepath.Join(tempDir, newDir[0])
	return nil
}

// create rules file
func (b *BPF) createUpdateRulesFile(direction string) (string, error) {

	if len(b.Program.RulesFile) < 1 {
		return "", fmt.Errorf("RulesFile name is empty")
	}

	fileName := path.Join(b.FilePath, direction, b.Program.RulesFile)

	if err := os.WriteFile(fileName, []byte(b.Program.Rules), 0644); err != nil {
		return "", fmt.Errorf("create or Update Rules File failed with error %w", err)
	}

	return fileName, nil

}

// fileExists checks if a file exists or not
func fileExists(filename string) bool {
	info, err := os.Stat(filename)
	if os.IsNotExist(err) {
		return false
	}
	return err == nil && !info.IsDir()
}

// Add eBPF map into BPFMaps list
func (b *BPF) AddBPFMap(mapName string) error {
	bpfMap, err := b.GetBPFMap(mapName)

	if err != nil {
		return err
	}

	b.BpfMaps[mapName] = *bpfMap
	return nil
}

func (b *BPF) GetBPFMap(mapName string) (*BPFMap, error) {

	if b.ProgMapCollection == nil {
		return nil, fmt.Errorf("no handle to prog map collection")
	}
	ebpfMap, ok := b.ProgMapCollection.Maps[mapName]
	if !ok {
		return nil, fmt.Errorf("not found")
	}

	ebpfInfo, err := ebpfMap.Info()
	if err != nil {
		return nil, fmt.Errorf("fetching map info failed %w", err)
	}

	tempMapID, ok := ebpfInfo.ID()
	if !ok {
		return nil, fmt.Errorf("fetching map id failed %w", err)
	}

	newBPFMap := BPFMap{
		Name:    mapName,
		MapID:   tempMapID,
		Type:    ebpfInfo.Type,
		BPFProg: b,
	}

	log.Info().Msgf("added mapID %d Name %s Type %s", newBPFMap.MapID, newBPFMap.Name, newBPFMap.Type)
	return &newBPFMap, nil
}

// Add eBPF map into BPFMaps list
func (b *BPF) AddMetricsBPFMap(mapName, aggregator string, key, samplesLength int) error {
	var tmpMetricsBPFMap MetricsBPFMap
	bpfMap, err := b.GetBPFMap(mapName)
	if err != nil {
		return fmt.Errorf("program %s metrics map %s not found : %w", b.Program.Name, mapName, err)
	}

	tmpMetricsBPFMap.BPFMap = *bpfMap
	tmpMetricsBPFMap.Key = key
	tmpMetricsBPFMap.Aggregator = aggregator
	tmpMetricsBPFMap.Values = ring.New(samplesLength)

	log.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)
	map_key := mapName + strconv.Itoa(key) + aggregator
	b.MetricsBpfMaps[map_key] = &tmpMetricsBPFMap

	return nil
}

// This method to fetch values from bpf maps and publish to metrics
func (b *BPF) MonitorMaps(ifaceName, ipv4_address string, intervals int) error {
	for _, element := range b.Program.MonitorMaps {
		log.Debug().Msgf("monitor maps element %s key %d aggregator %s", element.Name, element.Key, element.Aggregator)
		mapKey := element.Name + strconv.Itoa(element.Key) + element.Aggregator
		_, ok := b.MetricsBpfMaps[mapKey]
		if !ok {
			if err := b.AddMetricsBPFMap(element.Name, element.Aggregator, element.Key, intervals); err != nil {
				return fmt.Errorf("unable to fetch map %s key %d aggregator %s : %w", element.Name, element.Key, element.Aggregator, err)
			}
		}
		bpfMap := b.MetricsBpfMaps[mapKey]
		MetricName := element.Name + "_" + strconv.Itoa(element.Key) + "_" + element.Aggregator
		stats.SetValue(bpfMap.GetValue(), stats.BPFMonitorMap, b.Program.Name, MetricName, ifaceName, ipv4_address)
	}
	return nil
}

// Updating next program FD from program ID
func (b *BPF) PutNextProgFDFromID(progID int) error {
	if len(b.Program.MapName) == 0 {
		// no chaining map
		return nil
	}

	log.Info().Msgf("PutNextProgFDFromID : Map Name %s ID %d", b.Program.MapName, progID)
	ebpfMap, err := ebpf.NewMapFromID(b.ProgMapID)
	if err != nil {
		return fmt.Errorf("unable to access pinned next prog map %s %w", b.Program.MapName, err)
	}
	defer ebpfMap.Close()

	bpfProg, err := ebpf.NewProgramFromID(ebpf.ProgramID(progID))
	if err != nil {
		return fmt.Errorf("failed to get next prog FD from ID for program %s %w", b.Program.Name, err)
	}
	key := 0
	fd := bpfProg.FD()
	log.Info().Msgf("PutNextProgFDFromID : Map Name %s FD %d", b.Program.MapName, fd)
	if err = ebpfMap.Update(unsafe.Pointer(&key), unsafe.Pointer(&fd), 0); err != nil {
		return fmt.Errorf("unable to update prog next map %s %w", b.Program.MapName, err)
	}
	return nil
}

// GetProgID - This returns ID of the bpf program
func (b *BPF) GetProgID() (ebpf.ProgramID, error) {

	ebpfMap, err := ebpf.LoadPinnedMap(b.PrevMapNamePath, &ebpf.LoadPinOptions{ReadOnly: true})
	if err != nil {
		log.Error().Err(err).Msgf("unable to access pinned prog map %s", b.PrevMapNamePath)
		return 0, fmt.Errorf("unable to access pinned prog map %s %w", b.PrevMapNamePath, err)
	}
	defer ebpfMap.Close()
	var value ebpf.ProgramID
	key := 0

	if err = ebpfMap.Lookup(unsafe.Pointer(&key), unsafe.Pointer(&value)); err != nil {
		log.Warn().Err(err).Msgf("unable to look up prog map %s", b.PrevMapNamePath)
		return 0, fmt.Errorf("unable to look up prog map %w", err)
	}

	// verify progID before storing in locally.
	_, err = ebpf.NewProgramFromID(ebpf.ProgramID(value))
	if err != nil {
		log.Warn().Err(err).Msgf("failed to verify program ID %s", b.PrevMapNamePath)
		return 0, fmt.Errorf("failed to verify program ID %s %w", b.Program.Name, err)
	}

	log.Info().Msgf("GetProgID - Name %s PrevMapName %s ID %d", b.Program.Name, b.PrevMapNamePath, value)
	return value, nil
}

// RemoveNextProgFD Delete the entry if its last program in the chain.
// This method is called when sequence of the program changed to last in the chain
func (b *BPF) RemoveNextProgFD() error {
	if len(b.Program.MapName) == 0 {
		// no chaining map in case of root programs
		return nil
	}

	ebpfMap, err := ebpf.NewMapFromID(b.PrevProgMapID)
	if err != nil {
		return fmt.Errorf("unable to access pinned next prog map %s %w", b.Program.MapName, err)
	}
	defer ebpfMap.Close()
	key := 0

	if err := ebpfMap.Delete(unsafe.Pointer(&key)); err != nil {
		return fmt.Errorf("failed to delete prog fd entry : %w", err)
	}
	return nil
}

// RemovePrevProgFD Delete the entry if the last element
func (b *BPF) RemovePrevProgFD() error {
	ebpfMap, err := ebpf.NewMapFromID(b.PrevProgMapID)
	if err != nil {
		return fmt.Errorf("unable to access pinned prev prog map %s %w", b.PrevMapNamePath, err)
	}
	defer ebpfMap.Close()
	key := 0

	if err := ebpfMap.Delete(unsafe.Pointer(&key)); err != nil {
		// Some cases map may be empty ignore it.
		log.Debug().Err(err).Msg("RemovePrevProgFD failed")
	}
	return nil
}

// VerifyPinnedProgMap - making sure program fd map's pinned file is created if exists flag is true
func (b *BPF) VerifyPinnedProgMap(chain, exists bool) error {
	if !chain {
		return nil
	}
	var err error
	if len(b.Program.MapName) > 0 {
		log.Debug().Msgf("VerifyPinnedMap : Program %s MapName %s Exists %t", b.Program.Name, b.Program.MapName, exists)

		for i := 0; i < 10; i++ {
			_, err = os.Stat(b.MapNamePath)
			if err == nil && exists {
				log.Info().Msgf("VerifyPinnedProgMap creation : map file created %s", b.MapNamePath)
				return nil
			} else if err != nil && !exists {
				if _, err = os.Stat(b.MapNamePath); os.IsNotExist(err) {
					log.Info().Msgf("VerifyPinnedProgMap removal : map file removed successfully - %s ", b.MapNamePath)
					return nil
				} else if err != nil {
					log.Warn().Err(err).Msg("VerifyPinnedProgMap removal : Error checking for map file")
				} else {
					log.Warn().Msg("VerifyPinnedProgMap removal : program pinned file still exists, checking again after a second")
				}
			}
			time.Sleep(1 * time.Second)
		}

		if err != nil && exists {
			err = fmt.Errorf("VerifyPinnedProgMap creation : failed to find pinned file %s err %w", b.MapNamePath, err)
			log.Error().Err(err).Msg("")
		} else if err != nil {
			err = fmt.Errorf("VerifyPinnedProgMap removal : %s map file was never removed by BPF program %s err %w", b.MapNamePath, b.Program.Name, err)
			log.Error().Err(err).Msg("")
		}
		return err
	}

	return nil
}

// VerifyProcessObject - This method to verify cmd and process object is populated or not
func (b *BPF) VerifyProcessObject() error {

	if b.Cmd == nil {
		err := fmt.Errorf("command object is nil - %s", b.Program.Name)
		log.Error().Err(err).Msg("command object is nil -")
		return err
	}

	for i := 0; i < 10; i++ {
		if b.Cmd.Process != nil {
			return nil
		}
		log.Warn().Msgf("VerifyProcessObject: process object not found, checking again after a second")
		time.Sleep(1 * time.Second)
	}
	err := fmt.Errorf("process object is nil - %s", b.Program.Name)
	log.Error().Err(err).Msg("")
	return err
}

// VerifyMetricsMapsVanish - checks for all metrics maps references are removed from the kernel
func (b *BPF) VerifyMetricsMapsVanish() error {

	for i := 0; i < 10; i++ {
		mapExists := false
		for _, v := range b.BpfMaps {
			_, err := ebpf.NewMapFromID(v.MapID)
			if err == nil {
				log.Warn().Msgf("VerifyMetricsMapsVanish: bpf map reference still exists - %s", v.Name)
				mapExists = true
			}
		}
		if !mapExists {
			return nil
		}

		log.Warn().Msgf("VerifyMetricsMapsVanish: bpf map reference still exists - %s, checking again after a second", b.Program.Name)
		time.Sleep(1 * time.Second)
	}

	err := fmt.Errorf("metrics maps are never removed by Kernel %s", b.Program.Name)
	log.Error().Err(err).Msg("")
	return err
}

// UnloadProgram - Unload or detach the program from the interface and close all the program resources
func (b *BPF) UnloadProgram(ifaceName, direction string) error {
	// remove pinned files
	if err := b.RemovePinnedFiles(ifaceName); err != nil {
		log.Error().Err(err).Msgf("failed to remove map file for program %s => %s", ifaceName, b.Program.Name)
	}
	// Verifying program attached to the interface.
	// SeqID will be 0 for root program or any other program without chaining
	if b.Program.SeqID == 0 || !b.HostConfig.BpfChainingEnabled {
		if b.Program.ProgType == models.TCType {
			if utils.CheckTCXSupport() {
				if b.Link != nil {
					if err := b.Link.Close(); err != nil {
						log.Warn().Msgf("removing tc attached program %s failed iface %q direction %s error - %v", b.Program.Name, ifaceName, direction, err)
					}
				} else {
					log.Warn().Msgf("attach program %s link file is missing iface %q direction %s", b.Program.Name, ifaceName, direction)
				}
			} else {
				if err := b.UnloadTCProgram(ifaceName, direction); err != nil {
					log.Warn().Msgf("removing tc filter failed iface %q direction %s error - %v", ifaceName, direction, err)
				}
			}
		} else if b.Program.ProgType == models.XDPType {
			if err := b.Link.Close(); err != nil {
				log.Warn().Msgf("removing xdp attached program failed iface %q direction %s error - %v", ifaceName, direction, err)
			}
		}
	}

	for _, LinkObject := range b.ProbeLinks {
		(*LinkObject).Close()
	}

	// Release all the resources of the epbf program
	if b.ProgMapCollection != nil {
		b.ProgMapCollection.Close()
	}
	return nil
}

// RemovePinnedFiles - removes all the pinned files
func (b *BPF) RemovePinnedFiles(ifaceName string) error {
	if b.ProgMapCollection != nil {
		for k, v := range b.ProgMapCollection.Maps {
			var mapFilename string
			version := utils.ReplaceDotsWithUnderscores(b.Program.Version)
			if b.Program.ProgType == models.TCType {
				mapFilename = filepath.Join(b.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, ifaceName, b.Program.Name, version, k)
			} else {
				mapFilename = filepath.Join(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, k)
			}
			if err := v.Unpin(); err != nil {
				return fmt.Errorf("BPF program %s prog type %s ifacename %s map %s:failed to pin the map err - %w",
					b.Program.Name, b.Program.ProgType, ifaceName, mapFilename, err)
			}
		}
	}
	// remove pinned links
	if b.Link != nil {
		if err := b.Link.Unpin(); err != nil {
			return fmt.Errorf("unable to unpin the xdp link for %s with err : %w", b.Program.Name, err)
		}
	}

	// remove programs pins
	if b.ProgMapCollection != nil {
		for _, v := range b.ProgMapCollection.Programs {
			if err := v.Unpin(); err != nil {
				return err
			}
		}
	}
	return nil
}

// RemoveRootProgMapFile - removes root pinned prog map file
// This is invoked if any stale map file persists for root map
func (b *BPF) RemoveRootProgMapFile(ifacename string) error {
	var mapFilename string
	version := utils.ReplaceDotsWithUnderscores(b.Program.Version)
	switch b.Program.ProgType {
	case models.TCType:
		mapFilename = filepath.Join(b.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, ifacename, b.Program.Name, version, b.Program.MapName)
	case models.XDPType:
		mapFilename = filepath.Join(b.HostConfig.BpfMapDefaultPath, ifacename, b.Program.Name, version, b.Program.MapName)
	default:
		log.Warn().Msgf("RemoveRootProgMapFile: program %s map file %s - unknown type", b.Program.Name, b.MapNamePath)
		return fmt.Errorf("removeMapFile: program %s unknown type %s", b.Program.Name, b.Program.ProgType)
	}

	// codeQL Check
	if strings.Contains(mapFilename, "..") {
		return fmt.Errorf("%s contains relative path is not supported - %s", mapFilename, b.Program.Name)
	}

	if err := os.Remove(mapFilename); err != nil {
		if !os.IsNotExist(err) {
			log.Warn().Msgf("RemoveRootProgMapFile: %s program type %s map file remove unsuccessfully - %s err - %#v", b.Program.ProgType, b.Program.Name, mapFilename, err)
			return fmt.Errorf("%s - remove failed with error %w", mapFilename, err)
		}
	}
	return nil
}

// VerifyCleanupMaps - This method verifies map entries in the fs is removed
func (b *BPF) VerifyCleanupMaps(chain bool) error {
	// verify pinned file is removed.
	if err := b.VerifyPinnedProgMap(chain, false); err != nil {
		log.Error().Err(err).Msgf("stop user program - failed to remove pinned file %s", b.Program.Name)
		return fmt.Errorf("stop user program - failed to remove pinned file %s : %w", b.Program.Name, err)
	}

	// Verify all metrics map references are removed from kernel
	if err := b.VerifyMetricsMapsVanish(); err != nil {
		log.Error().Err(err).Msgf("stop user program - failed to remove metric map references %s", b.Program.Name)
		return fmt.Errorf("stop user program - failed to remove metric map references %s : %w", b.Program.Name, err)
	}

	return nil
}

// LoadBPFProgram - This method loads the eBPF program natively.
func (b *BPF) LoadBPFProgram(ifaceName string) error {
	ObjectFile := filepath.Join(b.FilePath, b.Program.ObjectFile)
	if _, err := os.Stat(ObjectFile); os.IsNotExist(err) {
		return fmt.Errorf("%s: file doesn't exist", ObjectFile)
	}

	// Allow the current process to lock memory for eBPF resources.
	if err := rlimit.RemoveMemlock(); err != nil {
		log.Error().Msgf("failed to remove memory lock limits  %#v", err)
		return fmt.Errorf("%s: remove rlimit lock failed : %w", b.Program.Name, err)
	}

	objSpec, err := ebpf.LoadCollectionSpec(ObjectFile)
	if err != nil {
		return fmt.Errorf("%s: loading collection spec failed - %w", ObjectFile, err)
	}

	version := utils.ReplaceDotsWithUnderscores(b.Program.Version)
	if err := b.CreatePinDirectories(ifaceName, b.Program.Name, version); err != nil {
		return err
	}
	var mapPinPath string
	if b.Program.ProgType == models.TCType {
		mapPinPath = filepath.Join(b.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, ifaceName, b.Program.Name, version)
	} else if b.Program.ProgType == models.XDPType {
		mapPinPath = filepath.Join(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version)
	}
	collOptions := ebpf.CollectionOptions{
		Maps: ebpf.MapOptions{
			PinPath: mapPinPath,
		},
	}

	// Load the BPF program with the updated map options
	prg, err := ebpf.NewCollectionWithOptions(objSpec, collOptions)
	if err != nil {
		return fmt.Errorf("%s: loading of bpf program failed - %w", b.Program.Name, err)
	}

	// Persist program handle
	b.ProgMapCollection = prg

	if err := b.LoadBPFProgramProbeTypes(objSpec); err != nil {
		return fmt.Errorf("LoadBPFProgramProbeTypes failed with error %v ", err)
	}

	//var bpfProg *ebpf.Program
	if len(b.Program.EntryFunctionName) > 0 {
		bpfProg := prg.Programs[b.Program.EntryFunctionName]
		if bpfProg == nil {
			return fmt.Errorf("%s entry function is not found in the loaded object file of the program %s", b.Program.EntryFunctionName, b.Program.Name)
		}

		progInfo, err := bpfProg.Info()
		if err != nil {
			return fmt.Errorf("%s: information of bpf program failed : %w", b.Program.Name, err)
		}

		ok := false
		b.ProgID, ok = progInfo.ID()
		if !ok {
			log.Warn().Msgf("Program ID fetch failed: %s", b.Program.Name)
		}

		// Initialise metric maps
		if err := b.InitialiseMetricMaps(); err != nil {
			return fmt.Errorf("initialising metric maps failed %w", err)
		}

		if err := b.PinBpfMaps(ifaceName); err != nil {
			return err
		}
	}
	return nil
}

// InitialiseMetricMaps - This method initialises all the monitor maps
func (b *BPF) InitialiseMetricMaps() error {
	if b.ProgMapCollection == nil {
		log.Warn().Msgf("prog is not loaded by l3afd")
		return nil
	}
	for _, tmpMap := range b.Program.MonitorMaps {
		tmpMetricsMap := b.ProgMapCollection.Maps[tmpMap.Name]
		if tmpMetricsMap == nil {
			log.Error().Msgf("%s map is not loaded", tmpMap.Name)
			continue
		}

		var err error
		log.Debug().Msgf("Program - %s map name %s key size %d value size %d\n", b.Program.Name, tmpMap.Name, tmpMetricsMap.KeySize(), tmpMetricsMap.ValueSize())
		if tmpMetricsMap.KeySize() == 1 {
			var k int8
			switch tmpMetricsMap.ValueSize() {
			case 1:
				var v int8
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			case 2:
				var v int16
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			case 4:
				var v int32
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			case 8:
				var v int64
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			default:
				log.Error().Msgf("unsupported key type int8 and value size - %d", tmpMetricsMap.ValueSize())
			}
		} else if tmpMetricsMap.KeySize() == 2 {
			var k int16
			switch tmpMetricsMap.ValueSize() {
			case 1:
				var v int8
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			case 2:
				var v int16
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			case 4:
				var v int32
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			case 8:
				var v int64
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			default:
				log.Error().Msgf("unsupported map key type int16 and value size - %d", tmpMetricsMap.ValueSize())
			}
		} else if tmpMetricsMap.KeySize() == 4 {
			var k int32
			switch tmpMetricsMap.ValueSize() {
			case 1:
				var v int8
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			case 2:
				var v int16
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			case 4:
				var v int32
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			case 8:
				var v int64
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			default:
				log.Error().Msgf("unsupported map key type int32 and value size - %d", tmpMetricsMap.ValueSize())
			}
		} else if tmpMetricsMap.KeySize() == 8 {
			var k int64
			switch tmpMetricsMap.ValueSize() {
			case 1:
				var v int8
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			case 2:
				var v int16
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			case 4:
				var v int32
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			case 8:
				var v int64
				err = tmpMetricsMap.Update(unsafe.Pointer(&k), unsafe.Pointer(&v), 0)
			default:
				log.Error().Msgf("unsupported key type int64 and value size - %d", tmpMetricsMap.ValueSize())
			}
		}
		if err != nil {
			return fmt.Errorf("update hash map element failed for map name %s error %v", tmpMap.Name, err)
		}
	}
	return nil
}

// IsLoaded - Method verifies whether bpf program is loaded or not
// Here it checks whether prog ID is valid and active
func (b *BPF) IsLoaded() bool {
	if b.ProgID == 0 {
		return false
	}
	ebpfProg, err := ebpf.NewProgramFromID(b.ProgID)
	if err != nil {
		log.Debug().Msgf("IsLoaded - %s is not loaded or invalid program id %d", b.Program.Name, uint32(b.ProgID))
		return false
	}
	defer ebpfProg.Close()
	return true
}

func (b *BPF) StartUserProgram(ifaceName, direction string, chain bool) error {
	cmd := filepath.Join(b.FilePath, b.Program.CmdStart)
	// Validate
	if err := assertExecutable(cmd); err != nil {
		return fmt.Errorf("no executable permissions on %s - error %w", b.Program.CmdStart, err)
	}

	args := make([]string, 0, len(b.Program.StartArgs)<<1)
	if len(ifaceName) > 0 {
		args = append(args, "--iface="+ifaceName) // attaching to interface
	}
	if len(direction) > 0 {
		args = append(args, "--direction="+direction) // direction xdpingress or ingress or egress
	}

	version := utils.ReplaceDotsWithUnderscores(b.Program.Version)
	if len(b.Program.Version) > 0 {
		args = append(args, "--version="+version) // version xdpingress or ingress or egress
	}

	if chain && b.ProgMapCollection == nil {
		// chaining from user program
		if len(b.PrevMapNamePath) > 1 {
			args = append(args, "--map-name="+b.PrevMapNamePath)
		}
	}

	if len(b.HostConfig.BPFLogDir) > 1 {
		args = append(args, "--log-dir="+b.HostConfig.BPFLogDir)
	}

	if len(b.Program.RulesFile) > 1 && len(b.Program.Rules) > 1 {
		fileName, err := b.createUpdateRulesFile(direction)
		if err == nil {
			args = append(args, "--rules-file="+fileName)
		}
	}

	for k, val := range b.Program.StartArgs {
		if v, ok := val.(string); !ok {
			err := fmt.Errorf("start args is not a string for the bpf program %s", b.Program.Name)
			log.Error().Err(err).Msgf("failed to convert start args value into string for program %s", b.Program.Name)
			return err
		} else {
			args = append(args, "--"+k+"="+v)
		}
	}

	log.Info().Msgf("BPF Program start command : %s %v", cmd, args)
	b.Cmd = execCommand(cmd, args...)
	if err := b.Cmd.Start(); err != nil {
		log.Info().Err(err).Msgf("user program failed - %s", b.Program.Name)
		return fmt.Errorf("failed to start : %s %v with err: %w", cmd, args, err)
	}
	if !b.Program.UserProgramDaemon {
		log.Info().Msgf("no user program - %s No Pid", b.Program.Name)
		if err := b.Cmd.Wait(); err != nil {
			log.Warn().Msgf("failed at wait - %s err %s", b.Program.Name, err.Error())
		}
		b.Cmd = nil
	} else {
		if err := b.SetPrLimits(); err != nil {
			log.Warn().Err(err).Msg("failed to set resource limits")
		}
		log.Info().Msgf("BPF program - %s User program Process id %d started", b.Program.Name, b.Cmd.Process.Pid)
	}

	return nil
}

// CreatePinDirectories - This method creates directory for ebpf objects
// TC maps are pinned to directory /sys/fs/bpf/tc/globals/<ifaceName>
// XDP maps are pinned to directory /sys/fs/bpf/<ifaceName>
// links are pinned to directory /sys/fs/bpf/links/<ifaceName>/<progName>/<progVersion>
// Program are pinned to directory  /sys/fs/bpf/progs/<ifaceName>/<progName>/<progVersion>
func (b *BPF) CreatePinDirectories(ifaceName, progName, progVersion string) error {
	var mapPathDir string
	if b.Program.ProgType == models.XDPType {
		mapPathDir = filepath.Join(b.HostConfig.BpfMapDefaultPath, ifaceName, progName, progVersion)
	} else if b.Program.ProgType == models.TCType {
		mapPathDir = filepath.Join(b.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, ifaceName, progName, progVersion)
	}
	// Create map dir for XDP and TC programs only
	if len(mapPathDir) > 0 {
		// codeQL Check
		if strings.Contains(mapPathDir, "..") {
			return fmt.Errorf("%s contains relative path is not supported - %s", mapPathDir, b.Program.Name)
		}
		if err := os.MkdirAll(mapPathDir, 0750); err != nil {
			return fmt.Errorf("%s failed to create map dir path of %s program %s with err : %w", mapPathDir, b.Program.ProgType, b.Program.Name, err)
		}
	}

	linksPathDir := filepath.Join(b.HostConfig.BpfMapDefaultPath, "links", ifaceName, progName, progVersion)
	if strings.Contains(linksPathDir, "..") {
		return fmt.Errorf("%s contains relative path is not supported - %s", linksPathDir, b.Program.Name)
	}
	if err := os.MkdirAll(linksPathDir, 0750); err != nil {
		return fmt.Errorf("%s failed to create map dir path of %s program %s with err : %w", linksPathDir, b.Program.ProgType, b.Program.Name, err)
	}

	ProgPathDir := filepath.Join(b.HostConfig.BpfMapDefaultPath, "progs", ifaceName, progName, progVersion)
	if strings.Contains(ProgPathDir, "..") {
		return fmt.Errorf("%s contains relative path is not supported - %s", ProgPathDir, b.Program.Name)
	}
	if err := os.MkdirAll(ProgPathDir, 0750); err != nil {
		return fmt.Errorf("%s failed to create map dir path of %s program %s with err : %w", ProgPathDir, b.Program.ProgType, b.Program.Name, err)
	}
	return nil
}

// AttachBPFProgram - method to attach bpf program to interface
func (b *BPF) AttachBPFProgram(ifaceName, direction string) error {
	version := utils.ReplaceDotsWithUnderscores(b.Program.Version)
	if b.Program.ProgType == models.XDPType {
		if err := b.LoadXDPAttachProgram(ifaceName); err != nil {
			return fmt.Errorf("failed to attach xdp program %s to inferface %s with err: %w", b.Program.Name, ifaceName, err)
		}
		// pin the program also
		progPinPath := utils.ProgPinPath(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, b.Program.EntryFunctionName, b.Program.ProgType)
		if err := b.ProgMapCollection.Programs[b.Program.EntryFunctionName].Pin(progPinPath); err != nil {
			return err
		}
	} else if b.Program.ProgType == models.TCType {
		if utils.CheckTCXSupport() {
			if err := b.LoadTCXAttachProgram(ifaceName, direction); err != nil {
				return fmt.Errorf("failed to attach tcx program %s to interface %s direction %s with err %w", b.Program.Name, ifaceName, direction, err)
			}
		} else {
			if err := b.LoadTCAttachProgram(ifaceName, direction); err != nil {
				return fmt.Errorf("failed to attach tc program %s to interface %s direction %s with err %w", b.Program.Name, ifaceName, direction, err)
			}
		}

		// pin the program also
		progPinPath := utils.ProgPinPath(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, b.Program.EntryFunctionName, b.Program.ProgType)
		if err := b.ProgMapCollection.Programs[b.Program.EntryFunctionName].Pin(progPinPath); err != nil {
			return err
		}
	}
	return nil
}

// PinBpfMaps - Pinning tc and xdp maps
func (b *BPF) PinBpfMaps(ifaceName string) error {
	version := utils.ReplaceDotsWithUnderscores(b.Program.Version)
	for k, v := range b.ProgMapCollection.Maps {
		var mapFilename string
		// ebpf programs temporary storage created by eBPF program skip it
		if k == ".bss" {
			continue
		}
		if b.Program.ProgType == models.TCType {
			mapFilename = filepath.Join(b.HostConfig.BpfMapDefaultPath, models.TCMapPinPath, ifaceName, b.Program.Name, version, k)
		} else {
			mapFilename = filepath.Join(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, k)
		}
		// In case one of the program pins the map then other program will skip
		if !fileExists(mapFilename) {
			if err := v.Pin(mapFilename); err != nil {
				return fmt.Errorf("eBPF program %s map %s:failed to pin the map err - %w", b.Program.Name, mapFilename, err)
			}
		}
	}
	return nil
}

// UpdateProgramMap - Store the program map reference
func (b *BPF) UpdateProgramMap(ifaceName string) error {
	// Verify chaining map is provided
	if len(b.Program.MapName) == 0 {
		return fmt.Errorf("program map name is missing for %s program %s", b.Program.ProgType, b.Program.Name)
	}

	bpfRootMap := b.ProgMapCollection.Maps[b.Program.MapName]

	ebpfInfo, err := bpfRootMap.Info()
	if err != nil {
		return fmt.Errorf("fetching map info failed for %s program %s to interface %s : %w", b.Program.ProgType, b.Program.Name, ifaceName, err)
	}

	var ok bool
	b.ProgMapID, ok = ebpfInfo.ID()
	if !ok {
		return fmt.Errorf("fetching map id failed for %s program %s to interface %s : %w", b.Program.ProgType, b.Program.Name, ifaceName, err)
	}

	return nil
}

// LoadBPFProgramChain - Load the BPF program and chain it.
func (b *BPF) LoadBPFProgramChain(ifaceName, direction string) error {

	if err := b.LoadBPFProgram(ifaceName); err != nil {
		return err
	}

	version := utils.ReplaceDotsWithUnderscores(b.Program.Version)
	// pin the program also
	progPinPath := utils.ProgPinPath(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, b.Program.EntryFunctionName, b.Program.ProgType)
	if err := b.ProgMapCollection.Programs[b.Program.EntryFunctionName].Pin(progPinPath); err != nil {
		return err
	}

	// Update program map id
	if err := b.UpdateProgramMap(ifaceName); err != nil {
		return err
	}

	// Link this program into previous program map
	ebpfMap, err := ebpf.NewMapFromID(b.PrevProgMapID)
	if err != nil {
		return fmt.Errorf("unable to access pinned previous prog map %s %w", b.PrevMapNamePath, err)
	}
	defer ebpfMap.Close()

	bpfProg := b.ProgMapCollection.Programs[b.Program.EntryFunctionName]
	if bpfProg == nil {
		return fmt.Errorf("%s entry function is not found in the loaded object file of the program %s", b.Program.EntryFunctionName, b.Program.Name)
	}

	key := 0
	fd := bpfProg.FD()
	log.Info().Msgf("previous program map path %s FD %d", b.PrevMapNamePath, fd)
	if err = ebpfMap.Update(unsafe.Pointer(&key), unsafe.Pointer(&fd), 0); err != nil {
		return fmt.Errorf("unable to update prog next map %s %v", b.Program.MapName, err)
	}
	log.Info().Msgf("eBPF program %s loaded on interface %s direction %s successfully", b.Program.Name, ifaceName, direction)
	return nil
}

func (b *BPF) StopUserProgram(ifaceName, direction string) error {
	// Stop User Programs if any
	if len(b.Program.CmdStop) < 1 && b.Program.UserProgramDaemon {
		// Loaded using user program
		if err := b.ProcessTerminate(); err != nil {
			return fmt.Errorf("BPFProgram %s process terminate failed with error: %w", b.Program.Name, err)
		}
		if b.Cmd != nil {
			if err := b.Cmd.Wait(); err != nil {
				log.Error().Err(err).Msgf("cmd wait at stopping bpf program %s errored", b.Program.Name)
			}
			b.Cmd = nil
		}
	} else if len(b.Program.CmdStop) > 0 && b.Program.UserProgramDaemon {
		cmd := filepath.Join(b.FilePath, b.Program.CmdStop)

		if err := assertExecutable(cmd); err != nil {
			return fmt.Errorf("no executable permissions on %s - error %w", b.Program.CmdStop, err)
		}

		args := make([]string, 0, len(b.Program.StopArgs)<<1)
		if len(ifaceName) > 0 {
			args = append(args, "--iface="+ifaceName) // detaching from iface
		}
		if len(direction) > 0 {
			args = append(args, "--direction="+direction) // xdpingress or ingress or egress
		}
		for k, val := range b.Program.StopArgs {
			if v, ok := val.(string); !ok {
				err := fmt.Errorf("stop args is not a string for the bpf program %s", b.Program.Name)
				log.Error().Err(err).Msgf("failed to convert stop args value into string for program %s", b.Program.Name)
				return err
			} else {
				args = append(args, "--"+k+" ="+v)
			}
		}
		log.Info().Msgf("bpf user program stop command : %s %v", cmd, args)
		prog := execCommand(cmd, args...)
		if err := prog.Run(); err != nil {
			log.Warn().Err(err).Msgf("l3afd : Failed to stop the user program %s", b.Program.CmdStop)
		}
		b.Cmd = nil
	}
	return nil
}

// DownloadArtifact will download artifact from provided urlpath and store it in buffer
func DownloadArtifact(urlpath string, timeout time.Duration, buf *bytes.Buffer) error {
	URL, err := url.Parse(urlpath)
	if err != nil {
		return fmt.Errorf("unknown url format : %w", err)
	}
	switch URL.Scheme {
	case models.HttpScheme, models.HttpsScheme:
		{
			timeOut := time.Duration(timeout) * time.Second
			var netTransport = &http.Transport{
				ResponseHeaderTimeout: timeOut,
			}
			client := http.Client{Transport: netTransport, Timeout: timeOut}
			// Get the data
			resp, err := client.Get(URL.String())
			if err != nil {
				return fmt.Errorf("download failed: %w", err)
			}
			defer resp.Body.Close()

			if resp.StatusCode != http.StatusOK {
				return 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())
			}
			buf.ReadFrom(resp.Body)
			return nil
		}
	case models.FileScheme:
		{
			if fileExists(URL.Path) {
				f, err := os.Open(URL.Path)
				if err != nil {
					return fmt.Errorf("opening err : %w", err)
				}
				buf.ReadFrom(f)
				f.Close()
			} else {
				return fmt.Errorf("artifact is not found")
			}
			return nil
		}
	default:
		return fmt.Errorf("unknown url scheme")
	}
}

// ExtractArtifact will extract artifact to given tempDir
func ExtractArtifact(artifactName string, buf *bytes.Buffer, tempDir string) error {
	switch artifact := artifactName; {
	case strings.HasSuffix(artifact, ".zip"):
		{
			c := bytes.NewReader(buf.Bytes())
			zipReader, err := zip.NewReader(c, int64(c.Len()))
			if err != nil {
				return fmt.Errorf("failed to create zip reader: %w", err)
			}
			for _, file := range zipReader.File {

				zippedFile, err := file.Open()
				if err != nil {
					return fmt.Errorf("unzip failed: %w", err)
				}
				defer zippedFile.Close()

				extractedFilePath, err := ValidatePath(file.Name, tempDir)
				if err != nil {
					return err
				}

				if file.FileInfo().IsDir() {
					os.MkdirAll(extractedFilePath, file.Mode())
				} else {
					outputFile, err := os.OpenFile(
						extractedFilePath,
						os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
						file.Mode(),
					)
					if err != nil {
						return fmt.Errorf("unzip failed to create file: %w", err)
					}
					defer outputFile.Close()

					buf := copyBufPool.Get().(*bytes.Buffer)
					_, err = io.CopyBuffer(outputFile, zippedFile, buf.Bytes())
					if err != nil {
						return fmt.Errorf("GetArtifacts failed to copy files: %w", err)
					}
					copyBufPool.Put(buf)
				}
			}
			return nil
		}
	case strings.HasSuffix(artifact, ".tar.gz"):
		{
			archive, err := gzip.NewReader(buf)
			if err != nil {
				return fmt.Errorf("failed to create Gzip reader: %w", err)
			}
			defer archive.Close()
			tarReader := tar.NewReader(archive)

			for {
				header, err := tarReader.Next()

				if err == io.EOF {
					break
				} else if err != nil {
					return fmt.Errorf("untar failed: %w", err)
				}

				fPath, err := ValidatePath(header.Name, tempDir)
				if err != nil {
					return err
				}

				info := header.FileInfo()
				if info.IsDir() {
					if err = os.MkdirAll(fPath, info.Mode()); err != nil {
						return fmt.Errorf("untar failed to create directories: %w", err)
					}
					continue
				}

				file, err := os.OpenFile(fPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
				if err != nil {
					return fmt.Errorf("untar failed to create file: %w", err)
				}
				defer file.Close()

				buf := copyBufPool.Get().(*bytes.Buffer)
				_, err = io.CopyBuffer(file, tarReader, buf.Bytes())
				if err != nil {
					return fmt.Errorf("GetArtifacts failed to copy files: %w", err)
				}
				copyBufPool.Put(buf)
			}
			return nil
		}
	default:
		return fmt.Errorf("unknown artifact format")
	}
}

// ValidatePath will validate any illegal file path
func ValidatePath(filePath string, destination string) (string, error) {
	destpath := filepath.Join(destination, filePath)
	if strings.Contains(filePath, "..") {
		return "", fmt.Errorf(" file contains filepath (%s) that includes (..)", filePath)
	}
	if !strings.HasPrefix(destpath, filepath.Clean(destination)+string(os.PathSeparator)) {
		return "", fmt.Errorf("%s: illegal file path", filePath)
	}
	return destpath, nil
}


================================================
FILE: bpfprogs/bpfCfgs_internal.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0
//
//go:build !configs
// +build !configs

// This file is used for walmart internal to run BPF specific configs.
// We will be removing this file in future.

package bpfprogs

import (
	"github.com/rs/zerolog/log"
)

func (b *BPF) RunBPFConfigs() error {
	log.Warn().Msg("Implement custom BPF specific configs")
	return nil
}


================================================
FILE: bpfprogs/bpf_test.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package bpfprogs

import (
	"archive/tar"
	"archive/zip"
	"bytes"
	"compress/gzip"
	"context"
	"io"
	"net/http"
	"os"
	"os/exec"
	"reflect"
	"runtime"
	"strings"
	"testing"

	"github.com/l3af-project/l3afd/v2/config"
	"github.com/l3af-project/l3afd/v2/mocks"
	"github.com/l3af-project/l3afd/v2/models"
	"github.com/l3af-project/l3afd/v2/stats"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/rs/zerolog/log"
	"go.uber.org/mock/gomock"
)

var mockedExitStatus = 1
var mockPid = 77

func fakeExecCommand(command string, args ...string) *exec.Cmd {
	cs := []string{"-test.run=TestHelperProcess", "--", command}
	cs = append(cs, args...)
	cmd := exec.Command(os.Args[0], cs...)
	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
	cmd.Process = &os.Process{Pid: mockPid}
	return cmd
}

func TestHelperProcess(t *testing.T) {
	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
		return
	}
	os.Exit(mockedExitStatus)
}

func TestNewBpfProgram(t *testing.T) {
	type args struct {
		program    models.BPFProgram
		logDir     string
		chain      bool
		direction  string
		ctx        context.Context
		datacenter string
		hostConfig *config.Config
	}
	execCommand = fakeExecCommand
	defer func() { execCommand = exec.Command }()
	tests := []struct {
		name string
		args args
		want *BPF
	}{
		{name: "GoodInput",
			args: args{
				program: models.BPFProgram{
					Name:              "nfprogram",
					Artifact:          "foo.tar.gz",
					CmdStart:          "foo",
					CmdStop:           "",
					CmdConfig:         "",
					Version:           "1.0",
					UserProgramDaemon: true,
					IsPlugin:          false,
					AdminStatus:       "enabled",
					ProgType:          "tc",
				},
				logDir:     "",
				chain:      false,
				direction:  "ingress",
				datacenter: "localdc",
				hostConfig: &config.Config{
					BPFLogDir:  "",
					DataCenter: "localdc",
				},
			},
			want: &BPF{
				Program: models.BPFProgram{
					Name:              "nfprogram",
					Artifact:          "foo.tar.gz",
					CmdStart:          "foo",
					CmdStop:           "",
					CmdConfig:         "",
					CmdUpdate:         "",
					Version:           "1.0",
					UserProgramDaemon: true,
					IsPlugin:          false,
					AdminStatus:       "enabled",
					ProgType:          "tc",
				},
				Cmd:            nil,
				FilePath:       "",
				BpfMaps:        make(map[string]BPFMap, 0),
				MetricsBpfMaps: make(map[string]*MetricsBPFMap, 0),
				Ctx:            nil,
				Done:           nil,
				HostConfig: &config.Config{
					BPFLogDir:  "",
					DataCenter: "localdc",
				},
			},
		},
		{name: "EmptyBPFProgram",
			args: args{
				program:    models.BPFProgram{},
				hostConfig: &config.Config{},
			},
			want: &BPF{
				Program:        models.BPFProgram{},
				Cmd:            nil,
				FilePath:       "",
				BpfMaps:        make(map[string]BPFMap, 0),
				MetricsBpfMaps: make(map[string]*MetricsBPFMap, 0),
				HostConfig:     &config.Config{},
			},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := NewBpfProgram(tt.args.ctx, tt.args.program, tt.args.hostConfig, ifaceName); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("NewBpfProgram() = %#v, want %#v", got, tt.want)
			}
		})
	}
}

func TestBPF_Stop(t *testing.T) {
	type fields struct {
		Program      models.BPFProgram
		Cmd          *exec.Cmd
		FilePath     string
		RestartCount int
		Direction    string
	}
	tests := []struct {
		name    string
		fields  fields
		wantErr bool
	}{
		{name: "NilCmd",
			fields: fields{
				Program: models.BPFProgram{
					Name:              "nfprogram",
					Artifact:          "foo.tar.gz",
					CmdStart:          "foo",
					CmdStop:           "",
					Version:           "1.0",
					UserProgramDaemon: true,
					AdminStatus:       "enabled",
				},
				Cmd:          nil,
				FilePath:     "/tmp/dummy/dummy",
				RestartCount: 3,
			},
			wantErr: true,
		},
		{name: "WithStopCmd",
			fields: fields{
				Program: models.BPFProgram{
					Name:              "nfprogram",
					Artifact:          "foo.tar.gz",
					CmdStart:          "foo",
					CmdStop:           "foo",
					Version:           "1.0",
					UserProgramDaemon: true,
					AdminStatus:       "enabled",
				},
				Cmd:          fakeExecCommand("/tmp/dummy/foo"),
				FilePath:     "/tmp/dummy/dummy",
				RestartCount: 3,
			},
			wantErr: true,
		},
		{name: "AnyBinaryFile",
			fields: fields{
				Program: models.BPFProgram{
					Name:              "nfprogram",
					Artifact:          "data.tar.gz",
					CmdStart:          GetTestExecutableName(),
					CmdStop:           GetTestExecutableName(),
					UserProgramDaemon: false,
					AdminStatus:       "enabled",
				},
				Cmd:          fakeExecCommand(GetTestExecutablePathName()),
				FilePath:     GetTestExecutablePath(),
				RestartCount: 3,
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b := &BPF{
				Program:      tt.fields.Program,
				Cmd:          tt.fields.Cmd,
				FilePath:     tt.fields.FilePath,
				RestartCount: tt.fields.RestartCount,
			}

			// Create the metric with expected labels
			stats.BPFRunning = prometheus.NewGaugeVec(
				prometheus.GaugeOpts{
					Name: "l3afd_BPFRunning",
					Help: "Indicates whether eBPF program is running",
				},
				[]string{"program", "version", "direction", "iface"},
			)

			if err := prometheus.Register(stats.BPFRunning); err != nil {
				log.Warn().Err(err).Msg("Failed to register BPFRunning metrics")
			}

			// Simulate program load
			stats.BPFRunning.With(prometheus.Labels{
				"program":   b.Program.Name,
				"version":   b.Program.Version,
				"direction": "ingress",
				"iface":     "eth0",
			}).Set(1)

			stats.BPFStartTime = prometheus.NewGaugeVec(
				prometheus.GaugeOpts{
					Namespace: "l3afd",
					Name:      "BPFStartTime",
					Help:      "This value indicates start time of the BPF program since unix epoch in seconds",
				},
				[]string{"host", "ebpf_program", "direction", "interface_name"},
			)

			if err := prometheus.Register(stats.BPFStartTime); err != nil {
				log.Warn().Err(err).Msg("Failed to register BPFStartTime metrics")
			}

			if err := b.Stop(ifaceName, "", models.IngressType, false); (err != nil) != tt.wantErr {
				t.Errorf("BPF.Stop() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestBPF_Start(t *testing.T) {
	type fields struct {
		Program      models.BPFProgram
		Cmd          *exec.Cmd
		FilePath     string
		RestartCount int
		ifaceName    string
		hostConfig   *config.Config
	}
	tests := []struct {
		name    string
		fields  fields
		wantErr bool
	}{
		{name: "NoFilePath",
			fields: fields{
				Program:      models.BPFProgram{},
				Cmd:          nil,
				FilePath:     "",
				RestartCount: 0,
				hostConfig: &config.Config{
					BPFLogDir: "",
				},
			},
			wantErr: true,
		},
		{name: "AnyBinary",
			fields: fields{
				Program: models.BPFProgram{
					Name:              "nfprogram",
					Artifact:          "data.tar.gz",
					CmdStart:          GetTestExecutableName(),
					CmdStop:           GetTestExecutableName(),
					UserProgramDaemon: true,
					AdminStatus:       "enabled",
				},
				Cmd:          nil,
				FilePath:     GetTestExecutablePath(),
				RestartCount: 0,
				hostConfig: &config.Config{
					BPFLogDir: "",
				},
			},
			wantErr: false,
		},
		{name: "UserProgramFalse",
			fields: fields{
				Program: models.BPFProgram{
					Name:              "nfprogram",
					Artifact:          "data.tar.gz",
					CmdStart:          GetTestExecutableName(),
					CmdStop:           GetTestExecutableName(),
					UserProgramDaemon: false,
					AdminStatus:       "enabled",
				},
				Cmd:          fakeExecCommand(GetTestExecutablePathName()),
				FilePath:     GetTestExecutablePath(),
				RestartCount: 0,
				hostConfig: &config.Config{
					BPFLogDir: "",
				},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b := &BPF{
				Program:      tt.fields.Program,
				Cmd:          tt.fields.Cmd,
				FilePath:     tt.fields.FilePath,
				RestartCount: tt.fields.RestartCount,
				HostConfig:   tt.fields.hostConfig,
			}
			if err := b.Start(tt.fields.ifaceName, "", models.IngressType, true); (err != nil) != tt.wantErr {
				t.Errorf("BPF.Start() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestBPF_isRunning(t *testing.T) {
	type fields struct {
		Program      models.BPFProgram
		Cmd          *exec.Cmd
		FilePath     string
		RestartCount int
		CmdStatus    string
	}
	tests := []struct {
		name    string
		fields  fields
		want    bool
		wantErr bool
	}{
		{
			name: "NoPID",
			fields: fields{
				Program:      models.BPFProgram{},
				Cmd:          nil,
				FilePath:     "",
				RestartCount: 0,
			},
			want:    false,
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b := &BPF{
				Program:      tt.fields.Program,
				Cmd:          tt.fields.Cmd,
				FilePath:     tt.fields.FilePath,
				RestartCount: tt.fields.RestartCount,
			}
			userProg, bpfProg, err := b.isRunning()
			if (err != nil) != tt.wantErr && (userProg == tt.want || bpfProg != tt.want) {
				t.Errorf("BPF.isRunning() user prog = %v, bpf prog = %v, error = %v, wantErr %v ", userProg, bpfProg, err, tt.wantErr)
			}
		})
	}
}

// RoundTripFunc .
type RoundTripFunc func(req *http.Request) *http.Response

// RoundTrip .
func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
	return f(req), nil
}

// NewTestClient returns *http.Client with Transport replaced to avoid making real calls
func NewTestClient(fn RoundTripFunc) *http.Client {
	return &http.Client{
		Transport: RoundTripFunc(fn),
	}
}
func TestBPF_GetArtifacts(t *testing.T) {

	type fields struct {
		Program      models.BPFProgram
		Cmd          *exec.Cmd
		FilePath     string
		RestartCount int
		Client       *http.Client
	}
	type args struct {
		conf *config.Config
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		wantErr bool
	}{
		{name: "EmptyArtifact",
			fields: fields{
				Program:      models.BPFProgram{},
				Cmd:          nil,
				FilePath:     "",
				RestartCount: 0,
			},
			args:    args{conf: &config.Config{BPFDir: "/tmp"}},
			wantErr: true,
		},
		{name: "DummyArtifact",
			fields: fields{
				Program: models.BPFProgram{
					Name:     "dummy",
					Version:  "1",
					Artifact: "dummy.tar.gz",
				},
				Cmd:          nil,
				FilePath:     "",
				RestartCount: 0,
				Client: NewTestClient(func(r *http.Request) *http.Response {
					buf := new(bytes.Buffer)
					writer := gzip.NewWriter(buf)
					defer writer.Close()
					tarWriter := tar.NewWriter(writer)
					defer tarWriter.Close()
					header := new(tar.Header)
					header.Name = "random"
					header.Mode = 0777
					tarWriter.WriteHeader(header)
					tarWriter.Write([]byte("random things"))
					return &http.Response{
						StatusCode: 200,
						Body:       io.NopCloser(buf),
						Header:     make(http.Header),
					}
				}),
			},
			args: args{conf: &config.Config{BPFDir: "/tmp",
				EBPFRepoURL: "https://l3af.io/"}},
			wantErr: true,
		},
		{
			name: "Unknown_url_with_http_scheme",
			fields: fields{
				Program: models.BPFProgram{
					EPRURL: "http://www.example.com",
				},
				Cmd:          nil,
				FilePath:     "",
				RestartCount: 0,
			},
			args: args{
				conf: &config.Config{
					BPFDir:      "/tmp",
					EBPFRepoURL: "https://l3af.io/",
				},
			},
			wantErr: true,
		},
		{
			name: "Unknown_url_with_file_scheme",
			fields: fields{
				Program: models.BPFProgram{
					EPRURL: "file:///Users/random/dummy.tar.gz",
				},
				Cmd:          nil,
				FilePath:     "",
				RestartCount: 0,
			},
			args: args{
				conf: &config.Config{
					BPFDir:      "/tmp",
					EBPFRepoURL: "https://l3af.io/",
				},
			},
			wantErr: true,
		},
		{
			name: "Unknown_scheme",
			fields: fields{
				Program: models.BPFProgram{
					EPRURL: "ftp://ftp.foo.org/dummy.tar.gz",
				},
				Cmd:          nil,
				FilePath:     "",
				RestartCount: 0,
			},
			args: args{
				conf: &config.Config{
					BPFDir:      "/tmp",
					EBPFRepoURL: "https://l3af.io/",
				},
			},
			wantErr: true,
		},
		{
			name: "ZipReaderFail",
			fields: fields{
				Program: models.BPFProgram{
					Name:     "testebpfprogram",
					Version:  "1.0",
					Artifact: "testebpfprogram.zip",
				},
				Cmd:          nil,
				FilePath:     "",
				RestartCount: 0,
				Client: NewTestClient(func(r *http.Request) *http.Response {
					buf := new(bytes.Buffer)
					writer := zip.NewWriter(buf)
					f, _ := writer.Create("testebpfprogram")
					data := strings.NewReader("this is just a test ebpf program")
					io.Copy(f, data)
					writer.Close()
					return &http.Response{
						StatusCode: 200,
						Body:       io.NopCloser(buf),
						Header:     make(http.Header),
					}
				}),
			},
			args: args{
				conf: &config.Config{
					BPFDir: "/tmp",
				},
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b := &BPF{
				Program:      tt.fields.Program,
				Cmd:          tt.fields.Cmd,
				FilePath:     tt.fields.FilePath,
				RestartCount: tt.fields.RestartCount,
			}
			ctrl := gomock.NewController(t)
			defer ctrl.Finish()
			m := mocks.NewMockplatformInterface(ctrl)
			if runtime.GOOS == "windows" {
				m.EXPECT().GetPlatform().Return("windows", nil).AnyTimes()
			} else {
				m.EXPECT().GetPlatform().Return("focal", nil).AnyTimes()
			}
			err := b.GetArtifacts(tt.args.conf)
			if (err != nil) != tt.wantErr {
				t.Errorf("BPF.download() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestBPF_SetPrLimits(t *testing.T) {
	type fields struct {
		Program models.BPFProgram
		Cmd     *exec.Cmd
		//		Pid          int
		FilePath     string
		RestartCount int
	}
	tests := []struct {
		name    string
		fields  fields
		wantErr bool
	}{
		{name: "DefaultLimitsWithNoCmd",
			fields: fields{
				Program:      models.BPFProgram{},
				Cmd:          nil,
				FilePath:     "",
				RestartCount: 0,
			},
			wantErr: true,
		},
		{name: "ValidLimitsWithNoCmd",
			fields: fields{
				Program: models.BPFProgram{
					CPU:    100,
					Memory: 1024,
				},
				Cmd:          fakeExecCommand("/foo/foo"),
				FilePath:     "",
				RestartCount: 0,
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b := &BPF{
				Program: tt.fields.Program,
				Cmd:     tt.fields.Cmd,
				//				Pid:          tt.fields.Pid,
				FilePath:     tt.fields.FilePath,
				RestartCount: tt.fields.RestartCount,
			}
			if err := b.SetPrLimits(); (err != nil) != tt.wantErr {
				t.Errorf("BPF.SetPrLimits() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func Test_assertExecute(t *testing.T) {
	type args struct {
		filepath string
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "InvalidFilepath",
			args: args{
				filepath: "/tmp/dummyfile",
			},
			wantErr: true,
		},
		{
			name: "ValidFilepath",
			args: args{
				filepath: GetTestExecutablePathName(),
			},
			wantErr: false,
		},
		{
			name: "ValidFilepathWihoutExecute",
			args: args{
				filepath: GetTestNonexecutablePathName(),
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := assertExecutable(tt.args.filepath); (err != nil) != tt.wantErr {
				t.Errorf("assertExecute() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func Test_fileExists(t *testing.T) {
	tests := []struct {
		name     string
		fileName string
		exist    bool
	}{
		{
			name:     "invalidfilename",
			fileName: "blahblah",
			exist:    false,
		},
	}
	for _, tt := range tests {
		if fileExists(tt.fileName) != tt.exist {
			t.Errorf("Invalid filename")
		}
	}
}

func Test_StopExternalRunningProcess(t *testing.T) {
	tests := []struct {
		name        string
		processName string
		wantErr     bool
	}{
		{
			name:        "emptyProcessName",
			processName: "",
			wantErr:     true,
		},
	}
	for _, tt := range tests {
		err := StopExternalRunningProcess(tt.processName)
		if (err != nil) != tt.wantErr {
			t.Errorf("Error During execution StopExternalRunningProcess : %v", err)
		}
	}
}

func Test_createUpdateRulesFile(t *testing.T) {
	type fields struct {
		Program models.BPFProgram
		Cmd     *exec.Cmd
		//		Pid          int
		FilePath     string
		RestartCount int
	}
	tests := []struct {
		name    string
		fields  fields
		wantErr bool
	}{
		{
			name: "emptyRuleFileName",
			fields: fields{
				Program: models.BPFProgram{
					RulesFile: "",
				},
				Cmd:          nil,
				FilePath:     "",
				RestartCount: 0,
			},
			wantErr: true,
		},
		{
			name: "invalidPath",
			fields: fields{
				Program: models.BPFProgram{
					RulesFile: "bad",
				},
				Cmd:          nil,
				FilePath:     "/dummy/fpp",
				RestartCount: 0,
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b := &BPF{
				Program:      tt.fields.Program,
				Cmd:          tt.fields.Cmd,
				FilePath:     tt.fields.FilePath,
				RestartCount: tt.fields.RestartCount,
			}
			_, err := b.createUpdateRulesFile("ingress")
			if (err != nil) != tt.wantErr {
				t.Errorf("createUpdateRulesFile() error : %v", err)
			}
		})
	}
}

func Test_PutNextProgFDFromID(t *testing.T) {
	type fields struct {
		Program      models.BPFProgram
		Cmd          *exec.Cmd
		FilePath     string
		RestartCount int
		hostConfig   *config.Config
	}
	tests := []struct {
		name       string
		fields     fields
		wantErr    bool
		progId     int
		hostConfig *config.Config
	}{
		{
			name: "emptyMapName",
			fields: fields{
				Program: models.BPFProgram{
					MapName: "",
				},
				hostConfig: &config.Config{
					BpfMapDefaultPath: "/sys/fs/bpf",
				},
			},
			wantErr: false,
			progId:  1,
		},
		{
			name: "invalidMapName",
			fields: fields{
				Program: models.BPFProgram{
					MapName: "invalidname",
				},
				hostConfig: &config.Config{
					BpfMapDefaultPath: "/sys/fs/bpf",
				},
			},
			wantErr: true,
			progId:  1,
		},
		{
			name: "invalidProgID",
			fields: fields{
				Program: models.BPFProgram{
					Name:              "ratelimiting",
					SeqID:             1,
					Artifact:          "l3af_ratelimiting.tar.gz",
					MapName:           "xdp_rl_ingress_next_prog",
					CmdStart:          "ratelimiting",
					Version:           "latest",
					UserProgramDaemon: true,
					AdminStatus:       "enabled",
					ProgType:          "xdp",
					CfgVersion:        1,
				},
				hostConfig: &config.Config{
					BpfMapDefaultPath: "/sys/fs/bpf",
				},
			},
			progId:  -1,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b := &BPF{
				Program:      tt.fields.Program,
				Cmd:          tt.fields.Cmd,
				FilePath:     tt.fields.FilePath,
				RestartCount: tt.fields.RestartCount,
				HostConfig:   tt.fields.hostConfig,
			}
			err := b.PutNextProgFDFromID(tt.progId)
			if (err != nil) != tt.wantErr {
				t.Errorf("PutNextProgFDFromID() error : %v", err)
			}
		})
	}
}

func Test_VerifyPinnedProgMapExists(t *testing.T) {
	type fields struct {
		Program      models.BPFProgram
		Cmd          *exec.Cmd
		FilePath     string
		RestartCount int
		hostConfig   *config.Config
	}
	tests := []struct {
		name    string
		fields  fields
		wantErr bool
	}{
		{
			name: "invalidMapName",
			fields: fields{
				Program: models.BPFProgram{
					MapName: "invalid",
				},
				hostConfig: &config.Config{
					BpfMapDefaultPath: "/sys/fs/bpf",
				},
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b := &BPF{
				Program:      tt.fields.Program,
				Cmd:          tt.fields.Cmd,
				FilePath:     tt.fields.FilePath,
				RestartCount: tt.fields.RestartCount,
				HostConfig:   tt.fields.hostConfig,
			}
			err := b.VerifyPinnedProgMap(true, true)
			if (err != nil) != tt.wantErr {
				t.Errorf("VerifyPinnedMapExists() error : %v", err)
			}
		})
	}
}
func Test_VerifyProcessObject(t *testing.T) {
	type fields struct {
		Program      models.BPFProgram
		Cmd          *exec.Cmd
		FilePath     string
		RestartCount int
	}
	tests := []struct {
		name    string
		fields  fields
		wantErr bool
	}{
		{
			name: "nilCmd",
			fields: fields{
				Program:      models.BPFProgram{},
				Cmd:          nil,
				FilePath:     "",
				RestartCount: 0,
			},
			wantErr: true,
		},
		{
			name: "nillCmdProcess",
			fields: fields{
				Program: models.BPFProgram{},
				Cmd: &exec.Cmd{
					Process: nil,
				},
				FilePath:     "",
				RestartCount: 0,
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b := &BPF{
				Program:      tt.fields.Program,
				Cmd:          tt.fields.Cmd,
				FilePath:     tt.fields.FilePath,
				RestartCount: tt.fields.RestartCount,
			}
			err := b.VerifyProcessObject()
			if (err != nil) != tt.wantErr {
				t.Errorf("VerifyProcessObject() error : %v", err)
			}
		})
	}
}

func Test_VerifyPinnedProgMapVanish(t *testing.T) {
	type fields struct {
		Program      models.BPFProgram
		Cmd          *exec.Cmd
		FilePath     string
		RestartCount int
		hostConfig   *config.Config
	}
	tests := []struct {
		name    string
		fields  fields
		wantErr bool
	}{
		{
			name: "emptyMapName",
			fields: fields{
				Program: models.BPFProgram{
					MapName: "",
				},
				hostConfig: &config.Config{
					BpfMapDefaultPath: "/sys/fs/bpf",
				},
			},
			wantErr: false,
		},
		{
			name: "invalidProgType",
			fields: fields{
				Program: models.BPFProgram{
					MapName:  "tc/globals/something",
					ProgType: models.TCType,
				},
				hostConfig: &config.Config{
					BpfMapDefaultPath: "/sys/fs/bpf",
				},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			b := &BPF{
				Program:      tt.fields.Program,
				Cmd:          tt.fields.Cmd,
				FilePath:     tt.fields.FilePath,
				RestartCount: tt.fields.RestartCount,
				HostConfig: &config.Config{
					BpfMapDefaultPath: tt.fields.hostConfig.BpfMapDefaultPath,
				},
			}
			err := b.VerifyPinnedProgMap(true, false)
			if (err != nil) != tt.wantErr {
				t.Errorf("VerifyPinnedMapVanish() error : %v", err)
			}
		})
	}
}


================================================
FILE: bpfprogs/bpf_test_unix.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0
//
//go:build !WINDOWS
// +build !WINDOWS

package bpfprogs

import (
	"fmt"
	"os"
)

func GetTestNonexecutablePathName() string {
	return "/var/log/syslog"
}

func GetTestExecutablePathName() string {
	return "/bin/date"
}

func GetTestExecutablePath() string {
	return "/bin"
}

func GetTestExecutableName() string {
	return "date"
}

// assertExecutable checks for executable permissions
func assertExecutable(fPath string) error {
	info, err := os.Stat(fPath)
	if err != nil {
		return fmt.Errorf("could not stat file: %s with error: %w", fPath, err)
	}

	if (info.Mode()&os.ModePerm)&os.FileMode(executePerm) == 0 {
		return fmt.Errorf("file: %s, is not executable", fPath)
	}
	return nil
}


================================================
FILE: bpfprogs/bpf_test_windows.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0
//
//go:build WINDOWS
// +build WINDOWS

package bpfprogs

import (
	"fmt"
	"os"
	"strings"
)

func GetTestNonexecutablePathName() string {
	return "c:/windows/system32/drivers/etc/host"
}

func GetTestExecutablePathName() string {
	return "c:/windows/system32/net.exe"
}

func GetTestExecutablePath() string {
	return "c:/windows/system32"
}

func GetTestExecutableName() string {
	return "net.exe"
}

// assertExecutable checks for executable permissions
func assertExecutable(fPath string) error {
	_, err := os.Stat(fPath)
	if err != nil {
		return fmt.Errorf("could not stat file: %s with error: %v", fPath, err)
	}

	// info.Mode() does not return the correct permissions on Windows,
	// it always has the 'x' permissions clear, so instead use the file suffix.
	if !strings.HasSuffix(fPath, ".exe") {
		return fmt.Errorf("file: %s, is not executable", fPath)
	}

	return nil
}


================================================
FILE: bpfprogs/bpf_unix.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0
//
//go:build !WINDOWS
// +build !WINDOWS

// Package bpfprogs provides primitives for l3afd's network function configs.
package bpfprogs

import (
	"bytes"
	"errors"
	"fmt"
	"net"
	"os"
	"strings"
	"syscall"
	"unsafe"

	"github.com/l3af-project/l3afd/v2/models"
	"github.com/l3af-project/l3afd/v2/utils"

	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/link"
	"github.com/florianl/go-tc"
	"github.com/florianl/go-tc/core"
	"github.com/rs/zerolog/log"
	"github.com/safchain/ethtool"
	"golang.org/x/sys/unix"
)

// DisableLRO - XDP programs are failing when LRO is enabled, to fix this we use to manually disable.
// # ethtool -K ens7 lro off
// # ethtool -k ens7 | grep large-receive-offload
// large-receive-offload: off
func DisableLRO(ifaceName string) error {
	ethHandle, err := ethtool.NewEthtool()
	if err != nil {
		err = fmt.Errorf("ethtool failed to get the handle %w", err)
		log.Error().Err(err).Msg("")
		return err
	}
	defer ethHandle.Close()

	config := make(map[string]bool, 1)
	config["rx-lro"] = false
	if err := ethHandle.Change(ifaceName, config); err != nil {
		err = fmt.Errorf("ethtool failed to disable LRO on %s with err %w", ifaceName, err)
		log.Error().Err(err).Msg("")
		return err
	}

	return nil
}

// prLimit set the memory and cpu limits for the bpf program
func prLimit(pid int, limit uintptr, rlimit *unix.Rlimit) error {
	_, _, errno := unix.RawSyscall6(unix.SYS_PRLIMIT64,
		uintptr(pid),
		limit,
		uintptr(unsafe.Pointer(rlimit)),
		0, 0, 0)

	if errno != 0 {
		log.Error().Msgf("Failed to set prlimit for process %d and errorno %d", pid, errno)
		return errors.New("failed to set prlimit")
	}

	return nil
}

// Set process resource limits only non-zero value
func (b *BPF) SetPrLimits() error {
	var rlimit unix.Rlimit

	if b.Cmd == nil {
		return errors.New("no Process to set limits")
	}

	if b.Program.Memory != 0 {
		rlimit.Cur = uint64(b.Program.Memory)
		rlimit.Max = uint64(b.Program.Memory)

		if err := prLimit(b.Cmd.Process.Pid, unix.RLIMIT_AS, &rlimit); err != nil {
			log.Error().Err(err).Msgf("Failed to set Memory limits - %s", b.Program.Name)
		}
	}

	if b.Program.CPU != 0 {
		rlimit.Cur = uint64(b.Program.CPU)
		rlimit.Max = uint64(b.Program.CPU)
		if err := prLimit(b.Cmd.Process.Pid, unix.RLIMIT_CPU, &rlimit); err != nil {
			log.Error().Err(err).Msgf("Failed to set CPU limits - %s", b.Program.Name)
		}
	}

	return nil
}

// ProcessTerminate - Send sigterm to the process
func (b *BPF) ProcessTerminate() error {
	if err := b.Cmd.Process.Signal(syscall.SIGTERM); err != nil {
		return fmt.Errorf("BPFProgram %s SIGTERM failed with error: %w", b.Program.Name, err)
	}
	return nil
}

// VerifyNMountBPFFS - Mounting bpf filesystem
func VerifyNMountBPFFS() error {
	dstPath := "/sys/fs/bpf"
	srcPath := "bpffs"
	fstype := "bpf"
	flags := 0

	mnts, err := os.ReadFile("/proc/mounts")
	if err != nil {
		return fmt.Errorf("failed to read procfs: %w", err)
	}

	if !strings.Contains(string(mnts), dstPath) {
		log.Warn().Msg("bpf filesystem is not mounted going to mount")
		if err = syscall.Mount(srcPath, dstPath, fstype, uintptr(flags), ""); err != nil {
			return fmt.Errorf("unable to mount %s at %s: %w", srcPath, dstPath, err)
		}
	}

	return VerifyNMountTraceFS()
}

// VerifyNMounTraceFS - Mounting trace filesystem
func VerifyNMountTraceFS() error {
	dstPath := "/sys/kernel/debug/tracing"
	srcPath := "tracefs"
	fstype := "tracefs"
	flags := syscall.MS_NODEV | syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_RELATIME

	mnts, err := os.ReadFile("/proc/self/mounts")
	if err != nil {
		return fmt.Errorf("failed to read procfs: %w", err)
	}

	if !strings.Contains(string(mnts), dstPath) {
		log.Warn().Msgf(" %s filesystem is not mounted going to mount", dstPath)
		if _, err = os.Stat(dstPath); err != nil {
			log.Warn().Msgf(" %s directory doesn't exists, creating", dstPath)
			if err := os.Mkdir(dstPath, 0700); err != nil {
				return fmt.Errorf("unable to create mount point %s : %w", dstPath, err)
			}
		}
		if err = syscall.Mount(srcPath, dstPath, fstype, uintptr(flags), ""); err != nil {
			return fmt.Errorf("unable to mount %s at %s: %w", srcPath, dstPath, err)
		}
	}

	return nil
}

// This method get the Linux distribution Codename. This logic works on ubuntu
// Here assumption is all edge nodes are running with lsb modules.
// It returns empty string in case of error
func GetPlatform() (string, error) {

	linuxDistrib := execCommand("lsb_release", "-cs")
	var out bytes.Buffer
	linuxDistrib.Stdout = &out

	if err := linuxDistrib.Run(); err != nil {
		return "", fmt.Errorf("l3afd/nf : Failed to run command with error: %w", err)
	}

	return strings.TrimSpace(out.String()), nil
}

func IsProcessRunning(pid int, name string) (bool, error) {
	procState, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", pid))
	if err != nil {
		return false, fmt.Errorf("BPF Program not running %s because of error: %w", name, err)
	}
	var u1, u2, state string
	_, err = fmt.Sscanf(string(procState), "%s %s %s", &u1, &u2, &state)
	if err != nil {
		return false, fmt.Errorf("failed to scan proc state with error: %w", err)
	}
	if state == "Z" {
		return false, fmt.Errorf("process %d in Zombie state", pid)
	}

	return true, nil
}

// VerifyNCreateTCDirs - Creating BPF sudo FS for pinning TC maps
func VerifyNCreateTCDirs() error {
	path := "/sys/fs/bpf/tc/globals"
	if _, err := os.Stat(path); err == nil {
		log.Debug().Msgf(" %s tc directory exists", path)
		return nil
	}
	log.Info().Msgf(" %s tc directory doesn't exists, creating", path)

	if err := os.MkdirAll(path, 0700); err != nil {
		return fmt.Errorf("unable to create directories to pin tc maps %s : %w", path, err)
	}
	return nil
}

// LoadTCAttachProgram - Load and attach tc root program filters or any tc program when chaining is disabled
func (b *BPF) LoadTCAttachProgram(ifaceName, direction string) error {
	iface, err := net.InterfaceByName(ifaceName)
	if err != nil {
		log.Error().Err(err).Msgf("LoadTCAttachProgram - look up network iface %q", ifaceName)
		return err
	}
	if err := b.LoadBPFProgram(ifaceName); err != nil {
		return err
	}
	// verify and add attribute clsact
	tcgo, err := tc.Open(&tc.Config{})
	if err != nil {
		return fmt.Errorf("could not open rtnetlink socket for interface %s : %w", ifaceName, err)
	}
	clsactFound := false
	htbFound := false
	ingressFound := false
	var htbHandle uint32
	var ingressHandle uint32
	var parentHandle uint32
	// get all the qdiscs from all interfaces
	qdiscs, err := tcgo.Qdisc().Get()
	if err != nil {
		return fmt.Errorf("could not get qdiscs for interface %s : %w", ifaceName, err)
	}

	for _, qdisc := range qdiscs {
		iface, err := net.InterfaceByIndex(int(qdisc.Ifindex))
		if err != nil {
			return fmt.Errorf("could not get interface %s from id %d: %w", ifaceName, qdisc.Ifindex, err)
		}
		if iface.Name == ifaceName {
			switch qdisc.Kind {
			case "clsact":
				clsactFound = true
			case "htb":
				htbFound = true
				htbHandle = qdisc.Msg.Handle
			case "ingress":
				ingressFound = true
				ingressHandle = qdisc.Msg.Handle
			default:
				log.Info().Msgf("Un-supported qdisc kind for interface %s : %s ", ifaceName, qdisc.Kind)
			}
		}
	}

	bpfRootProg := b.ProgMapCollection.Programs[b.Program.EntryFunctionName]

	var parent uint32
	var filter tc.Object

	progFD := uint32(bpfRootProg.FD())
	// Netlink attribute used in the Linux kernel
	bpfFlag := uint32(tc.BpfActDirect)
	if clsactFound {
		if direction == models.IngressType {
			parent = tc.HandleMinIngress
		} else if direction == models.EgressType {
			parent = tc.HandleMinEgress
		}

		filter = tc.Object{
			Msg: tc.Msg{
				Family:  unix.AF_UNSPEC,
				Ifindex: uint32(iface.Index),
				Handle:  0,
				Parent:  core.BuildHandle(tc.HandleRoot, parent),
				Info:    0x300,
			},
			Attribute: tc.Attribute{
				Kind: "bpf",
				BPF: &tc.Bpf{
					FD:    &progFD,
					Flags: &bpfFlag,
				},
			},
		}
	} else if !clsactFound && !ingressFound && !htbFound {
		qdisc := tc.Object{
			Msg: tc.Msg{
				Family:  unix.AF_UNSPEC,
				Ifindex: uint32(iface.Index),
				Handle:  core.BuildHandle(tc.HandleRoot, 0x0000),
				Parent:  tc.HandleIngress,
				Info:    0,
			},
			Attribute: tc.Attribute{
				Kind: "clsact",
			},
		}
		if err := tcgo.Qdisc().Add(&qdisc); err != nil {
			log.Info().Msgf("could not assign clsact to %s : %v, its already exists", ifaceName, err)
		}

		if direction == models.IngressType {
			parent = tc.HandleMinIngress
		} else if direction == models.EgressType {
			parent = tc.HandleMinEgress
		}

		filter = tc.Object{
			Msg: tc.Msg{
				Family:  unix.AF_UNSPEC,
				Ifindex: uint32(iface.Index),
				Handle:  0,
				Parent:  core.BuildHandle(tc.HandleRoot, parent),
				Info:    0x300,
			},
			Attribute: tc.Attribute{
				Kind: "bpf",
				BPF: &tc.Bpf{
					FD:    &progFD,
					Flags: &bpfFlag,
				},
			},
		}
	} else if !clsactFound && ingressFound && htbFound {
		if direction == models.IngressType {
			parentHandle = ingressHandle
		} else if direction == models.EgressType {
			parentHandle = htbHandle
		}

		// parentNew needs to handle of HTB and ingress 1:, and ffff:
		filter = tc.Object{
			Msg: tc.Msg{
				Family:  unix.AF_UNSPEC,
				Ifindex: uint32(iface.Index),
				Handle:  0,
				Parent:  parentHandle,
				Info:    0x300,
			},
			Attribute: tc.Attribute{
				Kind: "bpf",
				BPF: &tc.Bpf{
					FD:    &progFD,
					Flags: &bpfFlag,
				},
			},
		}
	} else {
		log.Info().Msgf("Unable to create qdisc object for interface %s", ifaceName)
	}

	// Storing Filter handle
	filterHandle := tcgo.Filter()
	// Attaching / Adding as filter
	if err := filterHandle.Add(&filter); err != nil {
		return fmt.Errorf("could not attach filter to interface %s for eBPF program %s : %w", ifaceName, b.Program.Name, err)
	}

	if b.HostConfig.BpfChainingEnabled {
		if err = b.UpdateProgramMap(ifaceName); err != nil {
			return err
		}
	}
	return nil
}

// UnloadTCProgram - Remove TC filters
func (b *BPF) UnloadTCProgram(ifaceName, direction string) error {
	iface, err := net.InterfaceByName(ifaceName)
	if err != nil {
		log.Error().Err(err).Msgf("UnloadTCProgram - look up network iface %q", ifaceName)
		return err
	}

	tcgo, err := tc.Open(&tc.Config{})
	if err != nil {
		log.Error().Err(err).Msgf("UnloadTCProgram - Unable to tc.Open(&tc.Config{}):  %q", ifaceName)
		return err
	}

	clsactFound := false
	htbFound := false
	ingressFound := false
	var htbHandle uint32
	var ingressHandle uint32
	var parentHandle uint32
	// get all the qdiscs from all interfaces
	qdiscs, err := tcgo.Qdisc().Get()
	if err != nil {
		log.Warn().Msgf("Could not get filters for interface \"%s\" direction %s ", ifaceName, direction)
		return fmt.Errorf("could not get filters for interface %s : %w", ifaceName, err)
	}
	for _, qdisc := range qdiscs {
		iface, err := net.InterfaceByIndex(int(qdisc.Ifindex))
		if err != nil {
			return fmt.Errorf("could not get interface %s from id %d: %v", ifaceName, qdisc.Ifindex, err)
		}
		if iface.Name == ifaceName {
			switch qdisc.Kind {
			case "clsact":
				clsactFound = true
			case "htb":
				htbFound = true
				htbHandle = qdisc.Msg.Handle
			case "ingress":
				ingressFound = true
				ingressHandle = qdisc.Msg.Handle
			default:
				log.Info().Msgf("qdisc kind for %s : %v", ifaceName, qdisc.Kind)
			}
		}
	}

	bpfRootProg := b.ProgMapCollection.Programs[b.Program.EntryFunctionName]
	// Storing Filter handle
	filterHandle := tcgo.Filter()
	var parent uint32
	var filter tc.Object

	if clsactFound && !ingressFound && !htbFound {
		if direction == models.IngressType {
			parent = tc.HandleMinIngress
		} else if direction == models.EgressType {
			parent = tc.HandleMinEgress
		}

		tcfilts, err := filterHandle.Get(&tc.Msg{
			Family:  unix.AF_UNSPEC,
			Ifindex: uint32(iface.Index),
			Handle:  0x0,
			Parent:  core.BuildHandle(tc.HandleRoot, parent),
		})

		if err != nil {
			log.Warn().Msgf("Could not get filters for interface \"%s\" direction %s ", ifaceName, direction)
			return fmt.Errorf("could not get filters for interface %s : %v", ifaceName, err)
		}

		progFD := uint32(bpfRootProg.FD())
		// Netlink attribute used in the Linux kernel
		bpfFlag := uint32(tc.BpfActDirect)

		if len(tcfilts) > 0 {
			filter = tc.Object{
				Msg: tc.Msg{
					Family:  unix.AF_UNSPEC,
					Ifindex: uint32(iface.Index),
					Handle:  0,
					Parent:  core.BuildHandle(tc.HandleRoot, parent),
					Info:    tcfilts[0].Msg.Info,
				},
				Attribute: tc.Attribute{
					Kind: "bpf",
					BPF: &tc.Bpf{
						FD:    &progFD,
						Flags: &bpfFlag,
					},
				},
			}
		} else {
			log.Warn().Msgf("unload TC program clasct filters not found  for interface %s direction %s", ifaceName, direction)
		}
	} else if !clsactFound && ingressFound && htbFound {
		if direction == models.EgressType {
			parentHandle = htbHandle
			// _ = pa("parentNew...1 ", parentNew)
		} else if direction == models.IngressType {
			parentHandle = ingressHandle
		}
		tcfilts, err := filterHandle.Get(&tc.Msg{
			Family:  unix.AF_UNSPEC,
			Ifindex: uint32(iface.Index),
			Handle:  0x0,
			Parent:  parentHandle,
		})

		if err != nil {
			log.Warn().Msgf("Could not get filters for interface \"%s\" direction %s ", ifaceName, direction)
			return fmt.Errorf("could not get filters for interface %s : %v", ifaceName, err)
		}

		progFD := uint32(bpfRootProg.FD())
		// Netlink attribute used in the Linux kernel
		bpfFlag := uint32(tc.BpfActDirect)

		var tcFilterIndex int
		for i, tcfilt := range tcfilts {
			// finding the Info field of the relevant BPF filter among all set filters for that qdisc
			if tcfilt.Attribute.Kind == "bpf" {
				tcFilterIndex = i
			}
		}
		// Add a check for if tcFilterIndex out of bounds
		if len(tcfilts) > 0 {
			filter = tc.Object{
				Msg: tc.Msg{
					Family:  unix.AF_UNSPEC,
					Ifindex: uint32(iface.Index),
					Handle:  0,
					Parent:  parentHandle,
					Info:    tcfilts[tcFilterIndex].Msg.Info,
				},
				Attribute: tc.Attribute{
					Kind: "bpf",
					BPF: &tc.Bpf{
						FD:    &progFD,
						Flags: &bpfFlag,
					},
				},
			}
		} else {
			log.Warn().Msgf("unload TC program ingress or htp filters not found for interface %s direction %s", ifaceName, direction)
		}
	}

	// Detaching / Deleting filter
	if err := filterHandle.Delete(&filter); err != nil {
		return fmt.Errorf("could not dettach tc filter for interface %s : Direction: %v, parentHandle: %v, Error:%w", ifaceName, direction, parentHandle, err)
	}

	return nil
}

// LoadTCXAttachProgram - Load and attach xdp root program or any xdp program when chaining is disabled
func (b *BPF) LoadTCXAttachProgram(ifaceName, direction string) error {

	iface, err := net.InterfaceByName(ifaceName)
	if err != nil {
		log.Error().Err(err).Msgf("LoadXDPAttachProgram -look up network iface %q", ifaceName)
		return err
	}

	if err := b.LoadBPFProgram(ifaceName); err != nil {
		return err
	}

	var attachType ebpf.AttachType
	if direction == models.IngressType {
		attachType = ebpf.AttachTCXIngress
	} else {
		attachType = ebpf.AttachTCXEgress
	}

	b.Link, err = link.AttachTCX(link.TCXOptions{
		Program:   b.ProgMapCollection.Programs[b.Program.EntryFunctionName],
		Interface: iface.Index,
		Attach:    attachType,
	})

	if err != nil {
		return fmt.Errorf("could not attach tc program %s to interface %s direction %s : %w", b.Program.Name, ifaceName, direction, err)
	}

	version := utils.ReplaceDotsWithUnderscores(b.Program.Version)
	// Pin the Link
	linkPinPath := utils.TCLinkPinPath(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, b.Program.ProgType, direction)
	if err := b.Link.Pin(linkPinPath); err != nil {
		return fmt.Errorf("tcx program pinning failed program %s direction %s interface %s : %w", b.Program.Name, direction, ifaceName, err)
	}

	if b.HostConfig.BpfChainingEnabled {
		if err = b.UpdateProgramMap(ifaceName); err != nil {
			return err
		}
	}
	return nil
}

// LoadXDPAttachProgram - Load and attach xdp root program or any xdp program when chaining is disabled
// This method has been moved to linux specific till cilium windows library implements link.AttachXDP
func (b *BPF) LoadXDPAttachProgram(ifaceName string) error {
	iface, err := net.InterfaceByName(ifaceName)
	if err != nil {
		log.Error().Err(err).Msgf("LoadXDPAttachProgram -look up network iface %q", ifaceName)
		return err
	}

	if err := b.LoadBPFProgram(ifaceName); err != nil {
		return err
	}
	b.Link, err = link.AttachXDP(link.XDPOptions{
		Program:   b.ProgMapCollection.Programs[b.Program.EntryFunctionName],
		Interface: iface.Index,
	})

	if err != nil {
		return fmt.Errorf("could not attach xdp program %s to interface %s : %w", b.Program.Name, ifaceName, err)
	}

	version := utils.ReplaceDotsWithUnderscores(b.Program.Version)
	// Pin the Link
	linkPinPath := utils.LinkPinPath(b.HostConfig.BpfMapDefaultPath, ifaceName, b.Program.Name, version, b.Program.ProgType)
	if err := b.Link.Pin(linkPinPath); err != nil {
		return fmt.Errorf("xdp program pinning failed program %s interface %s : %w", b.Program.Name, ifaceName, err)
	}

	if b.HostConfig.BpfChainingEnabled {
		if err = b.UpdateProgramMap(ifaceName); err != nil {
			return err
		}
	}
	return nil
}


================================================
FILE: bpfprogs/bpf_windows.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0
//
//go:build WINDOWS
// +build WINDOWS

// Package bpfprogs provides primitives for l3afd's network function configs.
package bpfprogs

import (
	"errors"
	"fmt"
	"os"

	"github.com/cilium/ebpf"
)

// DisableLRO - XDP programs are failing when Large Receive Offload is enabled, to fix this we use to manually disable.
func DisableLRO(ifaceName string) error {
	return nil
}

// Set process resource limits only non-zero value
func (b *BPF) SetPrLimits() error {
	if b.Cmd == nil {
		return errors.New("no Process to set limits")
	}
	return nil
}

// VerifyNMountBPFFS - Mounting bpf filesystem
func VerifyNMountBPFFS() error {
	return nil
}

func GetPlatform() (string, error) {
	return "Windows", nil
}

func IsProcessRunning(pid int, name string) (bool, error) {
	_, err := os.FindProcess(pid)
	if err != nil {
		return false, fmt.Errorf("BPF Program not running %s because of error: %w", name, err)
	}
	return true, nil
}

// ProcessTerminate - Kills the process
func (b *BPF) ProcessTerminate() error {
	if err := b.Cmd.Process.Kill(); err != nil {
		return fmt.Errorf("BPFProgram %s kill failed with error: %w", b.Program.Name, err)
	}
	return nil
}

// VerifyNCreateTCDirs - Creating BPF sudo FS for pinning TC maps
func VerifyNCreateTCDirs() error {
	return nil
}

// LoadTCAttachProgram - not implemented in windows
func (b *BPF) LoadTCAttachProgram(ifaceName, direction string) error {
	// not implement nothing todo
	return fmt.Errorf("LoadTCAttachProgram - TC programs Unsupported on windows")
}

// LoadTCXAttachProgram - not implemented in windows
func (b *BPF) LoadTCXAttachProgram(ifaceName, direction string) error {
	// not implement nothing todo
	return fmt.Errorf("LoadTCXAttachProgram - TC programs Unsupported on windows")
}

// UnloadTCProgram - Remove TC filters
func (b *BPF) UnloadTCProgram(ifaceName, direction string) error {
	// not implement nothing todo
	return fmt.Errorf("UnloadTCProgram - TC programs Unsupported on windows")
}

// LoadXDPAttachProgram - Attaches XDP program to interface
func (b *BPF) LoadXDPAttachProgram(ifaceName string) error {
	// not implement nothing todo
	return fmt.Errorf("LoadXDPAttachProgram - AttachXDP method is Unsupported on windows")
}

// LoadBPFProgramProbeType - Loads Probe type bpf program
func (b *BPF) LoadBPFProgramProbeTypes(objSpec *ebpf.CollectionSpec) error {
	// not implement nothing todo
	return fmt.Errorf("LoadBPFProgramProbeTypes - Probes are Unsupported on windows")
}


================================================
FILE: bpfprogs/bpfdebug.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package bpfprogs

import (
	"encoding/json"
	"errors"
	"net"
	"net/http"
	"strings"

	"github.com/l3af-project/l3afd/v2/models"
	"github.com/rs/zerolog/log"
)

var bpfcfgs *NFConfigs

func SetupBPFDebug(ebpfChainDebugAddr string, BPFConfigs *NFConfigs) {
	bpfcfgs = BPFConfigs
	go func() {
		if _, ok := models.AllNetListeners.Load("debug_http"); !ok {
			tcpAddr, err := net.ResolveTCPAddr("tcp", ebpfChainDebugAddr)
			if err != nil {
				log.Fatal().Err(err).Msgf("unable to resolve tcpaddr %v ", ebpfChainDebugAddr)
				return
			}
			listener, err := net.ListenTCP("tcp", tcpAddr)
			if err != nil {
				log.Fatal().Err(err).Msgf("unable to create tcp listener")
			}
			models.AllNetListeners.Store("debug_http", listener)
		}
		http.HandleFunc("/bpfs/", ViewHandler)
		// We just need to start a server.
		log.Info().Msg("Starting BPF debug server")
		val, _ := models.AllNetListeners.Load("debug_http")
		l, _ := val.(*net.TCPListener)
		if err := http.Serve(l, nil); !errors.Is(err, http.ErrServerClosed) {
			log.Fatal().Err(err).Msg("failed to start BPF chain debug server")
		}
	}()
}

func ViewHandler(w http.ResponseWriter, r *http.Request) {

	iface := strings.TrimPrefix(r.URL.Path, "/bpfs/")
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(bpfcfgs.BPFDetails(iface)); err != nil {
		log.Err(err).Msgf("unable to serialize json")
	}
}


================================================
FILE: bpfprogs/bpfmap.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package bpfprogs

import (
	"container/ring"
	"errors"
	"fmt"
	"math"
	"unsafe"

	"github.com/cilium/ebpf"
	"github.com/l3af-project/l3afd/v2/models"
	"github.com/rs/zerolog/log"
)

type BPFMap struct {
	Name  string
	MapID ebpf.MapID
	Type  ebpf.MapType

	// BPFProg reference in case of stale map id
	BPFProg *BPF `json:"-"`
}

// This stores Metrics map details.
type MetricsBPFMap struct {
	BPFMap
	Key        int
	Values     *ring.Ring
	Aggregator string
	LastValue  float64
}

// The RemoveMissingKeys function is used to delete missing entries of eBPF maps, which are used by eBPF Programs.
func (b *BPFMap) RemoveMissingKeys(args []models.KeyValue) error {
	ebpfMap, err := ebpf.NewMapFromID(b.MapID)
	if err != nil {
		return fmt.Errorf("access new map from ID failed %w", err)
	}
	defer ebpfMap.Close()
	KeyValueMap := make(map[int]bool, len(args))
	for _, k := range args {
		KeyValueMap[k.Key] = true
	}
	var key, nextKey int
	for {
		err := ebpfMap.NextKey(unsafe.Pointer(&key), unsafe.Pointer(&nextKey))
		if err != nil {
			if errors.Is(err, ebpf.ErrKeyNotExist) {
				break
			} else {
				return fmt.Errorf("get next key failed with error %w, mapid %d", err, b.MapID)
			}
		}
		key = nextKey
		_, IsKeyExists := KeyValueMap[key]
		if !IsKeyExists {
			log.Info().Msgf("removing key %v because it is missing\n", key)
			if err := ebpfMap.Delete(unsafe.Pointer(&key)); err != nil {
				return fmt.Errorf("delete key failed with error %w, mapid %d", err, b.MapID)
			}
		}
	}
	return nil
}

// The update function is used to update eBPF maps, which are used by eBPF programs.
func (b *BPFMap) Update(key, value int) error {

	log.Debug().Msgf("update map name %s ID %d", b.Name, b.MapID)
	ebpfMap, err := ebpf.NewMapFromID(b.MapID)
	if err != nil {
		return fmt.Errorf("access new map from ID failed %w", err)
	}
	defer ebpfMap.Close()
	log.Info().Msgf("updating map %s key %d mapid %d", b.Name, key, b.MapID)
	if err := ebpfMap.Update(unsafe.Pointer(&key), unsafe.Pointer(&value), 0); err != nil {
		return fmt.Errorf("update hash map element failed for key %d error %w", key, err)
	}
	return nil
}

// Get value of the map for given key
// There are 2 aggregators are supported here
// max-rate - this calculates delta requests / sec and stores absolute value.
// avg - stores the values in the circular queue
// We can implement more aggregate function as needed.
func (b *MetricsBPFMap) GetValue() float64 {
	ebpfMap, err := ebpf.NewMapFromID(b.MapID)
	if err != nil {
		// We have observed in smaller configuration VM's, if we restart BPF's
		// Stale mapID's are reported, in such cases re-checking map id
		log.Warn().Err(err).Msgf("GetValue : NewMapFromID failed ID %d, re-looking up of map id", b.MapID)
		tmpBPF, err := b.BPFProg.GetBPFMap(b.Name)
		if err != nil {
			log.Warn().Err(err).Msgf("GetValue: Update new map ID %d", tmpBPF.MapID)
			return 0
		}
		log.Info().Msgf("GetValue: Update new map ID %d", tmpBPF.MapID)
		b.MapID = tmpBPF.MapID
		ebpfMap, err = ebpf.NewMapFromID(b.MapID)
		if err != nil {
			log.Warn().Err(err).Msgf("GetValue : retry of NewMapFromID failed ID %d", b.MapID)
			return 0
		}
	}
	defer ebpfMap.Close()

	var value int64
	if err = ebpfMap.Lookup(unsafe.Pointer(&b.Key), unsafe.Pointer(&value)); err != nil {
		log.Warn().Err(err).Msgf("GetValue Lookup failed : Name %s ID %d", b.Name, b.MapID)
		return 0
	}

	var retVal float64
	switch b.Aggregator {
	case "scalar":
		retVal = float64(value)
	case "max-rate":
		b.Values = b.Values.Next()
		b.Values.Value = math.Abs(float64(float64(value) - b.LastValue))
		b.LastValue = float64(value)
		retVal = b.MaxValue()
	case "avg":
		b.Values.Value = value
		b.Values = b.Values.Next()
		retVal = b.AvgValue()
	default:
		log.Warn().Msgf("unsupported aggregator %s and value %d", b.Aggregator, value)
	}
	return retVal
}

// This method  finds the max value in the circular list
func (b *MetricsBPFMap) MaxValue() float64 {
	tmp := b.Values
	var max float64
	for i := 0; i < b.Values.Len(); i++ {
		if tmp.Value != nil {
			val := tmp.Value.(float64)
			if max < val {
				max = val
			}
		}
		tmp = tmp.Next()
	}
	return max
}

// This method calculates the average
func (b *MetricsBPFMap) AvgValue() float64 {
	tmp := b.Values.Next()
	var sum float64
	var n float64 = 0.0
	for i := 0; i < b.Values.Len(); i++ {
		if tmp.Value != nil {
			sum = sum + tmp.Value.(float64)
			n = n + 1
		}
		tmp = tmp.Next()
	}
	return sum / n
}


================================================
FILE: bpfprogs/bpfmap_test.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package bpfprogs

import (
	"container/ring"
	"reflect"
	"testing"
)

var TestValues *ring.Ring = ring.New(10)

func SetupTestValues() {
	a := [10]float64{8, 10, 6, 23, 4, 53, 32, 8, 2, 7}
	v := TestValues
	for i := 0; i < TestValues.Len(); i++ {
		v.Value = a[i]
		v = v.Next()
	}
}
func TestMetricsBPFMapMaxValue(t *testing.T) {
	type args struct {
		key        int
		Values     *ring.Ring
		aggregator string
	}
	SetupTestValues()
	tests := []struct {
		name    string
		args    args
		want    float64
		wantErr bool
	}{
		{
			name:    "max-rate",
			args:    args{key: 0, Values: TestValues, aggregator: "max-rate"},
			want:    53,
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			metricsMap := &MetricsBPFMap{
				Values:     TestValues,
				Key:        0,
				Aggregator: tt.args.aggregator,
				LastValue:  0,
			}
			got := (metricsMap.MaxValue())
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("MaxValue() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestMetricsBPFMapAvgValue(t *testing.T) {
	type args struct {
		key        int
		Values     *ring.Ring
		aggregator string
	}
	SetupTestValues()
	tests := []struct {
		name    string
		args    args
		want    float64
		wantErr bool
	}{
		{
			name:    "avg",
			args:    args{key: 0, Values: TestValues, aggregator: "avg"},
			want:    15.3,
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			metricsMap := &MetricsBPFMap{
				Values:     TestValues,
				Key:        0,
				Aggregator: tt.args.aggregator,
				LastValue:  0,
			}
			got := (metricsMap.AvgValue())
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("AvgValue() = %v, want %v", got, tt.want)
			}
		})
	}
}


================================================
FILE: bpfprogs/bpfmetrics.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

// Package bpfprogs provides primitives for NF process monitoring.
package bpfprogs

import (
	"container/list"
	"time"

	"github.com/l3af-project/l3afd/v2/models"

	"github.com/rs/zerolog/log"
)

type BpfMetrics struct {
	Chain     bool
	Intervals int
}

func NewpBpfMetrics(chain bool, interval int) *BpfMetrics {
	m := &BpfMetrics{
		Chain:     chain,
		Intervals: interval,
	}
	return m
}

func (c *BpfMetrics) BpfMetricsStart(xdpProgs, ingressTCProgs, egressTCProgs map[string]*list.List, probes *list.List, ifaces *map[string]string) {
	go c.BpfMetricsWorker(xdpProgs, ifaces)
	go c.BpfMetricsWorker(ingressTCProgs, ifaces)
	go c.BpfMetricsWorker(egressTCProgs, ifaces)
	go c.BpfMetricsProbeWorker(probes)
}

func (c *BpfMetrics) BpfMetricsWorker(bpfProgs map[string]*list.List, ifaces *map[string]string) {
	for range time.NewTicker(1 * time.Second).C {
		for ifaceName, bpfList := range bpfProgs {
			if bpfList == nil { // no bpf programs are running
				continue
			}
			for e := bpfList.Front(); e != nil; e = e.Next() {
				bpf := e.Value.(*BPF)
				if c.Chain && bpf.Program.SeqID == 0 { // do not monitor root program
					continue
				}
				if bpf.Program.AdminStatus == models.Disabled {
					continue
				}
				if err := bpf.MonitorMaps(ifaceName, (*ifaces)[ifaceName], c.Intervals); err != nil {
					log.Debug().Err(err).Msgf("pMonitor monitor maps failed - %s", bpf.Program.Name)
				}
			}
		}
	}
}

func (c *BpfMetrics) BpfMetricsProbeWorker(bpfProgs *list.List) {
	for range time.NewTicker(1 * time.Second).C {
		if bpfProgs == nil {
			time.Sleep(time.Second)
			continue
		}
		for e := bpfProgs.Front(); e != nil; e = e.Next() {
			bpf := e.Value.(*BPF)
			if bpf.Program.AdminStatus == models.Disabled {
				continue
			}
			if err := bpf.MonitorMaps("", "", c.Intervals); err != nil {
				log.Debug().Err(err).Msgf("pMonitor probe monitor maps failed - %s", bpf.Program.Name)
			}
		}
	}
}


================================================
FILE: bpfprogs/bpfmetrics_test.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

package bpfprogs

import (
	"container/list"
	"reflect"
	"testing"
)

func TestNewpKFMetrics(t *testing.T) {
	type args struct {
		chain    bool
		interval int
	}
	tests := []struct {
		name    string
		args    args
		want    *BpfMetrics
		wantErr bool
	}{
		{
			name:    "EmptypCheck",
			args:    args{chain: false, interval: 0},
			want:    &BpfMetrics{Chain: false, Intervals: 0},
			wantErr: false,
		},
		{
			name:    "ValidpCheck",
			args:    args{chain: true, interval: 10},
			want:    &BpfMetrics{Chain: true, Intervals: 10},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := NewpBpfMetrics(tt.args.chain, tt.args.interval)
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("NewKFMetrics() = %v, want %v", got, tt.want)
			}
		})
	}
}

func Test_BPFMetrics_Start(t *testing.T) {
	type fields struct {
		Chain    bool
		Interval int
	}
	type args struct {
		IngressXDPbpfProgs map[string]*list.List
		IngressTCbpfProgs  map[string]*list.List
		EgressTCbpfProgs   map[string]*list.List
		Probes             *list.List
		Ifaces             map[string]string
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		wantErr bool
	}{
		{
			name:   "EmptyBPF",
			fields: fields{Chain: true, Interval: 10},
			args: args{IngressXDPbpfProgs: make(map[string]*list.List),
				IngressTCbpfProgs: make(map[string]*list.List),
				EgressTCbpfProgs:  make(map[string]*list.List),
				Ifaces:            map[string]string{},
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			c := &BpfMetrics{
				Chain:     tt.fields.Chain,
				Intervals: tt.fields.Interval,
			}
			c.BpfMetricsStart(tt.args.IngressXDPbpfProgs, tt.args.IngressTCbpfProgs, tt.args.EgressTCbpfProgs, tt.args.Probes, &tt.args.Ifaces)
		})
	}
}


================================================
FILE: bpfprogs/nfconfig.go
================================================
// Copyright Contributors to the L3AF Project.
// SPDX-License-Identifier: Apache-2.0

// Package bpfprogs provides primitives for l3afd's network function configs.
package bpfprogs

import (
	"container/list"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"os"
	"reflect"
	"slices"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/link"

	"github.com/l3af-project/l3afd/v2/config"
	"github.com/l3af-project/l3afd/v2/models"
	"github.com/l3af-project/l3afd/v2/stats"
	"github.com/prometheus/client_golang/prometheus"

	"github.com/rs/zerolog/log"
)

type NFConfigs struct {
	Ctx            context.Context
	HostName       string
	HostInterfaces map[string]bool
	//	configs        sync.Map // key: string, val: *models.L3afDNFConfigDetail
	// These holds bpf programs in the list
	// map keys are network iface names index's are seq_id, position in the chain
	// root element will be root program
	IngressXDPBpfs map[string]*list.List
	IngressTCBpfs  map[string]*list.List
	EgressTCBpfs   map[string]*list.List
	ProbesBpfs     list.List

	HostConfig    *config.Config
	ProcessMon    *PCheck
	BpfMetricsMon *BpfMetrics

	// keep track of interfaces
	Ifaces map[string]string

	Mu *sync.Mutex
}

func NewNFConfigs(ctx context.Context, host string, hostConf *config.Config, pMon *PCheck, metricsMon *BpfMetrics) (*NFConfigs, error) {
	nfConfigs := &NFConfigs{
		Ctx:            ctx,
		HostName:       host,
		HostConfig:     hostConf,
		IngressXDPBpfs: make(map[string]*list.List),
		IngressTCBpfs:  make(map[string]*list.List),
		EgressTCBpfs:   make(map[string]*list.List),
		Mu:             new(sync.Mutex),
		Ifaces:         make(map[string]string),
	}

	var err error
	if nfConfigs.HostInterfaces, err = getHostInterfaces(); err != nil {
		errOut := fmt.Errorf("%s failed to get network interfaces %w", host, err)
		log.Error().Err(errOut)
		return nil, errOut
	}

	nfConfigs.ProcessMon = pMon
	nfConfigs.ProcessMon.PCheckStart(nfConfigs.IngressXDPBpfs, nfConfigs.In
Download .txt
gitextract_j3_x9dxv/

├── .github/
│   ├── dependabot.yml
│   ├── release.yml
│   └── workflows/
│       ├── ci-build-windows.yaml
│       ├── ci-build.yaml
│       ├── ci-e2e.yaml
│       ├── codeql.yaml
│       └── scorecards-analysis.yml
├── .gitignore
├── CMakeLists.txt
├── CODEOWNERS
├── LICENSE
├── Makefile
├── README.md
├── apis/
│   ├── configwatch.go
│   ├── configwatch_test.go
│   ├── handlers/
│   │   ├── addprog.go
│   │   ├── addprog_test.go
│   │   ├── deleteprog.go
│   │   ├── deleteprog_test.go
│   │   ├── getconfig.go
│   │   ├── getconfig_test.go
│   │   ├── restart_linux.go
│   │   ├── restart_linux_test.go
│   │   ├── restart_windows.go
│   │   ├── restart_windows_test.go
│   │   ├── updateconfig.go
│   │   └── updateconfig_test.go
│   └── routes.go
├── bpfprogs/
│   ├── bpf.go
│   ├── bpfCfgs_internal.go
│   ├── bpf_test.go
│   ├── bpf_test_unix.go
│   ├── bpf_test_windows.go
│   ├── bpf_unix.go
│   ├── bpf_windows.go
│   ├── bpfdebug.go
│   ├── bpfmap.go
│   ├── bpfmap_test.go
│   ├── bpfmetrics.go
│   ├── bpfmetrics_test.go
│   ├── nfconfig.go
│   ├── nfconfig_test.go
│   ├── probes.go
│   ├── probes_test.go
│   ├── processCheck.go
│   └── processCheck_test.go
├── build-docker/
│   ├── Dockerfile
│   └── start.sh
├── config/
│   ├── config.go
│   ├── config_loader.go
│   └── l3afd.cfg
├── config.yml
├── docs/
│   ├── CONTRIBUTING.md
│   ├── api/
│   │   └── README.md
│   ├── configdoc.md
│   ├── docs.go
│   ├── graceful-restart-guide.md
│   ├── prod-deploy-guide.md
│   ├── swagger.json
│   ├── swagger.md
│   └── swagger.yaml
├── go.mod
├── go.sum
├── main.go
├── main_test.go
├── mocks/
│   └── mocked_interfaces.go
├── models/
│   └── l3afd.go
├── pidfile/
│   └── pidfile.go
├── register_internal.go
├── restart/
│   ├── restart.go
│   └── restart_test.go
├── routes/
│   ├── route.go
│   └── router.go
├── signals/
│   ├── signal_unix.go
│   └── signal_windows.go
├── stats/
│   └── metrics.go
├── testdata/
│   ├── Test_l3af-config.json
│   └── l3afd.cdb
├── utils/
│   └── utils.go
└── version.go
Download .txt
SYMBOL INDEX (351 symbols across 49 files)

FILE: apis/configwatch.go
  type Server (line 39) | type Server struct
    method GracefulStop (line 165) | func (s *Server) GracefulStop(shutdownTimeout time.Duration) error {
    method getClientValidator (line 216) | func (s *Server) getClientValidator(helloInfo *tls.ClientHelloInfo) fu...
  function StartConfigWatcher (line 52) | func StartConfigWatcher(ctx context.Context, hostname, daemonName string...
  function isLoopback (line 181) | func isLoopback(addr string) bool {
  function MonitorTLS (line 196) | func MonitorTLS(start time.Time, expiry time.Time, conf *config.Config) {
  function toLowerCaseASCII (line 263) | func toLowerCaseASCII(in string) string {
  function validHostname (line 295) | func validHostname(host string, isPattern bool) bool {
  function matchExactly (line 340) | func matchExactly(hostA, hostB string) bool {
  function matchHostnamesWithRegexp (line 349) | func matchHostnamesWithRegexp(dnsName, sanMatchRule string) bool {

FILE: apis/configwatch_test.go
  function TestMatchHostnamesWithRegexp (line 11) | func TestMatchHostnamesWithRegexp(t *testing.T) {
  function TestMatchExactly (line 111) | func TestMatchExactly(t *testing.T) {
  function TestToLowerCaseASCII (line 145) | func TestToLowerCaseASCII(t *testing.T) {

FILE: apis/handlers/addprog.go
  function AddEbpfPrograms (line 28) | func AddEbpfPrograms(ctx context.Context, bpfcfg *bpfprogs.NFConfigs) ht...
  function IncWriteReq (line 79) | func IncWriteReq() {
  function DecWriteReq (line 84) | func DecWriteReq() {

FILE: apis/handlers/addprog_test.go
  constant dummypayload (line 16) | dummypayload string = `[
  function Test_addprog (line 32) | func Test_addprog(t *testing.T) {

FILE: apis/handlers/deleteprog.go
  function DeleteEbpfPrograms (line 28) | func DeleteEbpfPrograms(ctx context.Context, bpfcfg *bpfprogs.NFConfigs)...

FILE: apis/handlers/deleteprog_test.go
  constant payloadfordelete (line 16) | payloadfordelete string = `[
  function Test_DeleteEbpfPrograms (line 30) | func Test_DeleteEbpfPrograms(t *testing.T) {

FILE: apis/handlers/getconfig.go
  function InitConfigs (line 17) | func InitConfigs(cfgs *bpfprogs.NFConfigs) error {
  function GetConfig (line 30) | func GetConfig(w http.ResponseWriter, r *http.Request) {
  function GetConfigAll (line 69) | func GetConfigAll(w http.ResponseWriter, r *http.Request) {

FILE: apis/handlers/getconfig_test.go
  function Test_GetConfig (line 14) | func Test_GetConfig(t *testing.T) {
  function Test_GetConfigAll (line 53) | func Test_GetConfigAll(t *testing.T) {

FILE: apis/handlers/restart_linux.go
  function HandleRestart (line 38) | func HandleRestart(bpfcfg *bpfprogs.NFConfigs) http.HandlerFunc {

FILE: apis/handlers/restart_linux_test.go
  function Test_HandleRestart (line 13) | func Test_HandleRestart(t *testing.T) {

FILE: apis/handlers/restart_windows.go
  function HandleRestart (line 22) | func HandleRestart(bpfcfg *bpfprogs.NFConfigs) http.HandlerFunc {

FILE: apis/handlers/restart_windows_test.go
  function Test_HandleRestart (line 11) | func Test_HandleRestart(t *testing.T) {

FILE: apis/handlers/updateconfig.go
  function UpdateConfig (line 28) | func UpdateConfig(ctx context.Context, bpfcfg *bpfprogs.NFConfigs) http....

FILE: apis/handlers/updateconfig_test.go
  function Test_UpdateConfig (line 16) | func Test_UpdateConfig(t *testing.T) {

FILE: apis/routes.go
  function apiRoutes (line 14) | func apiRoutes(ctx context.Context, bpfcfg *bpfprogs.NFConfigs) []routes...

FILE: bpfprogs/bpf.go
  constant executePerm (line 48) | executePerm uint32 = 0111
  constant bpfStatus (line 49) | bpfStatus string = "RUNNING"
  type BPF (line 52) | type BPF struct
    method Stop (line 255) | func (b *BPF) Stop(ifaceName, ipv4_address, direction string, chain bo...
    method Start (line 366) | func (b *BPF) Start(ifaceName, ipv4_address, direction string, chain b...
    method UpdateBPFMaps (line 471) | func (b *BPF) UpdateBPFMaps(ifaceName, ipv4_address, direction string)...
    method UpdateArgs (line 493) | func (b *BPF) UpdateArgs(ifaceName, ipv4_address, direction string) er...
    method isRunning (line 545) | func (b *BPF) isRunning() (bool, bool, error) {
    method VerifyAndGetArtifacts (line 601) | func (b *BPF) VerifyAndGetArtifacts(conf *config.Config) error {
    method GetArtifacts (line 613) | func (b *BPF) GetArtifacts(conf *config.Config) error {
    method createUpdateRulesFile (line 657) | func (b *BPF) createUpdateRulesFile(direction string) (string, error) {
    method AddBPFMap (line 683) | func (b *BPF) AddBPFMap(mapName string) error {
    method GetBPFMap (line 694) | func (b *BPF) GetBPFMap(mapName string) (*BPFMap, error) {
    method AddMetricsBPFMap (line 726) | func (b *BPF) AddMetricsBPFMap(mapName, aggregator string, key, sample...
    method MonitorMaps (line 746) | func (b *BPF) MonitorMaps(ifaceName, ipv4_address string, intervals in...
    method PutNextProgFDFromID (line 764) | func (b *BPF) PutNextProgFDFromID(progID int) error {
    method GetProgID (line 791) | func (b *BPF) GetProgID() (ebpf.ProgramID, error) {
    method RemoveNextProgFD (line 820) | func (b *BPF) RemoveNextProgFD() error {
    method RemovePrevProgFD (line 840) | func (b *BPF) RemovePrevProgFD() error {
    method VerifyPinnedProgMap (line 856) | func (b *BPF) VerifyPinnedProgMap(chain, exists bool) error {
    method VerifyProcessObject (line 896) | func (b *BPF) VerifyProcessObject() error {
    method VerifyMetricsMapsVanish (line 917) | func (b *BPF) VerifyMetricsMapsVanish() error {
    method UnloadProgram (line 942) | func (b *BPF) UnloadProgram(ifaceName, direction string) error {
    method RemovePinnedFiles (line 983) | func (b *BPF) RemovePinnedFiles(ifaceName string) error {
    method RemoveRootProgMapFile (line 1019) | func (b *BPF) RemoveRootProgMapFile(ifacename string) error {
    method VerifyCleanupMaps (line 1047) | func (b *BPF) VerifyCleanupMaps(chain bool) error {
    method LoadBPFProgram (line 1064) | func (b *BPF) LoadBPFProgram(ifaceName string) error {
    method InitialiseMetricMaps (line 1141) | func (b *BPF) InitialiseMetricMaps() error {
    method IsLoaded (line 1237) | func (b *BPF) IsLoaded() bool {
    method StartUserProgram (line 1250) | func (b *BPF) StartUserProgram(ifaceName, direction string, chain bool...
    method CreatePinDirectories (line 1325) | func (b *BPF) CreatePinDirectories(ifaceName, progName, progVersion st...
    method AttachBPFProgram (line 1362) | func (b *BPF) AttachBPFProgram(ifaceName, direction string) error {
    method PinBpfMaps (line 1394) | func (b *BPF) PinBpfMaps(ifaceName string) error {
    method UpdateProgramMap (line 1418) | func (b *BPF) UpdateProgramMap(ifaceName string) error {
    method LoadBPFProgramChain (line 1441) | func (b *BPF) LoadBPFProgramChain(ifaceName, direction string) error {
    method StopUserProgram (line 1481) | func (b *BPF) StopUserProgram(ifaceName, direction string) error {
  function NewBpfProgram (line 72) | func NewBpfProgram(ctx context.Context, program models.BPFProgram, conf ...
  function LoadRootProgram (line 105) | func LoadRootProgram(ifaceName string, direction string, progType string...
  function StopExternalRunningProcess (line 216) | func StopExternalRunningProcess(processName string) error {
  function fileExists (line 674) | func fileExists(filename string) bool {
  function DownloadArtifact (line 1528) | func DownloadArtifact(urlpath string, timeout time.Duration, buf *bytes....
  function ExtractArtifact (line 1574) | func ExtractArtifact(artifactName string, buf *bytes.Buffer, tempDir str...
  function ValidatePath (line 1671) | func ValidatePath(filePath string, destination string) (string, error) {

FILE: bpfprogs/bpfCfgs_internal.go
  method RunBPFConfigs (line 16) | func (b *BPF) RunBPFConfigs() error {

FILE: bpfprogs/bpf_test.go
  function fakeExecCommand (line 34) | func fakeExecCommand(command string, args ...string) *exec.Cmd {
  function TestHelperProcess (line 43) | func TestHelperProcess(t *testing.T) {
  function TestNewBpfProgram (line 50) | func TestNewBpfProgram(t *testing.T) {
  function TestBPF_Stop (line 140) | func TestBPF_Stop(t *testing.T) {
  function TestBPF_Start (line 254) | func TestBPF_Start(t *testing.T) {
  function TestBPF_isRunning (line 335) | func TestBPF_isRunning(t *testing.T) {
  type RoundTripFunc (line 378) | type RoundTripFunc
    method RoundTrip (line 381) | func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, e...
  function NewTestClient (line 386) | func NewTestClient(fn RoundTripFunc) *http.Client {
  function TestBPF_GetArtifacts (line 391) | func TestBPF_GetArtifacts(t *testing.T) {
  function TestBPF_SetPrLimits (line 562) | func TestBPF_SetPrLimits(t *testing.T) {
  function Test_assertExecute (line 613) | func Test_assertExecute(t *testing.T) {
  function Test_fileExists (line 653) | func Test_fileExists(t *testing.T) {
  function Test_StopExternalRunningProcess (line 672) | func Test_StopExternalRunningProcess(t *testing.T) {
  function Test_createUpdateRulesFile (line 692) | func Test_createUpdateRulesFile(t *testing.T) {
  function Test_PutNextProgFDFromID (line 746) | func Test_PutNextProgFDFromID(t *testing.T) {
  function Test_VerifyPinnedProgMapExists (line 827) | func Test_VerifyPinnedProgMapExists(t *testing.T) {
  function Test_VerifyProcessObject (line 869) | func Test_VerifyProcessObject(t *testing.T) {
  function Test_VerifyPinnedProgMapVanish (line 920) | func Test_VerifyPinnedProgMapVanish(t *testing.T) {

FILE: bpfprogs/bpf_test_unix.go
  function GetTestNonexecutablePathName (line 14) | func GetTestNonexecutablePathName() string {
  function GetTestExecutablePathName (line 18) | func GetTestExecutablePathName() string {
  function GetTestExecutablePath (line 22) | func GetTestExecutablePath() string {
  function GetTestExecutableName (line 26) | func GetTestExecutableName() string {
  function assertExecutable (line 31) | func assertExecutable(fPath string) error {

FILE: bpfprogs/bpf_test_windows.go
  function GetTestNonexecutablePathName (line 15) | func GetTestNonexecutablePathName() string {
  function GetTestExecutablePathName (line 19) | func GetTestExecutablePathName() string {
  function GetTestExecutablePath (line 23) | func GetTestExecutablePath() string {
  function GetTestExecutableName (line 27) | func GetTestExecutableName() string {
  function assertExecutable (line 32) | func assertExecutable(fPath string) error {

FILE: bpfprogs/bpf_unix.go
  function DisableLRO (line 36) | func DisableLRO(ifaceName string) error {
  function prLimit (line 57) | func prLimit(pid int, limit uintptr, rlimit *unix.Rlimit) error {
  method SetPrLimits (line 73) | func (b *BPF) SetPrLimits() error {
  method ProcessTerminate (line 101) | func (b *BPF) ProcessTerminate() error {
  function VerifyNMountBPFFS (line 109) | func VerifyNMountBPFFS() error {
  function VerifyNMountTraceFS (line 131) | func VerifyNMountTraceFS() error {
  function GetPlatform (line 161) | func GetPlatform() (string, error) {
  function IsProcessRunning (line 174) | func IsProcessRunning(pid int, name string) (bool, error) {
  function VerifyNCreateTCDirs (line 192) | func VerifyNCreateTCDirs() error {
  method LoadTCAttachProgram (line 207) | func (b *BPF) LoadTCAttachProgram(ifaceName, direction string) error {
  method UnloadTCProgram (line 368) | func (b *BPF) UnloadTCProgram(ifaceName, direction string) error {
  method LoadTCXAttachProgram (line 525) | func (b *BPF) LoadTCXAttachProgram(ifaceName, direction string) error {
  method LoadXDPAttachProgram (line 571) | func (b *BPF) LoadXDPAttachProgram(ifaceName string) error {

FILE: bpfprogs/bpf_windows.go
  function DisableLRO (line 19) | func DisableLRO(ifaceName string) error {
  method SetPrLimits (line 24) | func (b *BPF) SetPrLimits() error {
  function VerifyNMountBPFFS (line 32) | func VerifyNMountBPFFS() error {
  function GetPlatform (line 36) | func GetPlatform() (string, error) {
  function IsProcessRunning (line 40) | func IsProcessRunning(pid int, name string) (bool, error) {
  method ProcessTerminate (line 49) | func (b *BPF) ProcessTerminate() error {
  function VerifyNCreateTCDirs (line 57) | func VerifyNCreateTCDirs() error {
  method LoadTCAttachProgram (line 62) | func (b *BPF) LoadTCAttachProgram(ifaceName, direction string) error {
  method LoadTCXAttachProgram (line 68) | func (b *BPF) LoadTCXAttachProgram(ifaceName, direction string) error {
  method UnloadTCProgram (line 74) | func (b *BPF) UnloadTCProgram(ifaceName, direction string) error {
  method LoadXDPAttachProgram (line 80) | func (b *BPF) LoadXDPAttachProgram(ifaceName string) error {
  method LoadBPFProgramProbeTypes (line 86) | func (b *BPF) LoadBPFProgramProbeTypes(objSpec *ebpf.CollectionSpec) err...

FILE: bpfprogs/bpfdebug.go
  function SetupBPFDebug (line 19) | func SetupBPFDebug(ebpfChainDebugAddr string, BPFConfigs *NFConfigs) {
  function ViewHandler (line 45) | func ViewHandler(w http.ResponseWriter, r *http.Request) {

FILE: bpfprogs/bpfmap.go
  type BPFMap (line 18) | type BPFMap struct
    method RemoveMissingKeys (line 37) | func (b *BPFMap) RemoveMissingKeys(args []models.KeyValue) error {
    method Update (line 70) | func (b *BPFMap) Update(key, value int) error {
  type MetricsBPFMap (line 28) | type MetricsBPFMap struct
    method GetValue (line 90) | func (b *MetricsBPFMap) GetValue() float64 {
    method MaxValue (line 137) | func (b *MetricsBPFMap) MaxValue() float64 {
    method AvgValue (line 153) | func (b *MetricsBPFMap) AvgValue() float64 {

FILE: bpfprogs/bpfmap_test.go
  function SetupTestValues (line 14) | func SetupTestValues() {
  function TestMetricsBPFMapMaxValue (line 22) | func TestMetricsBPFMapMaxValue(t *testing.T) {
  function TestMetricsBPFMapAvgValue (line 58) | func TestMetricsBPFMapAvgValue(t *testing.T) {

FILE: bpfprogs/bpfmetrics.go
  type BpfMetrics (line 16) | type BpfMetrics struct
    method BpfMetricsStart (line 29) | func (c *BpfMetrics) BpfMetricsStart(xdpProgs, ingressTCProgs, egressT...
    method BpfMetricsWorker (line 36) | func (c *BpfMetrics) BpfMetricsWorker(bpfProgs map[string]*list.List, ...
    method BpfMetricsProbeWorker (line 58) | func (c *BpfMetrics) BpfMetricsProbeWorker(bpfProgs *list.List) {
  function NewpBpfMetrics (line 21) | func NewpBpfMetrics(chain bool, interval int) *BpfMetrics {

FILE: bpfprogs/bpfmetrics_test.go
  function TestNewpKFMetrics (line 12) | func TestNewpKFMetrics(t *testing.T) {
  function Test_BPFMetrics_Start (line 46) | func Test_BPFMetrics_Start(t *testing.T) {

FILE: bpfprogs/nfconfig.go
  type NFConfigs (line 33) | type NFConfigs struct
    method Close (line 83) | func (c *NFConfigs) Close(ctx context.Context) error {
    method VerifyAndStartXDPRootProgram (line 152) | func (c *NFConfigs) VerifyAndStartXDPRootProgram(ifaceName, direction ...
    method VerifyAndStartTCRootProgram (line 179) | func (c *NFConfigs) VerifyAndStartTCRootProgram(ifaceName, direction s...
    method PushBackAndStartBPF (line 216) | func (c *NFConfigs) PushBackAndStartBPF(bpfProg *models.BPFProgram, if...
    method DownloadAndStartBPFProgram (line 240) | func (c *NFConfigs) DownloadAndStartBPFProgram(element *list.Element, ...
    method StopNRemoveAllBPFPrograms (line 266) | func (c *NFConfigs) StopNRemoveAllBPFPrograms(ifaceName, direction str...
    method StopNRemoveAllBPFProbePrograms (line 303) | func (c *NFConfigs) StopNRemoveAllBPFProbePrograms() error {
    method VerifyNUpdateBPFProgram (line 325) | func (c *NFConfigs) VerifyNUpdateBPFProgram(bpfProg *models.BPFProgram...
    method MoveToLocation (line 466) | func (c *NFConfigs) MoveToLocation(element *list.Element, bpfList *lis...
    method InsertAndStartBPFProgram (line 540) | func (c *NFConfigs) InsertAndStartBPFProgram(bpfProg *models.BPFProgra...
    method StopRootProgram (line 596) | func (c *NFConfigs) StopRootProgram(ifaceName, direction string) error {
    method LinkBPFPrograms (line 638) | func (c *NFConfigs) LinkBPFPrograms(leftBPF, rightBPF *BPF) error {
    method BPFDetails (line 650) | func (c *NFConfigs) BPFDetails(iface string) []*BPF {
    method Deploy (line 678) | func (c *NFConfigs) Deploy(ifaceName, HostName string, bpfProgs *model...
    method DeployeBPFPrograms (line 776) | func (c *NFConfigs) DeployeBPFPrograms(bpfProgs []models.L3afBPFProgra...
    method SaveConfigsToConfigStore (line 800) | func (c *NFConfigs) SaveConfigsToConfigStore() error {
    method EBPFPrograms (line 832) | func (c *NFConfigs) EBPFPrograms(iface string) models.L3afBPFPrograms {
    method EBPFProgramsAll (line 880) | func (c *NFConfigs) EBPFProgramsAll() []models.L3afBPFPrograms {
    method RemoveMissingNetIfacesNBPFProgsInConfig (line 892) | func (c *NFConfigs) RemoveMissingNetIfacesNBPFProgsInConfig(bpfProgCfg...
    method RemoveMissingBPFProgramsInConfig (line 952) | func (c *NFConfigs) RemoveMissingBPFProgramsInConfig(bpfProg models.L3...
    method AddAndStartBPF (line 1032) | func (c *NFConfigs) AddAndStartBPF(bpfProg *models.BPFProgram, ifaceNa...
    method AddProgramWithoutChaining (line 1093) | func (c *NFConfigs) AddProgramWithoutChaining(ifaceName string, bpfPro...
    method AddProgramsOnInterface (line 1151) | func (c *NFConfigs) AddProgramsOnInterface(ifaceName, HostName string,...
    method AddeBPFPrograms (line 1241) | func (c *NFConfigs) AddeBPFPrograms(bpfProgs []models.L3afBPFPrograms)...
    method CleanupProgramsOnInterface (line 1270) | func (c *NFConfigs) CleanupProgramsOnInterface(ifaceName string) {
    method DeleteProgramsOnInterface (line 1289) | func (c *NFConfigs) DeleteProgramsOnInterface(ifaceName, HostName stri...
    method DeleteProgramsOnInterfaceHelper (line 1390) | func (c *NFConfigs) DeleteProgramsOnInterfaceHelper(e *list.Element, i...
    method DeleteEbpfPrograms (line 1427) | func (c *NFConfigs) DeleteEbpfPrograms(bpfProgs []models.L3afBPFProgra...
    method AddProbePrograms (line 1461) | func (c *NFConfigs) AddProbePrograms(HostName string, bpfProgs []*mode...
    method PushBackAndStartProbe (line 1482) | func (c *NFConfigs) PushBackAndStartProbe(bpfProg *models.BPFProgram) ...
    method DownloadAndStartProbes (line 1493) | func (c *NFConfigs) DownloadAndStartProbes(element *list.Element) error {
    method GetL3AFHOSTDATA (line 1571) | func (c *NFConfigs) GetL3AFHOSTDATA() models.L3AFALLHOSTDATA {
    method StartAllUserProgramsAndProbes (line 1641) | func (c *NFConfigs) StartAllUserProgramsAndProbes() error {
    method StopAllProbesAndUserPrograms (line 1779) | func (c *NFConfigs) StopAllProbesAndUserPrograms() error {
  function NewNFConfigs (line 56) | func NewNFConfigs(ctx context.Context, host string, hostConf *config.Con...
  function getHostInterfaces (line 1016) | func getHostInterfaces() (map[string]bool, error) {
  function BinarySearch (line 1444) | func BinarySearch(names []string, target string) bool {
  function SerialzeProgram (line 1512) | func SerialzeProgram(e *list.Element) *models.L3AFMetaData {

FILE: bpfprogs/nfconfig_test.go
  function setupDBTest (line 37) | func setupDBTest() {
  function setupValidBPF (line 49) | func setupValidBPF() {
  function setupBPFProgramData (line 70) | func setupBPFProgramData() {
  function setupBPFProgramVersionChange (line 91) | func setupBPFProgramVersionChange() {
  function setupBPFProgramStatusChange (line 110) | func setupBPFProgramStatusChange() {
  function TestNewNFConfigs (line 131) | func TestNewNFConfigs(t *testing.T) {
  function TestNFConfigs_Deploy (line 181) | func TestNFConfigs_Deploy(t *testing.T) {
  function TestNFConfigs_Close (line 371) | func TestNFConfigs_Close(t *testing.T) {
  function Test_getHostInterfaces (line 420) | func Test_getHostInterfaces(t *testing.T) {
  function Test_BinarySearch (line 439) | func Test_BinarySearch(t *testing.T) {
  function Test_AddProgramsOnInterface (line 467) | func Test_AddProgramsOnInterface(t *testing.T) {
  function TestAddeBPFPrograms (line 587) | func TestAddeBPFPrograms(t *testing.T) {
  function TestDeleteProgramsOnInterface (line 741) | func TestDeleteProgramsOnInterface(t *testing.T) {
  function TestDeleteEbpfPrograms (line 837) | func TestDeleteEbpfPrograms(t *testing.T) {
  function TestAddAndStartBPF (line 969) | func TestAddAndStartBPF(t *testing.T) {
  function TestAddProgramWithoutChaining (line 1023) | func TestAddProgramWithoutChaining(t *testing.T) {

FILE: bpfprogs/probes.go
  method LoadBPFProgramProbeType (line 21) | func (b *BPF) LoadBPFProgramProbeType(prog *ebpf.Program, sectionName st...
  method LoadBPFProgramProbeTypes (line 48) | func (b *BPF) LoadBPFProgramProbeTypes(objSpec *ebpf.CollectionSpec) err...
  function GetProgramSectionDetails (line 74) | func GetProgramSectionDetails(sectionName string) (string, string, strin...
  method AttachProbePerfEvent (line 93) | func (b *BPF) AttachProbePerfEvent(hookName, progType string, prog *ebpf...
  method AttachUProbePerfEvent (line 123) | func (b *BPF) AttachUProbePerfEvent(hookName string, prog *ebpf.Program)...
  method AttachURetProbePerfEvent (line 140) | func (b *BPF) AttachURetProbePerfEvent(hookName string, prog *ebpf.Progr...
  function getSymbolName (line 156) | func getSymbolName(funcNames []string) string {

FILE: bpfprogs/probes_test.go
  function TestGetProgramSectionDetails (line 13) | func TestGetProgramSectionDetails(t *testing.T) {

FILE: bpfprogs/processCheck.go
  type PCheck (line 16) | type PCheck struct
    method PCheckStart (line 31) | func (c *PCheck) PCheckStart(xdpProgs, ingressTCProgs, egressTCProgs m...
    method pMonitorWorker (line 38) | func (c *PCheck) pMonitorWorker(bpfProgs map[string]*list.List, direct...
    method pMonitorProbeWorker (line 93) | func (c *PCheck) pMonitorProbeWorker(bpfProgs *list.List) {
  function NewPCheck (line 22) | func NewPCheck(rc int, chain bool, interval time.Duration) *PCheck {

FILE: bpfprogs/processCheck_test.go
  function TestNewpCheck (line 13) | func TestNewpCheck(t *testing.T) {
  function Test_pCheck_pCheckStart (line 48) | func Test_pCheck_pCheckStart(t *testing.T) {

FILE: config/config.go
  constant ENV_PROD (line 18) | ENV_PROD = "PROD"
  type Config (line 21) | type Config struct
  function ReadConfig (line 101) | func ReadConfig(configPath string) (*Config, error) {
  function loadTLSVersion (line 172) | func loadTLSVersion(cfgRdr *config.Config, fieldName string) (uint16, er...
  function loadXDPRootPackageName (line 186) | func loadXDPRootPackageName(cfgRdr *config.Config) string {
  function loadXDPRootArtifact (line 194) | func loadXDPRootArtifact(cfgRdr *config.Config) string {
  function loadXDPRootIngressMapName (line 202) | func loadXDPRootIngressMapName(cfgRdr *config.Config) string {
  function loadXDPRootCommand (line 210) | func loadXDPRootCommand(cfgRdr *config.Config) string {
  function loadXDPRootVersion (line 218) | func loadXDPRootVersion(cfgRdr *config.Config) string {
  function loadTCRootPackageName (line 226) | func loadTCRootPackageName(cfgRdr *config.Config) string {
  function loadTCRootArtifact (line 234) | func loadTCRootArtifact(cfgRdr *config.Config) string {
  function loadTCRootIngressMapName (line 242) | func loadTCRootIngressMapName(cfgRdr *config.Config) string {
  function loadTCRootEgressMapName (line 250) | func loadTCRootEgressMapName(cfgRdr *config.Config) string {
  function loadTCRootCommand (line 258) | func loadTCRootCommand(cfgRdr *config.Config) string {
  function loadTCRootVersion (line 266) | func loadTCRootVersion(cfgRdr *config.Config) string {

FILE: config/config_loader.go
  constant cfgFatalMsg (line 17) | cfgFatalMsg    = "Could not read %s value %q from group %q in config file"
  constant cfgOptionalMsg (line 18) | cfgOptionalMsg = "Using default value %v after failure to read group:%s;...
  function LoadConfigString (line 32) | func LoadConfigString(confReader *config.Config, group, field string) st...
  function LoadOptionalConfigString (line 40) | func LoadOptionalConfigString(confReader *config.Config, group, field, d...
  function LoadOptionalConfigStringEncKey (line 45) | func LoadOptionalConfigStringEncKey(confReader *config.Config, group, fi...
  function LoadConfigStringEncKey (line 55) | func LoadConfigStringEncKey(confReader *config.Config, group, field stri...
  function loadConfigStringEncKey (line 67) | func loadConfigStringEncKey(confReader *config.Config, group, field stri...
  function LoadConfigBool (line 76) | func LoadConfigBool(confReader *config.Config, group, field string) bool {
  function LoadOptionalConfigBool (line 84) | func LoadOptionalConfigBool(confReader *config.Config, group, field stri...
  function LoadConfigInt (line 93) | func LoadConfigInt(confReader *config.Config, group, field string) int {
  function LoadOptionalConfigInt (line 101) | func LoadOptionalConfigInt(confReader *config.Config, group, field strin...
  function LoadConfigFloat (line 110) | func LoadConfigFloat(confReader *config.Config, group, field string) flo...
  function LoadOptionalConfigFloat (line 118) | func LoadOptionalConfigFloat(confReader *config.Config, group, field str...
  function LoadConfigDuration (line 127) | func LoadConfigDuration(confReader *config.Config, group, field string) ...
  function LoadOptionalConfigDuration (line 135) | func LoadOptionalConfigDuration(confReader *config.Config, group, field ...
  function LoadConfigURL (line 149) | func LoadConfigURL(confReader *config.Config, group, field string) *url....
  function LoadOptionalConfigURL (line 157) | func LoadOptionalConfigURL(confReader *config.Config, group, field strin...
  function LoadConfigStringCSV (line 175) | func LoadConfigStringCSV(confReader *config.Config, group, field string)...
  function LoadOptionalConfigStringCSV (line 187) | func LoadOptionalConfigStringCSV(confReader *config.Config, group, field...

FILE: docs/docs.go
  constant docTemplate (line 6) | docTemplate = `{
  function init (line 517) | func init() {

FILE: main.go
  constant daemonName (line 38) | daemonName = "l3afd"
  function setupLogging (line 42) | func setupLogging(conf *config.Config) {
  function saveLogsToFile (line 69) | func saveLogsToFile(conf *config.Config) {
  function main (line 88) | func main() {
  function SetupNFConfigs (line 164) | func SetupNFConfigs(ctx context.Context, conf *config.Config) (*bpfprogs...
  function checkKernelVersion (line 186) | func checkKernelVersion(conf *config.Config) error {
  function ReadConfigsFromConfigStore (line 218) | func ReadConfigsFromConfigStore(conf *config.Config) ([]models.L3afBPFPr...
  function setupForRestartOuter (line 248) | func setupForRestartOuter(ctx context.Context, conf *config.Config) error {
  function setupForRestart (line 267) | func setupForRestart(ctx context.Context, conf *config.Config) error {
  function sendState (line 340) | func sendState(s string) {
  function populateVersions (line 368) | func populateVersions(conf *config.Config) {

FILE: main_test.go
  function TestTestConfigValid (line 13) | func TestTestConfigValid(t *testing.T) {

FILE: mocks/mocked_interfaces.go
  type MockplatformInterface (line 14) | type MockplatformInterface struct
    method EXPECT (line 32) | func (m *MockplatformInterface) EXPECT() *MockplatformInterfaceMockRec...
    method GetPlatform (line 37) | func (m *MockplatformInterface) GetPlatform() (string, error) {
  type MockplatformInterfaceMockRecorder (line 20) | type MockplatformInterfaceMockRecorder struct
    method GetPlatform (line 46) | func (mr *MockplatformInterfaceMockRecorder) GetPlatform() *gomock.Call {
  function NewMockplatformInterface (line 25) | func NewMockplatformInterface(ctrl *gomock.Controller) *MockplatformInte...

FILE: models/l3afd.go
  constant Enabled (line 12) | Enabled  = "enabled"
  constant Disabled (line 13) | Disabled = "disabled"
  constant StartType (line 15) | StartType  = "start"
  constant StopType (line 16) | StopType   = "stop"
  constant UpdateType (line 17) | UpdateType = "update"
  constant XDPType (line 19) | XDPType = "xdp"
  constant TCType (line 20) | TCType  = "tc"
  constant IngressType (line 22) | IngressType    = "ingress"
  constant EgressType (line 23) | EgressType     = "egress"
  constant XDPIngressType (line 24) | XDPIngressType = "xdpingress"
  constant TCMapPinPath (line 25) | TCMapPinPath   = "tc/globals"
  constant KProbe (line 27) | KProbe     = "kprobe"
  constant TracePoint (line 28) | TracePoint = "tracepoint"
  constant KRetProbe (line 29) | KRetProbe  = "kretprobe"
  constant UProbe (line 30) | UProbe     = "uprobe"
  constant URetProbe (line 31) | URetProbe  = "uretprobe"
  type L3afDNFArgs (line 34) | type L3afDNFArgs
  type BPFProgram (line 37) | type BPFProgram struct
  type L3afDNFMetricsMap (line 72) | type L3afDNFMetricsMap struct
  type KeyValue (line 79) | type KeyValue struct
  type L3afDMapArg (line 85) | type L3afDMapArg struct
  type L3afBPFPrograms (line 91) | type L3afBPFPrograms struct
  type BPFPrograms (line 99) | type BPFPrograms struct
  type L3afBPFProgramNames (line 107) | type L3afBPFProgramNames struct
  type BPFProgramNames (line 115) | type BPFProgramNames struct
  type MetaColl (line 122) | type MetaColl struct
  type MetaMetricsBPFMap (line 127) | type MetaMetricsBPFMap struct
  type Label (line 135) | type Label struct
  type MetricVec (line 140) | type MetricVec struct
  type L3AFMetaData (line 147) | type L3AFMetaData struct
  type L3AFALLHOSTDATA (line 162) | type L3AFALLHOSTDATA struct
  type RestartConfig (line 173) | type RestartConfig struct
  constant HttpScheme (line 185) | HttpScheme string = "http"
  constant HttpsScheme (line 186) | HttpsScheme string = "https"
  constant FileScheme (line 187) | FileScheme string = "file"
  constant StatusFailed (line 188) | StatusFailed string = "Failed"
  constant StatusReady (line 189) | StatusReady string = "Ready"
  constant HostSock (line 192) | HostSock string = "/tmp/l3afd.sock"
  constant StateSock (line 193) | StateSock string = "/tmp/l3afstate.sock"
  constant L3AFDRestartArtifactName (line 194) | L3AFDRestartArtifactName string = "l3afd.tar.gz"

FILE: pidfile/pidfile.go
  function CheckPIDConflict (line 17) | func CheckPIDConflict(pidFilename string) error {
  function CreatePID (line 79) | func CreatePID(pidFilename string) error {
  function RemovePID (line 88) | func RemovePID(pidFilename string) error {
  function SetupGracefulShutdown (line 96) | func SetupGracefulShutdown(shutdownHandler func() error, shutdownHandler...

FILE: register_internal.go
  function registerL3afD (line 18) | func registerL3afD(conf *config.Config) error {

FILE: restart/restart.go
  function convertBPFMap (line 33) | func convertBPFMap(in []string, g *bpfprogs.BPF, output *map[string]bpfp...
  function getCollection (line 63) | func getCollection(input models.MetaColl, output **ebpf.Collection, b *b...
  function getMetricsMaps (line 90) | func getMetricsMaps(input map[string]models.MetaMetricsBPFMap, b *bpfpro...
  function deserializeProgram (line 133) | func deserializeProgram(ctx context.Context, r *models.L3AFMetaData, hos...
  function getValueofLabel (line 180) | func getValueofLabel(l string, t []models.Label) string {
  function getCountVecByMetricName (line 190) | func getCountVecByMetricName(name string) *prometheus.CounterVec {
  function getGaugeVecByMetricName (line 206) | func getGaugeVecByMetricName(name string) *prometheus.GaugeVec {
  function Convert (line 220) | func Convert(ctx context.Context, t models.L3AFALLHOSTDATA, hostconfig *...
  function SetMetrics (line 278) | func SetMetrics(t models.L3AFALLHOSTDATA) {
  function GetNetListener (line 299) | func GetNetListener(fd int, fname string) (*net.TCPListener, error) {
  function AddSymlink (line 314) | func AddSymlink(sPath, symlink string) error {
  function RemoveSymlink (line 320) | func RemoveSymlink(symlink string) error {
  function ReadSymlink (line 326) | func ReadSymlink(symlink string) (string, error) {
  function GetNewVersion (line 335) | func GetNewVersion(artifactName, oldVersion, newVersion string, conf *co...
  function RollBackSymlink (line 389) | func RollBackSymlink(oldCfgPath, oldBinPath string, oldVersion, newVersi...

FILE: restart/restart_test.go
  function TestGetValueofLabel (line 12) | func TestGetValueofLabel(t *testing.T) {

FILE: routes/route.go
  type Route (line 9) | type Route struct

FILE: routes/router.go
  function NewRouter (line 12) | func NewRouter(routes []Route) *chi.Mux {

FILE: stats/metrics.go
  function SetupMetrics (line 29) | func SetupMetrics(hostname, daemonName, metricsAddr string) {
  function Add (line 157) | func Add(value float64, counterVec *prometheus.CounterVec, ebpfProgram, ...
  function Set (line 179) | func Set(value float64, gaugeVec *prometheus.GaugeVec, ebpfProgram, dire...
  function SetValue (line 202) | func SetValue(value float64, gaugeVec *prometheus.GaugeVec, ebpfProgram,...
  function SetWithVersion (line 224) | func SetWithVersion(value float64, gaugeVec *prometheus.GaugeVec, ebpfPr...

FILE: utils/utils.go
  function GetKernelVersion (line 16) | func GetKernelVersion() (string, error) {
  function CheckTCXSupport (line 31) | func CheckTCXSupport() bool {
  function ReplaceDotsWithUnderscores (line 66) | func ReplaceDotsWithUnderscores(version string) string {
  function LinkPinPath (line 71) | func LinkPinPath(bpfMapDefaultPath, ifaceName, programName, version, pro...
  function TCLinkPinPath (line 76) | func TCLinkPinPath(bpfMapDefaultPath, ifaceName, programName, version, p...
  function ProgPinPath (line 81) | func ProgPinPath(bpfMapDefaultPath, ifaceName, programName, version, ent...

FILE: version.go
  function initVersion (line 25) | func initVersion() {
  function VersionInfo (line 33) | func VersionInfo() string {
  function ShortVersion (line 55) | func ShortVersion() string {
Condensed preview — 80 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (530K chars).
[
  {
    "path": ".github/dependabot.yml",
    "chars": 602,
    "preview": "# Copyright Contributors to the L3AF Project.\n# SPDX-License-Identifier: Apache-2.0\n#\n# For documentation on the format "
  },
  {
    "path": ".github/release.yml",
    "chars": 410,
    "preview": "# .github/release.yml\n---\nchangelog:\n  exclude:\n    labels:\n      - ignore-for-release\n  categories:\n    - title: Breaki"
  },
  {
    "path": ".github/workflows/ci-build-windows.yaml",
    "chars": 2048,
    "preview": "# Copyright Contributors to the L3AF Project.\n# SPDX-License-Identifier: Apache-2.0\n#\n# For documentation on the github "
  },
  {
    "path": ".github/workflows/ci-build.yaml",
    "chars": 3689,
    "preview": "# Copyright Contributors to the L3AF Project.\n# SPDX-License-Identifier: Apache-2.0\n#\n# For documentation on the github "
  },
  {
    "path": ".github/workflows/ci-e2e.yaml",
    "chars": 1541,
    "preview": "# Copyright Contributors to the L3AF Project.\n# SPDX-License-Identifier: Apache-2.0\n#\n# For documentation on the github "
  },
  {
    "path": ".github/workflows/codeql.yaml",
    "chars": 1361,
    "preview": "# Copyright Contributors to the L3AF Project.\n# SPDX-License-Identifier: Apache-2.0\n#\n# For documentation on the github "
  },
  {
    "path": ".github/workflows/scorecards-analysis.yml",
    "chars": 2895,
    "preview": "# Copyright Contributors to the L3AF Project.\n# SPDX-License-Identifier: Apache-2.0\n#\n# For documentation on the github "
  },
  {
    "path": ".gitignore",
    "chars": 15,
    "preview": ".idea/\nvendor/\n"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 1559,
    "preview": "cmake_minimum_required(VERSION 3.10)\nproject(l3afd)\n\nadd_custom_target(swagger ALL\n                  DEPENDS ${CMAKE_SOU"
  },
  {
    "path": "CODEOWNERS",
    "chars": 79,
    "preview": "# Default Code Owners\n\n* @sanfern @charleskbliu0 @jniesz @dalalkaran @pmoroney\n"
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "Makefile",
    "chars": 613,
    "preview": ".PHONY: all\n\nexport GOPATH := $(HOME)/go\nall: swagger build\n\nswagger:\n\t@mkdir $(GOPATH) || true \n\t@go install github.com"
  },
  {
    "path": "README.md",
    "chars": 3103,
    "preview": "# L3AFD: Lightweight eBPF Daemon\n![L3AF_Logo](https://github.com/l3af-project/l3af-arch/blob/main/images/logos/Color/L3A"
  },
  {
    "path": "apis/configwatch.go",
    "chars": 10571,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !configs\n// +build !"
  },
  {
    "path": "apis/configwatch_test.go",
    "chars": 4497,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage apis\n\nimport (\n\t\"reflect\""
  },
  {
    "path": "apis/handlers/addprog.go",
    "chars": 2224,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage handlers\n\nimport (\n\t\"cont"
  },
  {
    "path": "apis/handlers/addprog_test.go",
    "chars": 2781,
    "preview": "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."
  },
  {
    "path": "apis/handlers/deleteprog.go",
    "chars": 2049,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage handlers\n\nimport (\n\t\"cont"
  },
  {
    "path": "apis/handlers/deleteprog_test.go",
    "chars": 2888,
    "preview": "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."
  },
  {
    "path": "apis/handlers/getconfig.go",
    "chars": 2583,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage handlers\n\nimport (\n\t\"enco"
  },
  {
    "path": "apis/handlers/getconfig_test.go",
    "chars": 1688,
    "preview": "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"
  },
  {
    "path": "apis/handlers/restart_linux.go",
    "chars": 10530,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage handlers\n\nimport (\n\t\"enco"
  },
  {
    "path": "apis/handlers/restart_linux_test.go",
    "chars": 643,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\npackage handlers\n\nimport (\n\t\"net/h"
  },
  {
    "path": "apis/handlers/restart_windows.go",
    "chars": 1054,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage handlers\n\nimport (\n\t\"net/"
  },
  {
    "path": "apis/handlers/restart_windows_test.go",
    "chars": 501,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\npackage handlers\n\nimport (\n\t\"net/h"
  },
  {
    "path": "apis/handlers/updateconfig.go",
    "chars": 2038,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage handlers\n\nimport (\n\t\"cont"
  },
  {
    "path": "apis/handlers/updateconfig_test.go",
    "chars": 2246,
    "preview": "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."
  },
  {
    "path": "apis/routes.go",
    "chars": 1158,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage apis\n\nimport (\n\t\"context\""
  },
  {
    "path": "bpfprogs/bpf.go",
    "chars": 59752,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package bpfprogs provides prim"
  },
  {
    "path": "bpfprogs/bpfCfgs_internal.go",
    "chars": 413,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !configs\n// +build !"
  },
  {
    "path": "bpfprogs/bpf_test.go",
    "chars": 22417,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"arch"
  },
  {
    "path": "bpfprogs/bpf_test_unix.go",
    "chars": 782,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !WINDOWS\n// +build !"
  },
  {
    "path": "bpfprogs/bpf_test_windows.go",
    "chars": 969,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build WINDOWS\n// +build WI"
  },
  {
    "path": "bpfprogs/bpf_unix.go",
    "chars": 17239,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !WINDOWS\n// +build !"
  },
  {
    "path": "bpfprogs/bpf_windows.go",
    "chars": 2543,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build WINDOWS\n// +build WI"
  },
  {
    "path": "bpfprogs/bpfdebug.go",
    "chars": 1509,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"enco"
  },
  {
    "path": "bpfprogs/bpfmap.go",
    "chars": 4521,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"cont"
  },
  {
    "path": "bpfprogs/bpfmap_test.go",
    "chars": 1828,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"cont"
  },
  {
    "path": "bpfprogs/bpfmetrics.go",
    "chars": 2004,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package bpfprogs provides prim"
  },
  {
    "path": "bpfprogs/bpfmetrics_test.go",
    "chars": 1924,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"cont"
  },
  {
    "path": "bpfprogs/nfconfig.go",
    "chars": 64585,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package bpfprogs provides prim"
  },
  {
    "path": "bpfprogs/nfconfig_test.go",
    "chars": 27967,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"cont"
  },
  {
    "path": "bpfprogs/probes.go",
    "chars": 4970,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !WINDOWS\n// +build !"
  },
  {
    "path": "bpfprogs/probes_test.go",
    "chars": 1210,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !WINDOWS\n// +build !"
  },
  {
    "path": "bpfprogs/processCheck.go",
    "chars": 5558,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package bpfprogs provides prim"
  },
  {
    "path": "bpfprogs/processCheck_test.go",
    "chars": 2069,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage bpfprogs\n\nimport (\n\t\"cont"
  },
  {
    "path": "build-docker/Dockerfile",
    "chars": 1138,
    "preview": "FROM ubuntu:jammy@sha256:6d7b5d3317a71adb5e175640150e44b8b9a9401a7dd394f44840626aff9fa94d\n\nARG DEBIAN_FRONTEND=nonintera"
  },
  {
    "path": "build-docker/start.sh",
    "chars": 800,
    "preview": "#!/bin/bash\n\n# Start 'l3afd' in the background\n/usr/local/l3afd/latest/l3afd --config /usr/local/l3afd/latest/l3afd.cfg "
  },
  {
    "path": "config/config.go",
    "chars": 12148,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package config provides primit"
  },
  {
    "path": "config/config_loader.go",
    "chars": 7226,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage config\n\nimport (\n\t\"encodi"
  },
  {
    "path": "config/l3afd.cfg",
    "chars": 1876,
    "preview": "[DEFAULT]\n\n[l3afd]\npid-file: /var/run/l3afd.pid\ndatacenter: dummy\nbpf-dir: /dev/shm\nbpf-log-dir:\nkernel-major-version: 5"
  },
  {
    "path": "config.yml",
    "chars": 62,
    "preview": "register_admind.go\nbpfprogs/cdbhelpers.go\nbpfprogs/bpfCfgs.go\n"
  },
  {
    "path": "docs/CONTRIBUTING.md",
    "chars": 4815,
    "preview": "# How To Contribute\n\n## Some Ways to Contribute\n- [Getting started with L3AF](#getting-started-with-l3af)\n- [Report pote"
  },
  {
    "path": "docs/api/README.md",
    "chars": 14359,
    "preview": "# L3AFD API Documentation\n\n# Update API\n\nSee [payload.json](https://github.com/l3af-project/l3af-arch/blob/main/dev_envi"
  },
  {
    "path": "docs/configdoc.md",
    "chars": 13035,
    "preview": "# L3AFD Config Options Documentation\n\nSee [l3afd.cfg](https://github.com/l3af-project/l3afd/blob/main/config/l3afd.cfg) "
  },
  {
    "path": "docs/docs.go",
    "chars": 17867,
    "preview": "// Package docs Code generated by swaggo/swag. DO NOT EDIT\npackage docs\n\nimport \"github.com/swaggo/swag\"\n\nconst docTempl"
  },
  {
    "path": "docs/graceful-restart-guide.md",
    "chars": 2309,
    "preview": "# Guide to L3AFD Graceful Restart\n\n## Prerequisites\nTo begin, ensure that you have a specific folder where the `l3afd` b"
  },
  {
    "path": "docs/prod-deploy-guide.md",
    "chars": 2207,
    "preview": "# Guide to use L3AF in production environments\n\n## Installing l3afd\n\nDownload the latest build artifacts for the last st"
  },
  {
    "path": "docs/swagger.json",
    "chars": 17184,
    "preview": "{\n    \"swagger\": \"2.0\",\n    \"info\": {\n        \"description\": \"Configuration APIs to deploy and get the details of the eB"
  },
  {
    "path": "docs/swagger.md",
    "chars": 1209,
    "preview": "## Swaggo setup\n\nOur first task is to install the libraries we are dependent on. Run the following commands from the com"
  },
  {
    "path": "docs/swagger.yaml",
    "chars": 8964,
    "preview": "basePath: /\ndefinitions:\n  models.BPFProgram:\n    properties:\n      admin_status:\n        description: Program admin sta"
  },
  {
    "path": "go.mod",
    "chars": 1818,
    "preview": "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 "
  },
  {
    "path": "go.sum",
    "chars": 23814,
    "preview": "github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=\ngithub.com/KyleBanks/depth v1.2.1/go.m"
  },
  {
    "path": "main.go",
    "chars": 11278,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"context\""
  },
  {
    "path": "main_test.go",
    "chars": 460,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage main\n\nimport (\n\t\"os\"\n\t\"te"
  },
  {
    "path": "mocks/mocked_interfaces.go",
    "chars": 1500,
    "preview": "// Code generated by MockGen. DO NOT EDIT.\n// Source: mock_interfaces.go\n\n// Package mocks is a generated GoMock package"
  },
  {
    "path": "models/l3afd.go",
    "chars": 7650,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage models\n\nimport (\n\t\"sync\"\n"
  },
  {
    "path": "pidfile/pidfile.go",
    "chars": 5292,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage pidfile\n\nimport (\n\t\"fmt\"\n"
  },
  {
    "path": "register_internal.go",
    "chars": 483,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !admind\n// +build !a"
  },
  {
    "path": "restart/restart.go",
    "chars": 13319,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package restart provides primi"
  },
  {
    "path": "restart/restart_test.go",
    "chars": 442,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage restart\n\nimport (\n\t\"testi"
  },
  {
    "path": "routes/route.go",
    "chars": 288,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage routes\n\nimport \"net/http\""
  },
  {
    "path": "routes/router.go",
    "chars": 465,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage routes\n\nimport (\n\tchi \"gi"
  },
  {
    "path": "signals/signal_unix.go",
    "chars": 293,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build !WINDOWS\n// +build !"
  },
  {
    "path": "signals/signal_windows.go",
    "chars": 210,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n//\n//go:build WINDOWS\n// +build WI"
  },
  {
    "path": "stats/metrics.go",
    "chars": 8038,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\npackage stats\n\nimport (\n\t\"errors\""
  },
  {
    "path": "testdata/Test_l3af-config.json",
    "chars": 5,
    "preview": "null\n"
  },
  {
    "path": "utils/utils.go",
    "chars": 2444,
    "preview": "// Copyright Contributors to the L3AF Project.\n// SPDX-License-Identifier: Apache-2.0\n\n// Package utils provides helper "
  },
  {
    "path": "version.go",
    "chars": 1466,
    "preview": "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 strin"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the l3af-project/l3afd GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 80 files (472.1 KB), approximately 138.2k tokens, and a symbol index with 351 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!