1
0
mirror of https://github.com/dtaht/unicast-extensions.git synced 2024-05-11 05:55:07 +00:00
Files
Seth Schoen 4db08f113b Comments
2020-10-16 11:51:37 -07:00

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)