191 lines
7.3 KiB
Markdown
191 lines
7.3 KiB
Markdown
# Pi-hole v6 Diagnostics
|
|
|
|
Quick-start for diagnosing "is Pi-hole causing my network issues?" questions. Pi-hole v6 changed the API, CLI, and database schema — the old `pihole -c` (chronometer), `/admin/api.php`, and v5 commands don't work.
|
|
|
|
## Decision Flow
|
|
|
|
1. **Check if Pi-hole is even the problem** before digging in. If DNS resolves through Pi-hole and no essential domains are blocked, the issue is at the WiFi/router/IP level — not Pi-hole.
|
|
2. If Pi-hole might be the culprit, query its SQLite database directly.
|
|
|
|
## Docker Status
|
|
|
|
```bash
|
|
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep pihole
|
|
```
|
|
|
|
Pi-hole v6 container info:
|
|
- Status should show `healthy`
|
|
- Binds port 53 TCP+UDP to host IP (typically `192.168.50.X:53`)
|
|
- Web UI on port 80 inside container, mapped to host (e.g., `8090:80`)
|
|
|
|
## Pi-hole Process Check
|
|
|
|
```bash
|
|
docker exec pihole pihole status
|
|
```
|
|
|
|
Expected output:
|
|
```
|
|
[✓] FTL is listening on port 53
|
|
[✓] UDP (IPv4)
|
|
[✓] TCP (IPv4)
|
|
[✓] UDP (IPv6)
|
|
[✓] TCP (IPv6)
|
|
[✓] Pi-hole blocking is enabled
|
|
```
|
|
|
|
## Querying the Database (When Web UI Is Locked)
|
|
|
|
Pi-hole v6's web API at `/api` requires authentication (session or app password). When locked out, query the SQLite database directly.
|
|
|
|
### Database Location
|
|
|
|
- **Inside container:** `/etc/pihole/pihole-FTL.db`
|
|
- **On host (Docker volume):** `/var/lib/docker/volumes/pihole_etc/_data/pihole-FTL.db`
|
|
|
|
### Access Pattern
|
|
|
|
The `/var/lib/docker` directory has `drwx--x---` permissions — regular users can't traverse it. Copy the DB to a readable location:
|
|
|
|
```bash
|
|
sudo cp /var/lib/docker/volumes/pihole_etc/_data/pihole-FTL.db /tmp/pihole-FTL.db
|
|
sudo cp /var/lib/docker/volumes/pihole_etc/_data/pihole-FTL.db-wal /tmp/pihole-FTL.db-wal
|
|
sudo cp /var/lib/docker/volumes/pihole_etc/_data/pihole-FTL.db-shm /tmp/pihole-FTL.db-shm
|
|
sudo chown $USER:$USER /tmp/pihole-FTL.db*
|
|
```
|
|
|
|
Then query with Python (the Pi-hole container has no Python/sqlite3):
|
|
|
|
```python
|
|
import sqlite3
|
|
conn = sqlite3.connect('/tmp/pihole-FTL.db')
|
|
cur = conn.cursor()
|
|
|
|
# Recent queries
|
|
cur.execute("""
|
|
SELECT datetime(timestamp, 'unixepoch', 'localtime'), domain, client, status
|
|
FROM queries ORDER BY id DESC LIMIT 30
|
|
""")
|
|
```
|
|
|
|
## Pi-hole v6 Status Codes
|
|
|
|
Status codes in the `queries` table:
|
|
|
|
| Code | Meaning | Notes |
|
|
|------|---------|-------|
|
|
| 1 | GRAVITY_BLOCKED | Domain in blocklist |
|
|
| 2 | FORWARDED | Sent to upstream DNS |
|
|
| 3 | CACHE | Answered from cache |
|
|
| 4 | REGEX_BLOCKED | Matched regex block rule |
|
|
| 5 | EXACT_BLOCKED | Exact-match block |
|
|
| 6 | UPSTREAM_BLOCKED | Blocked by upstream DNS |
|
|
| 9 | CACHE_STALE | Stale cache entry |
|
|
| 12 | ALREADY_FORWARDED | Duplicate suppressed |
|
|
| 14 | CACHED_BLOCKED | Cached as blocked (from prior CNAME-chain inspection) — **this is the dangerous one for false positives** |
|
|
| 16 | ALREADY_BLOCKED (variant) | Gravity already known |
|
|
| 17 | RETRIED | First attempt failed, retry succeeded |
|
|
|
|
**Status 17 is normal** — it's the dominant status in v6 and means the query succeeded on retry. It does NOT indicate a problem.
|
|
|
|
**Only status 1, 4, 5, 6 are actual blocks.** Everything else = allowed.
|
|
|
|
## Key Diagnostic Queries
|
|
|
|
### Blocked vs Allowed Ratio (last hour)
|
|
```sql
|
|
SELECT CASE WHEN status IN (1,4,5,6) THEN 'BLOCKED' ELSE 'ALLOWED' END, count(*)
|
|
FROM queries WHERE timestamp > strftime('%s','now','-1 hour')
|
|
GROUP BY 1
|
|
```
|
|
|
|
### Which Clients Are Using Pi-hole
|
|
```sql
|
|
SELECT client, count(*) FROM queries
|
|
WHERE timestamp > strftime('%s','now','-1 hour')
|
|
GROUP BY client ORDER BY count(*) DESC
|
|
```
|
|
|
|
### Recent Blocked Domains
|
|
```sql
|
|
SELECT domain, count(*) FROM queries
|
|
WHERE timestamp > strftime('%s','now','-1 hour') AND status IN (1,4,5)
|
|
GROUP BY domain ORDER BY count(*) DESC LIMIT 10
|
|
```
|
|
|
|
### Windows NCSI Pattern (Connectivity Check)
|
|
Windows devices query `dns.msftncsi.com` every ~60 seconds to verify internet connectivity. If this shows status 17 (RETRIED), the device may be reporting "no internet" even though DNS eventually resolves. This is typically an upstream DNS slowness issue, not a Pi-hole problem.
|
|
|
|
## 🔥 Primary Pitfall: Deep CNAME Inspection False Positives
|
|
|
|
**This is the #1 cause of "some devices have no internet after DNS change to Pi-hole."**
|
|
|
|
When `CNAMEdeepInspect = true` (default in v6), Pi-hole follows CNAME chains. If ANY domain in the chain matches a blocklist entry, the ENTIRE chain is cached as blocked (status 14). The blocklist (e.g., StevenBlack) includes subdomains like `pagead.l.google.com` — these are ad-specific, but CNAME inspection propagates the block to the PARENT `l.google.com`, which kills ALL services on Google infrastructure.
|
|
|
|
**Affected domains (false positives):**
|
|
- `connectivitycheck.gstatic.com` — Android TV/Shield connectivity check
|
|
- `dns.msftncsi.com` — Windows NCSI connectivity check
|
|
- `google.com` — basic connectivity
|
|
- `ota.nvidia.com` — Shield TV updates
|
|
- `clients3.google.com`, `android.apis.google.com` — Google Play Services
|
|
|
|
**Detection:** Look for status 14 on these domains:
|
|
```sql
|
|
SELECT domain, count(*) FROM queries WHERE status=14
|
|
GROUP BY domain ORDER BY count(*) DESC LIMIT 20;
|
|
```
|
|
|
|
**Fix — whitelist critical domains (preferred):**
|
|
```bash
|
|
docker exec pihole pihole -w connectivitycheck.gstatic.com
|
|
docker exec pihole pihole -w dns.msftncsi.com
|
|
docker exec pihole pihole -w clients3.google.com
|
|
docker exec pihole pihole -w ota.nvidia.com
|
|
```
|
|
|
|
**Alternative — disable deep CNAME inspection:**
|
|
Set `CNAMEdeepInspect = false` in `/etc/pihole/pihole.toml` (may allow some CNAME-cloaked ads through).
|
|
|
|
See also: `pihole-troubleshooting` skill for full diagnostic workflow.
|
|
|
|
## Red Flags (Pi-hole IS the Problem)
|
|
|
|
- Essential domains (google.com, microsoft.com, apple.com) appearing with status 1, 4, or 5
|
|
- Status 14 on connectivity-check domains (connectivitycheck.gstatic.com, dns.msftncsi.com) — indicates deep CNAME inspection false positive
|
|
- Large block percentage (>50%) in last hour
|
|
- DNS resolution fails from other hosts: `dig @192.168.50.X google.com` times out
|
|
- Pi-hole container unhealthy or in restart loop
|
|
|
|
## Red Herrings (Pi-hole is NOT the Problem)
|
|
|
|
- Tracking/analytics domains blocked (app-measurement.com, newrelic.com, doubleclick.net) — these are working as intended
|
|
- Status 17 (RETRIED) on queries — normal v6 behavior, queries succeed
|
|
- Some devices working while others don't — if Pi-hole DNS resolves for any device, the DNS layer is fine; connectivity issues are at WiFi/router/IP level (but FIRST rule out deep CNAME inspection false positives above)
|
|
|
|
## Router Investigation (When Pi-hole Is Ruled Out)
|
|
|
|
Check the ASUS router at 192.168.50.1:
|
|
1. **System Log → Wireless Log** — are affected devices connected?
|
|
2. **Network Map → Clients** — do they have valid IPs?
|
|
3. **WiFi Settings** — Smart Connect / band steering can confuse some devices. Try separating 2.4 GHz and 5 GHz SSIDs.
|
|
|
|
## Pi-hole v6 API (When You Have the Password)
|
|
|
|
```bash
|
|
# Get session
|
|
curl -s -X POST "http://192.168.50.X:8090/api/auth" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"password":"YOUR_PASSWORD"}'
|
|
|
|
# Get summary (after auth)
|
|
curl -s "http://192.168.50.X:8090/api/stats/summary" \
|
|
-H "Authorization: Bearer SESSION_TOKEN"
|
|
```
|
|
|
|
## Other v6 Changes from v5
|
|
|
|
- `pihole -c` (chronometer) → removed, use PADD
|
|
- `/admin/api.php` → `/api` (different path, authentication required)
|
|
- `gravity.db` → replaced by `gravity` table in `pihole-FTL.db`
|
|
- Container has no Python or sqlite3 — query from host
|