Ns-3/linux tunnel experiments
[nepi.git] / src / nepi / resources / linux / scripts / linux-ns3-fd-udp-connect.py
1 import base64
2 import errno
3 import os
4 import time
5 import signal
6 import socket
7 import tunchannel
8 import passfd
9
10 from optparse import OptionParser
11
12 # Trak SIGTERM, and set global termination flag instead of dying
13 TERMINATE = []
14 def _finalize(sig,frame):
15     global TERMINATE
16     TERMINATE.append(None)
17 signal.signal(signal.SIGTERM, _finalize)
18
19 # SIGUSR1 suspends forwading, SIGUSR2 resumes forwarding
20 SUSPEND = []
21 def _suspend(sig,frame):
22     global SUSPEND
23     if not SUSPEND:
24         SUSPEND.append(None)
25 signal.signal(signal.SIGUSR1, _suspend)
26
27 def _resume(sig,frame):
28     global SUSPEND
29     if SUSPEND:
30         SUSPEND.remove(None)
31 signal.signal(signal.SIGUSR2, _resume)
32
33 def get_options():
34     usage = ("usage: %prog -a <address> "
35             "-b <bwlimit> -c <cipher> -k <cipher-key> -q <txqueuelen> " 
36             "-l <local-port-file> -r <remote-port-file> -H <remote-host> "
37             "-R <ret-file> ")
38     
39     parser = OptionParser(usage = usage)
40
41     parser.add_option("-a", "--address", dest="address",
42         help="Socket address to send file descriptor to", type="str")
43
44     parser.add_option("-b", "--bwlimit", dest="bwlimit",
45         help="Specifies the interface's emulated bandwidth in bytes ",
46         default=None, type="int")
47     parser.add_option("-q", "--txqueuelen", dest="txqueuelen",
48         help="Specifies the interface's transmission queue length. ",
49         default=1000, type="int")
50     parser.add_option("-c", "--cipher", dest="cipher",
51         help="Cipher to encript communication. "
52             "One of PLAIN, AES, Blowfish, DES, DES3. ",
53         default=None, type="str")
54     parser.add_option("-k", "--cipher-key", dest="cipher_key",
55         help="Specify a symmetric encryption key with which to protect "
56             "packets across the tunnel. python-crypto must be installed "
57             "on the system." ,
58         default=None, type="str")
59
60     parser.add_option("-l", "--local-port-file", dest="local_port_file",
61         help = "File where to store the local binded UDP port number ", 
62         default = "local_port_file", type="str")
63     parser.add_option("-r", "--remote-port-file", dest="remote_port_file",
64         help = "File where to read the remote UDP port number to connect to", 
65         default = "remote_port_file", type="str")
66     parser.add_option("-H", "--remote-host", dest="remote_host",
67         help = "Remote host IP", default = "remote_host", type="str")
68     parser.add_option("-R", "--ret-file", dest="ret_file",
69         help = "File where to store return code (success of connection) ", 
70         default = "ret_file", type="str")
71
72     (options, args) = parser.parse_args()
73        
74     address = base64.b64decode(options.address)
75
76     return ( address, 
77             options.local_port_file, options.remote_port_file, 
78             options.remote_host, options.ret_file, options.bwlimit, 
79             options.cipher, options.cipher_key, options.txqueuelen )
80
81 if __name__ == '__main__':
82
83     (address, local_port_file, remote_port_file, remote_host, ret_file, 
84             bwlimit, cipher, cipher_key, txqueuelen) = get_options()
85
86     # Create a local socket to stablish the tunnel connection
87     hostaddr = socket.gethostbyname(socket.gethostname())
88     rsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
89     rsock.bind((hostaddr, 0))
90     (local_host, local_port) = rsock.getsockname()
91
92     # Save local port information to file
93     f = open(local_port_file, 'w')
94     f.write("%d\n" % local_port)
95     f.close()
96
97     # Wait until remote port information is available
98     while not os.path.exists(remote_port_file):
99         time.sleep(2)
100
101     remote_port = ''
102     # Read remote port from file
103     # Try until something is read...
104     # xxx: There seems to be a weird behavior where
105     #       even if the file exists and had the port number,
106     #       the read operation returns empty string!
107     #       Maybe a race condition?
108     for i in xrange(10):
109         f = open(remote_port_file, 'r')
110         remote_port = f.read()
111         f.close()
112
113         if remote_port:
114             break
115         
116         time.sleep(2)
117     
118     remote_port = remote_port.strip()
119     remote_port = int(remote_port)
120
121     # Connect local socket to remote port
122     rsock.connect((remote_host, remote_port))
123     remote = os.fdopen(rsock.fileno(), 'r+b', 0)
124
125     # create local socket to pass to fd-net-device
126     lsock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
127     lsock.bind("")
128     lsock.getsockname()
129     local = os.fdopen(lsock.fileno(), 'r+b', 0)
130
131     # pass local socket to fd-net-device
132     sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
133     sock.connect(address)
134     passfd.sendfd(sock, local, '0')
135
136     # TODO: Test connectivity!    
137
138     # Create a ret_file to indicate success
139     f = open(ret_file, 'w')
140     f.write("0")
141     f.close()
142
143     # Establish tunnel
144     tunchannel.tun_fwd(local, remote,
145         with_pi = False, # No PI headers 
146         ether_mode = IFF_TAP, # Ns-3 generates ethernet pkts
147         udp = True,
148         cipher_key = cipher_key,
149         cipher = cipher,
150         TERMINATE = TERMINATE,
151         SUSPEND = SUSPEND,
152         tunqueue = txqueuelen,
153         tunkqueue = 500,
154         bwlimit = bwlimit
155     ) 
156