class NSMain: def __init__(self): self.nat = NSNat(config.global_config["ignored_ip_address"], config.global_config["wan_interfaces"]) self.auto_switcher = NSAutoSwitcher(config.profiles) self.iptables = NSIptables() self.signaled = False async def start(self): await self.nat.start() await self.auto_switcher.start() for custom_iptables_rule in config.global_config["custom_iptables_rules"]: self.iptables.add_rule(custom_iptables_rule.get("table"), custom_iptables_rule.get("chain"), custom_iptables_rule.get("rule"), custom_iptables_rule.get("rule_num") or -1) def stop(self): if self.signaled: utils.log("Duplicate signals, ignoring") return utils.log("Signaled, stopping") self.signaled = True self.iptables.del_all() self.auto_switcher.stop() self.nat.stop()
def __init__(self, ignored_ip_address, wan_interfaces): self.wan_interfaces = wan_interfaces self.ignored_ip_address = ignored_ip_address self.watchers = [] self.iptables = NSIptables()
class NSNat: def __init__(self, ignored_ip_address, wan_interfaces): self.wan_interfaces = wan_interfaces self.ignored_ip_address = ignored_ip_address self.watchers = [] self.iptables = NSIptables() def _on_ip_change(self, old_ip, new_ip): utils.log((old_ip, new_ip)) if old_ip: self.iptables.del_rule( "nat", "PREROUTING", "-d %s -m socket --nowildcard -j ACCEPT" % old_ip) self.iptables.del_rule( "nat", "PREROUTING", "-d %s -j DNAT --to-destination %s" % (old_ip, config.global_config["dmz_host"])) if new_ip: self.iptables.add_rule( "nat", "PREROUTING", "-d %s -m socket --nowildcard -j ACCEPT" % new_ip) self.iptables.add_rule( "nat", "PREROUTING", "-d %s -j DNAT --to-destination %s" % (new_ip, config.global_config["dmz_host"])) async def start(self): self.iptables.add_rule( "nat", "POSTROUTING", "-d %s -j SNAT --to-source %s" % (config.global_config["dmz_host"], config.global_config["dmz_host_source"])) self.iptables.add_rule( "nat", "POSTROUTING", "-s %s -j MASQUERADE" % config.global_config["lan_hosts"]) self.iptables.add_rule( "mangle", "POSTROUTING", "-p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu" ) for wan_interface in self.wan_interfaces: watcher = NSIPWatcher(wan_interface, self.ignored_ip_address, self._on_ip_change) await watcher.start() self.watchers.append(watcher) def stop(self): utils.log() for watcher in self.watchers: watcher.stop() self.iptables.del_all()
def __init__(self, route, local_routes, extra_routes): self.route = route self.local_routes = local_routes self.extra_routes = extra_routes self.iptables = NSIptables()
class NSTransproxy: def __init__(self, route, local_routes, extra_routes): self.route = route self.local_routes = local_routes self.extra_routes = extra_routes self.iptables = NSIptables() async def exec_start(self): raise NotImplementedError() async def start(self): fwmark = config.global_config["transproxy"]["fwmark"] self.iptables.add_chain("mangle", "TRANSPROXY_MARK") # Mark packets to extra_routes for extra_cidr in self.extra_routes or []: self.iptables.add_rule( "mangle", "TRANSPROXY_MARK", "-d %s -j MARK --set-mark %d" % (extra_cidr, fwmark)) # Skip private CIDRs that isn't in extra_routes self.iptables.add_rule( "mangle", "TRANSPROXY_MARK", "-m set --match-set network-scripts-private-cidr dst -j RETURN") # Skip non-private local CIDRs for local_cidr in (self.local_routes or []) + ( config.global_config["transproxy"]["local_routes"] or []): self.iptables.add_rule("mangle", "TRANSPROXY_MARK", "-d %s -j RETURN" % local_cidr) # bypass-mainland-china: Mark all packets to non-Mainland China CIDRs # only-mainland-china: Mark all packets to Mainland China CIDRs # all: Mark all non-RETURNed packets if self.route == "bypass-mainland-china": self.iptables.add_rule( "mangle", "TRANSPROXY_MARK", "-m set ! --match-set network-scripts-mainland-china dst -j MARK --set-mark %d" % fwmark) elif self.route == "only-mainland-china": self.iptables.add_rule( "mangle", "TRANSPROXY_MARK", "-m set --match-set network-scripts-mainland-china dst -j MARK --set-mark %d" % fwmark) elif self.route == "all": self.iptables.add_rule("mangle", "TRANSPROXY_MARK", "-j MARK --set-mark %d" % fwmark) await self.exec_start() def exec_stop(self): raise NotImplementedError() def stop(self): self.exec_stop() self.iptables.del_all()
class NSShadowsocksTransproxy(NSTransproxy): def __init__(self, route, local_routes, extra_ruotes, config): super().__init__(route, local_routes, extra_ruotes) self.config = config self.iptables = NSIptables() self.stopped = False self.process = None async def _run_ss_redir(self): utils.log() run_group = config.global_config["transproxy"]["shadowsocks"][ "run_group"] run_gid = run_group if type(run_group) == int else grp.getgrnam( run_group).gr_gid local_port = config.global_config["transproxy"]["shadowsocks"][ "local_port"] server = self.config["server"] server_port = self.config["port"] password = self.config["password"] method = self.config["method"] ss_redir_command = "ss-redir -s %s -k %s -p %d -b 0.0.0.0 -l %d -m %s -u --no-delay" \ % (repr(server), repr(password), server_port, local_port, repr(method)) while True: self.process = await asyncio.create_subprocess_shell( ss_redir_command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT, preexec_fn=lambda: (os.setgid(run_gid), os.setsid())) if self.stopped: self.kill() utils.log("self.process.pid = %d" % self.process.pid) async for line in self.process.stdout: utils.log("ss-redir: %s" % line.decode("utf-8").strip()) utils.log("ss-redir exited with %d" % await self.process.wait()) if self.stopped: break await asyncio.sleep(config.global_config["transproxy"] ["shadowsocks"]["restart_interval"]) if self.stopped: break utils.log("Stopped") async def exec_start(self): fwmark = config.global_config["transproxy"]["fwmark"] ip_route_table = config.global_config["transproxy"]["ip_route_table"] run_group = config.global_config["transproxy"]["shadowsocks"][ "run_group"] local_port = config.global_config["transproxy"]["shadowsocks"][ "local_port"] # TCP self.iptables.add_rule( "nat", "PREROUTING", "-p tcp -m mark --mark %d -j REDIRECT --to-ports %d" % (fwmark, local_port)) self.iptables.add_rule( "nat", "OUTPUT", "-p tcp -m mark --mark %d -j REDIRECT --to-ports %d" % (fwmark, local_port)) # UDP self.iptables.add_rule( "mangle", "PREROUTING", "-p udp -m mark --mark %d -j TPROXY --on-port %d --tproxy-mark %d/%d" % (fwmark, local_port, fwmark, fwmark)) utils.system("ip route add local default dev lo table %s" % ip_route_table) utils.system("ip rule add fwmark %s lookup %s" % (fwmark, ip_route_table)) asyncio.create_task(self._run_ss_redir()) # Active the rules self.iptables.add_rule( "mangle", "OUTPUT", "-m owner ! --gid-owner %s -j TRANSPROXY_MARK" % run_group) self.iptables.add_rule("mangle", "PREROUTING", "-j TRANSPROXY_MARK", 1) def kill(self): try: self.process.kill() except ProcessLookupError as e: utils.log("Error killing process: " + str(e)) def exec_stop(self): fwmark = config.global_config["transproxy"]["fwmark"] ip_route_table = config.global_config["transproxy"]["ip_route_table"] self.iptables.del_all() utils.system("ip route del local default dev lo table %s" % ip_route_table) utils.system("ip rule del fwmark %s lookup %s" % (fwmark, ip_route_table)) self.stopped = True if not self.process: utils.log( "Not killing ss-redir process, since self.process is None") elif type(self.process.returncode) != int: utils.log("Killing ss-redir process") self.kill() else: utils.log( "Not killing ss-redir process, since self.process.returncode is int" )
def __init__(self, route, local_routes, extra_ruotes, config): super().__init__(route, local_routes, extra_ruotes) self.config = config self.iptables = NSIptables() self.stopped = False self.process = None
class NSWireGuardTransproxy(NSTransproxy): def __init__(self, route, local_routes, extra_ruotes, config): super().__init__(route, local_routes, extra_ruotes) self.config = config self.iptables = NSIptables() self.stopped = False self.started = False async def _try_run(self): utils.log() endpoint_host = self.config["endpoint_host"] endpoint_port = self.config["endpoint_port"] local_address = self.config["local_address"] private_key = self.config["private_key"] remote_public_key = self.config["remote_public_key"] fwmark = config.global_config["transproxy"]["fwmark"] ip_route_table = config.global_config["transproxy"]["ip_route_table"] interface_name = config.global_config["transproxy"]["wireguard"][ "interface_name"] resolve_retry_interval = config.global_config["transproxy"][ "wireguard"]["resolve_retry_interval"] endpoint_ip = None while not endpoint_ip: try: endpoint_ip = socket.gethostbyname(endpoint_host) except BaseException as e: utils.log("Failed to resolve %s: %s" % (endpoint_host, e)) if self.stopped: break await asyncio.sleep(resolve_retry_interval) if self.stopped: utils.log("Cancelled before add WireGuard interface.") return if endpoint_ip != endpoint_host: utils.log("Resolved endpoint host %s to %s" % (endpoint_host, endpoint_ip)) # Start WireGuard utils.system("ip link add dev %s type wireguard" % interface_name) utils.system("ip address add dev %s %s" % (interface_name, local_address)) utils.system( "wg set %s private-key <(echo %s) peer %s allowed-ips 0.0.0.0/0 endpoint %s:%d" % (interface_name, repr(private_key), repr(remote_public_key), endpoint_ip, endpoint_port)) utils.system("ip link set up dev %s" % interface_name) # Set ip route and ip rule utils.system("ip route add table %d default dev %s" % (ip_route_table, interface_name)) utils.system("ip rule add fwmark %d table %d" % (fwmark, ip_route_table)) self.iptables.add_rule( "mangle", "TRANSPROXY_MARK", "-d %s -p udp --dport %d -j RETURN" % (endpoint_ip, endpoint_port), 1) self.iptables.add_rule("mangle", "TRANSPROXY_MARK", "-i wireguard -j RETURN", 1) self.iptables.add_rule("nat", "POSTROUTING", "-o wireguard -j MASQUERADE") self.iptables.add_rule("mangle", "OUTPUT", "-j TRANSPROXY_MARK") self.iptables.add_rule("mangle", "PREROUTING", "-j TRANSPROXY_MARK") self.started = True async def exec_start(self): asyncio.create_task(self._try_run()) def exec_stop(self): self.stopped = True if self.started: fwmark = config.global_config["transproxy"]["fwmark"] ip_route_table = config.global_config["transproxy"][ "ip_route_table"] interface_name = config.global_config["transproxy"]["wireguard"][ "interface_name"] self.iptables.del_all() utils.system("ip route del table %d default dev %s" % (ip_route_table, interface_name)) utils.system("ip rule del fwmark %d table %d" % (fwmark, ip_route_table)) utils.system("ip link delete %s" % interface_name)
def __init__(self): self.nat = NSNat(config.global_config["ignored_ip_address"], config.global_config["wan_interfaces"]) self.auto_switcher = NSAutoSwitcher(config.profiles) self.iptables = NSIptables() self.signaled = False