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