mirror of
https://github.com/dtaht/unicast-extensions.git
synced 2024-05-11 05:55:07 +00:00
126 lines
3.7 KiB
Python
126 lines
3.7 KiB
Python
#!/usr/bin/env python
|
|
|
|
# Python prototype of simple nettest-like functionality to check whether
|
|
# or not one netns can reach another with a TCP connection -- but
|
|
# without any sleep in the successful case. (Unsuccessful cases may fail
|
|
# immediately or after a specified socket timeout, depending on the
|
|
# specific failure reason.)
|
|
|
|
# Exits with success or failure, depending on whether the connection
|
|
# worked.
|
|
|
|
import socket
|
|
import signal
|
|
import time
|
|
import os
|
|
import ctypes
|
|
import sys
|
|
|
|
# the environment this is looking for was created with
|
|
# ip netns add wow; ip netns add yow
|
|
# ip link add foo netns wow type veth peer name bar netns yow
|
|
# ip -n wow address add 192.168.11.3/24 dev foo; ip -n wow link set foo up
|
|
# ip -n yow address add 192.168.11.5/24 dev bar; ip -n yow link set bar up
|
|
|
|
parent_ns="wow"
|
|
parent_ip="192.168.11.3"
|
|
port=12345
|
|
child_ns="yow"
|
|
ret=0
|
|
done=False
|
|
socket.setdefaulttimeout(3)
|
|
|
|
def netns(s):
|
|
print("in netns")
|
|
# Attempt to join the network namespace s by means of
|
|
# a system-level call to open(2) followed by setns(2).
|
|
libc = ctypes.CDLL("/lib/x86_64-linux-gnu/libc.so.6")
|
|
ns_location = "/run/netns/" + s
|
|
fd = libc.open(ns_location, 0)
|
|
if fd < 0:
|
|
raise ValueError
|
|
result = libc.setns(fd, 0)
|
|
print(ctypes.get_errno())
|
|
if result < 0:
|
|
raise OSError
|
|
libc.close(fd)
|
|
|
|
def parent(child_pid, ns=parent_ns):
|
|
global ret
|
|
netns(ns)
|
|
print('PARENT: Switched to network namespace {}'.format(ns))
|
|
# TODO: Check return codes instead of relying on exceptions
|
|
# (since some failures may be reported in return code)
|
|
# Timeout is a slower path in case of a failure on the client
|
|
# side or in the connectivity between the client and server.
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.bind((parent_ip, port))
|
|
sock.listen(1)
|
|
print('PARENT: port bound; going to signal the child')
|
|
os.kill(child_pid, signal.SIGUSR1)
|
|
print('PARENT: signal sent')
|
|
a, b = sock.accept()
|
|
data = a.recv(100)
|
|
print('PARENT: -------> Received "{}" <------ (success)'.format(data))
|
|
print('PARENT: Going to wait for child...')
|
|
pid, code = os.wait()
|
|
print('PARENT: Done waiting with {}'.format(code))
|
|
ret += code
|
|
|
|
def handler(signum, frame, ns=child_ns):
|
|
global done
|
|
global ret
|
|
if signum == signal.SIGUSR1:
|
|
try:
|
|
print('CHILD: Received the signal')
|
|
netns(ns)
|
|
print('CHILD: Switched to network namespace {}'.format(ns))
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
print('CHILD: Connecting')
|
|
sock.connect((parent_ip, port))
|
|
print('CHILD: Sending data')
|
|
sock.send("hello world")
|
|
print('CHILD: Data sent')
|
|
except:
|
|
ret += 1
|
|
else:
|
|
print('CHILD: I got some other signal')
|
|
ret += 1
|
|
done=True
|
|
|
|
def child(ppid):
|
|
while not done:
|
|
# While waiting for the handler to execute, make sure the parent
|
|
# is still alive!
|
|
try:
|
|
os.kill(ppid, 0)
|
|
except OSError:
|
|
print('CHILD: The parent died')
|
|
raise OSError
|
|
pass
|
|
print('CHILD: going to exit')
|
|
|
|
if os.getuid():
|
|
print("need to be root for netns manipulation")
|
|
sys.exit(1)
|
|
|
|
signal.signal(signal.SIGUSR1, handler) # set before fork to avoid race!
|
|
ppid = os.getpid()
|
|
pid = os.fork()
|
|
if pid:
|
|
print('PARENT: child PID is {}'.format(pid))
|
|
try:
|
|
parent(pid)
|
|
except:
|
|
print('PARENT: exception encountered')
|
|
ret += 1
|
|
else:
|
|
print('CHILD: parent PID is {}'.format(ppid))
|
|
try:
|
|
child(ppid)
|
|
except:
|
|
print('CHILD: exception encountered')
|
|
ret += 1
|
|
|
|
sys.exit(ret)
|