vpn: Set authentication failure error code properly in provider
[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 #include <string.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <sys/ioctl.h>
33 #include <sys/types.h>
34 #include <linux/if_tun.h>
35 #include <net/if.h>
36
37 #include <dbus/dbus.h>
38
39 #include <glib/ghash.h>
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 kill_tun(char *tun_name)
69 {
70         struct ifreq ifr;
71         int fd, err;
72
73         memset(&ifr, 0, sizeof(ifr));
74         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
75         sprintf(ifr.ifr_name, "%s", tun_name);
76
77         fd = open("/dev/net/tun", O_RDWR);
78         if (fd < 0) {
79                 err = -errno;
80                 connman_error("Failed to open /dev/net/tun to device %s: %s",
81                               tun_name, strerror(errno));
82                 return err;
83         }
84
85         if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
86                 err = -errno;
87                 connman_error("Failed to TUNSETIFF for device %s to it: %s",
88                               tun_name, strerror(errno));
89                 close(fd);
90                 return err;
91         }
92
93         if (ioctl(fd, TUNSETPERSIST, 0)) {
94                 err = -errno;
95                 connman_error("Failed to set tun device %s nonpersistent: %s",
96                               tun_name, strerror(errno));
97                 close(fd);
98                 return err;
99         }
100         close(fd);
101         DBG("Killed tun device %s", tun_name);
102         return 0;
103 }
104
105 void vpn_died(struct connman_task *task, int exit_code, void *user_data)
106 {
107         struct connman_provider *provider = user_data;
108         struct vpn_data *data = connman_provider_get_data(provider);
109         int state = VPN_STATE_FAILURE;
110         enum connman_provider_error ret;
111
112         DBG("provider %p data %p", provider, data);
113
114         if (data == NULL)
115                 goto vpn_exit;
116
117         state = data->state;
118
119         kill_tun(data->if_name);
120         connman_provider_set_data(provider, NULL);
121         connman_rtnl_remove_watch(data->watch);
122
123 vpn_exit:
124         if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) {
125                 const char *name;
126                 struct vpn_driver_data *vpn_data;
127
128                 name = connman_provider_get_driver_name(provider);
129                 vpn_data = g_hash_table_lookup(driver_hash, name);
130                 if (vpn_data != NULL &&
131                                 vpn_data->vpn_driver->error_code != NULL)
132                         ret = vpn_data->vpn_driver->error_code(exit_code);
133                 else
134                         ret = CONNMAN_PROVIDER_ERROR_UNKNOWN;
135
136                 connman_provider_indicate_error(provider, ret);
137         } else
138                 connman_provider_set_state(provider,
139                                                 CONNMAN_PROVIDER_STATE_IDLE);
140
141         connman_provider_set_index(provider, -1);
142         connman_provider_unref(data->provider);
143         g_free(data);
144
145         connman_task_destroy(task);
146 }
147
148 static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
149 {
150         struct connman_provider *provider = user_data;
151         struct vpn_data *data = connman_provider_get_data(provider);
152
153         if ((data->flags & IFF_UP) != (flags & IFF_UP)) {
154                 if (flags & IFF_UP) {
155                         data->state = VPN_STATE_READY;
156                         connman_provider_set_state(provider,
157                                         CONNMAN_PROVIDER_STATE_READY);
158                 }
159         }
160         data->flags = flags;
161 }
162
163 static void vpn_notify(struct connman_task *task,
164                         DBusMessage *msg, void *user_data)
165 {
166         struct connman_provider *provider = user_data;
167         struct vpn_data *data;
168         struct vpn_driver_data *vpn_driver_data;
169         const char *name;
170         int state, index;
171
172         data = connman_provider_get_data(provider);
173
174         name = connman_provider_get_driver_name(provider);
175         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
176         if (vpn_driver_data == NULL)
177                 return;
178
179         state = vpn_driver_data->vpn_driver->notify(msg, provider);
180         switch (state) {
181         case VPN_STATE_CONNECT:
182         case VPN_STATE_READY:
183                 index = connman_provider_get_index(provider);
184                 data->watch = connman_rtnl_add_newlink_watch(index,
185                                                      vpn_newlink, provider);
186                 connman_inet_ifup(index);
187                 break;
188
189         case VPN_STATE_UNKNOWN:
190         case VPN_STATE_IDLE:
191         case VPN_STATE_DISCONNECT:
192         case VPN_STATE_FAILURE:
193                 connman_provider_set_state(provider,
194                                         CONNMAN_PROVIDER_STATE_DISCONNECT);
195                 break;
196
197         case VPN_STATE_AUTH_FAILURE:
198                 connman_provider_indicate_error(provider,
199                                         CONNMAN_PROVIDER_ERROR_AUTH_FAILED);
200                 break;
201         }
202 }
203
204 static int vpn_connect(struct connman_provider *provider)
205 {
206         struct vpn_data *data = connman_provider_get_data(provider);
207         struct vpn_driver_data *vpn_driver_data;
208         struct ifreq ifr;
209         const char *name;
210         int i, fd, index;
211         int ret = 0;
212
213         if (data != NULL)
214                 return -EISCONN;
215
216         data = g_try_new0(struct vpn_data, 1);
217         if (data == NULL)
218                 return -ENOMEM;
219
220         data->provider = connman_provider_ref(provider);
221         data->watch = 0;
222         data->flags = 0;
223         data->task = NULL;
224         data->state = VPN_STATE_IDLE;
225
226         connman_provider_set_data(provider, data);
227
228         name = connman_provider_get_driver_name(provider);
229         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
230
231         fd = open("/dev/net/tun", O_RDWR);
232         if (fd < 0) {
233                 i = -errno;
234                 connman_error("Failed to open /dev/net/tun: %s",
235                               strerror(errno));
236                 ret = i;
237                 goto exist_err;
238         }
239
240         memset(&ifr, 0, sizeof(ifr));
241         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
242
243         for (i = 0; i < 256; i++) {
244                 sprintf(ifr.ifr_name, "vpn%d", i);
245
246                 if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
247                         break;
248         }
249
250         if (i == 256) {
251                 connman_error("Failed to find available tun device");
252                 close(fd);
253                 ret = -ENODEV;
254                 goto exist_err;
255         }
256
257         data->if_name = (char *)g_strdup(ifr.ifr_name);
258         if (data->if_name == NULL) {
259                 connman_error("Failed to allocate memory");
260                 close(fd);
261                 ret = -ENOMEM;
262                 goto exist_err;
263         }
264
265         if (ioctl(fd, TUNSETPERSIST, 1)) {
266                 i = -errno;
267                 connman_error("Failed to set tun persistent: %s",
268                               strerror(errno));
269                 close(fd);
270                 ret = i;
271                 goto exist_err;
272         }
273
274         close(fd);
275
276         index = connman_inet_ifindex(data->if_name);
277         if (index < 0) {
278                 connman_error("Failed to get tun ifindex");
279                 kill_tun(data->if_name);
280                 ret = -EIO;
281                 goto exist_err;
282         }
283         connman_provider_set_index(provider, index);
284
285         data->task = connman_task_create(vpn_driver_data->program);
286
287         if (data->task == NULL) {
288                 ret = -ENOMEM;
289                 kill_tun(data->if_name);
290                 goto exist_err;
291         }
292
293         if (connman_task_set_notify(data->task, "notify",
294                                         vpn_notify, provider)) {
295                 ret = -ENOMEM;
296                 kill_tun(data->if_name);
297                 connman_task_destroy(data->task);
298                 data->task = NULL;
299                 goto exist_err;
300         }
301
302         ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
303                                                         data->if_name);
304         if (ret < 0) {
305                 kill_tun(data->if_name);
306                 connman_task_destroy(data->task);
307                 data->task = NULL;
308                 goto exist_err;
309         }
310
311         DBG("%s started with dev %s",
312                 vpn_driver_data->provider_driver.name, data->if_name);
313
314         data->state = VPN_STATE_CONNECT;
315
316         return -EINPROGRESS;
317
318 exist_err:
319         connman_provider_set_index(provider, -1);
320         connman_provider_set_data(provider, NULL);
321         connman_provider_unref(data->provider);
322         g_free(data);
323
324         return ret;
325 }
326
327 static int vpn_probe(struct connman_provider *provider)
328 {
329         return 0;
330 }
331
332 static int vpn_disconnect(struct connman_provider *provider)
333 {
334         struct vpn_data *data = connman_provider_get_data(provider);
335         struct vpn_driver_data *vpn_driver_data;
336         const char *name;
337
338         DBG("disconnect provider %p:", provider);
339
340         if (data == NULL)
341                 return 0;
342
343         name = connman_provider_get_driver_name(provider);
344         vpn_driver_data = g_hash_table_lookup(driver_hash, name);
345         if (vpn_driver_data->vpn_driver->disconnect)
346                 vpn_driver_data->vpn_driver->disconnect();
347
348         if (data->watch != 0)
349                 connman_rtnl_remove_watch(data->watch);
350
351         data->watch = 0;
352         data->state = VPN_STATE_DISCONNECT;
353         connman_task_stop(data->task);
354
355         return 0;
356 }
357
358 static int vpn_remove(struct connman_provider *provider)
359 {
360         struct vpn_data *data;
361
362         data = connman_provider_get_data(provider);
363         connman_provider_set_data(provider, NULL);
364         if (data == NULL)
365                 return 0;
366
367         if (data->watch != 0)
368                 connman_rtnl_remove_watch(data->watch);
369         data->watch = 0;
370         connman_task_stop(data->task);
371
372         g_usleep(G_USEC_PER_SEC);
373         kill_tun(data->if_name);
374         return 0;
375 }
376
377 int vpn_register(const char *name, struct vpn_driver *vpn_driver,
378                         const char *program)
379 {
380         struct vpn_driver_data *data;
381
382         data = g_try_new0(struct vpn_driver_data, 1);
383         if (data == NULL)
384                 return -ENOMEM;
385
386         data->name = name;
387         data->program = program;
388
389         data->vpn_driver = vpn_driver;
390
391         data->provider_driver.name = name;
392         data->provider_driver.disconnect = vpn_disconnect;
393         data->provider_driver.connect = vpn_connect;
394         data->provider_driver.probe = vpn_probe;
395         data->provider_driver.remove = vpn_remove;
396
397         if (driver_hash == NULL) {
398                 driver_hash = g_hash_table_new_full(g_str_hash,
399                                                         g_str_equal,
400                                                         NULL, g_free);
401         }
402
403         g_hash_table_insert(driver_hash, (char *)name, data);
404
405         connman_provider_driver_register(&data->provider_driver);
406
407         return 0;
408 }
409
410 void vpn_unregister(const char *name)
411 {
412         struct vpn_driver_data *data;
413
414         data = g_hash_table_lookup(driver_hash, name);
415         if (data == NULL)
416                 return;
417
418         connman_provider_driver_unregister(&data->provider_driver);
419
420         g_hash_table_remove(driver_hash, name);
421
422         if (g_hash_table_size(driver_hash) == 0)
423                 g_hash_table_destroy(driver_hash);
424 }