diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e829cb6..e9c3b59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: cd ../../ clang -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -Wextra -target bpf -c -o files/bin/zfw_tc_ingress.o src/zfw_tc_ingress.c clang -g -O2 -Wall -Wextra -target bpf -c -o files/bin/zfw_xdp_tun_ingress.o src/zfw_xdp_tun_ingress.c - clang -g -O2 -Wall -Wextra -target bpf -c -o files/bin/zfw_tc_outbound_track.o src/zfw_tc_outbound_track.c + clang -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -Wextra -target bpf -c -o files/bin/zfw_tc_outbound_track.o src/zfw_tc_outbound_track.c clang -g -O2 -Wall -D BPF_MAX_ENTRIES=100000 -O1 src/zfw.c -L ../../libbpf/src/root/usr/lib64/ -lbpf -lelf -lz -o files/bin/zfw -static clang -g -O2 -Wall -O1 src/zfw_monitor.c -L ../../libbpf/src/root/usr/lib64/ -lbpf -lelf -lz -o files/bin/zfw_monitor -static gcc -o files/bin/zfw_tunnwrapper src/zfw_tunnel_wrapper.c -l json-c @@ -164,7 +164,7 @@ jobs: cd ../../ clang -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -Wextra -target bpf -c -o files/bin/zfw_tc_ingress.o src/zfw_tc_ingress.c clang -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -Wextra -target bpf -c -o files/bin/zfw_xdp_tun_ingress.o src/zfw_xdp_tun_ingress.c - clang -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -Wextra -target bpf -c -o files/bin/zfw_tc_outbound_track.o src/zfw_tc_outbound_track.c + clang -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -Wextra -target bpf -c -o files/bin/zfw_tc_outbound_track.o src/zfw_tc_outbound_track.c clang -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -D BPF_MAX_ENTRIES=100000 -O1 src/zfw.c -L ../../libbpf/src/root/usr/lib64/ -lbpf -lelf -lz -o files/bin/zfw -static clang -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -O1 src/zfw_monitor.c -L ../../libbpf/src/root/usr/lib64/ -lbpf -lelf -lz -o files/bin/zfw_monitor -static gcc -o files/bin/zfw_tunnwrapper src/zfw_tunnel_wrapper.c -l json-c diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..c312cfd --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,343 @@ +--- +name: pr + +on: + pull_request: + types: [opened, synchronize] + +env: + APP_NAME: 'zfw' + MAINTAINER: 'Robert Caamano' + DESC: 'An ebpf based statefull fw for openziti edge-routers and tunnelers' + ROUTER_PREFIX: 'zfw-er' + TF_VAR_test_iterate_count: ${{ vars.TEST_ITERATE_COUNT }} + NF_API_CLIENT_ID: "${{ secrets.NF_API_CLIENT_ID }}" + NF_API_CLIENT_SECRET: "${{ secrets.NF_API_CLIENT_SECRET }}" + +jobs: + build_amd64_release: + runs-on: ubuntu-22.04 + outputs: + version: ${{ steps.version.outputs.version }} + strategy: + matrix: + goos: [linux] + ziti_type: [tunnel, router] + goarch: [amd64] + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Install EBPF Packages + run: | + sudo apt-get update -qq + sudo apt-get upgrade -yqq + sudo apt-get install -y jq gcc clang libc6-dev-i386 libbpfcc-dev libbpf-dev libjson-c-dev alien + + - name: Compile Object file from Source + run: | + git clone https://github.com/libbpf/libbpf.git + cd libbpf/src + mkdir build root + BUILD_STATIC_ONLY=y OBJDIR=build DESTDIR=root make install + cd ../../ + clang -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -Wextra -target bpf -c -o files/bin/zfw_tc_ingress.o src/zfw_tc_ingress.c + clang -g -O2 -Wall -Wextra -target bpf -c -o files/bin/zfw_xdp_tun_ingress.o src/zfw_xdp_tun_ingress.c + clang -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -Wextra -target bpf -c -o files/bin/zfw_tc_outbound_track.o src/zfw_tc_outbound_track.c + clang -g -O2 -Wall -D BPF_MAX_ENTRIES=100000 -O1 src/zfw.c -L ../../libbpf/src/root/usr/lib64/ -lbpf -lelf -lz -o files/bin/zfw -static + clang -g -O2 -Wall -O1 src/zfw_monitor.c -L ../../libbpf/src/root/usr/lib64/ -lbpf -lelf -lz -o files/bin/zfw_monitor -static + gcc -o files/bin/zfw_tunnwrapper src/zfw_tunnel_wrapper.c -l json-c + + - name: Get version + run: echo "version=`files/bin/zfw -V`" >> $GITHUB_OUTPUT + id: version + + - name: Deb directory + run: echo "deb_dir=${{ env.APP_NAME }}-${{ matrix.ziti_type }}_${{ steps.version.outputs.version }}_${{ matrix.goarch }}" >> $GITHUB_OUTPUT + id: deb_dir + + - name: Deb Object File + run: | + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN + touch ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + echo Package: ${{ env.APP_NAME }}-${{ matrix.ziti_type }} >> ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + echo Version: ${{ steps.version.outputs.version }} >> ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + echo Architecture: ${{ matrix.goarch }} >> ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + echo Maintainer: ${{ env.MAINTAINER }} >> ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + echo Description: ${{ env.DESC }} >> ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/user + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/etc + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/etc/systemd/system + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/usr/sbin + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/etc/logrotate.d + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/etc/cron.d + cp -p CHANGELOG.md ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p README.md ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p LICENSE ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/bin/zfw_tc_ingress.o ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/bin/zfw_tc_outbound_track.o ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/bin/zfw ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/bin/zfw_monitor ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/scripts/start_ebpf_${{ matrix.ziti_type }}.py ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/scripts/user_rules.sh.sample ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/user/ + cp -p files/scripts/zfwlogs ${{ steps.deb_dir.outputs.deb_dir }}/etc/logrotate.d/ + cp -p files/scripts/zfw_refresh ${{ steps.deb_dir.outputs.deb_dir }}/etc/cron.d/ + cp -p files/json/ebpf_config.json.sample ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/etc/ + cp -p files/services/zfw-logging.service ${{ steps.deb_dir.outputs.deb_dir }}/etc/systemd/system/ + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/zfw + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/zfw_monitor + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/start_ebpf_${{ matrix.ziti_type }}.py + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/user/user_rules.sh.sample + chmod 644 ${{ steps.deb_dir.outputs.deb_dir }}/etc/cron.d/zfw_refresh + ln -s /opt/openziti/bin/zfw ${{ steps.deb_dir.outputs.deb_dir }}/usr/sbin/zfw + ln -s /opt/openziti/bin/zfw_monitor ${{ steps.deb_dir.outputs.deb_dir }}/usr/sbin/zfw_monitor + + - name: Set Deb Predepends + if: ${{ matrix.ziti_type == 'tunnel' }} + run: | + echo 'Pre-Depends: ziti-edge-tunnel (>= 0.22.5)' >> ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + cp -p files/services/ziti-fw-init.service ${{ steps.deb_dir.outputs.deb_dir }}/etc/systemd/system/ + cp -p files/services/ziti-wrapper.service ${{ steps.deb_dir.outputs.deb_dir }}/etc/systemd/system/ + cp -p files/bin/zfw_tunnwrapper ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/scripts/set_xdp_redirect.py ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/bin/zfw_xdp_tun_ingress.o ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/zfw_tunnwrapper + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/set_xdp_redirect.py + + - name: Standalone FW service and router revert + if: ${{ matrix.ziti_type == 'router' }} + run: | + cp -p files/services/fw-init.service ${{ steps.deb_dir.outputs.deb_dir }}/etc/systemd/system/ + cp -p files/scripts/revert_ebpf_router.py ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/scripts/start_ebpf_controller.py ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/scripts/revert_ebpf_controller.py ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/revert_ebpf_router.py + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/start_ebpf_controller.py + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/revert_ebpf_controller.py + + - name: Build Deb package + run: | + dpkg-deb --build -Z gzip --root-owner-group ${{ steps.deb_dir.outputs.deb_dir }} + + - name: Build rpm package + run: | + sudo alien -r ${{ steps.deb_dir.outputs.deb_dir }}.deb + mv ${{ env.APP_NAME }}-${{ matrix.ziti_type }}-${{ steps.version.outputs.version }}-2.x86_64.rpm ${{ env.APP_NAME }}-${{ matrix.ziti_type }}-${{ steps.version.outputs.version }}.x86_64.rpm + + - uses: actions/upload-artifact@v4 + with: + name: artifact-${{ matrix.ziti_type }}-${{ matrix.goarch }}-deb + path: | + ./*.deb + + - uses: actions/upload-artifact@v4 + with: + name: artifact-${{ matrix.ziti_type }}-${{ matrix.goarch }}-rpm + path: | + ./*.rpm + + + build_arm64_release: + runs-on: [self-hosted, linux, ARM64] + outputs: + version: ${{ steps.version.outputs.version }} + strategy: + matrix: + goos: [linux] + ziti_type: [tunnel, router] + goarch: [arm64] + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Install EBPF Packages + run: | + sudo apt-get update -qq + sudo apt-get upgrade -yqq + sudo apt-get install -y jq gcc clang libbpfcc-dev libbpf-dev libjson-c-dev + sudo apt-get install -y linux-headers-$(uname -r) + + - name: Compile Object file from Source + run: | + git clone https://github.com/libbpf/libbpf.git + cd libbpf/src + mkdir build root + BUILD_STATIC_ONLY=y OBJDIR=build DESTDIR=root make install + cd ../../ + clang -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -Wextra -target bpf -c -o files/bin/zfw_tc_ingress.o src/zfw_tc_ingress.c + clang -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -Wextra -target bpf -c -o files/bin/zfw_xdp_tun_ingress.o src/zfw_xdp_tun_ingress.c + clang -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -Wextra -target bpf -c -o files/bin/zfw_tc_outbound_track.o src/zfw_tc_outbound_track.c + clang -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -D BPF_MAX_ENTRIES=100000 -O1 src/zfw.c -L ../../libbpf/src/root/usr/lib64/ -lbpf -lelf -lz -o files/bin/zfw -static + clang -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -O1 src/zfw_monitor.c -L ../../libbpf/src/root/usr/lib64/ -lbpf -lelf -lz -o files/bin/zfw_monitor -static + gcc -o files/bin/zfw_tunnwrapper src/zfw_tunnel_wrapper.c -l json-c + + - name: Get version + run: echo "version=`files/bin/zfw -V`" >> $GITHUB_OUTPUT + id: version + + - name: Deb directory + run: echo "deb_dir=${{ env.APP_NAME }}-${{ matrix.ziti_type }}_${{ steps.version.outputs.version }}_${{ matrix.goarch }}" >> $GITHUB_OUTPUT + id: deb_dir + + - name: Deb artifact directory setup + run: | + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN + touch ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + echo Package: ${{ env.APP_NAME }}-${{ matrix.ziti_type }} >> ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + echo Version: ${{ steps.version.outputs.version }} >> ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + echo Architecture: ${{ matrix.goarch }} >> ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + echo Maintainer: ${{ env.MAINTAINER }} >> ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + echo Description: ${{ env.DESC }} >> ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/user + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/etc + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/etc/systemd/system + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/usr/sbin + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/etc/logrotate.d + mkdir -p ${{ steps.deb_dir.outputs.deb_dir }}/etc/cron.d + cp -p CHANGELOG.md ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p README.md ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p LICENSE ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/bin/zfw_tc_ingress.o ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/bin/zfw_tc_outbound_track.o ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/bin/zfw ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/bin/zfw_monitor ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/scripts/start_ebpf_${{ matrix.ziti_type }}.py ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/scripts/user_rules.sh.sample ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/user/ + cp -p files/scripts/zfwlogs ${{ steps.deb_dir.outputs.deb_dir }}/etc/logrotate.d/ + cp -p files/scripts/zfw_refresh ${{ steps.deb_dir.outputs.deb_dir }}/etc/cron.d/ + cp -p files/json/ebpf_config.json.sample ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/etc/ + cp -p files/services/zfw-logging.service ${{ steps.deb_dir.outputs.deb_dir }}/etc/systemd/system/ + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/zfw + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/zfw_monitor + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/start_ebpf_${{ matrix.ziti_type }}.py + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/user/user_rules.sh.sample + chmod 644 ${{ steps.deb_dir.outputs.deb_dir }}/etc/cron.d/zfw_refresh + ln -s /opt/openziti/bin/zfw ${{ steps.deb_dir.outputs.deb_dir }}/usr/sbin/zfw + ln -s /opt/openziti/bin/zfw_monitor ${{ steps.deb_dir.outputs.deb_dir }}/usr/sbin/zfw_monitor + + - name: Set Deb Predepends + if: ${{ matrix.ziti_type == 'tunnel' }} + run: | + echo 'Pre-Depends: ziti-edge-tunnel (>= 0.22.5)' >> ${{ steps.deb_dir.outputs.deb_dir }}/DEBIAN/control + cp -p files/services/ziti-fw-init.service ${{ steps.deb_dir.outputs.deb_dir }}/etc/systemd/system/ + cp -p files/services/ziti-wrapper.service ${{ steps.deb_dir.outputs.deb_dir }}/etc/systemd/system/ + cp -p files/bin/zfw_tunnwrapper ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/scripts/set_xdp_redirect.py ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/bin/zfw_xdp_tun_ingress.o ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/zfw_tunnwrapper + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/set_xdp_redirect.py + + - name: Standalone FW service and router revert + if: ${{ matrix.ziti_type == 'router' }} + run: | + cp -p files/services/fw-init.service ${{ steps.deb_dir.outputs.deb_dir }}/etc/systemd/system/ + cp -p files/scripts/revert_ebpf_router.py ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/scripts/start_ebpf_controller.py ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + cp -p files/scripts/revert_ebpf_controller.py ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/ + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/revert_ebpf_router.py + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/start_ebpf_controller.py + chmod 744 ${{ steps.deb_dir.outputs.deb_dir }}/opt/openziti/bin/revert_ebpf_controller.py + + - name: Build deb package + run: | + dpkg-deb --build -Z gzip --root-owner-group ${{ steps.deb_dir.outputs.deb_dir }} + + - uses: actions/upload-artifact@v4 + with: + name: artifact-${{ matrix.ziti_type }}-${{ matrix.goarch }}-deb + path: | + ./*.deb + + + regression_test: + needs: [build_amd64_release, build_arm64_release] + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - + name: Checkout + uses: actions/checkout@v4 + with: + repository: netfoundry/cloud-network-lb-ingress + - + name: Authenticate to AWS Cloud + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: us-east-1 + role-to-assume: ${{ secrets.AWS_ROLE_FOR_GITHUB }} + role-session-name: GitHubActions + audience: sts.amazonaws.com + role-duration-seconds: 14400 + - + name: Install terraform jq + run: | + sudo apt-get update + sudo apt-get install -y gnupg software-properties-common + wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | \ + sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null + echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \ + https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \ + sudo tee /etc/apt/sources.list.d/hashicorp.list + shell: bash + - + name: Start test + if: success() || failure() + run: | + cd ${{ github.workspace }}/AWS/tf-provider/ + ssh-keygen -t rsa -b 4096 -C "cldeng@netfoundry.io" -f ./zfw_rsa -q -N "" + export TF_VAR_ssh_public_key=`cat ./zfw_rsa.pub` + ./test_cases.sh run + shell: bash + - + name: Check intercept side test result + if: success() || failure() + run: | + set +e + cd ${{ github.workspace }}/AWS/tf-provider/ + while : + do + sleep 900 + /usr/bin/ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ./zfw_rsa ziggy@$(terraform output -json | jq -r .client_public_ips.value[0]) -tq '/usr/bin/tail -n 1 /var/log/http_test.json' > ${{ github.workspace }}/AWS/tf-provider/result + /usr/bin/ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ./zfw_rsa ziggy@$(terraform output -json | jq -r .client_public_ips.value[1]) -tq '/usr/bin/tail -n 1 /var/log/http_test.json' >> ${{ github.workspace }}/AWS/tf-provider/result + /usr/bin/cat ${{ github.workspace }}/AWS/tf-provider/result + PASS=`/usr/bin/cat ${{ github.workspace }}/AWS/tf-provider/result | grep Passed |wc -l` + FAIL=`/usr/bin/cat ${{ github.workspace }}/AWS/tf-provider/result | grep Failed |wc -l` + echo $PASS + echo $FAIL + if [ $PASS == 2 ]; then + echo -e "\033[32mPASSED\033[m" + cat ./result + exit 0 + elif [ $PASS == 1 ]; then + echo -e "\033[33mPARTIALLYPASSED\033[m" + cat ./result + exit 1 + elif [ $FAIL == 2 ]; then + echo -e "\033[31mFAILED\033[m" + cat ./result + exit 1 + else + cat ./result + continue + fi + done + shell: bash + timeout-minutes: 180 + - + name: Clean up test + if: success() || failure() + run: | + cd ${{ github.workspace }}/AWS/tf-provider/ + export TF_VAR_ssh_public_key=`cat ./zfw_rsa.pub` + ./test_cases.sh cleanup + rm ./zfw_rsa* + shell: bash + + + + + + + \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2ffaeee..d5c93d9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,7 +39,7 @@ jobs: cd ../../ clang -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -Wextra -target bpf -c -o files/bin/zfw_tc_ingress.o src/zfw_tc_ingress.c clang -g -O2 -Wall -Wextra -target bpf -c -o files/bin/zfw_xdp_tun_ingress.o src/zfw_xdp_tun_ingress.c - clang -g -O2 -Wall -Wextra -target bpf -c -o files/bin/zfw_tc_outbound_track.o src/zfw_tc_outbound_track.c + clang -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -Wextra -target bpf -c -o files/bin/zfw_tc_outbound_track.o src/zfw_tc_outbound_track.c clang -g -O2 -Wall -D BPF_MAX_ENTRIES=100000 -O1 src/zfw.c -L ../../libbpf/src/root/usr/lib64/ -lbpf -lelf -lz -o files/bin/zfw -static clang -g -O2 -Wall -O1 src/zfw_monitor.c -L ../../libbpf/src/root/usr/lib64/ -lbpf -lelf -lz -o files/bin/zfw_monitor -static gcc -o files/bin/zfw_tunnwrapper src/zfw_tunnel_wrapper.c -l json-c @@ -162,7 +162,7 @@ jobs: cd ../../ clang -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -Wextra -target bpf -c -o files/bin/zfw_tc_ingress.o src/zfw_tc_ingress.c clang -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -Wextra -target bpf -c -o files/bin/zfw_xdp_tun_ingress.o src/zfw_xdp_tun_ingress.c - clang -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -Wextra -target bpf -c -o files/bin/zfw_tc_outbound_track.o src/zfw_tc_outbound_track.c + clang -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -Wextra -target bpf -c -o files/bin/zfw_tc_outbound_track.o src/zfw_tc_outbound_track.c clang -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -D BPF_MAX_ENTRIES=100000 -O1 src/zfw.c -L ../../libbpf/src/root/usr/lib64/ -lbpf -lelf -lz -o files/bin/zfw -static clang -g -O2 -Wall -I /usr/include/aarch64-linux-gnu/ -O1 src/zfw_monitor.c -L ../../libbpf/src/root/usr/lib64/ -lbpf -lelf -lz -o files/bin/zfw_monitor -static gcc -o files/bin/zfw_tunnwrapper src/zfw_tunnel_wrapper.c -l json-c diff --git a/CHANGELOG.md b/CHANGELOG.md index d8583ef..3b6efd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). --- +### +# [0.8.13] - 2024-08-12 +- Added Outbound tracking for IPv4 and IPv6 ICMP Echo +- Added Masquerade for passthrough icmp echos. +- Fixed an issue where both the packages and Makefile were limiting egress rule entries to 100 instead of 100000. +- Fixed issue where incorrect count check was being performed on insert for ipv6 rules to verify if they had reached + BPF_MAX_ENTRIES. + ### # [0.8.12] - 2024-08-07 - Change ci workflow display name and to trigger on push to branches other than main. diff --git a/src/Makefile b/src/Makefile index 83c1092..1d7164e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -30,9 +30,9 @@ else endif zfw_tc_outbound_track.o: zfw_tc_outbound_track.c ifeq ($(uname_m),aarch64) - $(CC) -g -O2 -Wall -Wextra -target bpf -c -o zfw_tc_outbound_track.o zfw_tc_outbound_track.c $(CFLAGS) + $(CC) -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -Wextra -target bpf -c -o zfw_tc_outbound_track.o zfw_tc_outbound_track.c $(CFLAGS) else - $(CC) -g -O2 -Wall -Wextra -target bpf -c -o zfw_tc_outbound_track.o zfw_tc_outbound_track.c + $(CC) -D BPF_MAX_ENTRIES=100000 -g -O2 -Wall -Wextra -target bpf -c -o zfw_tc_outbound_track.o zfw_tc_outbound_track.c endif zfw_tunnwrapper: zfw_tunnel_wrapper.c $(CC) -o zfw_tunnwrapper zfw_tunnel_wrapper.c -l json-c diff --git a/src/zfw.c b/src/zfw.c index 7d371cb..1a4bd1c 100644 --- a/src/zfw.c +++ b/src/zfw.c @@ -88,6 +88,9 @@ #define INGRESS_SERVER_RST_RCVD 24 #define INGRESS_SERVER_FINAL_ACK_RCVD 25 #define MATCHED_DROP_FILTER 26 +#define ICMP_MATCHED_EXPIRED_STATE 27 +#define ICMP_MATCHED_ACTIVE_STATE 28 +#define CLIENT_INITIATED_ICMP_ECHO 29 #define IP6_HEADER_TOO_BIG 30 #define IPV6_TUPLE_TOO_BIG 31 @@ -215,6 +218,8 @@ const char *egress6_map_path = "/sys/fs/bpf/tc/globals/zt_egress6_map"; const char *egress_count_map_path = "/sys/fs/bpf/tc/globals/egress_count_map"; const char *egress_count6_map_path = "/sys/fs/bpf/tc/globals/egress6_count_map"; const char *masquerade_map_path = "/sys/fs/bpf/tc/globals/masquerade_map"; +const char *icmp_masquerade_map_path = "/sys/fs/bpf/tc/globals/icmp_masquerade_map"; +const char *icmp_echo_map_path = "/sys/fs/bpf/tc/globals/icmp_echo_map"; char doc[] = "zfw -- ebpf firewall configuration tool"; const char *if_map_path; char *diag_interface; @@ -236,7 +241,7 @@ char *direction_string; char *masq_interface; char check_alt[IF_NAMESIZE]; -const char *argp_program_version = "0.8.12"; +const char *argp_program_version = "0.8.13"; struct ring_buffer *ring_buffer; __u32 if_list[MAX_IF_LIST_ENTRIES]; @@ -306,6 +311,7 @@ void open_range_map(); void if_list_ext_delete_key(struct port_extension_key key); bool interface_map(); void interface_map6(); +int get_key_count6(); void close_maps(int code); void if_delete_key(uint32_t key); void if6_delete_key(uint32_t key); @@ -651,14 +657,15 @@ void disable_ebpf() disable = true; tc = true; interface_tc(); - const char *maps[34] = {tproxy_map_path, diag_map_path, if_map_path, count_map_path, + const char *maps[36] = {tproxy_map_path, diag_map_path, if_map_path, count_map_path, udp_map_path, matched_map_path, tcp_map_path, tun_map_path, if_tun_map_path, transp_map_path, rb_map_path, ddos_saddr_map_path, ddos_dport_map_path, syn_count_map_path, tp_ext_map_path, if_list_ext_map_path, range_map_path, wildcard_port_map_path, tproxy6_map_path, if6_map_path, count6_map_path, matched6_map_path, egress_range_map_path, egress_if_list_ext_map_path, egress_ext_map_path, egress_map_path, egress6_map_path, egress_count_map_path, egress_count6_map_path, - egress_matched6_map_path, egress_matched_map_path, udp_ingress_map_path, tcp_ingress_map_path, masquerade_map_path}; - for (int map_count = 0; map_count < 34; map_count++) + egress_matched6_map_path, egress_matched_map_path, udp_ingress_map_path, tcp_ingress_map_path, + masquerade_map_path, icmp_masquerade_map_path, icmp_echo_map_path}; + for (int map_count = 0; map_count < 36; map_count++) { int stat = remove(maps[map_count]); @@ -3098,7 +3105,10 @@ static int process_events(void *ctx, void *data, size_t len) { state = "MATCHED_DROP_FILTER"; } - printf("code=%d\n", code); + else if (code == MATCHED_DROP_FILTER) + { + state = "MATCHED_DROP_FILTER"; + } if (state) { @@ -3116,10 +3126,36 @@ static int process_events(void *ctx, void *data, size_t len) } else if (evt->proto == IPPROTO_ICMP && ifname) { + char *state = NULL; __u16 code = evt->tracking_code; __u8 inner_ttl = evt->dest[0]; __u8 outer_ttl = evt->source[0]; - if (code == 4) + if (code == ICMP_MATCHED_ACTIVE_STATE) + { + state = "ICMP_MATCHED_ACTIVE_STATE"; + } + else if (code == ICMP_MATCHED_EXPIRED_STATE) + { + state = "ICMP_MATCHED_EXPIRED_STATE"; + } + else if (code == CLIENT_INITIATED_ICMP_ECHO) + { + state = "CLIENT_INITIATED_ICMP_ECHO"; + } + if(state) + { + sprintf(message, "%s : %s : %s : %s : %s > %s outbound_tracking ICMP %s ---> %s\n", ts, ifname, + (evt->direction == INGRESS) ? "INGRESS" : "EGRESS", protocol, saddr, daddr,(evt->direction == INGRESS) ? "ECHO-REPLY" : "ECHO" ,state); + if (logging) + { + res = write_log(log_file_name, message); + } + else + { + printf("%s", message); + } + } + else if (code == 4) { /*evt->sport is use repurposed store next hop mtu*/ sprintf(message, "%s : %s : %s : %s :%s --> reported next hop mtu:%d > FRAGMENTATION NEEDED IN PATH TO:%s:%d\n", ts, ifname, @@ -3426,6 +3462,35 @@ static int process_events(void *ctx, void *data, size_t len) printf("%s", message); } } + }else if (evt->proto == IPPROTO_ICMPV6 && ifname) + { + char *state = NULL; + __u16 code = evt->tracking_code; + if (code == ICMP_MATCHED_ACTIVE_STATE) + { + state = "ICMP_MATCHED_ACTIVE_STATE"; + } + else if (code == ICMP_MATCHED_EXPIRED_STATE) + { + state = "ICMP_MATCHED_EXPIRED_STATE"; + } + else if (code == CLIENT_INITIATED_ICMP_ECHO) + { + state = "CLIENT_INITIATED_ICMP_ECHO"; + } + if(state) + { + sprintf(message, "%s : %s : %s : %s : %s > %s outbound_tracking ICMP %s ---> %s\n", ts, ifname, + (evt->direction == INGRESS) ? "INGRESS" : "EGRESS", protocol, saddr6, daddr6,(evt->direction == INGRESS) ? "ECHO-REPLY" : "ECHO" ,state); + if (logging) + { + res = write_log(log_file_name, message); + } + else + { + printf("%s", message); + } + } } else if (ifname) { @@ -3577,7 +3642,7 @@ void map_insert6() printf("INSERT FAILURE -- INVALID PORT RANGE: low_port(%u) > high_port(%u)\n", low_port, high_port); close_maps(1); } - if (get_key_count() == BPF_MAX_ENTRIES) + if (get_key_count6() == BPF_MAX_ENTRIES) { printf("INSERT FAILURE -- MAX PREFIX TUPLES REACHED\n"); close_maps(1); diff --git a/src/zfw_monitor.c b/src/zfw_monitor.c index 4ce59e7..74e811c 100644 --- a/src/zfw_monitor.c +++ b/src/zfw_monitor.c @@ -65,6 +65,9 @@ #define INGRESS_SERVER_RST_RCVD 24 #define INGRESS_SERVER_FINAL_ACK_RCVD 25 #define MATCHED_DROP_FILTER 26 +#define ICMP_MATCHED_EXPIRED_STATE 27 +#define ICMP_MATCHED_ACTIVE_STATE 28 +#define CLIENT_INITIATED_ICMP_ECHO 29 #define IP6_HEADER_TOO_BIG 30 #define IPV6_TUPLE_TOO_BIG 31 @@ -78,7 +81,7 @@ char check_alt[IF_NAMESIZE]; char doc[] = "zfw_monitor -- ebpf firewall monitor tool"; const char *rb_map_path = "/sys/fs/bpf/tc/globals/rb_map"; const char *tproxy_map_path = "/sys/fs/bpf/tc/globals/zt_tproxy_map"; -const char *argp_program_version = "0.8.12"; +const char *argp_program_version = "0.8.13"; union bpf_attr rb_map; int rb_fd = -1; @@ -507,7 +510,6 @@ static int process_events(void *ctx, void *data, size_t len) { state = "MATCHED_DROP_FILTER"; } - printf("code=%d\n", code); if (state) { @@ -525,10 +527,36 @@ static int process_events(void *ctx, void *data, size_t len) } else if (evt->proto == IPPROTO_ICMP && ifname) { + char *state = NULL; __u16 code = evt->tracking_code; __u8 inner_ttl = evt->dest[0]; __u8 outer_ttl = evt->source[0]; - if (code == 4) + if (code == ICMP_MATCHED_ACTIVE_STATE) + { + state = "ICMP_MATCHED_ACTIVE_STATE"; + } + else if (code == ICMP_MATCHED_EXPIRED_STATE) + { + state = "ICMP_MATCHED_EXPIRED_STATE"; + } + else if (code == CLIENT_INITIATED_ICMP_ECHO) + { + state = "CLIENT_INITIATED_ICMP_ECHO"; + } + if(state) + { + sprintf(message, "%s : %s : %s : %s : %s > %s outbound_tracking ICMP %s ---> %s\n", ts, ifname, + (evt->direction == INGRESS) ? "INGRESS" : "EGRESS", protocol, saddr, daddr,(evt->direction == INGRESS) ? "ECHO-REPLY" : "ECHO" ,state); + if (logging) + { + res = write_log(log_file_name, message); + } + else + { + printf("%s", message); + } + } + else if (code == 4) { /*evt->sport is use repurposed store next hop mtu*/ sprintf(message, "%s : %s : %s : %s :%s --> reported next hop mtu:%d > FRAGMENTATION NEEDED IN PATH TO:%s:%d\n", ts, ifname, @@ -835,6 +863,35 @@ static int process_events(void *ctx, void *data, size_t len) printf("%s", message); } } + }else if (evt->proto == IPPROTO_ICMPV6 && ifname) + { + char *state = NULL; + __u16 code = evt->tracking_code; + if (code == ICMP_MATCHED_ACTIVE_STATE) + { + state = "ICMP_MATCHED_ACTIVE_STATE"; + } + else if (code == ICMP_MATCHED_EXPIRED_STATE) + { + state = "ICMP_MATCHED_EXPIRED_STATE"; + } + else if (code == CLIENT_INITIATED_ICMP_ECHO) + { + state = "CLIENT_INITIATED_ICMP_ECHO"; + } + if(state) + { + sprintf(message, "%s : %s : %s : %s : %s > %s outbound_tracking ICMP %s ---> %s\n", ts, ifname, + (evt->direction == INGRESS) ? "INGRESS" : "EGRESS", protocol, saddr6, daddr6,(evt->direction == INGRESS) ? "ECHO-REPLY" : "ECHO" ,state); + if (logging) + { + res = write_log(log_file_name, message); + } + else + { + printf("%s", message); + } + } } else if (ifname) { diff --git a/src/zfw_tc_ingress.c b/src/zfw_tc_ingress.c index d4c94be..493ef1a 100644 --- a/src/zfw_tc_ingress.c +++ b/src/zfw_tc_ingress.c @@ -74,6 +74,8 @@ #define INGRESS_TCP_CONNECTION_ESTABLISHED 20 #define INGRESS_CLIENT_FINAL_ACK_RCVD 21 #define MATCHED_DROP_FILTER 26 +#define ICMP_MATCHED_EXPIRED_STATE 27 +#define ICMP_MATCHED_ACTIVE_STATE 28 #define IP6_HEADER_TOO_BIG 30 #define IPV6_TUPLE_TOO_BIG 31 #ifndef memcpy @@ -183,6 +185,32 @@ struct tuple_key { __u16 dport; }; +/*Key to icmp_echo_map*/ +struct icmp_key { + union { + __u32 ip; + __u32 ip6[4]; + }__in46_u_dst; + union { + __u32 ip; + __u32 ip6[4]; + }__in46_u_src; + __u16 id; + __u16 seq; + __u32 ifindex; +}; + +/*Key to icmp_masquerade_map*/ +struct icmp_masq_key { + union { + __u32 ip; + __u32 ip6[4]; + }__in46_u_dest; + __u16 id; + __u16 seq; + __u32 ifindex; +}; + /*Key to masquerade_map*/ struct masq_key { uint32_t ifindex; @@ -195,7 +223,7 @@ struct masq_key { __u16 dport; }; -/*value to masquerade_map*/ +/*value to masquerade_map and icmp_masquerade_map*/ struct masq_value { union { __u32 ip; @@ -235,8 +263,11 @@ struct tcp_state { struct udp_state { unsigned long long tstamp; }; -unsigned int ifindex; +/*Value to icmp_echo_map*/ +struct icmp_state { + unsigned long long tstamp; +}; /*Value to matched_map*/ struct match_tracker { @@ -552,6 +583,25 @@ struct { __uint(pinning, LIBBPF_PIN_BY_NAME); } udp_map SEC(".maps"); +/*tracks inbound allowed sessions*/ +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(key_size, sizeof(struct tuple_key)); + __uint(value_size,sizeof(struct udp_state)); + __uint(max_entries, BPF_MAX_SESSIONS); + __uint(pinning, LIBBPF_PIN_BY_NAME); +} udp_ingress_map SEC(".maps"); + +/*tracks icmp echo sessions*/ +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(key_size, sizeof(struct icmp_key)); + __uint(value_size,sizeof(struct icmp_state)); + __uint(max_entries, BPF_MAX_SESSIONS); + __uint(pinning, LIBBPF_PIN_BY_NAME); +} icmp_echo_map SEC(".maps"); + +/*tracks udp and tcp masquerade*/ struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(key_size, sizeof(struct masq_key)); @@ -560,14 +610,14 @@ struct { __uint(pinning, LIBBPF_PIN_BY_NAME); } masquerade_map SEC(".maps"); -/*tracks inbound allowed sessions*/ +/*tracks icmp_echo masquerade*/ struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); - __uint(key_size, sizeof(struct tuple_key)); - __uint(value_size,sizeof(struct udp_state)); + __uint(key_size, sizeof(struct icmp_masq_key)); + __uint(value_size,sizeof(struct masq_value)); __uint(max_entries, BPF_MAX_SESSIONS); __uint(pinning, LIBBPF_PIN_BY_NAME); -} udp_ingress_map SEC(".maps"); +} icmp_masquerade_map SEC(".maps"); /*Hashmap to track tun interface inbound passthrough connections*/ struct { @@ -648,6 +698,16 @@ static inline struct masq_value *get_masquerade(struct masq_key key){ return mv; } +static inline struct masq_value *get_icmp_masquerade(struct icmp_masq_key key){ + struct masq_value *mv; + mv = bpf_map_lookup_elem(&icmp_masquerade_map, &key); + return mv; +} + +static inline void del_icmp_masquerade(struct icmp_masq_key key){ + bpf_map_delete_elem(&icmp_masquerade_map, &key); +} + /*Insert entry into ingress tcp state table*/ static inline void insert_ingress_tcp(struct tcp_state tstate, struct tuple_key key){ @@ -687,6 +747,17 @@ static inline void insert_udp_ingress(struct udp_state ustate, struct tuple_key bpf_map_update_elem(&udp_ingress_map, &key, &ustate,0); } +static inline struct icmp_state *get_icmp(struct icmp_key key){ + struct icmp_state *is; + is = bpf_map_lookup_elem(&icmp_echo_map, &key); + return is; +} + + +static inline void del_icmp(struct icmp_key key){ + bpf_map_delete_elem(&icmp_echo_map, &key); +} + /*Insert entry into tun state table*/ static inline void insert_tun(struct tun_state tustate, struct tun_key key){ bpf_map_update_elem(&tun_map, &key, &tustate,0); @@ -1136,6 +1207,7 @@ int bpf_sk_splice(struct __sk_buff *skb){ } else if(icmp){ if(ipv4){ + event.proto = IPPROTO_ICMP; struct iphdr *iph = (struct iphdr *)(skb->data + sizeof(*eth)); if ((unsigned long)(iph + 1) > (unsigned long)skb->data_end){ return TC_ACT_SHOT; @@ -1155,7 +1227,73 @@ int bpf_sk_splice(struct __sk_buff *skb){ } } else if((icmph->type == 0) && (icmph->code == 0)){ - return TC_ACT_OK; + if(local_diag->masquerade && local_ip4 && local_ip4->count && (local_ip4->ipaddr[0] == iph->daddr)){ + struct icmp_masq_key mk = {0}; + mk.__in46_u_dest.ip = iph->saddr; + __u16 *id = (__u16 *)((unsigned long)icmph + 4); + __u16 *seq = (__u16 *)((unsigned long)icmph + 6); + mk.id = *id; + mk.seq = *seq; + mk.ifindex = skb->ifindex; + struct masq_value *mv = get_icmp_masquerade(mk); + if(mv){ + __u32 l3_sum = bpf_csum_diff((__u32 *)&iph->daddr, sizeof(iph->daddr),(__u32 *)&mv->__in46_u_origin.ip, sizeof(mv->__in46_u_origin.ip), 0); + iph->daddr = mv->__in46_u_origin.ip; + /*Calculate l3 Checksum*/ + bpf_l3_csum_replace(skb, sizeof(struct ethhdr) + offsetof(struct iphdr, check), 0, l3_sum, 0); + iph = (struct iphdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(iph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + icmph = (struct icmphdr *)((unsigned long)iph + sizeof(*iph)); + if ((unsigned long)(icmph + 1) > (unsigned long)skb->data_end){ + event.error_code = ICMP_HEADER_TOO_BIG; + send_event(&event); + return TC_ACT_SHOT; + } + del_icmp_masquerade(mk); + } + } + struct icmp_key icmp_state_key = {0}; + icmp_state_key.__in46_u_dst.ip = iph->saddr; + icmp_state_key.__in46_u_src.ip = iph->daddr; + __u16 *id = (__u16 *)((unsigned long)icmph + 4); + __u16 *seq = (__u16 *)((unsigned long)icmph + 6); + icmp_state_key.id = *id; + icmp_state_key.seq = *seq; + icmp_state_key.ifindex = skb->ifindex; + unsigned long long tstamp = bpf_ktime_get_ns(); + struct icmp_state *istate = get_icmp(icmp_state_key); + if(istate){ + /*if icmp outbound state has been up for 30 seconds without traffic remove it from hashmap*/ + if(tstamp > (istate->tstamp + 15000000000)){ + del_icmp(icmp_state_key); + istate = get_icmp(icmp_state_key); + if(!istate){ + if(local_diag->verbose){ + __u32 saddr_array[4] = {iph->saddr,0,0,0}; + __u32 daddr_array[4] = {iph->daddr,0,0,0}; + memcpy(event.saddr, saddr_array, sizeof(saddr_array)); + memcpy(event.daddr, daddr_array, sizeof(daddr_array)); + event.tracking_code = ICMP_MATCHED_EXPIRED_STATE; + send_event(&event); + } + } + } + else{ + if(local_diag->verbose){ + __u32 saddr_array[4] = {iph->saddr,0,0,0}; + __u32 daddr_array[4] = {iph->daddr,0,0,0}; + memcpy(event.saddr, saddr_array, sizeof(saddr_array)); + memcpy(event.daddr, daddr_array, sizeof(daddr_array)); + event.tracking_code = ICMP_MATCHED_ACTIVE_STATE; + send_event(&event); + } + del_icmp(icmp_state_key); + return TC_ACT_OK; + } + } + return TC_ACT_SHOT; } else if(ipv4 && (icmph->type == 3)){ struct iphdr *inner_iph = (struct iphdr *)((unsigned long)icmph + sizeof(*icmph)); @@ -1230,6 +1368,7 @@ int bpf_sk_splice(struct __sk_buff *skb){ return TC_ACT_SHOT; } }else if(ipv6){ + event.proto = IPPROTO_ICMPV6; struct ipv6hdr *ip6h = (struct ipv6hdr *)(skb->data + sizeof(*eth)); if ((unsigned long)(ip6h + 1) > (unsigned long)skb->data_end){ return TC_ACT_SHOT; @@ -1249,7 +1388,72 @@ int bpf_sk_splice(struct __sk_buff *skb){ return TC_ACT_SHOT; } }else if((icmp6h->icmp6_type == 129) && (icmp6h->icmp6_code == 0)){ //echo-reply - return TC_ACT_OK; + if(local_diag->masquerade && local_ip6 && local_ip6->count && (local_ip6->ipaddr[0][0] == ip6h->daddr.in6_u.u6_addr32[0]) + && (local_ip6->ipaddr[0][1] == ip6h->daddr.in6_u.u6_addr32[1]) && (local_ip6->ipaddr[0][2] == ip6h->daddr.in6_u.u6_addr32[2]) + && (local_ip6->ipaddr[0][3] == ip6h->daddr.in6_u.u6_addr32[3])){ + struct icmp_masq_key mk = {0}; + memcpy(mk.__in46_u_dest.ip6, ip6h->saddr.in6_u.u6_addr32, sizeof(ip6h->saddr.in6_u.u6_addr32)); + __u16 *id = (__u16 *)((unsigned long)icmp6h + 4); + __u16 *seq = (__u16 *)((unsigned long)icmp6h + 6); + mk.id = *id; + mk.seq = *seq; + mk.ifindex = skb->ifindex; + struct masq_value *mv = get_icmp_masquerade(mk); + if(mv){ + memcpy(ip6h->daddr.in6_u.u6_addr32, mv->__in46_u_origin.ip6, sizeof(mv->__in46_u_origin.ip6)); + /*Calculate l4 Checksum*/ + for(int x = 0; x < 4; x++){ + bpf_l4_csum_replace(skb, sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + offsetof(struct icmp6hdr, icmp6_cksum), local_ip6->ipaddr[0][x], ip6h->daddr.in6_u.u6_addr32[x], BPF_F_PSEUDO_HDR | 4); + ip6h = (struct ipv6hdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(ip6h + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + icmp6h = (struct icmp6hdr *)((unsigned long)ip6h + sizeof(*ip6h)); + if ((unsigned long)(icmp6h + 1) > (unsigned long)skb->data_end){ + event.error_code = ICMP_HEADER_TOO_BIG; + send_event(&event); + return TC_ACT_SHOT; + } + } + del_icmp_masquerade(mk); + } + } + struct icmp_key icmp_state_key = {0}; + memcpy(icmp_state_key.__in46_u_dst.ip6, ip6h->saddr.in6_u.u6_addr32, sizeof(ip6h->saddr.in6_u.u6_addr32)); + memcpy(icmp_state_key.__in46_u_src.ip6, ip6h->daddr.in6_u.u6_addr32, sizeof(ip6h->daddr.in6_u.u6_addr32)); + __u16 *id = (__u16 *)((unsigned long)icmp6h + 4); + __u16 *seq = (__u16 *)((unsigned long)icmp6h + 6); + icmp_state_key.id = *id; + icmp_state_key.seq = *seq; + icmp_state_key.ifindex = skb->ifindex; + unsigned long long tstamp = bpf_ktime_get_ns(); + struct icmp_state *istate = get_icmp(icmp_state_key); + if(istate){ + /*if udp outbound state has been up for 30 seconds without traffic remove it from hashmap*/ + if(tstamp > (istate->tstamp + 15000000000)){ + del_icmp(icmp_state_key); + istate = get_icmp(icmp_state_key); + if(!istate){ + if(local_diag->verbose){ + memcpy(event.saddr, ip6h->saddr.in6_u.u6_addr32, sizeof(ip6h->saddr.in6_u.u6_addr32)); + memcpy(event.daddr, ip6h->daddr.in6_u.u6_addr32, sizeof(ip6h->daddr.in6_u.u6_addr32)); + event.tracking_code = ICMP_MATCHED_EXPIRED_STATE; + send_event(&event); + } + } + } + else{ + if(local_diag->verbose){ + memcpy(event.saddr, ip6h->saddr.in6_u.u6_addr32, sizeof(ip6h->saddr.in6_u.u6_addr32)); + memcpy(event.daddr, ip6h->daddr.in6_u.u6_addr32, sizeof(ip6h->daddr.in6_u.u6_addr32)); + event.tracking_code = ICMP_MATCHED_ACTIVE_STATE; + send_event(&event); + } + del_icmp(icmp_state_key); + return TC_ACT_OK; + } + } + return TC_ACT_SHOT; }else if((icmp6h->icmp6_type == 133) && (icmp6h->icmp6_code == 0)){ //router solicitation return TC_ACT_OK; }else if((icmp6h->icmp6_type == 135) && (icmp6h->icmp6_code == 0)){ //neighbor solicitation diff --git a/src/zfw_tc_outbound_track.c b/src/zfw_tc_outbound_track.c index 52d2b83..ec4e66d 100644 --- a/src/zfw_tc_outbound_track.c +++ b/src/zfw_tc_outbound_track.c @@ -60,6 +60,7 @@ #define INGRESS_SERVER_RST_RCVD 24 #define INGRESS_SERVER_FINAL_ACK_RCVD 25 #define MATCHED_DROP_FILTER 26 +#define CLIENT_INITIATED_ICMP_ECHO 29 #define IP6_HEADER_TOO_BIG 30 #define IPV6_TUPLE_TOO_BIG 31 #define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n)) @@ -96,6 +97,32 @@ struct tuple_key { __u16 dport; }; +/*Key to icmp_echo_map*/ +struct icmp_key { + union { + __u32 ip; + __u32 ip6[4]; + }__in46_u_dst; + union { + __u32 ip; + __u32 ip6[4]; + }__in46_u_src; + __u16 id; + __u16 seq; + __u32 ifindex; +}; + +/*Key to icmp_masquerade_map*/ +struct icmp_masq_key { + union { + __u32 ip; + __u32 ip6[4]; + }__in46_u_dest; + __u16 id; + __u16 seq; + __u32 ifindex; +}; + /*Value to tcp_map*/ struct tcp_state { unsigned long long tstamp; @@ -116,6 +143,11 @@ struct udp_state { unsigned long long tstamp; }; +/*Value to icmp_echo_map*/ +struct icmp_state { + unsigned long long tstamp; +}; + /*Key to masquerade_map*/ struct masq_key { uint32_t ifindex; @@ -128,7 +160,7 @@ struct masq_key { __u16 dport; }; -/*value to masquerade_map*/ +/*value to masquerade_map and icmp_masquerade_map*/ struct masq_value { union { __u32 ip; @@ -277,14 +309,6 @@ struct { __uint(pinning, LIBBPF_PIN_BY_NAME); } egress_matched_map SEC(".maps"); -struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __uint(key_size, sizeof(struct masq_key)); - __uint(value_size,sizeof(struct masq_value)); - __uint(max_entries, BPF_MAX_SESSIONS * 2); - __uint(pinning, LIBBPF_PIN_BY_NAME); -} masquerade_map SEC(".maps"); - struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(key_size, sizeof(struct match6_key)); @@ -413,6 +437,31 @@ struct { __uint(pinning, LIBBPF_PIN_BY_NAME); } udp_ingress_map SEC(".maps"); +/*tracks icmp echo sessions*/ +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(key_size, sizeof(struct icmp_key)); + __uint(value_size,sizeof(struct icmp_state)); + __uint(max_entries, BPF_MAX_SESSIONS); + __uint(pinning, LIBBPF_PIN_BY_NAME); +} icmp_echo_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(key_size, sizeof(struct masq_key)); + __uint(value_size,sizeof(struct masq_value)); + __uint(max_entries, BPF_MAX_SESSIONS * 2); + __uint(pinning, LIBBPF_PIN_BY_NAME); +} masquerade_map SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(key_size, sizeof(struct icmp_masq_key)); + __uint(value_size,sizeof(struct masq_value)); + __uint(max_entries, BPF_MAX_SESSIONS); + __uint(pinning, LIBBPF_PIN_BY_NAME); +} icmp_masquerade_map SEC(".maps"); + /*Ringbuf map*/ struct { __uint(type, BPF_MAP_TYPE_RINGBUF); @@ -468,6 +517,10 @@ static inline void insert_masquerade(struct masq_value mv, struct masq_key key){ bpf_map_update_elem(&masquerade_map, &key, &mv,0); } +static inline void insert_icmp_masquerade(struct masq_value mv, struct icmp_masq_key key){ + bpf_map_update_elem(&icmp_masquerade_map, &key, &mv,0); +} + /*Remove entry into tcp state table*/ static inline void del_tcp(struct tuple_key key){ @@ -515,6 +568,11 @@ static inline void del_udp_ingress(struct tuple_key key){ bpf_map_delete_elem(&udp_ingress_map, &key); } +/*Insert entry into icmp echo state table*/ +static inline void insert_icmp(struct icmp_state istate, struct icmp_key key){ + bpf_map_update_elem(&icmp_echo_map, &key, &istate,0); +} + static inline struct diag_ip4 *get_diag_ip4(__u32 key){ struct diag_ip4 *if_diag; if_diag = bpf_map_lookup_elem(&diag_map, &key); @@ -822,6 +880,56 @@ int bpf_sk_splice(struct __sk_buff *skb){ send_event(&event); return TC_ACT_SHOT; } + if((skb->ifindex != 1) && (icmph->type == 8) && (icmph->code == 0)){ + struct icmp_key ik = {0}; + ik.__in46_u_src.ip = iph->saddr; + ik.__in46_u_dst.ip = iph->daddr; + __u16 *id = (__u16 *)((unsigned long)icmph + 4); + __u16 *seq = (__u16 *)((unsigned long)icmph + 6); + ik.id = *id; + ik.seq = *seq; + ik.ifindex = skb->ifindex; + if(local_diag->masquerade && local_ip4 && local_ip4->count){ + __u32 l3_sum = bpf_csum_diff((__u32 *)&iph->saddr, sizeof(iph->saddr), (__u32 *)&local_ip4->ipaddr[0], sizeof(local_ip4->ipaddr[0]), 0); + struct masq_value mv = {0}; + mv.__in46_u_origin.ip = iph->saddr; + struct icmp_masq_key mk = {0}; + mk.__in46_u_dest.ip = iph->daddr; + __u16 *id = (__u16 *)((unsigned long)icmph + 4); + __u16 *seq = (__u16 *)((unsigned long)icmph + 6); + mk.id = *id; + mk.seq = *seq; + mk.ifindex = skb->ifindex; + insert_icmp_masquerade(mv, mk); + iph->saddr = local_ip4->ipaddr[0]; + /*Calculate l3 Checksum*/ + bpf_l3_csum_replace(skb, sizeof(struct ethhdr) + offsetof(struct iphdr, check), 0, l3_sum, 0); + iph = (struct iphdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(iph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + icmph = (struct icmphdr *)((unsigned long)iph + sizeof(*iph)); + if ((unsigned long)(icmph + 1) > (unsigned long)skb->data_end){ + event.error_code = ICMP_HEADER_TOO_BIG; + send_event(&event); + return TC_ACT_SHOT; + } + } + struct icmp_state is = { + tstamp + }; + insert_icmp(is, ik); + if(local_diag->verbose){ + __u32 saddr_array[4] = {iph->saddr,0,0,0}; + __u32 daddr_array[4] = {iph->daddr,0,0,0}; + memcpy(event.saddr, saddr_array, sizeof(saddr_array)); + memcpy(event.daddr, daddr_array, sizeof(daddr_array)); + event.tracking_code = CLIENT_INITIATED_ICMP_ECHO; + send_event(&event); + } + return TC_ACT_OK; + } + if((skb->ifindex != 1) && (icmph->type == 0) && (icmph->code == 0)){ if(local_diag->echo){ return TC_ACT_OK; @@ -848,6 +956,54 @@ int bpf_sk_splice(struct __sk_buff *skb){ send_event(&event); return TC_ACT_SHOT; } + if((skb->ifindex != 1) && (icmp6h->icmp6_type == 128) && (icmp6h->icmp6_code == 0)){ + struct icmp_key ik = {0}; + memcpy(ik.__in46_u_src.ip6, ip6h->saddr.in6_u.u6_addr32, sizeof(ip6h->saddr.in6_u.u6_addr32)); + memcpy(ik.__in46_u_dst.ip6, ip6h->daddr.in6_u.u6_addr32, sizeof(ip6h->daddr.in6_u.u6_addr32)); + __u16 *id = (__u16 *)((unsigned long)icmp6h + 4); + __u16 *seq = (__u16 *)((unsigned long)icmp6h + 6); + ik.id = *id; + ik.seq = *seq; + ik.ifindex = skb->ifindex; + if(local_diag->masquerade && local_ip6 && local_ip6->count){ + struct masq_value mv = {0}; + memcpy(mv.__in46_u_origin.ip6, ip6h->saddr.in6_u.u6_addr32, sizeof(mv.__in46_u_origin.ip6)); + struct icmp_masq_key mk = {0}; + memcpy(mk.__in46_u_dest.ip6, ip6h->daddr.in6_u.u6_addr32, sizeof(ip6h->daddr.in6_u.u6_addr32)); + __u16 *id = (__u16 *)((unsigned long)icmp6h + 4); + __u16 *seq = (__u16 *)((unsigned long)icmp6h + 6); + mk.id = *id; + mk.seq = *seq; + mk.ifindex = skb->ifindex; + insert_icmp_masquerade(mv, mk ); + memcpy(ip6h->saddr.in6_u.u6_addr32, local_ip6->ipaddr[0], sizeof(local_ip6->ipaddr[0])); + /*Calculate l4 Checksum*/ + for(int x = 0; x < 4; x++){ + bpf_l4_csum_replace(skb, sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + offsetof(struct icmp6hdr, icmp6_cksum), mv.__in46_u_origin.ip6[x], ip6h->saddr.in6_u.u6_addr32[x], BPF_F_PSEUDO_HDR | 4); + ip6h = (struct ipv6hdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(ip6h + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + icmp6h = (struct icmp6hdr *)((unsigned long)ip6h + sizeof(*ip6h)); + if ((unsigned long)(icmp6h + 1) > (unsigned long)skb->data_end){ + event.error_code = ICMP_HEADER_TOO_BIG; + send_event(&event); + return TC_ACT_SHOT; + } + } + } + struct icmp_state is = { + tstamp + }; + insert_icmp(is, ik); + if(local_diag->verbose){ + memcpy(event.saddr, ip6h->saddr.in6_u.u6_addr32, sizeof(ip6h->saddr.in6_u.u6_addr32)); + memcpy(event.daddr, ip6h->daddr.in6_u.u6_addr32, sizeof(ip6h->daddr.in6_u.u6_addr32)); + event.tracking_code = CLIENT_INITIATED_ICMP_ECHO; + send_event(&event); + } + return TC_ACT_OK; + } if(skb->ifindex != 1){ if((icmp6h->icmp6_type == 129) && (icmp6h->icmp6_code == 0)){ //echo-reply if(local_diag->ipv6_enable && local_diag->echo){