在全局复写脚本里加入如下内容即可.这样的话,匹配到Direct的情况下会走最上方的代理组

image.png

这里可以选择是走tailscale的网卡,还是就是正常的网卡.一般来说,只有开了tailscale且打算让流量走exit node出口走,才需要选第二个.

function main(config, profileName) {
  const DIRECT_GROUP = "Manual-DIRECT";
  const NATIVE_DIRECT = "Native-DIRECT";
  const TS_DIRECT = "Tailscale-DIRECT";
  const TS_IFACE = "utun10"; // 当前 Tailscale 接口;如果变了就改这里

  config.proxies ||= [];

  config.proxies = config.proxies.filter((p) => {
    if (!p || !p.name) return true;
    return (
      p.name !== NATIVE_DIRECT &&
      p.name !== TS_DIRECT &&
      !p.name.startsWith("Tailscale-DIRECT-utun")
    );
  });

  config.proxies.unshift({
    name: TS_DIRECT,
    type: "direct",
    udp: true,
    "interface-name": TS_IFACE,
    "ip-version": "ipv4-prefer",
  });

  config.proxies.unshift({
    name: NATIVE_DIRECT,
    type: "direct",
    udp: true,
    "ip-version": "ipv4-prefer",
  });

  config["proxy-groups"] ||= [];
  config["proxy-groups"] = config["proxy-groups"].filter(
    (g) => g.name !== DIRECT_GROUP && g.name !== "Smart-DIRECT"
  );

  config["proxy-groups"].unshift({
    name: DIRECT_GROUP,
    type: "select",
    proxies: [
      NATIVE_DIRECT,    // 默认选这个
      TS_DIRECT,        // 需要 Tailscale exit node 时手动切
    ],
  });

  const replaceDirect = (policy) =>
    policy === "DIRECT" || policy === "Smart-DIRECT" ? DIRECT_GROUP : policy;

  if (Array.isArray(config.rules)) {
    config.rules = config.rules.map((rule) => {
      if (typeof rule !== "string") return rule;

      const parts = rule.split(",");
      const policyIndex =
        parts[parts.length - 1] === "no-resolve"
          ? parts.length - 2
          : parts.length - 1;

      parts[policyIndex] = replaceDirect(parts[policyIndex]);
      return parts.join(",");
    });
  }

  for (const group of config["proxy-groups"] || []) {
    if (group.name === DIRECT_GROUP) continue;
    if (Array.isArray(group.proxies)) {
      group.proxies = group.proxies.map(replaceDirect);
    }
  }

  return config;
}

随后重启clash内核即可.

升级一版

我们能不能做到不要写死utun,而是每次手动选择一下看看哪个是通的

image.png

这样我们一看就知道,utun12是tailscale的网口.全局扩展脚本如下:

function main(config, profileName) {
  const DIRECT_GROUP = "Manual-DIRECT-IFACE";
  const NATIVE_DIRECT = "Native-DIRECT";
  const MAX_UTUN = 20;

  config.proxies ||= [];

  config.proxies = config.proxies.filter((p) => {
    if (!p || !p.name) return true;
    return (
      p.name !== NATIVE_DIRECT &&
      p.name !== "Tailscale-DIRECT" &&
      !p.name.startsWith("Tailscale-DIRECT-utun")
    );
  });

  config.proxies.unshift({
    name: NATIVE_DIRECT,
    type: "direct",
    udp: true,
    "ip-version": "ipv4-prefer",
  });

  const candidates = [];
  for (let i = 0; i <= MAX_UTUN; i++) {
    const name = `Tailscale-DIRECT-utun${i}`;
    candidates.push(name);

    config.proxies.push({
      name,
      type: "direct",
      udp: true,
      "interface-name": `utun${i}`,
      "ip-version": "ipv4-prefer",
    });
  }

  config["proxy-groups"] ||= [];
  config["proxy-groups"] = config["proxy-groups"].filter(
    (g) =>
      g.name !== DIRECT_GROUP &&
      g.name !== "Manual-DIRECT" &&
      g.name !== "Smart-DIRECT"
  );

  config["proxy-groups"].unshift({
    name: DIRECT_GROUP,
    type: "select",
    proxies: [
      NATIVE_DIRECT, // 默认
      ...candidates,
    ],
  });

  const replaceDirect = (policy) =>
    policy === "DIRECT" ||
    policy === "Smart-DIRECT" ||
    policy === "Manual-DIRECT"
      ? DIRECT_GROUP
      : policy;

  if (Array.isArray(config.rules)) {
    config.rules = config.rules.map((rule) => {
      if (typeof rule !== "string") return rule;

      const parts = rule.split(",");
      const policyIndex =
        parts[parts.length - 1] === "no-resolve"
          ? parts.length - 2
          : parts.length - 1;

      parts[policyIndex] = replaceDirect(parts[policyIndex]);
      return parts.join(",");
    });
  }

  for (const group of config["proxy-groups"] || []) {
    if (group.name === DIRECT_GROUP) continue;
    if (Array.isArray(group.proxies)) {
      group.proxies = group.proxies.map(replaceDirect);
    }
  }

  return config;
}