30ca66b234c304194b718cfd506b3196af4ffa80
[nepi.git] / src / nepi / resources / planetlab / scripts / pl-vif-create.py
1 #
2 #    NEPI, a framework to manage network experiments
3 #    Copyright (C) 2013 INRIA
4 #
5 #    This program is free software: you can redistribute it and/or modify
6 #    it under the terms of the GNU General Public License version 2 as
7 #    published by the Free Software Foundation;
8 #
9 #    This program is distributed in the hope that it will be useful,
10 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 #    GNU General Public License for more details.
13 #
14 #    You should have received a copy of the GNU General Public License
15 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 #
17 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
18
19 import base64
20 import errno
21 import passfd
22 import socket
23 import vsys
24 from optparse import OptionParser
25
26 STOP_MSG = "STOP"
27 PASSFD_MSG = "PASSFD"
28
29 def create_socket(socket_name):
30     sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
31     sock.bind(socket_name)
32     return sock
33
34 def recv_msg(conn):
35     msg = []
36     chunk = ''
37
38     while '\n' not in chunk:
39         try:
40             chunk = conn.recv(1024)
41         except (OSError, socket.error), e:
42             if e[0] != errno.EINTR:
43                 raise
44             # Ignore eintr errors
45             continue
46
47         if chunk:
48             msg.append(chunk)
49         else:
50             # empty chunk = EOF
51             break
52
53     msg = ''.join(msg).split('\n')[0]
54     # The message might have arguments that will be appended
55     # as a '|' separated list after the message type
56     args = msg.split("|")
57     msg = args.pop(0)
58
59     dmsg = base64.b64decode(msg)
60     dargs = []
61     for arg in args:
62         darg = base64.b64decode(arg)
63         dargs.append(darg.rstrip())
64
65     return (dmsg.rstrip(), dargs)
66
67 def send_reply(conn, reply):
68     encoded = base64.b64encode(reply)
69     conn.send("%s\n" % encoded)
70
71 def stop_action():
72     return "STOP-ACK"
73
74 def passfd_action(fd, args):
75     """ Sends the file descriptor associated to the TAP device 
76     to another process through a unix socket.
77     """
78     address = args.pop(0)
79     print address
80     sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
81     sock.connect(address)
82     passfd.sendfd(sock, fd, '0')
83     return "PASSFD-ACK"
84
85 def get_options():
86     usage = ("usage: %prog -t <vif-type> -a <ip4-address> -n <net-prefix> "
87         "-s <snat> -p <pointopoint> -q <txqueuelen> -f <vif-name-file> "
88         "-S <socket-name>")
89     
90     parser = OptionParser(usage = usage)
91
92     parser.add_option("-t", "--vif-type", dest="vif_type",
93         help = "Virtual interface type. Either IFF_TAP or IFF_TUN. "
94             "Defaults to IFF_TAP. ", type="str")
95
96     parser.add_option("-a", "--ip4-address", dest="ip4_address",
97         help = "IPv4 address to assign to interface. It must belong to the "
98             "network segment owned by the slice, given by the vsys_vnet tag. ",
99         type="str")
100
101     parser.add_option("-n", "--net-prefix", dest="net_prefix",
102         help = "IPv4 network prefix for the interface. It must be the one "
103             "given by the slice's vsys_vnet tag. ",
104         type="int")
105
106     parser.add_option("-s", "--snat", dest="snat", default = False,
107         action="store_true", help="Enable SNAT for the interface")
108
109     parser.add_option("-p", "--pointopoint", dest="pointopoint",
110         help = "Peer end point for the interface  ", default = None,
111         type="str")
112
113     parser.add_option("-q", "--txqueuelen", dest="txqueuelen",
114         help = "Size of transmision queue. Defaults to 0.",
115         default = 0,
116         type="int")
117
118     parser.add_option("-f", "--vif-name-file", dest="vif_name_file",
119         help = "File to store the virtual interface name assigned by the OS", 
120         default = "vif_name", type="str")
121
122     parser.add_option("-S", "--socket-name", dest="socket_name",
123         help = "Name for the unix socket used to interact with this process", 
124         type="str")
125
126     (options, args) = parser.parse_args()
127     
128     vif_type = vsys.IFF_TAP
129     if options.vif_type and options.vif_type == "IFF_TUN":
130         vif_type = vsys.IFF_TUN
131
132     return (vif_type, options.ip4_address, options.net_prefix, 
133             options.snat, options.pointopoint, options.txqueuelen,
134             options.vif_name_file, options.socket_name)
135
136 if __name__ == '__main__':
137
138     (vif_type, ip4_address, net_prefix, snat, pointopoint, 
139             txqueuelen, vif_name_file, socket_name) = get_options()
140
141     (fd, vif_name) = vsys.fd_tuntap(vif_type)
142   
143     vsys.vif_up(vif_name, ip4_address, net_prefix, snat = snat, 
144             pointopoint = pointopoint, txqueuelen = txqueuelen) 
145      
146     # Saving interface name to vif_name_file
147     f = open(vif_name_file, 'w')
148     f.write(vif_name)
149     f.close()
150
151     # create unix socket to receive instructions
152     sock = create_socket(socket_name)
153     sock.listen(0)
154
155     # wait for messages to arrive and process them
156     stop = False
157
158     while not stop:
159         conn, addr = sock.accept()
160         conn.settimeout(5)
161
162         while not stop:
163             try:
164                 (msg, args) = recv_msg(conn)
165             except socket.timeout, e:
166                 # Ingore time-out
167                 continue
168
169             if not msg:
170                 # Ignore - connection lost
171                 break
172
173             if msg == STOP_MSG:
174                 stop = True
175                 reply = stop_action()
176             elif msg == PASSFD_MSG:
177                 reply = passfd_action(fd, args)
178
179             try:
180                 send_reply(conn, reply)
181             except socket.error:
182                 break
183