Warhammer 40,000: Space Marine 2 in Russia: Fixing CSF (EAC) error via WireGuard bridge on the router
Stand
- Router: Keenetic Giga
- Firmware/ environment: Xkeen — running proxy core directly on Keenetic
- Core: Mihomo (clash.meta) with rule-based routing
- Rule lists: DigneZzZ/routing + MetaCubeX geo-data
All home network traffic is wrapped in Mihomo on the router (TPROXY), and then Mihomo distributes it according to rules: direct, or via VPN.
In short
SM2 emits CSF (or Error 4) and blocks access to network services. The reason is that game servers are unavailable from Russia by IP, and a regular VPN breaks anti-cheat. Solution: WireGuard bridge “entry into Russia → exit abroad”, configured directly in Mihomo as the default exit, so all traffic goes through a single clean foreign IP with open NAT.
Symptoms
- On startup, cannot connect to online services; in the menu —
Error code: CSF(orError 4,N-14/N-15). - Network modes unavailable; co-op PvP won’t start.
- Local toggles (file checks, EOS reinstall, firewall) do not help.
CSF basically = handoff failure of Easy Anti-Cheat: the game cannot authenticate you and kicks you at the login.
Why this happens in RF
Two independent problems stack up:
- Roskomnadzor blocks game servers by IP. SM2 backend and matchmaking run on foreign clouds (AWS, Azure). Some addresses are blocked, so you cannot directly access them from Russia.
- Regular VPN breaks anti-cheat:
- data-center IPs in EAC flag;
- strict/symmetric NAT for most proxies — the game needs open NAT (1/2);
- if the exit IP “jumps” (load balancing/nodes), the EAC session breaks instantly.
Trade-off: without VPN — IP block; with regular VPN — anti-cheat stumbles. Hence the endless CSF.
What didn’t work
Initially, the game was routed through a VLESS subscription (Mihomo, selective rules for Epic/EOS domains, EAC and game ports). Routing was correct — but CSF remained:
- exit IP of subscription nodes — data center, which EAC dislikes;
- symmetric NAT → matchmaking won’t start;
- different parts of the game traffic (login, servers, STUN) could go out via different IPs — the anti-cheat sees desynchronization.
Conclusion: it’s not about “where to direct,” but the quality of the exit — you need a clean IP and open NAT.
What worked: WireGuard bridge
WireGuard, configured as a bridge: entry point — a server inside Russia, exit — abroad.
Why exactly this:
- Entry into RF — connection from you to the entry point uses a Russian address: fast, stable, not hitting foreign IP blocks. The WG handshake is reliable.
- Exit abroad — externally you appear under a foreign IP, and game servers on AWS/Azure become accessible.
- Clean IP + full-cone NAT — such a bridge exit usually isn’t in the EAC flag, and NAT is open. Exactly what was missing.
- Single stable exit — one IP for the entire session, nothing jumps.
And the key — WG is brought up in Mihomo on the router, so all game traffic (login EOS, game servers, STUN, anti-cheat) goes through the same clean IP. This consistency finally defeats CSF.
Setup
1. Move wg.conf into Mihomo
Normal WireGuard config provided by the bridge provider:
[Interface]
PrivateKey = <YOUR_PRIVATE_KEY>
Address = 10.10.2.215/32
DNS = 8.8.8.8
[Peer]
PublicKey = <PUBLIC_KEY>"EY_SERVER>
Endpoint = <HOST_SERVER>:39547
PersistentKeepalive = 20
AllowedIPs = 0.0.0.0/0
Translates into a proxies entry for Mihomo as:
proxies:
- name: 🪽 WG
type: wireguard
private-key: <YOUR_PRIVATE_KEY> # = [Interface] PrivateKey
server: <HOST_SERVER> # = host from Endpoint (before ":")
port: 39547 # = port from Endpoint (after ":")
ip: 10.10.2.215 # = [Interface] Address without /mask
public-key: <SERVER_PUBLIC_KEY> # = [Peer] PublicKey
allowed-ips: ['0.0.0.0/0'] # = [Peer] AllowedIPs
persistent-keepalive: 20 # = [Peer] PersistentKeepalive
udp: true
mtu: 1408
remote-dns-resolve: true
dns: [8.8.8.8] # = [Interface] DNS
# pre-shared-key: <PSK> # = [Peer] PresharedKey (if any)
Table of field mappings:
wg.conf |
Mihomo |
|---|---|
[Interface] PrivateKey |
private-key |
[Interface] Address (IPv4) |
ip (without /32) |
[Interface] DNS |
dns (+ remote-dns-resolve: true) |
[Peer] PublicKey |
public-key |
[Peer] Endpoint host:port |
server + port |
[Peer] PresharedKey |
pre-shared-key |
[Peer] AllowedIPs |
allowed-ips |
[Peer] PersistentKeepalive |
persistent-keepalive |
2. Making WG the default exit
In the group selector put 🪽 WG first — it becomes the exit for all traffic going to the VPN. Subscriber nodes remain as a reserve:
proxy-groups:
- name: 🛡️ VPN
type: select
proxies:
- 🪽 WG # ← default exit (Moscow РФ → abroad)
- ⚡ Fastest # reserve: subscription nodes
- 📶 First available
3. Routing rules
Launch the game (Epic/EOS/EAC domains + SM2 backend + ports + STUN) at the top and route to 🛡️ VPN (= WG). Global model: everything in VPN, exceptions (RU/local/Direct) — bypass.
List of game domains (inline rule):
games-vpn:
type: inline
behavior: domain
payload:
- "+.epicgames.com" # Epic authentication
- "+.epicgames.dev" # EOS: matchmaking/relay/cross-play (core of multiplayer)
- "+.easyanticheat.net" # Easy Anti-Cheat
- "+.eac-cdn.com"
- "+.easy.ac"
- "+.prismray.io" # backend of SM2 online services (Saber)
- "+.hydrapi.net" # STUN/NAT traversal SM2
# ...epicgamescdn, unrealengine, fortnite, etc.
Order of rules (main):
rules:
# private networks and local access directly
- RULE-SET,direct-ip,DIRECT,no-resolve
- GEOIP,PRIVATE,DIRECT,no-resolve
- RULE-SET,geosite-private,DIRECT
# GAME → VPN(=WG), at the top, so nothing intercepts
- RULE-SET,games-vpn,🛡️ VPN
- AND,((NETWORK,tcp),(DST-PORT,11700-11720)),🛡️ VPN # SM2 ports (Saber)
- AND,((NETWORK,udp),(DST-PORT,48800-55000)),🛡️ VPN # SM2 ports (Saber)
# services needing a real RU-IP (Gosuslugi, pushes) — direct
- RULE-SET,vpndetect,DIRECT
# torrent trackers — direct
- RULE-SET,torrent-trackers,DIRECT
# bypass Roskomnadzor → VPN
- RULE-SET,refilter-d,🛡️ VPN
- RULE-SET,proxy,🛡️ VPN
# Russian sites/IP — direct
- RULE-SET,geosite-ru,🏠 RU
- RULE-SET,direct,DIRECT
- RULE-SET,geoip-ru,🏠 RU,no-resolve
# everything else → VPN(=WG)
- MATCH,🛡️ VPN
The lists refilter-*, proxy, direct, games, geoip-ru etc. are rule-providers from DigneZzZ/routing and MetaCubeX in .mrs format (compact for router).
The gist is that both game rules and the final MATCH lead to the same group 🛡️ VPN, which equals 🪽 WG. This means all game traffic exits through one WG-IP — exactly what EAC needs.
Important points, without which it won’t work
- One tunnel for all game traffic. If some connections go through a different IP, anti-cheat may fail again.
MATCH → VPN(=WG)guarantees a single exit even for “raw” IPs and STUN. - Open game ports: SM2 — TCP 11700–11720 and UDP 48800–55000; the tunnel must allow UDP.
- Open NAT (full-cone, type 1/2) on the bridge exit — otherwise co-op won’t form.
- Do not duplicate WG. One client per key. Do not run WireGuard on the router (as a separate interface) and Mihomo with the same keys at the same time — server dislikes two sessions.
- Client hygiene: verify file integrity in Steam, no mods (EAC triggers “Mods Detected” → also CSF), correct system time.
Result
CSF in Space Marine 2 from Russia is a sum of IP blocks and EAC’s hostility to data-center VPNs with strict NAT. The remedy is not “any VPN,” but a WireGuard bridge with Russian ingress and foreign egress, set up in Mihomo on the router and default-exit, so all game traffic goes through a single clean IP with open NAT. After that, EAC handshake passes and network modes finally work.
The same logic treats other EAC/EOS games blocked by IP.
