Skip to content

Wie es funktioniert ​

Der Tunnel trägt nur DNS ​

Sift baut ein VpnService mit genau zwei Routen: 10.120.0.2/32 für IPv4 und, falls das Netz ein IPv6-Default-Route hat, fd00:120::2/128. Alles andere – Spielverkehr, Streaming, Downloads – läuft über die physische Verbindung.

addDnsServer() allein reicht dafür nicht: Android legt dabei keine Route an. Erst die explizite /32-Route bringt DNS in den Tunnel. Umgekehrt sorgt die fehlende 0.0.0.0/0-Route dafür, dass tun0 für allen anderen Verkehr gar nicht erst in Frage kommt.

Aus diesem Grund ist der Ping-Zuschlag null: Der Verkehr nimmt denselben Pfad wie ohne VPN.

Der Paketpfad ​

Der Kern ist reines C17 in libsift_core.so:

  1. epoll_wait schläft, solange nichts passiert – kein Timer, kein Polling.
  2. Ein read() auf das TUN-Fd liefert ein Paket in einen statisch allokierten 2-KB-Puffer.
  3. Der Parser prüft Version, Header-Länge, Protokoll (nur UDP), Zielport 53 und dass es eine Anfrage und keine Antwort ist.
  4. Der QNAME wird kleingeschrieben und labelweise umgedreht.
  5. Ein Lookup im Trie entscheidet BLOCK oder ALLOW.
  6. Bei BLOCK wird die Antwort im selben Puffer gebaut, inklusive Checksummen. Bei ALLOW kommt der Cache oder der Upstream.

Es gibt keine Allokation pro Paket, keinen Thread pro Anfrage und kein memset über den Antwortpuffer.

Regeln: mmap statt Heap ​

Der Regelbestand liegt als komprimierter Reverse-Radix-Trie in einer Datei und wird read-only gemappt. Ein Knoten ist exakt 8 Byte groß; Labels liegen in einem String-Pool, Verweise sind uint32_t-Offsets, keine Zeiger.

Das ist der entscheidende Unterschied zu einer HashMap<String, …>: Die Seiten sind dateigestützt, vom Kernel reclaimbar und tauchen nicht als anonyme dirty memory auf. Ein Preset mit einigen hundert Domains belegt wenige Kilobyte; ein Import mit zehntausenden Einträgen bleibt im einstelligen Megabyte-Bereich – ohne dass der Java-Heap wächst.

Regeländerungen laufen über einen atomaren Austausch: kompilieren, fsync, rename, dann ein eventfd-Signal an den Engine-Thread. Der mappt die neue Datei, validiert Magic, Version und FNV-1a-Checksumme und tauscht den Zeiger. Ist die Datei kaputt, bleibt die alte Map aktiv.

Antworten richtig bauen ​

Geblockte A-Anfragen bekommen 0.0.0.0. Für AAAA gibt es NODATA statt :: – :: ist eine positive Antwort, und manche Clients versuchen trotzdem, dorthin zu verbinden. NODATA sagt sauber „kein IPv6 vorhanden“, und Happy Eyeballs fällt ohne Verzögerung auf IPv4 zurück.

Für HTTPS/SVCB gilt dasselbe: NODATA, damit Clients auf normales A/AAAA zurückfallen, statt an einem SVCB-Eintrag zu hängen.

CNAME-Uncloaking ​

Viele Tracker verstecken sich hinter einem unauffälligen Erstanbieter-Namen, der per CNAME auf ihr Netz zeigt. Sift läuft die CNAME-Kette einer Upstream-Antwort entlang – maximal vier Glieder, maximal 64 Records – und prüft jedes Ziel gegen den Trie. Trifft eines, wird die Antwort verworfen und der Client bekommt NXDOMAIN.

Cache ​

Ein LRU mit 256 Slots hält erlaubte Antworten. TTLs werden auf 30–3600 Sekunden geklemmt, damit kurze TTLs nicht jede Anfrage zum Upstream schicken und lange TTLs nicht ewig veralten. Zeitbasis ist CLOCK_BOOTTIME, damit ein 30-Sekunden-Eintrag einen zehnminütigen Systemschlaf überlebt.

Speicher, ehrlich gemessen ​

Die interessante Zahl ist nicht VmRSS – ein aus Zygote geforkter Prozess zählt geteilte Framework-Seiten voll mit. Gemessen wird der Unterschied zum leeren Dienst:

MetrikZielwert
Pss-Delta≤ 1.200 KB
Private_Dirty≤ 700 KB
RssAnon≤ 900 KB

Der native Anteil selbst liegt bei rund 60 KB privat, dazu kommt der dateigestützte Code und der gemappte Trie.

Sift ist ein lokaler DNS-Blocker für Android. Kein Account, keine Cloud.