2 * vsys.c: CPython functions to wrap PlanetLab vsys API'
3 * Alina Quereilhac - 02/09/2012
5 * Copyright (c) 2012 INRIA
16 #include <linux/if_tun.h>
17 #include <linux/ioctl.h>
20 #include <sys/ioctl.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
26 #define VSYS_TUNTAP "/vsys/fd_tuntap.control"
27 #define VSYS_VIFUP_IN "/vsys/vif_up.in"
28 #define VSYS_VIFUP_OUT "/vsys/vif_up.out"
29 #define VSYS_VIFDOWN_IN "/vsys/vif_down.in"
30 #define VSYS_VIFDOWN_OUT "/vsys/vif_down.out"
31 #define VSYS_VROUTE_IN "/vsys/vroute.in"
32 #define VSYS_VROUTE_OUT "/vsys/vroute.out"
34 const size_t SIZE = 4096;
36 int _fd_tuntap(int if_type, int no_pi, char *if_name);
37 int _vif_up(const char *if_name, const char *ip, const char *prefix, int snat,
39 int _vif_down(const char *if_name, char *msg);
40 int _vroute (const char *action, const char *network, const char *prefix,
41 const char *host, const char *device, char *msg);
43 /* Python wrapper for _fd_tuntap */
45 fd_tuntap(PyObject *self, PyObject *args) {
46 int if_type, fd, no_pi = 0;
50 if(!PyArg_ParseTuple(args, "i|i", &if_type, &no_pi))
53 if((if_name = malloc(SIZE)) == NULL)
54 return PyErr_SetFromErrno(PyExc_OSError);
56 // just in case, initialize the buffer to 0
57 memset(if_name, 0, SIZE);
59 Py_BEGIN_ALLOW_THREADS;
60 fd = _fd_tuntap(if_type, no_pi, if_name);
63 retval = Py_BuildValue("is", fd, if_name);
68 /* Python wrapper for _vif_up */
70 vif_up(PyObject *self, PyObject *args) {
71 int ret, snat = 0; //false
72 char *if_name, *ip, *prefix, *msg;
75 if(!PyArg_ParseTuple(args, "sss|i", &if_name, &ip, &prefix, &snat))
78 if((msg = malloc(SIZE)) == NULL)
79 return PyErr_SetFromErrno(PyExc_OSError);
81 // just in case, initialize the buffer to 0
84 Py_BEGIN_ALLOW_THREADS;
85 ret = _vif_up(if_name, ip, prefix, snat, msg);
88 retval = Py_BuildValue("is", ret, msg);
93 /* Python wrapper for _vif_down */
95 vif_down(PyObject *self, PyObject *args) {
100 if(!PyArg_ParseTuple(args, "s", &if_name))
103 if((msg = malloc(SIZE)) == NULL)
104 return PyErr_SetFromErrno(PyExc_OSError);
106 // just in case, initialize the buffer to 0
107 memset(msg, 0, SIZE);
109 Py_BEGIN_ALLOW_THREADS;
110 ret = _vif_down(if_name, msg);
111 Py_END_ALLOW_THREADS;
113 retval = Py_BuildValue("is", ret, msg);
118 /* Python wrapper for _vroute */
120 vroute(PyObject *self, PyObject *args) {
122 char *action, *network, *prefix, *host, *device, *msg;
125 if(!PyArg_ParseTuple(args, "sssss", &action, &network, &prefix, &host, &device))
128 if((msg = malloc(SIZE)) == NULL)
129 return PyErr_SetFromErrno(PyExc_OSError);
131 // just in case, initialize the buffer to 0
132 memset(msg, 0, SIZE);
134 Py_BEGIN_ALLOW_THREADS;
135 ret = _vroute(action, network, prefix, host, device, msg);
136 Py_END_ALLOW_THREADS;
138 retval = Py_BuildValue("is", ret, msg);
143 static PyMethodDef methods[] = {
144 {"fd_tuntap", fd_tuntap, METH_VARARGS, "(fd, if_name) = fd_tuntap(if_type, no_pi = False)"},
145 {"vif_up", vif_up, METH_VARARGS, "(code, msg) = vif_up(if_name, ip, prefix, snat = False)"},
146 {"vif_down", vif_down, METH_VARARGS, " (code, msg) = vif_up(if_name)"},
147 {"vroute", vroute, METH_VARARGS, "(code, msg) = vroute(action, network, prefix, host, device)"},
148 {NULL, NULL, 0, NULL}
151 PyMODINIT_FUNC init_vsys(void) {
153 m = Py_InitModule("_vsys", methods);
159 * _fifo_push(): Pushes parameters into a fifo.
162 * fifo_in: the name of the fifo to write parameters to
163 * fifo_out: the name of the fifo to read error from
164 * input: parameters formated as a string to push into the input fifo
165 * msg: buffer to return error to user
168 * On success, _fifo_push returns 0.
169 * On error, a negative integer is returned.
172 static int _fifo_push (const char *fifo_in, const char *fifo_out,
173 const char *input, char *msg) {
178 in = fopen (fifo_in, "a");
181 snprintf (msg, SIZE, "Failed to open FIFO %s", fifo_in);
185 out = fopen (fifo_out, "r");
188 snprintf (msg, SIZE, "Failed to open FIFO %s", fifo_out);
192 // send inputs to the fifo_in
193 fprintf (in, "%s", input);
195 // force flush and close the fifo_in so the program on the other side
196 // can process input.
199 nbytes = fread(msg, SIZE, 1, out);
201 // the error buffer will not be empty if we read an error
202 if ((nbytes > 0) && (strcmp(msg, "") != 0)) {
203 // an errror was read from the fifo_out ...
213 * _receive_fd(): Receives the file descriptor associated to the virtual
217 * vsys_sock: the vsys control socket which send the fd
218 * if_name: the buffer where the device name assigned by PlanetLab
219 * will be stored after creating the virtual interface
222 * On success, _receive_fd returns the file descriptor associated to the device.
223 * On error, a negative integer is returned.
226 static int _receive_fd(int vsys_sock, char *if_name) {
229 size_t ccmsg[CMSG_SPACE (sizeof(int)) / sizeof(size_t)];
230 struct cmsghdr *cmsg;
233 // Use IOV to read interface name
234 iov.iov_base = if_name;
235 iov.iov_len = IFNAMSIZ;
242 // old BSD implementations should use msg_accrights instead of msg_control.
243 // the interface is different.
244 msg.msg_control = ccmsg;
245 msg.msg_controllen = sizeof(ccmsg);
247 while (((rv = recvmsg (vsys_sock, &msg, 0)) == -1) && errno == EINTR)
250 cmsg = CMSG_FIRSTHDR (&msg);
251 if (cmsg->cmsg_type != SCM_RIGHTS){
252 snprintf (if_name, SIZE, "Got control message of unknown type");
256 int* retfd = (int*)CMSG_DATA (cmsg);
261 * _fd_tuntap(): Creates a TAP or TUN device in PlanetLab, and returns the
262 * device name and the associated file descriptor.
265 * if_type: the type of virtual device. Either IFF_TAP (0x0001) or
267 * no_pi: set flag IFF_NO_PI
268 * if_name: the buffer where the device name assigned by PlanetLab
269 * will be stored after creating the virtual interface.
270 * When an error ocurrs, if_name is used as message buffer
273 * On success, _fd_tuntap returns the file descriptor associated to the device.
274 * On error, a negative integer is returned.
277 int _fd_tuntap(int if_type, int no_pi, char *if_name) {
279 struct sockaddr_un addr;
281 vsys_sock = socket (AF_UNIX, SOCK_STREAM, 0);
283 if (vsys_sock == -1){
284 snprintf (if_name, SIZE, "%s", strerror(errno));
288 memset (&addr, 0, sizeof(struct sockaddr_un));
290 addr.sun_family = AF_UNIX;
291 strncpy (addr.sun_path, VSYS_TUNTAP, sizeof(addr.sun_path) - 1);
293 int ret = connect (vsys_sock, (struct sockaddr *) &addr,
294 sizeof(struct sockaddr_un));
297 snprintf (if_name, SIZE, "%s", strerror(errno));
301 // send vif type (either TAP or TUN)
302 ret = send (vsys_sock, &if_type, sizeof(if_type), 0);
304 if (ret != sizeof(if_type)){
305 snprintf (if_name, SIZE, "Could not send parameter to vsys control socket");
309 // receive the file descriptor associated to the virtual interface
310 fd = _receive_fd (vsys_sock, if_name);
313 snprintf (if_name, SIZE, "Invalid file descriptor");
318 * XXX: For now unsupported functionality
321 ret = ioctl (fd, TUNGETIFF, (void *)&ifr);
324 snprintf (if_name, SIZE, "%s", strerror(errno));
328 // flag IFF_NO_PI: don't add the extra PI header
330 ifr.ifr_flags |= IFF_NO_PI;
333 // flag IFF_ONE_QUEUE: disable normal network device queue and use only the
334 // tun/tap driver internal queue. QoS tools will not be
335 // able to control queueing for the device in this case.
336 ifr.ifr_flags |= IFF_ONE_QUEUE;
338 ret = ioctl (fd, TUNSETIFF, (void *)&ifr);
341 snprintf (if_name, SIZE, "%s", strerror(errno));
351 * _vif_up(): Configures a virtual interface with the values given by the user
352 * and sets its state UP
355 * if_name: the name of the virtual interface
356 * ip: the IP address to be assigned to the interface
357 * prefix: the network prefix associated to the IP address
358 * snat: whether to enable SNAT on the virtual interface.
359 * msg: buffer to return error to user
362 * On success, return 0.
363 * On error, a negative integer is returned.
366 int _vif_up (const char *if_name, const char *ip, const char *prefix,
367 int snat, char *msg) {
369 char input[SIZE]; // big buffer
371 snprintf (input, SIZE, "%s\n%s\n%s\nsnat=%d\n", if_name, ip, prefix, snat);
373 return _fifo_push (VSYS_VIFUP_IN, VSYS_VIFUP_OUT, input, msg);
378 * _vif_down(): Sets the state of a virtual interface DOWN
381 * if_name: the name of the virtual interface
382 * msg: buffer to return error to user
385 * On success, return 0.
386 * On error, a negative integer is returned.
389 int _vif_down (const char *if_name, char *msg) {
393 snprintf(input, SIZE, "%s\n", if_name);
395 return _fifo_push (VSYS_VIFDOWN_IN, VSYS_VIFDOWN_OUT, input, msg);
400 * vroute() : Adds or removes routes on PlanetLab virtual interfaces (TAP/TUN).
402 * Note that all networks and gateways must belong to the virtual
403 * network segment associated to the vsys_vnet tag for the slice.
406 * action: either 'add' or 'del'
407 * network: destintation network
408 * prefix: destination network prefix
409 * host: IP of gateway virtual interface
410 * device: name of the gateway virtual interface
411 * msg: buffer to return error to user
414 * On success, vroute returns 0.
415 * On error, a negative integer is returned.
418 int _vroute (const char *action, const char *network, const char *prefix,
419 const char *host, const char *device, char *msg) {
423 snprintf(input, SIZE, "%s %s/%s gw %s %s\n", action, network, prefix, host, device);
425 return _fifo_push (VSYS_VROUTE_IN, VSYS_VROUTE_OUT, input, msg);