Skip to content

Commit 4c1a505

Browse files
committed
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.
1 parent 41d1f73 commit 4c1a505

File tree

1 file changed

+79
-15
lines changed

1 file changed

+79
-15
lines changed

firewall.py

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
# python doesn't have a definition for this
77
IPPROTO_DIVERT = 254
88

9+
# return values from sysctl_set
10+
SUCCESS = 0
11+
SAME = 1
12+
FAILED = -1
13+
NONEXIST = -2
14+
915

1016
def nonfatal(func, *args):
1117
try:
@@ -135,6 +141,48 @@ def _fill_oldctls(prefix):
135141
raise Fatal('%r returned no data' % (argv,))
136142

137143

144+
KERNEL_FLAGS_PATH = '/Library/Preferences/SystemConfiguration/com.apple.Boot'
145+
KERNEL_FLAGS_NAME = 'Kernel Flags'
146+
def _defaults_read_kernel_flags():
147+
argv = ['defaults', 'read', KERNEL_FLAGS_PATH, KERNEL_FLAGS_NAME]
148+
debug1('>> %s\n' % ' '.join(argv))
149+
p = ssubprocess.Popen(argv, stdout = ssubprocess.PIPE)
150+
flagstr = p.stdout.read().strip()
151+
rv = p.wait()
152+
if rv:
153+
raise Fatal('%r returned %d' % (argv, rv))
154+
flags = flagstr and flagstr.split(' ') or []
155+
return flags
156+
157+
158+
def _defaults_write_kernel_flags(flags):
159+
flagstr = ' '.join(flags)
160+
argv = ['defaults', 'write', KERNEL_FLAGS_PATH, KERNEL_FLAGS_NAME,
161+
flagstr]
162+
debug1('>> %s\n' % ' '.join(argv))
163+
rv = ssubprocess.call(argv)
164+
if rv:
165+
raise Fatal('%r returned %d' (argv, rv))
166+
argv = ['plutil', '-convert', 'xml1', KERNEL_FLAGS_PATH + '.plist']
167+
debug1('>> %s\n' % ' '.join(argv))
168+
rv = ssubprocess.call(argv)
169+
if rv:
170+
raise Fatal('%r returned %d' (argv, rv))
171+
172+
173+
174+
def defaults_write_kernel_flag(name, val):
175+
flags = _defaults_read_kernel_flags()
176+
found = 0
177+
for i in range(len(flags)):
178+
if flags[i].startswith('%s=' % name):
179+
found += 1
180+
flags[i] = '%s=%s' % (name, val)
181+
if not found:
182+
flags.insert(0, '%s=%s' % (name, val))
183+
_defaults_write_kernel_flags(flags)
184+
185+
138186
def _sysctl_set(name, val):
139187
argv = ['sysctl', '-w', '%s=%s' % (name, val)]
140188
debug1('>> %s\n' % ' '.join(argv))
@@ -150,20 +198,24 @@ def sysctl_set(name, val, permanent=False):
150198
_fill_oldctls(PREFIX)
151199
if not (name in _oldctls):
152200
debug1('>> No such sysctl: %r\n' % name)
153-
return False
201+
return NONEXIST
154202
oldval = _oldctls[name]
155-
if val != oldval:
156-
rv = _sysctl_set(name, val)
157-
if rv==0 and permanent:
158-
debug1('>> ...saving permanently in /etc/sysctl.conf\n')
159-
f = open('/etc/sysctl.conf', 'a')
160-
f.write('\n'
161-
'# Added by sshuttle\n'
162-
'%s=%s\n' % (name, val))
163-
f.close()
164-
else:
165-
_changedctls.append(name)
166-
return True
203+
if val == oldval:
204+
return SAME
205+
206+
rv = _sysctl_set(name, val)
207+
if rv != 0:
208+
return FAILED
209+
if permanent:
210+
debug1('>> ...saving permanently in /etc/sysctl.conf\n')
211+
f = open('/etc/sysctl.conf', 'a')
212+
f.write('\n'
213+
'# Added by sshuttle\n'
214+
'%s=%s\n' % (name, val))
215+
f.close()
216+
else:
217+
_changedctls.append(name)
218+
return SUCCESS
167219

168220

169221
def _udp_unpack(p):
@@ -222,8 +274,8 @@ def do_ipfw(port, dnsport, subnets):
222274

223275
if subnets or dnsport:
224276
sysctl_set('net.inet.ip.fw.enable', 1)
225-
changed = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True)
226-
if changed:
277+
changeflag = sysctl_set('net.inet.ip.scopedroute', 0, permanent=True)
278+
if changeflag == SUCCESS:
227279
log("\n"
228280
" WARNING: ONE-TIME NETWORK DISRUPTION:\n"
229281
" =====================================\n"
@@ -234,6 +286,18 @@ def do_ipfw(port, dnsport, subnets):
234286
"ethernet port) NOW, then restart sshuttle. The fix is\n"
235287
"permanent; you only have to do this once.\n\n")
236288
sys.exit(1)
289+
elif changeflag == FAILED:
290+
log('Updating kernel boot flags.\n')
291+
defaults_write_kernel_flag('net.inet.ip.scopedroute', 0)
292+
log("\n"
293+
" YOU MUST REBOOT TO USE SSHUTTLE\n"
294+
" ===============================\n"
295+
"sshuttle has changed a MacOS kernel boot-time setting\n"
296+
"to work around a bug in MacOS 10.7 Lion. You will need\n"
297+
"to reboot before it takes effect. You only have to\n"
298+
"do this once.\n\n")
299+
sys.exit(1)
300+
237301

238302
ipfw('add', sport, 'check-state', 'ip',
239303
'from', 'any', 'to', 'any')

0 commit comments

Comments
 (0)