Handle CSTP rekey when stalled
[platform/upstream/openconnect.git] / mainloop.c
1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2011 Intel Corporation.
5  *
6  * Author: David Woodhouse <dwmw2@infradead.org>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to:
19  *
20  *   Free Software Foundation, Inc.
21  *   51 Franklin Street, Fifth Floor,
22  *   Boston, MA 02110-1301 USA
23  */
24
25 #include <errno.h>
26 #include <poll.h>
27 #include <limits.h>
28 #include <sys/select.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 #include <unistd.h>
32 #include <string.h>
33
34 #include "openconnect-internal.h"
35
36 void queue_packet(struct pkt **q, struct pkt *new)
37 {
38         while (*q)
39                 q = &(*q)->next;
40
41         new->next = NULL;
42         *q = new;
43 }
44
45 int queue_new_packet(struct pkt **q, void *buf, int len)
46 {
47         struct pkt *new = malloc(sizeof(struct pkt) + len);
48         if (!new)
49                 return -ENOMEM;
50
51         new->len = len;
52         new->next = NULL;
53         memcpy(new->data, buf, len);
54         queue_packet(q, new);
55         return 0;
56 }
57
58 int killed;
59
60 static void handle_sigint(int sig)
61 {
62         killed = sig;
63 }
64
65 int vpn_mainloop(struct openconnect_info *vpninfo)
66 {
67         struct sigaction sa;
68         memset(&sa, 0, sizeof(sa));
69         sa.sa_handler = handle_sigint;
70
71         sigaction(SIGTERM, &sa, NULL);
72         sigaction(SIGINT, &sa, NULL);
73         sigaction(SIGHUP, &sa, NULL);
74
75         while (!vpninfo->quit_reason) {
76                 int did_work = 0;
77                 int timeout = INT_MAX;
78                 struct timeval tv;
79                 fd_set rfds, wfds, efds;
80
81 #ifdef HAVE_DTLS
82                 if (vpninfo->new_dtls_ssl)
83                         dtls_try_handshake(vpninfo);
84
85                 if (vpninfo->dtls_attempt_period && !vpninfo->dtls_ssl && !vpninfo->new_dtls_ssl &&
86                     vpninfo->new_dtls_started + vpninfo->dtls_attempt_period < time(NULL)) {
87                         vpn_progress(vpninfo, PRG_TRACE, _("Attempt new DTLS connection\n"));
88                         connect_dtls_socket(vpninfo);
89                 }
90                 if (vpninfo->dtls_ssl)
91                         did_work += dtls_mainloop(vpninfo, &timeout);
92 #endif
93                 if (vpninfo->quit_reason)
94                         break;
95
96                 did_work += cstp_mainloop(vpninfo, &timeout);
97                 if (vpninfo->quit_reason)
98                         break;
99
100                 /* Tun must be last because it will set/clear its bit
101                    in the select_rfds according to the queue length */
102                 did_work += tun_mainloop(vpninfo, &timeout);
103                 if (vpninfo->quit_reason)
104                         break;
105
106                 if (killed) {
107                         if (killed == SIGHUP)
108                                 vpninfo->quit_reason = "Client received SIGHUP";
109                         else if (killed == SIGINT)
110                                 vpninfo->quit_reason = "Client received SIGINT";
111                         else
112                                 vpninfo->quit_reason = "Client killed";
113                         break;
114                 }
115
116                 if (did_work)
117                         continue;
118
119                 vpn_progress(vpninfo, PRG_TRACE,
120                              _("No work to do; sleeping for %d ms...\n"), timeout);
121                 memcpy(&rfds, &vpninfo->select_rfds, sizeof(rfds));
122                 memcpy(&wfds, &vpninfo->select_wfds, sizeof(wfds));
123                 memcpy(&efds, &vpninfo->select_efds, sizeof(efds));
124
125                 tv.tv_sec = timeout / 1000;
126                 tv.tv_usec = (timeout % 1000) * 1000;
127
128                 select(vpninfo->select_nfds, &rfds, &wfds, &efds, &tv);
129         }
130
131         cstp_bye(vpninfo, vpninfo->quit_reason);
132
133         shutdown_tun(vpninfo);
134         return 0;
135 }
136
137 /* Called when the socket is unwritable, to get the deadline for DPD.
138    Returns 1 if DPD deadline has already arrived. */
139 int ka_stalled_action(struct keepalive_info *ka, int *timeout)
140 {
141         time_t due, now = time(NULL);
142
143         if (ka->rekey) {
144                 due = ka->last_rekey + ka->rekey;
145
146                 if (now >= due)
147                         return KA_REKEY;
148
149                 if (*timeout > (due - now) * 1000)
150                         *timeout = (due - now) * 1000;
151         }
152
153         if (!ka->dpd)
154                 return KA_NONE;
155
156         due = ka->last_rx + (2 * ka->dpd);
157
158         if (now > due)
159                 return KA_DPD_DEAD;
160
161         if (*timeout > (due - now) * 1000)
162                 *timeout = (due - now) * 1000;
163
164         return KA_NONE;
165 }
166
167
168 int keepalive_action(struct keepalive_info *ka, int *timeout)
169 {
170         time_t now = time(NULL);
171
172         if (ka->rekey) {
173                 time_t due = ka->last_rekey + ka->rekey;
174
175                 if (now >= due)
176                         return KA_REKEY;
177
178                 if (*timeout > (due - now) * 1000)
179                         *timeout = (due - now) * 1000;
180         }
181
182         /* DPD is bidirectional -- PKT 3 out, PKT 4 back */
183         if (ka->dpd) {
184                 time_t due = ka->last_rx + ka->dpd;
185                 time_t overdue = ka->last_rx + (2 * ka->dpd);
186
187                 /* Peer didn't respond */
188                 if (now > overdue)
189                         return KA_DPD_DEAD;
190
191                 /* If we already have DPD outstanding, don't flood. Repeat by
192                    all means, but only after half the DPD period. */
193                 if (ka->last_dpd > ka->last_rx)
194                         due = ka->last_dpd + ka->dpd / 2;
195
196                 /* We haven't seen a packet from this host for $DPD seconds.
197                    Prod it to see if it's still alive */
198                 if (now >= due) {
199                         ka->last_dpd = now;
200                         return KA_DPD;
201                 }
202                 if (*timeout > (due - now) * 1000)
203                         *timeout = (due - now) * 1000;
204         }
205
206         /* Keepalive is just client -> server */
207         if (ka->keepalive) {
208                 time_t due = ka->last_tx + ka->keepalive;
209
210                 /* If we haven't sent anything for $KEEPALIVE seconds, send a
211                    dummy packet (which the server will discard) */
212                 if (now >= due)
213                         return KA_KEEPALIVE;
214
215                 if (*timeout > (due - now) * 1000)
216                         *timeout = (due - now) * 1000;
217         }
218
219         return KA_NONE;
220 }