Skip to content

Commit c18d9e3

Browse files
committed
test_p2p will muck with the local network stack
To allow us to bind to other addresses besides 127.0.0.1. We'll need something similar for IPv6, but for now we only need it for IPv4 on MacOS
1 parent 7515a9e commit c18d9e3

2 files changed

Lines changed: 85 additions & 0 deletions

File tree

.github/workflows/macos.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,21 @@ jobs:
6161
run: ./test_connection suite-quick
6262
shell: bash
6363

64+
- name: Setup mock IPs
65+
working-directory: ${{github.workspace}}/build/bin
66+
run: sudo python3 test_p2p.py --setup-mock-ips
67+
shell: bash
68+
6469
- name: Test p2p
6570
working-directory: ${{github.workspace}}/build/bin
6671
run: python3 test_p2p.py --spewlevel=debug --loglevel-p2prendezvous=debug
6772
shell: bash
6873

74+
- name: Cleanup mock IPs
75+
working-directory: ${{github.workspace}}/build/bin
76+
run: sudo python3 test_p2p.py --cleanup-mock-ips
77+
shell: bash
78+
6979
- name: Configure CMake (Release)
7080
run: cmake -B ${{github.workspace}}/build-release
7181
-DCMAKE_BUILD_TYPE=Release

tests/test_p2p.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import subprocess
1111
import threading
1212
import os
13+
import platform
1314
import sys
1415
import copy
1516
import time
@@ -23,10 +24,14 @@
2324
g_p2p_rendezvous_level = None
2425
g_stun_ip = "127.0.100.1"
2526
g_stun_port = 3478
27+
g_setup_mock_ips = False
28+
g_cleanup_mock_ips = False
2629

2730
def 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
4865
class 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+
167232
def _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

283348
ParseArgs()
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
286361
stun_server_script = './stun_server.py'
287362
if not os.path.exists( stun_server_script ):

0 commit comments

Comments
 (0)