daeuniverse / daeuniverse/dae

[Bug Report] must_rules bypass for DNS traffic broken since v2.0.0 (Must flag ignored in DNS fast path)

Open
#1,069 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
6.2k
Forks
402
Avg merge
1h 8m
Merged PRs (30d)
1

Description

## Bug Report

### Summary

`must_rules` no longer bypasses the DNS controller for DNS traffic (port 53) in v2.0.0. This is a regression introduced by PR #970, which added DNS fast-path handlers in the userspace UDP/TCP/ingress layers and removed the v1.x `Must` guard, but did not re-apply the `Must` check in the new fast paths.

### Environment

- **Dae version**: v2.0.0 (regression confirmed; v1.1.0 unaffected)
- **OS**: Linux
- **Kernel**: 7.x
- **Others**: WAN interface bound (localhost proxy mode)

### Current Behavior

In v1.x, the userspace UDP handler had this check (`control/udp.go:150`):

```go
if routingResult.Must > 0 {
isDns = false // Regard as plain traffic.
}
```

This made `must_rules` work for DNS queries: when a process matched a `must_rules` rule (e.g. `pname(dig) -> must_rules`), its DNS queries (port 53) would bypass the DNS controller and be forwarded as plain traffic, respecting the `@server` argument of tools like `dig`.

PR #970 (`85a1fc3`, "Enhance control plane features and improve CI workflows") introduced "DNS fast path" handlers in `control/udp.go`, `control/tcp.go`, and `control/control_plane.go` for performance (avoid `UdpEndpoint` lookup per DNS query). These fast paths are gated only by `Port() == 53` and unconditionally call `DnsController.Handle_`, **without checking `routingResult.Must`**. The same PR removed the v1.x `if Must > 0 { isDns = false }` guard.

As a result, in v2.0.0, `must_rules` rules have no effect on DNS traffic — all port-53 traffic is hijacked into the DNS controller regardless of the `Must` flag set by eBPF routing.

### Steps to Reproduce

**1. Add a `must_rules` rule for a DNS tool in the routing section:**

```
routing {
pname(mosdns) -> must_rules # or pname(dig), pname(smartdns), etc.
# ... other rules ...
fallback: direct
}
```

**2. Query a CDN domain via two different DNS servers.** `download.microsoft.com` has geographic routing, so different DNS resolvers return IPs from entirely different network ranges:

```
dig @114.114.114.114 download.microsoft.com +short
dig @8.8.8.8 download.microsoft.com +short
```

**3. Compare the two answers.**

### Expected Behavior

| | v1.x (and patched v2.0.0) | v2.0.0 (broken) |
|---|---|---|
| `must_rules` for DNS | Works — `@server` is respected | **Broken** — `@server` is ignored |
| `dig @A` vs `dig @B` on a geo-routed CDN | Different IPs (each server's node) | **Both equal dae's own `dns.upstream` answer** |
| Root cause | `if Must > 0 { isDns = false }` skips DNS controller | Fast path `if Port()==53 { Handle_() }` ignores `Must` flag |

The bug signal: **two different `@server` values yield the identical answer** (which equals your dae `dns.upstream`'s answer), proving `@server` was silently ignored.

### Impact

Any `must_rules` configuration that should bypass the DNS controller for port-53 traffic is silently ineffective in v2.0.0. This affects:
- DNS loop prevention for local DNS proxies (`pname(mosdns, dnsmasq, smartdns, AdGuardHome) -> must_rules`)
- Diagnostic tools (`dig`, `nslookup`, `host`) with `pname(...) -> must_rules`

### Root Cause Analysis

The `Must` flag is set by the eBPF layer (`tproxy.c` → `ROUTE_STATE_MUST`) when a `must_rules` rule matches, and is correctly propagated to userspace via `routingResult.Must`. However, the three DNS fast-path entries introduced by #970 never read this flag:

- `control/udp.go` — `if realDst.Port() == 53 { ... Handle_() ... }`
- `control/tcp.go` — `if dst.Port() == 53 { handleTCPDnsFastPath(...) }`
- `control/control_plane.go` — DNS ingress fast path → `Handle_()`

The fix is to guard these entries with a `Must` check, restoring the v1.x semantics. A PR with the fix will follow.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by tracing the DNS fast-path branches in control/udp.go, control/tcp.go, and control/control_plane.go, then reproduce the issue with the supplied dig commands and two @server values. Done means must_rules traffic bypasses the DNS controller as in v1.x, while ordinary port-53 traffic still uses the fast path.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, linux
Domain
networking
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.