vpn: Add support to allow ppp tunnelling
[platform/upstream/connman.git] / plugins / vpn.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #define _GNU_SOURCE
27 #include <string.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <stdio.h>
32 #include <errno.h>
33 #include <sys/ioctl.h>
34 #include <sys/types.h>
35 #include <linux/if_tun.h>
36 #include <net/if.h>
37
38 #include <dbus/dbus.h>
39
40 #include <glib/ghash.h>
41 #include <glib/gprintf.h>
42
43 #include <connman/provider.h>
44 #include <connman/log.h>
45 #include <connman/rtnl.h>
46 #include <connman/task.h>
47 #include <connman/inet.h>
48
49 #include "vpn.h"
50
51 struct vpn_data {
52         struct connman_provider *provider;
53         char *if_name;
54         unsigned flags;
55         unsigned int watch;
56         unsigned int state;
57         struct connman_task *task;
58 };
59
60 struct vpn_driver_data {
61         const char *name;
62         const char *program;
63         struct vpn_driver *vpn_driver;
64         struct connman_provider_driver provider_driver;
65 };
66
67 GHashTable *driver_hash = NULL;
68
69 static int stop_vpn(struct connman_provider *provider)
70 {
71         struct vpn_data *data = connman_provider_get_data(provider);
72         struct vpn_driver_data *vpn_driver_data;
73         const char *name;
74         struct ifreq ifr;
75         int fd, err;
76
77         if (data == NULL)
78                 return -EINVAL;
79
80         name = connman_provider_get_driver_name(provider);
81         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
82
83         if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
84                         vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN)
85                 return 0;
86
87         memset(&ifr, 0, sizeof(ifr));
88         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
89         sprintf(ifr.ifr_name, "%s", data->if_name);
90
91         fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
92         if (fd < 0) {
93                 err = -errno;
94                 connman_error("Failed to open /dev/net/tun to device %s: %s",
95                               data->if_name, strerror(errno));
96                 return err;
97         }
98
99         if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
100                 err = -errno;
101                 connman_error("Failed to TUNSETIFF for device %s to it: %s",
102                               data->if_name, strerror(errno));
103                 close(fd);
104                 return err;
105         }
106
107         if (ioctl(fd, TUNSETPERSIST, 0)) {
108                 err = -errno;
109                 connman_error("Failed to set tun device %s nonpersistent: %s",
110                               data->if_name, strerror(errno));
111                 close(fd);
112                 return err;
113         }
114         close(fd);
115         DBG("Killed tun device %s", data->if_name);
116         return 0;
117 }
118
119 void vpn_died(struct connman_task *task, int exit_code, void *user_data)
120 {
121         struct connman_provider *provider = user_data;
122         struct vpn_data *data = connman_provider_get_data(provider);
123         int state = VPN_STATE_FAILURE;
124         enum connman_provider_error ret;
125
126         DBG("provider %p data %p", provider, data);
127
128         if (data == NULL)
129                 goto vpn_exit;
130
131         state = data->state;
132
133         stop_vpn(provider);
134         connman_provider_set_data(provider, NULL);
135         connman_rtnl_remove_watch(data->watch);
136
137 vpn_exit:
138         if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) {
139                 const char *name;
140                 struct vpn_driver_data *vpn_data;
141
142                 name = connman_provider_get_driver_name(provider);
143                 vpn_data = g_hash_table_lookup(driver_hash, name);
144                 if (vpn_data != NULL &&
145                                 vpn_data->vpn_driver->error_code != NULL)
146                         ret = vpn_data->vpn_driver->error_code(exit_code);
147                 else
148                         ret = CONNMAN_PROVIDER_ERROR_UNKNOWN;
149
150                 connman_provider_indicate_error(provider, ret);
151         } else
152                 connman_provider_set_state(provider,
153                                                 CONNMAN_PROVIDER_STATE_IDLE);
154
155         connman_provider_set_index(provider, -1);
156         connman_provider_unref(data->provider);
157
158         g_free(data->if_name);
159         g_free(data);
160
161         connman_task_destroy(task);
162 }
163
164 int vpn_set_ifname(struct connman_provider *provider, const char *ifname)
165 {
166         struct vpn_data *data = connman_provider_get_data(provider);
167         int index;
168
169         if (ifname == NULL || data == NULL)
170                 return  -EIO;
171
172         index = connman_inet_ifindex(ifname);
173         if (index < 0)
174                 return  -EIO;
175
176         if (data->if_name != NULL)
177                 g_free(data->if_name);
178
179         data->if_name = (char *)g_strdup(ifname);
180         connman_provider_set_index(provider, index);
181
182         return 0;
183 }
184
185 static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
186 {
187         struct connman_provider *provider = user_data;
188         struct vpn_data *data = connman_provider_get_data(provider);
189
190         if ((data->flags & IFF_UP) != (flags & IFF_UP)) {
191                 if (flags & IFF_UP) {
192                         data->state = VPN_STATE_READY;
193                         connman_provider_set_state(provider,
194                                         CONNMAN_PROVIDER_STATE_READY);
195                 }
196         }
197         data->flags = flags;
198 }
199
200 static void vpn_notify(struct connman_task *task,
201                         DBusMessage *msg, void *user_data)
202 {
203         struct connman_provider *provider = user_data;
204         struct vpn_data *data;
205         struct vpn_driver_data *vpn_driver_data;
206         const char *name;
207         int state, index;
208
209         data = connman_provider_get_data(provider);
210
211         name = connman_provider_get_driver_name(provider);
212         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
213         if (vpn_driver_data == NULL)
214                 return;
215
216         state = vpn_driver_data->vpn_driver->notify(msg, provider);
217         switch (state) {
218         case VPN_STATE_CONNECT:
219         case VPN_STATE_READY:
220                 index = connman_provider_get_index(provider);
221                 data->watch = connman_rtnl_add_newlink_watch(index,
222                                                      vpn_newlink, provider);
223                 connman_inet_ifup(index);
224                 break;
225
226         case VPN_STATE_UNKNOWN:
227         case VPN_STATE_IDLE:
228         case VPN_STATE_DISCONNECT:
229         case VPN_STATE_FAILURE:
230                 connman_provider_set_state(provider,
231                                         CONNMAN_PROVIDER_STATE_DISCONNECT);
232                 break;
233
234         case VPN_STATE_AUTH_FAILURE:
235                 connman_provider_indicate_error(provider,
236                                         CONNMAN_PROVIDER_ERROR_AUTH_FAILED);
237                 break;
238         }
239 }
240
241 static int vpn_create_tun(struct connman_provider *provider)
242 {
243         struct vpn_data *data = connman_provider_get_data(provider);
244         struct ifreq ifr;
245         int i, fd, index;
246         int ret = 0;
247
248         if (data == NULL)
249                 return -EISCONN;
250
251         fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
252         if (fd < 0) {
253                 i = -errno;
254                 connman_error("Failed to open /dev/net/tun: %s",
255                               strerror(errno));
256                 ret = i;
257                 goto exist_err;
258         }
259
260         memset(&ifr, 0, sizeof(ifr));
261         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
262
263         for (i = 0; i < 256; i++) {
264                 sprintf(ifr.ifr_name, "vpn%d", i);
265
266                 if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
267                         break;
268         }
269
270         if (i == 256) {
271                 connman_error("Failed to find available tun device");
272                 close(fd);
273                 ret = -ENODEV;
274                 goto exist_err;
275         }
276
277         data->if_name = (char *)g_strdup(ifr.ifr_name);
278         if (data->if_name == NULL) {
279                 connman_error("Failed to allocate memory");
280                 close(fd);
281                 ret = -ENOMEM;
282                 goto exist_err;
283         }
284
285         if (ioctl(fd, TUNSETPERSIST, 1)) {
286                 i = -errno;
287                 connman_error("Failed to set tun persistent: %s",
288                               strerror(errno));
289                 close(fd);
290                 ret = i;
291                 goto exist_err;
292         }
293
294         close(fd);
295
296         index = connman_inet_ifindex(data->if_name);
297         if (index < 0) {
298                 connman_error("Failed to get tun ifindex");
299                 stop_vpn(provider);
300                 ret = -EIO;
301                 goto exist_err;
302         }
303         connman_provider_set_index(provider, index);
304
305         return 0;
306
307 exist_err:
308         return ret;
309 }
310
311 static int vpn_connect(struct connman_provider *provider)
312 {
313         struct vpn_data *data = connman_provider_get_data(provider);
314         struct vpn_driver_data *vpn_driver_data;
315         const char *name;
316         int ret = 0;
317
318         if (data != NULL)
319                 return -EISCONN;
320
321         data = g_try_new0(struct vpn_data, 1);
322         if (data == NULL)
323                 return -ENOMEM;
324
325         data->provider = connman_provider_ref(provider);
326         data->watch = 0;
327         data->flags = 0;
328         data->task = NULL;
329         data->state = VPN_STATE_IDLE;
330
331         connman_provider_set_data(provider, data);
332
333         name = connman_provider_get_driver_name(provider);
334         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
335
336         if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
337                 vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
338
339                 ret = vpn_create_tun(provider);
340                 if (ret < 0)
341                         goto exist_err;
342         }
343
344         data->task = connman_task_create(vpn_driver_data->program);
345
346         if (data->task == NULL) {
347                 ret = -ENOMEM;
348                 stop_vpn(provider);
349                 goto exist_err;
350         }
351
352         if (connman_task_set_notify(data->task, "notify",
353                                         vpn_notify, provider)) {
354                 ret = -ENOMEM;
355                 stop_vpn(provider);
356                 connman_task_destroy(data->task);
357                 data->task = NULL;
358                 goto exist_err;
359         }
360
361         ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
362                                                         data->if_name);
363         if (ret < 0) {
364                 stop_vpn(provider);
365                 connman_task_destroy(data->task);
366                 data->task = NULL;
367                 goto exist_err;
368         }
369
370         DBG("%s started with dev %s",
371                 vpn_driver_data->provider_driver.name, data->if_name);
372
373         data->state = VPN_STATE_CONNECT;
374
375         return -EINPROGRESS;
376
377 exist_err:
378         connman_provider_set_index(provider, -1);
379         connman_provider_set_data(provider, NULL);
380         connman_provider_unref(data->provider);
381         g_free(data->if_name);
382         g_free(data);
383
384         return ret;
385 }
386
387 static int vpn_probe(struct connman_provider *provider)
388 {
389         return 0;
390 }
391
392 static int vpn_disconnect(struct connman_provider *provider)
393 {
394         struct vpn_data *data = connman_provider_get_data(provider);
395         struct vpn_driver_data *vpn_driver_data;
396         const char *name;
397
398         DBG("disconnect provider %p:", provider);
399
400         if (data == NULL)
401                 return 0;
402
403         name = connman_provider_get_driver_name(provider);
404         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
405         if (vpn_driver_data->vpn_driver->disconnect)
406                 vpn_driver_data->vpn_driver->disconnect();
407
408         if (data->watch != 0)
409                 connman_rtnl_remove_watch(data->watch);
410
411         data->watch = 0;
412         data->state = VPN_STATE_DISCONNECT;
413         connman_task_stop(data->task);
414
415         return 0;
416 }
417
418 static int vpn_remove(struct connman_provider *provider)
419 {
420         struct vpn_data *data;
421
422         data = connman_provider_get_data(provider);
423         if (data == NULL)
424                 return 0;
425
426         if (data->watch != 0)
427                 connman_rtnl_remove_watch(data->watch);
428         data->watch = 0;
429         connman_task_stop(data->task);
430
431         g_usleep(G_USEC_PER_SEC);
432         stop_vpn(provider);
433         return 0;
434 }
435
436 static int vpn_save (struct connman_provider *provider, GKeyFile *keyfile)
437 {
438         struct vpn_driver_data *vpn_driver_data;
439         const char *name;
440
441         name = connman_provider_get_driver_name(provider);
442         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
443         if (vpn_driver_data != NULL &&
444                         vpn_driver_data->vpn_driver->save != NULL)
445                 return vpn_driver_data->vpn_driver->save(provider, keyfile);
446
447         return 0;
448 }
449
450 int vpn_register(const char *name, struct vpn_driver *vpn_driver,
451                         const char *program)
452 {
453         struct vpn_driver_data *data;
454
455         data = g_try_new0(struct vpn_driver_data, 1);
456         if (data == NULL)
457                 return -ENOMEM;
458
459         data->name = name;
460         data->program = program;
461
462         data->vpn_driver = vpn_driver;
463
464         data->provider_driver.name = name;
465         data->provider_driver.disconnect = vpn_disconnect;
466         data->provider_driver.connect = vpn_connect;
467         data->provider_driver.probe = vpn_probe;
468         data->provider_driver.remove = vpn_remove;
469         data->provider_driver.save = vpn_save;
470
471         if (driver_hash == NULL) {
472                 driver_hash = g_hash_table_new_full(g_str_hash,
473                                                         g_str_equal,
474                                                         NULL, g_free);
475         }
476
477         g_hash_table_insert(driver_hash, (char *)name, data);
478
479         connman_provider_driver_register(&data->provider_driver);
480
481         return 0;
482 }
483
484 void vpn_unregister(const char *name)
485 {
486         struct vpn_driver_data *data;
487
488         data = g_hash_table_lookup(driver_hash, name);
489         if (data == NULL)
490                 return;
491
492         connman_provider_driver_unregister(&data->provider_driver);
493
494         g_hash_table_remove(driver_hash, name);
495
496         if (g_hash_table_size(driver_hash) == 0)
497                 g_hash_table_destroy(driver_hash);
498 }