#!/usr/sbin/nft -f

table inet mihomo {
	set bypass_user {
		type uid
		flags interval
		auto-merge
	}

	set bypass_group {
		type gid
		flags interval
		auto-merge
		elements = {
			$MIHOMO_GROUP
		}
	}

	set bypass_dscp {
		type dscp
		flags interval
	}

	set dns_hijack_nfproto {
		type nf_proto
		flags interval
	}

	set proxy_nfproto {
		type nf_proto
		flags interval
	}

	set china_ip {
		type ipv4_addr
		flags interval
	}

	set china_ip6 {
		type ipv6_addr
		flags interval
	}

	set reserved_ip {
		type ipv4_addr
		flags interval
		auto-merge
	}

	set reserved_ip6 {
		type ipv6_addr
		flags interval
		auto-merge
	}

	set proxy_dport {
		type inet_proto . inet_service
		flags interval
		auto-merge
	}

	set acl_ip {
		type ipv4_addr
		flags interval
		auto-merge
	}

	set acl_ip6 {
		type ipv6_addr
		flags interval
		auto-merge
	}

	set acl_mac {
		type ether_addr
		flags interval
		auto-merge
	}

	set acl_interface {
		type ifname
		flags interval
		auto-merge
	}

	chain router_dns_hijack {
		meta skuid @bypass_user counter return
		meta skgid @bypass_group counter return
		meta nfproto @dns_hijack_nfproto meta l4proto udp th dport 53 counter redirect to :$DNS_PORT
	}

	chain all_dns_hijack {
		meta nfproto @dns_hijack_nfproto meta l4proto udp th dport 53 counter redirect to :$DNS_PORT
	}

	chain allow_dns_hijack {
		meta nfproto @dns_hijack_nfproto meta l4proto udp th dport 53 ip saddr @acl_ip counter redirect to :$DNS_PORT
		meta nfproto @dns_hijack_nfproto meta l4proto udp th dport 53 ip6 saddr @acl_ip6 counter redirect to :$DNS_PORT
		meta nfproto @dns_hijack_nfproto meta l4proto udp th dport 53 ether saddr @acl_mac counter redirect to :$DNS_PORT
		meta nfproto @dns_hijack_nfproto meta l4proto udp th dport 53 iifname @acl_interface counter redirect to :$DNS_PORT
	}

	chain block_dns_hijack {
		meta nfproto @dns_hijack_nfproto meta l4proto udp th dport 53 ip saddr @acl_ip counter return
		meta nfproto @dns_hijack_nfproto meta l4proto udp th dport 53 ip6 saddr @acl_ip6 counter return
		meta nfproto @dns_hijack_nfproto meta l4proto udp th dport 53 ether saddr @acl_mac counter return
		meta nfproto @dns_hijack_nfproto meta l4proto udp th dport 53 iifname @acl_interface counter return
		meta nfproto @dns_hijack_nfproto meta l4proto udp th dport 53 counter redirect to :$DNS_PORT
	}

	chain router_redirect {
		meta nfproto @proxy_nfproto meta l4proto tcp counter redirect to :$REDIR_PORT
	}

	chain all_redirect {
		meta nfproto @proxy_nfproto meta l4proto tcp counter redirect to :$REDIR_PORT
	}

	chain allow_redirect {
		meta nfproto @proxy_nfproto meta l4proto tcp ip saddr @acl_ip counter redirect to :$REDIR_PORT
		meta nfproto @proxy_nfproto meta l4proto tcp ip6 saddr @acl_ip6 counter redirect to :$REDIR_PORT
		meta nfproto @proxy_nfproto meta l4proto tcp ether saddr @acl_mac counter redirect to :$REDIR_PORT
		meta nfproto @proxy_nfproto meta l4proto tcp iifname @acl_interface counter redirect to :$REDIR_PORT
	}

	chain block_redirect {
		meta nfproto @proxy_nfproto meta l4proto tcp ip saddr @acl_ip counter return
		meta nfproto @proxy_nfproto meta l4proto tcp ip6 saddr @acl_ip6 counter return
		meta nfproto @proxy_nfproto meta l4proto tcp ether saddr @acl_mac counter return
		meta nfproto @proxy_nfproto meta l4proto tcp iifname @acl_interface counter return
		meta nfproto @proxy_nfproto meta l4proto tcp counter redirect to :$REDIR_PORT
	}

	chain router_tproxy {
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta mark set mark ^ $FW_MARK counter
	}

	chain all_tproxy {
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta mark set mark ^ $FW_MARK tproxy to :$TPROXY_PORT counter accept
	}

	chain allow_tproxy {
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip saddr @acl_ip meta mark set mark ^ $FW_MARK tproxy ip to :$TPROXY_PORT counter accept
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip6 saddr @acl_ip6 meta mark set mark ^ $FW_MARK tproxy ip6 to :$TPROXY_PORT counter accept
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ether saddr @acl_mac meta mark set mark ^ $FW_MARK tproxy to :$TPROXY_PORT counter accept
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } iifname @acl_interface meta mark set mark ^ $FW_MARK tproxy to :$TPROXY_PORT counter accept
	}

	chain block_tproxy {
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip saddr @acl_ip counter return
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip6 saddr @acl_ip6 counter return
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ether saddr @acl_mac counter return
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } iifname @acl_interface counter return
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta mark set mark ^ $FW_MARK tproxy to :$TPROXY_PORT counter accept
	}

	chain router_tun {
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta mark set mark ^ $FW_MARK counter
	}

	chain all_tun {
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta mark set mark ^ $FW_MARK counter
	}

	chain allow_tun {
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip saddr @acl_ip meta mark set mark ^ $FW_MARK counter
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip6 saddr @acl_ip6 meta mark set mark ^ $FW_MARK counter
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ether saddr @acl_mac meta mark set mark ^ $FW_MARK counter
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } iifname @acl_interface meta mark set mark ^ $FW_MARK counter
	}

	chain block_tun {
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip saddr @acl_ip counter return
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ip6 saddr @acl_ip6 counter return
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } ether saddr @acl_mac counter return
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } iifname @acl_interface counter return
		meta nfproto @proxy_nfproto meta l4proto { tcp, udp } meta mark set mark ^ $FW_MARK counter
	}

	chain dstnat {
		type nat hook prerouting priority dstnat + 1; policy accept;
		fib daddr type { local, multicast, broadcast, anycast } counter return
		ct direction reply counter return
		ip daddr @reserved_ip counter return
		ip6 daddr @reserved_ip6 counter return
		ip daddr @china_ip counter return
		ip6 daddr @china_ip6 counter return
		meta nfproto ipv4 meta l4proto . th dport != @proxy_dport ip daddr != $FAKE_IP counter return
		meta nfproto ipv6 meta l4proto . th dport != @proxy_dport counter return
		meta l4proto { tcp, udp } ip dscp == @bypass_dscp ip daddr != $FAKE_IP counter return
		meta l4proto { tcp, udp } ip6 dscp == @bypass_dscp counter return
	}

	chain nat_output {
		type nat hook output priority filter; policy accept;
		meta skuid @bypass_user counter return
		meta skgid @bypass_group counter return
		fib daddr type { local, multicast, broadcast, anycast } counter return
		ct direction reply counter return
		ip daddr @reserved_ip counter return
		ip6 daddr @reserved_ip6 counter return
		ip daddr @china_ip counter return
		ip6 daddr @china_ip6 counter return
		meta nfproto ipv4 meta l4proto . th dport != @proxy_dport ip daddr != $FAKE_IP counter return
		meta nfproto ipv6 meta l4proto . th dport != @proxy_dport counter return
		meta l4proto { tcp, udp } ip dscp == @bypass_dscp ip daddr != $FAKE_IP counter return
		meta l4proto { tcp, udp } ip6 dscp == @bypass_dscp counter return
	}

	chain mangle_prerouting {
		type filter hook prerouting priority mangle; policy accept;
		meta l4proto { tcp, udp } iifname lo meta mark & $FW_MARK_MASK == $FW_MARK tproxy to :$TPROXY_PORT counter accept
		meta l4proto { tcp, udp } iifname $TUN_DEVICE counter accept
		fib daddr type { local, multicast, broadcast, anycast } counter return
		ct direction reply counter return
		ip daddr @reserved_ip counter return
		ip6 daddr @reserved_ip6 counter return
		ip daddr @china_ip counter return
		ip6 daddr @china_ip6 counter return
		meta nfproto ipv4 meta l4proto . th dport != @proxy_dport ip daddr != $FAKE_IP counter return
		meta nfproto ipv6 meta l4proto . th dport != @proxy_dport counter return
		meta l4proto { tcp, udp } ip dscp == @bypass_dscp ip daddr != $FAKE_IP counter return
		meta l4proto { tcp, udp } ip6 dscp == @bypass_dscp counter return
		meta nfproto @dns_hijack_nfproto udp dport 53 counter return
	}

	chain mangle_output {
		type route hook output priority mangle; policy accept;
		meta skuid @bypass_user counter return
		meta skgid @bypass_group counter return
		fib daddr type { local, multicast, broadcast, anycast } counter return
		ct direction reply counter return
		ip daddr @reserved_ip counter return
		ip6 daddr @reserved_ip6 counter return
		ip daddr @china_ip counter return
		ip6 daddr @china_ip6 counter return
		meta nfproto ipv4 meta l4proto . th dport != @proxy_dport ip daddr != $FAKE_IP counter return
		meta nfproto ipv6 meta l4proto . th dport != @proxy_dport counter return
		meta l4proto { tcp, udp } ip dscp == @bypass_dscp ip daddr != $FAKE_IP counter return
		meta l4proto { tcp, udp } ip6 dscp == @bypass_dscp counter return
		meta nfproto @dns_hijack_nfproto udp dport 53 counter return
	}
}
