
Exécutez Radmin VPN sur Linux via Wine — pilote personnalisé, pont TAP, zéro perte de paquets
# Radmin VPN pour Linux
Exécutez [Radmin VPN](https://www.radmin-vpn.com/) sur Linux via Wine. Rejoignez des réseaux VPN, voyez vos pairs, jouez à des jeux — le tout sans machine virtuelle Windows.
> Je ne l'ai pas construit parce que c'était plus facile qu'une VM. Je l'ai construit parce que je pensais que c'était plus facile qu'une VM.
**Code assisté par IA.** Développé en collaboration entre un humain et Claude (Anthropic). Le pilote, les hooks et le pont ont été écrits avec une rétro-ingénierie assistée par IA du protocole pilote non documenté de Radmin VPN à l'aide de Ghidra. **Cela fonctionne, mais sans aucune garantie.** Non affilié à Famatech. Radmin VPN est propriétaire — téléchargez-le vous-même depuis [radmin-vpn.com](https://www.radmin-vpn.com/). Utilisation à vos propres risques.
## Comment ça fonctionne
Le service Windows de Radmin VPN communique avec un pilote miniport NDIS pour sa carte réseau virtuelle. Wine ne prend pas en charge NDIS, nous remplaçons donc le pilote par notre propre implémentation qui fait le pont vers un périphérique TAP Linux. Une DLL de hook gère les problèmes de compatibilité Wine (nommage de la carte, permissions du registre). Le résultat est un client Radmin VPN entièrement fonctionnel s'exécutant nativement sous Wine.
```
Application Linux ← TAP (radminvpn0) ← tap_bridge ← FIFO ← rvpnnetmp.sys (pilote Wine) ← RvControlSvc.exe
```
## Démarrage rapide (AppImage, recommandé)
Récupérez `RadminVPN-Linux-x86_64.AppImage` depuis la [dernière version](https://github.com/baptisterajaut/radmin-vpn-linux/releases/latest). Rien à installer — Wine est inclus.
> **Candidats à la version** (par ex. `v1.0.0-rc1`) sont publiés comme *pré-versions* GitHub. Le lien `latest` ci-dessus pointe toujours vers la version *stable* la plus récente, donc les pré-versions n'y apparaîtront pas — récupérez-les depuis la [liste complète des versions](https://github.com/baptisterajaut/radmin-vpn-linux/releases).
```bash
chmod +x RadminVPN-Linux-x86_64.AppImage
./RadminVPN-Linux-x86_64.AppImage
```
Au premier lancement, il télécharge le programme d'installation de Radmin VPN contre lequel il est validé (ou passez le vôtre avec `--installer`). Un terminal s'ouvre avec la progression, et une invite de mot de passe sudo est nécessaire pour la configuration TAP.
L'état persistant (wineprefix, MAC, journaux) se trouve dans `~/.local/share/radmin-vpn-linux/`.
> **Radmin se ferme tout seul ?** Son propre programme de mise à jour installe une version plus récente dans le
> préfixe en cours d'exécution. Faites plutôt la mise à niveau de manière contrôlée — cela conserve votre préfixe et votre identifiant Radmin :
> ```bash
> ./RadminVPN-Linux-x86_64.AppImage --update
> ```
> Puis désactivez « Mises à jour automatiques » dans les paramètres Radmin pour qu'il cesse d'essayer. Détails dans
> [le propre programme de mise à jour de Radmin](#le-propre-programme-de-mise-à-jour-de-radmin).
## Prérequis (compilation source / non-AppImage)
- **Wine** >= 11.0 (testé sur Wine 11.5 Arch Linux et Wine 11.6 Ubuntu 24.04)
- **mingw-w64** compilateurs croisés (`i686-w64-mingw32-gcc`, `x86_64-w64-mingw32-gcc`) — pour compiler depuis les sources
- **iconv** (glibc) — pour l'analyse des journaux du service
- **sudo** — pour la création du périphérique TAP et le routage
- **Support noyau TUN/TAP** — généralement intégré, vérifiez avec `modprobe tun`
- **Programme d'installation Radmin VPN** — téléchargé automatiquement si absent. La version contre laquelle ce projet est validé est épinglée à un seul endroit, `RADMIN_VERSION` dans `lib.sh` (actuellement **2.1.4951.1** ; 2.0.4899.9 fonctionne aussi). Radmin VPN **1.4 n'est pas pris en charge** — il s'enregistre et ouvre la carte mais ne termine jamais la connexion sous le shim Wine, laissant l'interface graphique bloquée sur « Connexion... ».
### Arch Linux
```bash
sudo pacman -S wine mingw-w64-gcc
```
### Ubuntu/Debian
```bash
sudo apt install wine64 wine32 gcc-mingw-w64
```
## Démarrage rapide depuis les sources
```bash
git clone https://github.com/baptisterajaut/radmin-vpn-linux.git
cd radmin-vpn-linux
# Option A : télécharger les binaires précompilés depuis GitHub Releases
mkdir -p build
TAG=$(curl -sI https://github.com/baptisterajaut/radmin-vpn-linux/releases/latest | grep -i location | grep -oP 'v[\d.]+')
curl -sL "https://github.com/baptisterajaut/radmin-vpn-linux/releases/download/${TAG}/radmin-vpn-linux-${TAG}.tar.gz" \
| tar xz -C build/
# Option B : compiler depuis les sources
make
# Télécharger le programme d'installation Radmin VPN depuis https://www.radmin-vpn.com/
./run.sh --installer ~/Downloads/Radmin_VPN_*.exe
```
Pour les exécutions suivantes, simplement :
```bash
./run.sh
```
### Options de ligne de commande
| Option | Description |
|------|-------------|
| `--installer <chemin>` | Chemin vers le programme d'installation `Radmin_VPN_*.exe` (première exécution uniquement). |
| `--no-ui` | Exécute le service sans lancer l'interface graphique Radmin. |
| `--update` | Met à niveau Radmin vers la version épinglée en place. Conserve le préfixe, donc l'identifiant RID enregistré auprès de Famatech survit. |
| `--no-broadcast-routes` | N'ajoute pas les routes broadcast/multicast → TAP. |
| `--filter-ui` | Lance l'interface GTK4 optionnelle de filtrage de paquets (désactivée par défaut). |
| `--fix-chat` | Corrige le `qwindows.dll` de Qt pour résoudre le crash de la fenêtre de chat sous Wine (désactivé par défaut). |
### Le propre programme de mise à jour de Radmin
L'interface graphique de Radmin peut télécharger une version plus récente et exécuter son programme d'installation *dans le préfixe actif*,
ce qui tue l'interface graphique et fait planter le service en cours d'exécution (le journal de l'interface graphique montre alors un programme d'installation
sous `AppData\\Local\\Temp`). Deux défenses : le lanceur fournit la version validée actuelle
pour que le programme de mise à jour n'ait rien à pousser, et une interface graphique qui meurt ne fait plus tomber le tunnel
avec elle — elle est redémarrée une fois, puis le VPN continue de fonctionner sans interface. Pour mettre à niveau
délibérément, utilisez `./run.sh --update`, qui arrête tout d'abord. Désactiver
« Mises à jour automatiques » dans les paramètres de l'interface graphique évite entièrement la course.
`--filter-ui` et `--fix-chat` sont tous deux facultatifs. L'interface de filtrage nécessite le binaire `rvpn_filter_ui`
(construit par `make`) ; la correction du chat nécessite `patch_qwindows_font.py` et un interpréteur Python 3.
## Modes sans interface / serveur
Deux lanceurs supplémentaires exécutent Radmin VPN sans bureau local — pour un VPS ou un hébergeur en datacenter. Les deux installent et configurent exactement comme `run.sh` (même wineprefix, même flux de première exécution `--installer`, même pont TAP) ; ils ne diffèrent que par la façon dont l'interface graphique est atteinte.
### `run_datacenter.sh` — interface graphique via le navigateur
Exécute la véritable interface graphique Radmin sur un affichage virtuel (Xvfb) et la sert via VNC + noVNC, afin que vous puissiez configurer vos réseaux depuis un navigateur puis la laisser tourner.
```bash
sudo apt install -y xvfb x11vnc websockify novnc # ou : make install-datacenter-deps
./run_datacenter.sh --installer ~/Downloads/Radmin_VPN_*.exe
```
| Option | Défaut | Description |
|------|---------|-------------|
| `--vnc-port <n>` | `5900` | Port du serveur x11vnc. |
| `--web-port <n>` | `6080` | Port du point d'accès web noVNC. |
| `--vnc-password <motdepasse>` | *(aucun)* | Mot de passe pour la session VNC / web. |
| `--web-bind <adresse>` | `127.0.0.1` | Adresse sur laquelle noVNC écoute. |
Par défaut, noVNC se lie à `127.0.0.1`, donc il n'est accessible que via un tunnel SSH :
```bash
ssh -L 6080:localhost:6080 votre-vps # puis ouvrez http://localhost:6080/vnc.html
```
Pour l'exposer publiquement, passez `--web-bind 0.0.0.0` **avec** `--vnc-password` — sinon quiconque atteint le port web atterrit sur un bureau non authentifié avec accès root. Le script affiche un avertissement explicite si vous vous liez publiquement sans mot de passe.
### `run_vps.sh` — service uniquement, GUID fixe
Démarre le service sans interface avec un GUID TAP codé en dur (pas de WMI Wine requis) et aucune interface graphique du tout. Si un énumérateur de réseau personnalisé (`rv_net_enum.exe`, non fourni dans ce dépôt) est présent un répertoire au-dessus, il est lancé ; sinon le service s'exécute simplement jusqu'à ce que vous fassiez Ctrl+C. Seule l'option `--installer` est acceptée.
```bash
./run_vps.sh --installer ~/Downloads/Radmin_VPN_*.exe
```
## Compilation depuis les sources
Nécessite les compilateurs croisés `mingw-w64`. Des binaires précompilés sont disponibles depuis [Releases](https://github.com/baptisterajaut/radmin-vpn-linux/releases) (construits par CI à chaque version étiquetée) si vous ne voulez pas installer mingw.
```bash
make # compile tout vers build/
make clean # supprime les artefacts de compilation
```
Produit :
- `build/rvpnnetmp.sys` — pilote noyau Wine (PE 64 bits)
- `build/adapter_hook.dll` — DLL de hook (PE 32 bits)
- `build/rvpn_launcher.exe` — injecteur de DLL (PE 32 bits)
- `build/netsh.exe` — remplacement de netsh (PE 32 bits, installé dans SysWOW64)
- `build/netsh64.exe` — remplacement de netsh (PE 64 bits, installé dans System32)
- `build/drvinst.exe` — stub sans opération remplaçant le véritable installateur de pilote NDIS de Radmin (problème #12)
- `build/tap_bridge` — pont TAP Linux natif
- `build/rvpn_dnsfix.so` — shim natif LD_PRELOAD, préchargé uniquement dans le service
- `build/rvpn_reuseport.so` — shim natif LD_PRELOAD, préchargé dans `wineserver`
- `build/rvpn_filter_ui` — interface GTK4 optionnelle de filtrage de paquets (`--filter-ui`)
### Construction de l'AppImage
```bash
make
./packaging/build-appimage.sh # → packaging/dist/RadminVPN-Linux-x86_64.AppImage
```
Télécharge la compilation Wine-Staging `amd64-wow64` de Kron4ek (~100 Mo) et `appimagetool` au premier lancement, les met en cache dans `packaging/dist/`. Nécessite `curl` et ImageMagick (`convert`).
## Ce que fait `run.sh`
1. **Première exécution** : installe Radmin VPN via Wine (`/VERYSILENT`), supprime le véritable pilote NDIS (incompatible avec Wine), enregistre notre pilote personnalisé
2. **Chaque exécution** : crée un périphérique TAP, démarre le pont TAP-vers-FIFO, configure le registre Wine (GUID de la carte, service pilote), lance le service et l'interface graphique Radmin VPN
3. **À la sortie** (Ctrl+C ou fermeture de l'interface graphique) : tue Wine, supprime le périphérique TAP, nettoie
Le wineprefix est stocké dans `./wineprefix/` (exécution depuis les sources) ou `~/.local/share/radmin-vpn-linux/wineprefix/` (AppImage). Une adresse MAC persistante est générée à la première exécution et enregistrée dans le wineprefix.
## Architecture
| Composant | Description |
|---|---|
| `rvpnnetmp.sys` | Pilote noyau Wine. Émule le miniport NDIS de Radmin. Gère les IOCTL (VERSION, STATUS, SETUP, PEERMAC), l'encodage/décodage des trames TLV, la file d'attente IRP pour les E/S chevauchées, le routage des trames basé sur MAC pour la prise en charge multi-pairs. |
| `adapter_hook.dll` | DLL compagnon chargée aux côtés de RvControlSvc.exe. Hooks IAT : renomme la carte TAP pour correspondre au nom attendu par Radmin, neutralise `RegSetKeySecurity` pour contourner un bug SCM de Wine où les services ne reçoivent pas le SID SYSTEM, et répond aux quatre recherches `Perf{Increment,Decrement}ULong{,Long}CounterValue` que `advapi32` de Wine n'exporte pas. Ce dernier point n'est pas cosmétique : Radmin 2.1 résout sept points d'entrée perflib dans une cascade tout-ou-rien, puis transmet l'objet compteur par-pair résultant comme charge utile de la poignée de main entre pairs — donc quatre exports de télémétrie manquants font tomber tout le chemin de données (problème #24, `docs/wine-perflib.md`). |
| `tap_bridge` | Binaire Linux natif. Relaie les trames Ethernet entre le périphérique TAP et les tubes nommés (FIFO) que le pilote Wine lit/écrit. |
| `netsh.exe` / `netsh64.exe` | Remplace le stub netsh de Wine (32 bits dans SysWOW64, 64 bits dans System32). Traduit les commandes Windows `netsh interface ip` en commandes Linux `ip addr`/`ip link` via un relais basé sur fichiers, validant l'adresse avant qu'elle n'atteigne le relais root. |
| `rvpn_launcher.exe` | Injecte `adapter_hook.dll` dans le processus du service Radmin via `CreateRemoteThread` + `LoadLibrary`. |
| `drvinst.exe` | Stub sans opération remplaçant le véritable installateur de pilote NDIS de Radmin. Radmin l'exécute au moment de l'exécution pour charger `NetMP60_1_1_64.sys`, qui fait avorter Wine 11.x via `NdisInitializeReadWriteLock` (problème #12) ; puisque notre pilote remplace déjà cette carte, le véritable ne doit jamais se charger. |
| `rvpn_dnsfix.so` | Shim natif `LD_PRELOAD`, injecté uniquement au lancement du service. Court-circuite le DNS inverse (`getnameinfo`/`gethostbyaddr`) des IPv4/IPv6 privées au niveau glibc. Radmin fait une résolution PTR de chaque adresse candidate locale qu'il collecte ; sur un hôte dont le résolveur bloque les requêtes PTR RFC1918, cela stagne pendant des minutes et le service s'enregistre mais ne devient jamais prêt (problème #16). La recherche est émise par le côté Unix de Wine, hors de portée de tout hook dans `adapter_hook.dll`. |
| `rvpn_reuseport.so` | Shim natif `LD_PRELOAD`, préchargé dans `wineserver` (pas dans le service — les sockets Unix appartiennent à wineserver). Définit `SO_REUSEPORT` sur chaque socket TCP à la création. Radmin 2.1 fait de la traversée NAT par réutilisation de port TCP : il lie un écouteur à `uplink:PORT`, annonce ce mappage, puis lie ses sockets pairs sortants au même port local avec `SO_REUSEADDR`, ce que Windows autorise. Wine traduit cette demande en aucune option de niveau Unix pour TCP (`server/sock.c`, `SO_REUSEPORT` est compilé uniquement pour `__APPLE__`), donc le noyau refuse la seconde liaison et le client voit `WSAEACCES` à chaque connexion pair (problème #24). La comptabilité des conflits de wineserver s'exécute toujours, donc les liaisons que Windows refuserait sont toujours refusées. |
| `rvpn_filter_ui` | Interface GTK4 optionnelle pour inspecter et filtrer les paquets traversant le pont. Désactivée par défaut ; lancez avec `--filter-ui`. |
## Dépannage
**Interface graphique bloquée sur « En attente de la carte »** : le pilote ne se charge pas. Vérifiez que `/tmp/radmin_driver.log` existe et contient du contenu. S'il est vide, l'enregistrement du service pilote peut manquer — supprimez le wineprefix et relancez.
**Enregistré mais jamais « prêt »** : le service atteint `Registered as #…` et s'arrête là. Deux causes connues, toutes deux signalées par le bloc de diagnostic imprimé au délai d'attente. (a) Un proxy transparent ou un tunnel avec sa propre politique de routage (sing-box, clash/mihomo, v2ray…) interceptant la connexion sortante — de telles piles répondent à la poignée de main TCP localement, donc le socket lit ESTABLISHED alors que rien n'est relayé. Excluez `26.0.0.0/8` de ses routes, et `*.radminte.com` aussi si vous pouvez y accéder directement. (b) Un résolveur qui bloque les recherches inverses d'adresses privées (un `docker0` à `172.17.0.1` est le déclencheur habituel). `rvpn_dnsfix.so` gère cela, mais c'est un shim 64 bits : un Wine système compilé en *old-wow64* l'ignore. Solution de contournement — ajoutez une ligne pour l'adresse dans `/etc/hosts`, ou utilisez `contrib/run-in-netns.sh` (qui amène le service à « prêt » mais maintient le VPN dans un espace de noms : les applications hôtes ne peuvent pas atteindre `26.0.0.0/8`, lisez l'avertissement dans son en-tête).
**Vos *autres* applications Wine se sont cassées après avoir exécuté Radmin (`unimplemented function ndis.sys.NdisInitializeReadWriteLock`)** : les versions jusqu'à 1.0.0 laissaient `winemenubuilder.exe` de Wine s'exécuter, ce qui réécrit les associations de fichiers Windows de l'hôte (`.exe`, `.msi`, `.lnk`, `.reg`, `.chm`, …) pour qu'elles s'ouvrent dans *notre* préfixe. D'autres programmes Windows démarrent alors dans le préfixe Radmin, et si le véritable miniport NDIS de Radmin (`RvNetMP60`) y est toujours enregistré, chacun d'eux avorte sur ce stub ndis. 1.0.1 désactive `winemenubuilder` partout, purge les entrées qu'il a écrites pour un préfixe Radmin, et supprime `RvNetMP60` de `system.reg` hors ligne — exécutez simplement 1.0.1 une fois et les deux sont réparés. Notez que `echo $WINEPREFIX` ne montre rien dans cette situation : le détournement vit dans `~/.local/share/applications/*.desktop`, pas dans votre environnement. Nettoyage manuel, si vous préférez :
```bash
grep -rl 'WINEPREFIX=[^"]*radmin' ~/.local/share/applications --include='*.desktop' | xargs -r rm
rm -rf ~/.local/share/applications/wine/Programs/"Radmin VPN"
update-desktop-database ~/.local/share/applications
```
**En ligne et dans vos réseaux, mais aucun pair ne se connecte jamais (`error: 0x700000000`)** : c'est Radmin 2.1 sur une compilation antérieure à 1.1.0. Le journal du service semble sain jusqu'aux connexions entre pairs, qui échouent alors en boucle avec ce code et abandonnent finalement avec `node offline`. Deux lacunes Wine indépendantes, toutes deux corrigées dans 1.1.0 et toutes deux expliquées ci-dessus dans les lignes `rvpn_reuseport.so` et `adapter_hook.dll` : Wine ne définit aucune option de niveau Unix pour un `SO_REUSEADDR` TCP, donc la traversée NAT par réutilisation de port de 2.1 obtient `WSAEACCES` à chaque connexion pair ; et `advapi32` de Wine manque quatre exports perflib, sans lesquels 2.1 envoie une charge utile nulle comme poignée de main entre pairs. Notez que le code lui-même ne porte aucune information — c'est l'abandon générique à la fin de la cascade d'erreurs, donc n'essayez pas d'en déduire un sous-système. La correction consiste à mettre à jour ce projet vers 1.1.0 ; rien ne doit changer côté Radmin.
**Le service meurt immédiatement** : vérifiez `/tmp/radmin_service.log` pour les erreurs Wine. Cause courante : ancien wineprefix d'une version Wine différente. Supprimez `./wineprefix/` et relancez.
**0 % de perte de paquets avec un pair, perte élevée avec plusieurs** : c'était le bug d'origine — corrigé par le routage des trames basé sur MAC dans le pilote. Assurez-vous d'utiliser la dernière compilation.
**Le premier ping est lent (~1 s)** : la dernière version met en cache les adresses MAC pour chaque paquet IP que le pilote voit. Si l'IP cible a envoyé au moins un paquet pendant que le pilote tournait, la MAC est déjà en cache et il n'y a aucun délai ARP. Seuls les pairs complètement silencieux (ceux qui n'ont jamais envoyé de paquet) nécessitent la résolution ARP initiale, ce qui ajoute ~1 seconde.
**Les jeux LAN ne voient pas les autres pairs / « auto-découverte » cassée** : la plupart des jeux LAN se découvrent mutuellement avec des sondes broadcast (UDP vers `255.255.255.255`) ou multicast (`224.0.0.0/4`). Sous Windows, le pilote TAP Radmin s'annonce comme l'interface préférée pour ces flux ; sous Linux, vous devez le dire explicitement au noyau. `run.sh` installe maintenant deux routes supplémentaires lorsque le VPN se lève :
```
ip route append 255.255.255.255/32 dev radminvpn0 metric 0
ip route append 224.0.0.0/4 dev radminvpn0 metric 0
```
Effet secondaire : mDNS / Bonjour / SSDP sur votre LAN physique (Chromecast, AirPrint, Sonos, téléviseurs intelligents, ...) passeront par le VPN pendant qu'il est actif. Si vous avez besoin de la découverte LAN locale et de Radmin en parallèle, exécutez avec `--no-broadcast-routes` :
```bash
./run.sh --no-broadcast-routes
```
Les routes sont limitées au périphérique TAP, donc elles sont automatiquement supprimées lorsque `run.sh` démonte le périphérique à la sortie.
## Limitations connues
- Une seule instance peut s'exécuter à la fois (FIFO partagés dans `/tmp/`)
- La route on-link `26.0.0.0/8` affecte tout le système pendant l'exécution (nettoyée à la sortie)
- Les routes broadcast par défaut (`255.255.255.255/32`) et multicast (`224.0.0.0/4`) sont dirigées vers le VPN — désactivez avec `--no-broadcast-routes` si vous avez besoin de mDNS / SSDP LAN local en parallèle
- Les versions plus anciennes de Wine (< 11.0) peuvent avoir un comportement d'E/S chevauchées différent qui casse le pilote
## Notes
**Contournement d'un bug Wine.** Le hook `RegSetKeySecurity` contourne une [limitation connue de Wine](https://forum.winehq.org/viewtopic.php?t=37183) où les services ne reçoivent pas le SID SYSTEM (S-1-5-18). Cela peut être corrigé en amont dans une future version de Wine.
## Crédits
Rétro-ingénierie et shim Wine : [@baptisterajaut](https://github.com/baptisterajaut).
- [@ayozetr](https://github.com/ayozetr) — a tracé le blocage « s'enregistre mais jamais prêt » jusqu'à une
recherche DNS inverse d'une adresse privée bloquée par le résolveur hôte, l'a prouvée avec une
reproduction bare-libc après que quatre théories erronées aient été écartées (la mienne incluse), et a écrit
l'original de `src/rvpn_dnsfix.c` et `contrib/run-in-netns.sh`.
- [@gringoestrangeiro](https://github.com/gringoestrangeiro) — travail de stabilité Linux : filtres de paquets,
cache ARP, récupération après crash, modes sans interface et datacenter.
- [@yuxiaole-bili](https://github.com/yuxiaole-bili) — a découvert que le SCM de Wine démarre automatiquement une seconde
instance de service non hookée qui se dispute la carte avec celle injectée.
- [@D1spell-tech](https://github.com/D1spell-tech), [@Milkiway13](https://github.com/Milkiway13) et
tous ceux qui ont collé un bloc de diagnostic complet — deux hôtes indépendants, c'est ce qui rend un bug traçable.
## Licence
GPL-3.0. Voir [LICENSE](https://github.com/baptisterajaut/radmin-vpn-linux/blob/main/LICENSE).
Dans l'esprit, ce code est dans le domaine public — faites-en ce que vous voulez. La GPL est ici comme filet de sécurité juridique : elle protège explicitement la rétro-ingénierie pour l'interopérabilité, ce que fait ce projet. Ceinture et bretelles.
Radmin VPN est un logiciel propriétaire de Famatech Corp. Ce projet fournit uniquement des outils d'interopérabilité — aucun code Famatech n'est inclus ni distribué.