all the python scripts are for python2, and fedora31 requires to be specific
[vsys-scripts.git] / slice-context / src / vsys.c
1 /* 
2  * vsys.c: CPython functions to wrap PlanetLab vsys API'
3  *   Alina Quereilhac - 02/09/2012
4  *
5  * Copyright (c) 2012 INRIA
6  *
7  */
8
9 #include <Python.h>
10 #ifndef _GNU_SOURCE
11 # define _GNU_SOURCE
12 #endif
13
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <linux/if_tun.h>
17 #include <linux/ioctl.h>
18 #include <net/if.h>
19 #include <string.h>
20 #include <sys/ioctl.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <unistd.h>
25
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"
33
34 const size_t SIZE = 4096; 
35
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,
38         char *msg);
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);
42
43 /* Python wrapper for _fd_tuntap */
44 static PyObject *
45 fd_tuntap(PyObject *self, PyObject *args) {
46     int if_type, fd, no_pi = 0;
47     char *if_name;
48     PyObject *retval;
49
50     if(!PyArg_ParseTuple(args, "i|i", &if_type, &no_pi))
51         return NULL;
52
53     if((if_name = malloc(SIZE)) == NULL)
54         return PyErr_SetFromErrno(PyExc_OSError);
55
56     // just in case, initialize the buffer to 0
57     memset(if_name, 0, SIZE);
58
59     Py_BEGIN_ALLOW_THREADS;
60     fd = _fd_tuntap(if_type, no_pi, if_name);
61     Py_END_ALLOW_THREADS;
62
63     retval = Py_BuildValue("is", fd, if_name);
64     free(if_name);
65     return retval;
66 }
67
68 /* Python wrapper for _vif_up */
69 static PyObject *
70 vif_up(PyObject *self, PyObject *args) {
71     int ret, snat = 0; //false
72     char *if_name, *ip, *prefix, *msg;
73     PyObject *retval;
74
75     if(!PyArg_ParseTuple(args, "sss|i", &if_name, &ip, &prefix, &snat))
76         return NULL;
77
78     if((msg = malloc(SIZE)) == NULL)
79         return PyErr_SetFromErrno(PyExc_OSError);
80
81     // just in case, initialize the buffer to 0
82     memset(msg, 0, SIZE);
83
84     Py_BEGIN_ALLOW_THREADS;
85     ret = _vif_up(if_name, ip, prefix, snat, msg);
86     Py_END_ALLOW_THREADS;
87
88     retval = Py_BuildValue("is", ret, msg);
89     free(msg);
90     return retval;
91 }
92
93 /* Python wrapper for _vif_down */
94 static PyObject *
95 vif_down(PyObject *self, PyObject *args) {
96     int ret;
97     char *if_name, *msg;
98     PyObject *retval;
99
100     if(!PyArg_ParseTuple(args, "s", &if_name))
101         return NULL;
102
103     if((msg = malloc(SIZE)) == NULL)
104         return PyErr_SetFromErrno(PyExc_OSError);
105
106     // just in case, initialize the buffer to 0
107     memset(msg, 0, SIZE);
108
109     Py_BEGIN_ALLOW_THREADS;
110     ret = _vif_down(if_name, msg);
111     Py_END_ALLOW_THREADS;
112
113     retval = Py_BuildValue("is", ret, msg);
114     free(msg);
115     return retval;
116 }
117
118 /* Python wrapper for _vroute */
119 static PyObject *
120 vroute(PyObject *self, PyObject *args) {
121     int ret;
122     char *action, *network, *prefix, *host, *device, *msg;
123     PyObject *retval;
124
125     if(!PyArg_ParseTuple(args, "sssss", &action, &network, &prefix, &host, &device))
126         return NULL;
127
128     if((msg = malloc(SIZE)) == NULL)
129         return PyErr_SetFromErrno(PyExc_OSError);
130
131     // just in case, initialize the buffer to 0
132     memset(msg, 0, SIZE);
133
134     Py_BEGIN_ALLOW_THREADS;
135     ret = _vroute(action, network, prefix, host, device, msg);
136     Py_END_ALLOW_THREADS;
137
138     retval = Py_BuildValue("is", ret, msg);
139     free(msg);
140     return retval;
141 }
142
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}
149 };
150
151 PyMODINIT_FUNC init_vsys(void) {
152     PyObject *m;
153     m = Py_InitModule("_vsys", methods);
154     if (m == NULL)
155         return;
156 }
157
158 /*
159  * _fifo_push(): Pushes parameters into a fifo.
160  *   
161  *   Parameters:
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
166  *
167  *  Return value:
168  *    On success, _fifo_push returns 0.
169  *    On error, a negative integer is returned.
170  *
171  */
172 static int _fifo_push (const char *fifo_in, const char *fifo_out, 
173         const char *input, char *msg) {
174   FILE *in;
175   FILE *out;
176   int nbytes;
177
178   in = fopen (fifo_in, "a");
179
180   if (in == NULL){
181     snprintf (msg, SIZE, "Failed to open FIFO %s", fifo_in);
182     return  -1;
183   }
184
185   out = fopen (fifo_out, "r");
186
187   if (out == NULL){
188     snprintf (msg, SIZE, "Failed to open FIFO %s", fifo_out);
189     return  -2;
190   }
191
192   // send inputs to the fifo_in
193   fprintf (in, "%s", input);
194
195   // force flush and close the fifo_in so the program on the other side
196   // can process input.
197   fclose (in);
198
199   nbytes = fread(msg, SIZE, 1, out);
200  
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 ...
204       return -3;
205   }
206
207   fclose (out);
208
209   return 0;
210 }
211
212 /*
213  * _receive_fd(): Receives the file descriptor associated to the virtual
214  *  device
215  *   
216  *  Parameters:
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 
220  *
221  *  Return value:
222  *    On success, _receive_fd returns the file descriptor associated to the device.
223  *    On error, a negative integer is returned.
224  *
225  */
226 static int _receive_fd(int vsys_sock, char *if_name) {
227   struct msghdr msg;
228   struct iovec iov;
229   size_t ccmsg[CMSG_SPACE (sizeof(int)) / sizeof(size_t)];
230   struct cmsghdr *cmsg;
231   int rv;
232
233   // Use IOV to read interface name
234   iov.iov_base = if_name;
235   iov.iov_len = IFNAMSIZ;
236
237   msg.msg_name = 0;
238   msg.msg_namelen = 0;
239   msg.msg_iov = &iov;
240   msg.msg_iovlen = 1;
241
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);
246
247   while (((rv = recvmsg (vsys_sock, &msg, 0)) == -1) && errno == EINTR)
248     {}
249
250   cmsg = CMSG_FIRSTHDR (&msg);
251   if (cmsg->cmsg_type != SCM_RIGHTS){
252     snprintf (if_name, SIZE, "Got control message of unknown type");
253     return -1;
254   }
255
256   int* retfd  = (int*)CMSG_DATA (cmsg);
257   return *retfd;
258 }
259
260 /*
261  * _fd_tuntap(): Creates a TAP or TUN device in PlanetLab, and returns the
262  * device name and the associated file descriptor.
263  *   
264  * Parameters:
265  *   if_type:     the type of virtual device. Either IFF_TAP (0x0001) or
266  *                      IFF_TUN (0x0002)
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
271  *
272  * Return value:
273  *   On success, _fd_tuntap returns the file descriptor associated to the device.
274  *   On error, a negative integer is returned.
275  *
276  */
277 int _fd_tuntap(int if_type, int no_pi, char *if_name) {
278   int vsys_sock, fd;
279   struct sockaddr_un addr;
280
281   vsys_sock = socket (AF_UNIX, SOCK_STREAM, 0);
282
283   if (vsys_sock == -1){
284     snprintf (if_name, SIZE, "%s", strerror(errno));
285     return -2;
286   }
287
288   memset (&addr, 0, sizeof(struct sockaddr_un));
289
290   addr.sun_family = AF_UNIX;
291   strncpy (addr.sun_path, VSYS_TUNTAP, sizeof(addr.sun_path) - 1);
292
293   int ret = connect (vsys_sock, (struct sockaddr *) &addr, 
294     sizeof(struct sockaddr_un));
295
296   if (ret == -1){
297     snprintf (if_name, SIZE, "%s", strerror(errno));
298     return -3;
299   }
300  
301   // send vif type (either TAP or TUN)
302   ret = send (vsys_sock, &if_type, sizeof(if_type), 0);
303
304   if (ret != sizeof(if_type)){
305     snprintf (if_name, SIZE, "Could not send parameter to vsys control socket");
306     return -4; 
307   }
308
309   // receive the file descriptor associated to the virtual interface
310   fd = _receive_fd (vsys_sock, if_name);
311
312   if (fd < 0){
313     snprintf (if_name, SIZE, "Invalid file descriptor");
314     return -5;
315   }
316  
317 /**
318 *  XXX: For now unsupported functionality
319 *
320   struct ifreq ifr;
321   ret = ioctl (fd, TUNGETIFF, (void *)&ifr);
322   if (ret == -1){
323     close (fd);
324     snprintf (if_name, SIZE, "%s", strerror(errno));
325     return -6;
326   }
327   
328   // flag IFF_NO_PI: don't add the extra PI header
329   if (no_pi){
330     ifr.ifr_flags |= IFF_NO_PI;
331   }
332
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;
337
338   ret = ioctl (fd, TUNSETIFF, (void *)&ifr);
339   if (ret == -1){
340     close (fd);
341     snprintf (if_name, SIZE, "%s", strerror(errno));
342     return -7;
343   }
344 */
345
346   return fd;
347
348 }
349
350 /*
351  * _vif_up(): Configures a virtual interface with the values given by the user
352  * and sets its state UP 
353  *
354  * Parameters:
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
360  *
361  * Return value:
362  *   On success, return 0. 
363  *   On error, a negative integer is returned.
364  *
365  */
366 int _vif_up (const char *if_name, const char *ip, const char *prefix, 
367         int snat, char *msg) {
368
369   char input[SIZE]; // big buffer
370
371   snprintf (input, SIZE, "%s\n%s\n%s\nsnat=%d\n", if_name, ip, prefix, snat);
372
373   return _fifo_push (VSYS_VIFUP_IN, VSYS_VIFUP_OUT, input, msg);
374
375 }
376
377 /*
378  * _vif_down(): Sets the state of a virtual interface DOWN 
379  *
380  * Parameters:
381  *   if_name:   the name of the virtual interface 
382  *   msg:       buffer to return error to user
383  *
384  * Return value:
385  *   On success, return 0. 
386  *   On error, a negative integer is returned.
387  *
388  */
389 int _vif_down (const char *if_name, char *msg) {
390
391   char input[SIZE];
392
393   snprintf(input, SIZE, "%s\n", if_name);
394
395   return _fifo_push (VSYS_VIFDOWN_IN, VSYS_VIFDOWN_OUT, input, msg);
396
397 }
398
399 /*
400  * vroute() : Adds or removes routes on PlanetLab virtual interfaces (TAP/TUN).
401  *
402  *   Note that all networks and gateways must belong to the virtual 
403  *   network segment associated to the vsys_vnet tag for the slice.
404  *
405  *  Parameters:
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
412  *
413  *  Return value:
414  *    On success, vroute returns 0.
415  *    On error, a negative integer is returned.
416  *
417  */
418 int _vroute (const char *action, const char *network, const char *prefix, 
419     const char *host, const char *device, char *msg) {
420
421   char input[SIZE];
422
423   snprintf(input, SIZE, "%s %s/%s gw %s %s\n", action, network, prefix, host, device);
424
425   return _fifo_push (VSYS_VROUTE_IN, VSYS_VROUTE_OUT, input, msg);
426
427 }
428