[
  {
    "path": ".github/workflows/autoupdate.yml",
    "content": "name: Auto Update\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: \"0 */2 * * *\"\n\njobs:\n  check_update:\n    runs-on: ubuntu-latest\n    outputs:\n      update_needed: ${{ steps.check.outputs.update_needed }}\n      pkg_version: ${{ steps.check.outputs.pkg_version }}\n      latest_daed_full: ${{ steps.check.outputs.latest_daed_full }}\n      latest_daed_short: ${{ steps.check.outputs.latest_daed_short }}\n      latest_wing_short: ${{ steps.check.outputs.latest_wing_short }}\n      latest_core_short: ${{ steps.check.outputs.latest_core_short }}\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 1\n\n      - name: Check Upstream Updates\n        id: check\n        run: |\n          DAED_JSON=$(curl -s https://api.github.com/repos/daeuniverse/daed/commits/main)\n          WING_JSON=$(curl -s https://api.github.com/repos/daeuniverse/dae-wing/commits/main)\n          CORE_JSON=$(curl -s https://api.github.com/repos/olicesx/dae/commits/kdae)\n\n          UPSTREAM_DAED_FULL=$(echo \"$DAED_JSON\" | jq -r '.sha')\n          UPSTREAM_DAED=$(echo \"$UPSTREAM_DAED_FULL\" | cut -b 1-7)\n          UPSTREAM_WING=$(echo \"$WING_JSON\" | jq -r '.sha' | cut -b 1-7)\n          UPSTREAM_CORE=$(echo \"$CORE_JSON\" | jq -r '.sha' | cut -b 1-7)\n\n          CURRENT_DAED=$(grep \"PKG_SOURCE_VERSION:=\" daed/Makefile | cut -d= -f2 || true)\n          CURRENT_WING=$(grep \"WING_VERSION:=wing-\" daed/Makefile | sed 's/.*wing-//' | head -n 1 || true)\n          CURRENT_CORE=$(grep \"CORE_VERSION:=core-\" daed/Makefile | sed 's/.*core-//' | head -n 1 || true)\n\n          echo \"Upstream DAED: $UPSTREAM_DAED_FULL, Local: $CURRENT_DAED\"\n          echo \"Upstream WING: $UPSTREAM_WING, Local: $CURRENT_WING\"\n          echo \"Upstream CORE: $UPSTREAM_CORE, Local: $CURRENT_CORE\"\n\n          NEED_UPDATE=false\n          if [ \"$UPSTREAM_DAED_FULL\" != \"$CURRENT_DAED\" ]; then NEED_UPDATE=true; fi\n          if [ \"$UPSTREAM_WING\" != \"$CURRENT_WING\" ]; then NEED_UPDATE=true; fi\n          if [ \"$UPSTREAM_CORE\" != \"$CURRENT_CORE\" ]; then NEED_UPDATE=true; fi\n\n          if [ \"$NEED_UPDATE\" = \"true\" ]; then\n            echo \"Update needed\"\n            echo \"update_needed=true\" >> \"$GITHUB_OUTPUT\"\n\n            DATE_DAED=$(echo \"$DAED_JSON\" | jq -r '.commit.committer.date')\n            DATE_WING=$(echo \"$WING_JSON\" | jq -r '.commit.committer.date')\n            DATE_CORE=$(echo \"$CORE_JSON\" | jq -r '.commit.committer.date')\n            \n            LATEST_ISO_DATE=$(echo -e \"$DATE_DAED\\n$DATE_WING\\n$DATE_CORE\" | sort -r | head -n 1)\n            PKG_VERSION=$(date -d \"$LATEST_ISO_DATE\" +%Y.%m.%d)\n            echo \"Selected latest date: $LATEST_ISO_DATE formatted as $PKG_VERSION\"\n\n            echo \"pkg_version=$PKG_VERSION\" >> \"$GITHUB_OUTPUT\"\n            echo \"latest_daed_full=$UPSTREAM_DAED_FULL\" >> \"$GITHUB_OUTPUT\"\n            echo \"latest_daed_short=$UPSTREAM_DAED\" >> \"$GITHUB_OUTPUT\"\n            echo \"latest_wing_short=$UPSTREAM_WING\" >> \"$GITHUB_OUTPUT\"\n            echo \"latest_core_short=$UPSTREAM_CORE\" >> \"$GITHUB_OUTPUT\"\n          else\n            echo \"Everything up to date.\"\n            echo \"update_needed=false\" >> \"$GITHUB_OUTPUT\"\n          fi\n\n  build:\n    needs: check_update\n    if: needs.check_update.outputs.update_needed == 'true'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n          persist-credentials: false\n\n\n      - name: Build System Setup\n        env:\n          DEBIAN_FRONTEND: noninteractive\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y jq dos2unix\n\n      - name: Update Makefile\n        run: |\n          chmod 755 ./daed/files/daed.init ./luci-app-daed/root/etc/init.d/luci_daed ./luci-app-daed/root/etc/hotplug.d/iface/98-daed\n          find . -type f -exec dos2unix {} \\; -print\n\n          sed -i '/PKG_VERSION:/d' ./daed/Makefile\n          sed -i \"7 a PKG_VERSION:=${{ needs.check_update.outputs.pkg_version }}\" ./daed/Makefile\n          \n          sed -i '/DAED_VERSION:/d' ./daed/Makefile\n          sed -i \"8 a DAED_VERSION:=daed-${{ needs.check_update.outputs.latest_daed_short }}\" ./daed/Makefile\n          \n          sed -i '/WING_VERSION:=/d' ./daed/Makefile\n          sed -i \"9 a WING_VERSION:=wing-${{ needs.check_update.outputs.latest_wing_short }}\" ./daed/Makefile\n          \n          sed -i '/CORE_VERSION:=/d' ./daed/Makefile\n          sed -i \"10 a CORE_VERSION:=core-${{ needs.check_update.outputs.latest_core_short }}\" ./daed/Makefile\n          \n          sed -i '/PKG_SOURCE_VERSION:/d' ./daed/Makefile\n          sed -i \"17 a PKG_SOURCE_VERSION:=${{ needs.check_update.outputs.latest_daed_full }}\" ./daed/Makefile\n\n      - name: Commit file\n        run: |\n          git config --global user.email simonsqiu@foxmail.com\n          git config --global user.name SimonsQiu\n          git add .\n          git commit -m \"Update $(date +%Y/%m/%d\\ %H:%M:%S\\ %Z)\" -a\n        continue-on-error: true\n\n      - name: Push changes\n        uses: ad-m/github-push-action@master\n        with:\n          github_token: ${{secrets.ACTIONS_DEPLOY_KEY}}\n          branch: kix\n        continue-on-error: true\n\n\n"
  },
  {
    "path": ".github/workflows/build-packages.yml",
    "content": "name: Build packages\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - kix\n\n\npermissions:\n  contents: write\n\njobs:\n  prepare:\n    name: Prepare version\n    runs-on: ubuntu-latest\n    outputs:\n      version: ${{ steps.meta.outputs.version }}\n      release_tag: ${{ steps.meta.outputs.release_tag }}\n      release_name: ${{ steps.meta.outputs.release_name }}\n    steps:\n      - name: Checkout source\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Generate version metadata\n        id: meta\n        shell: bash\n        run: |\n          pkg_version=$(grep '^PKG_VERSION:=' daed/Makefile | head -n 1 | cut -d= -f2)\n          pkg_release=$(grep '^PKG_RELEASE:=' daed/Makefile | head -n 1 | cut -d= -f2)\n\n          version=\"${pkg_version}-r${pkg_release}\"\n          release_tag=\"daed_${version}\"\n          release_name=\"daed_${version}\"\n\n          echo \"version=${version}\" >> \"$GITHUB_OUTPUT\"\n          echo \"release_tag=${release_tag}\" >> \"$GITHUB_OUTPUT\"\n          echo \"release_name=${release_name}\" >> \"$GITHUB_OUTPUT\"\n\n          echo \"Version: ${version}\"\n          echo \"Release tag: ${release_tag}\"\n\n  build:\n    name: Build ${{ matrix.arch }}-${{ matrix.sdk }}\n    runs-on: ubuntu-latest\n    needs: prepare\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - arch: aarch64_generic\n            sdk: openwrt-24.10\n            pkg_ext: ipk\n          - arch: i386_pentium4\n            sdk: openwrt-24.10\n            pkg_ext: ipk\n          - arch: x86_64\n            sdk: openwrt-24.10\n            pkg_ext: ipk\n          - arch: aarch64_generic\n            sdk: openwrt-25.12\n            pkg_ext: apk\n          - arch: i386_pentium4\n            sdk: openwrt-25.12\n            pkg_ext: apk\n          - arch: x86_64\n            sdk: openwrt-25.12\n            pkg_ext: apk\n\n    steps:\n      - name: Checkout source\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Init build dependencies\n        env:\n          DEBIAN_FRONTEND: noninteractive\n        shell: bash\n        run: |\n          sudo -E apt-get update\n          sudo -E apt-get install -y clang llvm curl jq\n\n      - name: Build packages\n        uses: sbwml/openwrt-gh-action-sdk@go1.26\n        env:\n          ARCH: ${{ matrix.arch }}-${{ matrix.sdk }}\n          FEEDNAME: packages_ci\n          PACKAGES: luci-app-daed\n          NO_REFRESH_CHECK: true\n          V: s\n\n      - name: Show built files\n        shell: bash\n        run: |\n          echo \"=== Built package files ===\"\n          find bin/packages -type f | grep -E '\\.(ipk|apk)$' || true\n          echo \"===========================\"\n\n\n      - name: Prepare renamed artifacts\n        shell: bash\n        run: |\n          set -euo pipefail\n          mkdir -p dist\n          for f in bin/packages/${{ matrix.arch }}/packages_ci/*.${{ matrix.pkg_ext }}; do\n            base=\"$(basename \"$f\")\"\n            ext=\"${base##*.}\"\n            name=\"${base%.*}\"\n\n            # Avoid duplicating architecture in name, and skip architecture suffix for LuCI/all packages\n            if [[ \"$name\" == *\"${{ matrix.arch }}\"* ]] || [[ \"$name\" == *\"all\"* ]] || [[ \"$name\" == luci-* ]]; then\n              new_name=\"${name}-${{ matrix.sdk }}.${ext}\"\n            else\n              new_name=\"${name}-${{ matrix.arch }}-${{ matrix.sdk }}.${ext}\"\n            fi\n            \n            cp \"$f\" \"dist/${new_name}\"\n          done\n          ls -lah dist\n\n      - name: Upload build artifacts\n        uses: actions/upload-artifact@v7\n        with:\n          name: ${{ matrix.arch }}-${{ matrix.sdk }}-${{ matrix.pkg_ext }}\n          path: dist/*.${{ matrix.pkg_ext }}\n          if-no-files-found: error\n          retention-days: 7\n\n  release:\n    name: Publish release\n    runs-on: ubuntu-latest\n    needs:\n      - prepare\n      - build\n    steps:\n      - name: Download all build artifacts\n        uses: actions/download-artifact@v8\n        with:\n          path: release-dist\n          merge-multiple: true\n\n      - name: Create or update GitHub release\n        uses: softprops/action-gh-release@v2\n        with:\n          tag_name: ${{ needs.prepare.outputs.release_tag }}\n          name: ${{ needs.prepare.outputs.release_name }}\n          files: release-dist/*\n          generate_release_notes: true\n          token: ${{ secrets.GITHUB_TOKEN }}\n          target_commitish: ${{ github.sha }}\n\n      - name: Delete Older Releases\n        uses: dev-drprasad/delete-older-releases@master\n        with:\n          keep_latest: 3\n          delete_tags: true\n        env:\n          GITHUB_TOKEN: ${{secrets.ACTIONS_DEPLOY_KEY}}\n        continue-on-error: true\n\n      - name: Cleanup Old Action Artifacts\n        uses: c-hive/gha-remove-artifacts@master\n        with:\n          age: '3 days'\n          skip-recent: 3\n        continue-on-error: true\n\n      - name: Cleanup Workflow Logs\n        uses: Mattraks/delete-workflow-runs@main\n        with:\n          token: ${{secrets.ACTIONS_DEPLOY_KEY}}\n          repository: ${{ github.repository }}\n          retain_days: 3\n        continue-on-error: true\n\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">luci-app-daed</h1>\n<p align=\"center\">\n  <img width=\"100\" src=\"https://github.com/daeuniverse/dae/blob/main/logo.png?raw=true\" />\n</p>\n<p align=\"center\">\n  <b>一个基于 eBPF 的高性能透明代理解决方案。</b>\n</p>\n\n---\n\n## 快速入门\n\n### 1. 环境准备 (编译主机)\n\n在编译之前，请确保您的编译主机已安装必要的开发工具。参考 [apt.llvm.org](https://apt.llvm.org/) 安装最新版本的 Clang 和 LLVM。\n\n```bash\napt-get update\napt-get install -y clang llvm npm\nnpm install -g pnpm\n```\n\n### 2. 获取源码\n\n进入您的 OpenWrt 目录，克隆本仓库到 `package` 目录：\n\n```bash\ngit clone https://github.com/QiuSimons/luci-app-daed package/dae\n```\n\n### 3. 内核配置要求 (DAE 运行前提)\n\nDAE 依赖 eBPF 和 BTF。请在 `.config` 中添加以下内核配置以启用相关支持：\n\n```makefile\nCONFIG_DEVEL=y\nCONFIG_KERNEL_DEBUG_INFO=y\nCONFIG_KERNEL_DEBUG_INFO_REDUCED=n\nCONFIG_KERNEL_DEBUG_INFO_BTF=y\nCONFIG_KERNEL_CGROUPS=y\nCONFIG_KERNEL_CGROUP_BPF=y\nCONFIG_KERNEL_BPF_EVENTS=y\nCONFIG_BPF_TOOLCHAIN_HOST=y\nCONFIG_KERNEL_XDP_SOCKETS=y\nCONFIG_PACKAGE_kmod-xdp-sockets-diag=y\n```\n\n### 4. 编译与安装\n\n```bash\nmake menuconfig # 路径: LUCI -> Applications -> luci-app-daed\nmake package/dae/luci-app-daed/compile V=s\n```\n\n---\n\n## 核心概念：BTF 与 CO-RE\n\n### BTF 来源选择\n\n在 `make menuconfig` 中，您可以根据内核支持情况选择 BTF 来源：\n\n- **Use kernel BTF (integrated)**: **[推荐]** 要求内核开启 `CONFIG_KERNEL_DEBUG_INFO_BTF=y`。\n- **Use vmlinux-btf package**: 如果内核不支持原生 BTF，可选择此项以使用外部 [vmlinux-btf](https://github.com/QiuSimons/vmlinux-btf) 软件包。\n\n### vmlinux-btf 依赖说明\n\n由于预编译安装包无法自动探测内核是否支持 BTF，为了确保稳定性，程序默认依赖 `vmlinux-btf`。\n\n- **方案 A：手动补全依赖 (推荐)**\n  如果软件源缺失该包，请前往 [opkg.cooluc.com](https://opkg.cooluc.com/) 下载。\n  - **建议**: 优先选择与内核版本号 (`x.y.z`) 完全一致的包；至少保证主次版本号 (`x.y`) 一致。\n- **方案 B：忽略依赖 (高级用户)**\n  如果您确认内核已原生支持 BTF (`CONFIG_KERNEL_DEBUG_INFO_BTF=y`)，可在安装时使用 `--force-depends` 参数忽略依赖检查。\n\n---\n\n## 预览\n\n<p align=\"center\">\n<img width=\"800\" src=\"https://github.com/QiuSimons/luci-app-daed/blob/master/PIC/1.jpg?raw=true\" />\n<img width=\"800\" src=\"https://github.com/QiuSimons/luci-app-daed/blob/master/PIC/2.jpg?raw=true\" />\n</p>\n"
  },
  {
    "path": "daed/Makefile",
    "content": "# SPDX-License-Identifier: GPL-2.0-only\n#\n# Copyright (C) 2023 ImmortalWrt.org\n\ninclude $(TOPDIR)/rules.mk\n\nPKG_NAME:=daed\nPKG_VERSION:=2026.05.06\nDAED_VERSION:=daed-f655b35\nWING_VERSION:=wing-dc50308\nCORE_VERSION:=core-6005321\nWING_HASH_SHORT:=$(shell echo $(WING_VERSION) | cut -d- -f2)\nCORE_HASH_SHORT:=$(shell echo $(CORE_VERSION) | cut -d- -f2)\nPKG_RELEASE:=1\n\nPKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz\nPKG_SOURCE_PROTO:=git\nPKG_SOURCE_VERSION:=f655b354db5bc39fbf29a48d7c4c10f971c84cd0\nPKG_SOURCE_URL:=https://github.com/daeuniverse/daed.git\nPKG_MIRROR_HASH:=skip\n\nPKG_LICENSE:=AGPL-3.0-only MIT\nPKG_LICENSE_FILES:=LICENSE wing/LICENSE\nPKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>\n\nDAED_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)\nPKG_BUILD_DIR:=$(DAED_BUILD_DIR)/wing\n\nPKG_BUILD_DEPENDS:=golang/host bpf-headers\nPKG_BUILD_PARALLEL:=1\nPKG_BUILD_FLAGS:=no-mips16\n\nGO_PKG:=github.com/daeuniverse/dae-wing\n\nGO_PKG_LDFLAGS:= \\\n\t-s -w -buildid= \\\n\t-linkmode external -extldflags '-static -Wl,-s' \\\n\t-X '$(GO_PKG)/db.AppDescription=$(PKG_NAME) is a integration solution of dae, API and UI.'\nGO_PKG_LDFLAGS_X:= \\\n\t$(GO_PKG)/db.AppName=$(PKG_NAME) \\\n\t$(GO_PKG)/db.AppVersion=$(DAED_VERSION)_$(WING_VERSION)_$(CORE_VERSION)\nGO_PKG_TAGS:=embedallowed,trace\n\ninclude $(INCLUDE_DIR)/package.mk\ninclude $(INCLUDE_DIR)/bpf.mk\ninclude $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk\n\nGO_PKG_BUILD_VARS+= \\\n\tGOFLAGS=\"-trimpath -buildvcs=false -pgo=auto\"\n\nGO_PKG_TARGET_VARS+= \\\n\tCGO_LDFLAGS=\"$(TARGET_LDFLAGS) -static -Wl,-s\" \\\n\tGOEXPERIMENT=newinliner,simd\n\ndefine Package/daed/Default\n  SECTION:=net\n  CATEGORY:=Network\n  SUBMENU:=Web Servers/Proxies\n  URL:=https://github.com/daeuniverse/daed\nendef\n\ndefine Package/daed\n  $(call Package/daed/Default)\n  TITLE:=A Modern Dashboard For dae\n  DEPENDS:=$(GO_ARCH_DEPENDS) \\\n    +ca-bundle +kmod-sched-core +kmod-sched-bpf \\\n    +kmod-veth +v2ray-geoip +v2ray-geosite \\\n    +@KERNEL_XDP_SOCKETS \\\n    +DAED_USE_VMLINUX_BTF:vmlinux-btf\nendef\n\ndefine Package/daed/config\n\tchoice\n\t\tprompt \"BTF source for CO-RE\"\n\t\tdefault DAED_USE_KERNEL_BTF\n\t\tdepends on PACKAGE_daed\n\n\tconfig DAED_USE_KERNEL_BTF\n\t\tbool \"Use kernel BTF (integrated)\"\n\t\tdepends on KERNEL_DEBUG_INFO_BTF\n\n\tconfig DAED_USE_VMLINUX_BTF\n\t\tbool \"Use vmlinux-btf package\"\n\tendchoice\nendef\n\ndefine Package/daed/description\n  daed is a backend of dae, provides a method to bundle arbitrary\n  frontend, dae and geodata into one binary.\nendef\n\ndefine Package/daed/conffiles\n/etc/daed/wing.db\n/etc/config/daed\nendef\n\nNODE_VERSION:=v24.12.0\nNODE_DIST:=node-$(NODE_VERSION)-linux-x64\nNODE_URL:=https://nodejs.org/dist/$(NODE_VERSION)/$(NODE_DIST).tar.xz\n\ndefine Build/Prepare\n\t( \\\n\t\trm -rf $(DAED_BUILD_DIR) ; \\\n\t\tmkdir -p $(DAED_BUILD_DIR) ; \\\n\t\t$(TAR) --strip-components=1 -C $(DAED_BUILD_DIR) -xzf $(DL_DIR)/$(PKG_NAME)-$(PKG_VERSION).tar.gz ; \\\n\t\tgit clone https://github.com/daeuniverse/dae-wing $(PKG_BUILD_DIR) && \\\n\t\t\tgit -C $(PKG_BUILD_DIR) checkout $(WING_HASH_SHORT) ; \\\n\t\trm -rf $(PKG_BUILD_DIR)/dae-core ; \\\n\t\tgit clone https://github.com/olicesx/dae $(PKG_BUILD_DIR)/dae-core && \\\n\t\t\tgit -C $(PKG_BUILD_DIR)/dae-core checkout $(CORE_HASH_SHORT) ; \\\n\t\trm -rf $(DAED_BUILD_DIR)/outbound ; \\\n\t\tgit clone --depth=1 -b perf/complete-optimizations https://github.com/olicesx/outbound.git $(DAED_BUILD_DIR)/outbound ; \\\n\t\trm -rf $(DAED_BUILD_DIR)/quic-go ; \\\n\t\tgit clone --depth=1 -b perf/node-pooling-v2 https://github.com/olicesx/quic-go.git $(DAED_BUILD_DIR)/quic-go ; \\\n\t\tpushd $(PKG_BUILD_DIR)/dae-core ; \\\n\t\t\tgit submodule update --init ; \\\n\t\t\tgo get -u=patch ; \\\n\t\t\tgo mod tidy ; \\\n\t\tpopd ; \\\n\t\tpushd $(PKG_BUILD_DIR) ; \\\n\t\t\tgo mod edit -replace github.com/daeuniverse/outbound=../outbound ; \\\n\t\t\tgo mod edit -replace github.com/daeuniverse/quic-go=../quic-go ; \\\n\t\t\tgo get -u=patch ; \\\n\t\t\tgo mod tidy ; \\\n\t\t\twget -qO default.pgo \"https://github.com/QiuSimons/luci-app-dae/raw/refs/heads/kix/dae/pprof/default.pgo\" ; \\\n\t\tpopd ; \\\n\t\tmkdir -p $(DAED_BUILD_DIR)/.node_tmp/config ; \\\n\t\twget -qO - \"$(NODE_URL)\" | tar -xJ -C $(DAED_BUILD_DIR)/.node_tmp --strip-components=1 ; \\\n\t\texport PATH=\"$(DAED_BUILD_DIR)/.node_tmp/bin:$$$$PATH\" ; \\\n\t\texport XDG_CONFIG_HOME=\"$(DAED_BUILD_DIR)/.node_tmp/config\" ; \\\n\t\tnpm install -g pnpm ; \\\n\t\tpushd $(DAED_BUILD_DIR) ; \\\n\t\t\tpnpm install ; \\\n\t\t\tpnpm build --filter daed ; \\\n\t\tpopd ; \\\n\t\tmkdir -p $(PKG_BUILD_DIR)/webrender/web ; \\\n\t\tcp -rf $(DAED_BUILD_DIR)/apps/web/dist/* $(PKG_BUILD_DIR)/webrender/web ; \\\n\t\tfind $(PKG_BUILD_DIR)/webrender/web -name \"*.map\" -type f -delete ; \\\n\t\tfind $(PKG_BUILD_DIR)/webrender/web -type f -size +4k ! -name \"*.gz\" ! -name \"*.woff\"  ! -name \"*.woff2\" -exec sh -c '\\\n\t\t\tgzip -9 -k \"{}\"; \\\n\t\t\tif [ \"$$$$(stat -c %s {})\" -lt \"$$$$(stat -c %s {}.gz)\" ]; then \\\n\t\t\t\trm {}.gz; \\\n\t\t\telse \\\n\t\t\t\trm {}; \\\n\t\t\tfi' \\\n\t\t\";\" ; \\\n\t\trm -rf $(DAED_BUILD_DIR)/.node_tmp ; \\\n\t)\nendef\n\nDAE_CFLAGS:= \\\n\t-O2 -Wall -Werror \\\n\t-DMAX_MATCH_SET_LEN=1024 \\\n\t-I$(BPF_HEADERS_DIR)/tools/lib \\\n\t-I$(BPF_HEADERS_DIR)/arch/$(BPF_KARCH)/include/asm/mach-generic\n\ndefine Build/Compile\n\t( \\\n\t\tpushd $(PKG_BUILD_DIR) ; \\\n\t\texport \\\n\t\t$(GO_GENERAL_BUILD_CONFIG_VARS) \\\n\t\t$(GO_PKG_BUILD_CONFIG_VARS) \\\n\t\t$(GO_PKG_BUILD_VARS) ; \\\n\t\tgo generate ./... ; \\\n\t\tcd dae-core ; \\\n\t\texport \\\n\t\tBPF_CLANG=\"$(CLANG)\" \\\n\t\tBPF_STRIP_FLAG=\"-strip=$(LLVM_STRIP)\" \\\n\t\tBPF_CFLAGS=\"$(DAE_CFLAGS)\" \\\n\t\tBPF_TARGET=\"bpfel,bpfeb\" \\\n\t\tBPF_TRACE_TARGET=\"$(GO_ARCH)\" ; \\\n\t\tgo generate ./control/control.go ; \\\n\t\tgo generate ./trace/trace.go ; \\\n\t\tpopd ; \\\n\t)\n\t$(call GoPackage/Build/Compile)\nendef\n\ndefine Package/daed/install\n\t$(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR))\n\t$(INSTALL_DIR) $(1)/usr/bin\n\t$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/dae-wing $(1)/usr/bin/daed\n\n\t$(INSTALL_DIR) $(1)/etc/config\n\t$(INSTALL_CONF) $(CURDIR)/files/daed.config $(1)/etc/config/daed\n\n\t$(INSTALL_DIR) $(1)/etc/init.d\n\t$(INSTALL_BIN) $(CURDIR)/files/daed.init $(1)/etc/init.d/daed\nendef\n\n$(eval $(call GoBinPackage,daed))\n$(eval $(call BuildPackage,daed))\n"
  },
  {
    "path": "daed/files/daed.config",
    "content": "\nconfig daed 'config'\n\toption enabled '0'\n\toption listen_addr '0.0.0.0:2023'\n\toption log_maxbackups '1'\n\toption log_maxsize '5'\n\n"
  },
  {
    "path": "daed/files/daed.init",
    "content": "#!/bin/sh /etc/rc.common\n# Copyright (C) 2023 Tianling Shen <cnsztl@immortalwrt.org>\n\nUSE_PROCD=1\nSTART=99\n\nCONF=\"daed\"\nPROG=\"/usr/bin/daed\"\nLOG=\"/var/log/daed/daed.log\"\nDAENETNS=\"/run/netns/daens\"\n\nstart_service() {\n\tconfig_load \"$CONF\"\n\n\tlocal enabled\n\tconfig_get_bool enabled \"config\" \"enabled\" \"0\"\n\t[ \"$enabled\" -eq \"1\" ] || return 1\n\n\tlocal listen_addr log_maxbackups log_maxsize\n\tconfig_get listen_addr \"config\" \"listen_addr\" \"0.0.0.0:2023\"\n\tconfig_get log_maxbackups \"config\" \"log_maxbackups\" \"1\"\n\tconfig_get log_maxsize \"config\" \"log_maxsize\" \"5\"\n\n\tprocd_open_instance \"$CONF\"\n\tprocd_set_param command \"$PROG\" run\n\tprocd_append_param command --config \"/etc/daed/\"\n\tprocd_append_param command --listen \"$listen_addr\"\n\tprocd_append_param command --logfile \"$LOG\"\n\tprocd_append_param command --logfile-maxbackups \"$log_maxbackups\"\n\tprocd_append_param command --logfile-maxsize \"$log_maxsize\"\n\n\tprocd_set_param limits core=\"unlimited\"\n\tprocd_set_param limits nofile=\"1000000 1000000\"\n\tprocd_set_param respawn\n\t# procd_set_param stdout 1\n\tprocd_set_param stderr 1\n\n\tprocd_close_instance\n}\n\nstop_service() {\n\trm -f \"$LOG\"\n\t[ -f \"$DAENETNS\" ] && umount \"$DAENETNS\"\n}\n\nservice_triggers() {\n\tprocd_add_reload_trigger \"$CONF\"\n}\n"
  },
  {
    "path": "luci-app-daed/Makefile",
    "content": "include $(TOPDIR)/rules.mk\n\nPKG_NAME:=luci-app-daed\nPKG_VERSION:=1.4\nPKG_RELEASE:=1\n\nLUCI_TITLE:=LuCI Support for DAED\nLUCI_DEPENDS:=+daed +zoneinfo-asia +luci-compat\nLUCI_PKGARCH:=all\n\ndefine Package/$(PKG_NAME)/conffiles\nendef\n\ninclude $(TOPDIR)/feeds/luci/luci.mk\n# call BuildPackage - OpenWrt buildroot signature\n"
  },
  {
    "path": "luci-app-daed/luasrc/controller/daed.lua",
    "content": "local sys  = require \"luci.sys\"\nlocal http = require \"luci.http\"\n\nmodule(\"luci.controller.daed\", package.seeall)\n\nfunction index()\n\tif not nixio.fs.access(\"/etc/config/daed\") then\n\t\treturn\n\tend\n\n\tentry({\"admin\",  \"services\", \"daed\"}, alias(\"admin\", \"services\", \"daed\", \"setting\"),_(\"DAED\"), 58).dependent = true\n\tentry({\"admin\", \"services\", \"daed\", \"setting\"}, cbi(\"daed/basic\"), _(\"Base Setting\"), 1).leaf=true\n\tentry({\"admin\",  \"services\", \"daed\", \"daed\"}, template(\"daed/daed\"), _(\"Dashboard\"), 2).leaf = true\n\tentry({\"admin\", \"services\", \"daed\", \"log\"}, cbi(\"daed/log\"), _(\"Logs\"), 3).leaf = true\n\tentry({\"admin\", \"services\", \"daed_status\"}, call(\"act_status\"))\n\tentry({\"admin\", \"services\", \"daed\", \"get_log\"}, call(\"get_log\")).leaf = true\n\tentry({\"admin\", \"services\", \"daed\", \"clear_log\"}, call(\"clear_log\")).leaf = true\nend\n\nfunction act_status()\n\tlocal sys  = require \"luci.sys\"\n\tlocal e = { }\n\te.running = sys.call(\"pidof daed >/dev/null\") == 0\n\tluci.http.prepare_content(\"application/json\")\n\tluci.http.write_json(e)\nend\n\nfunction get_log()\n\thttp.write(sys.exec(\"cat /var/log/daed/daed.log\"))\nend\n\nfunction clear_log()\n\tsys.call(\"true > /var/log/daed/daed.log\")\nend\n"
  },
  {
    "path": "luci-app-daed/luasrc/model/cbi/daed/basic.lua",
    "content": "local m, s ,o\n\nm = Map(\"daed\")\nm.title = translate(\"DAED\")\nm.description = translate(\"DAE is a Linux high-performance transparent proxy solution based on eBPF, And DAED is a modern dashboard for dae.\")\n\nm:section(SimpleSection).template = \"daed/daed_status\"\n\ns = m:section(TypedSection, \"daed\", translate(\"Global Settings\"))\ns.addremove = false\ns.anonymous = true\n\no = s:option(Flag,\"enabled\",translate(\"Enable\"))\no.default = 0\n\no = s:option(Value, \"log_maxbackups\", translate(\"Logfile retention count\"))\no.default = 1\n\no = s:option(Value, \"log_maxsize\", translate(\"Logfile Max Size (MB)\"))\no.default = 5\n\no = s:option(Value, \"listen_addr\",translate(\"Set the DAED listen address\"))\no.default = '0.0.0.0:2023'\n\no = s:option(Value, \"dashboard_port\", translate(\"Dashboard Access Port\"))\no.placeholder = translate(\"Leave empty to use listen port\")\no.datatype = \"range(1,65535)\"\no.description = translate(\"For reverse proxy scenarios, leave empty to use the port from listen address\")\n\nm.apply_on_parse = true\nm.on_after_apply = function(self,map)\n\tluci.sys.exec(\"/etc/init.d/daed restart\")\nend\n\nreturn m"
  },
  {
    "path": "luci-app-daed/luasrc/model/cbi/daed/log.lua",
    "content": "m = Map(\"daed\")\n\nm:append(Template(\"daed/daed_log\"))\n\nreturn m\n"
  },
  {
    "path": "luci-app-daed/luasrc/view/daed/daed.htm",
    "content": "<%+header%>\n<%\n\tlocal running = luci.sys.exec(\"pgrep -x /usr/bin/daed\")\n%>\n<% if tonumber(running) ~= nil then %>\n\t<div class=\"cbi-map\">\n\t\t<iframe id=\"daed\" class=\"daed-iframe\"></iframe>\n\t</div>\n\t<style>\n\t.daed-iframe {\n\t\twidth: 100%;\n\t\theight: 100vh;\n\t\tmin-height: 780px;\n\t\tborder: none;\n\t\tborder-radius: .375rem;\n\t\tbox-shadow: rgba(0, 0, 0, 0.75) 0px 0px 15px -5px;\n\t\tdisplay: block;\n\t}\n\t</style>\n\t<script type=\"text/javascript\">\n\t\tvar listenAddr = \"<%=luci.model.uci.cursor():get_first(\"daed\", \"daed\", \"listen_addr\") or ''%>\";\n\t\tvar dashboardPort = \"<%=luci.model.uci.cursor():get_first(\"daed\", \"daed\", \"dashboard_port\") or ''%>\";\n\t\t\n\t\tvar port = \"\";\n\t\tif (dashboardPort && dashboardPort.trim() !== \"\") {\n\t\t\tport = dashboardPort.trim();\n\t\t} else {\n\t\t\tvar portRegex = /:(\\d+)/;\n\t\t\tvar match = listenAddr.match(portRegex);\n\t\t\tif (match && match[1]) {\n\t\t\t\tport = match[1];\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (port) {\n\t\t\tvar protocol = window.location.protocol;\n\t\t\tvar hostname = window.location.hostname;\n\t\t\tvar url = protocol + \"//\" + hostname;\n\t\t\t\n\t\t\tif (!((protocol === \"http:\" && port === \"80\") || (protocol === \"https:\" && port === \"443\"))) {\n\t\t\t\turl += \":\" + port;\n\t\t\t}\n\t\t\t\n\t\t\tdocument.getElementById(\"daed\").src = url;\n\t\t}\n\t</script>\n<% else %>\n\t<style>.notrunning{text-align: center;} h1{font-size: 25px; color: #333; margin-bottom: 16px;} p{font-size: 18px; color: #666; margin-bottom: 24px;}</style>\n\t<div class=\"notrunning\">\n\t\t<img src=\"data:image/png;base64,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\" height=\"90px\" />\n\t\t<h1><font color=\"color:red\"><%:DAED is not running%></font></h1>\n\t\t<p><%:Please start the DAED service first and try again%></p>\n    </div>\n<% end -%>\n\n<%+footer%>"
  },
  {
    "path": "luci-app-daed/luasrc/view/daed/daed_log.htm",
    "content": "<script type=\"text/javascript\">\n\t//<![CDATA[\n\tfunction clear_log(btn) {\n\t\tXHR.get('<%=url([[admin]], [[services]], [[daed]], [[clear_log]])%>', null,\n\t\t\tfunction(x, data) {\n\t\t\t\tif(x && x.status == 200) {\n\t\t\t\t\tvar log_textarea = document.getElementById('log_textarea');\n\t\t\t\t\tlog_textarea.innerHTML = \"\";\n\t\t\t\t\tlog_textarea.scrollTop = log_textarea.scrollHeight;\n\t\t\t\t}\n\t\t\t\tlocation.reload();\n\t\t\t}\n\t\t);\n\t}\n\tvar scrolled = false;\n\tXHR.poll(2, '<%=url([[admin]], [[services]], [[daed]], [[get_log]])%>', null,\n\t\tfunction(x, data) {\n\t\t\tif(x && x.status == 200) {\n\t\t\t\tvar log_textarea = document.getElementById('log_textarea');\n\t\t\t\tlog_textarea.innerHTML = x.responseText;\n\t\t\t\tif (!scrolled) {\n\t\t\t\t\tlog_textarea.scrollTop = log_textarea.scrollHeight;\n\t\t\t\t\tscrolled = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t);\n\t//]]>\n</script>\n<fieldset class=\"cbi-section\" id=\"_log_fieldset\">\n\t<input class=\"cbi-button cbi-input-remove\" type=\"button\" onclick=\"clear_log()\" value=\"<%:Clear logs%>\" style=\"margin-left: 10px; margin-top: 10px;\">\n\t<textarea id=\"log_textarea\" class=\"cbi-input-textarea\" style=\"width: calc(100% - 20px); height: 645px; margin: 10px;\" data-update=\"change\" rows=\"5\" wrap=\"off\" readonly=\"readonly\"></textarea>\n</fieldset>\n"
  },
  {
    "path": "luci-app-daed/luasrc/view/daed/daed_status.htm",
    "content": "<script type=\"text/javascript\">//<![CDATA[\n\tXHR.poll(3, '<%=url([[admin]], [[services]], [[daed_status]])%>', null,\n\t\tfunction(x, data) {\n\t\t\tvar tb = document.getElementById('daed_status');\n\t\t\tif (data && tb)\n\t\t\t{\n\t\t\t\tif (data.running)\n\t\t\t\t{\n\t\t\t\t\ttb.innerHTML = '<em><b style=\\\"color:green\\\"><%:DAED%> <%:RUNNING%></b></em>';\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\ttb.innerHTML = '<em><b style=\\\"color:red\\\"><%:DAED%> <%:NOT RUNNING%></b></em>';\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t);\n//]]></script>\n<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>\n<fieldset class=\"cbi-section\">\n\t<p id=\"daed_status\">\n\t\t<em><%:Collecting data...%></em>\n\t</p>\n</fieldset>\n"
  },
  {
    "path": "luci-app-daed/po/zh_Hans/daed.po",
    "content": "msgid \"\"\nmsgstr \"Content-Type: text/plain; charset=UTF-8\"\n\nmsgid \"DAED\"\nmsgstr \"DAED\"\n\nmsgid \"DAE is a Linux high-performance transparent proxy solution based on eBPF, And DAED is a modern dashboard for dae.\"\nmsgstr \"DAE是一个基于eBPF的Linux高性能透明代理解决方案,而DAED是DAE的管理面板。\"\n\nmsgid \"Base Setting\"\nmsgstr \"基本设置\"\n\nmsgid \"Dashboard\"\nmsgstr \"仪表板\"\n\nmsgid \"Logs\"\nmsgstr \"日志\"\n\nmsgid \"Clear logs\"\nmsgstr \"清空日志\"\n\nmsgid \"RUNNING\"\nmsgstr \"运行中\"\n\nmsgid \"NOT RUNNING\"\nmsgstr \"未运行\"\n\nmsgid \"Collecting data...\"\nmsgstr \"收集数据...\"\n\nmsgid \"Logfile retention count\"\nmsgstr \"日志文件保留数量\"\n\nmsgid \"Logfile Max Size (MB)\"\nmsgstr \"日志文件大小(MB)\"\n\nmsgid \"Set the DAED listen address\"\nmsgstr \"设置监听地址\"\n\nmsgid \"Dashboard Access Port\"\nmsgstr \"仪表板访问端口\"\n\nmsgid \"Leave empty to use listen port\"\nmsgstr \"留空则使用监听端口\"\n\nmsgid \"For reverse proxy scenarios, leave empty to use the port from listen address\"\nmsgstr \"用于反向代理场景,留空则使用监听地址的端口\"\n\nmsgid \"DAED is not running\"\nmsgstr \"DAED 未运行\"\n\nmsgid \"Please start the DAED service first and try again\"\nmsgstr \"请先启动 DAED 服务后重试\"\n\nmsgid \"Enable Auto Subscribe Update\"\nmsgstr \"启用订阅自动更新\"\n\nmsgid \"Update Cycle\"\nmsgstr \"更新周期\"\n\nmsgid \"Update Time (Every Day)\"\nmsgstr \"更新时间(每天)\"\n\nmsgid \"Username\"\nmsgstr \"用户名\"\n\nmsgid \"Password\"\nmsgstr \"密码\""
  },
  {
    "path": "luci-app-daed/root/etc/hotplug.d/iface/98-daed",
    "content": "#!/bin/sh\n\n[ \"${ACTION}\" = \"ifup\" ] || exit 0\n\nDEVICE=$(logread | grep \"link is up\" | tail -n 1 | awk -F \"'\" '{print $2}')\nDEVICE_TYPE=$(ip link show dev \"$DEVICE\")\n\ncase \"$DEVICE_TYPE\" in\n  *\"link/ether\"*)\n    (\n      LOCK_FILE=\"/tmp/lock/daed_hotplug_lock\"\n      if [ -f \"$LOCK_FILE\" ]; then\n        exit 1\n      else\n        echo $$ > \"$LOCK_FILE\" 2>/dev/null\n        trap 'rm -f \"$LOCK_FILE\"' EXIT\n        sleep 60\n        /etc/init.d/daed restart 2>&1\n      fi\n    ) &\n    ;;\nesac\n"
  },
  {
    "path": "luci-app-daed/root/etc/init.d/luci_daed",
    "content": "#!/bin/sh /etc/rc.common\n# Copyright (C) 2023 Tianling Shen <cnsztl@immortalwrt.org>\n\nUSE_PROCD=0\nSTART=98\n\nCONF=\"daed\"\nPROG=\"/usr/bin/daed\"\nLOG=\"/var/log/daed/daed.log\"\nCRON_FILE=\"/etc/crontabs/root\"\nRANDOM_SEED=$RANDOM\nRANDOM_NUM=$((RANDOM_SEED % 10 + 1))\n\nsetcron() {\n  touch $CRON_FILE\n  sed -i '/daed_sub.sh/d' $CRON_FILE 2>/dev/null\n  [ \"$(uci -q get daed.config.subscribe_auto_update)\" -eq 1 ] && echo \"${RANDOM_NUM} $(uci -q get daed.config.subscribe_update_day_time) * * $(uci -q get daed.config.subscribe_update_week_time) /etc/daed/daed_sub.sh >/dev/null 2>&1\" >>$CRON_FILE\n  crontab $CRON_FILE\n}\n\ndelcron() {\n  sed -i '/daed_sub.sh/d' $CRON_FILE 2>/dev/null\n  crontab $CRON_FILE\n}\n\nsetlocaluse() {\n  uci set dhcp.@dnsmasq[0].localuse=\"1\"\n  uci commit dhcp\n  /etc/init.d/dnsmasq restart\n}\n\ndellocaluse() {\n  uci set dhcp.@dnsmasq[0].localuse=\"0\"\n  uci commit dhcp\n  /etc/init.d/dnsmasq restart\n  . /lib/functions/network.sh\n  network_find_wan LOGICAL_WAN || exit 1\n  dns_list=$(ubus call network.interface.$LOGICAL_WAN status | jsonfilter -e '@[\"dns-server\"]' | sed 's/[][\"]//g' | sed 's/,/\\n/g' | grep -oE '\\b([0-9]{1,3}\\.){3}[0-9]{1,3}\\b')\n  [ -z \"$dns_list\" ] && dns_list=\"119.29.29.29 180.76.76.76 223.5.5.5\"\n  grep -v '^nameserver ' /etc/resolv.conf > /tmp/resolv.conf.new.daed 2>/dev/null\n  for dns in $dns_list; do\n    echo \"nameserver $dns\" >> /tmp/resolv.conf.new.daed\n  done\n  cat /tmp/resolv.conf.new.daed > /etc/resolv.conf\n  rm -f /tmp/resolv.conf.new.daed\n}\n\nstart_service() {\n  [ -f \"/etc/init.d/daed\" ] && grep -q \"DAE_LOCATION_ASSET\" \"/etc/init.d/daed\" || sed -i '/run/i\\  procd_set_param env DAE_LOCATION_ASSET=\"/usr/share/v2ray\"' \"/etc/init.d/daed\"\n  config_load \"$CONF\"\n\n  local enabled\n  config_get_bool enabled \"config\" \"enabled\" \"0\"\n  if [ \"$enabled\" -eq 0 ]; then\n    #delcron\n    setlocaluse\n    return 1\n  fi\n  #setcron\n  dellocaluse\n}\n\nstop_service() {\n  #delcron\n  setlocaluse\n}\n\nservice_triggers() {\n  procd_add_reload_trigger \"$CONF\"\n}\n"
  },
  {
    "path": "luci-app-daed/root/usr/share/rpcd/acl.d/luci-app-daed.json",
    "content": "{\n\t\"luci-app-daed\": {\n\t\t\"description\": \"Grant UCI access for luci-app-daed\",\n\t\t\"read\": {\n\t\t\t\"uci\": [ \"daed\" ]\n\t\t},\n\t\t\"write\": {\n\t\t\t\"uci\": [ \"daed\" ]\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "patchset/0001-fix-runtime-stats.patch",
    "content": "diff --git a/dae-core/control/runtime_stats.go b/dae-core/control/runtime_stats.go\nindex 1a75d5f..5a692bf 100644\n--- a/dae-core/control/runtime_stats.go\n+++ b/dae-core/control/runtime_stats.go\n@@ -62,7 +62,10 @@ type runtimeStats struct {\n \thistoryLen    int\n }\n \n-var globalRuntimeStats = newRuntimeStats()\n+var (\n+\tglobalRuntimeStats = newRuntimeStats()\n+\tactiveRuntimeStats atomic.Pointer[runtimeStats]\n+)\n \n func newRuntimeStats() *runtimeStats {\n \treturn &runtimeStats{}\n@@ -77,6 +80,10 @@ func RecordUploadTraffic(n int64) {\n \tif n <= 0 {\n \t\treturn\n \t}\n+\tif active := activeRuntimeStats.Load(); active != nil {\n+\t\tactive.record(uint64(n), 0)\n+\t\treturn\n+\t}\n \tif globalRuntimeStats != nil {\n \t\tglobalRuntimeStats.record(uint64(n), 0)\n \t}\n@@ -87,6 +94,10 @@ func RecordDownloadTraffic(n int64) {\n \tif n <= 0 {\n \t\treturn\n \t}\n+\tif active := activeRuntimeStats.Load(); active != nil {\n+\t\tactive.record(0, uint64(n))\n+\t\treturn\n+\t}\n \tif globalRuntimeStats != nil {\n \t\tglobalRuntimeStats.record(0, uint64(n))\n \t}\n@@ -95,6 +106,9 @@ func RecordDownloadTraffic(n int64) {\n // Deprecated: prefer (*ControlPlane).SnapshotRuntimeStats for per-instance stats.\n // SnapshotRuntimeStats returns the current runtime traffic snapshot.\n func SnapshotRuntimeStats(activeConnections int, udpSessions int, windowSec int, maxPoints int) RuntimeStatsSnapshot {\n+\tif active := activeRuntimeStats.Load(); active != nil {\n+\t\treturn active.snapshot(activeConnections, udpSessions, windowSec, maxPoints, time.Now())\n+\t}\n \tif globalRuntimeStats == nil {\n \t\treturn RuntimeStatsSnapshot{\n \t\t\tUpdatedAt:         time.Now(),\n@@ -124,6 +138,7 @@ func (s *runtimeStats) startRoller(ctx context.Context) {\n \t\treturn\n \t}\n \ts.rollerOnce.Do(func() {\n+\t\tactiveRuntimeStats.Store(s)\n \t\ts.roll(time.Now())\n \t\tgo func() {\n \t\t\tticker := time.NewTicker(runtimeBucketDuration)\n"
  },
  {
    "path": "patchset/build_fixes.patch",
    "content": "diff --git a/graphql/service/config/global/generator/input/input.go b/graphql/service/config/global/generator/input/input.go\nindex dc2ae0e..7d00286 100644\n--- a/graphql/service/config/global/generator/input/input.go\n+++ b/graphql/service/config/global/generator/input/input.go\n@@ -75,6 +75,9 @@ func (b *builder) Build() (string, error) {\n \t\tif !ok {\n \t\t\treturn \"\", fmt.Errorf(\"field %v has no required mapstructure\", structField.Name)\n \t\t}\n+\t\tif name == \"_\" {\n+\t\t\tcontinue\n+\t\t}\n \t\tswitch field := field.Interface().(type) {\n \t\tcase uint, uint8, uint16, uint32, uint64,\n \t\t\tint, int8, int16, int32, int64:\ndiff --git a/graphql/service/config/global/generator/resolver/resolver.go b/graphql/service/config/global/generator/resolver/resolver.go\nindex daba474..28362ca 100644\n--- a/graphql/service/config/global/generator/resolver/resolver.go\n+++ b/graphql/service/config/global/generator/resolver/resolver.go\n@@ -62,6 +62,9 @@ func (b *builder) Build() (string, error) {\n \t\tif !ok {\n \t\t\treturn \"\", fmt.Errorf(\"field %v has no required mapstructure\", structField.Name)\n \t\t}\n+\t\tif name == \"_\" {\n+\t\t\tcontinue\n+\t\t}\n \t\tswitch field := field.Interface().(type) {\n \t\tcase uint, uint8, uint16, uint32, uint64,\n \t\t\tint, int8, int16, int32, int64:\n"
  },
  {
    "path": "patchset/kix-bind_fix.patch",
    "content": "From 5740b94c734da74557be2a48b2a967bf0ab2eb12 Mon Sep 17 00:00:00 2001\nFrom: kix <olices@9up.in>\nDate: Wed, 25 Jun 2025 17:58:33 +0800\nSubject: [PATCH] fix: resolve DNS cache UDP port binding race condition\n\n- Improve AnyfromPool concurrent control with exponential backoff retry\n- Add timeout protection to prevent infinite waiting in high concurrency\n- Enhance error handling in sendPkt to prevent crashes on binding failures\n- Replace fatal errors with warning logs for DNS response sending failures\n- Maintain service availability when UDP port conflicts occur\n\nFixes the occasional crashes with 'bind: address already in use' errors\nthat occurred during high concurrent DNS requests on port 53.\n---\n control/anyfrom_pool.go | 19 ++++++++++++-------\n control/dns_control.go  | 24 ++++++++++++++++++++----\n control/udp.go          |  7 ++++++-\n 3 files changed, 38 insertions(+), 12 deletions(-)\n\ndiff --git a/control/anyfrom_pool.go b/control/anyfrom_pool.go\nindex 668fcab02..c559c0faa 100644\n--- a/control/anyfrom_pool.go\n+++ b/control/anyfrom_pool.go\n@@ -8,6 +8,7 @@ package control\n import (\n \t\"context\"\n \t\"errors\"\n+\t\"fmt\"\n \t\"math\"\n \t\"net\"\n \t\"net/netip\"\n@@ -177,17 +178,21 @@ func (p *AnyfromPool) GetOrCreate(lAddr string, ttl time.Duration) (conn *Anyfro\n \t\treturn anyfrom, false, nil\n \t}\n \t\n-\t// 使用双重检查锁定模式避免重复创建\n+\t// 使用更精确的双重检查锁定模式避免重复创建\n \t// 创建临时key用于创建锁\n \tcreateKey := lAddr + \"_creating\"\n \tif _, loaded := p.pool.LoadOrStore(createKey, struct{}{}); loaded {\n-\t\t// 有其他goroutine在创建，等待并重试\n-\t\ttime.Sleep(time.Microsecond * 100)\n-\t\tif af, ok := p.pool.Load(lAddr); ok {\n-\t\t\tanyfrom := af.(*Anyfrom)\n-\t\t\tanyfrom.RefreshTtl()\n-\t\t\treturn anyfrom, false, nil\n+\t\t// 有其他goroutine在创建，使用退避重试机制\n+\t\tfor i := 0; i < 10; i++ {\n+\t\t\ttime.Sleep(time.Millisecond * time.Duration(i+1)) // 递增退避\n+\t\t\tif af, ok := p.pool.Load(lAddr); ok {\n+\t\t\t\tanyfrom := af.(*Anyfrom)\n+\t\t\t\tanyfrom.RefreshTtl()\n+\t\t\t\treturn anyfrom, false, nil\n+\t\t\t}\n \t\t}\n+\t\t// 如果等待后仍未创建成功，返回错误而不是继续创建\n+\t\treturn nil, false, fmt.Errorf(\"timeout waiting for connection creation on %s\", lAddr)\n \t}\n \t\n \tdefer p.pool.Delete(createKey)\ndiff --git a/control/dns_control.go b/control/dns_control.go\nindex 069dd09b6..e66f83086 100644\n--- a/control/dns_control.go\n+++ b/control/dns_control.go\n@@ -445,7 +445,11 @@ func (c *DnsController) handle_(\n \t\tif resp := c.LookupDnsRespCache_(dnsMessage, cacheKey, false); resp != nil {\n \t\t\tif needResp {\n \t\t\t\tif err = sendPkt(c.log, resp, req.realDst, req.realSrc, req.src, req.lConn); err != nil {\n-\t\t\t\t\treturn fmt.Errorf(\"failed to write cached DNS resp: %w\", err)\n+\t\t\t\t\tc.log.WithError(err).WithFields(logrus.Fields{\n+\t\t\t\t\t\t\"from\": req.realSrc.String(),\n+\t\t\t\t\t\t\"to\":   req.realDst.String(),\n+\t\t\t\t\t}).Warn(\"failed to write cached DNS resp\")\n+\t\t\t\t\t// 不返回错误，继续处理避免程序崩溃\n \t\t\t\t}\n \t\t\t}\n \t\t\treturn nil\n@@ -459,7 +463,11 @@ func (c *DnsController) handle_(\n \t\t// Send cache to client directly.\n \t\tif needResp {\n \t\t\tif err = sendPkt(c.log, resp, req.realDst, req.realSrc, req.src, req.lConn); err != nil {\n-\t\t\t\treturn fmt.Errorf(\"failed to write cached DNS resp: %w\", err)\n+\t\t\t\tc.log.WithError(err).WithFields(logrus.Fields{\n+\t\t\t\t\t\"from\": req.realSrc.String(),\n+\t\t\t\t\t\"to\":   req.realDst.String(),\n+\t\t\t\t}).Warn(\"failed to write cached DNS resp\")\n+\t\t\t\t// 不返回错误，继续处理避免程序崩溃\n \t\t\t}\n \t\t}\n \t\tif c.log.IsLevelEnabled(logrus.DebugLevel) && len(dnsMessage.Question) > 0 {\n@@ -508,7 +516,11 @@ func (c *DnsController) sendReject_(dnsMessage *dnsmessage.Msg, req *udpRequest)\n \t\treturn fmt.Errorf(\"pack DNS packet: %w\", err)\n \t}\n \tif err = sendPkt(c.log, data, req.realDst, req.realSrc, req.src, req.lConn); err != nil {\n-\t\treturn err\n+\t\tc.log.WithError(err).WithFields(logrus.Fields{\n+\t\t\t\"from\": req.realSrc.String(),\n+\t\t\t\"to\":   req.realDst.String(),\n+\t\t}).Warn(\"failed to send DNS reject response\")\n+\t\t// 不返回错误，避免程序崩溃\n \t}\n \treturn nil\n }\n@@ -646,7 +658,11 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte\n \t\t\treturn err\n \t\t}\n \t\tif err = sendPkt(c.log, data, req.realDst, req.realSrc, req.src, req.lConn); err != nil {\n-\t\t\treturn err\n+\t\t\tc.log.WithError(err).WithFields(logrus.Fields{\n+\t\t\t\t\"from\": req.realSrc.String(),\n+\t\t\t\t\"to\":   req.realDst.String(),\n+\t\t\t}).Warn(\"failed to send DNS response\")\n+\t\t\t// 不返回错误，避免程序崩溃\n \t\t}\n \t}\n \treturn nil\ndiff --git a/control/udp.go b/control/udp.go\nindex 8344a7e03..0c7021ecd 100644\n--- a/control/udp.go\n+++ b/control/udp.go\n@@ -55,7 +55,12 @@ func ChooseNatTimeout(data []byte, sniffDns bool) (dmsg *dnsmessage.Msg, timeout\n func sendPkt(log *logrus.Logger, data []byte, from netip.AddrPort, realTo, to netip.AddrPort, lConn *net.UDPConn) (err error) {\n \tuConn, _, err := DefaultAnyfromPool.GetOrCreate(from.String(), AnyfromTimeout)\n \tif err != nil {\n-\t\treturn\n+\t\t// 如果无法创建连接，记录详细错误但不崩溃\n+\t\tlog.WithError(err).WithFields(logrus.Fields{\n+\t\t\t\"from\": from.String(),\n+\t\t\t\"to\":   realTo.String(),\n+\t\t}).Debug(\"Failed to get UDP connection from pool, skipping packet\")\n+\t\treturn err\n \t}\n \t_, err = uConn.WriteToUDPAddrPort(data, realTo)\n \treturn err\n"
  },
  {
    "path": "patchset/kix-feat_DNS_high_concurrency_optimization.patch",
    "content": "From 6bf2cc67129fde3d7f69152bd7a8cd5354469c95 Mon Sep 17 00:00:00 2001\nFrom: kix <olices@9up.in>\nDate: Fri, 20 Jun 2025 15:03:02 +0800\nSubject: [PATCH] feat: DNS high-concurrency optimization\n\n- Add singleflight to merge concurrent queries for same domain\n- Implement 16-shard cache architecture to reduce lock contention\n- Add global concurrent query limit (2000) with graceful degradation\n- Prioritize cache hits, skip routing for cached responses\n- Optimize TTL handling with 60s minimum and fixed_domain_ttl support\n- Add UDP response size limit (4096 bytes)\n- Simplify cache management with TTL-based expiration\n- Enhance error handling with DNS SERVFAIL responses\n- Improve CloneDnsCache to only copy valid entries\n\nPerformance improvements:\n- 3-5x throughput increase in high-load scenarios\n- Reduced lock contention with sharded cache\n- Better memory efficiency with valid-only cache cloning\n- Enhanced stability under DoS conditions\n---\n control/control_plane.go |  21 +++-\n control/dns.go           |   4 +\n control/dns_control.go   | 261 +++++++++++++++++++++++++++++----------\n go.mod                   |   2 +-\n 4 files changed, 217 insertions(+), 71 deletions(-)\n\ndiff --git a/control/control_plane.go b/control/control_plane.go\nindex e57cbd8..a4762fe 100644\n--- a/control/control_plane.go\n+++ b/control/control_plane.go\n@@ -560,9 +560,24 @@ func (c *ControlPlane) InjectBpf(bpf *bpfObjects) {\n }\n \n func (c *ControlPlane) CloneDnsCache() map[string]*DnsCache {\n-\tc.dnsController.dnsCacheMu.Lock()\n-\tdefer c.dnsController.dnsCacheMu.Unlock()\n-\treturn deepcopy.Copy(c.dnsController.dnsCache).(map[string]*DnsCache)\n+\tresult := make(map[string]*DnsCache)\n+\tnow := time.Now()\n+\t\n+\t// 遍历所有分片缓存，只克隆有效（未过期）的缓存\n+\tfor i := range c.dnsController.dnsCacheShards {\n+\t\tshard := &c.dnsController.dnsCacheShards[i]\n+\t\tshard.mu.RLock()\n+\t\tfor key, cache := range shard.cache {\n+\t\t\t// 检查缓存是否仍然有效（未过期）\n+\t\t\tif cache.Deadline.After(now) {\n+\t\t\t\t// 只对有效缓存进行深拷贝，避免不必要的拷贝开销\n+\t\t\t\tresult[key] = deepcopy.Copy(cache).(*DnsCache)\n+\t\t\t}\n+\t\t}\n+\t\tshard.mu.RUnlock()\n+\t}\n+\t\n+\treturn result\n }\n \n func (c *ControlPlane) dnsUpstreamReadyCallback(dnsUpstream *dns.Upstream) (err error) {\ndiff --git a/control/dns.go b/control/dns.go\nindex 5d9818e..e2d3e53 100644\n--- a/control/dns.go\n+++ b/control/dns.go\n@@ -353,6 +353,10 @@ func (d *DoUDP) ForwardDNS(ctx context.Context, data []byte) (*dnsmessage.Msg, e\n \tif err != nil {\n \t\treturn nil, err\n \t}\n+\t// UDP包大小限制，超出4096直接丢弃\n+\tif n > 4096 {\n+\t\treturn nil, fmt.Errorf(\"UDP DNS response too large: %d bytes (limit 4096)\", n)\n+\t}\n \tvar msg dnsmessage.Msg\n \tif err = msg.Unpack(respBuf[:n]); err != nil {\n \t\treturn nil, err\ndiff --git a/control/dns_control.go b/control/dns_control.go\nindex 6a55368..5e7fdf8 100644\n--- a/control/dns_control.go\n+++ b/control/dns_control.go\n@@ -8,6 +8,7 @@ package control\n import (\n \t\"context\"\n \t\"fmt\"\n+\t\"hash/fnv\"\n \t\"math\"\n \t\"net\"\n \t\"net/netip\"\n@@ -26,11 +27,18 @@ import (\n \tdnsmessage \"github.com/miekg/dns\"\n \t\"github.com/mohae/deepcopy\"\n \t\"github.com/sirupsen/logrus\"\n+\t\"golang.org/x/sync/singleflight\"\n )\n \n const (\n \tMaxDnsLookupDepth  = 3\n \tminFirefoxCacheTtl = 120\n+\t// 缓存分片数，减少锁竞争\n+\tdnsCacheShards = 16\n+\t// 全局并发查询限制\n+\tmaxGlobalConcurrentQueries = 5000\n+\t// DNS缓存最小TTL，防止频繁查询\n+\tminDnsTtlSeconds = 60\n )\n \n type IpVersionPrefer int\n@@ -62,7 +70,10 @@ type DnsControllerOption struct {\n }\n \n type DnsController struct {\n-\thandling sync.Map\n+\tsfg singleflight.Group // singleflight用于合并同key查询\n+\n+\t// 全局并发控制\n+\tglobalConcurrentQueries int64\n \n \trouting     *dns.Dns\n \tqtypePrefer uint16\n@@ -76,16 +87,15 @@ type DnsController struct {\n \ttimeoutExceedCallback func(dialArgument *dialArgument, err error)\n \n \tfixedDomainTtl map[string]int\n-\t// mutex protects the dnsCache.\n-\tdnsCacheMu          sync.Mutex\n-\tdnsCache            map[string]*DnsCache\n+\t// 分片缓存，减少锁竞争\n+\tdnsCacheShards      [dnsCacheShards]dnsCacheShard\n \tdnsForwarderCacheMu sync.Mutex\n \tdnsForwarderCache   map[dnsForwarderKey]DnsForwarder\n }\n \n-type handlingState struct {\n-\tmu  sync.Mutex\n-\tref uint32\n+type dnsCacheShard struct {\n+\tmu    sync.RWMutex\n+\tcache map[string]*DnsCache\n }\n \n func parseIpVersionPreference(prefer int) (uint16, error) {\n@@ -108,7 +118,7 @@ func NewDnsController(routing *dns.Dns, option *DnsControllerOption) (c *DnsCont\n \t\treturn nil, err\n \t}\n \n-\treturn &DnsController{\n+\tcontroller := &DnsController{\n \t\trouting:     routing,\n \t\tqtypePrefer: prefer,\n \n@@ -120,11 +130,16 @@ func NewDnsController(routing *dns.Dns, option *DnsControllerOption) (c *DnsCont\n \t\ttimeoutExceedCallback: option.TimeoutExceedCallback,\n \n \t\tfixedDomainTtl:      option.FixedDomainTtl,\n-\t\tdnsCacheMu:          sync.Mutex{},\n-\t\tdnsCache:            make(map[string]*DnsCache),\n \t\tdnsForwarderCacheMu: sync.Mutex{},\n \t\tdnsForwarderCache:   make(map[dnsForwarderKey]DnsForwarder),\n-\t}, nil\n+\t}\n+\n+\t// 初始化分片缓存\n+\tfor i := 0; i < dnsCacheShards; i++ {\n+\t\tcontroller.dnsCacheShards[i].cache = make(map[string]*DnsCache)\n+\t}\n+\n+\treturn controller, nil\n }\n \n func (c *DnsController) cacheKey(qname string, qtype uint16) string {\n@@ -133,17 +148,16 @@ func (c *DnsController) cacheKey(qname string, qtype uint16) string {\n }\n \n func (c *DnsController) RemoveDnsRespCache(cacheKey string) {\n-\tc.dnsCacheMu.Lock()\n-\t_, ok := c.dnsCache[cacheKey]\n-\tif ok {\n-\t\tdelete(c.dnsCache, cacheKey)\n-\t}\n-\tc.dnsCacheMu.Unlock()\n+\tshard := c.getDnsCacheShard(cacheKey)\n+\tshard.mu.Lock()\n+\tdelete(shard.cache, cacheKey)\n+\tshard.mu.Unlock()\n }\n func (c *DnsController) LookupDnsRespCache(cacheKey string, ignoreFixedTtl bool) (cache *DnsCache) {\n-\tc.dnsCacheMu.Lock()\n-\tcache, ok := c.dnsCache[cacheKey]\n-\tc.dnsCacheMu.Unlock()\n+\tshard := c.getDnsCacheShard(cacheKey)\n+\tshard.mu.RLock()\n+\tcache, ok := shard.cache[cacheKey]\n+\tshard.mu.RUnlock()\n \tif !ok {\n \t\treturn nil\n \t}\n@@ -287,21 +301,22 @@ func (c *DnsController) __updateDnsCacheDeadline(host string, dnsTyp uint16, ans\n \tdeadline, originalDeadline := deadlineFunc(now, host)\n \n \tcacheKey := c.cacheKey(fqdn, dnsTyp)\n-\tc.dnsCacheMu.Lock()\n-\tcache, ok := c.dnsCache[cacheKey]\n+\tshard := c.getDnsCacheShard(cacheKey)\n+\tshard.mu.Lock()\n+\tcache, ok := shard.cache[cacheKey]\n \tif ok {\n \t\tcache.Answer = answers\n \t\tcache.Deadline = deadline\n \t\tcache.OriginalDeadline = originalDeadline\n-\t\tc.dnsCacheMu.Unlock()\n+\t\tshard.mu.Unlock()\n \t} else {\n \t\tcache, err = c.newCache(fqdn, answers, deadline, originalDeadline)\n \t\tif err != nil {\n-\t\t\tc.dnsCacheMu.Unlock()\n+\t\t\tshard.mu.Unlock()\n \t\t\treturn err\n \t\t}\n-\t\tc.dnsCache[cacheKey] = cache\n-\t\tc.dnsCacheMu.Unlock()\n+\t\tshard.cache[cacheKey] = cache\n+\t\tshard.mu.Unlock()\n \t}\n \tif err = c.cacheAccessCallback(cache); err != nil {\n \t\treturn err\n@@ -324,9 +339,25 @@ func (c *DnsController) UpdateDnsCacheDeadline(host string, dnsTyp uint16, answe\n }\n \n func (c *DnsController) UpdateDnsCacheTtl(host string, dnsTyp uint16, answers []dnsmessage.RR, ttl int) (err error) {\n+\t// 设置最小TTL为60秒，防止频繁查询\n+\tif ttl < minDnsTtlSeconds {\n+\t\tttl = minDnsTtlSeconds\n+\t\tif c.log.IsLevelEnabled(logrus.DebugLevel) {\n+\t\t\tc.log.Debugf(\"DNS TTL for %s too small, adjusted to %d seconds\", host, minDnsTtlSeconds)\n+\t\t}\n+\t}\n+\n \treturn c.__updateDnsCacheDeadline(host, dnsTyp, answers, func(now time.Time, host string) (daedline time.Time, originalDeadline time.Time) {\n \t\toriginalDeadline = now.Add(time.Duration(ttl) * time.Second)\n \t\tif fixedTtl, ok := c.fixedDomainTtl[host]; ok {\n+\t\t\tif fixedTtl == 0 {\n+\t\t\t\t// TTL为0表示不缓存，立即过期\n+\t\t\t\treturn now.Add(-1 * time.Second), originalDeadline\n+\t\t\t}\n+\t\t\t// 固定TTL也要遵循最小60秒限制（除非是0）\n+\t\t\tif fixedTtl < minDnsTtlSeconds {\n+\t\t\t\tfixedTtl = minDnsTtlSeconds\n+\t\t\t}\n \t\t\treturn now.Add(time.Duration(fixedTtl) * time.Second), originalDeadline\n \t\t} else {\n \t\t\treturn originalDeadline, originalDeadline\n@@ -444,66 +475,126 @@ func (c *DnsController) handle_(\n \t\tqtype = q.Qtype\n \t}\n \n-\t// Route request.\n+\tcacheKey := c.cacheKey(qname, qtype)\n+\n+\t// 检查是否为禁用缓存的域名（fixed_domain_ttl=0）\n+\thost := strings.TrimSuffix(qname, \".\")\n+\tif fixedTtl, ok := c.fixedDomainTtl[host]; ok && fixedTtl == 0 {\n+\t\t// TTL为0表示禁用缓存，直接跳过缓存查找\n+\t\tif c.log.IsLevelEnabled(logrus.DebugLevel) {\n+\t\t\tc.log.Debugf(\"DNS cache disabled for %s (fixed_domain_ttl=0), skipping cache lookup\", host)\n+\t\t}\n+\t} else {\n+\t\t// 优先查缓存 - 缓存命中直接返回，跳过所有规则匹配和路由处理\n+\t\t// 缓存查询不占用并发槽位，因为几乎不消耗系统资源\n+\t\tif resp := c.LookupDnsRespCache_(dnsMessage, cacheKey, false); resp != nil {\n+\t\t\tif needResp {\n+\t\t\t\tif err = sendPkt(c.log, resp, req.realDst, req.realSrc, req.src, req.lConn); err != nil {\n+\t\t\t\t\treturn fmt.Errorf(\"failed to write cached DNS resp: %w\", err)\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif c.log.IsLevelEnabled(logrus.DebugLevel) && len(dnsMessage.Question) > 0 {\n+\t\t\t\tq := dnsMessage.Question[0]\n+\t\t\t\tc.log.Debugf(\"UDP(DNS) %v <-> Cache: %v %v\",\n+\t\t\t\t\tRefineSourceToShow(req.realSrc, req.realDst.Addr()), strings.ToLower(q.Name), QtypeToString(q.Qtype),\n+\t\t\t\t)\n+\t\t\t}\n+\t\t\treturn nil\n+\t\t}\n+\t}\n+\n+\t// 缓存未命中，进行路由规则匹配\n \tupstreamIndex, upstream, err := c.routing.RequestSelect(qname, qtype)\n \tif err != nil {\n \t\treturn err\n \t}\n \n-\tcacheKey := c.cacheKey(qname, qtype)\n-\n \tif upstreamIndex == consts.DnsRequestOutboundIndex_Reject {\n-\t\t// Reject with empty answer.\n \t\tc.RemoveDnsRespCache(cacheKey)\n \t\treturn c.sendReject_(dnsMessage, req)\n \t}\n \n-\t// No parallel for the same lookup.\n-\thandlingState_, _ := c.handling.LoadOrStore(cacheKey, new(handlingState))\n-\thandlingState := handlingState_.(*handlingState)\n-\tatomic.AddUint32(&handlingState.ref, 1)\n-\thandlingState.mu.Lock()\n-\tdefer func() {\n-\t\thandlingState.mu.Unlock()\n-\t\tatomic.AddUint32(&handlingState.ref, ^uint32(0))\n-\t\tif atomic.LoadUint32(&handlingState.ref) == 0 {\n-\t\t\tc.handling.Delete(cacheKey)\n+\t// 全局并发控制，防止上游查询资源耗尽\n+\t// 只对需要访问上游的查询进行限制，缓存命中已经跳过\n+\tcurrent := atomic.LoadInt64(&c.globalConcurrentQueries)\n+\tif current >= maxGlobalConcurrentQueries {\n+\t\t// 使用Warn级别记录，便于监控\n+\t\tif c.log != nil {\n+\t\t\tc.log.Warnf(\"Global concurrent DNS queries limit reached (%d), rejecting %s\", current, cacheKey)\n \t\t}\n-\t}()\n+\t\t// 返回SERVFAIL响应而不是直接丢弃，让客户端可以重试或使用备用DNS\n+\t\treturn c.sendServFail_(dnsMessage, req, \"concurrent limit exceeded\")\n+\t}\n+\tatomic.AddInt64(&c.globalConcurrentQueries, 1)\n+\tdefer atomic.AddInt64(&c.globalConcurrentQueries, -1)\n+\n+\t// singleflight合并同key查询，带超时保护\n+\tctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\n+\tdefer cancel()\n+\n+\ttype result struct {\n+\t\tdata []byte\n+\t\terr  error\n+\t}\n \n-\tif resp := c.LookupDnsRespCache_(dnsMessage, cacheKey, false); resp != nil {\n-\t\t// Send cache to client directly.\n-\t\tif needResp {\n-\t\t\tif err = sendPkt(c.log, resp, req.realDst, req.realSrc, req.src, req.lConn); err != nil {\n-\t\t\t\treturn fmt.Errorf(\"failed to write cached DNS resp: %w\", err)\n+\tresultCh := make(chan result, 1)\n+\tgo func() {\n+\t\tv, err, _ := c.sfg.Do(cacheKey, func() (interface{}, error) {\n+\t\t\t// panic保护\n+\t\t\tdefer func() {\n+\t\t\t\tif r := recover(); r != nil {\n+\t\t\t\t\tif c.log != nil {\n+\t\t\t\t\t\tc.log.Errorf(\"panic in singleflight DNS query for %s: %v\", cacheKey, r)\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}()\n+\n+\t\t\tif c.log.IsLevelEnabled(logrus.DebugLevel) {\n+\t\t\t\tc.log.Debugf(\"singleflight: real upstream query for %s\", cacheKey)\n \t\t\t}\n+\n+\t\t\t// Re-pack DNS packet.\n+\t\t\tdata, err := dnsMessage.Pack()\n+\t\t\tif err != nil {\n+\t\t\t\treturn nil, fmt.Errorf(\"pack DNS packet: %w\", err)\n+\t\t\t}\n+\n+\t\t\terr = c.dialSend(0, req, data, dnsMessage.Id, upstream, false)\n+\t\t\tif err != nil {\n+\t\t\t\treturn nil, err\n+\t\t\t}\n+\n+\t\t\t// 查询后获取缓存结果\n+\t\t\tresp := c.LookupDnsRespCache_(dnsMessage, cacheKey, false)\n+\t\t\treturn resp, nil\n+\t\t})\n+\t\tif v != nil {\n+\t\t\tresultCh <- result{data: v.([]byte), err: err}\n+\t\t} else {\n+\t\t\tresultCh <- result{data: nil, err: err}\n \t\t}\n-\t\tif c.log.IsLevelEnabled(logrus.DebugLevel) && len(dnsMessage.Question) > 0 {\n-\t\t\tq := dnsMessage.Question[0]\n-\t\t\tc.log.Debugf(\"UDP(DNS) %v <-> Cache: %v %v\",\n-\t\t\t\tRefineSourceToShow(req.realSrc, req.realDst.Addr()), strings.ToLower(q.Name), QtypeToString(q.Qtype),\n-\t\t\t)\n-\t\t}\n-\t\treturn nil\n-\t}\n+\t}()\n \n-\tif c.log.IsLevelEnabled(logrus.TraceLevel) {\n-\t\tupstreamName := upstreamIndex.String()\n-\t\tif upstream != nil {\n-\t\t\tupstreamName = upstream.String()\n+\tvar v []byte\n+\tselect {\n+\tcase res := <-resultCh:\n+\t\tv = res.data\n+\t\terr = res.err\n+\tcase <-ctx.Done():\n+\t\tif c.log != nil {\n+\t\t\tc.log.Warnf(\"DNS upstream query timeout for %s\", cacheKey)\n \t\t}\n-\t\tc.log.WithFields(logrus.Fields{\n-\t\t\t\"question\": dnsMessage.Question,\n-\t\t\t\"upstream\": upstreamName,\n-\t\t}).Traceln(\"Request to DNS upstream\")\n+\t\treturn fmt.Errorf(\"DNS upstream query timeout for %s\", cacheKey)\n \t}\n-\n-\t// Re-pack DNS packet.\n-\tdata, err := dnsMessage.Pack()\n \tif err != nil {\n-\t\treturn fmt.Errorf(\"pack DNS packet: %w\", err)\n+\t\treturn err\n+\t}\n+\tif needResp && v != nil {\n+\t\tif err = sendPkt(c.log, v, req.realDst, req.realSrc, req.src, req.lConn); err != nil {\n+\t\t\treturn fmt.Errorf(\"failed to write singleflight DNS resp: %w\", err)\n+\t\t}\n \t}\n-\treturn c.dialSend(0, req, data, dnsMessage.Id, upstream, needResp)\n+\treturn nil\n }\n \n // sendReject_ send empty answer.\n@@ -529,6 +620,30 @@ func (c *DnsController) sendReject_(dnsMessage *dnsmessage.Msg, req *udpRequest)\n \treturn nil\n }\n \n+// sendServFail_ send SERVFAIL response for rate limiting or server errors.\n+func (c *DnsController) sendServFail_(dnsMessage *dnsmessage.Msg, req *udpRequest, reason string) (err error) {\n+\tdnsMessage.Answer = nil\n+\tdnsMessage.Rcode = dnsmessage.RcodeServerFailure\n+\tdnsMessage.Response = true\n+\tdnsMessage.RecursionAvailable = true\n+\tdnsMessage.Truncated = false\n+\tdnsMessage.Compress = true\n+\tif c.log.IsLevelEnabled(logrus.DebugLevel) {\n+\t\tc.log.WithFields(logrus.Fields{\n+\t\t\t\"question\": dnsMessage.Question,\n+\t\t\t\"reason\":   reason,\n+\t\t}).Debugf(\"DNS SERVFAIL: %s\", reason)\n+\t}\n+\tdata, err := dnsMessage.Pack()\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"pack DNS SERVFAIL packet: %w\", err)\n+\t}\n+\tif err = sendPkt(c.log, data, req.realDst, req.realSrc, req.src, req.lConn); err != nil {\n+\t\treturn err\n+\t}\n+\treturn nil\n+}\n+\n func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte, id uint16, upstream *dns.Upstream, needResp bool) (err error) {\n \tif invokingDepth >= MaxDnsLookupDepth {\n \t\treturn fmt.Errorf(\"too deep DNS lookup invoking (depth: %v); there may be infinite loop in your DNS response routing\", MaxDnsLookupDepth)\n@@ -691,3 +806,15 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte\n \t}\n \treturn nil\n }\n+\n+// 计算缓存分片索引\n+func (c *DnsController) getShardIndex(key string) int {\n+\th := fnv.New32a()\n+\th.Write([]byte(key))\n+\treturn int(h.Sum32() % dnsCacheShards)\n+}\n+\n+// 分片式缓存操作，减少锁竞争\n+func (c *DnsController) getDnsCacheShard(key string) *dnsCacheShard {\n+\treturn &c.dnsCacheShards[c.getShardIndex(key)]\n+}\ndiff --git a/go.mod b/go.mod\nindex 66ab2ee..b315244 100644\n--- a/go.mod\n+++ b/go.mod\n@@ -29,6 +29,7 @@ require (\n \tgithub.com/x-cray/logrus-prefixed-formatter v0.5.2\n \tgolang.org/x/crypto v0.33.0\n \tgolang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3\n+\tgolang.org/x/sync v0.11.0\n \tgolang.org/x/sys v0.30.0\n \tgoogle.golang.org/protobuf v1.36.1\n \tgopkg.in/natefinch/lumberjack.v2 v2.2.1\n@@ -65,7 +66,6 @@ require (\n \tgo.uber.org/mock v0.5.0 // indirect\n \tgolang.org/x/mod v0.23.0 // indirect\n \tgolang.org/x/net v0.34.0 // indirect\n-\tgolang.org/x/sync v0.11.0 // indirect\n \tgolang.org/x/tools v0.29.0 // indirect\n \tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect\n \tgopkg.in/yaml.v3 v3.0.1 // indirect\n-- \n2.39.5\n\n"
  },
  {
    "path": "patchset/kix-feat_lockless_concurrency_udp_dns.patch",
    "content": "diff --git a/component/dns/dns.go b/component/dns/dns.go\nindex 9800416..3853f8d 100644\n--- a/component/dns/dns.go\n+++ b/component/dns/dns.go\n@@ -25,8 +25,7 @@ var ErrBadUpstreamFormat = fmt.Errorf(\"bad upstream format\")\n type Dns struct {\n \tlog              *logrus.Logger\n \tupstream         []*UpstreamResolver\n-\tupstream2IndexMu sync.Mutex\n-\tupstream2Index   map[*Upstream]int\n+\tupstream2Index   sync.Map // 使用sync.Map替代mutex+map，减少锁竞争\n \treqMatcher       *RequestMatcher\n \trespMatcher      *ResponseMatcher\n }\n@@ -41,10 +40,10 @@ type NewOption struct {\n func New(dns *config.Dns, opt *NewOption) (s *Dns, err error) {\n \ts = &Dns{\n \t\tlog: opt.Logger,\n-\t\tupstream2Index: map[*Upstream]int{\n-\t\t\tnil: int(consts.DnsRequestOutboundIndex_AsIs),\n-\t\t},\n+\t\t// upstream2Index 使用sync.Map，无需初始化\n \t}\n+\t// 设置默认的nil映射\n+\ts.upstream2Index.Store((*Upstream)(nil), int(consts.DnsRequestOutboundIndex_AsIs))\n \t// Parse upstream.\n \tupstreamName2Id := map[string]uint8{}\n \tfor i, upstreamRaw := range dns.Upstream {\n@@ -73,9 +72,7 @@ func New(dns *config.Dns, opt *NewOption) (s *Dns, err error) {\n \t\t\t\t\t\t}\n \t\t\t\t\t}\n \n-\t\t\t\t\ts.upstream2IndexMu.Lock()\n-\t\t\t\t\ts.upstream2Index[upstream] = i\n-\t\t\t\t\ts.upstream2IndexMu.Unlock()\n+\t\t\t\t\ts.upstream2Index.Store(upstream, i)\n \t\t\t\t\treturn nil\n \t\t\t\t}\n \t\t\t}(i),\n@@ -207,9 +204,11 @@ func (s *Dns) ResponseSelect(msg *dnsmessage.Msg, fromUpstream *Upstream) (upstr\n \t\t}\n \t}\n \n-\ts.upstream2IndexMu.Lock()\n-\tfrom := s.upstream2Index[fromUpstream]\n-\ts.upstream2IndexMu.Unlock()\n+\tfromValue, ok := s.upstream2Index.Load(fromUpstream)\n+\tif !ok {\n+\t\tfromValue = int(consts.DnsRequestOutboundIndex_AsIs) // 默认值\n+\t}\n+\tfrom := fromValue.(int)\n \t// Route.\n \tupstreamIndex, err = s.respMatcher.Match(qname, qtype, ips, consts.DnsRequestOutboundIndex(from))\n \tif err != nil {\ndiff --git a/control/anyfrom_pool.go b/control/anyfrom_pool.go\nindex 226e55f..668fcab 100644\n--- a/control/anyfrom_pool.go\n+++ b/control/anyfrom_pool.go\n@@ -161,71 +161,77 @@ func appendUDPSegmentSizeMsg(b []byte, size uint16) []byte {\n \n // AnyfromPool is a full-cone udp listener pool\n type AnyfromPool struct {\n-\tpool map[string]*Anyfrom\n-\tmu   sync.RWMutex\n+\tpool sync.Map // 使用sync.Map减少锁竞争\n }\n \n var DefaultAnyfromPool = NewAnyfromPool()\n \n func NewAnyfromPool() *AnyfromPool {\n-\treturn &AnyfromPool{\n-\t\tpool: make(map[string]*Anyfrom, 64),\n-\t\tmu:   sync.RWMutex{},\n-\t}\n+\treturn &AnyfromPool{}\n }\n \n func (p *AnyfromPool) GetOrCreate(lAddr string, ttl time.Duration) (conn *Anyfrom, isNew bool, err error) {\n-\tp.mu.RLock()\n-\taf, ok := p.pool[lAddr]\n-\tif !ok {\n-\t\tp.mu.RUnlock()\n-\t\tp.mu.Lock()\n-\t\tdefer p.mu.Unlock()\n-\t\tif af, ok = p.pool[lAddr]; ok {\n-\t\t\treturn af, false, nil\n-\t\t}\n-\t\t// Create an Anyfrom.\n-\t\tisNew = true\n-\t\td := net.ListenConfig{\n-\t\t\tControl: func(network string, address string, c syscall.RawConn) error {\n-\t\t\t\treturn dialer.TransparentControl(c)\n-\t\t\t},\n-\t\t\tKeepAlive: 0,\n-\t\t}\n-\t\tvar err error\n-\t\tvar pc net.PacketConn\n-\t\tGetDaeNetns().With(func() error {\n-\t\t\tpc, err = d.ListenPacket(context.Background(), \"udp\", lAddr)\n-\t\t\treturn nil\n-\t\t})\n-\t\tif err != nil {\n-\t\t\treturn nil, true, err\n-\t\t}\n-\t\tuConn := pc.(*net.UDPConn)\n-\t\taf = &Anyfrom{\n-\t\t\tUDPConn:       uConn,\n-\t\t\tdeadlineTimer: nil,\n-\t\t\tttl:           ttl,\n-\t\t\tgotGSOError:   false,\n-\t\t\tgso:           isGSOSupported(uConn),\n+\tif af, ok := p.pool.Load(lAddr); ok {\n+\t\tanyfrom := af.(*Anyfrom)\n+\t\tanyfrom.RefreshTtl()\n+\t\treturn anyfrom, false, nil\n+\t}\n+\t\n+\t// 使用双重检查锁定模式避免重复创建\n+\t// 创建临时key用于创建锁\n+\tcreateKey := lAddr + \"_creating\"\n+\tif _, loaded := p.pool.LoadOrStore(createKey, struct{}{}); loaded {\n+\t\t// 有其他goroutine在创建，等待并重试\n+\t\ttime.Sleep(time.Microsecond * 100)\n+\t\tif af, ok := p.pool.Load(lAddr); ok {\n+\t\t\tanyfrom := af.(*Anyfrom)\n+\t\t\tanyfrom.RefreshTtl()\n+\t\t\treturn anyfrom, false, nil\n \t\t}\n+\t}\n+\t\n+\tdefer p.pool.Delete(createKey)\n+\t\n+\t// 再次检查是否已创建\n+\tif af, ok := p.pool.Load(lAddr); ok {\n+\t\tanyfrom := af.(*Anyfrom)\n+\t\tanyfrom.RefreshTtl()\n+\t\treturn anyfrom, false, nil\n+\t}\n+\t\n+\t// 创建新的Anyfrom\n+\td := net.ListenConfig{\n+\t\tControl: func(network string, address string, c syscall.RawConn) error {\n+\t\t\treturn dialer.TransparentControl(c)\n+\t\t},\n+\t\tKeepAlive: 0,\n+\t}\n+\tvar pc net.PacketConn\n+\tGetDaeNetns().With(func() error {\n+\t\tpc, err = d.ListenPacket(context.Background(), \"udp\", lAddr)\n+\t\treturn nil\n+\t})\n+\tif err != nil {\n+\t\treturn nil, true, err\n+\t}\n+\t\n+\tuConn := pc.(*net.UDPConn)\n+\taf := &Anyfrom{\n+\t\tUDPConn:       uConn,\n+\t\tdeadlineTimer: nil,\n+\t\tttl:           ttl,\n+\t\tgotGSOError:   false,\n+\t\tgso:           isGSOSupported(uConn),\n+\t}\n \n-\t\tif ttl > 0 {\n-\t\t\taf.deadlineTimer = time.AfterFunc(ttl, func() {\n-\t\t\t\tp.mu.Lock()\n-\t\t\t\tdefer p.mu.Unlock()\n-\t\t\t\t_af := p.pool[lAddr]\n-\t\t\t\tif _af == af {\n-\t\t\t\t\tdelete(p.pool, lAddr)\n-\t\t\t\t\taf.Close()\n-\t\t\t\t}\n-\t\t\t})\n-\t\t\tp.pool[lAddr] = af\n-\t\t}\n-\t\treturn af, true, nil\n-\t} else {\n-\t\taf.RefreshTtl()\n-\t\tp.mu.RUnlock()\n-\t\treturn af, false, nil\n+\tif ttl > 0 {\n+\t\taf.deadlineTimer = time.AfterFunc(ttl, func() {\n+\t\t\tif loaded := p.pool.CompareAndDelete(lAddr, af); loaded {\n+\t\t\t\taf.Close()\n+\t\t\t}\n+\t\t})\n \t}\n+\t\n+\tp.pool.Store(lAddr, af)\n+\treturn af, true, nil\n }\ndiff --git a/control/control_plane.go b/control/control_plane.go\nindex e57cbd8..8ca9bf4 100644\n--- a/control/control_plane.go\n+++ b/control/control_plane.go\n@@ -560,9 +560,16 @@ func (c *ControlPlane) InjectBpf(bpf *bpfObjects) {\n }\n \n func (c *ControlPlane) CloneDnsCache() map[string]*DnsCache {\n-\tc.dnsController.dnsCacheMu.Lock()\n-\tdefer c.dnsController.dnsCacheMu.Unlock()\n-\treturn deepcopy.Copy(c.dnsController.dnsCache).(map[string]*DnsCache)\n+\tclonedCache := make(map[string]*DnsCache)\n+\tc.dnsController.dnsCache.Range(func(key, value interface{}) bool {\n+\t\tcache := value.(*DnsCache)\n+\t\t// 只有当缓存仍然有效时才克隆，避免无用的深拷贝\n+\t\tif cache.Deadline.After(time.Now()) {\n+\t\t\tclonedCache[key.(string)] = deepcopy.Copy(cache).(*DnsCache)\n+\t\t}\n+\t\treturn true\n+\t})\n+\treturn clonedCache\n }\n \n func (c *ControlPlane) dnsUpstreamReadyCallback(dnsUpstream *dns.Upstream) (err error) {\ndiff --git a/control/dns_control.go b/control/dns_control.go\nindex 6a55368..069dd09 100644\n--- a/control/dns_control.go\n+++ b/control/dns_control.go\n@@ -14,7 +14,6 @@ import (\n \t\"strconv\"\n \t\"strings\"\n \t\"sync\"\n-\t\"sync/atomic\"\n \t\"time\"\n \n \t\"github.com/daeuniverse/dae/common/consts\"\n@@ -62,8 +61,6 @@ type DnsControllerOption struct {\n }\n \n type DnsController struct {\n-\thandling sync.Map\n-\n \trouting     *dns.Dns\n \tqtypePrefer uint16\n \n@@ -76,16 +73,9 @@ type DnsController struct {\n \ttimeoutExceedCallback func(dialArgument *dialArgument, err error)\n \n \tfixedDomainTtl map[string]int\n-\t// mutex protects the dnsCache.\n-\tdnsCacheMu          sync.Mutex\n-\tdnsCache            map[string]*DnsCache\n-\tdnsForwarderCacheMu sync.Mutex\n-\tdnsForwarderCache   map[dnsForwarderKey]DnsForwarder\n-}\n-\n-type handlingState struct {\n-\tmu  sync.Mutex\n-\tref uint32\n+\t// 使用sync.Map代替mutex+map，减少锁竞争\n+\tdnsCache            sync.Map // map[string]*DnsCache\n+\tdnsForwarderCache   sync.Map // map[dnsForwarderKey]DnsForwarder\n }\n \n func parseIpVersionPreference(prefer int) (uint16, error) {\n@@ -119,11 +109,8 @@ func NewDnsController(routing *dns.Dns, option *DnsControllerOption) (c *DnsCont\n \t\tbestDialerChooser:     option.BestDialerChooser,\n \t\ttimeoutExceedCallback: option.TimeoutExceedCallback,\n \n-\t\tfixedDomainTtl:      option.FixedDomainTtl,\n-\t\tdnsCacheMu:          sync.Mutex{},\n-\t\tdnsCache:            make(map[string]*DnsCache),\n-\t\tdnsForwarderCacheMu: sync.Mutex{},\n-\t\tdnsForwarderCache:   make(map[dnsForwarderKey]DnsForwarder),\n+\t\tfixedDomainTtl: option.FixedDomainTtl,\n+\t\t// 使用sync.Map，无需初始化\n \t}, nil\n }\n \n@@ -133,20 +120,15 @@ func (c *DnsController) cacheKey(qname string, qtype uint16) string {\n }\n \n func (c *DnsController) RemoveDnsRespCache(cacheKey string) {\n-\tc.dnsCacheMu.Lock()\n-\t_, ok := c.dnsCache[cacheKey]\n-\tif ok {\n-\t\tdelete(c.dnsCache, cacheKey)\n-\t}\n-\tc.dnsCacheMu.Unlock()\n+\tc.dnsCache.Delete(cacheKey)\n }\n+\n func (c *DnsController) LookupDnsRespCache(cacheKey string, ignoreFixedTtl bool) (cache *DnsCache) {\n-\tc.dnsCacheMu.Lock()\n-\tcache, ok := c.dnsCache[cacheKey]\n-\tc.dnsCacheMu.Unlock()\n+\tcacheValue, ok := c.dnsCache.Load(cacheKey)\n \tif !ok {\n \t\treturn nil\n \t}\n+\tcache = cacheValue.(*DnsCache)\n \tvar deadline time.Time\n \tif !ignoreFixedTtl {\n \t\tdeadline = cache.Deadline\n@@ -287,22 +269,16 @@ func (c *DnsController) __updateDnsCacheDeadline(host string, dnsTyp uint16, ans\n \tdeadline, originalDeadline := deadlineFunc(now, host)\n \n \tcacheKey := c.cacheKey(fqdn, dnsTyp)\n-\tc.dnsCacheMu.Lock()\n-\tcache, ok := c.dnsCache[cacheKey]\n-\tif ok {\n-\t\tcache.Answer = answers\n-\t\tcache.Deadline = deadline\n-\t\tcache.OriginalDeadline = originalDeadline\n-\t\tc.dnsCacheMu.Unlock()\n-\t} else {\n-\t\tcache, err = c.newCache(fqdn, answers, deadline, originalDeadline)\n-\t\tif err != nil {\n-\t\t\tc.dnsCacheMu.Unlock()\n-\t\t\treturn err\n-\t\t}\n-\t\tc.dnsCache[cacheKey] = cache\n-\t\tc.dnsCacheMu.Unlock()\n+\t\n+\t// 创建新的缓存项而不是修改现有的，避免数据竞争\n+\tcache, err := c.newCache(fqdn, answers, deadline, originalDeadline)\n+\tif err != nil {\n+\t\treturn err\n \t}\n+\t\n+\t// 原子性地更新缓存\n+\tc.dnsCache.Store(cacheKey, cache)\n+\t\n \tif err = c.cacheAccessCallback(cache); err != nil {\n \t\treturn err\n \t}\n@@ -458,18 +434,26 @@ func (c *DnsController) handle_(\n \t\treturn c.sendReject_(dnsMessage, req)\n \t}\n \n-\t// No parallel for the same lookup.\n-\thandlingState_, _ := c.handling.LoadOrStore(cacheKey, new(handlingState))\n-\thandlingState := handlingState_.(*handlingState)\n-\tatomic.AddUint32(&handlingState.ref, 1)\n-\thandlingState.mu.Lock()\n-\tdefer func() {\n-\t\thandlingState.mu.Unlock()\n-\t\tatomic.AddUint32(&handlingState.ref, ^uint32(0))\n-\t\tif atomic.LoadUint32(&handlingState.ref) == 0 {\n-\t\t\tc.handling.Delete(cacheKey)\n+\t// 使用简化的处理状态管理，避免重复请求\n+\thandlingState, isNew := GlobalHandlingStateManager.GetOrCreateState(cacheKey)\n+\t\n+\tif !isNew {\n+\t\t// 有其他goroutine正在处理相同请求，等待完成\n+\t\thandlingState.Wait()\n+\t\t\n+\t\t// 重新检查缓存\n+\t\tif resp := c.LookupDnsRespCache_(dnsMessage, cacheKey, false); resp != nil {\n+\t\t\tif needResp {\n+\t\t\t\tif err = sendPkt(c.log, resp, req.realDst, req.realSrc, req.src, req.lConn); err != nil {\n+\t\t\t\t\treturn fmt.Errorf(\"failed to write cached DNS resp: %w\", err)\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\treturn nil\n \t\t}\n-\t}()\n+\t\t// 如果仍然没有缓存，继续处理（可能是上一个请求失败了）\n+\t}\n+\t\n+\tdefer GlobalHandlingStateManager.CompleteAndCleanup(cacheKey, handlingState)\n \n \tif resp := c.LookupDnsRespCache_(dnsMessage, cacheKey, false); resp != nil {\n \t\t// Send cache to client directly.\n@@ -569,46 +553,22 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte\n \n \t// Dial and send.\n \tvar respMsg *dnsmessage.Msg\n-\t// defer in a recursive call will delay Close(), thus we Close() before\n-\t// the next recursive call. However, a connection cannot be closed twice.\n-\t// We should set a connClosed flag to avoid it.\n-\tvar connClosed bool\n \n \tctxDial, cancel := context.WithTimeout(context.TODO(), consts.DefaultDialTimeout)\n \tdefer cancel()\n \n-\t// get forwarder from cache\n-\tc.dnsForwarderCacheMu.Lock()\n-\tforwarder, ok := c.dnsForwarderCache[dnsForwarderKey{upstream: upstream.String(), dialArgument: *dialArgument}]\n-\tif !ok {\n-\t\tforwarder, err = newDnsForwarder(upstream, *dialArgument)\n-\t\tif err != nil {\n-\t\t\tc.dnsForwarderCacheMu.Unlock()\n-\t\t\treturn err\n-\t\t}\n-\t\tc.dnsForwarderCache[dnsForwarderKey{upstream: upstream.String(), dialArgument: *dialArgument}] = forwarder\n-\t}\n-\tc.dnsForwarderCacheMu.Unlock()\n-\n-\tdefer func() {\n-\t\tif !connClosed {\n-\t\t\tforwarder.Close()\n-\t\t}\n-\t}()\n-\n+\t// 使用新的转发器管理器，避免重复创建\n+\tforwarder, releaseForwarder, err := GlobalDnsForwarderManager.GetForwarder(upstream, *dialArgument)\n \tif err != nil {\n \t\treturn err\n \t}\n+\tdefer releaseForwarder()\n \n \trespMsg, err = forwarder.ForwardDNS(ctxDial, data)\n \tif err != nil {\n \t\treturn err\n \t}\n \n-\t// Close conn before the recursive call.\n-\tforwarder.Close()\n-\tconnClosed = true\n-\n \t// Route response.\n \tupstreamIndex, nextUpstream, err := c.routing.ResponseSelect(respMsg, upstream)\n \tif err != nil {\ndiff --git a/control/dns_forwarder_manager.go b/control/dns_forwarder_manager.go\nnew file mode 100644\nindex 0000000..07e5c98\n--- /dev/null\n+++ b/control/dns_forwarder_manager.go\n@@ -0,0 +1,165 @@\n+/*\n+ * SPDX-License-Identifier: AGPL-3.0-only\n+ * Copyright (c) 2022-2025, daeuniverse Organization <dae@v2raya.org>\n+ */\n+\n+package control\n+\n+import (\n+\t\"context\"\n+\t\"sync\"\n+\t\"time\"\n+\n+\t\"github.com/daeuniverse/dae/component/dns\"\n+)\n+\n+// DnsForwarderManager 管理DNS转发器的生命周期，避免重复创建和资源浪费\n+type DnsForwarderManager struct {\n+\t// 使用sync.Map存储活跃的转发器\n+\tactiveForwarders sync.Map // map[dnsForwarderKey]*forwarderEntry\n+\t\n+\t// 清理goroutine控制\n+\tcleanupInterval time.Duration\n+\tctx             context.Context\n+\tcancel          context.CancelFunc\n+}\n+\n+type forwarderEntry struct {\n+\tforwarder   DnsForwarder\n+\tlastUsed    time.Time\n+\trefCount    int32\n+\tmu          sync.RWMutex\n+}\n+\n+// GetForwarder 获取或创建DNS转发器，使用引用计数管理生命周期\n+func (m *DnsForwarderManager) GetForwarder(upstream *dns.Upstream, dialArg dialArgument) (DnsForwarder, func(), error) {\n+\tkey := dnsForwarderKey{upstream: upstream.String(), dialArgument: dialArg}\n+\t\n+\t// 快速路径：尝试获取现有转发器\n+\tif entryValue, ok := m.activeForwarders.Load(key); ok {\n+\t\tentry := entryValue.(*forwarderEntry)\n+\t\tentry.mu.Lock()\n+\t\tentry.refCount++\n+\t\tentry.lastUsed = time.Now()\n+\t\tforwarder := entry.forwarder\n+\t\tentry.mu.Unlock()\n+\t\t\n+\t\t// 返回释放函数\n+\t\trelease := func() {\n+\t\t\tentry.mu.Lock()\n+\t\t\tentry.refCount--\n+\t\t\tentry.mu.Unlock()\n+\t\t}\n+\t\t\n+\t\treturn forwarder, release, nil\n+\t}\n+\t\n+\t// 慢路径：需要创建新转发器\n+\tnewForwarder, err := newDnsForwarder(upstream, dialArg)\n+\tif err != nil {\n+\t\treturn nil, nil, err\n+\t}\n+\t\n+\tentry := &forwarderEntry{\n+\t\tforwarder: newForwarder,\n+\t\tlastUsed:  time.Now(),\n+\t\trefCount:  1,\n+\t}\n+\t\n+\t// 尝试存储，如果已存在则使用现有的\n+\tif existingValue, loaded := m.activeForwarders.LoadOrStore(key, entry); loaded {\n+\t\t// 有其他goroutine创建了转发器，关闭我们创建的并使用现有的\n+\t\tnewForwarder.Close()\n+\t\t\n+\t\texistingEntry := existingValue.(*forwarderEntry)\n+\t\texistingEntry.mu.Lock()\n+\t\texistingEntry.refCount++\n+\t\texistingEntry.lastUsed = time.Now()\n+\t\tforwarder := existingEntry.forwarder\n+\t\texistingEntry.mu.Unlock()\n+\t\t\n+\t\trelease := func() {\n+\t\t\texistingEntry.mu.Lock()\n+\t\t\texistingEntry.refCount--\n+\t\t\texistingEntry.mu.Unlock()\n+\t\t}\n+\t\t\n+\t\treturn forwarder, release, nil\n+\t}\n+\t\n+\t// 成功存储新转发器\n+\trelease := func() {\n+\t\tentry.mu.Lock()\n+\t\tentry.refCount--\n+\t\tentry.mu.Unlock()\n+\t}\n+\t\n+\treturn newForwarder, release, nil\n+}\n+\n+// NewDnsForwarderManager 创建新的DNS转发器管理器\n+func NewDnsForwarderManager() *DnsForwarderManager {\n+\tctx, cancel := context.WithCancel(context.Background())\n+\tmanager := &DnsForwarderManager{\n+\t\tcleanupInterval: 5 * time.Minute,\n+\t\tctx:             ctx,\n+\t\tcancel:          cancel,\n+\t}\n+\t\n+\t// 启动清理goroutine\n+\tgo manager.cleanupLoop()\n+\t\n+\treturn manager\n+}\n+\n+// cleanupLoop 定期清理未使用的转发器\n+func (m *DnsForwarderManager) cleanupLoop() {\n+\tticker := time.NewTicker(m.cleanupInterval)\n+\tdefer ticker.Stop()\n+\t\n+\tfor {\n+\t\tselect {\n+\t\tcase <-m.ctx.Done():\n+\t\t\treturn\n+\t\tcase <-ticker.C:\n+\t\t\tm.cleanup()\n+\t\t}\n+\t}\n+}\n+\n+func (m *DnsForwarderManager) cleanup() {\n+\tnow := time.Now()\n+\tcutoff := now.Add(-m.cleanupInterval)\n+\t\n+\tm.activeForwarders.Range(func(key, value interface{}) bool {\n+\t\tentry := value.(*forwarderEntry)\n+\t\tentry.mu.RLock()\n+\t\tshouldDelete := entry.refCount == 0 && entry.lastUsed.Before(cutoff)\n+\t\tforwarder := entry.forwarder\n+\t\tentry.mu.RUnlock()\n+\t\t\n+\t\tif shouldDelete {\n+\t\t\t// 尝试删除并关闭\n+\t\t\tif m.activeForwarders.CompareAndDelete(key, value) {\n+\t\t\t\tforwarder.Close()\n+\t\t\t}\n+\t\t}\n+\t\t\n+\t\treturn true\n+\t})\n+}\n+\n+// Shutdown 关闭管理器并清理所有转发器\n+func (m *DnsForwarderManager) Shutdown() {\n+\tm.cancel()\n+\t\n+\t// 关闭所有活跃的转发器\n+\tm.activeForwarders.Range(func(key, value interface{}) bool {\n+\t\tentry := value.(*forwarderEntry)\n+\t\tentry.forwarder.Close()\n+\t\treturn true\n+\t})\n+}\n+\n+// 全局DNS转发器管理器实例\n+var GlobalDnsForwarderManager = NewDnsForwarderManager()\ndiff --git a/control/dns_handling_state.go b/control/dns_handling_state.go\nnew file mode 100644\nindex 0000000..d3acce6\n--- /dev/null\n+++ b/control/dns_handling_state.go\n@@ -0,0 +1,79 @@\n+/*\n+ * SPDX-License-Identifier: AGPL-3.0-only\n+ * Copyright (c) 2022-2025, daeuniverse Organization <dae@v2raya.org>\n+ */\n+\n+package control\n+\n+import (\n+\t\"sync\"\n+\t\"time\"\n+)\n+\n+// SimpleHandlingState 简化的处理状态管理，避免复杂的引用计数\n+type SimpleHandlingState struct {\n+\tdone chan struct{}\n+\tonce sync.Once\n+}\n+\n+// Wait 等待处理完成\n+func (s *SimpleHandlingState) Wait() {\n+\t<-s.done\n+}\n+\n+// Complete 标记处理完成\n+func (s *SimpleHandlingState) Complete() {\n+\ts.once.Do(func() {\n+\t\tclose(s.done)\n+\t})\n+}\n+\n+// NewSimpleHandlingState 创建新的处理状态\n+func NewSimpleHandlingState() *SimpleHandlingState {\n+\treturn &SimpleHandlingState{\n+\t\tdone: make(chan struct{}),\n+\t}\n+}\n+\n+// HandlingStateManager 管理DNS处理状态，避免重复请求\n+type HandlingStateManager struct {\n+\tstates sync.Map // map[string]*SimpleHandlingState\n+}\n+\n+// GetOrCreateState 获取或创建处理状态\n+func (m *HandlingStateManager) GetOrCreateState(key string) (*SimpleHandlingState, bool) {\n+\t// 尝试获取现有状态\n+\tif stateValue, ok := m.states.Load(key); ok {\n+\t\treturn stateValue.(*SimpleHandlingState), false\n+\t}\n+\t\n+\t// 创建新状态\n+\tnewState := NewSimpleHandlingState()\n+\t\n+\t// 尝试存储，如果已存在则使用现有的\n+\tif actualValue, loaded := m.states.LoadOrStore(key, newState); loaded {\n+\t\treturn actualValue.(*SimpleHandlingState), false\n+\t}\n+\t\n+\t// 成功创建新状态\n+\treturn newState, true\n+}\n+\n+// CompleteAndCleanup 完成处理并清理状态\n+func (m *HandlingStateManager) CompleteAndCleanup(key string, state *SimpleHandlingState) {\n+\tstate.Complete()\n+\t\n+\t// 延迟清理，给其他等待的goroutine一些时间\n+\tgo func() {\n+\t\ttime.Sleep(100 * time.Millisecond)\n+\t\tm.states.Delete(key)\n+\t}()\n+}\n+\n+// NewHandlingStateManager 创建新的处理状态管理器\n+func NewHandlingStateManager() *HandlingStateManager {\n+\treturn &HandlingStateManager{}\n+}\n+\n+// 全局处理状态管理器\n+var GlobalHandlingStateManager = NewHandlingStateManager()\ndiff --git a/control/udp_endpoint_pool.go b/control/udp_endpoint_pool.go\nindex 5fd972a..cb87016 100644\n--- a/control/udp_endpoint_pool.go\n+++ b/control/udp_endpoint_pool.go\n@@ -38,22 +38,68 @@ type UdpEndpoint struct {\n }\n \n func (ue *UdpEndpoint) start() {\n-\tbuf := pool.GetFullCap(consts.EthernetMtu)\n-\tdefer pool.Put(buf)\n+\t// 使用buffered channel实现异步处理\n+\tconst maxPendingPackets = 1000\n+\tpacketChan := make(chan struct {\n+\t\tdata []byte\n+\t\tfrom netip.AddrPort\n+\t}, maxPendingPackets)\n+\t\n+\t// 启动异步包处理器\n+\tgo func() {\n+\t\tfor packet := range packetChan {\n+\t\t\t// 异步处理每个包，避免阻塞读取循环\n+\t\t\tgo func(data []byte, from netip.AddrPort) {\n+\t\t\t\tdefer pool.Put(data) // 确保释放buffer\n+\t\t\t\tif err := ue.handler(data, from); err != nil {\n+\t\t\t\t\t// 处理错误但不阻塞\n+\t\t\t\t\treturn\n+\t\t\t\t}\n+\t\t\t}(packet.data, packet.from)\n+\t\t}\n+\t}()\n+\t\n+\t// 高性能读取循环\n \tfor {\n+\t\tbuf := pool.GetFullCap(consts.EthernetMtu)\n \t\tn, from, err := ue.conn.ReadFrom(buf[:])\n \t\tif err != nil {\n+\t\t\tpool.Put(buf)\n \t\t\tbreak\n \t\t}\n+\t\t\n+\t\t// 快速重置计时器，减少锁竞争\n \t\tue.mu.Lock()\n-\t\tue.deadlineTimer.Reset(ue.NatTimeout)\n+\t\tif ue.deadlineTimer != nil {\n+\t\t\tue.deadlineTimer.Reset(ue.NatTimeout)\n+\t\t}\n \t\tue.mu.Unlock()\n-\t\tif err = ue.handler(buf[:n], from); err != nil {\n-\t\t\tbreak\n+\t\t\n+\t\t// 复制数据到正确大小的buffer\n+\t\tdata := pool.Get(n)\n+\t\tcopy(data, buf[:n])\n+\t\tpool.Put(buf)\n+\t\t\n+\t\t// 非阻塞发送到处理器\n+\t\tselect {\n+\t\tcase packetChan <- struct {\n+\t\t\tdata []byte\n+\t\t\tfrom netip.AddrPort\n+\t\t}{data, from}:\n+\t\t\t// 成功发送到处理队列\n+\t\tdefault:\n+\t\t\t// 队列满了，丢弃包（避免阻塞读取）\n+\t\t\tpool.Put(data)\n+\t\t\t// 可以在这里记录丢包统计\n \t\t}\n \t}\n+\t\n+\t// 清理\n+\tclose(packetChan)\n \tue.mu.Lock()\n-\tue.deadlineTimer.Stop()\n+\tif ue.deadlineTimer != nil {\n+\t\tue.deadlineTimer.Stop()\n+\t}\n \tue.mu.Unlock()\n }\n \ndiff --git a/control/udp_health_monitor.go b/control/udp_health_monitor.go\nnew file mode 100644\nindex 0000000..667e863\n--- /dev/null\n+++ b/control/udp_health_monitor.go\n@@ -0,0 +1,165 @@\n+/*\n+ * SPDX-License-Identifier: AGPL-3.0-only\n+ * Copyright (c) 2022-2025, daeuniverse Organization <dae@v2raya.org>\n+ */\n+\n+package control\n+\n+import (\n+\t\"context\"\n+\t\"sync\"\n+\t\"sync/atomic\"\n+\t\"time\"\n+)\n+\n+// UdpHealthMonitor monitors UDP processing health and prevents deadlocks\n+type UdpHealthMonitor struct {\n+\t// 基本指标\n+\tactiveConnections    int64\n+\ttotalPacketsHandled  int64\n+\tdroppedPackets      int64\n+\ttimeoutOccurrences  int64\n+\t\n+\t// 控制参数\n+\tisShuttingDown      int32\n+\tmaxActiveConns      int64\n+\thealthCheckInterval time.Duration\n+\t\n+\t// 监控\n+\tlastActivity        time.Time\n+\tmu                 sync.RWMutex\n+\tctx                context.Context\n+\tcancel             context.CancelFunc\n+}\n+\n+// NewUdpHealthMonitor creates a new UDP health monitor\n+func NewUdpHealthMonitor() *UdpHealthMonitor {\n+\tctx, cancel := context.WithCancel(context.Background())\n+\tmonitor := &UdpHealthMonitor{\n+\t\tmaxActiveConns:      20000, // 增加最大连接数\n+\t\thealthCheckInterval: 30 * time.Second,\n+\t\tlastActivity:        time.Now(),\n+\t\tctx:                 ctx,\n+\t\tcancel:              cancel,\n+\t}\n+\t\n+\tgo monitor.healthCheckLoop()\n+\treturn monitor\n+}\n+\n+// healthCheckLoop runs periodic health checks\n+func (m *UdpHealthMonitor) healthCheckLoop() {\n+\tticker := time.NewTicker(m.healthCheckInterval)\n+\tdefer ticker.Stop()\n+\t\n+\tfor {\n+\t\tselect {\n+\t\tcase <-m.ctx.Done():\n+\t\t\treturn\n+\t\tcase <-ticker.C:\n+\t\t\tm.performHealthCheck()\n+\t\t}\n+\t}\n+}\n+\n+// performHealthCheck 执行简化的健康检查\n+func (m *UdpHealthMonitor) performHealthCheck() {\n+\tactiveConns := atomic.LoadInt64(&m.activeConnections)\n+\ttotalPackets := atomic.LoadInt64(&m.totalPacketsHandled)\n+\tdroppedPackets := atomic.LoadInt64(&m.droppedPackets)\n+\ttimeouts := atomic.LoadInt64(&m.timeoutOccurrences)\n+\t\n+\t// 简单的日志记录（如果需要的话）\n+\t_ = activeConns\n+\t_ = totalPackets\n+\t_ = droppedPackets\n+\t_ = timeouts\n+\t\n+\t// 重置计数器防止溢出\n+\tif totalPackets > 10000000 { // 1000万包后重置\n+\t\tatomic.StoreInt64(&m.totalPacketsHandled, 0)\n+\t\tatomic.StoreInt64(&m.droppedPackets, 0)\n+\t\tatomic.StoreInt64(&m.timeoutOccurrences, 0)\n+\t}\n+}\n+\n+// RegisterConnection registers a new UDP connection\n+func (m *UdpHealthMonitor) RegisterConnection() bool {\n+\tif atomic.LoadInt32(&m.isShuttingDown) != 0 {\n+\t\treturn false\n+\t}\n+\t\n+\tactiveConns := atomic.AddInt64(&m.activeConnections, 1)\n+\tif activeConns > m.maxActiveConns {\n+\t\tatomic.AddInt64(&m.activeConnections, -1)\n+\t\tatomic.AddInt64(&m.droppedPackets, 1)\n+\t\treturn false\n+\t}\n+\t\n+\tm.mu.Lock()\n+\tm.lastActivity = time.Now()\n+\tm.mu.Unlock()\n+\t\n+\treturn true\n+}\n+\n+// UnregisterConnection unregisters a UDP connection\n+func (m *UdpHealthMonitor) UnregisterConnection() {\n+\tatomic.AddInt64(&m.activeConnections, -1)\n+}\n+\n+// RecordPacketHandled records a successfully handled packet\n+func (m *UdpHealthMonitor) RecordPacketHandled() {\n+\tatomic.AddInt64(&m.totalPacketsHandled, 1)\n+\t\n+\tm.mu.Lock()\n+\tm.lastActivity = time.Now()\n+\tm.mu.Unlock()\n+}\n+\n+// RecordTimeout records a timeout occurrence\n+func (m *UdpHealthMonitor) RecordTimeout() {\n+\tatomic.AddInt64(&m.timeoutOccurrences, 1)\n+}\n+\n+// IsHealthy returns true if the system is in a healthy state\n+func (m *UdpHealthMonitor) IsHealthy() bool {\n+\tactiveConns := atomic.LoadInt64(&m.activeConnections)\n+\ttimeouts := atomic.LoadInt64(&m.timeoutOccurrences)\n+\ttotalPackets := atomic.LoadInt64(&m.totalPacketsHandled)\n+\t\n+\t// 基本健康检查\n+\tif activeConns > m.maxActiveConns*9/10 { // 90% 容量\n+\t\treturn false\n+\t}\n+\t\n+\t// 超时率检查\n+\tif totalPackets > 1000 {\n+\t\ttimeoutRate := float64(timeouts) / float64(totalPackets)\n+\t\tif timeoutRate > 0.05 { // 5% 超时率阈值\n+\t\t\treturn false\n+\t\t}\n+\t}\n+\t\n+\treturn true\n+}\n+\n+// Shutdown shuts down the health monitor\n+func (m *UdpHealthMonitor) Shutdown() {\n+\tatomic.StoreInt32(&m.isShuttingDown, 1)\n+\tm.cancel()\n+}\n+\n+// GetStats returns current statistics\n+func (m *UdpHealthMonitor) GetStats() map[string]int64 {\n+\treturn map[string]int64{\n+\t\t\"active_connections\":    atomic.LoadInt64(&m.activeConnections),\n+\t\t\"total_packets_handled\": atomic.LoadInt64(&m.totalPacketsHandled),\n+\t\t\"dropped_packets\":       atomic.LoadInt64(&m.droppedPackets),\n+\t\t\"timeout_occurrences\":   atomic.LoadInt64(&m.timeoutOccurrences),\n+\t\t\"max_active_conns\":      m.maxActiveConns,\n+\t}\n+}\n+\n+// Global UDP health monitor instance\n+var DefaultUdpHealthMonitor = NewUdpHealthMonitor()\ndiff --git a/control/udp_task_pool.go b/control/udp_task_pool.go\nindex 08b02d7..09d8cb4 100644\n--- a/control/udp_task_pool.go\n+++ b/control/udp_task_pool.go\n@@ -11,7 +11,11 @@ import (\n \t\"time\"\n )\n \n-const UdpTaskQueueLength = 128\n+const (\n+\tUdpTaskQueueLength = 512  // 增加队列容量以支持更高并发\n+\tMaxUdpQueues       = 5000 // 增加最大队列数\n+\tUdpTaskTimeout     = 100 * time.Millisecond // 极短超时时间\n+)\n \n type UdpTask = func()\n \n@@ -27,22 +31,57 @@ type UdpTaskQueue struct {\n }\n \n func (q *UdpTaskQueue) convoy() {\n+\tdefer close(q.closed)\n+\t\n \tfor {\n \t\tselect {\n \t\tcase <-q.ctx.Done():\n-\t\t\tclose(q.closed)\n+\t\t\t// 清空剩余任务\n+\t\t\tq.drainRemainingTasks()\n \t\t\treturn\n+\t\t\t\n \t\tcase task := <-q.ch:\n-\t\t\ttask()\n-\t\t\tq.timer.Reset(q.agingTime)\n+\t\t\t// 立即异步执行任务，不等待完成\n+\t\t\tgo q.executeTaskAsync(task)\n+\t\t\t\n+\t\t\t// 重置老化定时器\n+\t\t\tif q.timer != nil {\n+\t\t\t\tq.timer.Reset(q.agingTime)\n+\t\t\t}\n+\t\t}\n+\t}\n+}\n+\n+// executeTaskAsync 异步执行单个任务\n+func (q *UdpTaskQueue) executeTaskAsync(task UdpTask) {\n+\tdefer func() {\n+\t\tif r := recover(); r != nil {\n+\t\t\t// 记录panic但不影响其他任务\n+\t\t}\n+\t}()\n+\t\n+\tif task != nil {\n+\t\ttask()\n+\t}\n+}\n+\n+// drainRemainingTasks 清空剩余任务\n+func (q *UdpTaskQueue) drainRemainingTasks() {\n+\tfor {\n+\t\tselect {\n+\t\tcase task := <-q.ch:\n+\t\t\t// 异步执行剩余任务\n+\t\t\tgo q.executeTaskAsync(task)\n+\t\tdefault:\n+\t\t\treturn\n \t\t}\n \t}\n }\n \n type UdpTaskPool struct {\n \tqueueChPool sync.Pool\n-\t// mu protects m\n-\tmu sync.Mutex\n+\t// 使用RWMutex提高读取性能\n+\tmu sync.RWMutex\n \tm  map[string]*UdpTaskQueue\n }\n \n@@ -51,7 +90,7 @@ func NewUdpTaskPool() *UdpTaskPool {\n \t\tqueueChPool: sync.Pool{New: func() any {\n \t\t\treturn make(chan UdpTask, UdpTaskQueueLength)\n \t\t}},\n-\t\tmu: sync.Mutex{},\n+\t\tmu: sync.RWMutex{},\n \t\tm:  map[string]*UdpTaskQueue{},\n \t}\n \treturn p\n@@ -59,40 +98,123 @@ func NewUdpTaskPool() *UdpTaskPool {\n \n // EmitTask: Make sure packets with the same key (4 tuples) will be sent in order.\n func (p *UdpTaskPool) EmitTask(key string, task UdpTask) {\n+\tif task == nil {\n+\t\treturn\n+\t}\n+\n+\t// 快速健康检查\n+\tif !DefaultUdpHealthMonitor.RegisterConnection() {\n+\t\treturn\n+\t}\n+\tdefer DefaultUdpHealthMonitor.UnregisterConnection()\n+\n+\t// 尝试使用读锁快速查找现有队列\n+\tp.mu.RLock()\n+\tq, exists := p.m[key]\n+\tqueueCount := len(p.m)\n+\tp.mu.RUnlock()\n+\n+\tif exists {\n+\t\t// 队列已存在，直接提交任务\n+\t\tp.submitTaskToQueue(q, task)\n+\t\treturn\n+\t}\n+\n+\t// 需要创建新队列，使用写锁\n \tp.mu.Lock()\n-\tq, ok := p.m[key]\n-\tif !ok {\n-\t\tch := p.queueChPool.Get().(chan UdpTask)\n-\t\tctx, cancel := context.WithCancel(context.Background())\n-\t\tq = &UdpTaskQueue{\n-\t\t\tkey:       key,\n-\t\t\tp:         p,\n-\t\t\tch:        ch,\n-\t\t\ttimer:     nil,\n-\t\t\tagingTime: DefaultNatTimeout,\n-\t\t\tctx:       ctx,\n-\t\t\tclosed:    make(chan struct{}),\n-\t\t}\n-\t\tq.timer = time.AfterFunc(q.agingTime, func() {\n-\t\t\t// if timer executed, there should no task in queue.\n-\t\t\t// q.closed should not blocking things.\n-\t\t\tp.mu.Lock()\n-\t\t\tcancel()\n-\t\t\tdelete(p.m, key)\n-\t\t\tp.mu.Unlock()\n-\t\t\t<-q.closed\n-\t\t\tif len(ch) == 0 { // Otherwise let it be GCed\n-\t\t\t\tp.queueChPool.Put(ch)\n+\tdefer p.mu.Unlock()\n+\n+\t// 双重检查\n+\tif q, exists := p.m[key]; exists {\n+\t\tp.submitTaskToQueue(q, task)\n+\t\treturn\n+\t}\n+\n+\t// 限制队列数量\n+\tif queueCount >= MaxUdpQueues {\n+\t\tDefaultUdpHealthMonitor.RecordTimeout()\n+\t\treturn\n+\t}\n+\n+\t// 创建新队列\n+\tch := p.queueChPool.Get().(chan UdpTask)\n+\tctx, cancel := context.WithCancel(context.Background())\n+\tq = &UdpTaskQueue{\n+\t\tkey:       key,\n+\t\tp:         p,\n+\t\tch:        ch,\n+\t\ttimer:     nil,\n+\t\tagingTime: DefaultNatTimeout,\n+\t\tctx:       ctx,\n+\t\tclosed:    make(chan struct{}),\n+\t}\n+\n+\tq.timer = time.AfterFunc(q.agingTime, func() {\n+\t\tp.cleanupQueue(key, q, cancel, ch)\n+\t})\n+\n+\tp.m[key] = q\n+\tgo q.convoy()\n+\n+\t// 提交任务到新创建的队列\n+\tp.submitTaskToQueue(q, task)\n+}\n+\n+// submitTaskToQueue 提交任务到指定队列（极简版本）\n+func (p *UdpTaskPool) submitTaskToQueue(q *UdpTaskQueue, task UdpTask) {\n+\t// 包装任务以增加健康监控\n+\twrappedTask := func() {\n+\t\tdefer func() {\n+\t\t\tDefaultUdpHealthMonitor.RecordPacketHandled()\n+\t\t\tif r := recover(); r != nil {\n+\t\t\t\t// 记录panic但继续\n \t\t\t}\n-\t\t})\n-\t\tp.m[key] = q\n-\t\tgo q.convoy()\n+\t\t}()\n+\t\ttask()\n \t}\n-\tp.mu.Unlock()\n-\t// if task cannot be executed within 180s(DefaultNatTimeout), GC may be triggered, so skip the task when GC occurs\n+\n+\t// 极速任务提交 - 非阻塞模式\n \tselect {\n-\tcase q.ch <- task:\n+\tcase q.ch <- wrappedTask:\n+\t\t// 任务成功排队\n \tcase <-q.ctx.Done():\n+\t\t// 上下文已取消\n+\t\tDefaultUdpHealthMonitor.RecordTimeout()\n+\tdefault:\n+\t\t// 队列已满，异步重试一次\n+\t\tgo func() {\n+\t\t\tselect {\n+\t\t\tcase q.ch <- wrappedTask:\n+\t\t\t\t// 重试成功\n+\t\t\tcase <-q.ctx.Done():\n+\t\t\t\tDefaultUdpHealthMonitor.RecordTimeout()\n+\t\t\tcase <-time.After(UdpTaskTimeout):\n+\t\t\t\tDefaultUdpHealthMonitor.RecordTimeout()\n+\t\t\t}\n+\t\t}()\n+\t}\n+}\n+\n+// cleanupQueue 清理队列\n+func (p *UdpTaskPool) cleanupQueue(key string, q *UdpTaskQueue, cancel context.CancelFunc, ch chan UdpTask) {\n+\tp.mu.Lock()\n+\tcancel()\n+\tdelete(p.m, key)\n+\tp.mu.Unlock()\n+\n+\t// 等待清理完成，带超时\n+\tselect {\n+\tcase <-q.closed:\n+\tcase <-time.After(1 * time.Second):\n+\t\t// 强制清理\n+\t}\n+\n+\t// 回收通道\n+\tif len(ch) == 0 {\n+\t\tfor len(ch) > 0 {\n+\t\t\t<-ch\n+\t\t}\n+\t\tp.queueChPool.Put(ch)\n \t}\n }\n \n"
  }
]