device: Combine two if statements with identical outcome
[framework/connectivity/connman.git] / plugins / vpn.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  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/gprintf.h>
41
42 #include <connman/provider.h>
43 #include <connman/log.h>
44 #include <connman/rtnl.h>
45 #include <connman/task.h>
46 #include <connman/inet.h>
47
48 #include "vpn.h"
49
50 struct vpn_data {
51         struct connman_provider *provider;
52         char *if_name;
53         unsigned flags;
54         unsigned int watch;
55         unsigned int state;
56         struct connman_task *task;
57 };
58
59 struct vpn_driver_data {
60         const char *name;
61         const char *program;
62         struct vpn_driver *vpn_driver;
63         struct connman_provider_driver provider_driver;
64 };
65
66 GHashTable *driver_hash = NULL;
67
68 static int stop_vpn(struct connman_provider *provider)
69 {
70         struct vpn_data *data = connman_provider_get_data(provider);
71         struct vpn_driver_data *vpn_driver_data;
72         const char *name;
73         struct ifreq ifr;
74         int fd, err;
75
76         if (data == NULL)
77                 return -EINVAL;
78
79         name = connman_provider_get_driver_name(provider);
80         if (name == NULL)
81                 return -EINVAL;
82
83         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
84
85         if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
86                         vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN)
87                 return 0;
88
89         memset(&ifr, 0, sizeof(ifr));
90         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
91         sprintf(ifr.ifr_name, "%s", data->if_name);
92
93         fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
94         if (fd < 0) {
95                 err = -errno;
96                 connman_error("Failed to open /dev/net/tun to device %s: %s",
97                               data->if_name, strerror(errno));
98                 return err;
99         }
100
101         if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
102                 err = -errno;
103                 connman_error("Failed to TUNSETIFF for device %s to it: %s",
104                               data->if_name, strerror(errno));
105                 close(fd);
106                 return err;
107         }
108
109         if (ioctl(fd, TUNSETPERSIST, 0)) {
110                 err = -errno;
111                 connman_error("Failed to set tun device %s nonpersistent: %s",
112                               data->if_name, strerror(errno));
113                 close(fd);
114                 return err;
115         }
116         close(fd);
117         DBG("Killed tun device %s", data->if_name);
118         return 0;
119 }
120
121 void vpn_died(struct connman_task *task, int exit_code, void *user_data)
122 {
123         struct connman_provider *provider = user_data;
124         struct vpn_data *data = connman_provider_get_data(provider);
125         int state = VPN_STATE_FAILURE;
126         enum connman_provider_error ret;
127
128         DBG("provider %p data %p", provider, data);
129
130         if (data == NULL)
131                 goto vpn_exit;
132
133         state = data->state;
134
135         stop_vpn(provider);
136         connman_provider_set_data(provider, NULL);
137
138         if (data->watch != 0) {
139                 connman_provider_unref(provider);
140                 connman_rtnl_remove_watch(data->watch);
141                 data->watch = 0;
142         }
143
144 vpn_exit:
145         if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) {
146                 const char *name;
147                 struct vpn_driver_data *vpn_data = NULL;
148
149                 name = connman_provider_get_driver_name(provider);
150                 if (name != NULL)
151                         vpn_data = g_hash_table_lookup(driver_hash, name);
152
153                 if (vpn_data != NULL &&
154                                 vpn_data->vpn_driver->error_code != NULL)
155                         ret = vpn_data->vpn_driver->error_code(exit_code);
156                 else
157                         ret = CONNMAN_PROVIDER_ERROR_UNKNOWN;
158
159                 connman_provider_indicate_error(provider, ret);
160         } else
161                 connman_provider_set_state(provider,
162                                                 CONNMAN_PROVIDER_STATE_IDLE);
163
164         connman_provider_set_index(provider, -1);
165
166         if (data != NULL) {
167                 connman_provider_unref(data->provider);
168                 g_free(data->if_name);
169                 g_free(data);
170         }
171
172         connman_task_destroy(task);
173 }
174
175 int vpn_set_ifname(struct connman_provider *provider, const char *ifname)
176 {
177         struct vpn_data *data = connman_provider_get_data(provider);
178         int index;
179
180         if (ifname == NULL || data == NULL)
181                 return  -EIO;
182
183         index = connman_inet_ifindex(ifname);
184         if (index < 0)
185                 return  -EIO;
186
187         if (data->if_name != NULL)
188                 g_free(data->if_name);
189
190         data->if_name = (char *)g_strdup(ifname);
191         connman_provider_set_index(provider, index);
192
193         return 0;
194 }
195
196 static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
197 {
198         struct connman_provider *provider = user_data;
199         struct vpn_data *data = connman_provider_get_data(provider);
200
201         if ((data->flags & IFF_UP) != (flags & IFF_UP)) {
202                 if (flags & IFF_UP) {
203                         data->state = VPN_STATE_READY;
204                         connman_provider_set_state(provider,
205                                         CONNMAN_PROVIDER_STATE_READY);
206                 }
207         }
208         data->flags = flags;
209 }
210
211 static DBusMessage *vpn_notify(struct connman_task *task,
212                         DBusMessage *msg, void *user_data)
213 {
214         struct connman_provider *provider = user_data;
215         struct vpn_data *data;
216         struct vpn_driver_data *vpn_driver_data;
217         const char *name;
218         int state, index;
219
220         data = connman_provider_get_data(provider);
221
222         name = connman_provider_get_driver_name(provider);
223         if (name == NULL)
224                 return NULL;
225
226         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
227         if (vpn_driver_data == NULL)
228                 return NULL;
229
230         state = vpn_driver_data->vpn_driver->notify(msg, provider);
231         switch (state) {
232         case VPN_STATE_CONNECT:
233         case VPN_STATE_READY:
234                 index = connman_provider_get_index(provider);
235                 connman_provider_ref(provider);
236                 data->watch = connman_rtnl_add_newlink_watch(index,
237                                                      vpn_newlink, provider);
238                 connman_inet_ifup(index);
239                 break;
240
241         case VPN_STATE_UNKNOWN:
242         case VPN_STATE_IDLE:
243         case VPN_STATE_DISCONNECT:
244         case VPN_STATE_FAILURE:
245                 connman_provider_set_state(provider,
246                                         CONNMAN_PROVIDER_STATE_DISCONNECT);
247                 break;
248
249         case VPN_STATE_AUTH_FAILURE:
250                 connman_provider_indicate_error(provider,
251                                         CONNMAN_PROVIDER_ERROR_AUTH_FAILED);
252                 break;
253         }
254
255         return NULL;
256 }
257
258 static int vpn_create_tun(struct connman_provider *provider)
259 {
260         struct vpn_data *data = connman_provider_get_data(provider);
261         struct ifreq ifr;
262         int i, fd, index;
263         int ret = 0;
264
265         if (data == NULL)
266                 return -EISCONN;
267
268         fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
269         if (fd < 0) {
270                 i = -errno;
271                 connman_error("Failed to open /dev/net/tun: %s",
272                               strerror(errno));
273                 ret = i;
274                 goto exist_err;
275         }
276
277         memset(&ifr, 0, sizeof(ifr));
278         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
279
280         for (i = 0; i < 256; i++) {
281                 sprintf(ifr.ifr_name, "vpn%d", i);
282
283                 if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
284                         break;
285         }
286
287         if (i == 256) {
288                 connman_error("Failed to find available tun device");
289                 close(fd);
290                 ret = -ENODEV;
291                 goto exist_err;
292         }
293
294         data->if_name = (char *)g_strdup(ifr.ifr_name);
295         if (data->if_name == NULL) {
296                 connman_error("Failed to allocate memory");
297                 close(fd);
298                 ret = -ENOMEM;
299                 goto exist_err;
300         }
301
302         if (ioctl(fd, TUNSETPERSIST, 1)) {
303                 i = -errno;
304                 connman_error("Failed to set tun persistent: %s",
305                               strerror(errno));
306                 close(fd);
307                 ret = i;
308                 goto exist_err;
309         }
310
311         close(fd);
312
313         index = connman_inet_ifindex(data->if_name);
314         if (index < 0) {
315                 connman_error("Failed to get tun ifindex");
316                 stop_vpn(provider);
317                 ret = -EIO;
318                 goto exist_err;
319         }
320         connman_provider_set_index(provider, index);
321
322         return 0;
323
324 exist_err:
325         return ret;
326 }
327
328 static int vpn_connect(struct connman_provider *provider)
329 {
330         struct vpn_data *data = connman_provider_get_data(provider);
331         struct vpn_driver_data *vpn_driver_data;
332         const char *name;
333         int ret = 0;
334
335         if (data != NULL)
336                 return -EISCONN;
337
338         data = g_try_new0(struct vpn_data, 1);
339         if (data == NULL)
340                 return -ENOMEM;
341
342         data->provider = connman_provider_ref(provider);
343         data->watch = 0;
344         data->flags = 0;
345         data->task = NULL;
346         data->state = VPN_STATE_IDLE;
347
348         connman_provider_set_data(provider, data);
349
350         name = connman_provider_get_driver_name(provider);
351         if (name == NULL)
352                 return -EINVAL;
353
354         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
355
356         if (vpn_driver_data == NULL || vpn_driver_data->vpn_driver == NULL) {
357                 ret = -EINVAL;
358                 goto exist_err;
359         }
360
361         if (vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
362                 ret = vpn_create_tun(provider);
363                 if (ret < 0)
364                         goto exist_err;
365         }
366
367         data->task = connman_task_create(vpn_driver_data->program);
368
369         if (data->task == NULL) {
370                 ret = -ENOMEM;
371                 stop_vpn(provider);
372                 goto exist_err;
373         }
374
375         if (connman_task_set_notify(data->task, "notify",
376                                         vpn_notify, provider)) {
377                 ret = -ENOMEM;
378                 stop_vpn(provider);
379                 connman_task_destroy(data->task);
380                 data->task = NULL;
381                 goto exist_err;
382         }
383
384         ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
385                                                         data->if_name);
386         if (ret < 0) {
387                 stop_vpn(provider);
388                 connman_task_destroy(data->task);
389                 data->task = NULL;
390                 goto exist_err;
391         }
392
393         DBG("%s started with dev %s",
394                 vpn_driver_data->provider_driver.name, data->if_name);
395
396         data->state = VPN_STATE_CONNECT;
397
398         return -EINPROGRESS;
399
400 exist_err:
401         connman_provider_set_index(provider, -1);
402         connman_provider_set_data(provider, NULL);
403         connman_provider_unref(data->provider);
404         g_free(data->if_name);
405         g_free(data);
406
407         return ret;
408 }
409
410 static int vpn_probe(struct connman_provider *provider)
411 {
412         return 0;
413 }
414
415 static int vpn_disconnect(struct connman_provider *provider)
416 {
417         struct vpn_data *data = connman_provider_get_data(provider);
418         struct vpn_driver_data *vpn_driver_data;
419         const char *name;
420
421         DBG("disconnect provider %p:", provider);
422
423         if (data == NULL)
424                 return 0;
425
426         name = connman_provider_get_driver_name(provider);
427         if (name == NULL)
428                 return 0;
429
430         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
431         if (vpn_driver_data->vpn_driver->disconnect)
432                 vpn_driver_data->vpn_driver->disconnect();
433
434         if (data->watch != 0) {
435                 connman_provider_unref(provider);
436                 connman_rtnl_remove_watch(data->watch);
437                 data->watch = 0;
438         }
439
440         data->state = VPN_STATE_DISCONNECT;
441         connman_task_stop(data->task);
442
443         return 0;
444 }
445
446 static int vpn_remove(struct connman_provider *provider)
447 {
448         struct vpn_data *data;
449
450         data = connman_provider_get_data(provider);
451         if (data == NULL)
452                 return 0;
453
454         if (data->watch != 0) {
455                 connman_provider_unref(provider);
456                 connman_rtnl_remove_watch(data->watch);
457                 data->watch = 0;
458         }
459
460         connman_task_stop(data->task);
461
462         g_usleep(G_USEC_PER_SEC);
463         stop_vpn(provider);
464         return 0;
465 }
466
467 static int vpn_save (struct connman_provider *provider, GKeyFile *keyfile)
468 {
469         struct vpn_driver_data *vpn_driver_data;
470         const char *name;
471
472         name = connman_provider_get_driver_name(provider);
473         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
474         if (vpn_driver_data != NULL &&
475                         vpn_driver_data->vpn_driver->save != NULL)
476                 return vpn_driver_data->vpn_driver->save(provider, keyfile);
477
478         return 0;
479 }
480
481 int vpn_register(const char *name, struct vpn_driver *vpn_driver,
482                         const char *program)
483 {
484         struct vpn_driver_data *data;
485
486         data = g_try_new0(struct vpn_driver_data, 1);
487         if (data == NULL)
488                 return -ENOMEM;
489
490         data->name = name;
491         data->program = program;
492
493         data->vpn_driver = vpn_driver;
494
495         data->provider_driver.name = name;
496         data->provider_driver.disconnect = vpn_disconnect;
497         data->provider_driver.connect = vpn_connect;
498         data->provider_driver.probe = vpn_probe;
499         data->provider_driver.remove = vpn_remove;
500         data->provider_driver.save = vpn_save;
501
502         if (driver_hash == NULL)
503                 driver_hash = g_hash_table_new_full(g_str_hash,
504                                                         g_str_equal,
505                                                         NULL, g_free);
506
507         if (driver_hash == NULL) {
508                 connman_error("driver_hash not initialized for %s", name);
509                 g_free(data);
510                 return -ENOMEM;
511         }
512
513         g_hash_table_replace(driver_hash, (char *)name, data);
514
515         connman_provider_driver_register(&data->provider_driver);
516
517         return 0;
518 }
519
520 void vpn_unregister(const char *name)
521 {
522         struct vpn_driver_data *data;
523
524         data = g_hash_table_lookup(driver_hash, name);
525         if (data == NULL)
526                 return;
527
528         connman_provider_driver_unregister(&data->provider_driver);
529
530         g_hash_table_remove(driver_hash, name);
531
532         if (g_hash_table_size(driver_hash) == 0)
533                 g_hash_table_destroy(driver_hash);
534 }