Fix bug to remove vpn services when turning offline mode on
[framework/connectivity/connman.git] / plugins / openconnect.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 <sys/wait.h>
31 #include <stdio.h>
32 #include <errno.h>
33 #include <sys/ioctl.h>
34 #include <linux/if_tun.h>
35 #include <net/if.h>
36 #include <stdint.h>
37
38 #include <glib/garray.h>
39 #include <glib/gerror.h>
40 #include <glib/gmain.h>
41 #include <glib/gspawn.h>
42
43 #define CONNMAN_API_SUBJECT_TO_CHANGE
44 #include <connman/plugin.h>
45 #include <connman/device.h>
46 #include <connman/element.h>
47 #include <connman/provider.h>
48 #include <connman/log.h>
49 #include <connman/element.h>
50 #include <connman/rtnl.h>
51 #include <connman/task.h>
52
53 #include "inet.h"
54
55 enum oc_state {
56         OC_STATE_UNKNOWN       = 0,
57         OC_STATE_IDLE          = 1,
58         OC_STATE_CONNECT       = 2,
59         OC_STATE_READY         = 3,
60         OC_STATE_DISCONNECT    = 4,
61         OC_STATE_FAILURE       = 5,
62 };
63
64 struct oc_data {
65         struct connman_provider *provider;
66         char *if_name;
67         unsigned flags;
68         unsigned int watch;
69         unsigned int state;
70         struct connman_task *task;
71 };
72
73 static int kill_tun(char *tun_name)
74 {
75         struct ifreq ifr;
76         int fd, err;
77
78         memset(&ifr, 0, sizeof(ifr));
79         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
80         sprintf(ifr.ifr_name, "%s", tun_name);
81
82         fd = open("/dev/net/tun", O_RDWR);
83         if (fd < 0) {
84                 err = -errno;
85                 connman_error("Failed to open /dev/net/tun to device %s: %s",
86                               tun_name, strerror(errno));
87                 return err;
88         }
89
90         if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
91                 err = -errno;
92                 connman_error("Failed to TUNSETIFF for device %s to it: %s",
93                               tun_name, strerror(errno));
94                 close(fd);
95                 return err;
96         }
97
98         if (ioctl(fd, TUNSETPERSIST, 0)) {
99                 err = -errno;
100                 connman_error("Failed to set tun device %s nonpersistent: %s",
101                               tun_name, strerror(errno));
102                 close(fd);
103                 return err;
104         }
105         close(fd);
106         DBG("Killed tun device %s", tun_name);
107         return 0;
108 }
109
110 static void openconnect_died(struct connman_task *task, void *user_data)
111 {
112         struct connman_provider *provider = user_data;
113         struct oc_data *data = connman_provider_get_data(provider);
114         int state = data->state;
115
116         DBG("provider %p data %p", provider, data);
117
118         if (!data)
119                 goto oc_exit;
120
121         kill_tun(data->if_name);
122         connman_provider_set_data(provider, NULL);
123         connman_rtnl_remove_watch(data->watch);
124
125  oc_exit:
126         if (state != OC_STATE_READY && state != OC_STATE_DISCONNECT)
127                 connman_provider_set_state(provider,
128                                                 CONNMAN_PROVIDER_STATE_FAILURE);
129         else
130                 connman_provider_set_state(provider,
131                                                 CONNMAN_PROVIDER_STATE_IDLE);
132
133         connman_provider_set_index(provider, -1);
134         connman_provider_unref(data->provider);
135         g_free(data);
136
137         connman_task_destroy(task);
138 }
139
140 static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
141 {
142         struct connman_provider *provider = user_data;
143         struct oc_data *data = connman_provider_get_data(provider);
144
145         if ((data->flags & IFF_UP) != (flags & IFF_UP)) {
146                 if (flags & IFF_UP) {
147                         data->state = OC_STATE_READY;
148                         connman_provider_set_state(provider,
149                                         CONNMAN_PROVIDER_STATE_READY);
150                 }
151         }
152         data->flags = flags;
153 }
154
155 static void openconnect_task_notify(struct connman_task *task,
156                                     DBusMessage *msg, void *user_data)
157 {
158         DBusMessageIter iter, dict;
159         struct connman_provider *provider = user_data;
160         struct oc_data *data;
161         const char *reason, *key, *value;
162         const char *domain = NULL;
163         int index;
164
165         dbus_message_iter_init(msg, &iter);
166
167         dbus_message_iter_get_basic(&iter, &reason);
168         dbus_message_iter_next(&iter);
169
170         if (!provider) {
171                 connman_error("No provider found");
172                 return;
173         }
174
175         data = connman_provider_get_data(provider);
176         if (!data) {
177                 DBG("provider %p no data", provider);
178                 return;
179         }
180
181         if (strcmp(reason, "connect")) {
182                 connman_provider_set_state(provider,
183                                         CONNMAN_PROVIDER_STATE_DISCONNECT);
184                 return;
185         }
186
187         domain = connman_provider_get_string(provider, "VPN.Domain");
188
189         dbus_message_iter_recurse(&iter, &dict);
190
191         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
192                 DBusMessageIter entry;
193
194                 dbus_message_iter_recurse(&dict, &entry);
195                 dbus_message_iter_get_basic(&entry, &key);
196                 dbus_message_iter_next(&entry);
197                 dbus_message_iter_get_basic(&entry, &value);
198
199                 if (strcmp(key, "CISCO_CSTP_OPTIONS"))
200                         DBG("%s = %s", key, value);
201
202                 if (!strcmp(key, "VPNGATEWAY"))
203                         connman_provider_set_string(provider, "Gateway", value);
204
205                 if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
206                         connman_provider_set_string(provider, "Address", value);
207
208                 if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
209                         connman_provider_set_string(provider, "Netmask", value);
210
211                 if (!strcmp(key, "INTERNAL_IP4_DNS"))
212                         connman_provider_set_string(provider, "DNS", value);
213
214                 if (!strcmp(key, "CISCO_PROXY_PAC"))
215                         connman_provider_set_string(provider, "PAC", value);
216
217                 if (domain == NULL && !strcmp(key, "CISCO_DEF_DOMAIN"))
218                         domain = value;
219
220                 dbus_message_iter_next(&dict);
221         }
222
223         index = connman_provider_get_index(provider);
224         connman_provider_set_string(provider, "Domain", domain);
225         data->watch = connman_rtnl_add_newlink_watch(index,
226                                                      vpn_newlink, provider);
227
228         connman_inet_ifup(index);
229 }
230
231 static int oc_connect(struct connman_provider *provider)
232 {
233         struct oc_data *data = connman_provider_get_data(provider);
234         struct ifreq ifr;
235         int oc_fd, fd, i, index;
236         const char *vpnhost, *vpncookie, *cafile, *mtu;
237         int ret = 0;
238
239         if (data != NULL)
240                 return -EISCONN;
241
242         data = g_try_new0(struct oc_data, 1);
243         if (data == NULL)
244                 return -ENOMEM;
245
246         data->provider = connman_provider_ref(provider);
247         data->watch = 0;
248         data->flags = 0;
249         data->task = NULL;
250         data->state = OC_STATE_IDLE;
251
252         connman_provider_set_data(provider, data);
253
254         vpnhost = connman_provider_get_string(provider, "Host");
255         if (!vpnhost) {
256                 connman_error("Host not set; cannot enable VPN");
257                 ret = -EINVAL;
258                 goto exist_err;
259         }
260
261         vpncookie = connman_provider_get_string(provider, "OpenConnect.Cookie");
262         if (!vpncookie) {
263                 connman_error("OpenConnect.Cookie not set; cannot enable VPN");
264                 ret = -EINVAL;
265                 goto exist_err;
266         }
267
268         cafile = connman_provider_get_string(provider, "OpenConnect.CACert");
269         mtu = connman_provider_get_string(provider, "VPN.MTU");
270
271         fd = open("/dev/net/tun", O_RDWR);
272         if (fd < 0) {
273                 i = -errno;
274                 connman_error("Failed to open /dev/net/tun: %s",
275                               strerror(errno));
276                 ret = i;
277                 goto exist_err;
278         }
279
280         memset(&ifr, 0, sizeof(ifr));
281         ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
282
283         for (i = 0; i < 256; i++) {
284                 sprintf(ifr.ifr_name, "vpn%d", i);
285
286                 if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
287                         break;
288         }
289
290         if (i == 256) {
291                 connman_error("Failed to find available tun device");
292                 close(fd);
293                 ret = -ENODEV;
294                 goto exist_err;
295         }
296
297         data->if_name = (char *)g_strdup(ifr.ifr_name);
298         if (!data->if_name) {
299                 ret = -ENOMEM;
300                 goto exist_err;
301         }
302
303         if (ioctl(fd, TUNSETPERSIST, 1)) {
304                 i = -errno;
305                 connman_error("Failed to set tun persistent: %s",
306                               strerror(errno));
307                 close(fd);
308                 ret = i;
309                 goto exist_err;
310         }
311
312         close(fd);
313
314         index = connman_inet_ifindex(data->if_name);
315         if (index < 0) {
316                 connman_error("Failed to get tun ifindex");
317                 kill_tun(data->if_name);
318                 ret = -EIO;
319                 goto exist_err;
320         }
321         connman_provider_set_index(provider, index);
322
323         data->task = connman_task_create(OPENCONNECT);
324
325         if (data->task == NULL) {
326                 ret = -ENOMEM;
327                 kill_tun(data->if_name);
328                 goto exist_err;
329         }
330
331         if (connman_task_set_notify(data->task, "notify",
332                                         openconnect_task_notify, provider)) {
333                 ret = -ENOMEM;
334                 kill_tun(data->if_name);
335                 connman_task_destroy(data->task);
336                 data->task = NULL;
337                 goto exist_err;
338         }
339
340         if (cafile)
341                 connman_task_add_argument(data->task, "--cafile",
342                                                         (char *)cafile);
343         if (mtu)
344                 connman_task_add_argument(data->task, "--mtu", (char *)mtu);
345
346         connman_task_add_argument(data->task, "--syslog", NULL);
347         connman_task_add_argument(data->task, "--cookie-on-stdin", NULL);
348
349         connman_task_add_argument(data->task, "--script",
350                                   SCRIPTDIR "/openconnect-script");
351
352         connman_task_add_argument(data->task, "--interface", data->if_name);
353
354         connman_task_add_argument(data->task, (char *)vpnhost, NULL);
355
356         ret = connman_task_run(data->task, openconnect_died, provider,
357                                &oc_fd, NULL, NULL);
358         if (ret) {
359                 connman_error("Openconnect failed to start");
360                 kill_tun(data->if_name);
361                 ret = -EIO;
362                 connman_task_destroy(data->task);
363                 data->task = NULL;
364                 goto exist_err;
365         }
366
367         DBG("openconnect started with dev %s", data->if_name);
368
369         if (write(oc_fd, vpncookie, strlen(vpncookie)) !=
370             (ssize_t)strlen(vpncookie) ||
371             write(oc_fd, "\n", 1) != 1) {
372                 connman_error("openconnect failed to take cookie on stdin");
373                 connman_provider_set_data(provider, NULL);
374                 connman_task_stop(data->task);
375                 ret = -EIO;
376                 goto exist_err;
377         }
378
379         data->state = OC_STATE_CONNECT;
380
381         return -EINPROGRESS;
382
383  exist_err:
384         connman_provider_set_index(provider, -1);
385         connman_provider_set_data(provider, NULL);
386         connman_provider_unref(data->provider);
387         g_free(data);
388
389         return ret;
390 }
391
392 static int oc_probe(struct connman_provider *provider)
393 {
394         return 0;
395 }
396
397 static int oc_disconnect(struct connman_provider *provider)
398 {
399         struct oc_data *data = connman_provider_get_data(provider);
400
401         DBG("disconnect provider %p:", provider);
402
403         if (data == NULL)
404                 return 0;
405
406         if (data->watch != 0)
407                 connman_rtnl_remove_watch(data->watch);
408
409         data->watch = 0;
410         data->state = OC_STATE_DISCONNECT;
411         connman_task_stop(data->task);
412
413         return 0;
414 }
415
416 static int oc_remove(struct connman_provider *provider)
417 {
418         struct oc_data *data;
419
420         data = connman_provider_get_data(provider);
421         connman_provider_set_data(provider, NULL);
422         if (data == NULL)
423                 return 0;
424
425         if (data->watch != 0)
426                 connman_rtnl_remove_watch(data->watch);
427         data->watch = 0;
428         connman_task_stop(data->task);
429
430         g_usleep(G_USEC_PER_SEC);
431         kill_tun(data->if_name);
432         return 0;
433 }
434
435 static struct connman_provider_driver provider_driver = {
436         .name           = "openconnect",
437         .disconnect     = oc_disconnect,
438         .connect        = oc_connect,
439         .probe          = oc_probe,
440         .remove         = oc_remove,
441 };
442
443 static int openconnect_init(void)
444 {
445         connman_provider_driver_register(&provider_driver);
446
447         return 0;
448 }
449
450 static void openconnect_exit(void)
451 {
452         connman_provider_driver_unregister(&provider_driver);
453 }
454
455 CONNMAN_PLUGIN_DEFINE(openconnect, "OpenConnect VPN plugin", VERSION,
456         CONNMAN_PLUGIN_PRIORITY_DEFAULT, openconnect_init, openconnect_exit)