Esempio n. 1
0
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()
Esempio n. 2
0
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()
Esempio n. 3
0
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()
Esempio n. 4
0
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"
            )
Esempio n. 5
0
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)