summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian May <brian@linuxpenguins.xyz>2015-12-06 11:33:52 +1100
committerBrian May <brian@linuxpenguins.xyz>2015-12-06 11:33:52 +1100
commitaaa6062329064eaaf3e0c94dc5f2def80512d2ab (patch)
tree8282802e6d0b951740ff1b3a20cda49ec458b745
parentda4ce19121ffbded893763456b9ab4b4f29751bd (diff)
Remove IPFW support.
This is no longer used by modern MacOSX and not getting tested. It also required a do_wait() function which was a complication for sshuttle as a whole. Can get resurrected if required.
-rw-r--r--README.rst8
-rw-r--r--sshuttle/__main__.py4
-rw-r--r--sshuttle/firewall.py7
-rw-r--r--sshuttle/methods/__init__.py6
-rw-r--r--sshuttle/methods/ipfw.py237
-rw-r--r--sshuttle/sshuttle.md20
-rw-r--r--sshuttle/tests/test_firewall.py2
7 files changed, 7 insertions, 277 deletions
diff --git a/README.rst b/README.rst
index 2b05d02..45b8485 100644
--- a/README.rst
+++ b/README.rst
@@ -43,17 +43,9 @@ Client side Requirements
| | | * IPv6 TCP + |
| | | * IPv6 UDP + |
+-------+--------+------------+-----------------------------------------------+
-| BSD | IPFW | * IPv4 TCP | Your kernel needs to be compiled with |
-| | | | `IPFIREWALL_FORWARD` and you need to have ipfw|
-| | | | available. |
-+-------+--------+------------+-----------------------------------------------+
| MacOS | PF | * IPv4 TCP + You need to have the pfctl command. |
+-------+--------+------------+-----------------------------------------------+
-The IPFW method is depreciated. It was originally required for MacOS support,
-however is no longer maintained. It is likely to get removed from future
-versions of sshuttle.
-
Server side Requirements
------------------------
diff --git a/sshuttle/__main__.py b/sshuttle/__main__.py
index 5a47c4f..4425866 100644
--- a/sshuttle/__main__.py
+++ b/sshuttle/__main__.py
@@ -119,7 +119,7 @@ H,auto-hosts scan for remote hostnames and update local /etc/hosts
N,auto-nets automatically determine subnets to route
dns capture local DNS requests and forward to the remote DNS server
ns-hosts= capture and forward remote DNS requests to the following servers
-method= auto, nat, tproxy, pf or ipfw
+method= auto, nat, tproxy or pf
python= path to python interpreter on the remote server
r,remote= ssh hostname (and optional username) of remote sshuttle server
x,exclude= exclude this subnet (can be used more than once)
@@ -181,7 +181,7 @@ try:
includes = parse_subnet_file(opt.subnets)
if not opt.method:
method_name = "auto"
- elif opt.method in ["auto", "nat", "tproxy", "ipfw", "pf"]:
+ elif opt.method in ["auto", "nat", "tproxy", "pf"]:
method_name = opt.method
else:
o.fatal("method_name %s not supported" % opt.method)
diff --git a/sshuttle/firewall.py b/sshuttle/firewall.py
index 0c10080..039b9cf 100644
--- a/sshuttle/firewall.py
+++ b/sshuttle/firewall.py
@@ -178,12 +178,11 @@ def main(method_name, syslog):
try:
debug1('firewall manager: setting up.\n')
- do_wait = None
nslist_v6 = [i for i in nslist if i[0] == socket.AF_INET6]
subnets_v6 = [i for i in subnets if i[0] == socket.AF_INET6]
if port_v6 > 0:
debug2('firewall manager: setting up IPv6.\n')
- do_wait = method.setup_firewall(
+ method.setup_firewall(
port_v6, dnsport_v6, nslist_v6,
socket.AF_INET6, subnets_v6, udp)
elif len(subnets_v6) > 0:
@@ -193,7 +192,7 @@ def main(method_name, syslog):
subnets_v4 = [i for i in subnets if i[0] == socket.AF_INET]
if port_v4 > 0:
debug2('firewall manager: setting up IPv4.\n')
- do_wait = method.setup_firewall(
+ method.setup_firewall(
port_v4, dnsport_v4, nslist_v4,
socket.AF_INET, subnets_v4, udp)
elif len(subnets_v4) > 0:
@@ -213,8 +212,6 @@ def main(method_name, syslog):
# to stay running so that we don't need a *second* password
# authentication at shutdown time - that cleanup is important!
while 1:
- if do_wait is not None:
- do_wait()
line = stdin.readline(128)
if line.startswith('HOST '):
(name, ip) = line[5:].strip().split(',', 1)
diff --git a/sshuttle/methods/__init__.py b/sshuttle/methods/__init__.py
index 35e828b..856ed1a 100644
--- a/sshuttle/methods/__init__.py
+++ b/sshuttle/methods/__init__.py
@@ -86,14 +86,12 @@ def get_method(method_name):
def get_auto_method():
- if _program_exists('ipfw'):
- method_name = "ipfw"
- elif _program_exists('iptables'):
+ if _program_exists('iptables'):
method_name = "nat"
elif _program_exists('pfctl'):
method_name = "pf"
else:
raise Fatal(
- "can't find either ipfw, iptables or pfctl; check your PATH")
+ "can't find either iptables or pfctl; check your PATH")
return get_method(method_name)
diff --git a/sshuttle/methods/ipfw.py b/sshuttle/methods/ipfw.py
deleted file mode 100644
index 678d00f..0000000
--- a/sshuttle/methods/ipfw.py
+++ /dev/null
@@ -1,237 +0,0 @@
-import sys
-import select
-import socket
-import struct
-import subprocess as ssubprocess
-from sshuttle.helpers import log, debug1, debug3, islocal, \
- Fatal, family_to_string
-from sshuttle.methods import BaseMethod
-
-
-# python doesn't have a definition for this
-IPPROTO_DIVERT = 254
-
-
-def ipfw_rule_exists(n):
- argv = ['ipfw', 'list']
- p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE)
- found = False
- for line in p.stdout:
- if line.startswith('%05d ' % n):
- if not ('ipttl 42' in line
- or ('skipto %d' % (n + 1)) in line
- or 'check-state' in line):
- log('non-sshuttle ipfw rule: %r\n' % line.strip())
- raise Fatal('non-sshuttle ipfw rule #%d already exists!' % n)
- found = True
- rv = p.wait()
- if rv:
- raise Fatal('%r returned %d' % (argv, rv))
- return found
-
-
-_oldctls = {}
-
-
-def _fill_oldctls(prefix):
- argv = ['sysctl', prefix]
- p = ssubprocess.Popen(argv, stdout=ssubprocess.PIPE)
- for line in p.stdout:
- assert(line[-1] == '\n')
- (k, v) = line[:-1].split(': ', 1)
- _oldctls[k] = v
- rv = p.wait()
- if rv:
- raise Fatal('%r returned %d' % (argv, rv))
- if not line:
- raise Fatal('%r returned no data' % (argv,))
-
-
-def _sysctl_set(name, val):
- argv = ['sysctl', '-w', '%s=%s' % (name, val)]
- debug1('>> %s\n' % ' '.join(argv))
- return ssubprocess.call(argv, stdout=open('/dev/null', 'w'))
-
-
-_changedctls = []
-
-
-def sysctl_set(name, val, permanent=False):
- PREFIX = 'net.inet.ip'
- assert(name.startswith(PREFIX + '.'))
- val = str(val)
- if not _oldctls:
- _fill_oldctls(PREFIX)
- if not (name in _oldctls):
- debug1('>> No such sysctl: %r\n' % name)
- return False
- oldval = _oldctls[name]
- if val != oldval:
- rv = _sysctl_set(name, val)
- if rv == 0 and permanent:
- debug1('>> ...saving permanently in /etc/sysctl.conf\n')
- f = open('/etc/sysctl.conf', 'a')
- f.write('\n'
- '# Added by sshuttle\n'
- '%s=%s\n' % (name, val))
- f.close()
- else:
- _changedctls.append(name)
- return True
-
-
-def _udp_unpack(p):
- src = (socket.inet_ntoa(p[12:16]), struct.unpack('!H', p[20:22])[0])
- dst = (socket.inet_ntoa(p[16:20]), struct.unpack('!H', p[22:24])[0])
- return src, dst
-
-
-def _udp_repack(p, src, dst):
- addrs = socket.inet_aton(src[0]) + socket.inet_aton(dst[0])
- ports = struct.pack('!HH', src[1], dst[1])
- return p[:12] + addrs + ports + p[24:]
-
-
-_real_dns_server = [None]
-
-
-def _handle_diversion(divertsock, dnsport):
- p, tag = divertsock.recvfrom(4096)
- src, dst = _udp_unpack(p)
- debug3('got diverted packet from %r to %r\n' % (src, dst))
- if dst[1] == 53:
- # outgoing DNS
- debug3('...packet is a DNS request.\n')
- _real_dns_server[0] = dst
- dst = ('127.0.0.1', dnsport)
- elif src[1] == dnsport:
- if islocal(src[0], divertsock.family):
- debug3('...packet is a DNS response.\n')
- src = _real_dns_server[0]
- else:
- log('weird?! unexpected divert from %r to %r\n' % (src, dst))
- assert(0)
- newp = _udp_repack(p, src, dst)
- divertsock.sendto(newp, tag)
-
-
-def ipfw(*args):
- argv = ['ipfw', '-q'] + list(args)
- debug1('>> %s\n' % ' '.join(argv))
- rv = ssubprocess.call(argv)
- if rv:
- raise Fatal('%r returned %d' % (argv, rv))
-
-
-class Method(BaseMethod):
-
- def setup_firewall(self, port, dnsport, nslist, family, subnets, udp):
- # IPv6 not supported
- if family not in [socket.AF_INET, ]:
- raise Exception(
- 'Address family "%s" unsupported by ipfw method_name'
- % family_to_string(family))
- if udp:
- raise Exception("UDP not supported by ipfw method_name")
-
- sport = str(port)
- xsport = str(port + 1)
-
- # cleanup any existing rules
- if ipfw_rule_exists(port):
- ipfw('delete', sport)
-
- while _changedctls:
- name = _changedctls.pop()
- oldval = _oldctls[name]
- _sysctl_set(name, oldval)
-
- if subnets or dnsport:
- sysctl_set('net.inet.ip.fw.enable', 1)
- changed = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True)
- if changed:
- log("\n"
- " WARNING: ONE-TIME NETWORK DISRUPTION:\n"
- " =====================================\n"
- "sshuttle has changed a MacOS kernel setting to work around\n"
- "a bug in MacOS 10.6. This will cause your network to drop\n"
- "within 5-10 minutes unless you restart your network\n"
- "interface (change wireless networks or unplug/plug the\n"
- "ethernet port) NOW, then restart sshuttle. The fix is\n"
- "permanent; you only have to do this once.\n\n")
- sys.exit(1)
-
- ipfw('add', sport, 'check-state', 'ip',
- 'from', 'any', 'to', 'any')
-
- if subnets:
- # create new subnet entries
- for f, swidth, sexclude, snet \
- in sorted(subnets, key=lambda s: s[1], reverse=True):
- if sexclude:
- ipfw('add', sport, 'skipto', xsport,
- 'tcp',
- 'from', 'any', 'to', '%s/%s' % (snet, swidth))
- else:
- ipfw('add', sport, 'fwd', '127.0.0.1,%d' % port,
- 'tcp',
- 'from', 'any', 'to', '%s/%s' % (snet, swidth),
- 'not', 'ipttl', '42', 'keep-state', 'setup')
-
- # This part is much crazier than it is on Linux, because MacOS (at
- # least 10.6, and probably other versions, and maybe FreeBSD too)
- # doesn't correctly fixup the dstip/dstport for UDP packets when it
- # puts them through a 'fwd' rule. It also doesn't fixup the
- # srcip/srcport in the response packet. In Linux iptables, all that
- # happens magically for us, so we just redirect the packets and relax.
- #
- # On MacOS, we have to fix the ports ourselves. For that, we use a
- # 'divert' socket, which receives raw packets and lets us mangle them.
- #
- # Here's how it works. Let's say the local DNS server is 1.1.1.1:53,
- # and the remote DNS server is 2.2.2.2:53, and the local transproxy
- # port is 10.0.0.1:12300, and a client machine is making a request from
- # 10.0.0.5:9999. We see a packet like this:
- # 10.0.0.5:9999 -> 1.1.1.1:53
- # Since the destip:port matches one of our local nameservers, it will
- # match a 'fwd' rule, thus grabbing it on the local machine. However,
- # the local kernel will then see a packet addressed to *:53 and not
- # know what to do with it; there's nobody listening on port 53. Thus,
- # we divert it, rewriting it into this:
- # 10.0.0.5:9999 -> 10.0.0.1:12300
- # This gets proxied out to the server, which sends it to 2.2.2.2:53,
- # and the answer comes back, and the proxy sends it back out like this:
- # 10.0.0.1:12300 -> 10.0.0.5:9999
- # But that's wrong! The original machine expected an answer from
- # 1.1.1.1:53, so we have to divert the *answer* and rewrite it:
- # 1.1.1.1:53 -> 10.0.0.5:9999
- #
- # See? Easy stuff.
- if dnsport:
- divertsock = socket.socket(socket.AF_INET, socket.SOCK_RAW,
- IPPROTO_DIVERT)
- divertsock.bind(('0.0.0.0', port)) # IP field is ignored
-
- for f, ip in [i for i in nslist if i[0] == family]:
- # relabel and then catch outgoing DNS requests
- ipfw('add', sport, 'divert', sport,
- 'udp',
- 'from', 'any', 'to', '%s/32' % ip, '53',
- 'not', 'ipttl', '42')
- # relabel DNS responses
- ipfw('add', sport, 'divert', sport,
- 'udp',
- 'from', 'any', str(dnsport), 'to', 'any',
- 'not', 'ipttl', '42')
-
- def do_wait():
- while 1:
- r, w, x = select.select([sys.stdin, divertsock], [], [])
- if divertsock in r:
- _handle_diversion(divertsock, dnsport)
- if sys.stdin in r:
- return
- else:
- do_wait = None
-
- return do_wait
diff --git a/sshuttle/sshuttle.md b/sshuttle/sshuttle.md
index 8f772a1..82eb580 100644
--- a/sshuttle/sshuttle.md
+++ b/sshuttle/sshuttle.md
@@ -227,7 +227,7 @@ conflicts between client and server.
Unlike most VPNs, sshuttle forwards sessions, not packets.
That is, it uses kernel transparent proxying (`iptables
-REDIRECT` rules on Linux, or `ipfw fwd` rules on BSD) to
+REDIRECT` rules on Linux) to
capture outgoing TCP sessions, then creates entirely
separate TCP sessions out to the original destination at
the other end of the tunnel.
@@ -256,24 +256,6 @@ between the two separate streams, so a tcp-based tunnel is
fine.
-# BUGS
-
-On MacOS 10.6 (at least up to 10.6.6), your network will
-stop responding about 10 minutes after the first time you
-start sshuttle, because of a MacOS kernel bug relating to
-arp and the net.inet.ip.scopedroute sysctl. To fix it,
-just switch your wireless off and on. Sshuttle makes the
-kernel setting it changes permanent, so this won't happen
-again, even after a reboot.
-
-On MacOS, sshuttle will set the kernel boot flag
-net.inet.ip.scopedroute to 0, which interferes with OS X
-Internet Sharing and some VPN clients. To reset this flag,
-you can remove any reference to net.inet.ip.scopedroute from
-/Library/Preferences/SystemConfiguration/com.apple.Boot.plist
-and reboot.
-
-
# SEE ALSO
`ssh`(1), `python`(1)
diff --git a/sshuttle/tests/test_firewall.py b/sshuttle/tests/test_firewall.py
index 10734d9..86a017a 100644
--- a/sshuttle/tests/test_firewall.py
+++ b/sshuttle/tests/test_firewall.py
@@ -97,8 +97,6 @@ def test_main(mock_get_method, mock_setup_daemon, mock_rewrite_etc_hosts):
2,
[(2, 24, False, u'1.2.3.0'), (2, 32, True, u'1.2.3.66')],
True),
- call().setup_firewall()(),
- call().setup_firewall()(),
call().setup_firewall(1024, 0, [], 10, [], True),
call().setup_firewall(1025, 0, [], 2, [], True),
]