This article show you the ultimate way to set up a transparent proxy on Linux using clash and iptables to bypass the GFW in China.

We use:

* [clash](https://github.com/Dreamacro/clash) as the proxy software
* [online subconverter](https://github.com/tindy2013/subconverter) and crontab to periodically update config file.
* [ACL4SSR](https://github.com/ACL4SSR/ACL4SSR/tree/master) to provide GFW rules.

You can go to [github gist](https://gist.github.com/cld4h/9a03ec2f826a25be5ab974fdbc540de4) to download all files mentioned in this article.

All files you need:

```txt
.
โ”œโ”€โ”€ clash-base-config.yaml  ---๐ŸŸขbase config for clash to work on tproxy and fake-ip mode
โ”œโ”€โ”€ clash.service           ---๐ŸŸขsystemd unit file to start up clash
โ”œโ”€โ”€ clean.sh                ---๐ŸŸขscript to clean iptables
โ”œโ”€โ”€ config.ini              ---๐ŸŸขconfig file for subconverter
โ”œโ”€โ”€ iptables.sh             ---๐ŸŸขiptables config file
โ”œโ”€โ”€ update-config.sh        ---๐ŸŸกsubscription update script; "XXXXXXXX" need to be replaced
โ”œโ”€โ”€ config.yaml             ---โญ•clash config file, to be generated from update-config.sh
โ”œโ”€โ”€ README.md
โ””โ”€โ”€ README.zh-cn.md

๐ŸŸข: Doesn't need to change
๐ŸŸก: Needs to change
โญ•: Needs to be generated
```

## Reference

[clash-tproxy](https://www.sobyte.net/post/2022-02/clash-tproxy/)

[Transparent proxy demo code](https://git.breakpoint.cc/cgit/fw/tcprdr.git)

## Prerequisite

* a `clash` installed. (By default in `/usr/bin/clash`)
* iptables
* systemd (It's totally fine to use other init programs, but here I'll only show the configuration of systemd, which is the default of most GNU/Linux distros.)
* crontab


## Steps

### Create a system user called clash

```
useradd -r -M -s /usr/sbin/nologin clash
```

Since we want to proxy all internet traffics, we need to prevent the traffic from looping.
Thus, we create a distinct user called `clash` (the name can be arbitrary) to run the proxy software `clash`.
We'll make an iptables rule in the following steps to distinguish traffic coming from the user `clash`.

### Create iptables and routing rules

Create a shell script of iptables command called `/etc/clash/iptables.sh`.

The file contains 3 main sections: 

1. iptables rules to hijack the dns query traffic, 
2. config for `clash` chain
3. config for `clash_local` chain

iptables:

```txt
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                                                                      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         โ”‚             PREROUTING                                                INPUT          โ”‚         โ”‚
โ”‚         โ”‚                                                                                      โ”‚         โ”‚
โ”‚         โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚         โ”‚
โ”‚         โ”‚  โ”‚                                     โ”‚         /โ”€โ”€โ”€\        โ”‚                    โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ”‚ โ”Œโ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ” โ”‚        /     \       โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚         โ”‚
โ”‚         โ”œโ”€โ”€โ”คโ–บโ”‚rawโ”œโ”€โ–บโ”‚conntrackโ”œโ”€โ–บโ”‚mangleโ”œโ”€โ–บโ”‚natโ”œโ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ–บ<routing>โ”€โ”€โ”€โ”€โ”€โ”€โ”คโ–บโ”‚mangleโ”œโ”€โ–บโ”‚filterโ”œโ”€โ”คโ–บโ”‚         โ”‚
โ”‚         โ”‚  โ”‚ โ””โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”˜ โ”‚        \     /       โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ”‚                                     โ”‚         \โ”€โ”ฌโ”€/        โ”‚                    โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚         โ”‚
โ”‚         โ”‚                                                    โ”‚                                 โ”‚         โ”‚
โ”‚         โ”‚                               โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”‚                                 โ”‚         โ”‚
โ”‚         โ”‚                               โ”‚          โ”‚         โ”‚                                 โ”‚         โ”‚
โ”‚         โ”‚                               โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚         โ”‚                                 โ”‚         โ”‚
โ”‚         โ”‚                               โ”‚ โ”‚mangleโ—„โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                 โ”‚         โ”‚
โ”‚ Network โ”‚                               โ”‚ โ””โ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜ โ”‚                                           โ”‚  Local  โ”‚
โ”‚Interfaceโ”‚                               โ”‚    โ”‚     โ”‚ FORWARD                                   โ”‚ Process โ”‚
โ”‚         โ”‚                               โ”‚ โ”Œโ”€โ”€โ–ผโ”€โ”€โ”€โ” โ”‚                                  /โ”€โ”€โ”€\    โ”‚         โ”‚
โ”‚         โ”‚               โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”คfilterโ”‚ โ”‚                                 /     \   โ”‚         โ”‚
โ”‚         โ”‚               โ”‚               โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚                                <routing>โ—„โ”€โ”ค         โ”‚
โ”‚         โ”‚               โ”‚               โ”‚          โ”‚                                 \     /   โ”‚         โ”‚
โ”‚         โ”‚               โ”‚               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                  \โ”€โ”ฌโ”€/    โ”‚         โ”‚
โ”‚         โ”‚               โ”‚                                                               โ”‚      โ”‚         โ”‚
โ”‚         โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”               โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ” โ”‚         โ”‚
โ”‚         โ”‚  โ”‚            โ”‚    โ”‚     โ”€โ”€โ”€โ”€โ”€     โ”‚                                          โ”‚    โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ”‚ โ”Œโ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ–ผโ”€โ”€โ” โ”‚    /     \    โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ–ผโ”€โ”€โ” โ”‚ โ”‚         โ”‚
โ”‚         โ”‚โ—„โ”€โ”ผโ”€โ”คnatโ”‚โ—„โ”€โ”คmangleโ”‚โ—„โ”œโ”€โ”€โ”€<reroute>โ”€โ”€โ”€โ”ผโ”€โ”คfilterโ”‚โ—„โ”€โ”คnatโ”‚โ—„โ”€โ”คmangleโ”‚โ—„โ”€โ”คconntrackโ”‚โ—„โ”€โ”คrawโ”‚ โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ”‚ โ””โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚    \check/    โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”˜ โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ”‚                 โ”‚     โ”€โ”€โ”€โ”€โ”€     โ”‚                                               โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚         โ”‚
โ”‚         โ”‚     POSTROUTING                                        OUTPUT                        โ”‚         โ”‚
โ”‚         โ”‚                                                                                      โ”‚         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                                                                      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

`/etc/clash/iptables.sh`

```
#!/usr/bin/env bash

# set -ex options:
# -e Exit immediately if a command exits with a non-zero status
# -x Print commands and their arguments as they are executed.
set -ex

# ENABLE ipv4 forward
sysctl -w net.ipv4.ip_forward=1

# ROUTE RULES
ip rule add fwmark 666 table 666
ip route add local 0.0.0.0/0 table 666 dev lo

##################################
## hijack the dns query traffic ##
##################################

# Redirect all dns query traffic from LAN to port 1053
# Later clash will return a fake ip in 198.18.0.1/16
iptables -t nat -I PREROUTING -p udp --dport 53 -j REDIRECT --to 1053
# same for ipv6
ip6tables -t nat -I PREROUTING -p udp --dport 53 -j REDIRECT --to 1053


# Redirect dns query traffic from all local processes (except owned by user clash) to port 1053
# Later clash will return a fake ip in 198.18.0.1/16
iptables -t nat -N clash_dns
iptables -t nat -A OUTPUT -p udp --dport 53 -j clash_dns
iptables -t nat -A clash_dns -m owner --uid-owner clash -j RETURN
iptables -t nat -A clash_dns -p udp -j REDIRECT --to-ports 1053
# same for ipv6
ip6tables -t nat -N clash_dns
ip6tables -t nat -A OUTPUT -p udp --dport 53 -j clash_dns
ip6tables -t nat -A clash_dns -m owner --uid-owner clash -j RETURN
ip6tables -t nat -A clash_dns -p udp -j REDIRECT --to-ports 1053

##############################
## config for `clash` chain ##
##############################

# `clash` chain for using tproxy to redirect traffic to the clash listening port 7893
iptables -t mangle -N clash

# skip traffic to LAN or reserved address
# reference:
# https://en.wikipedia.org/wiki/List_of_assigned_/8_IPv4_address_blocks
# https://en.wikipedia.org/wiki/Private_network#IPv4
# IANA - Local Identification
iptables -t mangle -A clash -d 0.0.0.0/8 -j RETURN
# IANA - Loopback
iptables -t mangle -A clash -d 127.0.0.0/8 -j RETURN
# IANA - Private Use
iptables -t mangle -A clash -d 10.0.0.0/8 -j RETURN
iptables -t mangle -A clash -d 172.16.0.0/12 -j RETURN
iptables -t mangle -A clash -d 192.168.0.0/16 -j RETURN
# IPv4 Link-Local Addresses
iptables -t mangle -A clash -d 169.254.0.0/16 -j RETURN
# Multicast
iptables -t mangle -A clash -d 224.0.0.0/4 -j RETURN
# Future Use
iptables -t mangle -A clash -d 240.0.0.0/4 -j RETURN

# Forward all the other traffic to port 7893 and set tproxy mark
iptables -t mangle -A clash -p tcp -j TPROXY --on-port 7893 --tproxy-mark 666
iptables -t mangle -A clash -p udp -j TPROXY --on-port 7893 --tproxy-mark 666

# Append the `clash` chain to PREROUTING to enable it
iptables -t mangle -A PREROUTING -j clash

####################################
## config for `clash_local` chain ##
####################################

# `clash_local` chain to manipulate the traffic from local process: set fwmark 666 on traffic to public address
# (which will later reroute to dev lo, then redirect to tproxy port 7893 on the `clash` chain of `PREROUTING`)
iptables -t mangle -N clash_local

# rerouting traffic from nerdctl container
#iptables -t mangle -A clash_local -i nerdctl2 -p udp -j MARK --set-mark 666
#iptables -t mangle -A clash_local -i nerdctl2 -p tcp -j MARK --set-mark 666

# Don't touch traffic to LAN and reserved address 
iptables -t mangle -A clash_local -d 0.0.0.0/8 -j RETURN
iptables -t mangle -A clash_local -d 127.0.0.0/8 -j RETURN
iptables -t mangle -A clash_local -d 10.0.0.0/8 -j RETURN
iptables -t mangle -A clash_local -d 172.16.0.0/12 -j RETURN
iptables -t mangle -A clash_local -d 192.168.0.0/16 -j RETURN
iptables -t mangle -A clash_local -d 169.254.0.0/16 -j RETURN
iptables -t mangle -A clash_local -d 224.0.0.0/4 -j RETURN
iptables -t mangle -A clash_local -d 240.0.0.0/4 -j RETURN

# set mark for local traffic
# clash_local set mark for the traffic from local process,
# the marked traffic will come back to PREROUTING chain.
iptables -t mangle -A clash_local -p tcp -j MARK --set-mark 666
iptables -t mangle -A clash_local -p udp -j MARK --set-mark 666

# Don't touch traffic from a serving port
# https web server
# iptables -t mangle -I OUTPUT -p tcp --sport 443 -j RETURN
# transmission
# iptables -t mangle -I OUTPUT -p tcp --sport 51413 -j RETURN

# Don't touch traffic from the user `clash` to prevent looping
iptables -t mangle -A OUTPUT -p tcp -m owner --uid-owner clash -j RETURN
iptables -t mangle -A OUTPUT -p udp -m owner --uid-owner clash -j RETURN

# Append the `clash_local` chain to OUTPUT to enable it
iptables -t mangle -A OUTPUT -j clash_local

#######################
## unimportant staff ##
#######################

# fix ICMP(ping)
# This step does NOT make the ping result validate 
# (clash doesn't support forwarding ICMP), it just makes ping have a result.
# set --to-destination to a accessable address.
#sysctl -w net.ipv4.conf.all.route_localnet=1
#iptables -t nat -A PREROUTING -p icmp -d 198.18.0.0/16 -j DNAT --to-destination 127.0.0.1
#iptables -t nat -A OUTPUT -p icmp -d 198.18.0.0/16 -j DNAT --to-destination 127.0.0.1
```

After applying the script above, the system iptables should be like this:

```txt
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                                                                      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         โ”‚             PREROUTING           rule1*                               INPUT          โ”‚         โ”‚
โ”‚         โ”‚                                    โ”‚                                                 โ”‚         โ”‚
โ”‚         โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”                      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚         โ”‚
โ”‚         โ”‚  โ”‚                                 โ”‚   โ”‚         /โ”€โ”€โ”€\        โ”‚                    โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ”‚ โ”Œโ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”ดโ”€โ” โ”‚        /     \       โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚         โ”‚
โ”‚         โ”œโ”€โ”€โ”คโ–บโ”‚rawโ”œโ”€โ–บโ”‚conntrackโ”œโ”€โ–บโ”‚mangleโ”œโ”€โ–บโ”‚natโ”œโ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ–บ<routing>โ”€โ”€โ”€โ”€โ”€โ”€โ”คโ–บโ”‚mangleโ”œโ”€โ–บโ”‚filterโ”œโ”€โ”คโ–บโ”‚         โ”‚
โ”‚         โ”‚  โ”‚ โ””โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”˜ โ”‚        \     /       โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ”‚                      โ”‚              โ”‚         \โ”€โ”ฌโ”€/        โ”‚                    โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚          โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚         โ”‚
โ”‚         โ”‚                         โ”‚                          โ”‚                                 โ”‚         โ”‚
โ”‚         โ”‚                      โ”Œโ”€โ”€โ”ดโ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”‚                                 โ”‚         โ”‚
โ”‚         โ”‚                      โ”‚clashโ”‚  โ”‚          โ”‚         โ”‚                                 โ”‚         โ”‚
โ”‚         โ”‚                      โ””โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚         โ”‚                                 โ”‚         โ”‚
โ”‚         โ”‚                               โ”‚ โ”‚mangleโ—„โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                 โ”‚         โ”‚
โ”‚ Network โ”‚                               โ”‚ โ””โ”€โ”€โ”ฌโ”€โ”€โ”€โ”˜ โ”‚              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                โ”‚  Local  โ”‚
โ”‚Interfaceโ”‚                               โ”‚    โ”‚     โ”‚ FORWARD      โ”‚clash_localโ”‚                โ”‚ Process โ”‚
โ”‚         โ”‚                               โ”‚ โ”Œโ”€โ”€โ–ผโ”€โ”€โ”€โ” โ”‚              โ””โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       /โ”€โ”€โ”€\    โ”‚         โ”‚
โ”‚         โ”‚               โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”คfilterโ”‚ โ”‚                โ”‚                /     \   โ”‚         โ”‚
โ”‚         โ”‚               โ”‚               โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚                โ”œโ”€โ”€โ”€rule3*      <routing>โ—„โ”€โ”ค         โ”‚
โ”‚         โ”‚               โ”‚               โ”‚          โ”‚                โ”‚                \     /   โ”‚         โ”‚
โ”‚         โ”‚               โ”‚               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                โ”œโ”€โ”€โ”€rule2*        \โ”€โ”ฌโ”€/    โ”‚         โ”‚
โ”‚         โ”‚               โ”‚                                           โ”‚                   โ”‚      โ”‚         โ”‚
โ”‚         โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”               โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ” โ”‚         โ”‚
โ”‚         โ”‚  โ”‚            โ”‚    โ”‚     โ”€โ”€โ”€โ”€โ”€     โ”‚                      โ”‚                   โ”‚    โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ”‚ โ”Œโ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ–ผโ”€โ”€โ” โ”‚    /     \    โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”ดโ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ–ผโ”€โ”€โ” โ”‚ โ”‚         โ”‚
โ”‚         โ”‚โ—„โ”€โ”ผโ”€โ”คnatโ”‚โ—„โ”€โ”คmangleโ”‚โ—„โ”œโ”€โ”€โ”€<reroute>โ”€โ”€โ”€โ”ผโ”€โ”คfilterโ”‚โ—„โ”€โ”คnatโ”‚โ—„โ”€โ”คmangleโ”‚โ—„โ”€โ”คconntrackโ”‚โ—„โ”€โ”คrawโ”‚ โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ”‚ โ””โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚    \check/    โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”ฌโ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”˜ โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ”‚                 โ”‚     โ”€โ”€โ”€โ”€โ”€     โ”‚            โ”‚                                  โ”‚ โ”‚         โ”‚
โ”‚         โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚         โ”‚
โ”‚         โ”‚     POSTROUTING                                 โ”‚      OUTPUT                        โ”‚         โ”‚
โ”‚         โ”‚                                          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”                                 โ”‚         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                          โ”‚clash_dnsโ”‚                                 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                                     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

 *rule1: iptables -t nat -I PREROUTING -p udp --dport 53 -j REDIRECT --to 1053
 *rule2: iptables -t mangle -A OUTPUT -p tcp -m owner --uid-owner clash -j RETURN
 *rule3: iptables -t mangle -A OUTPUT -p udp -m owner --uid-owner clash -j RETURN
```
 
It's also worth to take a look at the route rules in `iptables.sh`:
 
```sh
ip rule add fwmark 666 table 666
ip route add local 0.0.0.0/0 table 666 dev lo
```

`ip rule` manipulates rules in the routing policy database control the route selection algorithm.

So here the `ip rule` command make the kernel to lookup table 666 for packets with fwmark 666

[ip rule reference is here.](http://linux-ip.net/html/tools-ip-rule.html)

The `ip route` command add a default route to device lo on routing table 666.

We can use `ip route show table 666` to see the route added by `ip route add local 0.0.0.0/0 table 666 dev lo
`

See the route table value and name mapping:
`cat /etc/iproute2/rt_tables`



 ### Create a cleaning script

Create a shell script to clean the iptables.

`-D` or `--delete`:

Delete one or more rules from the selected chain.
There are two versions of this command:
the rule can be specified as a number in the chain (starting at 1 for the first rule) or a rule to match.

`-F` or `--flush`:

Flush the selected chain (all the chains in the table if none is given).  
This is equivalent to deleting all the rules one by one.

`-X` or `--delete-chain`:

Delete the optional user-defined chain specified.
There must be no references to the chain.
If there are, you must delete or replace the referring rules before the chain can be deleted.
The chain must be empty, i.e. not contain any rules.
If no argument is given, it will attempt to delete every non-builtin chain in the table.

```sh
#!/usr/bin/env bash

set -ex

ip rule del fwmark 666 table 666 || true
ip route del local 0.0.0.0/0 table 666 dev lo || true

iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to 1053 || true
ip6tables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to 1053 || true
iptables -t nat -D OUTPUT -p udp --dport 53 -j clash_dns || true
ip6tables -t nat -D OUTPUT -p udp --dport 53 -j clash_dns || true
iptables -t nat -F clash_dns || true
iptables -t nat -X clash_dns || true
ip6tables -t nat -F clash_dns || true
ip6tables -t nat -X clash_dns || true
#iptables -t nat -D PREROUTING -p icmp -d 198.18.0.0/16 -j DNAT --to-destination 127.0.0.1
#iptables -t nat -D OUTPUT -p icmp -d 198.18.0.0/16 -j DNAT --to-destination 127.0.0.1

iptables -t mangle -D PREROUTING -j clash || true
iptables -t mangle -F clash || true
iptables -t mangle -X clash || true

iptables -t mangle -D OUTPUT -p tcp -m owner --uid-owner clash -j RETURN || true
iptables -t mangle -D OUTPUT -p udp -m owner --uid-owner clash -j RETURN || true
iptables -t mangle -D OUTPUT -j clash_local || true
iptables -t mangle -F clash_local || true
iptables -t mangle -X clash_local || true
```

### Fixing ping

One major drawback of the fake-ip mode is that you can't ping a fake ip.

The solution above to redirect all ping request to the localhost is "ๆŽฉ่€ณ็›—้“ƒ", it doesn't provide any useful information.

Sadly, all devices from LAN can't use ping, but fortunately you can alias ping to `sudo -u clash ping` for this gateway to use ping.

```sh
alias ping="sudo -u clash ping"
```

### Provide a systemd unit file to start clash at a single command

This step is optional, but I felt it's extremely convenient to start clash by running a single command.

You can also make clash start at boot time.

The systemd config file is in the appended files with the name `clash.service`

put it under `/usr/lib/systemd/system`, and run `sudo systemctl start clash` to start running clash.

To start it at boot time, run `sudo systemctl enable clash`.

### Configuring clash

Now let's set clash up to work.

Clash use `-d` option to set configuration directory and `-f` option to specify configuration file.

The default configuration file name is `config.yaml` when `-f` is omitted.

We want to use `crontab` to periodically update the clash config from a subscription url.

#### Construct the subscription URL

TL;DR. Just replace `XXXXXXXX` with your [encoded](https://www.urlencoder.io/) subscription URL .

```
https://sub.xeton.dev/sub?target=clash&new_name=true&filename=config.yaml&url=XXXXXXXX&config=https%3A%2F%2Fgist.githubusercontent.com%2Fcld4h%2F9a03ec2f826a25be5ab974fdbc540de4%2Fraw%2Fconfig.ini
```

We use [subconverter](https://github.com/tindy2013/subconverter) to generate config file for clash.

You need to change a few parameters in the subscription URL 
to generate a proper config file for clash to work in fake-ip mode.

To be specific:

1. the `url` parameter should be the correct subscription URL and [encoded](https://www.urlencoder.io/).
2. Multiple subscription URL should be seperated by `|`, or `%7C` after [URL Encoding](https://www.urlencoder.io/).
3. the `config` parameter should point to [a correct configuration file](https://gist.githubusercontent.com/cld4h/9a03ec2f826a25be5ab974fdbc540de4/raw/config.ini). โš ๏ธ

โš ๏ธ NOTICE: the configuration file for subconverter is extremely important!

[ACL4SSR project provide a default configuration file for subconverter](https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini
), it's fine but NOT ENOUGH for clash to work on fake-ip mode.

To make it work, you need specify `clash_rule_base` by inserting this line to [the configuration file for **subconverter**](https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini)

```
clash_rule_base=https://gist.githubusercontent.com/cld4h/9a03ec2f826a25be5ab974fdbc540de4/raw/clash-base-config.yaml
```

What this line does is that it tells the subconverter to use [the file in the url above](https://gist.githubusercontent.com/cld4h/9a03ec2f826a25be5ab974fdbc540de4/raw/clash-base-config.yaml) instead of the default clash base configuration.

Or you can just use [the config.ini here](https://gist.githubusercontent.com/cld4h/9a03ec2f826a25be5ab974fdbc540de4/raw/config.ini)

Sample url:

```txt
https://sub.xeton.dev/sub?        --- backend address
target=clash                      --- client type
&new_name=true                    --- create a new filename
&filename=config.yaml             --- filename
&url=                             --- Node info
ss%3A%2F%2FYmYtY2ZiOnRlc3QvIUAjOkAxOTIuMTY4LjEwMC4xOjg4ODg%23example-server
%7C                               --- the delimiter | to seperate multiple urls
ss%3A%2F%2FYmYtY2ZiOnRlc3QvIUAjOkAxOTIuMTY4LjEwMC4xOjg4ODg%23example-server-2
&config=                          --- config file for subconverter; .ini format
https%3A%2F%2Fgist.githubusercontent.com%2Fcld4h%2F9a03ec2f826a25be5ab974fdbc540de4%2Fraw%2Fconfig.ini
                                  --- options below is unnecessary and can be omitted
&list=false                       --- ็”จไบŽ่พ“ๅ‡บ Surge Node List ๆˆ–่€… Clash Proxy Provider ๆˆ–่€… Quantumult (X) ็š„่Š‚็‚น่ฎข้˜… ๆˆ–่€… ่งฃ็ ๅŽ็š„ SIP002
&tfo=false                        --- ็”จไบŽๅผ€ๅฏ่ฏฅ่ฎข้˜…้“พๆŽฅ็š„ TCP Fast Open๏ผŒ้ป˜่ฎคไธบ false
&scv=false                        --- ็”จไบŽๅ…ณ้—ญ TLS ่Š‚็‚น็š„่ฏไนฆๆฃ€ๆŸฅ๏ผŒ้ป˜่ฎคไธบ false
&fdn=false                        --- ็”จไบŽ่ฟ‡ๆปค็›ฎๆ ‡็ฑปๅž‹ไธๆ”ฏๆŒ็š„่Š‚็‚น๏ผŒ้ป˜่ฎคไธบ true
&sort=false                       --- ็”จไบŽๅฏน่พ“ๅ‡บ็š„่Š‚็‚นๆˆ–็ญ–็•ฅ็ป„ๆŒ‰่Š‚็‚นๅ่ฟ›่กŒๅ†ๆฌกๆŽ’ๅบ๏ผŒ้ป˜่ฎคไธบ false
```
#### Set up a cronjob

Create a shell script `/etc/clash/update-config.sh` with the following content:

```sh
#!/usr/bin/env bash
set -ex
/usr/bin/curl "https://sub.xeton.dev/sub?target=clash&new_name=true&filename=config.yaml&url=XXXXXXXX&config=https%3A%2F%2Fgist.githubusercontent.com%2Fcld4h%2F9a03ec2f826a25be5ab974fdbc540de4%2Fraw%2Fconfig.ini" -o /etc/clash/config.yaml
```

โš ๏ธ The URL should be replace.

Manually executing it to make sure the config file is generated successfully.

Make sure you have the following contents in your `config.yaml` file for clash:

```yaml
tproxy-port: 7893

dns:
  enable: true
  listen: 0.0.0.0:1053
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
```

Add a [cronjob](https://crontab.guru) by executing `sudo crontab -e` and add this line to executing the script at 10:00 AM every day.

```sh
0 10 * * * /usr/bin/bash /etc/clash/update-config.sh
```