fix(discord): restore voice UDP routing on Cloudflare Anycast while isolating web traffic

- Preserve raw Cloudflare subnets in community-subnets-discord.lst in updates.uc instead of destructive deletion.
- In singbox/generator_routes.uc:
  * Exclude Cloudflare Anycast CIDRs from general IP route rules (preventing web hijacking).
  * Generate a dedicated Discord Voice route rule for Cloudflare subnets scoped to network: 'udp' and voice port ranges (50000-65535, 3478, 19302, 5000-5020).
  * Provide DEFAULT_DISCORD_VOICE_SUBNETS fallback in core/ip.uc.
- In nft/apply.uc:
  * Route Cloudflare subnets to ip_ports set with Discord voice ports (5000-5020, 3478, 19302, 50000-65535).
  * Automatically enable ip_port rules when section has community_subnets for Discord.
- In section.js (Routing Rule Tracer):
  * Filter out Cloudflare subnets when resolving domain web traffic to Discord community subnets.
  * Accurately display voice UDP routing notice for Anycast ranges.
- Update tests/community_subnets_sanitization.sh with voice UDP and sing-box validation.
This commit is contained in:
Dushnilin
2026-09-13 11:06:28 +03:00
parent 70bb7280c1
commit 4b7faf18a8
6 changed files with 215 additions and 44 deletions
@@ -11394,6 +11394,19 @@ function getSharedCdnWarning(cidr) {
return null;
}
function isCloudflareSharedCidr(cidr) {
if (!cidr) return false;
const normalized = cidr.trim();
for (const cfCidr of CLOUDFLARE_SHARED_CIDRS) {
if (normalized === cfCidr) return true;
}
const ipOnly = normalized.split("/")[0];
for (const cfCidr of CLOUDFLARE_SHARED_CIDRS) {
if (ipMatchesCidr(ipOnly, cfCidr)) return true;
}
return false;
}
let _subnetListCache = {};
async function readSubnetListFile(service) {
@@ -11861,17 +11874,24 @@ async function performTrace(query) {
const subnets = await readSubnetListFile(community);
const matchedCidr = matchIpInCidrs(queryForMatching, subnets);
if (matchedCidr) {
const isDiscordCfVoice = community === "discord" && isCloudflareSharedCidr(matchedCidr);
return {
matched: true,
sectionName: secName,
label: label,
action: action,
ruleType: "Community Subnets (" + community + ")",
ruleType: isDiscordCfVoice
? "Community Subnets (" + community + " Voice UDP)"
: "Community Subnets (" + community + ")",
pattern: matchedCidr,
matchedIp: queryForMatching,
priority: i + 1,
totalSections: totalSections,
sharedCdnWarning: getSharedCdnWarning(matchedCidr),
sharedCdnWarning: isDiscordCfVoice
? _(
"Subnet %s is a Cloudflare Anycast range used by Discord Voice. Only UDP voice traffic (ports 50000-65535, 3478, 19302, 5000-5020) is routed through this section; web traffic (TCP 80/443) goes direct.",
).format(matchedCidr)
: getSharedCdnWarning(matchedCidr),
};
}
if (
@@ -12105,7 +12125,10 @@ async function performTrace(query) {
}
for (const community of communityLists) {
const subnets = await readSubnetListFile(community);
let subnets = await readSubnetListFile(community);
if (community === "discord") {
subnets = subnets.filter((s) => !isCloudflareSharedCidr(s));
}
const matchedCommunityCidr = matchIpInCidrs(ip, subnets);
if (matchedCommunityCidr) {
return {