Merge branch 'master' into nat-ipv6

This commit is contained in:
Scott Kuhl 2021-05-30 21:09:32 -04:00
commit 3f201095ea
19 changed files with 196 additions and 233 deletions

View File

@ -50,6 +50,14 @@ if [ "$FILE_NAME" == "" ]; then
exit 1 exit 1
fi fi
# Verify that the resulting file name begins with /etc/sudoers.d
FILE_NAME="$(realpath "/etc/sudoers.d/$FILE_NAME")"
if [[ "$FILE_NAME" != "/etc/sudoers.d/"* ]] ; then
echo -n "Invalid sudoers filename: Final sudoers file "
echo "location ($FILE_NAME) does not begin with /etc/sudoers.d"
exit 1
fi
# Make a temp file to hold the sudoers config # Make a temp file to hold the sudoers config
umask 077 umask 077
TEMP_FILE=$(mktemp) TEMP_FILE=$(mktemp)
@ -62,9 +70,9 @@ visudo_code=$?
rm "$TEMP_FILE" rm "$TEMP_FILE"
if [ $visudo_code -eq 0 ]; then if [ $visudo_code -eq 0 ]; then
echo "$CONTENT" > "/etc/sudoers.d/$FILE_NAME" echo "$CONTENT" > "$FILE_NAME"
chmod 0440 "/etc/sudoers.d/$FILE_NAME" chmod 0440 "$FILE_NAME"
echo "The sudoers file /etc/sudoers.d/$FILE_NAME has been successfully created!" echo "The sudoers file $FILE_NAME has been successfully created!"
exit 0 exit 0
else else

View File

@ -10,8 +10,8 @@ Synopsis
Description Description
----------- -----------
:program:`sshuttle` allows you to create a VPN connection from your :program:`sshuttle` allows you to create a VPN connection from your
machine to any remote server that you can connect to via machine to any remote server that you can connect to via ssh, as long
ssh, as long as that server has python 3.6 or higher. as that server has a sufficiently new Python installation.
To work, you must have root access on the local machine, To work, you must have root access on the local machine,
but you can have a normal account on the server. but you can have a normal account on the server.
@ -31,22 +31,23 @@ Options
.. option:: <subnets> .. option:: <subnets>
A list of subnets to route over the VPN, in the form A list of subnets to route over the VPN, in the form
``a.b.c.d[/width][port[-port]]``. Valid examples are 1.2.3.4 (a ``a.b.c.d[/width][port[-port]]``. Valid examples are 1.2.3.4 (a
single IP address), 1.2.3.4/32 (equivalent to 1.2.3.4), single IP address) and 1.2.3.4/32 (equivalent to 1.2.3.4),
1.2.3.0/24 (a 24-bit subnet, ie. with a 255.255.255.0 1.2.3.0/24 (a 24-bit subnet, ie. with a 255.255.255.0 netmask).
netmask), and 0/0 ('just route everything through the Specify subnets 0/0 to match all IPv4 addresses and ::/0 to match
VPN'). Any of the previous examples are also valid if you append all IPv6 addresses. Any of the previous examples are also valid if
a port or a port range, so 1.2.3.4:8000 will only tunnel traffic you append a port or a port range, so 1.2.3.4:8000 will only
that has as the destination port 8000 of 1.2.3.4 and tunnel traffic that has as the destination port 8000 of 1.2.3.4
1.2.3.0/24:8000-9000 will tunnel traffic going to any port between and 1.2.3.0/24:8000-9000 will tunnel traffic going to any port
8000 and 9000 (inclusive) for all IPs in the 1.2.3.0/24 subnet. between 8000 and 9000 (inclusive) for all IPs in the 1.2.3.0/24
A hostname can be provided instead of an IP address. If the subnet. A hostname can be provided instead of an IP address. If
hostname resolves to multiple IPs, all of the IPs are included. the hostname resolves to multiple IPs, all of the IPs are
If a width is provided with a hostname that the width is applied included. If a width is provided with a hostname, the width is
to all of the hostnames IPs (if they are all either IPv4 or IPv6). applied to all of the hostnames IPs (if they are all either IPv4
Widths cannot be supplied to hostnames that resolve to both IPv4 or IPv6). Widths cannot be supplied to hostnames that resolve to
and IPv6. Valid examples are example.com, example.com:8000, both IPv4 and IPv6. Valid examples are example.com,
example.com/24, example.com/24:8000 and example.com:8000-9000. example.com:8000, example.com/24, example.com/24:8000 and
example.com:8000-9000.
.. option:: --method <auto|nat|nft|tproxy|pf|ipfw> .. option:: --method <auto|nat|nft|tproxy|pf|ipfw>
@ -141,7 +142,10 @@ Options
The remote hostname and optional username and ssh The remote hostname and optional username and ssh
port number to use for connecting to the remote server. port number to use for connecting to the remote server.
For example, example.com, testuser@example.com, For example, example.com, testuser@example.com,
testuser@example.com:2222, or example.com:2244. testuser@example.com:2222, or example.com:2244. This
hostname is passed to ssh, so it will recognize any
aliases and settings you may have configured in
~/.ssh/config.
.. option:: -x <subnet>, --exclude=<subnet> .. option:: -x <subnet>, --exclude=<subnet>
@ -274,9 +278,10 @@ Options
Set the file name for the sudoers.d file to be added. Default is Set the file name for the sudoers.d file to be added. Default is
"sshuttle_auto". Only works with --sudoers. "sshuttle_auto". Only works with --sudoers.
.. option:: -t, --tmark .. option:: -t <mark>, --tmark=<mark>
Transproxy optional traffic mark with provided MARK value. An option used by the tproxy method: Use the specified traffic
mark. The mark must be a hexadecimal value. Defaults to 0x01.
.. option:: --version .. option:: --version
@ -305,11 +310,8 @@ Arguments read from a file must be one per line, as shown below::
--option2 --option2
value2 value2
Comments in config file The configuration file supports comments for human-readable
....................... annotations. For example::
It's possible to add comments in the configuration file. This allows annotating the
various subnets with human-readable descriptions, like::
# company-internal API # company-internal API
8.8.8.8/32 8.8.8.8/32
@ -319,51 +321,96 @@ various subnets with human-readable descriptions, like::
Examples Examples
-------- --------
Test locally by proxying all local connections, without using ssh::
$ sshuttle -v 0/0 Use the following command to route all IPv4 TCP traffic through remote
(-r) host example.com (and possibly other traffic too, depending on
the selected --method). The 0/0 subnet, short for 0.0.0.0/0, matches
all IPv4 addresses. The ::/0 subnet, matching all IPv6 addresses could
be added to the example. We also exclude (-x) example.com:22 so that
we can establish ssh connections from our local machine to the remote
host without them being routed through sshuttle. Excluding the remote
host may be necessary on some machines for sshuttle to work properly.
Press Ctrl+C to exit. To also route DNS queries through sshuttle, try
adding --dns. Add or remove -v options to see more or less
information::
Starting sshuttle proxy. $ sshuttle -r example.com -x example.com:22 0/0
Listening on ('0.0.0.0', 12300).
[local sudo] Password: Starting sshuttle proxy (version ...).
firewall manager ready. [local sudo] Password:
c : connecting to server... fw: Starting firewall with Python version 3.9.5
s: available routes: fw: ready method name nat.
s: 192.168.42.0/24 c : IPv6 disabled since it isn't supported by method nat.
c : connected. c : Method: nat
firewall manager: starting transproxy. c : IPv4: on
c : Accept: 192.168.42.106:50035 -> 192.168.42.121:139. c : IPv6: off (not available with nat method)
c : Accept: 192.168.42.121:47523 -> 77.141.99.22:443. c : UDP : off (not available with nat method)
...etc... c : DNS : off (available)
c : User: off (available)
c : Subnets to forward through remote host (type, IP, cidr mask width, startPort, endPort):
c : (<AddressFamily.AF_INET: 2>, '0.0.0.0', 0, 0, 0)
c : Subnets to exclude from forwarding:
c : (<AddressFamily.AF_INET: 2>, '...', 32, 22, 22)
c : (<AddressFamily.AF_INET: 2>, '127.0.0.1', 32, 0, 0)
c : TCP redirector listening on ('127.0.0.1', 12299).
c : Starting client with Python version 3.9.5
c : Connecting to server...
user@example.com's password:
s: Starting server with Python version 3.6.8
s: latency control setting = True
s: auto-nets:False
c : Connected to server.
fw: setting up.
fw: iptables -w -t nat -N sshuttle-12299
fw: iptables -w -t nat -F sshuttle-12299
...
Accept: 192.168.42.121:60554 -> 77.141.99.22:22.
^C ^C
firewall manager: undoing changes.
KeyboardInterrupt
c : Keyboard interrupt: exiting. c : Keyboard interrupt: exiting.
c : SW#8:192.168.42.121:47523: deleting c : SW'unknown':Mux#1: deleting (1 remain)
c : SW#6:192.168.42.106:50035: deleting c : SW#7:192.168.42.121:60554: deleting (0 remain)
Test connection to a remote server, with automatic hostname
Connect to a remote server, with automatic hostname
and subnet guessing:: and subnet guessing::
$ sshuttle -vNHr example.org $ sshuttle -vNHr example.com -x example.com:22
Starting sshuttle proxy (version ...).
Starting sshuttle proxy. [local sudo] Password:
Listening on ('0.0.0.0', 12300). fw: Starting firewall with Python version 3.9.5
firewall manager ready. fw: ready method name nat.
c : connecting to server... c : IPv6 disabled since it isn't supported by method nat.
c : Method: nat
c : IPv4: on
c : IPv6: off (not available with nat method)
c : UDP : off (not available with nat method)
c : DNS : off (available)
c : User: off (available)
c : Subnets to forward through remote host (type, IP, cidr mask width, startPort, endPort):
c : NOTE: Additional subnets to forward may be added below by --auto-nets.
c : Subnets to exclude from forwarding:
c : (<AddressFamily.AF_INET: 2>, '...', 32, 22, 22)
c : (<AddressFamily.AF_INET: 2>, '127.0.0.1', 32, 0, 0)
c : TCP redirector listening on ('127.0.0.1', 12300).
c : Starting client with Python version 3.9.5
c : Connecting to server...
user@example.com's password:
s: Starting server with Python version 3.6.8
s: latency control setting = True
s: auto-nets:True
c : Connected to server.
c : seed_hosts: []
s: available routes: s: available routes:
s: 77.141.99.0/24 s: 77.141.99.0/24
c : connected. fw: setting up.
c : seed_hosts: [] fw: iptables -w -t nat -N sshuttle-12300
firewall manager: starting transproxy. fw: iptables -w -t nat -F sshuttle-12300
hostwatch: Found: testbox1: 1.2.3.4 ...
hostwatch: Found: mytest2: 5.6.7.8
hostwatch: Found: domaincontroller: 99.1.2.3
c : Accept: 192.168.42.121:60554 -> 77.141.99.22:22. c : Accept: 192.168.42.121:60554 -> 77.141.99.22:22.
^C ^C
firewall manager: undoing changes.
c : Keyboard interrupt: exiting. c : Keyboard interrupt: exiting.
c : SW#6:192.168.42.121:60554: deleting c : SW'unknown':Mux#1: deleting (1 remain)
c : SW#7:192.168.42.121:60554: deleting (0 remain)
Run :program:`sshuttle` with a `/etc/sshuttle.conf` configuration file:: Run :program:`sshuttle` with a `/etc/sshuttle.conf` configuration file::

View File

@ -12,7 +12,8 @@ There are some things you need to consider for TPROXY to work:
ip -6 route add local default dev lo table 100 ip -6 route add local default dev lo table 100
ip -6 rule add fwmark {TMARK} lookup 100 ip -6 rule add fwmark {TMARK} lookup 100
where {TMARK} is the identifier mark passed with -t or --tmark flag (default value is 1). where {TMARK} is the identifier mark passed with -t or --tmark flag
as a hexadecimal string (default value is '0x01').
- The ``--auto-nets`` feature does not detect IPv6 routes automatically. Add IPv6 - The ``--auto-nets`` feature does not detect IPv6 routes automatically. Add IPv6
routes manually. e.g. by adding ``'::/0'`` to the end of the command line. routes manually. e.g. by adding ``'::/0'`` to the end of the command line.

View File

@ -11,6 +11,10 @@ Forward all traffic::
sshuttle -r username@sshserver 0.0.0.0/0 sshuttle -r username@sshserver 0.0.0.0/0
- Use the :option:`sshuttle -r` parameter to specify a remote server. - Use the :option:`sshuttle -r` parameter to specify a remote server.
One some systems, you may also need to use the :option:`sshuttle -x`
parameter to exclude sshserver or sshserver:22 so that your local
machine can communicate directly to sshserver without it being
redirected by sshuttle.
- By default sshuttle will automatically choose a method to use. Override with - By default sshuttle will automatically choose a method to use. Override with
the :option:`sshuttle --method` parameter. the :option:`sshuttle --method` parameter.

View File

@ -201,12 +201,11 @@ class FirewallClient:
def __init__(self, method_name, sudo_pythonpath, ttl): def __init__(self, method_name, sudo_pythonpath, ttl):
self.auto_nets = [] self.auto_nets = []
python_path = os.path.dirname(os.path.dirname(__file__))
argvbase = ([sys.executable, sys.argv[0]] + argvbase = ([sys.executable, sys.argv[0]] +
['-v'] * (helpers.verbose or 0) + ['-v'] * (helpers.verbose or 0) +
['--method', method_name] + ['--method', method_name] +
['--firewall'] + ['--firewall'])
['--ttl', str(ttl)])
if ssyslog._p: if ssyslog._p:
argvbase += ['--syslog'] argvbase += ['--syslog']
@ -224,7 +223,8 @@ class FirewallClient:
if sudo_pythonpath: if sudo_pythonpath:
elev_prefix += ['/usr/bin/env', elev_prefix += ['/usr/bin/env',
'PYTHONPATH=%s' % python_path] 'PYTHONPATH=%s' %
os.path.dirname(os.path.dirname(__file__))]
argv_tries = [elev_prefix + argvbase, argvbase] argv_tries = [elev_prefix + argvbase, argvbase]
# we can't use stdin/stdout=subprocess.PIPE here, as we normally would, # we can't use stdin/stdout=subprocess.PIPE here, as we normally would,
@ -261,7 +261,7 @@ class FirewallClient:
def setup(self, subnets_include, subnets_exclude, nslist, def setup(self, subnets_include, subnets_exclude, nslist,
redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4, udp, redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4, udp,
user, tmark, ttl): user, ttl, tmark):
self.subnets_include = subnets_include self.subnets_include = subnets_include
self.subnets_exclude = subnets_exclude self.subnets_exclude = subnets_exclude
self.nslist = nslist self.nslist = nslist
@ -311,7 +311,9 @@ class FirewallClient:
else: else:
user = b'%d' % self.user user = b'%d' % self.user
self.pfile.write(b'GO %d %s\n' % (udp, user)) self.pfile.write(b'GO %d %s %d %s\n' %
(udp, user, self.ttl,
bytes(self.tmark, 'ascii')))
self.pfile.flush() self.pfile.flush()
line = self.pfile.readline() line = self.pfile.readline()
@ -1003,7 +1005,7 @@ def main(listenip_v6, listenip_v4,
# start the firewall # start the firewall
fw.setup(subnets_include, subnets_exclude, nslist, fw.setup(subnets_include, subnets_exclude, nslist,
redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4, redirectport_v6, redirectport_v4, dnsport_v6, dnsport_v4,
required.udp, user, tmark, ttl) required.udp, user, ttl, tmark)
# start the client process # start the client process
try: try:

View File

@ -20,7 +20,7 @@ def main():
return 1 return 1
if not opt.sudoers_filename: if not opt.sudoers_filename:
log('--sudoers-file must be set or omited.') log('--sudoers-file must be set or omitted.')
return 1 return 1
sudoers( sudoers(
@ -85,6 +85,13 @@ def main():
ipport_v4 = "auto" ipport_v4 = "auto"
# parse_ipport6('[::1]:0') # parse_ipport6('[::1]:0')
ipport_v6 = "auto" if not opt.disable_ipv6 else None ipport_v6 = "auto" if not opt.disable_ipv6 else None
try:
int(opt.tmark, 16)
except ValueError:
parser.error("--tmark must be a hexadecimal value")
opt.tmark = opt.tmark.lower() # make 'x' in 0x lowercase
if not opt.tmark.startswith("0x"): # accept without 0x prefix
opt.tmark = "0x%s" % opt.tmark
if opt.syslog: if opt.syslog:
ssyslog.start_syslog() ssyslog.start_syslog()
ssyslog.close_stdin() ssyslog.close_stdin()

View File

@ -223,11 +223,13 @@ def main(method_name, syslog, ttl):
raise Fatal('expected GO but got %r' % line) raise Fatal('expected GO but got %r' % line)
_, _, args = line.partition(" ") _, _, args = line.partition(" ")
udp, user = args.strip().split(" ", 1) udp, user, ttl, tmark = args.strip().split(" ", 3)
udp = bool(int(udp)) udp = bool(int(udp))
if user == '-': if user == '-':
user = None user = None
debug2('Got udp: %r, user: %r' % (udp, user)) ttl = int(ttl)
debug2('Got udp: %r, user: %r, ttl: %s, tmark: %s' %
(udp, user, ttl, tmark))
subnets_v6 = [i for i in subnets if i[0] == socket.AF_INET6] subnets_v6 = [i for i in subnets if i[0] == socket.AF_INET6]
nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6] nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6]
@ -242,14 +244,14 @@ def main(method_name, syslog, ttl):
method.setup_firewall( method.setup_firewall(
port_v6, dnsport_v6, nslist_v6, port_v6, dnsport_v6, nslist_v6,
socket.AF_INET6, subnets_v6, udp, socket.AF_INET6, subnets_v6, udp,
user, ttl) user, ttl, tmark)
if subnets_v4 or nslist_v4: if subnets_v4 or nslist_v4:
debug2('setting up IPv4.') debug2('setting up IPv4.')
method.setup_firewall( method.setup_firewall(
port_v4, dnsport_v4, nslist_v4, port_v4, dnsport_v4, nslist_v4,
socket.AF_INET, subnets_v4, udp, socket.AF_INET, subnets_v4, udp,
user, ttl) user, ttl, tmark)
flush_systemd_dns_cache() flush_systemd_dns_cache()
stdout.write('STARTED\n') stdout.write('STARTED\n')

View File

@ -16,8 +16,6 @@ NETSTAT_POLL_TIME = 30
CACHEFILE = os.path.expanduser('~/.sshuttle.hosts') CACHEFILE = os.path.expanduser('~/.sshuttle.hosts')
_nmb_ok = True
_smb_ok = True
hostnames = {} hostnames = {}
queue = {} queue = {}
try: try:
@ -141,110 +139,11 @@ def _check_netstat():
check_host(ip) check_host(ip)
def _check_smb(hostname):
return
global _smb_ok
if not _smb_ok:
return
debug2(' > smb: %s' % hostname)
argv = ['smbclient', '-U', '%', '-L', hostname]
try:
p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null,
env=get_env())
lines = p.stdout.readlines()
p.wait()
except OSError:
_, e = sys.exc_info()[:2]
log('%r failed: %r' % (argv, e))
_smb_ok = False
return
lines.reverse()
# junk at top
while lines:
line = lines.pop().strip()
if re.match(r'Server\s+', line):
break
# server list section:
# Server Comment
# ------ -------
while lines:
line = lines.pop().strip()
if not line or re.match(r'-+\s+-+', line):
continue
if re.match(r'Workgroup\s+Master', line):
break
words = line.split()
hostname = words[0].lower()
debug3('< %s' % hostname)
check_host(hostname)
# workgroup list section:
# Workgroup Master
# --------- ------
while lines:
line = lines.pop().strip()
if re.match(r'-+\s+', line):
continue
if not line:
break
words = line.split()
(workgroup, hostname) = (words[0].lower(), words[1].lower())
debug3('< group(%s) -> %s' % (workgroup, hostname))
check_host(hostname)
check_workgroup(workgroup)
if lines:
assert(0)
def _check_nmb(hostname, is_workgroup, is_master):
return
global _nmb_ok
if not _nmb_ok:
return
debug2(' > n%d%d: %s' % (is_workgroup, is_master, hostname))
argv = ['nmblookup'] + ['-M'] * is_master + ['--', hostname]
try:
p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE, stderr=null,
env=get_env)
lines = p.stdout.readlines()
rv = p.wait()
except OSError:
_, e = sys.exc_info()[:2]
log('%r failed: %r' % (argv, e))
_nmb_ok = False
return
if rv:
log('%r returned %d' % (argv, rv))
return
for line in lines:
m = re.match(r'(\d+\.\d+\.\d+\.\d+) (\w+)<\w\w>\n', line)
if m:
g = m.groups()
(ip, name) = (g[0], g[1].lower())
debug3('< %s -> %s' % (name, ip))
if is_workgroup:
_enqueue(_check_smb, ip)
else:
found_host(name, ip)
check_host(name)
def check_host(hostname): def check_host(hostname):
if _is_ip(hostname): if _is_ip(hostname):
_enqueue(_check_revdns, hostname) _enqueue(_check_revdns, hostname)
else: else:
_enqueue(_check_dns, hostname) _enqueue(_check_dns, hostname)
_enqueue(_check_smb, hostname)
_enqueue(_check_nmb, hostname, False, False)
def check_workgroup(hostname):
_enqueue(_check_nmb, hostname, True, False)
_enqueue(_check_nmb, hostname, True, True)
def _enqueue(op, *args): def _enqueue(op, *args):
@ -277,8 +176,6 @@ def hw_main(seed_hosts, auto_hosts):
_enqueue(_check_netstat) _enqueue(_check_netstat)
check_host('localhost') check_host('localhost')
check_host(socket.gethostname()) check_host(socket.gethostname())
check_workgroup('workgroup')
check_workgroup('-')
while 1: while 1:
now = time.time() now = time.time()

View File

@ -91,7 +91,7 @@ class BaseMethod(object):
(key, self.name)) (key, self.name))
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp, def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
user): user, ttl, tmark):
raise NotImplementedError() raise NotImplementedError()
def restore_firewall(self, port, family, udp, user): def restore_firewall(self, port, family, udp, user):

View File

@ -189,7 +189,7 @@ class Method(BaseMethod):
# udp_listener.v6.setsockopt(SOL_IPV6, IPV6_RECVDSTADDR, 1) # udp_listener.v6.setsockopt(SOL_IPV6, IPV6_RECVDSTADDR, 1)
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp, def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
user, ttl): user, ttl, tmark):
# IPv6 not supported # IPv6 not supported
if family not in [socket.AF_INET]: if family not in [socket.AF_INET]:
raise Exception( raise Exception(

View File

@ -13,7 +13,7 @@ class Method(BaseMethod):
# recently-started one will win (because we use "-I OUTPUT 1" instead of # recently-started one will win (because we use "-I OUTPUT 1" instead of
# "-A OUTPUT"). # "-A OUTPUT").
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp, def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
user, ttl): user, ttl, tmark):
if family != socket.AF_INET and family != socket.AF_INET6: if family != socket.AF_INET and family != socket.AF_INET6:
raise Exception( raise Exception(
'Address family "%s" unsupported by nat method_name' 'Address family "%s" unsupported by nat method_name'

View File

@ -13,7 +13,7 @@ class Method(BaseMethod):
# recently-started one will win (because we use "-I OUTPUT 1" instead of # recently-started one will win (because we use "-I OUTPUT 1" instead of
# "-A OUTPUT"). # "-A OUTPUT").
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp, def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
user, ttl): user, ttl, tmark):
if udp: if udp:
raise Exception("UDP not supported by nft") raise Exception("UDP not supported by nft")

View File

@ -444,7 +444,7 @@ class Method(BaseMethod):
return sock.getsockname() return sock.getsockname()
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp, def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
user, ttl): user, ttl, tmark):
if family not in [socket.AF_INET, socket.AF_INET6]: if family not in [socket.AF_INET, socket.AF_INET6]:
raise Exception( raise Exception(
'Address family "%s" unsupported by pf method_name' 'Address family "%s" unsupported by pf method_name'

View File

@ -151,17 +151,7 @@ class Method(BaseMethod):
udp_listener.v6.setsockopt(SOL_IPV6, IPV6_RECVORIGDSTADDR, 1) udp_listener.v6.setsockopt(SOL_IPV6, IPV6_RECVORIGDSTADDR, 1)
def setup_firewall(self, port, dnsport, nslist, family, subnets, udp, def setup_firewall(self, port, dnsport, nslist, family, subnets, udp,
user, ttl): user, ttl, tmark):
if self.firewall is None:
tmark = '1'
else:
tmark = self.firewall.tmark
self.setup_firewall_tproxy(port, dnsport, nslist, family, subnets, udp,
user, tmark)
def setup_firewall_tproxy(self, port, dnsport, nslist, family, subnets,
udp, user, tmark):
if family not in [socket.AF_INET, socket.AF_INET6]: if family not in [socket.AF_INET, socket.AF_INET6]:
raise Exception( raise Exception(
'Address family "%s" unsupported by tproxy method' 'Address family "%s" unsupported by tproxy method'
@ -192,8 +182,8 @@ class Method(BaseMethod):
_ipt('-F', divert_chain) _ipt('-F', divert_chain)
_ipt('-N', tproxy_chain) _ipt('-N', tproxy_chain)
_ipt('-F', tproxy_chain) _ipt('-F', tproxy_chain)
_ipt('-I', 'OUTPUT', tmark, '-j', mark_chain) _ipt('-I', 'OUTPUT', '1', '-j', mark_chain)
_ipt('-I', 'PREROUTING', tmark, '-j', tproxy_chain) _ipt('-I', 'PREROUTING', '1', '-j', tproxy_chain)
# Don't have packets sent to any of our local IP addresses go # Don't have packets sent to any of our local IP addresses go
# through the tproxy or mark chains. # through the tproxy or mark chains.
@ -224,7 +214,7 @@ class Method(BaseMethod):
'--dest', '%s/32' % ip, '--dest', '%s/32' % ip,
'-m', 'udp', '-p', 'udp', '--dport', '53') '-m', 'udp', '-p', 'udp', '--dport', '53')
_ipt('-A', tproxy_chain, '-j', 'TPROXY', _ipt('-A', tproxy_chain, '-j', 'TPROXY',
'--tproxy-mark', '0x'+tmark+'/0x'+tmark, '--tproxy-mark', tmark,
'--dest', '%s/32' % ip, '--dest', '%s/32' % ip,
'-m', 'udp', '-p', 'udp', '--dport', '53', '-m', 'udp', '-p', 'udp', '--dport', '53',
'--on-port', str(dnsport)) '--on-port', str(dnsport))
@ -249,7 +239,7 @@ class Method(BaseMethod):
'-m', 'tcp', '-m', 'tcp',
*tcp_ports) *tcp_ports)
_ipt('-A', tproxy_chain, '-j', 'TPROXY', _ipt('-A', tproxy_chain, '-j', 'TPROXY',
'--tproxy-mark', '0x'+tmark+'/0x'+tmark, '--tproxy-mark', tmark,
'--dest', '%s/%s' % (snet, swidth), '--dest', '%s/%s' % (snet, swidth),
'-m', 'tcp', '-m', 'tcp',
*(tcp_ports + ('--on-port', str(port)))) *(tcp_ports + ('--on-port', str(port))))
@ -273,7 +263,7 @@ class Method(BaseMethod):
'-m', 'udp', '-m', 'udp',
*udp_ports) *udp_ports)
_ipt('-A', tproxy_chain, '-j', 'TPROXY', _ipt('-A', tproxy_chain, '-j', 'TPROXY',
'--tproxy-mark', '0x'+tmark+'/0x'+tmark, '--tproxy-mark', tmark,
'--dest', '%s/%s' % (snet, swidth), '--dest', '%s/%s' % (snet, swidth),
'-m', 'udp', '-m', 'udp',
*(udp_ports + ('--on-port', str(port)))) *(udp_ports + ('--on-port', str(port))))

View File

@ -132,6 +132,7 @@ def parse_ipport(s):
def parse_list(lst): def parse_list(lst):
"""Parse a comma separated string into a list."""
return re.split(r'[\s,]+', lst.strip()) if lst else [] return re.split(r'[\s,]+', lst.strip()) if lst else []
@ -220,6 +221,7 @@ parser.add_argument(
type=parse_list, type=parse_list,
help=""" help="""
capture and forward DNS requests made to the following servers capture and forward DNS requests made to the following servers
(comma separated)
""" """
) )
parser.add_argument( parser.add_argument(
@ -280,7 +282,7 @@ parser.add_argument(
action="count", action="count",
default=0, default=0,
help=""" help="""
increase debug message verbosity increase debug message verbosity (can be used more than once)
""" """
) )
parser.add_argument( parser.add_argument(
@ -445,8 +447,9 @@ parser.add_argument(
parser.add_argument( parser.add_argument(
"-t", "--tmark", "-t", "--tmark",
metavar="[MARK]", metavar="[MARK]",
default="1", default="0x01",
help=""" help="""
transproxy optional traffic mark with provided MARK value tproxy optional traffic mark with provided MARK value in
hexadecimal (default '0x01')
""" """
) )

View File

@ -15,7 +15,7 @@ NSLIST
{inet},1.2.3.33 {inet},1.2.3.33
{inet6},2404:6800:4004:80c::33 {inet6},2404:6800:4004:80c::33
PORTS 1024,1025,1026,1027 PORTS 1024,1025,1026,1027
GO 1 - GO 1 - 63 0x01
HOST 1.2.3.3,existing HOST 1.2.3.3,existing
""".format(inet=AF_INET, inet6=AF_INET6)) """.format(inet=AF_INET, inet6=AF_INET6))
stdout = Mock() stdout = Mock()
@ -126,7 +126,7 @@ def test_main(mock_get_method, mock_setup_daemon, mock_rewrite_etc_hosts):
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 80, 80)], (AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 80, 80)],
True, True,
None, None,
63), 63, '0x01'),
call().setup_firewall( call().setup_firewall(
1025, 1027, 1025, 1027,
[(AF_INET, u'1.2.3.33')], [(AF_INET, u'1.2.3.33')],
@ -135,7 +135,7 @@ def test_main(mock_get_method, mock_setup_daemon, mock_rewrite_etc_hosts):
(AF_INET, 32, True, u'1.2.3.66', 8080, 8080)], (AF_INET, 32, True, u'1.2.3.66', 8080, 8080)],
True, True,
None, None,
63), 63, '0x01'),
call().restore_firewall(1024, AF_INET6, True, None), call().restore_firewall(1024, AF_INET6, True, None),
call().restore_firewall(1025, AF_INET, True, None), call().restore_firewall(1025, AF_INET, True, None),
] ]

View File

@ -103,7 +103,7 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 80, 80)], (AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 80, 80)],
False, False,
None, None,
63) 63, '0x01')
assert mock_ipt_chain_exists.mock_calls == [ assert mock_ipt_chain_exists.mock_calls == [
call(AF_INET6, 'nat', 'sshuttle-1024') call(AF_INET6, 'nat', 'sshuttle-1024')
@ -150,7 +150,7 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
(AF_INET, 32, True, u'1.2.3.66', 8080, 8080)], (AF_INET, 32, True, u'1.2.3.66', 8080, 8080)],
True, True,
None, None,
63) 63, '0x01')
assert str(excinfo.value) == 'UDP not supported by nat method_name' assert str(excinfo.value) == 'UDP not supported by nat method_name'
assert mock_ipt_chain_exists.mock_calls == [] assert mock_ipt_chain_exists.mock_calls == []
assert mock_ipt_ttl.mock_calls == [] assert mock_ipt_ttl.mock_calls == []
@ -164,7 +164,7 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
(AF_INET, 32, True, u'1.2.3.66', 8080, 8080)], (AF_INET, 32, True, u'1.2.3.66', 8080, 8080)],
False, False,
None, None,
63) 63, '0x01')
assert mock_ipt_chain_exists.mock_calls == [ assert mock_ipt_chain_exists.mock_calls == [
call(AF_INET, 'nat', 'sshuttle-1025') call(AF_INET, 'nat', 'sshuttle-1025')
] ]

View File

@ -187,7 +187,7 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)], (AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
False, False,
None, None,
63) 63, '0x01')
assert mock_ioctl.mock_calls == [ assert mock_ioctl.mock_calls == [
call(mock_pf_get_dev(), 0xC4704433, ANY), call(mock_pf_get_dev(), 0xC4704433, ANY),
call(mock_pf_get_dev(), 0xCC20441A, ANY), call(mock_pf_get_dev(), 0xCC20441A, ANY),
@ -227,7 +227,7 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
(AF_INET, 32, True, u'1.2.3.66', 80, 80)], (AF_INET, 32, True, u'1.2.3.66', 80, 80)],
True, True,
None, None,
63) 63, '0x01')
assert str(excinfo.value) == 'UDP not supported by pf method_name' assert str(excinfo.value) == 'UDP not supported by pf method_name'
assert mock_pf_get_dev.mock_calls == [] assert mock_pf_get_dev.mock_calls == []
assert mock_ioctl.mock_calls == [] assert mock_ioctl.mock_calls == []
@ -241,7 +241,7 @@ def test_setup_firewall_darwin(mock_pf_get_dev, mock_ioctl, mock_pfctl):
(AF_INET, 32, True, u'1.2.3.66', 80, 80)], (AF_INET, 32, True, u'1.2.3.66', 80, 80)],
False, False,
None, None,
63) 63, '0x01')
assert mock_ioctl.mock_calls == [ assert mock_ioctl.mock_calls == [
call(mock_pf_get_dev(), 0xC4704433, ANY), call(mock_pf_get_dev(), 0xC4704433, ANY),
call(mock_pf_get_dev(), 0xCC20441A, ANY), call(mock_pf_get_dev(), 0xCC20441A, ANY),
@ -302,7 +302,7 @@ def test_setup_firewall_freebsd(mock_pf_get_dev, mock_ioctl, mock_pfctl,
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)], (AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
False, False,
None, None,
63) 63, '0x01')
assert mock_pfctl.mock_calls == [ assert mock_pfctl.mock_calls == [
call('-s all'), call('-s all'),
@ -335,7 +335,7 @@ def test_setup_firewall_freebsd(mock_pf_get_dev, mock_ioctl, mock_pfctl,
(AF_INET, 32, True, u'1.2.3.66', 80, 80)], (AF_INET, 32, True, u'1.2.3.66', 80, 80)],
True, True,
None, None,
63) 63, '0x01')
assert str(excinfo.value) == 'UDP not supported by pf method_name' assert str(excinfo.value) == 'UDP not supported by pf method_name'
assert mock_pf_get_dev.mock_calls == [] assert mock_pf_get_dev.mock_calls == []
assert mock_ioctl.mock_calls == [] assert mock_ioctl.mock_calls == []
@ -349,7 +349,7 @@ def test_setup_firewall_freebsd(mock_pf_get_dev, mock_ioctl, mock_pfctl,
(AF_INET, 32, True, u'1.2.3.66', 80, 80)], (AF_INET, 32, True, u'1.2.3.66', 80, 80)],
False, False,
None, None,
63) 63, '0x01')
assert mock_ioctl.mock_calls == [ assert mock_ioctl.mock_calls == [
call(mock_pf_get_dev(), 0xC4704433, ANY), call(mock_pf_get_dev(), 0xC4704433, ANY),
call(mock_pf_get_dev(), 0xCBE0441A, ANY), call(mock_pf_get_dev(), 0xCBE0441A, ANY),
@ -408,7 +408,7 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)], (AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
False, False,
None, None,
63) 63, '0x01')
assert mock_ioctl.mock_calls == [ assert mock_ioctl.mock_calls == [
call(mock_pf_get_dev(), 0xcd60441a, ANY), call(mock_pf_get_dev(), 0xcd60441a, ANY),
@ -445,7 +445,7 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
(AF_INET, 32, True, u'1.2.3.66', 80, 80)], (AF_INET, 32, True, u'1.2.3.66', 80, 80)],
True, True,
None, None,
63) 63, '0x01')
assert str(excinfo.value) == 'UDP not supported by pf method_name' assert str(excinfo.value) == 'UDP not supported by pf method_name'
assert mock_pf_get_dev.mock_calls == [] assert mock_pf_get_dev.mock_calls == []
assert mock_ioctl.mock_calls == [] assert mock_ioctl.mock_calls == []
@ -459,7 +459,7 @@ def test_setup_firewall_openbsd(mock_pf_get_dev, mock_ioctl, mock_pfctl):
(AF_INET, 32, True, u'1.2.3.66', 80, 80)], (AF_INET, 32, True, u'1.2.3.66', 80, 80)],
False, False,
None, None,
63) 63, '0x01')
assert mock_ioctl.mock_calls == [ assert mock_ioctl.mock_calls == [
call(mock_pf_get_dev(), 0xcd60441a, ANY), call(mock_pf_get_dev(), 0xcd60441a, ANY),
call(mock_pf_get_dev(), 0xcd60441a, ANY), call(mock_pf_get_dev(), 0xcd60441a, ANY),

View File

@ -109,7 +109,7 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
(AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)], (AF_INET6, 128, True, u'2404:6800:4004:80c::101f', 8080, 8080)],
True, True,
None, None,
63) 63, '0x01')
assert mock_ipt_chain_exists.mock_calls == [ assert mock_ipt_chain_exists.mock_calls == [
call(AF_INET6, 'mangle', 'sshuttle-m-1024'), call(AF_INET6, 'mangle', 'sshuttle-m-1024'),
call(AF_INET6, 'mangle', 'sshuttle-t-1024'), call(AF_INET6, 'mangle', 'sshuttle-t-1024'),
@ -139,17 +139,17 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN', call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN',
'-m', 'addrtype', '--dst-type', 'LOCAL'), '-m', 'addrtype', '--dst-type', 'LOCAL'),
call(AF_INET6, 'mangle', '-A', 'sshuttle-d-1024', '-j', 'MARK', call(AF_INET6, 'mangle', '-A', 'sshuttle-d-1024', '-j', 'MARK',
'--set-mark', '1'), '--set-mark', '0x01'),
call(AF_INET6, 'mangle', '-A', 'sshuttle-d-1024', '-j', 'ACCEPT'), call(AF_INET6, 'mangle', '-A', 'sshuttle-d-1024', '-j', 'ACCEPT'),
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-m', 'socket', call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-m', 'socket',
'-j', 'sshuttle-d-1024', '-m', 'tcp', '-p', 'tcp'), '-j', 'sshuttle-d-1024', '-m', 'tcp', '-p', 'tcp'),
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-m', 'socket', call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-m', 'socket',
'-j', 'sshuttle-d-1024', '-m', 'udp', '-p', 'udp'), '-j', 'sshuttle-d-1024', '-m', 'udp', '-p', 'udp'),
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK', call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
'--set-mark', '1', '--dest', u'2404:6800:4004:80c::33/32', '--set-mark', '0x01', '--dest', u'2404:6800:4004:80c::33/32',
'-m', 'udp', '-p', 'udp', '--dport', '53'), '-m', 'udp', '-p', 'udp', '--dport', '53'),
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY', call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
'--tproxy-mark', '0x1/0x1', '--tproxy-mark', '0x01',
'--dest', u'2404:6800:4004:80c::33/32', '--dest', u'2404:6800:4004:80c::33/32',
'-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1026'), '-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1026'),
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN', call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'RETURN',
@ -165,17 +165,19 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
'--dest', u'2404:6800:4004:80c::101f/128', '--dest', u'2404:6800:4004:80c::101f/128',
'-m', 'udp', '-p', 'udp', '--dport', '8080:8080'), '-m', 'udp', '-p', 'udp', '--dport', '8080:8080'),
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK', call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
'--set-mark', '1', '--dest', u'2404:6800:4004:80c::/64', '--set-mark', '0x01', '--dest', u'2404:6800:4004:80c::/64',
'-m', 'tcp', '-p', 'tcp', '--dport', '8000:9000'), '-m', 'tcp', '-p', 'tcp', '--dport', '8000:9000'),
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY', call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
'--tproxy-mark', '0x1/0x1', '--dest', u'2404:6800:4004:80c::/64', '--tproxy-mark', '0x01', '--dest',
u'2404:6800:4004:80c::/64',
'-m', 'tcp', '-p', 'tcp', '--dport', '8000:9000', '-m', 'tcp', '-p', 'tcp', '--dport', '8000:9000',
'--on-port', '1024'), '--on-port', '1024'),
call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK', call(AF_INET6, 'mangle', '-A', 'sshuttle-m-1024', '-j', 'MARK',
'--set-mark', '1', '--dest', u'2404:6800:4004:80c::/64', '--set-mark', '0x01', '--dest', u'2404:6800:4004:80c::/64',
'-m', 'udp', '-p', 'udp', '--dport', '8000:9000'), '-m', 'udp', '-p', 'udp', '--dport', '8000:9000'),
call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY', call(AF_INET6, 'mangle', '-A', 'sshuttle-t-1024', '-j', 'TPROXY',
'--tproxy-mark', '0x1/0x1', '--dest', u'2404:6800:4004:80c::/64', '--tproxy-mark', '0x01', '--dest',
u'2404:6800:4004:80c::/64',
'-m', 'udp', '-p', 'udp', '--dport', '8000:9000', '-m', 'udp', '-p', 'udp', '--dport', '8000:9000',
'--on-port', '1024') '--on-port', '1024')
] ]
@ -214,7 +216,7 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
(AF_INET, 32, True, u'1.2.3.66', 80, 80)], (AF_INET, 32, True, u'1.2.3.66', 80, 80)],
True, True,
None, None,
63) 63, '0x01')
assert mock_ipt_chain_exists.mock_calls == [ assert mock_ipt_chain_exists.mock_calls == [
call(AF_INET, 'mangle', 'sshuttle-m-1025'), call(AF_INET, 'mangle', 'sshuttle-m-1025'),
call(AF_INET, 'mangle', 'sshuttle-t-1025'), call(AF_INET, 'mangle', 'sshuttle-t-1025'),
@ -244,17 +246,17 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN', call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN',
'-m', 'addrtype', '--dst-type', 'LOCAL'), '-m', 'addrtype', '--dst-type', 'LOCAL'),
call(AF_INET, 'mangle', '-A', 'sshuttle-d-1025', call(AF_INET, 'mangle', '-A', 'sshuttle-d-1025',
'-j', 'MARK', '--set-mark', '1'), '-j', 'MARK', '--set-mark', '0x01'),
call(AF_INET, 'mangle', '-A', 'sshuttle-d-1025', '-j', 'ACCEPT'), call(AF_INET, 'mangle', '-A', 'sshuttle-d-1025', '-j', 'ACCEPT'),
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-m', 'socket', call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-m', 'socket',
'-j', 'sshuttle-d-1025', '-m', 'tcp', '-p', 'tcp'), '-j', 'sshuttle-d-1025', '-m', 'tcp', '-p', 'tcp'),
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-m', 'socket', call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-m', 'socket',
'-j', 'sshuttle-d-1025', '-m', 'udp', '-p', 'udp'), '-j', 'sshuttle-d-1025', '-m', 'udp', '-p', 'udp'),
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK', call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
'--set-mark', '1', '--dest', u'1.2.3.33/32', '--set-mark', '0x01', '--dest', u'1.2.3.33/32',
'-m', 'udp', '-p', 'udp', '--dport', '53'), '-m', 'udp', '-p', 'udp', '--dport', '53'),
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY', call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY',
'--tproxy-mark', '0x1/0x1', '--dest', u'1.2.3.33/32', '--tproxy-mark', '0x01', '--dest', u'1.2.3.33/32',
'-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1027'), '-m', 'udp', '-p', 'udp', '--dport', '53', '--on-port', '1027'),
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN', call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'RETURN',
'--dest', u'1.2.3.66/32', '-m', 'tcp', '-p', 'tcp', '--dest', u'1.2.3.66/32', '-m', 'tcp', '-p', 'tcp',
@ -269,16 +271,16 @@ def test_setup_firewall(mock_ipt_chain_exists, mock_ipt_ttl, mock_ipt):
'--dest', u'1.2.3.66/32', '-m', 'udp', '-p', 'udp', '--dest', u'1.2.3.66/32', '-m', 'udp', '-p', 'udp',
'--dport', '80:80'), '--dport', '80:80'),
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK', call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
'--set-mark', '1', '--dest', u'1.2.3.0/24', '--set-mark', '0x01', '--dest', u'1.2.3.0/24',
'-m', 'tcp', '-p', 'tcp'), '-m', 'tcp', '-p', 'tcp'),
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY', call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY',
'--tproxy-mark', '0x1/0x1', '--dest', u'1.2.3.0/24', '--tproxy-mark', '0x01', '--dest', u'1.2.3.0/24',
'-m', 'tcp', '-p', 'tcp', '--on-port', '1025'), '-m', 'tcp', '-p', 'tcp', '--on-port', '1025'),
call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK', call(AF_INET, 'mangle', '-A', 'sshuttle-m-1025', '-j', 'MARK',
'--set-mark', '1', '--dest', u'1.2.3.0/24', '--set-mark', '0x01', '--dest', u'1.2.3.0/24',
'-m', 'udp', '-p', 'udp'), '-m', 'udp', '-p', 'udp'),
call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY', call(AF_INET, 'mangle', '-A', 'sshuttle-t-1025', '-j', 'TPROXY',
'--tproxy-mark', '0x1/0x1', '--dest', u'1.2.3.0/24', '--tproxy-mark', '0x01', '--dest', u'1.2.3.0/24',
'-m', 'udp', '-p', 'udp', '--on-port', '1025') '-m', 'udp', '-p', 'udp', '--on-port', '1025')
] ]
mock_ipt_chain_exists.reset_mock() mock_ipt_chain_exists.reset_mock()