Repository: kmesh-net/kmesh

Issue:
[lfx-mentorship-2025-Mar-May] Kmesh eBPF unit test
- Description: As the community features continue to expand, the number of eBPF programs in the data plane has increased. Due to the inherent limitations of eBPF (third-state encoding, neither user space nor kernel space, running in a kernel virtual machine with a dedicated instruction set), Kmesh implements complex governance logic through features like tail call and map-in-map, which poses challenges for data plane quality protection.
eBPF, a recently introduced programmable technology in the kernel, currently has an immature ecosystem. The industry is actively exploring eBPF testing capabilities (e.g., Unit Testing eBPF). This project aims to develop an eBPF UT testing framework in conjunction with the Kmesh project to ensure the quality of the Kmesh data plane.
- Expected Outcome:
  - Export Kmesh eBPF programs to support UT test case.
  - Export design documentation for eBPF UT tests
- Recommended Skills: C, eBPF,  (go)
- Mentor(s): @bitcoffeeiux ([liuxin350@huawei.com](mailto:liuxin350@huawei.com))
- Useful Link:
  - https://github.com/kmesh-net/kmesh/blob/main/docs/proposal/unit_test_framework_zh.md
  - https://github.com/kmesh-net/kmesh/blob/main/test/unittest/workload

Discussion:
@Dhiren-Mhatre: Hello @LiZhenCheng9527 and @bitcoffeeiux,
I am very interested in contributing to the "[lfx-mentorship-2025-Mar-May] Kmesh eBPF Unit Test" project. With a strong background in Golang, I have previously contributed to open-source projects such as Prometheus and the Palisadoes Foundation, where I gained valuable experience in building scalable and efficient systems.
The project's focus on developing an eBPF unit testing framework for Kmesh aligns perfectly with my interests in low-level programming and kernel technologies. I am eager to apply my skills in C and Golang while learning more about eBPF's capabilities and challenges.
I look forward to the opportunity to collaborate with the team and contribute to ensuring the quality of Kmesh's data plane.
@btwshivam: Hello @bitcoffeeiux I’m Shivam, i have experience in eBPF testing and kernel-level development. I recently worked on unit testing for Inspektor Gadget, focusing on running eBPF gadget tests across different kernel versions.

I’m very interested in contributing to the Kmesh eBPF Unit Test Project and helping build a robust testing framework. I’ve gone through the documentation and would love to discuss the current testing challenges and possible approaches.@btwshivam: Hey, @LiZhenCheng9527 I’d like to pick a small task, such as writing a basic eBPF unit test. Could you suggest a good starting point or any existing gaps in the test suite that I can work on?@bdharsan04: hey @btwshivam, i guess [this](https://who.ldelossa.is/posts/unit-testing-ebpf/) is a good place to start regarding writing a basic eBPF unit test 

Solution patch:
diff --git a/.github/workflows/e2e-ipv6.yml b/.github/workflows/e2e-ipv6-istio-1.25.yml
similarity index 85%
rename from .github/workflows/e2e-ipv6.yml
rename to .github/workflows/e2e-ipv6-istio-1.25.yml
index 48ce4437..8b28e6c5 100644
--- a/.github/workflows/e2e-ipv6.yml
+++ b/.github/workflows/e2e-ipv6-istio-1.25.yml
@@ -1,4 +1,4 @@
-name: E2E IPv6 Test(istio 1.22)
+name: E2E IPv6 Test(istio 1.25)
 on: 
   pull_request:
     paths-ignore:
@@ -23,4 +23,4 @@ jobs:
     - name: E2E IPv6 Test
       shell: bash
       run: |
-        sudo make e2e-ipv6
\ No newline at end of file
+        sudo ISTIO_VERSION="1.25.1" make e2e-ipv6
\ No newline at end of file
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e-istio-1.25.yml
similarity index 86%
rename from .github/workflows/e2e.yml
rename to .github/workflows/e2e-istio-1.25.yml
index 9a2f9fa1..4d474693 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e-istio-1.25.yml
@@ -1,4 +1,4 @@
-name: E2E Test(istio 1.22)
+name: E2E Test(istio 1.25)
 on: 
   pull_request:
     paths-ignore:
@@ -23,4 +23,4 @@ jobs:
     - name: E2E Test
       shell: bash
       run: |
-        sudo make e2e
\ No newline at end of file
+        sudo ISTIO_VERSION="1.25.1" make e2e
\ No newline at end of file
diff --git a/.github/workflows/main-5.15.yml b/.github/workflows/main-5.15.yml
index 9fcbdcd5..e3df57e6 100644
--- a/.github/workflows/main-5.15.yml
+++ b/.github/workflows/main-5.15.yml
@@ -36,3 +36,7 @@ jobs:
       - name: Go Test
         run: |
           sudo make test RUN_IN_CONTAINER=1
+
+      - name: eBPF Unit Test
+        run: |
+          sudo make ebpf_unit_test RUN_IN_CONTAINER=1 V=1
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 0fba1bba..240830aa 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -61,6 +61,10 @@ jobs:
       run: |
         sudo env LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:$GITHUB_WORKSPACE/api/v2-c:$GITHUB_WORKSPACE/bpf/deserialization_to_bpf_map PKG_CONFIG_PATH=$GITHUB_WORKSPACE/mk go test -race -v -vet=off -coverprofile=coverage.out ./pkg/...
 
+    - name: eBPF Unit Test
+      run: |
+        sudo env LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:$GITHUB_WORKSPACE/api/v2-c:$GITHUB_WORKSPACE/bpf/deserialization_to_bpf_map make ebpf_unit_test V=1
+
     - name: Upload coverage reports to Codecov
       uses: codecov/codecov-action@v4
       with:
diff --git a/Makefile b/Makefile
index f7bd28b7..6b2c9253 100644
--- a/Makefile
+++ b/Makefile
@@ -210,13 +210,28 @@ e2e-ipv6:
 format:
 	./hack/format.sh
 
+
+GO_TEST_FLAGS:=
+ifeq ($(V),1)
+		GO_TEST_FLAGS += --verbose
+endif
+
 .PHONY: test
 ifeq ($(RUN_IN_CONTAINER),1)
 test:
-	./hack/run-ut.sh --docker
+	./hack/run-ut.sh --docker $(GO_TEST_FLAGS)
 else
 test:
-	./hack/run-ut.sh --local
+	./hack/run-ut.sh --local $(GO_TEST_FLAGS)
+endif
+
+.PHONY: ebpf_unit_test
+ifeq ($(RUN_IN_CONTAINER),1)
+ebpf_unit_test:
+	./hack/run-ebpf-ut.sh --docker $(GO_TEST_FLAGS)
+else
+ebpf_unit_test:
+	./hack/run-ebpf-ut.sh --local $(GO_TEST_FLAGS)
 endif
 
 UPDATE_VERSION ?= ${VERSION}
diff --git a/bpf/include/bpf_common.h b/bpf/include/bpf_common.h
index 2a739fe1..0169d90a 100644
--- a/bpf/include/bpf_common.h
+++ b/bpf/include/bpf_common.h
@@ -44,8 +44,17 @@ struct {
 
 struct sock_storage_data {
     __u64 connect_ns;
+    __u64 last_report_ns;
     __u8 direction;
     __u8 connect_success;
+    // whether has to proxied by waypoint
+    bool via_waypoint;
+    // whether tlv encoded
+    bool has_encoded;
+    // prevent duplicating setting of original dst
+    bool has_set_ip;
+    // original dst info
+    struct bpf_sock_tuple sk_tuple;
 };
 
 struct {
@@ -305,4 +314,21 @@ static inline bool is_managed_by_kmesh(struct bpf_sock_ops *skops)
     return (*value == 0);
 }
 
+static inline bool is_managed_by_kmesh_skb(struct __sk_buff *skb)
+{
+    struct manager_key key = {0};
+    if (skb->family == AF_INET)
+        key.addr.ip4 = skb->local_ip4;
+    if (skb->family == AF_INET6) {
+        if (is_ipv4_mapped_addr(skb->local_ip6))
+            key.addr.ip4 = skb->local_ip6[3];
+        else
+            IP6_COPY(key.addr.ip6, skb->local_ip6);
+    }
+
+    int *value = bpf_map_lookup_elem(&map_of_manager, &key);
+    if (!value)
+        return false;
+    return (*value == 0);
+}
 #endif
\ No newline at end of file
diff --git a/bpf/kmesh/bpf2go/bpf2go.go b/bpf/kmesh/bpf2go/bpf2go.go
index f79735de..01a27276 100644
--- a/bpf/kmesh/bpf2go/bpf2go.go
+++ b/bpf/kmesh/bpf2go/bpf2go.go
@@ -24,6 +24,7 @@ package bpf2go
 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go --output-dir dualengine --go-package dualengine -cc clang --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshSockopsWorkload ../workload/sockops.c -- -I../workload/include -I../../include -I../probes -DKERNEL_VERSION_HIGHER_5_13_0=1
 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go --output-dir dualengine --go-package dualengine -cc clang  --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshXDPAuth ../workload/xdp.c -- -I../workload/include -I../../include -I../../../api/v2-c -DKERNEL_VERSION_HIGHER_5_13_0=1
 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go --output-dir dualengine --go-package dualengine -cc clang  --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshSendmsg ../workload/sendmsg.c -- -I../workload/include -I../../include -DKERNEL_VERSION_HIGHER_5_13_0=1
+//go:generate go run github.com/cilium/ebpf/cmd/bpf2go --output-dir dualengine --go-package dualengine -cc clang  --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshCgroupSkb ../workload/cgroup_skb.c -- -I../workload/include -I../../include -I../probes -DKERNEL_VERSION_HIGHER_5_13_0=1
 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go --output-dir general --go-package general -cc clang  --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshTcMarkEncrypt ../general/tc_mark_encrypt.c -- -I../general/include -I../../include -DKERNEL_VERSION_HIGHER_5_13_0=1
 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go --output-dir general --go-package general -cc clang  --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshTcMarkDecrypt ../general/tc_mark_decrypt.c -- -I../general/include -I../../include -DKERNEL_VERSION_HIGHER_5_13_0=1
 
@@ -33,5 +34,6 @@ package bpf2go
 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go --output-dir dualengine --go-package dualengine  -cc clang  --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshSockopsWorkloadCompat ../workload/sockops.c -- -I../workload/include -I../../include -I../probes -DKERNEL_VERSION_HIGHER_5_13_0=0
 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go --output-dir dualengine --go-package dualengine -cc clang  --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshXDPAuthCompat ../workload/xdp.c -- -I../workload/include -I../../include -I../../../api/v2-c -DKERNEL_VERSION_HIGHER_5_13_0=0
 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go --output-dir dualengine --go-package dualengine -cc clang  --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshSendmsgCompat ../workload/sendmsg.c -- -I../workload/include -I../../include -DKERNEL_VERSION_HIGHER_5_13_0=0
+//go:generate go run github.com/cilium/ebpf/cmd/bpf2go --output-dir dualengine --go-package dualengine -cc clang  --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshCgroupSkbCompat ../workload/cgroup_skb.c -- -I../workload/include -I../../include -I../probes -DKERNEL_VERSION_HIGHER_5_13_0=0
 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go --output-dir general --go-package general -cc clang  --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshTcMarkEncryptCompat ../general/tc_mark_encrypt.c -- -I../general/include -I../../include -DKERNEL_VERSION_HIGHER_5_13_0=0
 //go:generate go run github.com/cilium/ebpf/cmd/bpf2go --output-dir general --go-package general -cc clang  --cflags $EXTRA_CFLAGS --cflags $EXTRA_CDEFINE KmeshTcMarkDecryptCompat ../general/tc_mark_decrypt.c -- -I../general/include -I../../include -DKERNEL_VERSION_HIGHER_5_13_0=0
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshcgroupskb_bpfeb.go b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupskb_bpfeb.go
new file mode 100644
index 00000000..cbac535e
--- /dev/null
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupskb_bpfeb.go
@@ -0,0 +1,233 @@
+// Code generated by bpf2go; DO NOT EDIT.
+//go:build mips || mips64 || ppc64 || s390x
+
+package dualengine
+
+import (
+	"bytes"
+	_ "embed"
+	"fmt"
+	"io"
+
+	"github.com/cilium/ebpf"
+)
+
+type KmeshCgroupSkbBpfSockTuple struct {
+	Ipv4 struct {
+		Saddr uint32
+		Daddr uint32
+		Sport uint16
+		Dport uint16
+	}
+	_ [24]byte
+}
+
+type KmeshCgroupSkbBuf struct{ Data [40]int8 }
+
+type KmeshCgroupSkbManagerKey struct {
+	NetnsCookie uint64
+	_           [8]byte
+}
+
+type KmeshCgroupSkbOperationUsageData struct {
+	StartTime     uint64
+	EndTime       uint64
+	PidTgid       uint64
+	OperationType uint32
+	_             [4]byte
+}
+
+type KmeshCgroupSkbOperationUsageKey struct {
+	SocketCookie  uint64
+	OperationType uint32
+	_             [4]byte
+}
+
+type KmeshCgroupSkbSockStorageData struct {
+	ConnectNs      uint64
+	LastReportNs   uint64
+	Direction      uint8
+	ConnectSuccess uint8
+	_              [6]byte
+}
+
+// LoadKmeshCgroupSkb returns the embedded CollectionSpec for KmeshCgroupSkb.
+func LoadKmeshCgroupSkb() (*ebpf.CollectionSpec, error) {
+	reader := bytes.NewReader(_KmeshCgroupSkbBytes)
+	spec, err := ebpf.LoadCollectionSpecFromReader(reader)
+	if err != nil {
+		return nil, fmt.Errorf("can't load KmeshCgroupSkb: %w", err)
+	}
+
+	return spec, err
+}
+
+// LoadKmeshCgroupSkbObjects loads KmeshCgroupSkb and converts it into a struct.
+//
+// The following types are suitable as obj argument:
+//
+//	*KmeshCgroupSkbObjects
+//	*KmeshCgroupSkbPrograms
+//	*KmeshCgroupSkbMaps
+//
+// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
+func LoadKmeshCgroupSkbObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
+	spec, err := LoadKmeshCgroupSkb()
+	if err != nil {
+		return err
+	}
+
+	return spec.LoadAndAssign(obj, opts)
+}
+
+// KmeshCgroupSkbSpecs contains maps and programs before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbSpecs struct {
+	KmeshCgroupSkbProgramSpecs
+	KmeshCgroupSkbMapSpecs
+	KmeshCgroupSkbVariableSpecs
+}
+
+// KmeshCgroupSkbProgramSpecs contains programs before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbProgramSpecs struct {
+	CgroupSkbEgressProg  *ebpf.ProgramSpec `ebpf:"cgroup_skb_egress_prog"`
+	CgroupSkbIngressProg *ebpf.ProgramSpec `ebpf:"cgroup_skb_ingress_prog"`
+}
+
+// KmeshCgroupSkbMapSpecs contains maps before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbMapSpecs struct {
+	KmAuthReq     *ebpf.MapSpec `ebpf:"km_auth_req"`
+	KmAuthRes     *ebpf.MapSpec `ebpf:"km_auth_res"`
+	KmBackend     *ebpf.MapSpec `ebpf:"km_backend"`
+	KmEndpoint    *ebpf.MapSpec `ebpf:"km_endpoint"`
+	KmFrontend    *ebpf.MapSpec `ebpf:"km_frontend"`
+	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
+	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
+	KmOrigDst     *ebpf.MapSpec `ebpf:"km_orig_dst"`
+	KmPerfInfo    *ebpf.MapSpec `ebpf:"km_perf_info"`
+	KmPerfMap     *ebpf.MapSpec `ebpf:"km_perf_map"`
+	KmService     *ebpf.MapSpec `ebpf:"km_service"`
+	KmSockstorage *ebpf.MapSpec `ebpf:"km_sockstorage"`
+	KmTcpProbe    *ebpf.MapSpec `ebpf:"km_tcp_probe"`
+	KmTmpbuf      *ebpf.MapSpec `ebpf:"km_tmpbuf"`
+	KmWlpolicy    *ebpf.MapSpec `ebpf:"km_wlpolicy"`
+	KmeshMap1600  *ebpf.MapSpec `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.MapSpec `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.MapSpec `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.MapSpec `ebpf:"kmesh_map64"`
+}
+
+// KmeshCgroupSkbVariableSpecs contains global variables before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbVariableSpecs struct {
+	BpfLogLevel      *ebpf.VariableSpec `ebpf:"bpf_log_level"`
+	EnableMonitoring *ebpf.VariableSpec `ebpf:"enable_monitoring"`
+}
+
+// KmeshCgroupSkbObjects contains all objects after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbObjects struct {
+	KmeshCgroupSkbPrograms
+	KmeshCgroupSkbMaps
+	KmeshCgroupSkbVariables
+}
+
+func (o *KmeshCgroupSkbObjects) Close() error {
+	return _KmeshCgroupSkbClose(
+		&o.KmeshCgroupSkbPrograms,
+		&o.KmeshCgroupSkbMaps,
+	)
+}
+
+// KmeshCgroupSkbMaps contains all maps after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbMaps struct {
+	KmAuthReq     *ebpf.Map `ebpf:"km_auth_req"`
+	KmAuthRes     *ebpf.Map `ebpf:"km_auth_res"`
+	KmBackend     *ebpf.Map `ebpf:"km_backend"`
+	KmEndpoint    *ebpf.Map `ebpf:"km_endpoint"`
+	KmFrontend    *ebpf.Map `ebpf:"km_frontend"`
+	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
+	KmManage      *ebpf.Map `ebpf:"km_manage"`
+	KmOrigDst     *ebpf.Map `ebpf:"km_orig_dst"`
+	KmPerfInfo    *ebpf.Map `ebpf:"km_perf_info"`
+	KmPerfMap     *ebpf.Map `ebpf:"km_perf_map"`
+	KmService     *ebpf.Map `ebpf:"km_service"`
+	KmSockstorage *ebpf.Map `ebpf:"km_sockstorage"`
+	KmTcpProbe    *ebpf.Map `ebpf:"km_tcp_probe"`
+	KmTmpbuf      *ebpf.Map `ebpf:"km_tmpbuf"`
+	KmWlpolicy    *ebpf.Map `ebpf:"km_wlpolicy"`
+	KmeshMap1600  *ebpf.Map `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.Map `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.Map `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.Map `ebpf:"kmesh_map64"`
+}
+
+func (m *KmeshCgroupSkbMaps) Close() error {
+	return _KmeshCgroupSkbClose(
+		m.KmAuthReq,
+		m.KmAuthRes,
+		m.KmBackend,
+		m.KmEndpoint,
+		m.KmFrontend,
+		m.KmLogEvent,
+		m.KmManage,
+		m.KmOrigDst,
+		m.KmPerfInfo,
+		m.KmPerfMap,
+		m.KmService,
+		m.KmSockstorage,
+		m.KmTcpProbe,
+		m.KmTmpbuf,
+		m.KmWlpolicy,
+		m.KmeshMap1600,
+		m.KmeshMap192,
+		m.KmeshMap296,
+		m.KmeshMap64,
+	)
+}
+
+// KmeshCgroupSkbVariables contains all global variables after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbVariables struct {
+	BpfLogLevel      *ebpf.Variable `ebpf:"bpf_log_level"`
+	EnableMonitoring *ebpf.Variable `ebpf:"enable_monitoring"`
+}
+
+// KmeshCgroupSkbPrograms contains all programs after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbPrograms struct {
+	CgroupSkbEgressProg  *ebpf.Program `ebpf:"cgroup_skb_egress_prog"`
+	CgroupSkbIngressProg *ebpf.Program `ebpf:"cgroup_skb_ingress_prog"`
+}
+
+func (p *KmeshCgroupSkbPrograms) Close() error {
+	return _KmeshCgroupSkbClose(
+		p.CgroupSkbEgressProg,
+		p.CgroupSkbIngressProg,
+	)
+}
+
+func _KmeshCgroupSkbClose(closers ...io.Closer) error {
+	for _, closer := range closers {
+		if err := closer.Close(); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// Do not access this directly.
+//
+//go:embed kmeshcgroupskb_bpfeb.o
+var _KmeshCgroupSkbBytes []byte
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshcgroupskb_bpfel.go b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupskb_bpfel.go
new file mode 100644
index 00000000..a7094e34
--- /dev/null
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupskb_bpfel.go
@@ -0,0 +1,233 @@
+// Code generated by bpf2go; DO NOT EDIT.
+//go:build 386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64
+
+package dualengine
+
+import (
+	"bytes"
+	_ "embed"
+	"fmt"
+	"io"
+
+	"github.com/cilium/ebpf"
+)
+
+type KmeshCgroupSkbBpfSockTuple struct {
+	Ipv4 struct {
+		Saddr uint32
+		Daddr uint32
+		Sport uint16
+		Dport uint16
+	}
+	_ [24]byte
+}
+
+type KmeshCgroupSkbBuf struct{ Data [40]int8 }
+
+type KmeshCgroupSkbManagerKey struct {
+	NetnsCookie uint64
+	_           [8]byte
+}
+
+type KmeshCgroupSkbOperationUsageData struct {
+	StartTime     uint64
+	EndTime       uint64
+	PidTgid       uint64
+	OperationType uint32
+	_             [4]byte
+}
+
+type KmeshCgroupSkbOperationUsageKey struct {
+	SocketCookie  uint64
+	OperationType uint32
+	_             [4]byte
+}
+
+type KmeshCgroupSkbSockStorageData struct {
+	ConnectNs      uint64
+	LastReportNs   uint64
+	Direction      uint8
+	ConnectSuccess uint8
+	_              [6]byte
+}
+
+// LoadKmeshCgroupSkb returns the embedded CollectionSpec for KmeshCgroupSkb.
+func LoadKmeshCgroupSkb() (*ebpf.CollectionSpec, error) {
+	reader := bytes.NewReader(_KmeshCgroupSkbBytes)
+	spec, err := ebpf.LoadCollectionSpecFromReader(reader)
+	if err != nil {
+		return nil, fmt.Errorf("can't load KmeshCgroupSkb: %w", err)
+	}
+
+	return spec, err
+}
+
+// LoadKmeshCgroupSkbObjects loads KmeshCgroupSkb and converts it into a struct.
+//
+// The following types are suitable as obj argument:
+//
+//	*KmeshCgroupSkbObjects
+//	*KmeshCgroupSkbPrograms
+//	*KmeshCgroupSkbMaps
+//
+// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
+func LoadKmeshCgroupSkbObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
+	spec, err := LoadKmeshCgroupSkb()
+	if err != nil {
+		return err
+	}
+
+	return spec.LoadAndAssign(obj, opts)
+}
+
+// KmeshCgroupSkbSpecs contains maps and programs before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbSpecs struct {
+	KmeshCgroupSkbProgramSpecs
+	KmeshCgroupSkbMapSpecs
+	KmeshCgroupSkbVariableSpecs
+}
+
+// KmeshCgroupSkbProgramSpecs contains programs before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbProgramSpecs struct {
+	CgroupSkbEgressProg  *ebpf.ProgramSpec `ebpf:"cgroup_skb_egress_prog"`
+	CgroupSkbIngressProg *ebpf.ProgramSpec `ebpf:"cgroup_skb_ingress_prog"`
+}
+
+// KmeshCgroupSkbMapSpecs contains maps before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbMapSpecs struct {
+	KmAuthReq     *ebpf.MapSpec `ebpf:"km_auth_req"`
+	KmAuthRes     *ebpf.MapSpec `ebpf:"km_auth_res"`
+	KmBackend     *ebpf.MapSpec `ebpf:"km_backend"`
+	KmEndpoint    *ebpf.MapSpec `ebpf:"km_endpoint"`
+	KmFrontend    *ebpf.MapSpec `ebpf:"km_frontend"`
+	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
+	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
+	KmOrigDst     *ebpf.MapSpec `ebpf:"km_orig_dst"`
+	KmPerfInfo    *ebpf.MapSpec `ebpf:"km_perf_info"`
+	KmPerfMap     *ebpf.MapSpec `ebpf:"km_perf_map"`
+	KmService     *ebpf.MapSpec `ebpf:"km_service"`
+	KmSockstorage *ebpf.MapSpec `ebpf:"km_sockstorage"`
+	KmTcpProbe    *ebpf.MapSpec `ebpf:"km_tcp_probe"`
+	KmTmpbuf      *ebpf.MapSpec `ebpf:"km_tmpbuf"`
+	KmWlpolicy    *ebpf.MapSpec `ebpf:"km_wlpolicy"`
+	KmeshMap1600  *ebpf.MapSpec `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.MapSpec `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.MapSpec `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.MapSpec `ebpf:"kmesh_map64"`
+}
+
+// KmeshCgroupSkbVariableSpecs contains global variables before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbVariableSpecs struct {
+	BpfLogLevel      *ebpf.VariableSpec `ebpf:"bpf_log_level"`
+	EnableMonitoring *ebpf.VariableSpec `ebpf:"enable_monitoring"`
+}
+
+// KmeshCgroupSkbObjects contains all objects after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbObjects struct {
+	KmeshCgroupSkbPrograms
+	KmeshCgroupSkbMaps
+	KmeshCgroupSkbVariables
+}
+
+func (o *KmeshCgroupSkbObjects) Close() error {
+	return _KmeshCgroupSkbClose(
+		&o.KmeshCgroupSkbPrograms,
+		&o.KmeshCgroupSkbMaps,
+	)
+}
+
+// KmeshCgroupSkbMaps contains all maps after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbMaps struct {
+	KmAuthReq     *ebpf.Map `ebpf:"km_auth_req"`
+	KmAuthRes     *ebpf.Map `ebpf:"km_auth_res"`
+	KmBackend     *ebpf.Map `ebpf:"km_backend"`
+	KmEndpoint    *ebpf.Map `ebpf:"km_endpoint"`
+	KmFrontend    *ebpf.Map `ebpf:"km_frontend"`
+	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
+	KmManage      *ebpf.Map `ebpf:"km_manage"`
+	KmOrigDst     *ebpf.Map `ebpf:"km_orig_dst"`
+	KmPerfInfo    *ebpf.Map `ebpf:"km_perf_info"`
+	KmPerfMap     *ebpf.Map `ebpf:"km_perf_map"`
+	KmService     *ebpf.Map `ebpf:"km_service"`
+	KmSockstorage *ebpf.Map `ebpf:"km_sockstorage"`
+	KmTcpProbe    *ebpf.Map `ebpf:"km_tcp_probe"`
+	KmTmpbuf      *ebpf.Map `ebpf:"km_tmpbuf"`
+	KmWlpolicy    *ebpf.Map `ebpf:"km_wlpolicy"`
+	KmeshMap1600  *ebpf.Map `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.Map `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.Map `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.Map `ebpf:"kmesh_map64"`
+}
+
+func (m *KmeshCgroupSkbMaps) Close() error {
+	return _KmeshCgroupSkbClose(
+		m.KmAuthReq,
+		m.KmAuthRes,
+		m.KmBackend,
+		m.KmEndpoint,
+		m.KmFrontend,
+		m.KmLogEvent,
+		m.KmManage,
+		m.KmOrigDst,
+		m.KmPerfInfo,
+		m.KmPerfMap,
+		m.KmService,
+		m.KmSockstorage,
+		m.KmTcpProbe,
+		m.KmTmpbuf,
+		m.KmWlpolicy,
+		m.KmeshMap1600,
+		m.KmeshMap192,
+		m.KmeshMap296,
+		m.KmeshMap64,
+	)
+}
+
+// KmeshCgroupSkbVariables contains all global variables after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbVariables struct {
+	BpfLogLevel      *ebpf.Variable `ebpf:"bpf_log_level"`
+	EnableMonitoring *ebpf.Variable `ebpf:"enable_monitoring"`
+}
+
+// KmeshCgroupSkbPrograms contains all programs after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbPrograms struct {
+	CgroupSkbEgressProg  *ebpf.Program `ebpf:"cgroup_skb_egress_prog"`
+	CgroupSkbIngressProg *ebpf.Program `ebpf:"cgroup_skb_ingress_prog"`
+}
+
+func (p *KmeshCgroupSkbPrograms) Close() error {
+	return _KmeshCgroupSkbClose(
+		p.CgroupSkbEgressProg,
+		p.CgroupSkbIngressProg,
+	)
+}
+
+func _KmeshCgroupSkbClose(closers ...io.Closer) error {
+	for _, closer := range closers {
+		if err := closer.Close(); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// Do not access this directly.
+//
+//go:embed kmeshcgroupskb_bpfel.o
+var _KmeshCgroupSkbBytes []byte
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshcgroupskbcompat_bpfeb.go b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupskbcompat_bpfeb.go
new file mode 100644
index 00000000..7de8437a
--- /dev/null
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupskbcompat_bpfeb.go
@@ -0,0 +1,233 @@
+// Code generated by bpf2go; DO NOT EDIT.
+//go:build mips || mips64 || ppc64 || s390x
+
+package dualengine
+
+import (
+	"bytes"
+	_ "embed"
+	"fmt"
+	"io"
+
+	"github.com/cilium/ebpf"
+)
+
+type KmeshCgroupSkbCompatBpfSockTuple struct {
+	Ipv4 struct {
+		Saddr uint32
+		Daddr uint32
+		Sport uint16
+		Dport uint16
+	}
+	_ [24]byte
+}
+
+type KmeshCgroupSkbCompatBuf struct{ Data [40]int8 }
+
+type KmeshCgroupSkbCompatManagerKey struct {
+	NetnsCookie uint64
+	_           [8]byte
+}
+
+type KmeshCgroupSkbCompatOperationUsageData struct {
+	StartTime     uint64
+	EndTime       uint64
+	PidTgid       uint64
+	OperationType uint32
+	_             [4]byte
+}
+
+type KmeshCgroupSkbCompatOperationUsageKey struct {
+	SocketCookie  uint64
+	OperationType uint32
+	_             [4]byte
+}
+
+type KmeshCgroupSkbCompatSockStorageData struct {
+	ConnectNs      uint64
+	LastReportNs   uint64
+	Direction      uint8
+	ConnectSuccess uint8
+	_              [6]byte
+}
+
+// LoadKmeshCgroupSkbCompat returns the embedded CollectionSpec for KmeshCgroupSkbCompat.
+func LoadKmeshCgroupSkbCompat() (*ebpf.CollectionSpec, error) {
+	reader := bytes.NewReader(_KmeshCgroupSkbCompatBytes)
+	spec, err := ebpf.LoadCollectionSpecFromReader(reader)
+	if err != nil {
+		return nil, fmt.Errorf("can't load KmeshCgroupSkbCompat: %w", err)
+	}
+
+	return spec, err
+}
+
+// LoadKmeshCgroupSkbCompatObjects loads KmeshCgroupSkbCompat and converts it into a struct.
+//
+// The following types are suitable as obj argument:
+//
+//	*KmeshCgroupSkbCompatObjects
+//	*KmeshCgroupSkbCompatPrograms
+//	*KmeshCgroupSkbCompatMaps
+//
+// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
+func LoadKmeshCgroupSkbCompatObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
+	spec, err := LoadKmeshCgroupSkbCompat()
+	if err != nil {
+		return err
+	}
+
+	return spec.LoadAndAssign(obj, opts)
+}
+
+// KmeshCgroupSkbCompatSpecs contains maps and programs before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbCompatSpecs struct {
+	KmeshCgroupSkbCompatProgramSpecs
+	KmeshCgroupSkbCompatMapSpecs
+	KmeshCgroupSkbCompatVariableSpecs
+}
+
+// KmeshCgroupSkbCompatProgramSpecs contains programs before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbCompatProgramSpecs struct {
+	CgroupSkbEgressProg  *ebpf.ProgramSpec `ebpf:"cgroup_skb_egress_prog"`
+	CgroupSkbIngressProg *ebpf.ProgramSpec `ebpf:"cgroup_skb_ingress_prog"`
+}
+
+// KmeshCgroupSkbCompatMapSpecs contains maps before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbCompatMapSpecs struct {
+	KmAuthReq     *ebpf.MapSpec `ebpf:"km_auth_req"`
+	KmAuthRes     *ebpf.MapSpec `ebpf:"km_auth_res"`
+	KmBackend     *ebpf.MapSpec `ebpf:"km_backend"`
+	KmEndpoint    *ebpf.MapSpec `ebpf:"km_endpoint"`
+	KmFrontend    *ebpf.MapSpec `ebpf:"km_frontend"`
+	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
+	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
+	KmOrigDst     *ebpf.MapSpec `ebpf:"km_orig_dst"`
+	KmPerfInfo    *ebpf.MapSpec `ebpf:"km_perf_info"`
+	KmPerfMap     *ebpf.MapSpec `ebpf:"km_perf_map"`
+	KmService     *ebpf.MapSpec `ebpf:"km_service"`
+	KmSockstorage *ebpf.MapSpec `ebpf:"km_sockstorage"`
+	KmTcpProbe    *ebpf.MapSpec `ebpf:"km_tcp_probe"`
+	KmTmpbuf      *ebpf.MapSpec `ebpf:"km_tmpbuf"`
+	KmWlpolicy    *ebpf.MapSpec `ebpf:"km_wlpolicy"`
+	KmeshMap1600  *ebpf.MapSpec `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.MapSpec `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.MapSpec `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.MapSpec `ebpf:"kmesh_map64"`
+}
+
+// KmeshCgroupSkbCompatVariableSpecs contains global variables before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbCompatVariableSpecs struct {
+	BpfLogLevel      *ebpf.VariableSpec `ebpf:"bpf_log_level"`
+	EnableMonitoring *ebpf.VariableSpec `ebpf:"enable_monitoring"`
+}
+
+// KmeshCgroupSkbCompatObjects contains all objects after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbCompatObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbCompatObjects struct {
+	KmeshCgroupSkbCompatPrograms
+	KmeshCgroupSkbCompatMaps
+	KmeshCgroupSkbCompatVariables
+}
+
+func (o *KmeshCgroupSkbCompatObjects) Close() error {
+	return _KmeshCgroupSkbCompatClose(
+		&o.KmeshCgroupSkbCompatPrograms,
+		&o.KmeshCgroupSkbCompatMaps,
+	)
+}
+
+// KmeshCgroupSkbCompatMaps contains all maps after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbCompatObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbCompatMaps struct {
+	KmAuthReq     *ebpf.Map `ebpf:"km_auth_req"`
+	KmAuthRes     *ebpf.Map `ebpf:"km_auth_res"`
+	KmBackend     *ebpf.Map `ebpf:"km_backend"`
+	KmEndpoint    *ebpf.Map `ebpf:"km_endpoint"`
+	KmFrontend    *ebpf.Map `ebpf:"km_frontend"`
+	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
+	KmManage      *ebpf.Map `ebpf:"km_manage"`
+	KmOrigDst     *ebpf.Map `ebpf:"km_orig_dst"`
+	KmPerfInfo    *ebpf.Map `ebpf:"km_perf_info"`
+	KmPerfMap     *ebpf.Map `ebpf:"km_perf_map"`
+	KmService     *ebpf.Map `ebpf:"km_service"`
+	KmSockstorage *ebpf.Map `ebpf:"km_sockstorage"`
+	KmTcpProbe    *ebpf.Map `ebpf:"km_tcp_probe"`
+	KmTmpbuf      *ebpf.Map `ebpf:"km_tmpbuf"`
+	KmWlpolicy    *ebpf.Map `ebpf:"km_wlpolicy"`
+	KmeshMap1600  *ebpf.Map `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.Map `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.Map `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.Map `ebpf:"kmesh_map64"`
+}
+
+func (m *KmeshCgroupSkbCompatMaps) Close() error {
+	return _KmeshCgroupSkbCompatClose(
+		m.KmAuthReq,
+		m.KmAuthRes,
+		m.KmBackend,
+		m.KmEndpoint,
+		m.KmFrontend,
+		m.KmLogEvent,
+		m.KmManage,
+		m.KmOrigDst,
+		m.KmPerfInfo,
+		m.KmPerfMap,
+		m.KmService,
+		m.KmSockstorage,
+		m.KmTcpProbe,
+		m.KmTmpbuf,
+		m.KmWlpolicy,
+		m.KmeshMap1600,
+		m.KmeshMap192,
+		m.KmeshMap296,
+		m.KmeshMap64,
+	)
+}
+
+// KmeshCgroupSkbCompatVariables contains all global variables after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbCompatObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbCompatVariables struct {
+	BpfLogLevel      *ebpf.Variable `ebpf:"bpf_log_level"`
+	EnableMonitoring *ebpf.Variable `ebpf:"enable_monitoring"`
+}
+
+// KmeshCgroupSkbCompatPrograms contains all programs after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbCompatObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbCompatPrograms struct {
+	CgroupSkbEgressProg  *ebpf.Program `ebpf:"cgroup_skb_egress_prog"`
+	CgroupSkbIngressProg *ebpf.Program `ebpf:"cgroup_skb_ingress_prog"`
+}
+
+func (p *KmeshCgroupSkbCompatPrograms) Close() error {
+	return _KmeshCgroupSkbCompatClose(
+		p.CgroupSkbEgressProg,
+		p.CgroupSkbIngressProg,
+	)
+}
+
+func _KmeshCgroupSkbCompatClose(closers ...io.Closer) error {
+	for _, closer := range closers {
+		if err := closer.Close(); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// Do not access this directly.
+//
+//go:embed kmeshcgroupskbcompat_bpfeb.o
+var _KmeshCgroupSkbCompatBytes []byte
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshcgroupskbcompat_bpfel.go b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupskbcompat_bpfel.go
new file mode 100644
index 00000000..dd3a8974
--- /dev/null
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupskbcompat_bpfel.go
@@ -0,0 +1,233 @@
+// Code generated by bpf2go; DO NOT EDIT.
+//go:build 386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64
+
+package dualengine
+
+import (
+	"bytes"
+	_ "embed"
+	"fmt"
+	"io"
+
+	"github.com/cilium/ebpf"
+)
+
+type KmeshCgroupSkbCompatBpfSockTuple struct {
+	Ipv4 struct {
+		Saddr uint32
+		Daddr uint32
+		Sport uint16
+		Dport uint16
+	}
+	_ [24]byte
+}
+
+type KmeshCgroupSkbCompatBuf struct{ Data [40]int8 }
+
+type KmeshCgroupSkbCompatManagerKey struct {
+	NetnsCookie uint64
+	_           [8]byte
+}
+
+type KmeshCgroupSkbCompatOperationUsageData struct {
+	StartTime     uint64
+	EndTime       uint64
+	PidTgid       uint64
+	OperationType uint32
+	_             [4]byte
+}
+
+type KmeshCgroupSkbCompatOperationUsageKey struct {
+	SocketCookie  uint64
+	OperationType uint32
+	_             [4]byte
+}
+
+type KmeshCgroupSkbCompatSockStorageData struct {
+	ConnectNs      uint64
+	LastReportNs   uint64
+	Direction      uint8
+	ConnectSuccess uint8
+	_              [6]byte
+}
+
+// LoadKmeshCgroupSkbCompat returns the embedded CollectionSpec for KmeshCgroupSkbCompat.
+func LoadKmeshCgroupSkbCompat() (*ebpf.CollectionSpec, error) {
+	reader := bytes.NewReader(_KmeshCgroupSkbCompatBytes)
+	spec, err := ebpf.LoadCollectionSpecFromReader(reader)
+	if err != nil {
+		return nil, fmt.Errorf("can't load KmeshCgroupSkbCompat: %w", err)
+	}
+
+	return spec, err
+}
+
+// LoadKmeshCgroupSkbCompatObjects loads KmeshCgroupSkbCompat and converts it into a struct.
+//
+// The following types are suitable as obj argument:
+//
+//	*KmeshCgroupSkbCompatObjects
+//	*KmeshCgroupSkbCompatPrograms
+//	*KmeshCgroupSkbCompatMaps
+//
+// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
+func LoadKmeshCgroupSkbCompatObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
+	spec, err := LoadKmeshCgroupSkbCompat()
+	if err != nil {
+		return err
+	}
+
+	return spec.LoadAndAssign(obj, opts)
+}
+
+// KmeshCgroupSkbCompatSpecs contains maps and programs before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbCompatSpecs struct {
+	KmeshCgroupSkbCompatProgramSpecs
+	KmeshCgroupSkbCompatMapSpecs
+	KmeshCgroupSkbCompatVariableSpecs
+}
+
+// KmeshCgroupSkbCompatProgramSpecs contains programs before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbCompatProgramSpecs struct {
+	CgroupSkbEgressProg  *ebpf.ProgramSpec `ebpf:"cgroup_skb_egress_prog"`
+	CgroupSkbIngressProg *ebpf.ProgramSpec `ebpf:"cgroup_skb_ingress_prog"`
+}
+
+// KmeshCgroupSkbCompatMapSpecs contains maps before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbCompatMapSpecs struct {
+	KmAuthReq     *ebpf.MapSpec `ebpf:"km_auth_req"`
+	KmAuthRes     *ebpf.MapSpec `ebpf:"km_auth_res"`
+	KmBackend     *ebpf.MapSpec `ebpf:"km_backend"`
+	KmEndpoint    *ebpf.MapSpec `ebpf:"km_endpoint"`
+	KmFrontend    *ebpf.MapSpec `ebpf:"km_frontend"`
+	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
+	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
+	KmOrigDst     *ebpf.MapSpec `ebpf:"km_orig_dst"`
+	KmPerfInfo    *ebpf.MapSpec `ebpf:"km_perf_info"`
+	KmPerfMap     *ebpf.MapSpec `ebpf:"km_perf_map"`
+	KmService     *ebpf.MapSpec `ebpf:"km_service"`
+	KmSockstorage *ebpf.MapSpec `ebpf:"km_sockstorage"`
+	KmTcpProbe    *ebpf.MapSpec `ebpf:"km_tcp_probe"`
+	KmTmpbuf      *ebpf.MapSpec `ebpf:"km_tmpbuf"`
+	KmWlpolicy    *ebpf.MapSpec `ebpf:"km_wlpolicy"`
+	KmeshMap1600  *ebpf.MapSpec `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.MapSpec `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.MapSpec `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.MapSpec `ebpf:"kmesh_map64"`
+}
+
+// KmeshCgroupSkbCompatVariableSpecs contains global variables before they are loaded into the kernel.
+//
+// It can be passed ebpf.CollectionSpec.Assign.
+type KmeshCgroupSkbCompatVariableSpecs struct {
+	BpfLogLevel      *ebpf.VariableSpec `ebpf:"bpf_log_level"`
+	EnableMonitoring *ebpf.VariableSpec `ebpf:"enable_monitoring"`
+}
+
+// KmeshCgroupSkbCompatObjects contains all objects after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbCompatObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbCompatObjects struct {
+	KmeshCgroupSkbCompatPrograms
+	KmeshCgroupSkbCompatMaps
+	KmeshCgroupSkbCompatVariables
+}
+
+func (o *KmeshCgroupSkbCompatObjects) Close() error {
+	return _KmeshCgroupSkbCompatClose(
+		&o.KmeshCgroupSkbCompatPrograms,
+		&o.KmeshCgroupSkbCompatMaps,
+	)
+}
+
+// KmeshCgroupSkbCompatMaps contains all maps after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbCompatObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbCompatMaps struct {
+	KmAuthReq     *ebpf.Map `ebpf:"km_auth_req"`
+	KmAuthRes     *ebpf.Map `ebpf:"km_auth_res"`
+	KmBackend     *ebpf.Map `ebpf:"km_backend"`
+	KmEndpoint    *ebpf.Map `ebpf:"km_endpoint"`
+	KmFrontend    *ebpf.Map `ebpf:"km_frontend"`
+	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
+	KmManage      *ebpf.Map `ebpf:"km_manage"`
+	KmOrigDst     *ebpf.Map `ebpf:"km_orig_dst"`
+	KmPerfInfo    *ebpf.Map `ebpf:"km_perf_info"`
+	KmPerfMap     *ebpf.Map `ebpf:"km_perf_map"`
+	KmService     *ebpf.Map `ebpf:"km_service"`
+	KmSockstorage *ebpf.Map `ebpf:"km_sockstorage"`
+	KmTcpProbe    *ebpf.Map `ebpf:"km_tcp_probe"`
+	KmTmpbuf      *ebpf.Map `ebpf:"km_tmpbuf"`
+	KmWlpolicy    *ebpf.Map `ebpf:"km_wlpolicy"`
+	KmeshMap1600  *ebpf.Map `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.Map `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.Map `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.Map `ebpf:"kmesh_map64"`
+}
+
+func (m *KmeshCgroupSkbCompatMaps) Close() error {
+	return _KmeshCgroupSkbCompatClose(
+		m.KmAuthReq,
+		m.KmAuthRes,
+		m.KmBackend,
+		m.KmEndpoint,
+		m.KmFrontend,
+		m.KmLogEvent,
+		m.KmManage,
+		m.KmOrigDst,
+		m.KmPerfInfo,
+		m.KmPerfMap,
+		m.KmService,
+		m.KmSockstorage,
+		m.KmTcpProbe,
+		m.KmTmpbuf,
+		m.KmWlpolicy,
+		m.KmeshMap1600,
+		m.KmeshMap192,
+		m.KmeshMap296,
+		m.KmeshMap64,
+	)
+}
+
+// KmeshCgroupSkbCompatVariables contains all global variables after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbCompatObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbCompatVariables struct {
+	BpfLogLevel      *ebpf.Variable `ebpf:"bpf_log_level"`
+	EnableMonitoring *ebpf.Variable `ebpf:"enable_monitoring"`
+}
+
+// KmeshCgroupSkbCompatPrograms contains all programs after they have been loaded into the kernel.
+//
+// It can be passed to LoadKmeshCgroupSkbCompatObjects or ebpf.CollectionSpec.LoadAndAssign.
+type KmeshCgroupSkbCompatPrograms struct {
+	CgroupSkbEgressProg  *ebpf.Program `ebpf:"cgroup_skb_egress_prog"`
+	CgroupSkbIngressProg *ebpf.Program `ebpf:"cgroup_skb_ingress_prog"`
+}
+
+func (p *KmeshCgroupSkbCompatPrograms) Close() error {
+	return _KmeshCgroupSkbCompatClose(
+		p.CgroupSkbEgressProg,
+		p.CgroupSkbIngressProg,
+	)
+}
+
+func _KmeshCgroupSkbCompatClose(closers ...io.Closer) error {
+	for _, closer := range closers {
+		if err := closer.Close(); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// Do not access this directly.
+//
+//go:embed kmeshcgroupskbcompat_bpfel.o
+var _KmeshCgroupSkbCompatBytes []byte
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkload_bpfeb.go b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkload_bpfeb.go
index ff175193..c7166ba4 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkload_bpfeb.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkload_bpfeb.go
@@ -45,9 +45,15 @@ type KmeshCgroupSockWorkloadOperationUsageKey struct {
 
 type KmeshCgroupSockWorkloadSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        KmeshCgroupSockWorkloadBpfSockTuple
+	_              [4]byte
 }
 
 // LoadKmeshCgroupSockWorkload returns the embedded CollectionSpec for KmeshCgroupSockWorkload.
@@ -108,7 +114,6 @@ type KmeshCgroupSockWorkloadMapSpecs struct {
 	KmFrontend    *ebpf.MapSpec `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
 	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.MapSpec `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.MapSpec `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.MapSpec `ebpf:"km_perf_map"`
 	KmService     *ebpf.MapSpec `ebpf:"km_service"`
@@ -159,7 +164,6 @@ type KmeshCgroupSockWorkloadMaps struct {
 	KmFrontend    *ebpf.Map `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
 	KmManage      *ebpf.Map `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.Map `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.Map `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.Map `ebpf:"km_perf_map"`
 	KmService     *ebpf.Map `ebpf:"km_service"`
@@ -184,7 +188,6 @@ func (m *KmeshCgroupSockWorkloadMaps) Close() error {
 		m.KmFrontend,
 		m.KmLogEvent,
 		m.KmManage,
-		m.KmOrigDst,
 		m.KmPerfInfo,
 		m.KmPerfMap,
 		m.KmService,
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkload_bpfel.go b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkload_bpfel.go
index d0ffd5df..1cb9ba40 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkload_bpfel.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkload_bpfel.go
@@ -45,9 +45,15 @@ type KmeshCgroupSockWorkloadOperationUsageKey struct {
 
 type KmeshCgroupSockWorkloadSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        KmeshCgroupSockWorkloadBpfSockTuple
+	_              [4]byte
 }
 
 // LoadKmeshCgroupSockWorkload returns the embedded CollectionSpec for KmeshCgroupSockWorkload.
@@ -108,7 +114,6 @@ type KmeshCgroupSockWorkloadMapSpecs struct {
 	KmFrontend    *ebpf.MapSpec `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
 	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.MapSpec `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.MapSpec `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.MapSpec `ebpf:"km_perf_map"`
 	KmService     *ebpf.MapSpec `ebpf:"km_service"`
@@ -159,7 +164,6 @@ type KmeshCgroupSockWorkloadMaps struct {
 	KmFrontend    *ebpf.Map `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
 	KmManage      *ebpf.Map `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.Map `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.Map `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.Map `ebpf:"km_perf_map"`
 	KmService     *ebpf.Map `ebpf:"km_service"`
@@ -184,7 +188,6 @@ func (m *KmeshCgroupSockWorkloadMaps) Close() error {
 		m.KmFrontend,
 		m.KmLogEvent,
 		m.KmManage,
-		m.KmOrigDst,
 		m.KmPerfInfo,
 		m.KmPerfMap,
 		m.KmService,
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkloadcompat_bpfeb.go b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkloadcompat_bpfeb.go
index 4119e6e7..2fcdd500 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkloadcompat_bpfeb.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkloadcompat_bpfeb.go
@@ -45,9 +45,15 @@ type KmeshCgroupSockWorkloadCompatOperationUsageKey struct {
 
 type KmeshCgroupSockWorkloadCompatSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        KmeshCgroupSockWorkloadCompatBpfSockTuple
+	_              [4]byte
 }
 
 // LoadKmeshCgroupSockWorkloadCompat returns the embedded CollectionSpec for KmeshCgroupSockWorkloadCompat.
@@ -108,7 +114,6 @@ type KmeshCgroupSockWorkloadCompatMapSpecs struct {
 	KmFrontend    *ebpf.MapSpec `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
 	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.MapSpec `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.MapSpec `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.MapSpec `ebpf:"km_perf_map"`
 	KmService     *ebpf.MapSpec `ebpf:"km_service"`
@@ -159,7 +164,6 @@ type KmeshCgroupSockWorkloadCompatMaps struct {
 	KmFrontend    *ebpf.Map `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
 	KmManage      *ebpf.Map `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.Map `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.Map `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.Map `ebpf:"km_perf_map"`
 	KmService     *ebpf.Map `ebpf:"km_service"`
@@ -184,7 +188,6 @@ func (m *KmeshCgroupSockWorkloadCompatMaps) Close() error {
 		m.KmFrontend,
 		m.KmLogEvent,
 		m.KmManage,
-		m.KmOrigDst,
 		m.KmPerfInfo,
 		m.KmPerfMap,
 		m.KmService,
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkloadcompat_bpfel.go b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkloadcompat_bpfel.go
index 8d17ea3c..0c4014ae 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkloadcompat_bpfel.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshcgroupsockworkloadcompat_bpfel.go
@@ -45,9 +45,15 @@ type KmeshCgroupSockWorkloadCompatOperationUsageKey struct {
 
 type KmeshCgroupSockWorkloadCompatSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        KmeshCgroupSockWorkloadCompatBpfSockTuple
+	_              [4]byte
 }
 
 // LoadKmeshCgroupSockWorkloadCompat returns the embedded CollectionSpec for KmeshCgroupSockWorkloadCompat.
@@ -108,7 +114,6 @@ type KmeshCgroupSockWorkloadCompatMapSpecs struct {
 	KmFrontend    *ebpf.MapSpec `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
 	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.MapSpec `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.MapSpec `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.MapSpec `ebpf:"km_perf_map"`
 	KmService     *ebpf.MapSpec `ebpf:"km_service"`
@@ -159,7 +164,6 @@ type KmeshCgroupSockWorkloadCompatMaps struct {
 	KmFrontend    *ebpf.Map `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
 	KmManage      *ebpf.Map `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.Map `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.Map `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.Map `ebpf:"km_perf_map"`
 	KmService     *ebpf.Map `ebpf:"km_service"`
@@ -184,7 +188,6 @@ func (m *KmeshCgroupSockWorkloadCompatMaps) Close() error {
 		m.KmFrontend,
 		m.KmLogEvent,
 		m.KmManage,
-		m.KmOrigDst,
 		m.KmPerfInfo,
 		m.KmPerfMap,
 		m.KmService,
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshsendmsg_bpfeb.go b/bpf/kmesh/bpf2go/dualengine/kmeshsendmsg_bpfeb.go
index f816fafa..7f5d79f3 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshsendmsg_bpfeb.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshsendmsg_bpfeb.go
@@ -12,17 +12,32 @@ import (
 	"github.com/cilium/ebpf"
 )
 
-type KmeshSendmsgBpfSockTuple struct {
-	Ipv4 struct {
-		Saddr uint32
-		Daddr uint32
-		Sport uint16
-		Dport uint16
-	}
-	_ [24]byte
+type KmeshSendmsgBuf struct{ Data [40]int8 }
+
+type KmeshSendmsgManagerKey struct {
+	NetnsCookie uint64
+	_           [8]byte
 }
 
-type KmeshSendmsgBuf struct{ Data [40]int8 }
+type KmeshSendmsgSockStorageData struct {
+	ConnectNs      uint64
+	Direction      uint8
+	ConnectSuccess uint8
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        struct {
+		Ipv4 struct {
+			Saddr uint32
+			Daddr uint32
+			Sport uint16
+			Dport uint16
+		}
+		_ [24]byte
+	}
+	_ [4]byte
+}
 
 // LoadKmeshSendmsg returns the embedded CollectionSpec for KmeshSendmsg.
 func LoadKmeshSendmsg() (*ebpf.CollectionSpec, error) {
@@ -73,9 +88,14 @@ type KmeshSendmsgProgramSpecs struct {
 //
 // It can be passed ebpf.CollectionSpec.Assign.
 type KmeshSendmsgMapSpecs struct {
-	KmLogEvent *ebpf.MapSpec `ebpf:"km_log_event"`
-	KmOrigDst  *ebpf.MapSpec `ebpf:"km_orig_dst"`
-	KmTmpbuf   *ebpf.MapSpec `ebpf:"km_tmpbuf"`
+	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
+	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
+	KmSockstorage *ebpf.MapSpec `ebpf:"km_sockstorage"`
+	KmTmpbuf      *ebpf.MapSpec `ebpf:"km_tmpbuf"`
+	KmeshMap1600  *ebpf.MapSpec `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.MapSpec `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.MapSpec `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.MapSpec `ebpf:"kmesh_map64"`
 }
 
 // KmeshSendmsgVariableSpecs contains global variables before they are loaded into the kernel.
@@ -105,16 +125,26 @@ func (o *KmeshSendmsgObjects) Close() error {
 //
 // It can be passed to LoadKmeshSendmsgObjects or ebpf.CollectionSpec.LoadAndAssign.
 type KmeshSendmsgMaps struct {
-	KmLogEvent *ebpf.Map `ebpf:"km_log_event"`
-	KmOrigDst  *ebpf.Map `ebpf:"km_orig_dst"`
-	KmTmpbuf   *ebpf.Map `ebpf:"km_tmpbuf"`
+	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
+	KmManage      *ebpf.Map `ebpf:"km_manage"`
+	KmSockstorage *ebpf.Map `ebpf:"km_sockstorage"`
+	KmTmpbuf      *ebpf.Map `ebpf:"km_tmpbuf"`
+	KmeshMap1600  *ebpf.Map `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.Map `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.Map `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.Map `ebpf:"kmesh_map64"`
 }
 
 func (m *KmeshSendmsgMaps) Close() error {
 	return _KmeshSendmsgClose(
 		m.KmLogEvent,
-		m.KmOrigDst,
+		m.KmManage,
+		m.KmSockstorage,
 		m.KmTmpbuf,
+		m.KmeshMap1600,
+		m.KmeshMap192,
+		m.KmeshMap296,
+		m.KmeshMap64,
 	)
 }
 
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshsendmsg_bpfel.go b/bpf/kmesh/bpf2go/dualengine/kmeshsendmsg_bpfel.go
index 78117dd2..5335e006 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshsendmsg_bpfel.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshsendmsg_bpfel.go
@@ -12,17 +12,32 @@ import (
 	"github.com/cilium/ebpf"
 )
 
-type KmeshSendmsgBpfSockTuple struct {
-	Ipv4 struct {
-		Saddr uint32
-		Daddr uint32
-		Sport uint16
-		Dport uint16
-	}
-	_ [24]byte
+type KmeshSendmsgBuf struct{ Data [40]int8 }
+
+type KmeshSendmsgManagerKey struct {
+	NetnsCookie uint64
+	_           [8]byte
 }
 
-type KmeshSendmsgBuf struct{ Data [40]int8 }
+type KmeshSendmsgSockStorageData struct {
+	ConnectNs      uint64
+	Direction      uint8
+	ConnectSuccess uint8
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        struct {
+		Ipv4 struct {
+			Saddr uint32
+			Daddr uint32
+			Sport uint16
+			Dport uint16
+		}
+		_ [24]byte
+	}
+	_ [4]byte
+}
 
 // LoadKmeshSendmsg returns the embedded CollectionSpec for KmeshSendmsg.
 func LoadKmeshSendmsg() (*ebpf.CollectionSpec, error) {
@@ -73,9 +88,14 @@ type KmeshSendmsgProgramSpecs struct {
 //
 // It can be passed ebpf.CollectionSpec.Assign.
 type KmeshSendmsgMapSpecs struct {
-	KmLogEvent *ebpf.MapSpec `ebpf:"km_log_event"`
-	KmOrigDst  *ebpf.MapSpec `ebpf:"km_orig_dst"`
-	KmTmpbuf   *ebpf.MapSpec `ebpf:"km_tmpbuf"`
+	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
+	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
+	KmSockstorage *ebpf.MapSpec `ebpf:"km_sockstorage"`
+	KmTmpbuf      *ebpf.MapSpec `ebpf:"km_tmpbuf"`
+	KmeshMap1600  *ebpf.MapSpec `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.MapSpec `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.MapSpec `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.MapSpec `ebpf:"kmesh_map64"`
 }
 
 // KmeshSendmsgVariableSpecs contains global variables before they are loaded into the kernel.
@@ -105,16 +125,26 @@ func (o *KmeshSendmsgObjects) Close() error {
 //
 // It can be passed to LoadKmeshSendmsgObjects or ebpf.CollectionSpec.LoadAndAssign.
 type KmeshSendmsgMaps struct {
-	KmLogEvent *ebpf.Map `ebpf:"km_log_event"`
-	KmOrigDst  *ebpf.Map `ebpf:"km_orig_dst"`
-	KmTmpbuf   *ebpf.Map `ebpf:"km_tmpbuf"`
+	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
+	KmManage      *ebpf.Map `ebpf:"km_manage"`
+	KmSockstorage *ebpf.Map `ebpf:"km_sockstorage"`
+	KmTmpbuf      *ebpf.Map `ebpf:"km_tmpbuf"`
+	KmeshMap1600  *ebpf.Map `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.Map `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.Map `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.Map `ebpf:"kmesh_map64"`
 }
 
 func (m *KmeshSendmsgMaps) Close() error {
 	return _KmeshSendmsgClose(
 		m.KmLogEvent,
-		m.KmOrigDst,
+		m.KmManage,
+		m.KmSockstorage,
 		m.KmTmpbuf,
+		m.KmeshMap1600,
+		m.KmeshMap192,
+		m.KmeshMap296,
+		m.KmeshMap64,
 	)
 }
 
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshsendmsgcompat_bpfeb.go b/bpf/kmesh/bpf2go/dualengine/kmeshsendmsgcompat_bpfeb.go
index f0152cb6..384b8abb 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshsendmsgcompat_bpfeb.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshsendmsgcompat_bpfeb.go
@@ -12,17 +12,32 @@ import (
 	"github.com/cilium/ebpf"
 )
 
-type KmeshSendmsgCompatBpfSockTuple struct {
-	Ipv4 struct {
-		Saddr uint32
-		Daddr uint32
-		Sport uint16
-		Dport uint16
-	}
-	_ [24]byte
+type KmeshSendmsgCompatBuf struct{ Data [40]int8 }
+
+type KmeshSendmsgCompatManagerKey struct {
+	NetnsCookie uint64
+	_           [8]byte
 }
 
-type KmeshSendmsgCompatBuf struct{ Data [40]int8 }
+type KmeshSendmsgCompatSockStorageData struct {
+	ConnectNs      uint64
+	Direction      uint8
+	ConnectSuccess uint8
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        struct {
+		Ipv4 struct {
+			Saddr uint32
+			Daddr uint32
+			Sport uint16
+			Dport uint16
+		}
+		_ [24]byte
+	}
+	_ [4]byte
+}
 
 // LoadKmeshSendmsgCompat returns the embedded CollectionSpec for KmeshSendmsgCompat.
 func LoadKmeshSendmsgCompat() (*ebpf.CollectionSpec, error) {
@@ -73,9 +88,14 @@ type KmeshSendmsgCompatProgramSpecs struct {
 //
 // It can be passed ebpf.CollectionSpec.Assign.
 type KmeshSendmsgCompatMapSpecs struct {
-	KmLogEvent *ebpf.MapSpec `ebpf:"km_log_event"`
-	KmOrigDst  *ebpf.MapSpec `ebpf:"km_orig_dst"`
-	KmTmpbuf   *ebpf.MapSpec `ebpf:"km_tmpbuf"`
+	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
+	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
+	KmSockstorage *ebpf.MapSpec `ebpf:"km_sockstorage"`
+	KmTmpbuf      *ebpf.MapSpec `ebpf:"km_tmpbuf"`
+	KmeshMap1600  *ebpf.MapSpec `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.MapSpec `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.MapSpec `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.MapSpec `ebpf:"kmesh_map64"`
 }
 
 // KmeshSendmsgCompatVariableSpecs contains global variables before they are loaded into the kernel.
@@ -105,16 +125,26 @@ func (o *KmeshSendmsgCompatObjects) Close() error {
 //
 // It can be passed to LoadKmeshSendmsgCompatObjects or ebpf.CollectionSpec.LoadAndAssign.
 type KmeshSendmsgCompatMaps struct {
-	KmLogEvent *ebpf.Map `ebpf:"km_log_event"`
-	KmOrigDst  *ebpf.Map `ebpf:"km_orig_dst"`
-	KmTmpbuf   *ebpf.Map `ebpf:"km_tmpbuf"`
+	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
+	KmManage      *ebpf.Map `ebpf:"km_manage"`
+	KmSockstorage *ebpf.Map `ebpf:"km_sockstorage"`
+	KmTmpbuf      *ebpf.Map `ebpf:"km_tmpbuf"`
+	KmeshMap1600  *ebpf.Map `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.Map `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.Map `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.Map `ebpf:"kmesh_map64"`
 }
 
 func (m *KmeshSendmsgCompatMaps) Close() error {
 	return _KmeshSendmsgCompatClose(
 		m.KmLogEvent,
-		m.KmOrigDst,
+		m.KmManage,
+		m.KmSockstorage,
 		m.KmTmpbuf,
+		m.KmeshMap1600,
+		m.KmeshMap192,
+		m.KmeshMap296,
+		m.KmeshMap64,
 	)
 }
 
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshsendmsgcompat_bpfel.go b/bpf/kmesh/bpf2go/dualengine/kmeshsendmsgcompat_bpfel.go
index 00fe1648..b262f3b9 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshsendmsgcompat_bpfel.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshsendmsgcompat_bpfel.go
@@ -12,17 +12,32 @@ import (
 	"github.com/cilium/ebpf"
 )
 
-type KmeshSendmsgCompatBpfSockTuple struct {
-	Ipv4 struct {
-		Saddr uint32
-		Daddr uint32
-		Sport uint16
-		Dport uint16
-	}
-	_ [24]byte
+type KmeshSendmsgCompatBuf struct{ Data [40]int8 }
+
+type KmeshSendmsgCompatManagerKey struct {
+	NetnsCookie uint64
+	_           [8]byte
 }
 
-type KmeshSendmsgCompatBuf struct{ Data [40]int8 }
+type KmeshSendmsgCompatSockStorageData struct {
+	ConnectNs      uint64
+	Direction      uint8
+	ConnectSuccess uint8
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        struct {
+		Ipv4 struct {
+			Saddr uint32
+			Daddr uint32
+			Sport uint16
+			Dport uint16
+		}
+		_ [24]byte
+	}
+	_ [4]byte
+}
 
 // LoadKmeshSendmsgCompat returns the embedded CollectionSpec for KmeshSendmsgCompat.
 func LoadKmeshSendmsgCompat() (*ebpf.CollectionSpec, error) {
@@ -73,9 +88,14 @@ type KmeshSendmsgCompatProgramSpecs struct {
 //
 // It can be passed ebpf.CollectionSpec.Assign.
 type KmeshSendmsgCompatMapSpecs struct {
-	KmLogEvent *ebpf.MapSpec `ebpf:"km_log_event"`
-	KmOrigDst  *ebpf.MapSpec `ebpf:"km_orig_dst"`
-	KmTmpbuf   *ebpf.MapSpec `ebpf:"km_tmpbuf"`
+	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
+	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
+	KmSockstorage *ebpf.MapSpec `ebpf:"km_sockstorage"`
+	KmTmpbuf      *ebpf.MapSpec `ebpf:"km_tmpbuf"`
+	KmeshMap1600  *ebpf.MapSpec `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.MapSpec `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.MapSpec `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.MapSpec `ebpf:"kmesh_map64"`
 }
 
 // KmeshSendmsgCompatVariableSpecs contains global variables before they are loaded into the kernel.
@@ -105,16 +125,26 @@ func (o *KmeshSendmsgCompatObjects) Close() error {
 //
 // It can be passed to LoadKmeshSendmsgCompatObjects or ebpf.CollectionSpec.LoadAndAssign.
 type KmeshSendmsgCompatMaps struct {
-	KmLogEvent *ebpf.Map `ebpf:"km_log_event"`
-	KmOrigDst  *ebpf.Map `ebpf:"km_orig_dst"`
-	KmTmpbuf   *ebpf.Map `ebpf:"km_tmpbuf"`
+	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
+	KmManage      *ebpf.Map `ebpf:"km_manage"`
+	KmSockstorage *ebpf.Map `ebpf:"km_sockstorage"`
+	KmTmpbuf      *ebpf.Map `ebpf:"km_tmpbuf"`
+	KmeshMap1600  *ebpf.Map `ebpf:"kmesh_map1600"`
+	KmeshMap192   *ebpf.Map `ebpf:"kmesh_map192"`
+	KmeshMap296   *ebpf.Map `ebpf:"kmesh_map296"`
+	KmeshMap64    *ebpf.Map `ebpf:"kmesh_map64"`
 }
 
 func (m *KmeshSendmsgCompatMaps) Close() error {
 	return _KmeshSendmsgCompatClose(
 		m.KmLogEvent,
-		m.KmOrigDst,
+		m.KmManage,
+		m.KmSockstorage,
 		m.KmTmpbuf,
+		m.KmeshMap1600,
+		m.KmeshMap192,
+		m.KmeshMap296,
+		m.KmeshMap64,
 	)
 }
 
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkload_bpfeb.go b/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkload_bpfeb.go
index addd0170..94189548 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkload_bpfeb.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkload_bpfeb.go
@@ -45,9 +45,15 @@ type KmeshSockopsWorkloadOperationUsageKey struct {
 
 type KmeshSockopsWorkloadSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        KmeshSockopsWorkloadBpfSockTuple
+	_              [4]byte
 }
 
 // LoadKmeshSockopsWorkload returns the embedded CollectionSpec for KmeshSockopsWorkload.
@@ -106,7 +112,6 @@ type KmeshSockopsWorkloadMapSpecs struct {
 	KmFrontend    *ebpf.MapSpec `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
 	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.MapSpec `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.MapSpec `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.MapSpec `ebpf:"km_perf_map"`
 	KmService     *ebpf.MapSpec `ebpf:"km_service"`
@@ -158,7 +163,6 @@ type KmeshSockopsWorkloadMaps struct {
 	KmFrontend    *ebpf.Map `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
 	KmManage      *ebpf.Map `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.Map `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.Map `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.Map `ebpf:"km_perf_map"`
 	KmService     *ebpf.Map `ebpf:"km_service"`
@@ -182,7 +186,6 @@ func (m *KmeshSockopsWorkloadMaps) Close() error {
 		m.KmFrontend,
 		m.KmLogEvent,
 		m.KmManage,
-		m.KmOrigDst,
 		m.KmPerfInfo,
 		m.KmPerfMap,
 		m.KmService,
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkload_bpfel.go b/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkload_bpfel.go
index 75afab78..e5305783 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkload_bpfel.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkload_bpfel.go
@@ -45,9 +45,15 @@ type KmeshSockopsWorkloadOperationUsageKey struct {
 
 type KmeshSockopsWorkloadSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        KmeshSockopsWorkloadBpfSockTuple
+	_              [4]byte
 }
 
 // LoadKmeshSockopsWorkload returns the embedded CollectionSpec for KmeshSockopsWorkload.
@@ -106,7 +112,6 @@ type KmeshSockopsWorkloadMapSpecs struct {
 	KmFrontend    *ebpf.MapSpec `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
 	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.MapSpec `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.MapSpec `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.MapSpec `ebpf:"km_perf_map"`
 	KmService     *ebpf.MapSpec `ebpf:"km_service"`
@@ -158,7 +163,6 @@ type KmeshSockopsWorkloadMaps struct {
 	KmFrontend    *ebpf.Map `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
 	KmManage      *ebpf.Map `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.Map `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.Map `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.Map `ebpf:"km_perf_map"`
 	KmService     *ebpf.Map `ebpf:"km_service"`
@@ -182,7 +186,6 @@ func (m *KmeshSockopsWorkloadMaps) Close() error {
 		m.KmFrontend,
 		m.KmLogEvent,
 		m.KmManage,
-		m.KmOrigDst,
 		m.KmPerfInfo,
 		m.KmPerfMap,
 		m.KmService,
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkloadcompat_bpfeb.go b/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkloadcompat_bpfeb.go
index f1bfa877..1279a78c 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkloadcompat_bpfeb.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkloadcompat_bpfeb.go
@@ -45,9 +45,15 @@ type KmeshSockopsWorkloadCompatOperationUsageKey struct {
 
 type KmeshSockopsWorkloadCompatSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        KmeshSockopsWorkloadCompatBpfSockTuple
+	_              [4]byte
 }
 
 // LoadKmeshSockopsWorkloadCompat returns the embedded CollectionSpec for KmeshSockopsWorkloadCompat.
@@ -106,7 +112,6 @@ type KmeshSockopsWorkloadCompatMapSpecs struct {
 	KmFrontend    *ebpf.MapSpec `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
 	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.MapSpec `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.MapSpec `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.MapSpec `ebpf:"km_perf_map"`
 	KmService     *ebpf.MapSpec `ebpf:"km_service"`
@@ -158,7 +163,6 @@ type KmeshSockopsWorkloadCompatMaps struct {
 	KmFrontend    *ebpf.Map `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
 	KmManage      *ebpf.Map `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.Map `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.Map `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.Map `ebpf:"km_perf_map"`
 	KmService     *ebpf.Map `ebpf:"km_service"`
@@ -182,7 +186,6 @@ func (m *KmeshSockopsWorkloadCompatMaps) Close() error {
 		m.KmFrontend,
 		m.KmLogEvent,
 		m.KmManage,
-		m.KmOrigDst,
 		m.KmPerfInfo,
 		m.KmPerfMap,
 		m.KmService,
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkloadcompat_bpfel.go b/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkloadcompat_bpfel.go
index 9729ab03..78760435 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkloadcompat_bpfel.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshsockopsworkloadcompat_bpfel.go
@@ -45,9 +45,15 @@ type KmeshSockopsWorkloadCompatOperationUsageKey struct {
 
 type KmeshSockopsWorkloadCompatSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        KmeshSockopsWorkloadCompatBpfSockTuple
+	_              [4]byte
 }
 
 // LoadKmeshSockopsWorkloadCompat returns the embedded CollectionSpec for KmeshSockopsWorkloadCompat.
@@ -106,7 +112,6 @@ type KmeshSockopsWorkloadCompatMapSpecs struct {
 	KmFrontend    *ebpf.MapSpec `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.MapSpec `ebpf:"km_log_event"`
 	KmManage      *ebpf.MapSpec `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.MapSpec `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.MapSpec `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.MapSpec `ebpf:"km_perf_map"`
 	KmService     *ebpf.MapSpec `ebpf:"km_service"`
@@ -158,7 +163,6 @@ type KmeshSockopsWorkloadCompatMaps struct {
 	KmFrontend    *ebpf.Map `ebpf:"km_frontend"`
 	KmLogEvent    *ebpf.Map `ebpf:"km_log_event"`
 	KmManage      *ebpf.Map `ebpf:"km_manage"`
-	KmOrigDst     *ebpf.Map `ebpf:"km_orig_dst"`
 	KmPerfInfo    *ebpf.Map `ebpf:"km_perf_info"`
 	KmPerfMap     *ebpf.Map `ebpf:"km_perf_map"`
 	KmService     *ebpf.Map `ebpf:"km_service"`
@@ -182,7 +186,6 @@ func (m *KmeshSockopsWorkloadCompatMaps) Close() error {
 		m.KmFrontend,
 		m.KmLogEvent,
 		m.KmManage,
-		m.KmOrigDst,
 		m.KmPerfInfo,
 		m.KmPerfMap,
 		m.KmService,
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshxdpauth_bpfeb.go b/bpf/kmesh/bpf2go/dualengine/kmeshxdpauth_bpfeb.go
index fe2491c8..0cd77749 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshxdpauth_bpfeb.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshxdpauth_bpfeb.go
@@ -31,9 +31,15 @@ type KmeshXDPAuthManagerKey struct {
 
 type KmeshXDPAuthSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        KmeshXDPAuthBpfSockTuple
+	_              [4]byte
 }
 
 // LoadKmeshXDPAuth returns the embedded CollectionSpec for KmeshXDPAuth.
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshxdpauth_bpfel.go b/bpf/kmesh/bpf2go/dualengine/kmeshxdpauth_bpfel.go
index c6fcae42..e1fe7cf5 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshxdpauth_bpfel.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshxdpauth_bpfel.go
@@ -31,9 +31,15 @@ type KmeshXDPAuthManagerKey struct {
 
 type KmeshXDPAuthSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        KmeshXDPAuthBpfSockTuple
+	_              [4]byte
 }
 
 // LoadKmeshXDPAuth returns the embedded CollectionSpec for KmeshXDPAuth.
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshxdpauthcompat_bpfeb.go b/bpf/kmesh/bpf2go/dualengine/kmeshxdpauthcompat_bpfeb.go
index ee1f32ca..78dbdb4d 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshxdpauthcompat_bpfeb.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshxdpauthcompat_bpfeb.go
@@ -31,9 +31,15 @@ type KmeshXDPAuthCompatManagerKey struct {
 
 type KmeshXDPAuthCompatSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        KmeshXDPAuthCompatBpfSockTuple
+	_              [4]byte
 }
 
 // LoadKmeshXDPAuthCompat returns the embedded CollectionSpec for KmeshXDPAuthCompat.
diff --git a/bpf/kmesh/bpf2go/dualengine/kmeshxdpauthcompat_bpfel.go b/bpf/kmesh/bpf2go/dualengine/kmeshxdpauthcompat_bpfel.go
index c4950004..ac06381a 100644
--- a/bpf/kmesh/bpf2go/dualengine/kmeshxdpauthcompat_bpfel.go
+++ b/bpf/kmesh/bpf2go/dualengine/kmeshxdpauthcompat_bpfel.go
@@ -31,9 +31,15 @@ type KmeshXDPAuthCompatManagerKey struct {
 
 type KmeshXDPAuthCompatSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        KmeshXDPAuthCompatBpfSockTuple
+	_              [4]byte
 }
 
 // LoadKmeshXDPAuthCompat returns the embedded CollectionSpec for KmeshXDPAuthCompat.
diff --git a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsock_bpfeb.go b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsock_bpfeb.go
index 663d99c9..dcd6f986 100644
--- a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsock_bpfeb.go
+++ b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsock_bpfeb.go
@@ -40,9 +40,23 @@ type KmeshCgroupSockRatelimitValue struct {
 
 type KmeshCgroupSockSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        struct {
+		Ipv4 struct {
+			Saddr uint32
+			Daddr uint32
+			Sport uint16
+			Dport uint16
+		}
+		_ [24]byte
+	}
+	_ [4]byte
 }
 
 // LoadKmeshCgroupSock returns the embedded CollectionSpec for KmeshCgroupSock.
diff --git a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsock_bpfel.go b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsock_bpfel.go
index 8f81872a..1d210dd3 100644
--- a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsock_bpfel.go
+++ b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsock_bpfel.go
@@ -40,9 +40,23 @@ type KmeshCgroupSockRatelimitValue struct {
 
 type KmeshCgroupSockSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        struct {
+		Ipv4 struct {
+			Saddr uint32
+			Daddr uint32
+			Sport uint16
+			Dport uint16
+		}
+		_ [24]byte
+	}
+	_ [4]byte
 }
 
 // LoadKmeshCgroupSock returns the embedded CollectionSpec for KmeshCgroupSock.
diff --git a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsockcompat_bpfeb.go b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsockcompat_bpfeb.go
index 9b96b542..3b06eaae 100644
--- a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsockcompat_bpfeb.go
+++ b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsockcompat_bpfeb.go
@@ -40,9 +40,23 @@ type KmeshCgroupSockCompatRatelimitValue struct {
 
 type KmeshCgroupSockCompatSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        struct {
+		Ipv4 struct {
+			Saddr uint32
+			Daddr uint32
+			Sport uint16
+			Dport uint16
+		}
+		_ [24]byte
+	}
+	_ [4]byte
 }
 
 // LoadKmeshCgroupSockCompat returns the embedded CollectionSpec for KmeshCgroupSockCompat.
diff --git a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsockcompat_bpfel.go b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsockcompat_bpfel.go
index 5b52df92..79bf64d2 100644
--- a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsockcompat_bpfel.go
+++ b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshcgroupsockcompat_bpfel.go
@@ -40,9 +40,23 @@ type KmeshCgroupSockCompatRatelimitValue struct {
 
 type KmeshCgroupSockCompatSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        struct {
+		Ipv4 struct {
+			Saddr uint32
+			Daddr uint32
+			Sport uint16
+			Dport uint16
+		}
+		_ [24]byte
+	}
+	_ [4]byte
 }
 
 // LoadKmeshCgroupSockCompat returns the embedded CollectionSpec for KmeshCgroupSockCompat.
diff --git a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockops_bpfeb.go b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockops_bpfeb.go
index 523ecac8..5dcd8fe2 100644
--- a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockops_bpfeb.go
+++ b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockops_bpfeb.go
@@ -23,9 +23,23 @@ type KmeshSockopsManagerKey struct {
 
 type KmeshSockopsSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        struct {
+		Ipv4 struct {
+			Saddr uint32
+			Daddr uint32
+			Sport uint16
+			Dport uint16
+		}
+		_ [24]byte
+	}
+	_ [4]byte
 }
 
 // LoadKmeshSockops returns the embedded CollectionSpec for KmeshSockops.
diff --git a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockops_bpfel.go b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockops_bpfel.go
index 61b22b6e..c027a1fa 100644
--- a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockops_bpfel.go
+++ b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockops_bpfel.go
@@ -23,9 +23,23 @@ type KmeshSockopsManagerKey struct {
 
 type KmeshSockopsSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        struct {
+		Ipv4 struct {
+			Saddr uint32
+			Daddr uint32
+			Sport uint16
+			Dport uint16
+		}
+		_ [24]byte
+	}
+	_ [4]byte
 }
 
 // LoadKmeshSockops returns the embedded CollectionSpec for KmeshSockops.
diff --git a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockopscompat_bpfeb.go b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockopscompat_bpfeb.go
index 08981a5e..62a13d0c 100644
--- a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockopscompat_bpfeb.go
+++ b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockopscompat_bpfeb.go
@@ -23,9 +23,23 @@ type KmeshSockopsCompatManagerKey struct {
 
 type KmeshSockopsCompatSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        struct {
+		Ipv4 struct {
+			Saddr uint32
+			Daddr uint32
+			Sport uint16
+			Dport uint16
+		}
+		_ [24]byte
+	}
+	_ [4]byte
 }
 
 // LoadKmeshSockopsCompat returns the embedded CollectionSpec for KmeshSockopsCompat.
diff --git a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockopscompat_bpfel.go b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockopscompat_bpfel.go
index 1acbb6d5..58e2c2a3 100644
--- a/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockopscompat_bpfel.go
+++ b/bpf/kmesh/bpf2go/kernelnative/normal/kmeshsockopscompat_bpfel.go
@@ -23,9 +23,23 @@ type KmeshSockopsCompatManagerKey struct {
 
 type KmeshSockopsCompatSockStorageData struct {
 	ConnectNs      uint64
+	LastReportNs   uint64
 	Direction      uint8
 	ConnectSuccess uint8
-	_              [6]byte
+	ViaWaypoint    bool
+	HasEncoded     bool
+	HasSetIp       bool
+	_              [3]byte
+	SkTuple        struct {
+		Ipv4 struct {
+			Saddr uint32
+			Daddr uint32
+			Sport uint16
+			Dport uint16
+		}
+		_ [24]byte
+	}
+	_ [4]byte
 }
 
 // LoadKmeshSockopsCompat returns the embedded CollectionSpec for KmeshSockopsCompat.
diff --git a/bpf/kmesh/probes/performance_probe.h b/bpf/kmesh/probes/performance_probe.h
index daafdf30..a8dfa9db 100644
--- a/bpf/kmesh/probes/performance_probe.h
+++ b/bpf/kmesh/probes/performance_probe.h
@@ -6,8 +6,7 @@
 
 #define PERF_MONITOR 0
 
-#include "bpf_common.h"
-#include "config.h"
+#include "workload.h"
 
 enum {
     SOCK_TRAFFIC_CONTROL = 1,
diff --git a/bpf/kmesh/probes/probe.h b/bpf/kmesh/probes/probe.h
index 1b1c46ec..86e4c6d5 100644
--- a/bpf/kmesh/probes/probe.h
+++ b/bpf/kmesh/probes/probe.h
@@ -7,6 +7,8 @@
 #include "tcp_probe.h"
 #include "performance_probe.h"
 
+#define LONG_CONN_THRESHOLD_TIME (5 * 1000000000ULL) // 5s
+
 volatile __u32 enable_monitoring = 0;
 
 static inline bool is_monitoring_enable()
@@ -57,7 +59,6 @@ static inline void observe_on_connect_established(struct bpf_sock *sk, __u8 dire
         storage->connect_ns = bpf_ktime_get_ns();
     storage->direction = direction;
     storage->connect_success = true;
-
     tcp_report(sk, tcp_sock, storage, BPF_TCP_ESTABLISHED);
 }
 
@@ -82,4 +83,24 @@ static inline void observe_on_close(struct bpf_sock *sk)
 
     tcp_report(sk, tcp_sock, storage, BPF_TCP_CLOSE);
 }
+
+static inline void observe_on_data(struct bpf_sock *sk)
+{
+    struct bpf_tcp_sock *tcp_sock = NULL;
+    struct sock_storage_data *storage = NULL;
+    if (!sk)
+        return;
+    tcp_sock = bpf_tcp_sock(sk);
+    if (!tcp_sock)
+        return;
+
+    storage = bpf_sk_storage_get(&map_of_sock_storage, sk, 0, 0);
+    if (!storage) {
+        return;
+    }
+    __u64 now = bpf_ktime_get_ns();
+    if ((storage->last_report_ns != 0) && (now - storage->last_report_ns > LONG_CONN_THRESHOLD_TIME)) {
+        tcp_report(sk, tcp_sock, storage, BPF_TCP_ESTABLISHED);
+    }
+}
 #endif
\ No newline at end of file
diff --git a/bpf/kmesh/probes/tcp_probe.h b/bpf/kmesh/probes/tcp_probe.h
index 831604f0..77939b78 100644
--- a/bpf/kmesh/probes/tcp_probe.h
+++ b/bpf/kmesh/probes/tcp_probe.h
@@ -35,18 +35,19 @@ struct tcp_probe_info {
     __u32 type;
     struct bpf_sock_tuple tuple;
     struct orig_dst_info orig_dst;
-    __u32 sent_bytes;
-    __u32 received_bytes;
+    __u32 sent_bytes;     /* Total send bytes from start to last_report_ns */
+    __u32 received_bytes; /* Total recv bytes from start to last_report_ns */
     __u32 conn_success;
     __u32 direction;
     __u32 state;    /* tcp state */
     __u64 duration; // ns
-    __u64 close_ns;
+    __u64 start_ns;
+    __u64 last_report_ns; /*timestamp of the last metrics report*/
     __u32 protocol;
-    __u32 srtt_us; /* smoothed round trip time << 3 in usecs */
-    __u32 rtt_min;
-    __u32 total_retrans; /* Total retransmits for entire connection */
-    __u32 lost_out;      /* Lost packets			*/
+    __u32 srtt_us;       /* smoothed round trip time << 3 in usecs until last_report_ns */
+    __u32 rtt_min;       /* min round trip time in usecs until last_report_ns */
+    __u32 total_retrans; /* Total retransmits from start to last_report_ns */
+    __u32 lost_out;      /* Lost packets from start to last_report_ns	*/
 };
 
 struct {
@@ -106,25 +107,23 @@ static inline void get_tcp_probe_info(struct bpf_tcp_sock *tcp_sock, struct tcp_
     return;
 }
 
-// construct_orig_dst_info try to read the dst_info from map_of_orig_dst first
+// construct_orig_dst_info try to read the dst_info from map_of_sock_storage first
 // if not found, use the tuple info for orig_dst
 static inline void construct_orig_dst_info(struct bpf_sock *sk, struct tcp_probe_info *info)
 {
-    __u64 *current_sk = (__u64 *)sk;
-    struct bpf_sock_tuple *dst;
-    dst = bpf_map_lookup_elem(&map_of_orig_dst, &current_sk);
-
-    // when dst not found, metric controller will read orig dst from actual dst
-    if (!dst) {
+    struct sock_storage_data *storage = NULL;
+    storage = bpf_sk_storage_get(&map_of_sock_storage, sk, 0, 0);
+    if (!storage) {
+        BPF_LOG(ERR, PROBE, "on close: bpf_sk_storage_get failed\n");
         return;
     }
 
     if (sk->family == AF_INET) {
-        info->orig_dst.ipv4.addr = dst->ipv4.daddr;
-        info->orig_dst.ipv4.port = bpf_ntohs(dst->ipv4.dport);
+        info->orig_dst.ipv4.addr = storage->sk_tuple.ipv4.daddr;
+        info->orig_dst.ipv4.port = bpf_ntohs(storage->sk_tuple.ipv4.dport);
     } else {
-        bpf_memcpy(info->orig_dst.ipv6.addr, dst->ipv6.daddr, IPV6_ADDR_LEN);
-        info->orig_dst.ipv6.port = bpf_ntohs(dst->ipv6.dport);
+        bpf_memcpy(info->orig_dst.ipv6.addr, storage->sk_tuple.ipv6.daddr, IPV6_ADDR_LEN);
+        info->orig_dst.ipv6.port = bpf_ntohs(storage->sk_tuple.ipv6.dport);
     }
 
     if (is_ipv4_mapped_addr(info->orig_dst.ipv6.addr)) {
@@ -147,12 +146,9 @@ tcp_report(struct bpf_sock *sk, struct bpf_tcp_sock *tcp_sock, struct sock_stora
     }
 
     construct_tuple(sk, &info->tuple, storage->direction);
+    info->start_ns = storage->connect_ns;
     info->state = state;
     info->direction = storage->direction;
-    if (state == BPF_TCP_CLOSE) {
-        info->close_ns = bpf_ktime_get_ns();
-        info->duration = info->close_ns - storage->connect_ns;
-    }
     info->conn_success = storage->connect_success;
     get_tcp_probe_info(tcp_sock, info);
     (*info).type = (sk->family == AF_INET) ? IPV4 : IPV6;
@@ -161,6 +157,9 @@ tcp_report(struct bpf_sock *sk, struct bpf_tcp_sock *tcp_sock, struct sock_stora
     }
 
     construct_orig_dst_info(sk, info);
+    info->last_report_ns = bpf_ktime_get_ns();
+    info->duration = info->last_report_ns - storage->connect_ns;
+    storage->last_report_ns = info->last_report_ns;
     bpf_ringbuf_submit(info, 0);
 }
 
diff --git a/bpf/kmesh/workload/cgroup_skb.c b/bpf/kmesh/workload/cgroup_skb.c
new file mode 100644
index 00000000..7a57fd31
--- /dev/null
+++ b/bpf/kmesh/workload/cgroup_skb.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright Authors of Kmesh */
+
+#include <linux/bpf.h>
+#include <sys/socket.h>
+#include <bpf/bpf_endian.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_log.h"
+#include "encoder.h"
+#include "bpf_common.h"
+#include "probe.h"
+
+SEC("cgroup_skb/ingress")
+int cgroup_skb_ingress_prog(struct __sk_buff *skb)
+{
+    if (skb->family != AF_INET && skb->family != AF_INET6)
+        return SK_PASS;
+
+    struct bpf_sock *sk = skb->sk;
+    if (!sk)
+        return SK_PASS;
+
+    if (!is_managed_by_kmesh_skb(skb))
+        return SK_PASS;
+    observe_on_data(sk);
+    return SK_PASS;
+}
+
+SEC("cgroup_skb/egress")
+int cgroup_skb_egress_prog(struct __sk_buff *skb)
+{
+    if (skb->family != AF_INET && skb->family != AF_INET6)
+        return SK_PASS;
+
+    struct bpf_sock *sk = skb->sk;
+    if (!sk)
+        return SK_PASS;
+
+    if (!is_managed_by_kmesh_skb(skb))
+        return SK_PASS;
+    observe_on_data(sk);
+    return SK_PASS;
+}
+
+char _license[] SEC("license") = "Dual BSD/GPL";
+int _version SEC("version") = 1;
\ No newline at end of file
diff --git a/bpf/kmesh/workload/cgroup_sock.c b/bpf/kmesh/workload/cgroup_sock.c
index d94faf75..862cff7a 100644
--- a/bpf/kmesh/workload/cgroup_sock.c
+++ b/bpf/kmesh/workload/cgroup_sock.c
@@ -58,33 +58,30 @@ static inline int sock_traffic_control(struct kmesh_context *kmesh_ctx)
 
 static inline int set_original_dst_info(struct kmesh_context *kmesh_ctx)
 {
-    int ret;
-    struct bpf_sock_tuple sk_tuple = {0};
+    struct bpf_sock *sk = (struct bpf_sock *)kmesh_ctx->ctx->sk;
     ctx_buff_t *ctx = (ctx_buff_t *)kmesh_ctx->ctx;
-    __u64 *sk = (__u64 *)ctx->sk;
 
-    if (kmesh_ctx->via_waypoint) {
-        // since this field is never used, we use it
-        // to indicate whether the request will be handled by waypoint
-        sk_tuple.ipv4.saddr = 1;
+    struct sock_storage_data *storage = NULL;
+    storage = bpf_sk_storage_get(&map_of_sock_storage, sk, 0, BPF_LOCAL_STORAGE_GET_F_CREATE);
+    if (!storage) {
+        BPF_LOG(ERR, KMESH, "failed to get storage from map_of_sock_storage");
+        return 0;
     }
 
-    if (ctx->family == AF_INET) {
-        sk_tuple.ipv4.daddr = kmesh_ctx->orig_dst_addr.ip4;
-        sk_tuple.ipv4.dport = ctx->user_port;
-    } else if (ctx->family == AF_INET6) {
-        bpf_memcpy(sk_tuple.ipv6.daddr, kmesh_ctx->orig_dst_addr.ip6, IPV6_ADDR_LEN);
-        sk_tuple.ipv6.dport = ctx->user_port;
+    if (kmesh_ctx->via_waypoint) {
+        storage->via_waypoint = true;
     }
 
-    ret = bpf_map_update_elem(&map_of_orig_dst, &(sk), &sk_tuple, BPF_NOEXIST);
-    if (ret) {
-        // only record the first dst info for each socket
-        if (ret == -EEXIST)
-            return 0;
-        BPF_LOG(ERR, BACKEND, "record original dst address failed: %d\n", ret);
-        return ret;
+    if (ctx->family == AF_INET && !storage->has_set_ip) {
+        storage->sk_tuple.ipv4.daddr = kmesh_ctx->orig_dst_addr.ip4;
+        storage->sk_tuple.ipv4.dport = ctx->user_port;
+        storage->has_set_ip = true;
+    } else if (ctx->family == AF_INET6 && !storage->has_set_ip) {
+        bpf_memcpy(storage->sk_tuple.ipv6.daddr, kmesh_ctx->orig_dst_addr.ip6, IPV6_ADDR_LEN);
+        storage->sk_tuple.ipv6.dport = ctx->user_port;
+        storage->has_set_ip = true;
     }
+
     return 0;
 }
 
diff --git a/bpf/kmesh/workload/include/authz.h b/bpf/kmesh/workload/include/authz.h
index 97e8b496..66f89d7d 100644
--- a/bpf/kmesh/workload/include/authz.h
+++ b/bpf/kmesh/workload/include/authz.h
@@ -658,17 +658,27 @@ int policy_check(struct xdp_md *ctx)
             BPF_LOG(
                 DEBUG,
                 AUTH,
-                "src ip: %u, dst ip %u, dst port: %u\n",
+                "src ip: %s, src port:%u",
                 ip2str(&tuple_key.ipv4.saddr, true),
+                bpf_ntohs(tuple_key.ipv4.sport));
+            BPF_LOG(
+                DEBUG,
+                AUTH,
+                "dst ip: %s, dst port:%u\n",
                 ip2str(&tuple_key.ipv4.daddr, true),
                 bpf_ntohs(tuple_key.ipv4.dport));
         } else {
             BPF_LOG(
                 DEBUG,
                 AUTH,
-                "src ip: %u, dst ip %u, dst port: %u\n",
-                ip2str(&tuple_key.ipv6.saddr[0], false),
-                ip2str(&tuple_key.ipv6.daddr[0], false),
+                "src ip: %s, src port:%u",
+                ip2str(tuple_key.ipv6.saddr, false),
+                bpf_ntohs(tuple_key.ipv6.sport));
+            BPF_LOG(
+                DEBUG,
+                AUTH,
+                "dst ip: %s, dst port:%u\n",
+                ip2str(tuple_key.ipv6.daddr, false),
                 bpf_ntohs(tuple_key.ipv6.dport));
         }
         if (bpf_map_delete_elem(&kmesh_tc_args, &tuple_key) != 0) {
diff --git a/bpf/kmesh/workload/include/backend.h b/bpf/kmesh/workload/include/backend.h
index 2c22b8f2..7d3759c2 100644
--- a/bpf/kmesh/workload/include/backend.h
+++ b/bpf/kmesh/workload/include/backend.h
@@ -5,7 +5,6 @@
 #define __ROUTE_BACKEND_H__
 
 #include "workload_common.h"
-#include "encoder.h"
 #include "tail_call.h"
 
 static inline backend_value *map_lookup_backend(const backend_key *key)
diff --git a/bpf/kmesh/workload/include/config.h b/bpf/kmesh/workload/include/config.h
index 5307eb1e..d997656f 100644
--- a/bpf/kmesh/workload/include/config.h
+++ b/bpf/kmesh/workload/include/config.h
@@ -23,7 +23,6 @@
 #define map_of_auth_req      km_auth_req
 #define map_of_tcp_probe     km_tcp_probe
 #define map_of_authz_policy  km_authz_policy
-#define map_of_orig_dst      km_orig_dst
 #define map_of_cgr_tail_call km_cgr_tailcall
 #define map_of_xdp_tailcall  km_xdp_tailcall
 #define map_of_kmesh_socket  km_socket
diff --git a/bpf/kmesh/workload/include/encoder.h b/bpf/kmesh/workload/include/encoder.h
deleted file mode 100644
index 3c3481fd..00000000
--- a/bpf/kmesh/workload/include/encoder.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
-/* Copyright Authors of Kmesh */
-
-#ifndef __ENCODER_H__
-#define __ENCODER_H__
-
-#include "config.h"
-#include "common.h"
-
-struct {
-    __uint(type, BPF_MAP_TYPE_HASH);
-    __type(key, __u64);
-    __type(value, struct bpf_sock_tuple);
-    __uint(max_entries, MAP_SIZE_OF_DSTINFO);
-    __uint(map_flags, BPF_F_NO_PREALLOC);
-} map_of_orig_dst SEC(".maps");
-
-#endif /*__ENCODER_H__*/
\ No newline at end of file
diff --git a/bpf/kmesh/workload/sendmsg.c b/bpf/kmesh/workload/sendmsg.c
index edaab9bf..f1847f2d 100644
--- a/bpf/kmesh/workload/sendmsg.c
+++ b/bpf/kmesh/workload/sendmsg.c
@@ -6,7 +6,7 @@
 #include <bpf/bpf_endian.h>
 #include <bpf/bpf_helpers.h>
 #include "bpf_log.h"
-#include "encoder.h"
+#include "bpf_common.h"
 
 /*
  * sk msg is used to encode metadata into the payload when the client sends
@@ -81,30 +81,23 @@ static inline int check_overflow(struct sk_msg_md *msg, __u8 *begin, __u32 lengt
 
 static inline int get_origin_dst(struct sk_msg_md *msg, struct ip_addr *dst_ip, __u16 *dst_port)
 {
-    __u64 *current_sk = (__u64 *)msg->sk;
-    struct bpf_sock_tuple *dst;
+    struct bpf_sock *sk = (struct bpf_sock *)msg->sk;
 
-    dst = bpf_map_lookup_elem(&map_of_orig_dst, &current_sk);
-
-    // !dst->ipv4.saddr indicates this is not from waypoint
-    // dst->ipv4.sport indicates this connection is already encoded
-    // for circumstances above, we just return
-    if (!dst || !dst->ipv4.saddr || dst->ipv4.sport)
+    struct sock_storage_data *storage = NULL;
+    storage = bpf_sk_storage_get(&map_of_sock_storage, sk, 0, 0);
+    if (!storage || !storage->via_waypoint || storage->has_encoded) {
         return -ENOENT;
+    }
 
     if (msg->family == AF_INET) {
-        dst_ip->ip4 = dst->ipv4.daddr;
-        *dst_port = dst->ipv4.dport;
+        dst_ip->ip4 = storage->sk_tuple.ipv4.daddr;
+        *dst_port = storage->sk_tuple.ipv4.dport;
     } else {
-        bpf_memcpy(dst_ip->ip6, dst->ipv6.daddr, IPV6_ADDR_LEN);
-        *dst_port = dst->ipv6.dport;
+        bpf_memcpy(dst_ip->ip6, storage->sk_tuple.ipv6.daddr, IPV6_ADDR_LEN);
+        *dst_port = storage->sk_tuple.ipv6.dport;
     }
 
-    // since this field is never used, we use it to indicate whether the connection is already encoded
-    dst->ipv4.sport = 1;
-    int ret = bpf_map_update_elem(&map_of_orig_dst, &current_sk, dst, BPF_EXIST);
-    if (ret)
-        BPF_LOG(ERR, SENDMSG, "update dst info failed, ret: %d\n", ret);
+    storage->has_encoded = true;
 
     return 0;
 }
diff --git a/bpf/kmesh/workload/sockops.c b/bpf/kmesh/workload/sockops.c
index 2745c394..bceb7f42 100644
--- a/bpf/kmesh/workload/sockops.c
+++ b/bpf/kmesh/workload/sockops.c
@@ -9,7 +9,6 @@
 #include "bpf_log.h"
 #include "workload.h"
 #include "config.h"
-#include "encoder.h"
 #include "bpf_common.h"
 #include "probe.h"
 #include "config.h"
@@ -119,14 +118,6 @@ static inline void clean_auth_map(struct bpf_sock_ops *skops)
         BPF_LOG(ERR, SOCKOPS, "map_of_auth_result bpf_map_delete_elem failed, ret: %d", ret);
 }
 
-static inline void clean_dstinfo_map(struct bpf_sock_ops *skops)
-{
-    __u64 *key = (__u64 *)skops->sk;
-    int ret = bpf_map_delete_elem(&map_of_orig_dst, &key);
-    if (ret && ret != -ENOENT)
-        BPF_LOG(ERR, SOCKOPS, "bpf map delete destination info failed, ret: %d", ret);
-}
-
 // insert an IP tuple into the ringbuf
 static inline void auth_ip_tuple(struct bpf_sock_ops *skops)
 {
@@ -164,6 +155,7 @@ int sockops_prog(struct bpf_sock_ops *skops)
 {
     if (skops->family != AF_INET && skops->family != AF_INET6)
         return 0;
+
     switch (skops->op) {
     case BPF_SOCK_OPS_TCP_CONNECT_CB:
         skops_handle_kmesh_managed_process(skops);
@@ -174,10 +166,19 @@ int sockops_prog(struct bpf_sock_ops *skops)
         observe_on_connect_established(skops->sk, OUTBOUND);
         if (bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_STATE_CB_FLAG) != 0)
             BPF_LOG(ERR, SOCKOPS, "set sockops cb failed!\n");
-        __u64 *current_sk = (__u64 *)skops->sk;
-        struct bpf_sock_tuple *dst = bpf_map_lookup_elem(&map_of_orig_dst, &current_sk);
-        if (dst != NULL)
+        struct bpf_sock *sk = (struct bpf_sock *)skops->sk;
+        if (!sk) {
+            break;
+        }
+        struct sock_storage_data *storage = NULL;
+        storage = bpf_sk_storage_get(&map_of_sock_storage, sk, 0, 0);
+        if (!storage) {
+            break;
+        }
+
+        if (storage->via_waypoint) {
             enable_encoding_metadata(skops);
+        }
         break;
     case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
         if (!is_managed_by_kmesh(skops) || skip_specific_probe(skops))
@@ -191,7 +192,6 @@ int sockops_prog(struct bpf_sock_ops *skops)
         if (skops->args[1] == BPF_TCP_CLOSE) {
             observe_on_close(skops->sk);
             clean_auth_map(skops);
-            clean_dstinfo_map(skops);
         }
         break;
     default:
diff --git a/docs/proposal/pics/tcp_long_conn_design.png b/docs/proposal/pics/tcp_long_conn_design.png
new file mode 100644
index 00000000..069f0298
Binary files /dev/null and b/docs/proposal/pics/tcp_long_conn_design.png differ
diff --git a/docs/proposal/tcp_long_connection_metrics.md b/docs/proposal/tcp_long_connection_metrics.md
index 34c166d9..19d58aec 100644
--- a/docs/proposal/tcp_long_connection_metrics.md
+++ b/docs/proposal/tcp_long_connection_metrics.md
@@ -91,7 +91,7 @@ The "Design Details" section below is for the real
 nitty-gritty.
 -->
 
-TCP connection information will be collected using eBPF sockops, sk_msg and kprobes hooks, and stored in ebpf hash maps using socket cookie as a unique key for hashmap. RingBuffer map is used for sending connection info periodically to userspace.
+TCP connection information will be collected using eBPF cgroup_skb hook. RingBuffer map is used for sending connection info periodically to userspace.
 
 
 ### Design Details
@@ -105,185 +105,69 @@ proposal will be implemented, this is the place to discuss them.
 
 #### Collecting Metrics
 
-Decelearing ebpf hash map in probe.h to store information about tcp_connections.
+Decelearing ebpf cgroup_skb hooks, which will trigger when the traffic passes through the cgroup socket.
 
 ```
-// Ebpf map to store active tcp connections
-struct {
-    __uint(type, BPF_MAP_TYPE_HASH);
-    __type(key, __u64); // use sock_cookie as key
-    __type(value, struct tcp_probe_info);
-    __uint(max_entries, MAP_SIZE_OF_TCP_CONNS);
-    __uint(map_flags, BPF_F_NO_PREALLOC);
-} map_of_tcp_conns SEC(".maps");
-
-```
-Sockpos ebpf hook is triggered at various socket events, we will use this hook to store and refresh connection information at the time of connection established, connection state change, retransmits (also trigger in packet losss).
-Updating workload/sockops.c
-
-```
-SEC("sockops_active")
-int sockops_active_prog(struct bpf_sock_ops *skops)
+SEC("cgroup_skb/ingress")
+int cgroup_skb_ingress_prog(struct __sk_buff *skb)
 {
-    __u64 sock_cookie = bpf_get_socket_cookie(skops);
-
-    if (skops->family != AF_INET && skops->family != AF_INET6)
-        return 0;
-
-    switch (skops->op) {
-    case BPF_SOCK_OPS_TCP_CONNECT_CB:
-        skops_handle_kmesh_managed_process(skops);
-        break;
-
-    case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
-        if (!is_managed_by_kmesh(skops))
-            break;
-        observe_on_connect_established(skops->sk, sock_cookie, OUTBOUND);
-        if (bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_STATE_CB_FLAG) != 0
-            || bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_RETRANS_CB_FLAG) != 0
-            || bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_RTT_CB_FLAG) != 0) {
-            BPF_LOG(ERR, SOCKOPS, "set sockops cb failed!\n");
-        }
-        __u64 *current_sk = (__u64 *)skops->sk;
-        struct bpf_sock_tuple *dst = bpf_map_lookup_elem(&map_of_orig_dst, current_sk);
-        if (dst != NULL)
-            enable_encoding_metadata(skops);
-        break;
-
-    default:
-        break;
-    }
-    return 0;
-}
+    if (skb->family != AF_INET && skb->family != AF_INET6)
+        return SK_PASS;
 
-SEC("sockops_passive")
-int sockops_passive_prog(struct bpf_sock_ops *skops)
-{
-    __u64 sock_cookie = bpf_get_socket_cookie(skops);
-
-    if (skops->family != AF_INET && skops->family != AF_INET6)
-        return 0;
-
-    switch (skops->op) {
-    case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
-        if (!is_managed_by_kmesh(skops) || skip_specific_probe(skops))
-            break;
-        observe_on_connect_established(skops->sk, sock_cookie, INBOUND);
-        if (bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_STATE_CB_FLAG) != 0
-            || bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_RETRANS_CB_FLAG) != 0
-            || bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_RTT_CB_FLAG) != 0) {
-            BPF_LOG(ERR, SOCKOPS, "set sockops cb failed!\n");
-        }
-        auth_ip_tuple(skops);
-        break;
-
-    default:
-        break;
-    }
-    return 0;
-}
+    struct bpf_sock *sk = skb->sk;
+    if (!sk)
+        return SK_PASS;
 
-SEC("sockops_utils")
-int sockops_utils_prog(struct bpf_sock_ops *skops)
-{
-    // Filter by IPv4 or IPv6
-    if (skops->family != AF_INET && skops->family != AF_INET6)
-        return 0;
-
-    switch (skops->op) {
-    case BPF_SOCK_OPS_STATE_CB:
-        if (skops->args[1] == BPF_TCP_CLOSE) {
-            clean_auth_map(skops);
-            clean_dstinfo_map(skops);
-        }
-        if (!is_managed_by_kmesh(skops))
-            break;
-        observe_on_status_change(skops->sk, skops->args[0]);
-        break;
-
-    case BPF_SOCK_OPS_RETRANS_CB:
-        if (!is_managed_by_kmesh(skops))
-            break;
-        observe_on_retransmit(skops->sk);
-        break;
-
-    case BPF_SOCK_OPS_RTT_CB:
-        if (!is_managed_by_kmesh(skops))
-            break;
-        observe_on_rtt(skops->sk);
-        break;
-
-    default:
-        break;
-    }
-    return 0;
+    if (!is_managed_by_kmesh_skb(skb))
+        return SK_PASS;
+    observe_on_data(sk);
+    return SK_PASS;
 }
 
-```
-
-Sk_msg hook is triggered when the packet leaves the socket, we will be using sk_msg ebpf hook for refreshing sent bytes data, also we are triggering flush_conn function here to send the connection info to userspace using ringbuffer map.
-Updating sendmsg_prog func in send_msg.c
-
-```
-SEC("sk_msg")
-int sendmsg_prog(struct sk_msg_md *msg)
+SEC("cgroup_skb/egress")
+int cgroup_skb_egress_prog(struct __sk_buff *skb)
 {
-    __u32 off = 0;
-    if (msg->family != AF_INET && msg->family != AF_INET6)
+    if (skb->family != AF_INET && skb->family != AF_INET6)
         return SK_PASS;
 
-    // encode org dst addr
-    encode_metadata_org_dst_addr(msg, &off, (msg->family == AF_INET));
-
-    struct bpf_sock *sk = msg->sk;
-
-    if (sk) {
-        if (is_managed_by_kmesh_skmsg(msg)) {
-            observe_on_data(sk);
-        }
-    } else {
-        BPF_LOG(ERR, KMESH, "sk_lookup success\n");
-    }
-    int key = 0;
-    __u64 *last_time = bpf_map_lookup_elem(&tcp_conn_last_flush, &key);
-    __u64 now = bpf_ktime_get_ns();
+    struct bpf_sock *sk = skb->sk;
+    if (!sk)
+        return SK_PASS;
 
-    if (!last_time) {
-        __u64 init_time = now;
-        // Initialize last flush time if not set
-        bpf_map_update_elem(&tcp_conn_last_flush, &key, &init_time, BPF_ANY);
-    } else if ((now - *last_time) >= TIMER_INTERVAL_NS) {
-        flush_tcp_conns();
-        // Update last flush time
-        bpf_map_update_elem(&tcp_conn_last_flush, &key, &now, BPF_ANY);
-    }
+    if (!is_managed_by_kmesh_skb(skb))
+        return SK_PASS;
+    observe_on_data(sk);
     return SK_PASS;
 }
 
 ```
+Observe_on_data function checks if the time elapsed after lsat_report is greaater than 5 sec. and if it is greater, it report's the conn_info to the ring_buffer and updates the last_report_ns.
 
-For refreshing the received bytes by a connection, we will attach a kprobe on tcp_rcv_established.
-Creating workload/kprobe.c
 ```
-SEC("kprobe/tcp_rcv_established")
-int bpf_tcp_rcv_established(struct pt_regs *ctx) {
-   
-    struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM2(ctx);
-     struct bpf_sock *sk = skb->sk;
-    if (sk) {
-        if (is_managed_by_kmesh_skb(skb)) {
-            observe_on_data(sk);
-        }
-    } else {
-        BPF_LOG(ERR, KMESH, "sk_lookup success\n");
+static inline void observe_on_data(struct bpf_sock *sk)
+{
+    struct bpf_tcp_sock *tcp_sock = NULL;
+    struct sock_storage_data *storage = NULL;
+    if (!sk)
+        return;
+    tcp_sock = bpf_tcp_sock(sk);
+    if (!tcp_sock)
+        return;
+
+    storage = bpf_sk_storage_get(&map_of_sock_storage, sk, 0, 0);
+    if (!storage) {
+        return;
+    }
+    __u64 now = bpf_ktime_get_ns();
+    if ((storage->last_report_ns != 0) && (now - storage->last_report_ns > LONG_CONN_THRESHOLD_TIME)) {
+        tcp_report(sk, tcp_sock, storage, BPF_TCP_ESTABLISHED);
     }
-    return 0;
 }
 ```
 
-We will update functions of tcp_probe.h to store and refresh the connection information on the hash map.
-
 We will update the functions of metric.go for periodic updating the workload and service metrics, also we will create a new metric for long tcp connections.
+
+![design](./pics/tcp_long_conn_design.png)
 #### User Stories (Optional)
 
 <!--
diff --git a/go.mod b/go.mod
index 50dc4e83..4a652f97 100644
--- a/go.mod
+++ b/go.mod
@@ -12,6 +12,7 @@ require (
 	github.com/cncf/xds/go v0.0.0-20241213214725-57cfbe6fad57
 	github.com/containernetworking/cni v1.2.3
 	github.com/containernetworking/plugins v1.6.1
+	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
 	github.com/envoyproxy/go-control-plane v0.13.2-0.20241125134052-fc612d4a3afa
 	github.com/fsnotify/fsnotify v1.8.0
 	github.com/hashicorp/go-multierror v1.1.1
@@ -67,7 +68,6 @@ require (
 	github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
 	github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
 	github.com/cyphar/filepath-securejoin v0.3.5 // indirect
-	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
 	github.com/docker/cli v27.4.0+incompatible // indirect
 	github.com/docker/distribution v2.8.3+incompatible // indirect
diff --git a/hack/run-ebpf-ut.sh b/hack/run-ebpf-ut.sh
new file mode 100755
index 00000000..f468524d
--- /dev/null
+++ b/hack/run-ebpf-ut.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+
+ROOT_DIR=$(git rev-parse --show-toplevel)
+
+. $ROOT_DIR/hack/utils.sh
+
+DOCKER_EBPF_TEST_COMMAND="make -C /kmesh/test/bpf_ut run"
+LOCAL_EBPF_TEST_COMMAND="make -C $ROOT_DIR/test/bpf_ut run"
+
+function docker_run_ebpf_ut() {
+    local container_id=$1
+
+    docker exec $container_id bash -c "$DOCKER_EBPF_TEST_COMMAND"
+    
+    exit_code=$?
+    return $exit_code
+}
+
+function run_ebpf_ut_local() {
+    bash $ROOT_DIR/build.sh
+    export PKG_CONFIG_PATH=$ROOT_DIR/mk
+    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ROOT_DIR/api/v2-c:$ROOT_DIR/bpf/deserialization_to_bpf_map
+    eval "$LOCAL_EBPF_TEST_COMMAND"  
+}
+
+function run_ebpf_ut_in_docker() {
+    container_id=$(run_docker_container)
+    build_kmesh $container_id
+    docker_run_ebpf_ut $container_id
+    ut_exit_code=$?
+    clean_container $container_id
+    exit $ut_exit_code
+}
+
+function clean() {
+    make clean $ROOT_DIR
+}
+
+TEST_FLAGS=""
+
+if [[ "$*" == *"-v"* ]] || [[ "$*" == *"--verbose"* ]]; then
+    TEST_FLAGS="$TEST_FLAGS V=1"
+fi
+
+DOCKER_EBPF_TEST_COMMAND="$DOCKER_EBPF_TEST_COMMAND $TEST_FLAGS"
+LOCAL_EBPF_TEST_COMMAND="$LOCAL_EBPF_TEST_COMMAND $TEST_FLAGS"
+
+# Running ebpf ut with docker by default
+if [ -z "$1" -o "$1" == "-d"  -o  "$1" == "--docker" ]; then
+    run_ebpf_ut_in_docker
+    exit
+fi
+
+if [ "$1" == "-l"  -o  "$1" == "--local" ]; then
+    run_ebpf_ut_local
+    exit
+fi
+
+if [ "$1" == "-h"  -o  "$1" == "--help" ]; then
+    echo run-ebpf-ut.sh -h/--help : Help.
+    echo run-ebpf-ut.sh -d/--docker: run ebpf unit test in docker.
+    echo run-ebpf-ut.sh -l/--local: run ebpf unit test locally.
+    exit
+fi
+
+if [ "$1" == "-c"  -o  "$1" == "--clean" ]; then
+    clean
+    exit
+fi 
diff --git a/kmesh_compile_env_pre.sh b/kmesh_compile_env_pre.sh
index 523c97b9..8e6f548e 100644
--- a/kmesh_compile_env_pre.sh
+++ b/kmesh_compile_env_pre.sh
@@ -17,7 +17,7 @@ function dependency_pkg_install() {
         echo "Checking for required packages on a Debian-based system..."
 
       
-        packages=(git make clang libbpf-dev llvm linux-tools-generic protobuf-compiler libprotobuf-dev libprotobuf-c-dev protobuf-c-compiler cmake pkg-config)
+        packages=(git make clang libbpf-dev llvm linux-tools-generic protobuf-compiler libprotobuf-dev libprotobuf-c-dev protobuf-c-compiler cmake pkg-config gcc-multilib)
 
        
         update_needed=false
@@ -51,7 +51,7 @@ function dependency_pkg_install() {
         echo "Checking for required packages on a Red Hat-based system..."
 
         # List of required packages
-        packages=(git make clang llvm libboundscheck protobuf protobuf-c protobuf-c-devel bpftool libbpf libbpf-devel cmake pkg-config)
+        packages=(git make clang llvm libboundscheck protobuf protobuf-c protobuf-c-devel bpftool libbpf libbpf-devel cmake pkg-config glibc-devel libstdc++-devel)
 
         # Install each missing package
         for pkg in "${packages[@]}"; do
diff --git a/pkg/bpf/workload/loader.go b/pkg/bpf/workload/loader.go
index ef3f2dd5..04d81c28 100644
--- a/pkg/bpf/workload/loader.go
+++ b/pkg/bpf/workload/loader.go
@@ -35,11 +35,12 @@ import (
 var log = logger.NewLoggerScope("bpf_workload")
 
 type BpfWorkload struct {
-	SockConn SockConnWorkload
-	SockOps  BpfSockOpsWorkload
-	XdpAuth  BpfXdpAuthWorkload
-	SendMsg  BpfSendMsgWorkload
-	Tc       *general.BpfTCGeneral
+	SockConn  SockConnWorkload
+	SockOps   BpfSockOpsWorkload
+	XdpAuth   BpfXdpAuthWorkload
+	SendMsg   BpfSendMsgWorkload
+	CgroupSkb BpfCroupSkbWorkload
+	Tc        *general.BpfTCGeneral
 }
 
 func NewBpfWorkload(cfg *options.BpfConfig) (*BpfWorkload, error) {
@@ -53,6 +54,9 @@ func NewBpfWorkload(cfg *options.BpfConfig) (*BpfWorkload, error) {
 		return nil, err
 	}
 
+	if err := workloadObj.CgroupSkb.NewBpf(cfg); err != nil {
+		return nil, err
+	}
 	if err := workloadObj.XdpAuth.NewBpf(cfg); err != nil {
 		return nil, err
 	}
@@ -90,6 +94,13 @@ func (w *BpfWorkload) Start() error {
 		return fmt.Errorf("failed to set api env")
 	}
 
+	if err := w.DeserialInit(); err != nil {
+		return fmt.Errorf("failed to init deserialization: %v", err)
+	}
+	return nil
+}
+
+func (w *BpfWorkload) DeserialInit() error {
 	ret := C.deserial_init()
 	if ret != 0 {
 		return fmt.Errorf("deserial_init failed:%v", ret)
@@ -129,6 +140,10 @@ func (w *BpfWorkload) Load() error {
 		return err
 	}
 
+	if err := w.CgroupSkb.LoadCgroupSkb(); err != nil {
+		return err
+	}
+
 	if err := w.Tc.LoadTC(); err != nil {
 		return err
 	}
@@ -148,6 +163,10 @@ func (w *BpfWorkload) Attach() error {
 		return err
 	}
 
+	if err := w.CgroupSkb.Attach(); err != nil {
+		return err
+	}
+
 	return nil
 }
 
@@ -160,6 +179,10 @@ func (w *BpfWorkload) Detach() error {
 		return err
 	}
 
+	if err := w.CgroupSkb.Detach(); err != nil {
+		return err
+	}
+
 	if err := w.SockOps.Detach(); err != nil {
 		return err
 	}
diff --git a/pkg/bpf/workload/skb.go b/pkg/bpf/workload/skb.go
new file mode 100644
index 00000000..88e1b180
--- /dev/null
+++ b/pkg/bpf/workload/skb.go
@@ -0,0 +1,189 @@
+/*
+ * Copyright The Kmesh Authors.
+ *
+ * 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.
+ */
+
+package workload
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"reflect"
+	"syscall"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/link"
+
+	bpf2go "kmesh.net/kmesh/bpf/kmesh/bpf2go/dualengine"
+	"kmesh.net/kmesh/daemon/options"
+	"kmesh.net/kmesh/pkg/bpf/general"
+	"kmesh.net/kmesh/pkg/bpf/restart"
+	"kmesh.net/kmesh/pkg/bpf/utils"
+	helper "kmesh.net/kmesh/pkg/utils"
+)
+
+type BpfCroupSkbWorkload struct {
+	Info   general.BpfInfo
+	Link   link.Link
+	InfoEg general.BpfInfo
+	LinkEg link.Link
+	bpf2go.KmeshCgroupSkbObjects
+}
+
+func (cs *BpfCroupSkbWorkload) NewBpf(cfg *options.BpfConfig) error {
+	cs.Info.MapPath = cfg.BpfFsPath + "/bpf_kmesh_workload/map/"
+	cs.Info.BpfFsPath = cfg.BpfFsPath + "/bpf_kmesh_workload/cgroup_skb/"
+	cs.Info.Cgroup2Path = cfg.Cgroup2Path
+	cs.InfoEg = cs.Info
+	if err := os.MkdirAll(cs.Info.MapPath,
+		syscall.S_IRUSR|syscall.S_IWUSR|syscall.S_IXUSR|
+			syscall.S_IRGRP|syscall.S_IXGRP); err != nil && !os.IsExist(err) {
+		return err
+	}
+
+	if err := os.MkdirAll(cs.Info.BpfFsPath,
+		syscall.S_IRUSR|syscall.S_IWUSR|syscall.S_IXUSR|
+			syscall.S_IRGRP|syscall.S_IXGRP); err != nil && !os.IsExist(err) {
+		return err
+	}
+
+	return nil
+}
+
+func (cs *BpfCroupSkbWorkload) loadKmeshCgroupSkbObjects() (*ebpf.CollectionSpec, error) {
+	var (
+		err  error
+		spec *ebpf.CollectionSpec
+		opts ebpf.CollectionOptions
+	)
+
+	opts.Maps.PinPath = cs.Info.MapPath
+
+	if helper.KernelVersionLowerThan5_13() {
+		spec, err = bpf2go.LoadKmeshCgroupSkbCompat()
+	} else {
+		spec, err = bpf2go.LoadKmeshCgroupSkb()
+	}
+	if err != nil {
+		return nil, err
+	}
+	if spec == nil {
+		return nil, fmt.Errorf("error: loadKmeshCgroupSkbObjects() spec is nil")
+	}
+
+	utils.SetMapPinType(spec, ebpf.PinByName)
+	if err = spec.LoadAndAssign(&cs.KmeshCgroupSkbObjects, &opts); err != nil {
+		return nil, err
+	}
+
+	return spec, nil
+}
+
+func (cs *BpfCroupSkbWorkload) LoadCgroupSkb() error {
+	/* load kmesh recvmsg main bpf prog*/
+	spec, err := cs.loadKmeshCgroupSkbObjects()
+	if err != nil {
+		return err
+	}
+
+	progIn := spec.Programs["cgroup_skb_ingress_prog"]
+	cs.Info.Type = progIn.Type
+	cs.Info.AttachType = progIn.AttachType
+	progEg := spec.Programs["cgroup_skb_egress_prog"]
+	cs.InfoEg.Type = progEg.Type
+	cs.InfoEg.AttachType = progEg.AttachType
+
+	return nil
+}
+
+func (cs *BpfCroupSkbWorkload) Attach() error {
+	var err error
+	cgopt := link.CgroupOptions{
+		Path:    cs.Info.Cgroup2Path,
+		Attach:  cs.Info.AttachType,
+		Program: cs.KmeshCgroupSkbObjects.CgroupSkbIngressProg,
+	}
+	cgopt2 := link.CgroupOptions{
+		Path:    cs.InfoEg.Cgroup2Path,
+		Attach:  cs.InfoEg.AttachType,
+		Program: cs.KmeshCgroupSkbObjects.CgroupSkbEgressProg,
+	}
+	pinPathIn := filepath.Join(cs.Info.BpfFsPath, "cgroup_skb_ingress_prog")
+	pinPathEg := filepath.Join(cs.Info.BpfFsPath, "cgroup_skb_egress_prog")
+	if restart.GetStartType() == restart.Restart {
+		if cs.Link, err = utils.BpfProgUpdate(pinPathIn, cgopt); err != nil {
+			return err
+		}
+		if cs.LinkEg, err = utils.BpfProgUpdate(pinPathIn, cgopt2); err != nil {
+			return err
+		}
+	} else {
+		lk, err := link.AttachCgroup(cgopt)
+		if err != nil {
+			return err
+		}
+		cs.Link = lk
+
+		if err := lk.Pin(pinPathIn); err != nil {
+			return err
+		}
+		lk2, err := link.AttachCgroup(cgopt2)
+		if err != nil {
+			return err
+		}
+		cs.LinkEg = lk2
+		if err := lk2.Pin(pinPathEg); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (cs *BpfCroupSkbWorkload) close() error {
+	if err := cs.KmeshCgroupSkbObjects.Close(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (cs *BpfCroupSkbWorkload) Detach() error {
+	if err := cs.close(); err != nil {
+		return err
+	}
+
+	program_value := reflect.ValueOf(cs.KmeshCgroupSkbObjects.KmeshCgroupSkbPrograms)
+	if err := utils.UnpinPrograms(&program_value); err != nil {
+		return err
+	}
+
+	map_value := reflect.ValueOf(cs.KmeshCgroupSkbObjects.KmeshCgroupSkbMaps)
+	if err := utils.UnpinMaps(&map_value); err != nil {
+		return err
+	}
+
+	if err := os.RemoveAll(cs.Info.BpfFsPath); err != nil && !os.IsNotExist(err) {
+		return err
+	}
+
+	if cs.Link != nil {
+		return cs.Link.Close()
+	}
+
+	if cs.LinkEg != nil {
+		return cs.LinkEg.Close()
+	}
+	return nil
+}
diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go
index 610684b5..2e1681ee 100644
--- a/pkg/constants/constants.go
+++ b/pkg/constants/constants.go
@@ -76,8 +76,9 @@ const (
 	KmKernelNativeBpfPath = "/bpf_kmesh"
 	KmDualEngineBpfPath   = "/bpf_kmesh_workload"
 
-	TailCallMap = "tail_call_map"
-	Prog_link   = "prog_link"
+	TailCallMap    = "tail_call_map"
+	XDPTailCallMap = "km_xdp_tailcall"
+	Prog_link      = "prog_link"
 
 	ALL_CIDR = "0.0.0.0/0"
 )
diff --git a/pkg/controller/telemetry/accesslog.go b/pkg/controller/telemetry/accesslog.go
index 05783ba2..0f3a470a 100644
--- a/pkg/controller/telemetry/accesslog.go
+++ b/pkg/controller/telemetry/accesslog.go
@@ -26,6 +26,7 @@ import (
 
 type logInfo struct {
 	direction       string
+	state           string
 	sourceAddress   string
 	sourceWorkload  string
 	sourceNamespace string
@@ -76,19 +77,19 @@ func (l *logInfo) withDestinationService(service *workloadapi.Service) *logInfo
 	return l
 }
 
-func OutputAccesslog(data requestMetric, accesslog logInfo) {
-	logStr := buildAccesslog(data, accesslog)
+func OutputAccesslog(data requestMetric, conn_metrics connMetric, accesslog logInfo) {
+	logStr := buildAccesslog(data, conn_metrics, accesslog)
 	fmt.Println("accesslog:", logStr)
 }
 
-func buildAccesslog(data requestMetric, accesslog logInfo) string {
-	closeTime := data.closeTime
-	uptime := calculateUptime(osStartTime, closeTime)
-
+func buildAccesslog(data requestMetric, conn_metrics connMetric, accesslog logInfo) string {
+	uptime := calculateUptime(osStartTime, data.lastReportTime)
+	startTime := calculateUptime(osStartTime, data.startTime)
+	startTimeInfo := fmt.Sprintf("%v", startTime)
 	timeInfo := fmt.Sprintf("%v", uptime)
 	sourceInfo := fmt.Sprintf("src.addr=%s, src.workload=%s, src.namespace=%s", accesslog.sourceAddress, accesslog.sourceWorkload, accesslog.sourceNamespace)
 	destinationInfo := fmt.Sprintf("dst.addr=%s, dst.service=%s, dst.workload=%s, dst.namespace=%s", accesslog.destinationAddress, accesslog.destinationService, accesslog.destinationWorkload, accesslog.destinationNamespace)
-	connectionInfo := fmt.Sprintf("direction=%s, sent_bytes=%d, received_bytes=%d, srtt=%dus, min_rtt=%dus, duration=%vms", accesslog.direction, data.sentBytes, data.receivedBytes, data.srtt, data.minRtt, (float64(data.duration) / 1000000.0))
+	connectionInfo := fmt.Sprintf("start_time=%s, direction=%s, state=%s, sent_bytes=%d, received_bytes=%d, packet_loss=%d, retransmissions=%d, srtt=%dus, min_rtt=%dus, duration=%vms", startTimeInfo, accesslog.direction, accesslog.state, conn_metrics.sentBytes, conn_metrics.receivedBytes, conn_metrics.packetLost, conn_metrics.totalRetrans, data.srtt, data.minRtt, (float64(data.duration) / 1000000.0))
 
 	logResult := fmt.Sprintf("%s %s, %s, %s", timeInfo, sourceInfo, destinationInfo, connectionInfo)
 	return logResult
diff --git a/pkg/controller/telemetry/metric.go b/pkg/controller/telemetry/metric.go
index 06b34501..8e6fbaa7 100644
--- a/pkg/controller/telemetry/metric.go
+++ b/pkg/controller/telemetry/metric.go
@@ -51,6 +51,22 @@ const (
 
 var osStartTime time.Time
 
+var TCP_STATES = map[uint32]string{
+	1:  "BPF_TCP_ESTABLISHED",
+	2:  "BPF_TCP_SYN_SENT",
+	3:  "BPF_TCP_SYN_RECV",
+	4:  "BPF_TCP_FIN_WAIT1",
+	5:  "BPF_TCP_FIN_WAIT2",
+	6:  "BPF_TCP_TIME_WAIT",
+	7:  "BPF_TCP_CLOSE",
+	8:  "BPF_TCP_CLOSE_WAIT",
+	9:  "BPF_TCP_LAST_ACK",
+	10: "BPF_TCP_LISTEN",
+	11: "BPF_TCP_CLOSING",
+	12: "BPF_TCP_NEW_SYN_RECV",
+	13: "BPF_TCP_MAX_STATES",
+}
+
 type MetricController struct {
 	EnableAccesslog      atomic.Bool
 	EnableMonitoring     atomic.Bool
@@ -85,18 +101,18 @@ type statistics struct {
 	ReceivedBytes  uint32
 	ConnectSuccess uint32
 	Direction      uint32
-	State          uint32
-	Duration       uint64
-	CloseTime      uint64
-	// TODO: statistics below are not used for now
-	Protocol    uint32
-	SRttTime    uint32
-	RttMin      uint32
-	Retransmits uint32
-	LostPackets uint32
-}
-
-// connectionDataV4 read from ebpf ringbuf and padding with `_`
+	State          uint32 // TCP state ex: BPF_TCP_ESTABLISHED
+	Duration       uint64 // duration of the connection till now
+	StartTime      uint64 // time when the connection is established
+	LastReportTime uint64 // time when the metric is reported
+	Protocol       uint32
+	SRttTime       uint32 // smoothed RTT
+	RttMin         uint32 // minimum RTT
+	Retransmits    uint32 // total retransmits
+	LostPackets    uint32 // total lost packets
+}
+
+// connectionDataV4 read from ebpf km_tcp_probe ringbuf and padding with `_`
 type connectionDataV4 struct {
 	SrcAddr      uint32
 	DstAddr      uint32
@@ -105,12 +121,12 @@ type connectionDataV4 struct {
 	_            [6]uint32
 	OriginalAddr uint32
 	OriginalPort uint16
-	_            uint16
-	_            [3]uint32
+	_            [7]uint16
 	statistics
+	_ uint32
 }
 
-// connectionDataV6 read from ebpf ringbuf and padding with `_`
+// connectionDataV6 read from ebpf km_tcp_probe ringbuf and padding with `_`
 type connectionDataV6 struct {
 	SrcAddr      [4]uint32
 	DstAddr      [4]uint32
@@ -120,26 +136,39 @@ type connectionDataV6 struct {
 	OriginalPort uint16
 	_            uint16
 	statistics
+	_ uint32
+}
+
+type connMetric struct {
+	receivedBytes uint32 // total bytes received till now
+	sentBytes     uint32 // total bytes sent till now
+	totalRetrans  uint32 // total retransmits till now
+	packetLost    uint32 // total packets lost till now
+}
+
+type connectionSrcDst struct {
+	src     [4]uint32
+	dst     [4]uint32
+	srcPort uint16
+	dstPort uint16
 }
 
 type requestMetric struct {
-	src           [4]uint32
-	dst           [4]uint32
-	srcPort       uint16
-	dstPort       uint16
-	origDstAddr   [4]uint32
-	origDstPort   uint16
-	direction     uint32
-	receivedBytes uint32
-	sentBytes     uint32
-	state         uint32
-	success       uint32
-	duration      uint64
-	closeTime     uint64
-	srtt          uint32
-	minRtt        uint32
-	totalRetrans  uint32
-	PacketLost    uint32
+	conSrcDstInfo  connectionSrcDst
+	origDstAddr    [4]uint32
+	origDstPort    uint16
+	direction      uint32
+	receivedBytes  uint32 // total bytes received after previous report
+	sentBytes      uint32 // total bytes sent after previous report
+	state          uint32
+	success        uint32
+	duration       uint64
+	startTime      uint64
+	lastReportTime uint64
+	srtt           uint32
+	minRtt         uint32
+	totalRetrans   uint32 // total retransmits after previous report
+	packetLost     uint32 // total packets lost after previous report
 }
 
 type workloadMetricLabels struct {
@@ -308,15 +337,18 @@ func (m *MetricController) Run(ctx context.Context, mapOfTcpInfo *ebpf.Map) {
 
 	reader, err := ringbuf.NewReader(mapOfTcpInfo)
 	if err != nil {
-		log.Errorf("open metric notify ringbuf map FAILED, err: %v", err)
+		log.Errorf("open km_tcp_probe ringbuf map FAILED, err: %v", err)
 		return
 	}
+
 	defer func() {
 		if err := reader.Close(); err != nil {
 			log.Errorf("ringbuf reader Close FAILED, err: %v", err)
 		}
 	}()
 
+	tcp_conns := make(map[connectionSrcDst]connMetric)
+
 	// Register metrics to Prometheus and start Prometheus server
 	go RunPrometheusClient(ctx)
 	go func() {
@@ -346,8 +378,10 @@ func (m *MetricController) Run(ctx context.Context, mapOfTcpInfo *ebpf.Map) {
 				log.Errorf("ringbuf reader FAILED to read, err: %v", err)
 				continue
 			}
-			if len(rec.RawSample) != int(unsafe.Sizeof(connectionDataV4{})) {
-				log.Errorf("wrong length %v of a msg, should be %v", len(rec.RawSample), int(unsafe.Sizeof(connectionDataV4{})))
+
+			// len(rec.RawSample) = 128
+			if len(rec.RawSample) != int(unsafe.Sizeof(connectionDataV4{}))-int(8) {
+				log.Errorf("wrong length %v of a msg, should be %v", len(rec.RawSample), int(unsafe.Sizeof(connectionDataV4{}))-int(8))
 				continue
 			}
 			connectType := binary.LittleEndian.Uint32(rec.RawSample)
@@ -355,9 +389,17 @@ func (m *MetricController) Run(ctx context.Context, mapOfTcpInfo *ebpf.Map) {
 			buf := bytes.NewBuffer(originInfo)
 			switch connectType {
 			case constants.MSG_TYPE_IPV4:
-				data, err = buildV4Metric(buf)
+				data, err = buildV4Metric(buf, tcp_conns)
+				if err != nil {
+					log.Errorf("get connectionV4 info failed: %v", err)
+					continue
+				}
 			case constants.MSG_TYPE_IPV6:
-				data, err = buildV6Metric(buf)
+				data, err = buildV6Metric(buf, tcp_conns)
+				if err != nil {
+					log.Errorf("get connectionV6 info failed: %v", err)
+					continue
+				}
 			default:
 				log.Errorf("get connection info failed: %v", err)
 				continue
@@ -369,8 +411,13 @@ func (m *MetricController) Run(ctx context.Context, mapOfTcpInfo *ebpf.Map) {
 				workloadLabels = m.buildWorkloadMetric(&data)
 			}
 
-			if data.state == TCP_CLOSTED && m.EnableAccesslog.Load() {
-				OutputAccesslog(data, accesslog)
+			if m.EnableAccesslog.Load() {
+				// accesslogs at start of connection, at interval of 5 sec during connection lifecycle and at close of connection
+				OutputAccesslog(data, tcp_conns[data.conSrcDstInfo], accesslog)
+			}
+
+			if data.state == TCP_CLOSTED {
+				delete(tcp_conns, data.conSrcDstInfo)
 			}
 
 			m.mutex.Lock()
@@ -383,86 +430,101 @@ func (m *MetricController) Run(ctx context.Context, mapOfTcpInfo *ebpf.Map) {
 	}
 }
 
-func buildV4Metric(buf *bytes.Buffer) (requestMetric, error) {
+func buildV4Metric(buf *bytes.Buffer, tcp_conns map[connectionSrcDst]connMetric) (requestMetric, error) {
 	data := requestMetric{}
 	connectData := connectionDataV4{}
+
 	if err := binary.Read(buf, binary.LittleEndian, &connectData); err != nil {
 		return data, err
 	}
 
-	data.src[0] = connectData.SrcAddr
-	data.dst[0] = connectData.DstAddr
+	data.conSrcDstInfo.src[0] = connectData.SrcAddr
+	data.conSrcDstInfo.dst[0] = connectData.DstAddr
 	data.direction = connectData.Direction
-	data.dstPort = connectData.DstPort
-	data.srcPort = connectData.SrcPort
+	data.conSrcDstInfo.dstPort = connectData.DstPort
+	data.conSrcDstInfo.srcPort = connectData.SrcPort
 
 	// original addr is 0 indicates the connection is
 	// workload-type or and not redirected,
 	// so we just take from the actual destination
 	if connectData.OriginalAddr == 0 {
-		data.origDstAddr[0] = data.dst[0]
-		data.origDstPort = data.dstPort
+		data.origDstAddr[0] = data.conSrcDstInfo.dst[0]
+		data.origDstPort = data.conSrcDstInfo.dstPort
 	} else {
 		data.origDstAddr[0] = connectData.OriginalAddr
 		data.origDstPort = connectData.OriginalPort
 	}
 
-	data.sentBytes = connectData.SentBytes
-	data.receivedBytes = connectData.ReceivedBytes
+	data.sentBytes = connectData.SentBytes - tcp_conns[data.conSrcDstInfo].sentBytes
+	data.receivedBytes = connectData.ReceivedBytes - tcp_conns[data.conSrcDstInfo].receivedBytes
 	data.state = connectData.State
 	data.success = connectData.ConnectSuccess
 	data.duration = connectData.Duration
-	data.closeTime = connectData.CloseTime
+	data.startTime = connectData.StartTime
+	data.lastReportTime = connectData.LastReportTime
 	data.srtt = connectData.statistics.SRttTime
 	data.minRtt = connectData.statistics.RttMin
-	data.totalRetrans = connectData.statistics.Retransmits
-	data.PacketLost = connectData.statistics.LostPackets
+	data.totalRetrans = connectData.statistics.Retransmits - tcp_conns[data.conSrcDstInfo].totalRetrans
+	data.packetLost = connectData.statistics.LostPackets - tcp_conns[data.conSrcDstInfo].packetLost
+	tcp_conns[data.conSrcDstInfo] = connMetric{
+		receivedBytes: connectData.ReceivedBytes,
+		sentBytes:     connectData.SentBytes,
+		totalRetrans:  connectData.statistics.Retransmits,
+		packetLost:    connectData.statistics.LostPackets,
+	}
 
 	return data, nil
 }
 
-func buildV6Metric(buf *bytes.Buffer) (requestMetric, error) {
+func buildV6Metric(buf *bytes.Buffer, tcp_conns map[connectionSrcDst]connMetric) (requestMetric, error) {
 	data := requestMetric{}
 	connectData := connectionDataV6{}
 	if err := binary.Read(buf, binary.LittleEndian, &connectData); err != nil {
 		return data, err
 	}
-	data.src = connectData.SrcAddr
-	data.dst = connectData.DstAddr
+	data.conSrcDstInfo.src = connectData.SrcAddr
+	data.conSrcDstInfo.dst = connectData.DstAddr
 	data.direction = connectData.Direction
-	data.dstPort = connectData.DstPort
-	data.srcPort = connectData.SrcPort
+	data.conSrcDstInfo.dstPort = connectData.DstPort
+	data.conSrcDstInfo.srcPort = connectData.SrcPort
 
 	// original addr is 0 indicates the connection is
 	// workload-type or and not redirected,
 	// so we just take from the actual destination
 	if !isOrigDstSet(connectData.OriginalAddr) {
-		data.origDstAddr = data.dst
-		data.origDstPort = data.dstPort
+		data.origDstAddr = data.conSrcDstInfo.dst
+		data.origDstPort = data.conSrcDstInfo.dstPort
 	} else {
 		data.origDstAddr = connectData.OriginalAddr
 		data.origDstPort = connectData.OriginalPort
 	}
 
-	data.sentBytes = connectData.SentBytes
-	data.receivedBytes = connectData.ReceivedBytes
+	data.sentBytes = connectData.SentBytes - tcp_conns[data.conSrcDstInfo].sentBytes
+	data.receivedBytes = connectData.ReceivedBytes - tcp_conns[data.conSrcDstInfo].receivedBytes
 	data.state = connectData.State
 	data.success = connectData.ConnectSuccess
 	data.duration = connectData.Duration
-	data.closeTime = connectData.CloseTime
+	data.startTime = connectData.StartTime
+	data.lastReportTime = connectData.LastReportTime
 	data.srtt = connectData.statistics.SRttTime
 	data.minRtt = connectData.statistics.RttMin
-	data.totalRetrans = connectData.statistics.Retransmits
-	data.PacketLost = connectData.statistics.LostPackets
+	data.totalRetrans = connectData.statistics.Retransmits - tcp_conns[data.conSrcDstInfo].totalRetrans
+	data.packetLost = connectData.statistics.LostPackets - tcp_conns[data.conSrcDstInfo].packetLost
+	tcp_conns[data.conSrcDstInfo] = connMetric{
+		receivedBytes: connectData.ReceivedBytes,
+		sentBytes:     connectData.SentBytes,
+		totalRetrans:  connectData.statistics.Retransmits,
+		packetLost:    connectData.statistics.LostPackets,
+	}
 
 	return data, nil
 }
 
 func (m *MetricController) buildWorkloadMetric(data *requestMetric) workloadMetricLabels {
 	var dstAddr, srcAddr []byte
-	for i := range data.dst {
-		dstAddr = binary.LittleEndian.AppendUint32(dstAddr, data.dst[i])
-		srcAddr = binary.LittleEndian.AppendUint32(srcAddr, data.src[i])
+	for i := range data.conSrcDstInfo.dst {
+		dstAddr = binary.LittleEndian.AppendUint32(dstAddr, data.conSrcDstInfo.dst[i])
+		srcAddr = binary.LittleEndian.AppendUint32(srcAddr, data.conSrcDstInfo.src[i])
 	}
 
 	dstWorkload, dstIP := m.getWorkloadByAddress(restoreIPv4(dstAddr))
@@ -550,9 +612,9 @@ func (m *MetricController) fetchOriginalService(address []byte, port uint32) *wo
 
 func (m *MetricController) buildServiceMetric(data *requestMetric) (serviceMetricLabels, logInfo) {
 	var dstAddr, srcAddr, origAddr []byte
-	for i := range data.dst {
-		dstAddr = binary.LittleEndian.AppendUint32(dstAddr, data.dst[i])
-		srcAddr = binary.LittleEndian.AppendUint32(srcAddr, data.src[i])
+	for i := range data.conSrcDstInfo.dst {
+		dstAddr = binary.LittleEndian.AppendUint32(dstAddr, data.conSrcDstInfo.dst[i])
+		srcAddr = binary.LittleEndian.AppendUint32(srcAddr, data.conSrcDstInfo.src[i])
 		origAddr = binary.LittleEndian.AppendUint32(origAddr, data.origDstAddr[i])
 	}
 
@@ -574,8 +636,8 @@ func (m *MetricController) buildServiceMetric(data *requestMetric) (serviceMetri
 
 	accesslog := NewLogInfo()
 	accesslog.withSource(srcWorkload).withDestination(dstWorkload).withDestinationService(dstService)
-	accesslog.destinationAddress = dstIp + ":" + fmt.Sprintf("%d", data.dstPort)
-	accesslog.sourceAddress = srcIp + ":" + fmt.Sprintf("%d", data.srcPort)
+	accesslog.destinationAddress = dstIp + ":" + fmt.Sprintf("%d", data.conSrcDstInfo.dstPort)
+	accesslog.sourceAddress = srcIp + ":" + fmt.Sprintf("%d", data.conSrcDstInfo.srcPort)
 
 	switch data.direction {
 	case constants.INBOUND:
@@ -586,6 +648,7 @@ func (m *MetricController) buildServiceMetric(data *requestMetric) (serviceMetri
 		accesslog.direction = "OUTBOUND"
 	}
 
+	accesslog.state = TCP_STATES[data.state]
 	return *trafficLabels, *accesslog
 }
 
@@ -621,7 +684,7 @@ func (m *MetricController) updateWorkloadMetricCache(data requestMetric, labels
 		v.WorkloadConnReceivedBytes = v.WorkloadConnReceivedBytes + float64(data.receivedBytes)
 		v.WorkloadConnSentBytes = v.WorkloadConnSentBytes + float64(data.sentBytes)
 		v.WorkloadConnTotalRetrans = v.WorkloadConnTotalRetrans + float64(data.totalRetrans)
-		v.WorkloadConnPacketLost = v.WorkloadConnPacketLost + float64(data.PacketLost)
+		v.WorkloadConnPacketLost = v.WorkloadConnPacketLost + float64(data.packetLost)
 	} else {
 		newWorkloadMetricInfo := workloadMetricInfo{}
 		if data.state == TCP_ESTABLISHED {
@@ -636,7 +699,7 @@ func (m *MetricController) updateWorkloadMetricCache(data requestMetric, labels
 		newWorkloadMetricInfo.WorkloadConnReceivedBytes = float64(data.receivedBytes)
 		newWorkloadMetricInfo.WorkloadConnSentBytes = float64(data.sentBytes)
 		newWorkloadMetricInfo.WorkloadConnTotalRetrans = float64(data.totalRetrans)
-		newWorkloadMetricInfo.WorkloadConnPacketLost = float64(data.PacketLost)
+		newWorkloadMetricInfo.WorkloadConnPacketLost = float64(data.packetLost)
 		m.workloadMetricCache[labels] = &newWorkloadMetricInfo
 	}
 }



Test patch:
diff --git a/pkg/controller/telemetry/accesslog_test.go b/pkg/controller/telemetry/accesslog_test.go
index b7280a5d..df6739bc 100644
--- a/pkg/controller/telemetry/accesslog_test.go
+++ b/pkg/controller/telemetry/accesslog_test.go
@@ -25,8 +25,9 @@ import (
 
 func Test_buildAccesslog(t *testing.T) {
 	type args struct {
-		data      requestMetric
-		accesslog logInfo
+		conn_metrics connMetric
+		req_metrics  requestMetric
+		accesslog    logInfo
 	}
 	tests := []struct {
 		name string
@@ -36,11 +37,14 @@ func Test_buildAccesslog(t *testing.T) {
 		{
 			name: "build accesslog",
 			args: args{
-				data: requestMetric{
+				conn_metrics: connMetric{
 					sentBytes:     uint32(60),
 					receivedBytes: uint32(172),
-					duration:      uint64(2236000),
-					closeTime:     uint64(3506247005837715),
+				},
+				req_metrics: requestMetric{
+					duration:       uint64(2236000),
+					startTime:      uint64(3506247005837715),
+					lastReportTime: uint64(3506247005837715),
 				},
 				accesslog: logInfo{
 					direction:            "INBOUND",
@@ -51,15 +55,16 @@ func Test_buildAccesslog(t *testing.T) {
 					destinationService:   "httpbin.ambient-demo.svc.cluster.local",
 					destinationWorkload:  "httpbin-86b8ffc5ff-bhvxx",
 					destinationNamespace: "kmesh-system",
+					state:                "BPF_TCP_SYN_RECV",
 				},
 			},
-			want: "2024-08-14 10:11:27.005837715 +0000 UTC src.addr=10.244.0.10:47667, src.workload=sleep-7656cf8794-9v2gv, src.namespace=kmesh-system, dst.addr=10.244.0.7:8080, dst.service=httpbin.ambient-demo.svc.cluster.local, dst.workload=httpbin-86b8ffc5ff-bhvxx, dst.namespace=kmesh-system, direction=INBOUND, sent_bytes=60, received_bytes=172, srtt=0us, min_rtt=0us, duration=2.236ms",
+			want: "2024-08-14 10:11:27.005837715 +0000 UTC src.addr=10.244.0.10:47667, src.workload=sleep-7656cf8794-9v2gv, src.namespace=kmesh-system, dst.addr=10.244.0.7:8080, dst.service=httpbin.ambient-demo.svc.cluster.local, dst.workload=httpbin-86b8ffc5ff-bhvxx, dst.namespace=kmesh-system, start_time=2024-08-14 10:11:27.005837715 +0000 UTC, direction=INBOUND, state=BPF_TCP_SYN_RECV, sent_bytes=60, received_bytes=172, packet_loss=0, retransmissions=0, srtt=0us, min_rtt=0us, duration=2.236ms",
 		},
 	}
 	osStartTime = time.Date(2024, 7, 4, 20, 14, 0, 0, time.UTC)
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got := buildAccesslog(tt.args.data, tt.args.accesslog)
+			got := buildAccesslog(tt.args.req_metrics, tt.args.conn_metrics, tt.args.accesslog)
 			assert.Equal(t, tt.want, got)
 		})
 	}
diff --git a/pkg/controller/telemetry/metric_test.go b/pkg/controller/telemetry/metric_test.go
index e5ba0315..4f69a736 100644
--- a/pkg/controller/telemetry/metric_test.go
+++ b/pkg/controller/telemetry/metric_test.go
@@ -187,10 +187,14 @@ func TestBuildMetricsToPrometheus(t *testing.T) {
 			name: "test build workload metrisc to metricCache",
 			args: args{
 				data: requestMetric{
-					src:           [4]uint32{183763210, 0, 0, 0},
-					dst:           [4]uint32{183762951, 0, 0, 0},
+					conSrcDstInfo: connectionSrcDst{
+						src: [4]uint32{183763210, 0, 0, 0},
+						dst: [4]uint32{183762951, 0, 0, 0},
+					},
 					sentBytes:     0x0000003,
 					receivedBytes: 0x0000004,
+					packetLost:    0x0000001,
+					totalRetrans:  0x0000002,
 					state:         TCP_ESTABLISHED,
 				},
 				labels: workloadMetricLabels{
@@ -224,6 +228,8 @@ func TestBuildMetricsToPrometheus(t *testing.T) {
 				1,
 				4,
 				3,
+				1,
+				2,
 			},
 		},
 	}
@@ -239,6 +245,8 @@ func TestBuildMetricsToPrometheus(t *testing.T) {
 			assert.Equal(t, m.workloadMetricCache[tt.args.labels].WorkloadConnOpened, tt.want[1])
 			assert.Equal(t, m.workloadMetricCache[tt.args.labels].WorkloadConnReceivedBytes, tt.want[2])
 			assert.Equal(t, m.workloadMetricCache[tt.args.labels].WorkloadConnSentBytes, tt.want[3])
+			assert.Equal(t, m.workloadMetricCache[tt.args.labels].WorkloadConnPacketLost, tt.want[4])
+			assert.Equal(t, m.workloadMetricCache[tt.args.labels].WorkloadConnTotalRetrans, tt.want[5])
 		})
 	}
 }
@@ -257,8 +265,10 @@ func TestBuildServiceMetricsToPrometheus(t *testing.T) {
 			name: "build service metrics in metricCache",
 			args: args{
 				data: requestMetric{
-					src:           [4]uint32{183763210, 0, 0, 0},
-					dst:           [4]uint32{183762951, 0, 0, 0},
+					conSrcDstInfo: connectionSrcDst{
+						src: [4]uint32{183763210, 0, 0, 0},
+						dst: [4]uint32{183762951, 0, 0, 0},
+					},
 					sentBytes:     0x0000009,
 					receivedBytes: 0x0000008,
 					state:         TCP_ESTABLISHED,
@@ -391,10 +401,14 @@ func TestBuildworkloadMetric(t *testing.T) {
 			name: "normal capability test",
 			args: args{
 				data: &requestMetric{
-					src:           [4]uint32{521736970, 0, 0, 0},
-					dst:           [4]uint32{383822016, 0, 0, 0},
+					conSrcDstInfo: connectionSrcDst{
+						src: [4]uint32{521736970, 0, 0, 0},
+						dst: [4]uint32{383822016, 0, 0, 0},
+					},
 					sentBytes:     uint32(156),
 					receivedBytes: uint32(1024),
+					packetLost:    uint32(5),
+					totalRetrans:  uint32(120),
 				},
 			},
 			want: workloadMetricLabels{
@@ -583,12 +597,14 @@ func TestBuildServiceMetric(t *testing.T) {
 			name: "destination exists with service attached, workload-type request",
 			args: args{
 				data: &requestMetric{
-					// sleep
-					src: [4]uint32{nets.ConvertIpToUint32("10.19.25.33"), 0, 0, 0},
-					// kmesh-daemon
-					dst:     [4]uint32{nets.ConvertIpToUint32("10.19.25.31"), 0, 0, 0},
-					dstPort: uint16(8000),
-					srcPort: uint16(8000),
+					conSrcDstInfo: connectionSrcDst{
+						// sleep
+						src: [4]uint32{nets.ConvertIpToUint32("10.19.25.33"), 0, 0, 0},
+						// kmesh-daemon
+						dst:     [4]uint32{nets.ConvertIpToUint32("10.19.25.31"), 0, 0, 0},
+						dstPort: uint16(8000),
+						srcPort: uint16(8000),
+					},
 					// kmesh-daemon
 					origDstAddr:   [4]uint32{nets.ConvertIpToUint32("10.19.25.31"), 0, 0, 0},
 					origDstPort:   uint16(8000),
@@ -637,12 +653,14 @@ func TestBuildServiceMetric(t *testing.T) {
 			name: "destination exists with service attached, service-type request",
 			args: args{
 				data: &requestMetric{
-					// sleep
-					src: [4]uint32{nets.ConvertIpToUint32("10.19.25.33"), 0, 0, 0},
-					// kmesh-daemon
-					dst:     [4]uint32{nets.ConvertIpToUint32("10.19.25.31"), 0, 0, 0},
-					dstPort: uint16(8000),
-					srcPort: uint16(8000),
+					conSrcDstInfo: connectionSrcDst{
+						// sleep
+						src: [4]uint32{nets.ConvertIpToUint32("10.19.25.33"), 0, 0, 0},
+						// kmesh-daemon
+						dst:     [4]uint32{nets.ConvertIpToUint32("10.19.25.31"), 0, 0, 0},
+						dstPort: uint16(8000),
+						srcPort: uint16(8000),
+					},
 					// kmesh-daemon
 					origDstAddr:   [4]uint32{nets.ConvertIpToUint32("192.168.1.22"), 0, 0, 0},
 					origDstPort:   uint16(8000),
@@ -691,12 +709,14 @@ func TestBuildServiceMetric(t *testing.T) {
 			name: "nil destination workload",
 			args: args{
 				data: &requestMetric{
-					// sleep
-					src: [4]uint32{nets.ConvertIpToUint32("10.19.25.33"), 0, 0, 0},
-					// unknown address
-					dst:     [4]uint32{nets.ConvertIpToUint32("191.168.224.22"), 0, 0, 0},
-					dstPort: uint16(80),
-					srcPort: uint16(8000),
+					conSrcDstInfo: connectionSrcDst{
+						// sleep
+						src: [4]uint32{nets.ConvertIpToUint32("10.19.25.33"), 0, 0, 0},
+						// unknown address
+						dst:     [4]uint32{nets.ConvertIpToUint32("191.168.224.22"), 0, 0, 0},
+						dstPort: uint16(80),
+						srcPort: uint16(8000),
+					},
 					// unknown address
 					origDstAddr:   [4]uint32{nets.ConvertIpToUint32("191.168.224.22"), 0, 0, 0},
 					origDstPort:   uint16(80),
@@ -745,12 +765,14 @@ func TestBuildServiceMetric(t *testing.T) {
 			name: "service-type request, redirected to waypoint",
 			args: args{
 				data: &requestMetric{
-					// sleep
-					src:     [4]uint32{nets.ConvertIpToUint32("10.19.25.33"), 0, 0, 0},
-					srcPort: uint16(49875),
-					// waypoint
-					dst:     [4]uint32{nets.ConvertIpToUint32("10.19.25.32"), 0, 0, 0},
-					dstPort: uint16(80),
+					conSrcDstInfo: connectionSrcDst{
+						// sleep
+						src:     [4]uint32{nets.ConvertIpToUint32("10.19.25.33"), 0, 0, 0},
+						srcPort: uint16(49875),
+						// waypoint
+						dst:     [4]uint32{nets.ConvertIpToUint32("10.19.25.32"), 0, 0, 0},
+						dstPort: uint16(80),
+					},
 					// httpbin service
 					origDstAddr:   [4]uint32{nets.ConvertIpToUint32("192.168.1.23"), 0, 0, 0},
 					origDstPort:   uint16(80),
@@ -799,12 +821,14 @@ func TestBuildServiceMetric(t *testing.T) {
 			name: "workload-type request, redirected to waypoint",
 			args: args{
 				data: &requestMetric{
-					// sleep
-					src:     [4]uint32{nets.ConvertIpToUint32("10.19.25.33"), 0, 0, 0},
-					srcPort: uint16(49875),
-					// waypoint
-					dst:     [4]uint32{nets.ConvertIpToUint32("10.19.25.32"), 0, 0, 0},
-					dstPort: uint16(80),
+					conSrcDstInfo: connectionSrcDst{
+						// sleep
+						src:     [4]uint32{nets.ConvertIpToUint32("10.19.25.33"), 0, 0, 0},
+						srcPort: uint16(49875),
+						// waypoint
+						dst:     [4]uint32{nets.ConvertIpToUint32("10.19.25.32"), 0, 0, 0},
+						dstPort: uint16(80),
+					},
 					// solelyWorkload
 					origDstAddr:   [4]uint32{nets.ConvertIpToUint32("10.19.25.34"), 0, 0, 0},
 					origDstPort:   uint16(80),
@@ -853,12 +877,14 @@ func TestBuildServiceMetric(t *testing.T) {
 			name: "destination workload exists without service attached",
 			args: args{
 				data: &requestMetric{
-					// sleep
-					src:     [4]uint32{nets.ConvertIpToUint32("10.19.25.33"), 0, 0, 0},
-					srcPort: uint16(49875),
-					// solely workload
-					dst:     [4]uint32{nets.ConvertIpToUint32("10.19.25.34"), 0, 0, 0},
-					dstPort: uint16(80),
+					conSrcDstInfo: connectionSrcDst{
+						// sleep
+						src:     [4]uint32{nets.ConvertIpToUint32("10.19.25.33"), 0, 0, 0},
+						srcPort: uint16(49875),
+						// solely workload
+						dst:     [4]uint32{nets.ConvertIpToUint32("10.19.25.34"), 0, 0, 0},
+						dstPort: uint16(80),
+					},
 					// httpbin service
 					origDstAddr:   [4]uint32{nets.ConvertIpToUint32("10.19.25.34"), 0, 0, 0},
 					origDstPort:   uint16(80),
@@ -1031,6 +1057,8 @@ func TestMetricController_updatePrometheusMetric(t *testing.T) {
 				WorkloadConnFailed:        3,
 				WorkloadConnSentBytes:     4,
 				WorkloadConnReceivedBytes: 5,
+				WorkloadConnPacketLost:    6,
+				WorkloadConnTotalRetrans:  7,
 			},
 			serviceMetricCache: serviceMetricInfo{
 				ServiceConnOpened:        6,
diff --git a/test/bpf_ut/.gitignore b/test/bpf_ut/.gitignore
new file mode 100644
index 00000000..cc62e439
--- /dev/null
+++ b/test/bpf_ut/.gitignore
@@ -0,0 +1,2 @@
+*.d
+*.o
\ No newline at end of file
diff --git a/test/bpf_ut/Makefile b/test/bpf_ut/Makefile
new file mode 100644
index 00000000..4efad7a9
--- /dev/null
+++ b/test/bpf_ut/Makefile
@@ -0,0 +1,76 @@
+MAKEFLAGS += -r
+
+QUIET ?= @
+CLANG ?= clang
+GO ?= go
+
+ROOT_DIR := $(shell git rev-parse --show-toplevel)
+TEST_DIR := $(ROOT_DIR)/test/bpf_ut
+
+FLAGS := -isystem /usr/include -I/usr/local/include
+FLAGS += -I./include  -I$(ROOT_DIR)/bpf/include  
+FLAGS += -fPIC -D__NR_CPUS__=$(shell nproc --all) -D__TARGET_ARCH_x86_64 -D__x86_64__ -D_GNU_SOURCE -DKERNEL_VERSION_HIGHER_5_13_0=1
+FLAGS += -O2 -g
+
+CLANG_FLAGS := ${FLAGS} --target=bpf -std=gnu99
+# eBPF verifier enforces unaligned access checks where necessary, so don't
+# let clang complain too early.
+CLANG_FLAGS += -Wall -Wextra
+CLANG_FLAGS += -Wno-address-of-packed-member
+CLANG_FLAGS += -Wno-unknown-warning-option
+CLANG_FLAGS += -Wno-gnu-variable-sized-type-not-at-end
+CLANG_FLAGS += -Wenum-conversion
+CLANG_FLAGS += -Wimplicit-fallthrough
+# Create dependency files for each .o file.
+CLANG_FLAGS += -MD
+
+.PHONY: all clean run
+
+TEST_OBJECTS = xdp_shutdown_in_userspace_test.o xdp_authz_offload_test.o
+
+XDP_FLAGS = -I$(ROOT_DIR)/bpf/kmesh/ -I$(ROOT_DIR)/bpf/kmesh/workload/include -I$(ROOT_DIR)/api/v2-c
+xdp_%.o: xdp_%.c clean
+	$(QUIET) $(CLANG) $(CLANG_FLAGS) $(XDP_FLAGS) -c $< -o $@
+
+
+all: $(TEST_OBJECTS)
+
+clean:
+	$(QUIET) rm -f $(wildcard *.o)
+	$(QUIET) rm -f $(wildcard *.d)
+	$(QUIET) echo "clean all object files"
+
+BPF_TEST_FLAGS:=
+ifneq ($(shell id -u), 0)
+		BPF_TEST_FLAGS += -exec "sudo -E"
+endif
+ifeq ($(V),1)
+    BPF_TEST_FLAGS += -test.v
+endif
+ifeq ($(COVER),1)
+	ifndef COVERFORMAT
+		COVERFORMAT:=html
+	endif
+    BPF_TEST_FLAGS += -coverage-report $(ROOT_DIR)/bpf-coverage.$(COVERFORMAT) -coverage-format $(COVERFORMAT)
+ifdef NOCOVER
+    BPF_TEST_FLAGS += -no-test-coverage "$(NOCOVER)"
+endif
+endif
+ifeq ($(INSTRLOG),1)
+    BPF_TEST_FLAGS += -instrumentation-log $(ROOT_DIR)/test/bpf-instrumentation.log
+endif
+ifdef RUN
+    BPF_TEST_FLAGS += -run $(RUN)
+endif
+ifdef BPF_TEST_DUMP_CTX
+    BPF_TEST_FLAGS += -dump-ctx
+endif
+ifdef BPF_TEST_FILE
+	BPF_TEST_FLAGS += -test $(BPF_TEST_FILE)
+endif
+
+run: all
+	$(QUIET)echo "rebuild and run bpf tests"
+	$(GO) test ./bpftest -bpf-ut-path $(ROOT_DIR)/test/bpf_ut $(BPF_TEST_FLAGS)
+
+-include $(TEST_OBJECTS:.o=.d)
diff --git a/test/bpf_ut/bpftest/bpf_test.go b/test/bpf_ut/bpftest/bpf_test.go
new file mode 100644
index 00000000..cc12fb24
--- /dev/null
+++ b/test/bpf_ut/bpftest/bpf_test.go
@@ -0,0 +1,521 @@
+/*
+ * Copyright The Kmesh Authors.
+ *
+ * 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.
+ */
+
+package bpftests
+
+//go:generate protoc --go_out=. trf.proto
+
+import (
+	"context"
+	"encoding/binary"
+	"encoding/hex"
+	"errors"
+	"flag"
+	"fmt"
+	"path"
+	"regexp"
+	"strings"
+	"testing"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/rlimit"
+	"github.com/davecgh/go-spew/spew"
+	"github.com/vishvananda/netlink/nl"
+	"google.golang.org/protobuf/encoding/protowire"
+	"google.golang.org/protobuf/proto"
+
+	"kmesh.net/kmesh/pkg/bpf/factory"
+	"kmesh.net/kmesh/pkg/logger"
+	"kmesh.net/kmesh/pkg/utils"
+)
+
+var (
+	testPath = flag.String("bpf-ut-path", "", "Path to the eBPF unit tests")
+	dumpCtx  = flag.Bool("dump-ctx", false, "If set, the program context will be dumped after a CHECK and SETUP run.")
+)
+
+type unitTest struct {
+	name             string
+	setupInUserSpace func(t *testing.T, coll *ebpf.Collection) // Builds test environment in user space, used for operations that cannot be completed in kernel space (e.g., cannot pass the eBPF verifier)
+}
+
+type unitTests struct {
+	objFilename string
+	uts         []unitTest
+}
+
+func (uts *unitTests) run() func(t *testing.T) {
+	return func(t *testing.T) {
+		for _, ut := range uts.uts {
+			t.Run(ut.name, func(t *testing.T) {
+				loadAndRunSpec(t, uts.objFilename, &ut)
+			})
+		}
+	}
+}
+
+func TestBPF(t *testing.T) {
+	if testPath == nil || *testPath == "" {
+		t.Skip("Set -bpf-ut-path to run BPF tests")
+	}
+
+	if err := rlimit.RemoveMemlock(); err != nil {
+		t.Log(err)
+	}
+
+	t.Run("Workload", testWorkload)
+}
+
+// common functions
+
+func loadAndRunSpec(t *testing.T, objFilename string, tt *unitTest) {
+	elfPath := path.Join(*testPath, objFilename)
+	t.Logf("Running test %s", elfPath)
+
+	spec := loadAndPrepSpec(t, elfPath)
+
+	var (
+		coll *ebpf.Collection
+		err  error
+	)
+
+	// Load the eBPF collection into the kernel
+	coll, err = ebpf.NewCollection(spec)
+
+	// Check for errors, specifically handle eBPF verifier errors separately
+	// as they provide more detailed diagnostics
+	if err != nil {
+		var ve *ebpf.VerifierError
+		if errors.As(err, &ve) {
+			t.Fatalf("verifier error: %+v", ve)
+		} else {
+			t.Fatal("loading collection:", err)
+		}
+	}
+	defer coll.Close()
+
+	// Iterate through all programs and organize them by test name and function type
+	// based on their section names matching the pattern "<test_name>/(check|jump|pktgen)"
+	programs := programSet{}
+	for progName, spec := range spec.Programs {
+		match := checkProgRegex.FindStringSubmatch(spec.SectionName)
+		if len(match) == 0 || match[1] != tt.name {
+			continue
+		}
+
+		switch match[2] {
+		case "pktgen":
+			programs.pktgenProg = coll.Programs[progName]
+		case "jump":
+			programs.jumpProg = coll.Programs[progName]
+		case "check":
+			programs.checkProg = coll.Programs[progName]
+		default:
+			t.Fatalf("Unknown program type '%s' for program '%s' in section '%s'",
+				match[2], progName, spec.SectionName)
+		}
+	}
+
+	// Ensure test that has a jump program also has a check program
+	if programs.checkProg == nil {
+		t.Fatalf(
+			"File '%s' contains a jump program in section '%s' but no check program.",
+			elfPath,
+			tt.name,
+		)
+	}
+
+	if !utils.KernelVersionLowerThan5_13() {
+		// TODO: use t.Context() instead of context.Background() when go 1.24 is required
+		logger.StartLogReader(context.Background(), coll.Maps["km_log_event"])
+	}
+	if tt.setupInUserSpace != nil {
+		tt.setupInUserSpace(t, coll)
+	}
+
+	// run the test
+	subTest(t, programs, coll.Maps[suiteResultMap])
+}
+
+// loadAndPrepSpec loads an eBPF Collection Specification from the provided ELF file
+// and prepares it for testing. It disables pinning for all maps to avoid interference
+// between tests. Additionally, it filters out programs that don't support BPF_PROG_RUN,
+// keeping only XDP, SchedACT, and SchedCLS type programs.
+//
+// Parameters:
+//   - t: The testing context, used for logging and reporting failures
+//   - elfPath: Path to the ELF file containing the eBPF program
+//
+// Returns:
+//   - *ebpf.CollectionSpec: The prepared collection specification
+//
+// The function will call t.Fatalf if loading the specification fails.
+func loadAndPrepSpec(t *testing.T, elfPath string) *ebpf.CollectionSpec {
+	spec, err := ebpf.LoadCollectionSpec(elfPath)
+	if err != nil {
+		t.Fatalf("load spec %s: %v", elfPath, err)
+	}
+
+	// Unpin all maps, as we don't want to interfere with other tests
+	for _, m := range spec.Maps {
+		m.Pinning = ebpf.PinNone
+	}
+
+	for n, p := range spec.Programs {
+		switch p.Type {
+		// https://docs.ebpf.io/linux/syscall/BPF_PROG_TEST_RUN/
+		case ebpf.XDP, ebpf.SchedACT, ebpf.SchedCLS, ebpf.SocketFilter, ebpf.CGroupSKB:
+			continue
+		}
+
+		t.Logf("Skipping program '%s' of type '%s': BPF_PROG_RUN not supported", p.Name, p.Type)
+		delete(spec.Programs, n)
+	}
+
+	return spec
+}
+
+// setBpfConfig sets the BPF configuration variables in the eBPF collection
+// based on the provided GlobalBpfConfig. It sets the log level and authorization
+// offload settings.
+func setBpfConfig(t *testing.T, coll *ebpf.Collection, config *factory.GlobalBpfConfig) {
+	if err := coll.Variables["bpf_log_level"].Set(&config.BpfLogLevel); err != nil {
+		t.Fatalf("failed to set bpf_log_level: %v", err)
+	}
+	if err := coll.Variables["authz_offload"].Set(&config.AuthzOffload); err != nil {
+		t.Fatalf("failed to set authz_offload: %v", err)
+	}
+}
+
+// registerTailCall registers a tail call in the eBPF collection by updating the specified
+// tail call map at the given index with the file descriptor of the named program.
+//
+// Parameters:
+//   - t: Testing context for logging and error reporting
+//   - coll: eBPF collection containing maps and programs
+//   - tail_call_map_name: Name of the tail call map to update
+//   - index: Index in the map where the program reference should be stored
+//   - tail_call_prog_name: Name of the program to be called
+//
+// If the map or program is not found in the collection, the function logs the issue and
+// continues without error. This is expected behavior when testing modules that don't
+// require the specific tail call functionality.
+//
+// The function will only fail the test if the map and program exist but the update operation
+// encounters an error.
+func registerTailCall(t *testing.T, coll *ebpf.Collection, tail_call_map_name string, index uint32, tail_call_prog_name string) {
+	if tailCallMap, ok := coll.Maps[tail_call_map_name]; ok {
+		if prog, ok := coll.Programs[tail_call_prog_name]; ok {
+			if err := tailCallMap.Update(
+				index,
+				uint32(prog.FD()),
+				ebpf.UpdateAny); err != nil {
+				t.Fatalf("Failed to register tail call: %v", err)
+			}
+			t.Logf("Successfully registered tail call %s -> %s[%d]", tail_call_prog_name, tail_call_map_name, index)
+		} else {
+			t.Logf("Program %s not found in collection", tail_call_prog_name)
+		}
+	} else {
+		t.Logf("Map %s not found in collection", tail_call_map_name)
+	}
+}
+
+type programSet struct {
+	pktgenProg *ebpf.Program
+	jumpProg   *ebpf.Program
+	checkProg  *ebpf.Program
+}
+
+var checkProgRegex = regexp.MustCompile(`[^/]+/test/([^/]+)/((?:check)|(?:jump)|(?:pktgen))`)
+
+const (
+	ResultSuccess = 1
+
+	suiteResultMap = "suite_result_map"
+)
+
+func subTest(t *testing.T, progSet programSet, resultMap *ebpf.Map) {
+	// create ctx with the max allowed size(4k - head room - tailroom)
+	data := make([]byte, 4096-256-320)
+
+	// ctx is only used for tc programs
+	// non-empty ctx passed to non-tc programs will cause error: invalid argument
+	ctx := make([]byte, 0)
+	if progSet.checkProg.Type() == ebpf.SchedCLS {
+		// sizeof(struct __sk_buff) < 256, let's make it 256
+		ctx = make([]byte, 256)
+	}
+
+	var (
+		statusCode uint32
+		err        error
+	)
+	if progSet.pktgenProg != nil {
+		if _, data, ctx, err = runBpfProgram(progSet.pktgenProg, data, ctx); err != nil {
+			t.Fatalf("error while running pktgen prog: %s", err)
+		}
+
+		if *dumpCtx {
+			t.Log("Pktgen returned status: ")
+			t.Log(statusCode)
+			t.Log("data after pktgen: ")
+			t.Log(spew.Sdump(data))
+			t.Log("ctx after pktgen: ")
+			t.Log(spew.Sdump(ctx))
+		}
+	}
+
+	if progSet.jumpProg != nil {
+		if statusCode, data, ctx, err = runBpfProgram(progSet.jumpProg, data, ctx); err != nil {
+			t.Fatalf("error while running jump prog: %s", err)
+		}
+
+		if *dumpCtx {
+			t.Log("Jump returned status: ")
+			t.Log(statusCode)
+			t.Log("data after jump: ")
+			t.Log(spew.Sdump(data))
+			t.Log("ctx after jump: ")
+			t.Log(spew.Sdump(ctx))
+		}
+
+		// Write the return value from ebpf program as status code into the first 4 bytes of data
+		status := make([]byte, 4)
+		nl.NativeEndian().PutUint32(status, statusCode)
+		data = append(status, data...)
+	}
+
+	// Run check program
+	if statusCode, data, ctx, err = runBpfProgram(progSet.checkProg, data, ctx); err != nil {
+		t.Fatal("error while running check program:", err)
+	}
+
+	if *dumpCtx {
+		t.Log("Check returned status: ")
+		t.Log(statusCode)
+		t.Logf("data after check: %d", len(data))
+		t.Log(spew.Sdump(data))
+		t.Log("ctx after check: ")
+		t.Log(spew.Sdump(ctx))
+	}
+
+	// Clear map value after each test
+	defer func() {
+		for _, m := range []*ebpf.Map{resultMap} {
+			if m == nil {
+				continue
+			}
+
+			var key int32
+			value := make([]byte, m.ValueSize())
+			m.Lookup(&key, &value)
+			for i := 0; i < len(value); i++ {
+				value[i] = 0
+			}
+			m.Update(&key, &value, ebpf.UpdateAny)
+		}
+	}()
+
+	var key int32
+	value := make([]byte, resultMap.ValueSize())
+	err = resultMap.Lookup(&key, &value)
+	if err != nil {
+		t.Fatal("error while getting suite result:", err)
+	}
+
+	// Detect the length of the result, since the proto.Unmarshal doesn't like trailing zeros.
+	valueLen := 0
+	valueC := value
+	for {
+		_, _, len := protowire.ConsumeField(valueC)
+		if len <= 0 {
+			break
+		}
+		valueLen += len
+		valueC = valueC[len:]
+	}
+
+	result := &SuiteResult{}
+	err = proto.Unmarshal(value[:valueLen], result)
+	if err != nil {
+		t.Fatal("error while unmarshalling suite result:", err)
+	}
+
+	for _, testResult := range result.Results {
+		// Remove the C-string, null-terminator.
+		name := strings.TrimSuffix(testResult.Name, "\x00")
+		t.Run(name, func(tt *testing.T) {
+			if len(testResult.TestLog) > 0 && testing.Verbose() || testResult.Status != SuiteResult_TestResult_PASS {
+				for _, log := range testResult.TestLog {
+					tt.Logf("%s", log.FmtString())
+				}
+			}
+
+			switch testResult.Status {
+			case SuiteResult_TestResult_ERROR:
+				tt.Fatal("Test failed due to unknown error in test framework")
+			case SuiteResult_TestResult_FAIL:
+				tt.Fail()
+			case SuiteResult_TestResult_SKIP:
+				tt.Skip()
+			}
+		})
+	}
+
+	if len(result.SuiteLog) > 0 && testing.Verbose() ||
+		SuiteResult_TestResult_TestStatus(statusCode) != SuiteResult_TestResult_PASS {
+		for _, log := range result.SuiteLog {
+			t.Logf("%s", log.FmtString())
+		}
+	}
+
+	switch SuiteResult_TestResult_TestStatus(statusCode) {
+	case SuiteResult_TestResult_ERROR:
+		t.Fatal("Test failed due to unknown error in test framework")
+	case SuiteResult_TestResult_FAIL:
+		t.Fail()
+	case SuiteResult_TestResult_SKIP:
+		t.SkipNow()
+	}
+}
+
+// A simplified version of fmt.Printf logic, the meaning of % specifiers changed to match the kernels printk specifiers.
+// In the eBPF code a user can for example call `test_log("expected 123, got %llu", some_val)` the %llu meaning
+// long-long-unsigned translates into a uint64, the rendered out would for example be -> 'expected 123, got 234'.
+// https://www.kernel.org/doc/Documentation/printk-formats.txt
+// https://github.com/libbpf/libbpf/blob/4eb6485c08867edaa5a0a81c64ddb23580420340/src/bpf_helper_defs.h#L152
+func (l *Log) FmtString() string {
+	var sb strings.Builder
+
+	end := len(l.Fmt)
+	argNum := 0
+
+	for i := 0; i < end; {
+		lasti := i
+		for i < end && l.Fmt[i] != '%' {
+			i++
+		}
+		if i > lasti {
+			sb.WriteString(strings.TrimSuffix(l.Fmt[lasti:i], "\x00"))
+		}
+		if i >= end {
+			// done processing format string
+			break
+		}
+
+		// Process one verb
+		i++
+
+		var spec []byte
+	loop:
+		for ; i < end; i++ {
+			c := l.Fmt[i]
+			switch c {
+			case 'd', 'i', 'u', 'x', 's':
+				spec = append(spec, c)
+				break loop
+			case 'l':
+				spec = append(spec, c)
+			default:
+				break loop
+			}
+		}
+		// Advance to to next char
+		i++
+
+		// No argument left over to print for the current verb.
+		if argNum >= len(l.Args) {
+			sb.WriteString("%!")
+			sb.WriteString(string(spec))
+			sb.WriteString("(MISSING)")
+			continue
+		}
+
+		switch string(spec) {
+		case "u":
+			fmt.Fprint(&sb, uint16(l.Args[argNum]))
+		case "d", "i", "s":
+			fmt.Fprint(&sb, int16(l.Args[argNum]))
+		case "x":
+			hb := make([]byte, 2)
+			binary.BigEndian.PutUint16(hb, uint16(l.Args[argNum]))
+			fmt.Fprint(&sb, hex.EncodeToString(hb))
+
+		case "lu":
+			fmt.Fprint(&sb, uint32(l.Args[argNum]))
+		case "ld", "li", "ls":
+			fmt.Fprint(&sb, int32(l.Args[argNum]))
+		case "lx":
+			hb := make([]byte, 4)
+			binary.BigEndian.PutUint32(hb, uint32(l.Args[argNum]))
+			fmt.Fprint(&sb, hex.EncodeToString(hb))
+
+		case "llu":
+			fmt.Fprint(&sb, uint64(l.Args[argNum]))
+		case "lld", "lli", "lls":
+			fmt.Fprint(&sb, int64(l.Args[argNum]))
+		case "llx":
+			hb := make([]byte, 8)
+			binary.BigEndian.PutUint64(hb, uint64(l.Args[argNum]))
+			fmt.Fprint(&sb, hex.EncodeToString(hb))
+
+		default:
+			sb.WriteString("%!")
+			sb.WriteString(string(spec))
+			sb.WriteString("(INVALID)")
+			continue
+		}
+
+		argNum++
+	}
+
+	return sb.String()
+}
+
+// runBpfProgram executes an eBPF program with the provided data and context.
+//
+// Parameters:
+//   - prog: A pointer to the eBPF program to execute.
+//   - data: Input data buffer for the eBPF program.
+//   - ctx: Input context buffer for the eBPF program.
+//
+// Returns:
+//   - statusCode: The return value from the eBPF program execution.
+//   - dataOut: The modified data buffer after program execution.
+//   - ctxOut: The modified context buffer after program execution.
+//   - err: An error if program execution fails.
+//
+// The function allocates sufficient space for output buffers, with additional padding
+// for the data buffer to accommodate potential size increases during program execution.
+// It runs the BPF program exactly once (Repeat: 1).
+func runBpfProgram(prog *ebpf.Program, data, ctx []byte) (statusCode uint32, dataOut, ctxOut []byte, err error) {
+	dataOut = make([]byte, len(data))
+	if len(dataOut) > 0 {
+		// See comments at https://github.com/cilium/ebpf/blob/20c4d8896bdde990ce6b80d59a4262aa3ccb891d/prog.go#L563-L567
+		dataOut = make([]byte, len(data)+256+2)
+	}
+	ctxOut = make([]byte, len(ctx))
+	opts := &ebpf.RunOptions{
+		Data:       data,
+		DataOut:    dataOut,
+		Context:    ctx,
+		ContextOut: ctxOut,
+		Repeat:     1,
+	}
+	ret, err := prog.Run(opts)
+	return ret, opts.DataOut, ctxOut, err
+}
diff --git a/test/bpf_ut/bpftest/trf.pb.go b/test/bpf_ut/bpftest/trf.pb.go
new file mode 100644
index 00000000..4e2f5344
--- /dev/null
+++ b/test/bpf_ut/bpftest/trf.pb.go
@@ -0,0 +1,345 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.36.5
+// 	protoc        v4.25.1
+// source: trf.proto
+
+/*
+ * Copyright The Kmesh Authors.
+ *
+ * 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.
+ */
+
+package bpftests
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+	unsafe "unsafe"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type SuiteResult_TestResult_TestStatus int32
+
+const (
+	// Unable to execute test, not indicative of the test itself, usually an unexpected error in the framework
+	// like out of memory conditions.
+	SuiteResult_TestResult_ERROR SuiteResult_TestResult_TestStatus = 0
+	SuiteResult_TestResult_PASS  SuiteResult_TestResult_TestStatus = 1
+	SuiteResult_TestResult_FAIL  SuiteResult_TestResult_TestStatus = 2
+	// The test was skipped, for example because the feature under test is disabled or unavailable with the
+	// current settings or on this platform.
+	SuiteResult_TestResult_SKIP SuiteResult_TestResult_TestStatus = 3
+)
+
+// Enum value maps for SuiteResult_TestResult_TestStatus.
+var (
+	SuiteResult_TestResult_TestStatus_name = map[int32]string{
+		0: "ERROR",
+		1: "PASS",
+		2: "FAIL",
+		3: "SKIP",
+	}
+	SuiteResult_TestResult_TestStatus_value = map[string]int32{
+		"ERROR": 0,
+		"PASS":  1,
+		"FAIL":  2,
+		"SKIP":  3,
+	}
+)
+
+func (x SuiteResult_TestResult_TestStatus) Enum() *SuiteResult_TestResult_TestStatus {
+	p := new(SuiteResult_TestResult_TestStatus)
+	*p = x
+	return p
+}
+
+func (x SuiteResult_TestResult_TestStatus) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (SuiteResult_TestResult_TestStatus) Descriptor() protoreflect.EnumDescriptor {
+	return file_trf_proto_enumTypes[0].Descriptor()
+}
+
+func (SuiteResult_TestResult_TestStatus) Type() protoreflect.EnumType {
+	return &file_trf_proto_enumTypes[0]
+}
+
+func (x SuiteResult_TestResult_TestStatus) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use SuiteResult_TestResult_TestStatus.Descriptor instead.
+func (SuiteResult_TestResult_TestStatus) EnumDescriptor() ([]byte, []int) {
+	return file_trf_proto_rawDescGZIP(), []int{0, 0, 0}
+}
+
+// TRF (Test Result Format)
+type SuiteResult struct {
+	state         protoimpl.MessageState    `protogen:"open.v1"`
+	Results       []*SuiteResult_TestResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"`
+	SuiteLog      []*Log                    `protobuf:"bytes,2,rep,name=suite_log,json=suiteLog,proto3" json:"suite_log,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *SuiteResult) Reset() {
+	*x = SuiteResult{}
+	mi := &file_trf_proto_msgTypes[0]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *SuiteResult) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SuiteResult) ProtoMessage() {}
+
+func (x *SuiteResult) ProtoReflect() protoreflect.Message {
+	mi := &file_trf_proto_msgTypes[0]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SuiteResult.ProtoReflect.Descriptor instead.
+func (*SuiteResult) Descriptor() ([]byte, []int) {
+	return file_trf_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *SuiteResult) GetResults() []*SuiteResult_TestResult {
+	if x != nil {
+		return x.Results
+	}
+	return nil
+}
+
+func (x *SuiteResult) GetSuiteLog() []*Log {
+	if x != nil {
+		return x.SuiteLog
+	}
+	return nil
+}
+
+// bpf_trace_printk style logging
+type Log struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Fmt           string                 `protobuf:"bytes,1,opt,name=fmt,proto3" json:"fmt,omitempty"`
+	Args          []uint64               `protobuf:"fixed64,2,rep,packed,name=args,proto3" json:"args,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *Log) Reset() {
+	*x = Log{}
+	mi := &file_trf_proto_msgTypes[1]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *Log) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Log) ProtoMessage() {}
+
+func (x *Log) ProtoReflect() protoreflect.Message {
+	mi := &file_trf_proto_msgTypes[1]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Log.ProtoReflect.Descriptor instead.
+func (*Log) Descriptor() ([]byte, []int) {
+	return file_trf_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Log) GetFmt() string {
+	if x != nil {
+		return x.Fmt
+	}
+	return ""
+}
+
+func (x *Log) GetArgs() []uint64 {
+	if x != nil {
+		return x.Args
+	}
+	return nil
+}
+
+type SuiteResult_TestResult struct {
+	state         protoimpl.MessageState            `protogen:"open.v1"`
+	Name          string                            `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	Status        SuiteResult_TestResult_TestStatus `protobuf:"varint,2,opt,name=status,proto3,enum=SuiteResult_TestResult_TestStatus" json:"status,omitempty"`
+	TestLog       []*Log                            `protobuf:"bytes,3,rep,name=test_log,json=testLog,proto3" json:"test_log,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *SuiteResult_TestResult) Reset() {
+	*x = SuiteResult_TestResult{}
+	mi := &file_trf_proto_msgTypes[2]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *SuiteResult_TestResult) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SuiteResult_TestResult) ProtoMessage() {}
+
+func (x *SuiteResult_TestResult) ProtoReflect() protoreflect.Message {
+	mi := &file_trf_proto_msgTypes[2]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SuiteResult_TestResult.ProtoReflect.Descriptor instead.
+func (*SuiteResult_TestResult) Descriptor() ([]byte, []int) {
+	return file_trf_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *SuiteResult_TestResult) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *SuiteResult_TestResult) GetStatus() SuiteResult_TestResult_TestStatus {
+	if x != nil {
+		return x.Status
+	}
+	return SuiteResult_TestResult_ERROR
+}
+
+func (x *SuiteResult_TestResult) GetTestLog() []*Log {
+	if x != nil {
+		return x.TestLog
+	}
+	return nil
+}
+
+var File_trf_proto protoreflect.FileDescriptor
+
+var file_trf_proto_rawDesc = string([]byte{
+	0x0a, 0x09, 0x74, 0x72, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x02, 0x0a, 0x0b,
+	0x53, 0x75, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x31, 0x0a, 0x07, 0x72,
+	0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x53,
+	0x75, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52,
+	0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x21,
+	0x0a, 0x09, 0x73, 0x75, 0x69, 0x74, 0x65, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x02, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x04, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x08, 0x73, 0x75, 0x69, 0x74, 0x65, 0x4c, 0x6f,
+	0x67, 0x1a, 0xb4, 0x01, 0x0a, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74,
+	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x53, 0x75, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x75,
+	0x6c, 0x74, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x54, 0x65,
+	0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
+	0x12, 0x1f, 0x0a, 0x08, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x03, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x07, 0x74, 0x65, 0x73, 0x74, 0x4c, 0x6f,
+	0x67, 0x22, 0x35, 0x0a, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,
+	0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x50, 0x41,
+	0x53, 0x53, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x02, 0x12, 0x08,
+	0x0a, 0x04, 0x53, 0x4b, 0x49, 0x50, 0x10, 0x03, 0x22, 0x2b, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12,
+	0x10, 0x0a, 0x03, 0x66, 0x6d, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x66, 0x6d,
+	0x74, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x06, 0x52,
+	0x04, 0x61, 0x72, 0x67, 0x73, 0x42, 0x0c, 0x5a, 0x0a, 0x2e, 0x3b, 0x62, 0x70, 0x66, 0x74, 0x65,
+	0x73, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+})
+
+var (
+	file_trf_proto_rawDescOnce sync.Once
+	file_trf_proto_rawDescData []byte
+)
+
+func file_trf_proto_rawDescGZIP() []byte {
+	file_trf_proto_rawDescOnce.Do(func() {
+		file_trf_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_trf_proto_rawDesc), len(file_trf_proto_rawDesc)))
+	})
+	return file_trf_proto_rawDescData
+}
+
+var file_trf_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_trf_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_trf_proto_goTypes = []any{
+	(SuiteResult_TestResult_TestStatus)(0), // 0: SuiteResult.TestResult.TestStatus
+	(*SuiteResult)(nil),                    // 1: SuiteResult
+	(*Log)(nil),                            // 2: Log
+	(*SuiteResult_TestResult)(nil),         // 3: SuiteResult.TestResult
+}
+var file_trf_proto_depIdxs = []int32{
+	3, // 0: SuiteResult.results:type_name -> SuiteResult.TestResult
+	2, // 1: SuiteResult.suite_log:type_name -> Log
+	0, // 2: SuiteResult.TestResult.status:type_name -> SuiteResult.TestResult.TestStatus
+	2, // 3: SuiteResult.TestResult.test_log:type_name -> Log
+	4, // [4:4] is the sub-list for method output_type
+	4, // [4:4] is the sub-list for method input_type
+	4, // [4:4] is the sub-list for extension type_name
+	4, // [4:4] is the sub-list for extension extendee
+	0, // [0:4] is the sub-list for field type_name
+}
+
+func init() { file_trf_proto_init() }
+func file_trf_proto_init() {
+	if File_trf_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: unsafe.Slice(unsafe.StringData(file_trf_proto_rawDesc), len(file_trf_proto_rawDesc)),
+			NumEnums:      1,
+			NumMessages:   3,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_trf_proto_goTypes,
+		DependencyIndexes: file_trf_proto_depIdxs,
+		EnumInfos:         file_trf_proto_enumTypes,
+		MessageInfos:      file_trf_proto_msgTypes,
+	}.Build()
+	File_trf_proto = out.File
+	file_trf_proto_goTypes = nil
+	file_trf_proto_depIdxs = nil
+}
diff --git a/test/bpf_ut/bpftest/trf.proto b/test/bpf_ut/bpftest/trf.proto
new file mode 100644
index 00000000..3e1f21ce
--- /dev/null
+++ b/test/bpf_ut/bpftest/trf.proto
@@ -0,0 +1,31 @@
+syntax = "proto3";
+option go_package = ".;bpftests";
+
+// TRF (Test Result Format)
+message SuiteResult {
+    message TestResult {
+        enum TestStatus {
+            // Unable to execute test, not indicative of the test itself, usually an unexpected error in the framework
+            // like out of memory conditions.
+            ERROR = 0;
+            PASS = 1;
+            FAIL = 2;
+            // The test was skipped, for example because the feature under test is disabled or unavailable with the
+            // current settings or on this platform.
+            SKIP = 3;
+        }
+
+        string name = 1;
+        TestStatus status = 2;
+        repeated Log test_log = 3;
+    }
+
+    repeated TestResult results = 1;
+    repeated Log suite_log = 2;
+}
+
+// bpf_trace_printk style logging
+message Log {
+    string fmt = 1;
+    repeated fixed64 args = 2;
+}
diff --git a/test/bpf_ut/bpftest/workload_test.go b/test/bpf_ut/bpftest/workload_test.go
new file mode 100644
index 00000000..b96d349f
--- /dev/null
+++ b/test/bpf_ut/bpftest/workload_test.go
@@ -0,0 +1,281 @@
+//go:build linux && (amd64 || arm64) && !aix && !ppc64
+
+/*
+ * Copyright The Kmesh Authors.
+ *
+ * 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.
+ */
+
+package bpftests
+
+import (
+	"testing"
+
+	"github.com/cilium/ebpf"
+
+	"kmesh.net/kmesh/api/v2/workloadapi/security"
+	bpf2go "kmesh.net/kmesh/bpf/kmesh/bpf2go/dualengine"
+	"kmesh.net/kmesh/daemon/options"
+	"kmesh.net/kmesh/pkg/bpf/factory"
+	bpfUtils "kmesh.net/kmesh/pkg/bpf/utils"
+	bpfWorkload "kmesh.net/kmesh/pkg/bpf/workload"
+	"kmesh.net/kmesh/pkg/cache/v2/maps"
+	"kmesh.net/kmesh/pkg/constants"
+	controllerWorkload "kmesh.net/kmesh/pkg/controller/workload"
+	"kmesh.net/kmesh/pkg/controller/workload/bpfcache"
+)
+
+func testWorkload(t *testing.T) {
+	t.Run("XDP", testXDP)
+}
+
+func testXDP(t *testing.T) {
+	XDPtests := []unitTests{
+		{
+			objFilename: "xdp_shutdown_in_userspace_test.o",
+			uts: []unitTest{
+				{
+					name: "1_shutdown_in_userspace__should_shutdown",
+					setupInUserSpace: func(t *testing.T, coll *ebpf.Collection) {
+						workload_xdp_registerTailCall(t, coll)
+						setBpfConfig(t, coll, &factory.GlobalBpfConfig{
+							BpfLogLevel:  constants.BPF_LOG_DEBUG,
+							AuthzOffload: constants.DISABLED,
+						})
+					},
+				},
+				{
+					name: "2_shutdown_in_userspace__should_not_shutdown",
+					setupInUserSpace: func(t *testing.T, coll *ebpf.Collection) {
+						workload_xdp_registerTailCall(t, coll)
+						setBpfConfig(t, coll, &factory.GlobalBpfConfig{
+							BpfLogLevel:  constants.BPF_LOG_DEBUG,
+							AuthzOffload: constants.DISABLED,
+						})
+					},
+				},
+			},
+		},
+		{
+			objFilename: "xdp_authz_offload_test.o",
+			uts: []unitTest{
+				{
+					name: "3_deny_policy_matched",
+					setupInUserSpace: func(t *testing.T, coll *ebpf.Collection) {
+						// xdp_authz(struct xdp_md *ctx) -tail call->
+						// policies_check(struct xdp_md *ctx) -tail call->
+						// policy_check(struct xdp_md *ctx)
+						//   if(matched)
+						//     return match_ctx->action == ISTIO__SECURITY__ACTION__DENY ? XDP_DROP : XDP_PASS;
+
+						setBpfConfig(t, coll, &factory.GlobalBpfConfig{
+							BpfLogLevel:  constants.BPF_LOG_DEBUG,
+							AuthzOffload: constants.ENABLED,
+						})
+
+						workload_xdp_registerTailCall(t, coll)
+						workload_setMapsEnv(t, coll)
+						if workload, err := bpfWorkload.NewBpfWorkload(&options.BpfConfig{
+							Mode:        constants.DualEngineMode,
+							BpfFsPath:   constants.BpfFsPath,
+							Cgroup2Path: constants.Cgroup2Path,
+						}); err != nil {
+							t.Fatalf("NewBpfWorkload failed: %v", err)
+						} else {
+							if err := workload.DeserialInit(); err != nil {
+								t.Fatalf("DeserialInit failed: %v", err)
+							}
+						}
+						workloadProcessor := controllerWorkload.NewProcessor(bpf2go.KmeshCgroupSockWorkloadMaps{
+							KmWlpolicy: coll.Maps["km_wlpolicy"],
+							KmFrontend: coll.Maps["km_frontend"],
+						})
+
+						workloadbpf := workloadProcessor.GetBpfCache()
+						if err := workloadbpf.FrontendUpdate(&bpfcache.FrontendKey{
+							Ip: [16]byte{10, 1, 0, 15},
+						}, &bpfcache.FrontendValue{
+							UpstreamId: 0x01,
+						}); err != nil {
+							t.Fatalf("FrontendUpdate failed: %v", err)
+						}
+						if err := workloadbpf.WorkloadPolicyUpdate(&bpfcache.WorkloadPolicyKey{
+							WorklodId: 0x01,
+						}, &bpfcache.WorkloadPolicyValue{
+							PolicyIds: [4]uint32{1},
+						}); err != nil {
+							t.Fatalf("WorkloadPolicyUpdate failed: %v", err)
+						}
+
+						// set policy
+						policy_k := uint32(1)
+						denyPolicy := &security.Authorization{
+							Name:   "bpfut_deny__10.0.0.15->10.1.0.15:80",
+							Action: security.Action_DENY,
+							Rules: []*security.Rule{
+								{
+									Clauses: []*security.Clause{
+										{
+											Matches: []*security.Match{
+												{
+													SourceIps: []*security.Address{
+														{
+															Address: []byte{10, 0, 0, 15},
+															Length:  32,
+														},
+													},
+													DestinationIps: []*security.Address{
+														{
+															Address: []byte{10, 1, 0, 15},
+															Length:  32,
+														},
+													},
+													DestinationPorts: []uint32{80},
+												},
+											},
+										},
+									},
+								},
+							},
+						}
+						if err := maps.AuthorizationUpdate(policy_k, denyPolicy); err != nil {
+							t.Fatalf("AuthorizationUpdate failed: %v", err)
+						}
+					},
+				},
+				{
+					name: "4_allow_policy_matched",
+					setupInUserSpace: func(t *testing.T, coll *ebpf.Collection) {
+						// xdp_authz(struct xdp_md *ctx) -tail call->
+						// policies_check(struct xdp_md *ctx) -tail call->
+						// policy_check(struct xdp_md *ctx)
+						//   if(matched)
+						//     return match_ctx->action == ISTIO__SECURITY__ACTION__DENY ? XDP_DROP : XDP_PASS;
+
+						setBpfConfig(t, coll, &factory.GlobalBpfConfig{
+							BpfLogLevel:  constants.BPF_LOG_DEBUG,
+							AuthzOffload: constants.ENABLED,
+						})
+
+						workload_xdp_registerTailCall(t, coll)
+						workload_setMapsEnv(t, coll)
+						if workload, err := bpfWorkload.NewBpfWorkload(&options.BpfConfig{
+							Mode:        constants.DualEngineMode,
+							BpfFsPath:   constants.BpfFsPath,
+							Cgroup2Path: constants.Cgroup2Path,
+						}); err != nil {
+							t.Fatalf("NewBpfWorkload failed: %v", err)
+						} else {
+							if err := workload.DeserialInit(); err != nil {
+								t.Fatalf("DeserialInit failed: %v", err)
+							}
+						}
+						bpfmaps := bpf2go.KmeshCgroupSockWorkloadMaps{
+							KmWlpolicy: coll.Maps["km_wlpolicy"],
+							KmFrontend: coll.Maps["km_frontend"],
+						}
+						workloadProcessor := controllerWorkload.NewProcessor(bpfmaps)
+
+						workloadbpf := workloadProcessor.GetBpfCache()
+						if err := workloadbpf.FrontendUpdate(&bpfcache.FrontendKey{
+							Ip: [16]byte{10, 1, 0, 15},
+						}, &bpfcache.FrontendValue{
+							UpstreamId: 0x01,
+						}); err != nil {
+							t.Fatalf("FrontendUpdate failed: %v", err)
+						}
+						if err := workloadbpf.WorkloadPolicyUpdate(&bpfcache.WorkloadPolicyKey{
+							WorklodId: 0x01,
+						}, &bpfcache.WorkloadPolicyValue{
+							PolicyIds: [4]uint32{0},
+						}); err != nil {
+							t.Fatalf("WorkloadPolicyUpdate failed: %v", err)
+						}
+
+						policy_k := uint32(0)
+						allowPolicy := &security.Authorization{
+							Name:   "bpfut_allow__10.0.0.15->10.1.0.15:80",
+							Action: security.Action_ALLOW,
+							Rules: []*security.Rule{
+								{
+									Clauses: []*security.Clause{
+										{
+											Matches: []*security.Match{
+												{
+													SourceIps: []*security.Address{
+														{
+															Address: []byte{10, 0, 0, 15},
+															Length:  32,
+														},
+													},
+													DestinationIps: []*security.Address{
+														{
+															Address: []byte{10, 1, 0, 15},
+															Length:  32,
+														},
+													},
+													DestinationPorts: []uint32{80},
+												},
+											},
+										},
+									},
+								},
+							},
+						}
+						if err := maps.AuthorizationUpdate(policy_k, allowPolicy); err != nil {
+							t.Fatalf("AuthorizationUpdate failed: %v", err)
+						}
+					},
+				},
+			},
+		},
+	}
+
+	for _, tt := range XDPtests {
+		t.Run(tt.objFilename, tt.run())
+	}
+}
+
+// workload_xdp_registerTailCall registers the tail call for XDP programs.
+func workload_xdp_registerTailCall(t *testing.T, coll *ebpf.Collection) {
+	if coll == nil {
+		t.Fatal("coll is nil")
+	}
+	registerTailCall(t, coll, constants.XDPTailCallMap, constants.TailCallPoliciesCheck, "policies_check")
+	registerTailCall(t, coll, constants.XDPTailCallMap, constants.TailCallPolicyCheck, "policy_check")
+	registerTailCall(t, coll, constants.XDPTailCallMap, constants.TailCallAuthInUserSpace, "xdp_shutdown_in_userspace")
+}
+
+// workload_setMapsEnv prepares the BPF testing environment by configuring a BPF workload
+// and setting up environment variables for various BPF maps.
+func workload_setMapsEnv(t *testing.T, coll *ebpf.Collection) {
+	if coll == nil {
+		t.Fatal("coll is nil")
+	}
+
+	if err := bpfUtils.SetEnvByBpfMapId(coll.Maps["km_authz_policy"], "Authorization"); err != nil {
+		t.Fatalf("SetEnvByBpfMapId failed: %v", err)
+	}
+	if err := bpfUtils.SetEnvByBpfMapId(coll.Maps["kmesh_map64"], "KmeshMap64"); err != nil {
+		t.Fatalf("SetEnvByBpfMapId failed: %v", err)
+	}
+	if err := bpfUtils.SetEnvByBpfMapId(coll.Maps["kmesh_map192"], "KmeshMap192"); err != nil {
+		t.Fatalf("SetEnvByBpfMapId failed: %v", err)
+	}
+	if err := bpfUtils.SetEnvByBpfMapId(coll.Maps["kmesh_map296"], "KmeshMap296"); err != nil {
+		t.Fatalf("SetEnvByBpfMapId failed: %v", err)
+	}
+	if err := bpfUtils.SetEnvByBpfMapId(coll.Maps["kmesh_map1600"], "KmeshMap1600"); err != nil {
+		t.Fatalf("SetEnvByBpfMapId failed: %v", err)
+	}
+}
diff --git a/test/bpf_ut/include/ut_common.h b/test/bpf_ut/include/ut_common.h
new file mode 100644
index 00000000..4d8b830c
--- /dev/null
+++ b/test/bpf_ut/include/ut_common.h
@@ -0,0 +1,342 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/* Copyright Authors of Kmesh */
+
+#pragma once
+
+#include <linux/types.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+#define ENABLE_SERVICE_PROTOCOL_DIFFERENTIATION 1
+
+/* We can use this macro inside the actual datapath code
+ * to compile-in the code for testing. The primary usecase
+ * is initializing map-in-map or prog-map.
+ */
+#define BPF_TEST
+
+#ifndef __maybe_unused
+#define __maybe_unused __attribute__((__unused__))
+#endif
+
+#ifndef ___bpf_concat
+#define ___bpf_concat(a, b) a##b
+#endif
+#ifndef ___bpf_apply
+#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
+#endif
+#ifndef ___bpf_nth
+#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
+#endif
+#ifndef ___bpf_narg
+#define ___bpf_narg(...) ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#endif
+
+#define __bpf_log_arg0(ptr, arg)                                                                                       \
+    do {                                                                                                               \
+    } while (0)
+#define __bpf_log_arg1(ptr, arg)                                                                                       \
+    *(ptr++) = MKR_LOG_ARG;                                                                                            \
+    *(__u64 *)(ptr) = arg;                                                                                             \
+    ptr += sizeof(__u64)
+#define __bpf_log_arg2(ptr, arg, args...)                                                                              \
+    *(ptr++) = MKR_LOG_ARG;                                                                                            \
+    *(__u64 *)(ptr) = arg;                                                                                             \
+    ptr += sizeof(__u64);                                                                                              \
+    __bpf_log_arg1(ptr, args)
+#define __bpf_log_arg3(ptr, arg, args...)                                                                              \
+    *(ptr++) = MKR_LOG_ARG;                                                                                            \
+    *(__u64 *)(ptr) = arg;                                                                                             \
+    ptr += sizeof(__u64);                                                                                              \
+    __bpf_log_arg2(ptr, args)
+#define __bpf_log_arg4(ptr, arg, args...)                                                                              \
+    *(ptr++) = MKR_LOG_ARG;                                                                                            \
+    *(__u64 *)(ptr) = arg;                                                                                             \
+    ptr += sizeof(__u64);                                                                                              \
+    __bpf_log_arg3(ptr, args)
+#define __bpf_log_arg5(ptr, arg, args...)                                                                              \
+    *(ptr++) = MKR_LOG_ARG;                                                                                            \
+    *(__u64 *)(ptr) = arg;                                                                                             \
+    ptr += sizeof(__u64);                                                                                              \
+    __bpf_log_arg4(ptr, args)
+#define __bpf_log_arg6(ptr, arg, args...)                                                                              \
+    *(ptr++) = MKR_LOG_ARG;                                                                                            \
+    *(__u64 *)(ptr) = arg;                                                                                             \
+    ptr += sizeof(__u64);                                                                                              \
+    __bpf_log_arg5(ptr, args)
+#define __bpf_log_arg7(ptr, arg, args...)                                                                              \
+    *(ptr++) = MKR_LOG_ARG;                                                                                            \
+    *(__u64 *)(ptr) = arg;                                                                                             \
+    ptr += sizeof(__u64);                                                                                              \
+    __bpf_log_arg6(ptr, args)
+#define __bpf_log_arg8(ptr, arg, args...)                                                                              \
+    *(ptr++) = MKR_LOG_ARG;                                                                                            \
+    *(__u64 *)(ptr) = arg;                                                                                             \
+    ptr += sizeof(__u64);                                                                                              \
+    __bpf_log_arg7(ptr, args)
+#define __bpf_log_arg9(ptr, arg, args...)                                                                              \
+    *(ptr++) = MKR_LOG_ARG;                                                                                            \
+    *(__u64 *)(ptr) = arg;                                                                                             \
+    ptr += sizeof(__u64);                                                                                              \
+    __bpf_log_arg8(ptr, args)
+#define __bpf_log_arg10(ptr, arg, args...)                                                                             \
+    *(ptr++) = MKR_LOG_ARG;                                                                                            \
+    *(__u64 *)(ptr) = arg;                                                                                             \
+    ptr += sizeof(__u64);                                                                                              \
+    __bpf_log_arg9(ptr, args)
+#define __bpf_log_arg11(ptr, arg, args...)                                                                             \
+    *(ptr++) = MKR_LOG_ARG;                                                                                            \
+    *(__u64 *)(ptr) = arg;                                                                                             \
+    ptr += sizeof(__u64);                                                                                              \
+    __bpf_log_arg10(ptr, args)
+#define __bpf_log_arg12(ptr, arg, args...)                                                                             \
+    *(ptr++) = MKR_LOG_ARG;                                                                                            \
+    *(__u64 *)(ptr) = arg;                                                                                             \
+    ptr += sizeof(__u64);                                                                                              \
+    __bpf_log_arg11(ptr, args)
+#define __bpf_log_arg(ptr, args...) ___bpf_apply(__bpf_log_arg, ___bpf_narg(args))(ptr, args)
+
+/* These values have to stay in sync with the enum */
+/* values in bpf/tests/bpftest/trf.proto */
+#define TEST_ERROR 0
+#define TEST_PASS  1
+#define TEST_FAIL  2
+#define TEST_SKIP  3
+
+/* Max number of cpus to check when doing percpu hash assertions */
+#define NR_CPUS 128
+
+/* IP_DF is defined in linux/ip.h, but we don't want to include that */
+#ifndef IP_DF
+#define IP_DF 0x4000
+#endif
+
+/* Use an array map with 1 key and a large value size as buffer to write results */
+/* into. */
+struct {
+    __uint(type, BPF_MAP_TYPE_ARRAY);
+    __uint(key_size, sizeof(__u32));
+    __uint(value_size, 8192);
+    __uint(pinning, LIBBPF_PIN_BY_NAME);
+    __uint(max_entries, 1);
+} suite_result_map SEC(".maps");
+
+/* Values for the markers below are derived from this guide: */
+/* https://developers.google.com/protocol-buffers/docs/encoding#structure */
+
+#define PROTOBUF_WIRE_TYPE(field, type) ((field) << 3 | (type))
+
+#define PROTOBUF_VARINT           0
+#define PROTOBUF_FIXED64          1
+#define PROTOBUF_LENGTH_DELIMITED 2
+
+/* message SuiteResult */
+#define MKR_TEST_RESULT PROTOBUF_WIRE_TYPE(1, PROTOBUF_LENGTH_DELIMITED)
+#define MKR_SUITE_LOG   PROTOBUF_WIRE_TYPE(2, PROTOBUF_LENGTH_DELIMITED)
+
+/* message TestResult */
+#define MKR_TEST_NAME   PROTOBUF_WIRE_TYPE(1, PROTOBUF_LENGTH_DELIMITED)
+#define MKR_TEST_STATUS PROTOBUF_WIRE_TYPE(2, PROTOBUF_VARINT)
+#define MKR_TEST_LOG    PROTOBUF_WIRE_TYPE(3, PROTOBUF_LENGTH_DELIMITED)
+
+/* message Log */
+#define MKR_LOG_FMT PROTOBUF_WIRE_TYPE(1, PROTOBUF_LENGTH_DELIMITED)
+#define MKR_LOG_ARG PROTOBUF_WIRE_TYPE(2, PROTOBUF_FIXED64)
+
+/* Write a message to the unit log
+ *	The conversion specifiers supported by *fmt* are the same as for
+ *      bpf_trace_printk(). They are **%d**, **%i**, **%u**, **%x**, **%ld**,
+ *      **%li**, **%lu**, **%lx**, **%lld**, **%lli**, **%llu**, **%llx**,
+ *      **%p**. No modifier (size of field, padding with zeroes, etc.)
+ *      is available
+ *
+ * Each log message is automatically prefixed with the source file name and
+ * line number in the format "file:line: " to help with identifying the exact
+ * origin of the log entry in the source code. This information is particularly
+ * useful for debugging and tracing test execution flow.
+ */
+#define test_log(fmt, args...)                                                                                         \
+    ({                                                                                                                 \
+        const char ____fileline[] = __FILE__ ":" STRINGIZE(__LINE__) ": ";                                             \
+        const char ____testfmt[] = fmt;                                                                                \
+        const int ____fileline_len = sizeof(____fileline) - 1; /* exclude null terminator */                           \
+        const int ____fmt_len = sizeof(____testfmt);                                                                   \
+        if (test_result_cursor) {                                                                                      \
+            *(suite_result_cursor++) = MKR_TEST_LOG;                                                                   \
+        } else {                                                                                                       \
+            *(suite_result_cursor++) = MKR_SUITE_LOG;                                                                  \
+        }                                                                                                              \
+        *(suite_result_cursor++) =                                                                                     \
+            2 + ____fileline_len + ____fmt_len + ___bpf_narg(args) + (___bpf_narg(args) * sizeof(unsigned long long)); \
+        *(suite_result_cursor++) = MKR_LOG_FMT;                                                                        \
+        *(suite_result_cursor++) = ____fileline_len + ____fmt_len;                                                     \
+        bpf_memcpy(suite_result_cursor, ____fileline, ____fileline_len);                                               \
+        suite_result_cursor += ____fileline_len;                                                                       \
+        bpf_memcpy(suite_result_cursor, ____testfmt, ____fmt_len);                                                     \
+        suite_result_cursor += ____fmt_len;                                                                            \
+                                                                                                                       \
+        if (___bpf_narg(args) > 0) {                                                                                   \
+            __bpf_log_arg(suite_result_cursor, args);                                                                  \
+        }                                                                                                              \
+    })
+
+/* This is a hack to allow us to convert the integer produced by __LINE__ */
+/* to a string so we can concat it at compile time. */
+#define STRINGIZE(x)  STRINGIZE2(x)
+#define STRINGIZE2(x) #x
+#define LINE_STRING   STRINGIZE(__LINE__)
+
+/* Mark the current test as failed */
+#define test_fail()                                                                                                    \
+    if (test_result_cursor) {                                                                                          \
+        *test_result_status = TEST_FAIL;                                                                               \
+    } else {                                                                                                           \
+        suite_result = TEST_FAIL;                                                                                      \
+    }
+
+/* Mark the current test as failed and exit the current TEST/CHECK */
+#define test_fail_now()                                                                                                \
+    if (test_result_cursor) {                                                                                          \
+        *test_result_status = TEST_FAIL;                                                                               \
+        break;                                                                                                         \
+    } else {                                                                                                           \
+        return TEST_FAIL;                                                                                              \
+    }
+
+/* Mark the current test as skipped */
+#define test_skip()                                                                                                    \
+    if (test_result_cursor) {                                                                                          \
+        *test_result_status = TEST_SKIP;                                                                               \
+    } else {                                                                                                           \
+        suite_result = TEST_SKIP;                                                                                      \
+    }
+
+/* Mark the current test as skipped and exit the current TEST/CHECK */
+#define test_skip_now()                                                                                                \
+    if (test_result_cursor) {                                                                                          \
+        *test_result_status = TEST_SKIP;                                                                               \
+        break;                                                                                                         \
+    } else {                                                                                                           \
+        return TEST_SKIP;                                                                                              \
+    }
+
+/* Write message to the log and mark current test as failed. */
+#define test_error(fmt, ...)                                                                                           \
+    {                                                                                                                  \
+        test_log(fmt, ##__VA_ARGS__);                                                                                  \
+        test_fail();                                                                                                   \
+    }
+
+/* Log a message bpf_then fail_now */
+#define test_fatal(fmt, ...)                                                                                           \
+    {                                                                                                                  \
+        test_log(fmt, ##__VA_ARGS__);                                                                                  \
+        test_fail_now()                                                                                                \
+    }
+
+/* Assert that `cond` is true, fail the rest otherwise */
+#define assert(cond)                                                                                                   \
+    if (!(cond)) {                                                                                                     \
+        test_log("assert failed at " __FILE__ ":" LINE_STRING);                                                        \
+        test_fail_now();                                                                                               \
+    }
+
+/* Declare bpf_map_lookup_elem with the test_ prefix to avoid conflicts in the */
+/* future. */
+static void *(*test_bpf_map_lookup_elem)(void *map, const void *key) = (void *)1;
+
+/* Init sets up a number of variables which will be used by other macros. */
+/* - suite_result will be returned from the eBPF program */
+/* - test_result_status is a pointer into the suite_result_map when in a test */
+/* - suite_result_cursor keeps track of where in the suite result we are. */
+/* - test_result_cursor is a pointer to the varint of a test result, used to */
+/*   write the amount of bytes used after a test is done. */
+#define test_init()                                                                                                    \
+    char suite_result = TEST_PASS;                                                                                     \
+    __maybe_unused char *test_result_status = 0;                                                                       \
+    char *suite_result_cursor;                                                                                         \
+    {                                                                                                                  \
+        __u32 __key = 0;                                                                                               \
+        suite_result_cursor = test_bpf_map_lookup_elem(&suite_result_map, &__key);                                     \
+        if (!suite_result_cursor) {                                                                                    \
+            return TEST_ERROR;                                                                                         \
+        }                                                                                                              \
+    }                                                                                                                  \
+    __maybe_unused char *test_result_cursor = 0;                                                                       \
+    __maybe_unused __u16 test_result_size;                                                                             \
+    do {
+/* */
+/* Each test is single iteration do-while loop so we can break, to exit the */
+/* test without unique label names and goto's */
+#define TEST(name, body)                                                                                               \
+    do {                                                                                                               \
+        *(suite_result_cursor++) = MKR_TEST_RESULT;                                                                    \
+        /* test_result_cursor will stay at test result length varint */                                                \
+        test_result_cursor = suite_result_cursor;                                                                      \
+        /* Reserve 2 bytes for the varint indicating test result length */                                             \
+        suite_result_cursor += 2;                                                                                      \
+                                                                                                                       \
+        static const char ____name[] = name;                                                                           \
+        *(suite_result_cursor++) = MKR_TEST_NAME;                                                                      \
+        *(suite_result_cursor++) = sizeof(____name);                                                                   \
+        memcpy(suite_result_cursor, ____name, sizeof(____name));                                                       \
+        suite_result_cursor += sizeof(____name);                                                                       \
+                                                                                                                       \
+        *(suite_result_cursor++) = MKR_TEST_STATUS;                                                                    \
+        test_result_status = suite_result_cursor;                                                                      \
+                                                                                                                       \
+        *test_result_status = TEST_PASS;                                                                               \
+        suite_result_cursor++;                                                                                         \
+                                                                                                                       \
+        body                                                                                                           \
+    } while (0);                                                                                                       \
+    /* Write the total size of the test result in bytes as varint */                                                   \
+    test_result_size = (__u16)((long)suite_result_cursor - (long)test_result_cursor) - 2;                              \
+    if (test_result_size > 127) {                                                                                      \
+        *(test_result_cursor) = (__u8)(test_result_size & 0b01111111) | 0b10000000;                                    \
+        test_result_size >>= 7;                                                                                        \
+        *(test_result_cursor + 1) = (__u8)test_result_size;                                                            \
+    } else {                                                                                                           \
+        *test_result_cursor = (__u8)(test_result_size) | 0b10000000;                                                   \
+    }                                                                                                                  \
+    test_result_cursor = 0;
+
+#define test_finish()                                                                                                  \
+    }                                                                                                                  \
+    while (0)                                                                                                          \
+        ;                                                                                                              \
+    return suite_result
+
+#define PKTGEN(progtype, name) SEC(progtype "/test/" name "/pktgen")
+#define JUMP(progtype, name)   SEC(progtype "/test/" name "/jump")
+#define CHECK(progtype, name)  SEC(progtype "/test/" name "/check")
+
+/* Asserts that the sum of per-cpu metrics map slots for a key equals count */
+#define assert_metrics_count(key, count)                                                                               \
+    ({                                                                                                                 \
+        struct metrics_value *__entry = NULL;                                                                          \
+        __u64 sum = 0;                                                                                                 \
+        /* Iterate until lookup encounters null when hitting cpu number */                                             \
+        /* Assumes at most 128 CPUS */                                                                                 \
+        for (int i = 0; i < NR_CPUS; i++) {                                                                            \
+            __entry = map_lookup_percpu_elem(&METRICS_MAP, &key, i);                                                   \
+            if (!__entry) {                                                                                            \
+                break;                                                                                                 \
+            }                                                                                                          \
+            sum += __entry->count;                                                                                     \
+        }                                                                                                              \
+        assert(sum == count);                                                                                          \
+    })
+
+// memcmp
+static inline int memcmp(const void *s1, const void *s2, __u32 n)
+{
+    const char *p1 = s1;
+    const char *p2 = s2;
+
+    for (__u32 i = 0; i < n; i++) {
+        if (p1[i] != p2[i])
+            return p1[i] - p2[i];
+    }
+    return 0;
+}
diff --git a/test/bpf_ut/include/xdp_common.h b/test/bpf_ut/include/xdp_common.h
new file mode 100644
index 00000000..15d9cab3
--- /dev/null
+++ b/test/bpf_ut/include/xdp_common.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/* Copyright Authors of Kmesh */
+
+#pragma once
+
+#define build_xdp_packet(ctx, p_ethhdr, p_iphdr, p_tcphdr, body, body_len)                                             \
+    ({                                                                                                                 \
+        int __ret = TEST_PASS;                                                                                         \
+        unsigned int data_len = (ctx)->data_end - (ctx)->data;                                                         \
+                                                                                                                       \
+        /* Adjust packet size - ensure we have enough space */                                                         \
+        int offset = 4096 - 256 - 320 - data_len;                                                                      \
+        if (bpf_xdp_adjust_tail(ctx, offset) != 0) {                                                                   \
+            return TEST_ERROR;                                                                                         \
+        }                                                                                                              \
+                                                                                                                       \
+        void *data = (void *)(long)((ctx)->data);                                                                      \
+        void *data_end = (void *)(long)((ctx)->data_end);                                                              \
+                                                                                                                       \
+        /* Set Ethernet header */                                                                                      \
+        if (data + sizeof(struct ethhdr) > data_end)                                                                   \
+            return TEST_ERROR;                                                                                         \
+        if (p_ethhdr) {                                                                                                \
+            bpf_memcpy(data, (p_ethhdr), sizeof(struct ethhdr));                                                       \
+        } else {                                                                                                       \
+            /* Default Ethernet header */                                                                              \
+            __maybe_unused const struct ethhdr default_eth = {                                                         \
+                .h_source = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},                                                      \
+                .h_dest = {0x12, 0x23, 0x34, 0x45, 0x56, 0x67},                                                        \
+                .h_proto = bpf_htons(ETH_P_IP)};                                                                       \
+            bpf_memcpy(data, &default_eth, sizeof(struct ethhdr));                                                     \
+        }                                                                                                              \
+        data += sizeof(struct ethhdr);                                                                                 \
+                                                                                                                       \
+        /* Set IP header */                                                                                            \
+        if (data + sizeof(struct iphdr) > data_end)                                                                    \
+            return TEST_ERROR;                                                                                         \
+        if (p_iphdr) {                                                                                                 \
+            bpf_memcpy(data, (p_iphdr), sizeof(struct iphdr));                                                         \
+        } else {                                                                                                       \
+            /* Default IP header */                                                                                    \
+            __maybe_unused const unsigned int ip_payload_size = sizeof(struct tcphdr) + (body ? body_len : 0);         \
+            __maybe_unused const struct iphdr default_ip = {                                                           \
+                .version = 4,                                                                                          \
+                .ihl = 5,                                                                                              \
+                .tot_len = bpf_htons(sizeof(struct iphdr) + ip_payload_size),                                          \
+                .id = 0x5438,                                                                                          \
+                .frag_off = bpf_htons(IP_DF),                                                                          \
+                .ttl = 64,                                                                                             \
+                .protocol = IPPROTO_TCP,                                                                               \
+                .saddr = 0x0F00000A, /* 10.0.0.15 */                                                                   \
+                .daddr = 0x0100000A  /* 10.0.0.1 - assuming DEST_IP */                                                 \
+            };                                                                                                         \
+            bpf_memcpy(data, &default_ip, sizeof(struct iphdr));                                                       \
+        }                                                                                                              \
+        data += sizeof(struct iphdr);                                                                                  \
+                                                                                                                       \
+        /* Set TCP header */                                                                                           \
+        if (data + sizeof(struct tcphdr) > data_end)                                                                   \
+            return TEST_ERROR;                                                                                         \
+        if (p_tcphdr) {                                                                                                \
+            bpf_memcpy(data, (p_tcphdr), sizeof(struct tcphdr));                                                       \
+        } else {                                                                                                       \
+            /* Default TCP header */                                                                                   \
+            __maybe_unused const struct tcphdr default_tcp = {                                                         \
+                .source = bpf_htons(23445),                                                                            \
+                .dest = bpf_htons(80), /* assuming DEST_PORT */                                                        \
+                .seq = 2922048129,                                                                                     \
+                .doff = 5, /* 5 * 4 = 20 bytes, no options */                                                          \
+                .syn = 1,                                                                                              \
+                .window = 64240};                                                                                      \
+            bpf_memcpy(data, &default_tcp, sizeof(struct tcphdr));                                                     \
+        }                                                                                                              \
+        data += sizeof(struct tcphdr);                                                                                 \
+                                                                                                                       \
+        /* Set payload data */                                                                                         \
+        if (body && body_len > 0) {                                                                                    \
+            if (data + body_len > data_end)                                                                            \
+                return TEST_ERROR;                                                                                     \
+            bpf_memcpy(data, body, body_len);                                                                          \
+            data += body_len;                                                                                          \
+        }                                                                                                              \
+        /* Shrink ctx to the exact size we used */                                                                     \
+        offset = (int)((long)data - (long)data_end);                                                                   \
+        bpf_xdp_adjust_tail(ctx, offset);                                                                              \
+        __ret;                                                                                                         \
+    })
+
+/**
+ * @brief Verifies that a processed packet matches expected values
+ *
+ * This macro checks if the XDP packet matches expected values by sequentially validating:
+ * - Status code
+ * - Ethernet header
+ * - IP header
+ * - TCP header
+ * - Packet body (payload data)
+ *
+ * @param ctx              XDP context containing packet data
+ * @param exp_status_code  Expected status code pointer (if NULL, status code check is skipped)
+ * @param exp_ethhdr       Expected Ethernet header pointer (if NULL, Ethernet header check is skipped)
+ * @param exp_iphdr        Expected IP header pointer (if NULL, IP header check is skipped)
+ * @param exp_tcphdr       Expected TCP header pointer (if NULL, TCP header check is skipped)
+ * @param exp_body         Expected packet body/payload pointer (if NULL, body check is skipped)
+ * @param exp_body_len     Length of the expected body data to check
+ *
+ * The macro fails the test with an error message if any of the checks fail or if there are
+ * bounds violations when accessing packet data.
+ */
+#define check_xdp_packet(ctx, exp_status_code, exp_ethhdr, exp_iphdr, exp_tcphdr, exp_body, exp_body_len)              \
+    do {                                                                                                               \
+        void *data = (void *)(long)((ctx)->data);                                                                      \
+        void *data_end = (void *)(long)((ctx)->data_end);                                                              \
+                                                                                                                       \
+        if (data + sizeof(__u32) > data_end)                                                                           \
+            test_fatal("status code out of bounds");                                                                   \
+        if (exp_status_code) {                                                                                         \
+            __u32 *status_code = (__u32 *)data;                                                                        \
+            if (*status_code != *(exp_status_code))                                                                    \
+                test_fatal("status code mismatch, expected %u, got %u", *(exp_status_code), *status_code);             \
+        }                                                                                                              \
+        data += sizeof(__u32);                                                                                         \
+                                                                                                                       \
+        if (data + sizeof(struct ethhdr) > data_end)                                                                   \
+            test_fatal("ethhdr out of bounds");                                                                        \
+        if (exp_ethhdr) {                                                                                              \
+            struct ethhdr *eth = (struct ethhdr *)data;                                                                \
+            if (memcmp(eth, (exp_ethhdr), sizeof(struct ethhdr)) != 0)                                                 \
+                test_fatal("ethhdr mismatch");                                                                         \
+        }                                                                                                              \
+        data += sizeof(struct ethhdr);                                                                                 \
+                                                                                                                       \
+        if (data + sizeof(struct iphdr) > data_end)                                                                    \
+            test_fatal("iphdr out of bounds");                                                                         \
+        if (exp_iphdr) {                                                                                               \
+            struct iphdr *ip = (struct iphdr *)data;                                                                   \
+            if (memcmp(ip, (exp_iphdr), sizeof(struct iphdr)) != 0)                                                    \
+                test_fatal("iphdr mismatch");                                                                          \
+        }                                                                                                              \
+        data += sizeof(struct iphdr);                                                                                  \
+                                                                                                                       \
+        if (data + sizeof(struct tcphdr) > data_end)                                                                   \
+            test_fatal("tcphdr out of bounds");                                                                        \
+        if (exp_tcphdr) {                                                                                              \
+            struct tcphdr *tcp = (struct tcphdr *)data;                                                                \
+            if (memcmp(tcp, (exp_tcphdr), sizeof(struct tcphdr)) != 0)                                                 \
+                test_fatal("tcphdr mismatch");                                                                         \
+        }                                                                                                              \
+        data += sizeof(struct tcphdr);                                                                                 \
+        if (data + exp_body_len > data_end)                                                                            \
+            test_fatal("body out of bounds");                                                                          \
+        if (exp_body) {                                                                                                \
+            char *body = (char *)data;                                                                                 \
+            if (memcmp(body, (exp_body), exp_body_len) != 0)                                                           \
+                test_fatal("body mismatch");                                                                           \
+        }                                                                                                              \
+    } while (0)
diff --git a/test/bpf_ut/xdp_authz_offload_test.c b/test/bpf_ut/xdp_authz_offload_test.c
new file mode 100644
index 00000000..fff5dfcc
--- /dev/null
+++ b/test/bpf_ut/xdp_authz_offload_test.c
@@ -0,0 +1,108 @@
+#include "ut_common.h"
+#include "xdp_common.h"
+
+#include <linux/bpf.h>
+
+#include "workload/xdp.c"
+
+struct {
+    __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+    __uint(key_size, sizeof(__u32));
+    __uint(max_entries, 2);
+    __array(values, int());
+} entry_call_map SEC(".maps") = {
+    .values =
+        {
+            [0] = &xdp_authz,
+        },
+};
+
+/* 10.0.0.15:23445 -> 10.1.0.15:80 */
+#define SRC_IP    0x0F00000A /* 10.0.0.15 */
+#define SRC_PORT  23445
+#define DEST_IP   0x0F00010A /* 10.1.0.15 */
+#define DEST_PORT 80
+
+PKTGEN("xdp", "3_deny_policy_matched")
+int test1_pktgen(struct xdp_md *ctx)
+{
+    const struct iphdr l3 = {
+        .version = 4,
+        .ihl = 5,
+        .tot_len = 40, /* 20 bytes l3 + 20 bytes l4 + 20 bytes data */
+        .id = 0x5438,
+        .frag_off = bpf_htons(IP_DF),
+        .ttl = 64,
+        .protocol = IPPROTO_TCP,
+        .saddr = SRC_IP,
+        .daddr = DEST_IP,
+    };
+    const struct tcphdr l4 = {
+        .source = bpf_htons(SRC_PORT),
+        .dest = bpf_htons(DEST_PORT),
+        .seq = 2922048129,
+        .doff = 0, /* no options */
+        .syn = 1,
+        .window = 64240,
+    };
+
+    return build_xdp_packet(ctx, NULL, &l3, &l4, NULL, 0);
+}
+
+JUMP("xdp", "3_deny_policy_matched")
+int test1_jump(struct xdp_md *ctx)
+{
+    bpf_tail_call(ctx, &entry_call_map, 0);
+    return TEST_ERROR;
+}
+
+CHECK("xdp", "3_deny_policy_matched")
+int test1_check(const struct xdp_md *ctx)
+{
+    const __u32 exp_status_code = XDP_DROP;
+    test_init();
+    check_xdp_packet(ctx, &exp_status_code, NULL, NULL, NULL, NULL, 0);
+    test_finish();
+}
+
+PKTGEN("xdp", "4_allow_policy_matched")
+int test2_pktgen(struct xdp_md *ctx)
+{
+    const struct iphdr l3 = {
+        .version = 4,
+        .ihl = 5,
+        .tot_len = 40, /* 20 bytes l3 + 20 bytes l4 + 20 bytes data */
+        .id = 0x5438,
+        .frag_off = bpf_htons(IP_DF),
+        .ttl = 64,
+        .protocol = IPPROTO_TCP,
+        .saddr = SRC_IP,
+        .daddr = DEST_IP,
+    };
+    const struct tcphdr l4 = {
+        .source = bpf_htons(SRC_PORT),
+        .dest = bpf_htons(DEST_PORT),
+        .seq = 2922048129,
+        .doff = 0, /* no options */
+        .syn = 1,
+        .window = 64240,
+    };
+
+    return build_xdp_packet(ctx, NULL, &l3, &l4, NULL, 0);
+}
+
+JUMP("xdp", "4_allow_policy_matched")
+int test2_jump(struct xdp_md *ctx)
+{
+    bpf_tail_call(ctx, &entry_call_map, 0);
+    return TEST_ERROR;
+}
+
+CHECK("xdp", "4_allow_policy_matched")
+int test2_check(const struct xdp_md *ctx)
+{
+    const __u32 exp_status_code = XDP_PASS;
+    test_init();
+    check_xdp_packet(ctx, &exp_status_code, NULL, NULL, NULL, NULL, 0);
+    test_finish();
+}
diff --git a/test/bpf_ut/xdp_shutdown_in_userspace_test.c b/test/bpf_ut/xdp_shutdown_in_userspace_test.c
new file mode 100644
index 00000000..429b9f21
--- /dev/null
+++ b/test/bpf_ut/xdp_shutdown_in_userspace_test.c
@@ -0,0 +1,175 @@
+#include "ut_common.h"
+#include "xdp_common.h"
+
+#include <linux/bpf.h>
+#include <linux/if_ether.h>
+
+#include "workload/xdp.c"
+
+struct {
+    __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+    __uint(key_size, sizeof(__u32));
+    __uint(max_entries, 2);
+    __array(values, int());
+} entry_call_map SEC(".maps") = {
+    .values =
+        {
+            [0] = &xdp_authz,
+        },
+};
+
+/* 10.0.0.15:23445 -> 10.1.0.15:80 */
+#define SRC_IP    0x0F00000A /* 10.0.0.15 */
+#define SRC_PORT  23445
+#define DEST_IP   0x0F00010A /* 10.1.0.15 */
+#define DEST_PORT 80
+
+// 1_shutdown_in_userspace__should_shutdown
+PKTGEN("xdp", "1_shutdown_in_userspace__should_shutdown")
+int test1_pktgen(struct xdp_md *ctx)
+{
+    const struct ethhdr l2 = {
+        .h_source = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
+        .h_dest = {0x12, 0x23, 0x34, 0x45, 0x56, 0x67},
+        .h_proto = bpf_htons(ETH_P_IP)};
+    const struct iphdr l3 = {
+        .version = 4,
+        .ihl = 5,
+        .tot_len = 40, /* 20 bytes l3 + 20 bytes l4 + 20 bytes data */
+        .id = 0x5438,
+        .frag_off = bpf_htons(IP_DF),
+        .ttl = 64,
+        .protocol = IPPROTO_TCP,
+        .saddr = SRC_IP,
+        .daddr = DEST_IP,
+    };
+    const struct tcphdr l4 = {
+        .source = bpf_htons(SRC_PORT),
+        .dest = bpf_htons(DEST_PORT),
+        .seq = 2922048129,
+        .doff = 0, /* no options */
+        .syn = 1,
+        .window = 64240,
+    };
+    const char body[20] = "Should not change!!";
+
+    return build_xdp_packet(ctx, &l2, &l3, &l4, body, (uint)sizeof(body));
+}
+
+JUMP("xdp", "1_shutdown_in_userspace__should_shutdown")
+int test1_jump(struct xdp_md *ctx)
+{
+    /* build the test context */
+    struct bpf_sock_tuple tuple_key = {0};
+    struct xdp_info info = {0};
+    __u32 auth_deny = AUTH_DENY;
+    if (construct_tuple_key(ctx, &tuple_key, &info) != PARSER_SUCC) {
+        BPF_LOG(ERR, UNITTEST, "failed to construct tuple key\n");
+        return TEST_ERROR;
+    }
+    if (bpf_map_update_elem(&map_of_auth_result, &tuple_key, &auth_deny, BPF_ANY) != 0) {
+        BPF_LOG(ERR, UNITTEST, "failed to update auth result in map_of_auth_result");
+        return TEST_ERROR;
+    }
+
+    /* Jump into the entrypoint */
+    bpf_tail_call(ctx, &entry_call_map, 0);
+    /* Fail if we didn't jump */
+    return TEST_ERROR;
+}
+
+CHECK("xdp", "1_shutdown_in_userspace__should_shutdown")
+int test1_check(const struct xdp_md *ctx)
+{
+    const __u32 expected_status_code = XDP_PASS;
+    const struct ethhdr expected_ethhdr = {
+        .h_source = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
+        .h_dest = {0x12, 0x23, 0x34, 0x45, 0x56, 0x67},
+        .h_proto = bpf_htons(ETH_P_IP)};
+    const struct iphdr expected_iphdr = {
+        .version = 4,
+        .ihl = 5,
+        .tot_len = 40, /* 20 bytes l3 + 20 bytes l4 + 20 bytes data */
+        .id = 0x5438,
+        .frag_off = bpf_htons(IP_DF),
+        .ttl = 64,
+        .protocol = IPPROTO_TCP,
+        .saddr = SRC_IP,
+        .daddr = DEST_IP,
+    };
+    const struct tcphdr expected_tcphdr = {
+        .source = bpf_htons(SRC_PORT),
+        .dest = bpf_htons(DEST_PORT),
+        .seq = 2922048129,
+        .doff = 0,
+        .rst = 1, // RST bit set
+        .window = 64240,
+    };
+    const char expected_body[20] = "Should not change!!";
+    test_init();
+
+    check_xdp_packet(
+        ctx,
+        &expected_status_code,
+        &expected_ethhdr,
+        &expected_iphdr,
+        &expected_tcphdr,
+        expected_body,
+        sizeof(expected_body));
+
+    test_finish();
+}
+
+// 2_shutdown_in_userspace__should_not_shutdown
+PKTGEN("xdp", "2_shutdown_in_userspace__should_not_shutdown")
+int test2_pktgen(struct xdp_md *ctx)
+{
+    const struct iphdr l3 = {
+        .version = 4,
+        .ihl = 5,
+        .tot_len = 40, /* 20 bytes l3 + 20 bytes l4 + 20 bytes data */
+        .id = 0x5438,
+        .frag_off = bpf_htons(IP_DF),
+        .ttl = 64,
+        .protocol = IPPROTO_TCP,
+        .saddr = SRC_IP,
+        .daddr = DEST_IP,
+    };
+    const struct tcphdr l4 = {
+        .source = bpf_htons(SRC_PORT),
+        .dest = bpf_htons(DEST_PORT),
+        .seq = 2922048129,
+        .doff = 0, /* no options */
+        .syn = 1,
+        .window = 64240,
+    };
+    return build_xdp_packet(ctx, NULL, &l3, &l4, NULL, 0);
+}
+
+JUMP("xdp", "2_shutdown_in_userspace__should_not_shutdown")
+int test2_jump(struct xdp_md *ctx)
+{
+    /* Jump into the entrypoint */
+    bpf_tail_call(ctx, &entry_call_map, 0);
+    /* Fail if we didn't jump */
+    return TEST_ERROR;
+}
+
+CHECK("xdp", "2_shutdown_in_userspace__should_not_shutdown")
+int test2_check(const struct xdp_md *ctx)
+{
+    const __u32 expected_status_code = XDP_PASS;
+    const struct tcphdr expected_tcphdr = {
+        .source = bpf_htons(23445),
+        .dest = bpf_htons(DEST_PORT),
+        .seq = 2922048129,
+        .doff = 0, /* no options */
+        .syn = 1,
+        .window = 64240,
+    };
+    test_init();
+
+    check_xdp_packet(ctx, &expected_status_code, NULL, NULL, &expected_tcphdr, NULL, 0);
+
+    test_finish();
+}