diff options
author | Avery Pennarun <apenwarr@gmail.com> | 2012-01-08 18:18:42 -0500 |
---|---|---|
committer | Avery Pennarun <apenwarr@gmail.com> | 2012-01-08 19:01:18 -0500 |
commit | 4c1a505e37f6a0c59acfd347c290ab9d3258f6a7 (patch) | |
tree | dc9ddb146876945fce54ecbe55095a335e1644ed | |
parent | 41d1f73dc2f68369b6831ac492988e071ec2bfbd (diff) |
firewall.py: workaround MacOS 10.7 Lion bug.
On top of the bug that already existed in 10.6, Lion also makes the sysctl
needed to fix the problem into a read-only variable, so we have to actually
change it at kernel boot time and force people to reboot. Nice job, Apple.
-rw-r--r-- | firewall.py | 94 |
1 files changed, 79 insertions, 15 deletions
diff --git a/firewall.py b/firewall.py index 98f0fe0..83ff6dc 100644 --- a/firewall.py +++ b/firewall.py @@ -6,6 +6,12 @@ from helpers import * # python doesn't have a definition for this IPPROTO_DIVERT = 254 +# return values from sysctl_set +SUCCESS = 0 +SAME = 1 +FAILED = -1 +NONEXIST = -2 + def nonfatal(func, *args): try: @@ -135,6 +141,48 @@ def _fill_oldctls(prefix): raise Fatal('%r returned no data' % (argv,)) +KERNEL_FLAGS_PATH = '/Library/Preferences/SystemConfiguration/com.apple.Boot' +KERNEL_FLAGS_NAME = 'Kernel Flags' +def _defaults_read_kernel_flags(): + argv = ['defaults', 'read', KERNEL_FLAGS_PATH, KERNEL_FLAGS_NAME] + debug1('>> %s\n' % ' '.join(argv)) + p = ssubprocess.Popen(argv, stdout = ssubprocess.PIPE) + flagstr = p.stdout.read().strip() + rv = p.wait() + if rv: + raise Fatal('%r returned %d' % (argv, rv)) + flags = flagstr and flagstr.split(' ') or [] + return flags + + +def _defaults_write_kernel_flags(flags): + flagstr = ' '.join(flags) + argv = ['defaults', 'write', KERNEL_FLAGS_PATH, KERNEL_FLAGS_NAME, + flagstr] + debug1('>> %s\n' % ' '.join(argv)) + rv = ssubprocess.call(argv) + if rv: + raise Fatal('%r returned %d' (argv, rv)) + argv = ['plutil', '-convert', 'xml1', KERNEL_FLAGS_PATH + '.plist'] + debug1('>> %s\n' % ' '.join(argv)) + rv = ssubprocess.call(argv) + if rv: + raise Fatal('%r returned %d' (argv, rv)) + + + +def defaults_write_kernel_flag(name, val): + flags = _defaults_read_kernel_flags() + found = 0 + for i in range(len(flags)): + if flags[i].startswith('%s=' % name): + found += 1 + flags[i] = '%s=%s' % (name, val) + if not found: + flags.insert(0, '%s=%s' % (name, val)) + _defaults_write_kernel_flags(flags) + + def _sysctl_set(name, val): argv = ['sysctl', '-w', '%s=%s' % (name, val)] debug1('>> %s\n' % ' '.join(argv)) @@ -150,20 +198,24 @@ def sysctl_set(name, val, permanent=False): _fill_oldctls(PREFIX) if not (name in _oldctls): debug1('>> No such sysctl: %r\n' % name) - return False + return NONEXIST 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 + if val == oldval: + return SAME + + rv = _sysctl_set(name, val) + if rv != 0: + return FAILED + if 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 SUCCESS def _udp_unpack(p): @@ -222,8 +274,8 @@ def do_ipfw(port, dnsport, subnets): if subnets or dnsport: sysctl_set('net.inet.ip.fw.enable', 1) - changed = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True) - if changed: + changeflag = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True) + if changeflag == SUCCESS: log("\n" " WARNING: ONE-TIME NETWORK DISRUPTION:\n" " =====================================\n" @@ -234,6 +286,18 @@ def do_ipfw(port, dnsport, subnets): "ethernet port) NOW, then restart sshuttle. The fix is\n" "permanent; you only have to do this once.\n\n") sys.exit(1) + elif changeflag == FAILED: + log('Updating kernel boot flags.\n') + defaults_write_kernel_flag('net.inet.ip.scopedroute', 0) + log("\n" + " YOU MUST REBOOT TO USE SSHUTTLE\n" + " ===============================\n" + "sshuttle has changed a MacOS kernel boot-time setting\n" + "to work around a bug in MacOS 10.7 Lion. You will need\n" + "to reboot before it takes effect. You only have to\n" + "do this once.\n\n") + sys.exit(1) + ipfw('add', sport, 'check-state', 'ip', 'from', 'any', 'to', 'any') |