1010import subprocess
1111import threading
1212import os
13+ import platform
1314import sys
1415import copy
1516import time
2324g_p2p_rendezvous_level = None
2425g_stun_ip = "127.0.100.1"
2526g_stun_port = 3478
27+ g_setup_mock_ips = False
28+ g_cleanup_mock_ips = False
2629
2730def ParseArgs ():
2831 global g_spew_level
2932 global g_p2p_rendezvous_level
33+ global g_setup_mock_ips
34+ global g_cleanup_mock_ips
3035
3136 parser = argparse .ArgumentParser ()
3237 parser .add_argument (
@@ -40,9 +45,21 @@ def ParseArgs():
4045 choices = [ 'msg' , 'verbose' , 'debug' ],
4146 help = 'Control detail level specifically for P2P rendezvous-related spew.'
4247 )
48+ parser .add_argument (
49+ '--setup-mock-ips' ,
50+ action = 'store_true' ,
51+ help = 'Add any addresses needed by the mock network that are not already bindable. Exits without running tests.'
52+ )
53+ parser .add_argument (
54+ '--cleanup-mock-ips' ,
55+ action = 'store_true' ,
56+ help = 'Remove addresses added by --setup-mock-ips. Exits without running tests.'
57+ )
4358 args = parser .parse_args ()
4459 g_spew_level = args .spewlevel
4560 g_p2p_rendezvous_level = args .loglevel_p2prendezvous
61+ g_setup_mock_ips = args .setup_mock_ips
62+ g_cleanup_mock_ips = args .cleanup_mock_ips
4663
4764# Thread class that runs a process and captures its output
4865class RunProcessInThread (threading .Thread ):
@@ -164,6 +181,54 @@ def StartClientInThread( role, local, remote, extra_args=[] ):
164181_DEAD_INT = '127.0.9.2' # address used for disabled adapters
165182_CLI_SAME_LAN = '127.0.1.3' # client on the same /24 private LAN as _SRV_INT
166183
184+ # All addresses that the mock network needs to be able to bind sockets to.
185+ _ALL_MOCK_ADDRS = [
186+ g_stun_ip ,
187+ _SRV_GW , _CLI_GW , _SRV_GW2 , _CLI_GW2 ,
188+ _SRV_INT , _CLI_INT , _SRV_INT2 , _CLI_INT2 , _DEAD_INT , _CLI_SAME_LAN ,
189+ ]
190+
191+ def _IsAddressBindable ( addr ):
192+ import socket
193+ try :
194+ s = socket .socket ( socket .AF_INET , socket .SOCK_DGRAM )
195+ s .bind ( ( addr , 0 ) )
196+ s .close ()
197+ return True
198+ except OSError :
199+ return False
200+
201+ def SetupMockIPs ():
202+ """Add loopback aliases for every mock address that is not already bindable."""
203+ if platform .system () != 'Darwin' :
204+ print ( "Nothing to do on this platform." )
205+ return
206+ for addr in _ALL_MOCK_ADDRS :
207+ subprocess .run ( [ 'ifconfig' , 'lo0' , 'alias' , addr ], check = True )
208+ print ( "Added %d loopback alias(es)." % len ( _ALL_MOCK_ADDRS ) )
209+
210+ def CleanupMockIPs ():
211+ """Remove loopback aliases added by --setup-mock-ips."""
212+ if platform .system () != 'Darwin' :
213+ print ( "Nothing to do on this platform." )
214+ return
215+ for addr in _ALL_MOCK_ADDRS :
216+ if addr == '127.0.0.1' :
217+ continue
218+ subprocess .run ( [ 'ifconfig' , 'lo0' , '-alias' , addr ], check = False )
219+ print ( "Removed loopback aliases." )
220+
221+ def CheckMockIPsBindable ():
222+ """Verify all mock addresses are bindable; exit with an error if any are not."""
223+ missing = [ addr for addr in _ALL_MOCK_ADDRS if not _IsAddressBindable ( addr ) ]
224+ if not missing :
225+ return
226+ print ( "ERROR: the following addresses required by the mock network are not bindable:" )
227+ for addr in missing :
228+ print ( " " + addr )
229+ print ( "Run 'sudo %s --setup-mock-ips' to add the required loopback aliases." % sys .argv [0 ] )
230+ sys .exit (1 )
231+
167232def _nat ( internal , gateway , nat_type ):
168233 # Gateway must be declared before the adapter that uses it
169234 return [ '--mock-gateway' , gateway , '--mock-nat' , nat_type , '--mock-adapter' , internal ]
@@ -282,6 +347,16 @@ def ClientServerTest( server_extra_args=[], client_extra_args=[], expected_route
282347
283348ParseArgs ()
284349
350+ if g_setup_mock_ips :
351+ SetupMockIPs ()
352+ sys .exit (0 )
353+
354+ if g_cleanup_mock_ips :
355+ CleanupMockIPs ()
356+ sys .exit (0 )
357+
358+ CheckMockIPsBindable ()
359+
285360# Find and start the STUN server
286361stun_server_script = './stun_server.py'
287362if not os .path .exists ( stun_server_script ):
0 commit comments