1 /*
2 * OpenConnect (SSL + DTLS) VPN client
3 *
4 * Copyright © 2008-2015 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
18 #include <config.h>
19
20 #include <errno.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #ifndef _WIN32
29 #include <netinet/in.h>
30 #include <sys/socket.h>
31 #endif
32
33 #include "openconnect-internal.h"
34
35 /*
36 * The master-secret is generated randomly by the client. The server
37 * responds with a DTLS Session-ID. These, done over the HTTPS
38 * connection, are enough to 'resume' a DTLS session, bypassing all
39 * the normal setup of a normal DTLS connection.
40 *
41 * Cisco use a version of the protocol which predates RFC4347, but
42 * isn't quite the same as the pre-RFC version of the protocol which
43 * was in OpenSSL 0.9.8e -- it includes backports of some later
44 * OpenSSL patches.
45 *
46 * The openssl/ directory of this source tree should contain both a
47 * small patch against OpenSSL 0.9.8e to make it support Cisco's
48 * snapshot of the protocol, and a larger patch against newer OpenSSL
49 * which gives us an option to use the old protocol again.
50 *
51 * Cisco's server also seems to respond to the official version of the
52 * protocol, with a change in the ChangeCipherSpec packet which implies
53 * that it does know the difference and isn't just repeating the version
54 * number seen in the ClientHello. But although I can make the handshake
55 * complete by hacking tls1_mac() to use the _old_ protocol version
56 * number when calculating the MAC, the server still seems to be ignoring
57 * my subsequent data packets. So we use the old protocol, which is what
58 * their clients use anyway.
59 */
60
61 #if defined(OPENCONNECT_OPENSSL)
62 #define DTLS_SEND SSL_write
63 #define DTLS_RECV SSL_read
64 #elif defined(OPENCONNECT_GNUTLS)
65 #define DTLS_SEND gnutls_record_send
66 #define DTLS_RECV gnutls_record_recv
67 #endif
68
openconnect_bin2hex(const char * prefix,const uint8_t * data,unsigned len)69 char *openconnect_bin2hex(const char *prefix, const uint8_t *data, unsigned len)
70 {
71 struct oc_text_buf *buf;
72 char *p = NULL;
73
74 buf = buf_alloc();
75 if (prefix)
76 buf_append(buf, "%s", prefix);
77 buf_append_hex(buf, data, len);
78
79 if (!buf_error(buf)) {
80 p = buf->data;
81 buf->data = NULL;
82 }
83 buf_free(buf);
84
85 return p;
86 }
87
openconnect_bin2base64(const char * prefix,const uint8_t * data,unsigned len)88 char *openconnect_bin2base64(const char *prefix, const uint8_t *data, unsigned len)
89 {
90 struct oc_text_buf *buf;
91 char *p = NULL;
92
93 buf = buf_alloc();
94 if (prefix)
95 buf_append(buf, "%s", prefix);
96 buf_append_base64(buf, data, len);
97
98 if (!buf_error(buf)) {
99 p = buf->data;
100 buf->data = NULL;
101 }
102 buf_free(buf);
103
104 return p;
105 }
106
connect_dtls_socket(struct openconnect_info * vpninfo)107 static int connect_dtls_socket(struct openconnect_info *vpninfo)
108 {
109 int dtls_fd, ret;
110
111 /* Sanity check for the removal of new_dtls_{fd,ssl} */
112 if (vpninfo->dtls_fd != -1) {
113 vpn_progress(vpninfo, PRG_ERR, _("DTLS connection attempted with an existing fd\n"));
114 vpninfo->dtls_attempt_period = 0;
115 return -EINVAL;
116 }
117
118 if (!vpninfo->dtls_addr) {
119 vpn_progress(vpninfo, PRG_ERR, _("No DTLS address\n"));
120 vpninfo->dtls_attempt_period = 0;
121 return -EINVAL;
122 }
123
124 if (!vpninfo->dtls_cipher) {
125 /* We probably didn't offer it any ciphers it liked */
126 vpn_progress(vpninfo, PRG_ERR, _("Server offered no DTLS cipher option\n"));
127 vpninfo->dtls_attempt_period = 0;
128 return -EINVAL;
129 }
130
131 if (vpninfo->proxy) {
132 /* XXX: Theoretically, SOCKS5 proxies can do UDP too */
133 vpn_progress(vpninfo, PRG_ERR, _("No DTLS when connected via proxy\n"));
134 vpninfo->dtls_attempt_period = 0;
135 return -EINVAL;
136 }
137
138 dtls_fd = udp_connect(vpninfo);
139 if (dtls_fd < 0)
140 return -EINVAL;
141
142
143 ret = start_dtls_handshake(vpninfo, dtls_fd);
144 if (ret) {
145 closesocket(dtls_fd);
146 return ret;
147 }
148
149 vpninfo->dtls_state = DTLS_CONNECTING;
150
151 vpninfo->dtls_fd = dtls_fd;
152 monitor_fd_new(vpninfo, dtls);
153 monitor_read_fd(vpninfo, dtls);
154 monitor_except_fd(vpninfo, dtls);
155
156 time(&vpninfo->new_dtls_started);
157
158 return dtls_try_handshake(vpninfo);
159 }
160
dtls_close(struct openconnect_info * vpninfo)161 void dtls_close(struct openconnect_info *vpninfo)
162 {
163 if (vpninfo->dtls_ssl) {
164 dtls_ssl_free(vpninfo);
165 closesocket(vpninfo->dtls_fd);
166 unmonitor_read_fd(vpninfo, dtls);
167 unmonitor_write_fd(vpninfo, dtls);
168 unmonitor_except_fd(vpninfo, dtls);
169 vpninfo->dtls_ssl = NULL;
170 vpninfo->dtls_fd = -1;
171 }
172 vpninfo->dtls_state = DTLS_SLEEPING;
173 }
174
dtls_reconnect(struct openconnect_info * vpninfo)175 static int dtls_reconnect(struct openconnect_info *vpninfo)
176 {
177 dtls_close(vpninfo);
178
179 if (vpninfo->dtls_state == DTLS_DISABLED)
180 return -EINVAL;
181
182 vpninfo->dtls_state = DTLS_SLEEPING;
183 return connect_dtls_socket(vpninfo);
184 }
185
dtls_setup(struct openconnect_info * vpninfo,int dtls_attempt_period)186 int dtls_setup(struct openconnect_info *vpninfo, int dtls_attempt_period)
187 {
188 struct oc_vpn_option *dtls_opt = vpninfo->dtls_options;
189 int dtls_port = 0;
190
191 if (vpninfo->dtls_state == DTLS_DISABLED)
192 return -EINVAL;
193
194 vpninfo->dtls_attempt_period = dtls_attempt_period;
195 if (!dtls_attempt_period)
196 return 0;
197
198 while (dtls_opt) {
199 vpn_progress(vpninfo, PRG_DEBUG,
200 _("DTLS option %s : %s\n"),
201 dtls_opt->option, dtls_opt->value);
202
203 if (!strcmp(dtls_opt->option, "X-DTLS-Port")) {
204 dtls_port = atol(dtls_opt->value);
205 } else if (!strcmp(dtls_opt->option, "X-DTLS-Keepalive")) {
206 vpninfo->dtls_times.keepalive = atol(dtls_opt->value);
207 } else if (!strcmp(dtls_opt->option, "X-DTLS-DPD")) {
208 int j = atol(dtls_opt->value);
209 if (j && (!vpninfo->dtls_times.dpd || j < vpninfo->dtls_times.dpd))
210 vpninfo->dtls_times.dpd = j;
211 } else if (!strcmp(dtls_opt->option, "X-DTLS-Rekey-Method")) {
212 if (!strcmp(dtls_opt->value, "new-tunnel"))
213 vpninfo->dtls_times.rekey_method = REKEY_TUNNEL;
214 else if (!strcmp(dtls_opt->value, "ssl"))
215 vpninfo->dtls_times.rekey_method = REKEY_SSL;
216 else
217 vpninfo->dtls_times.rekey_method = REKEY_NONE;
218 } else if (!strcmp(dtls_opt->option, "X-DTLS-Rekey-Time")) {
219 vpninfo->dtls_times.rekey = atol(dtls_opt->value);
220 }
221
222 dtls_opt = dtls_opt->next;
223 }
224 if (!dtls_port) {
225 vpninfo->dtls_attempt_period = 0;
226 return -EINVAL;
227 }
228 if (vpninfo->dtls_times.rekey <= 0)
229 vpninfo->dtls_times.rekey_method = REKEY_NONE;
230
231 if (udp_sockaddr(vpninfo, dtls_port)) {
232 vpninfo->dtls_attempt_period = 0;
233 return -EINVAL;
234 }
235 if (connect_dtls_socket(vpninfo))
236 return -EINVAL;
237
238 vpn_progress(vpninfo, PRG_DEBUG,
239 _("DTLS initialised. DPD %d, Keepalive %d\n"),
240 vpninfo->dtls_times.dpd, vpninfo->dtls_times.keepalive);
241
242 return 0;
243 }
244
udp_tos_update(struct openconnect_info * vpninfo,struct pkt * pkt)245 int udp_tos_update(struct openconnect_info *vpninfo, struct pkt *pkt)
246 {
247 int tos;
248
249 /* Extract TOS field from IP header (IPv4 and IPv6 differ) */
250 switch(pkt->data[0] >> 4) {
251 case 4:
252 tos = pkt->data[1];
253 break;
254 case 6:
255 tos = (load_be16(pkt->data) >> 4) & 0xff;
256 break;
257 default:
258 vpn_progress(vpninfo, PRG_ERR,
259 _("Unknown packet (len %d) received: %02x %02x %02x %02x...\n"),
260 pkt->len, pkt->data[0], pkt->data[1], pkt->data[2], pkt->data[3]);
261 return -EINVAL;
262 }
263
264 /* set the actual value */
265 if (tos != vpninfo->dtls_tos_current) {
266 vpn_progress(vpninfo, PRG_DEBUG, _("TOS this: %d, TOS last: %d\n"),
267 tos, vpninfo->dtls_tos_current);
268 if (setsockopt(vpninfo->dtls_fd, vpninfo->dtls_tos_proto,
269 vpninfo->dtls_tos_optname, (void *)&tos, sizeof(tos)))
270 vpn_perror(vpninfo, _("UDP setsockopt"));
271 else
272 vpninfo->dtls_tos_current = tos;
273 }
274 return 0;
275 }
276
dtls_mainloop(struct openconnect_info * vpninfo,int * timeout,int readable)277 int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
278 {
279 int work_done = 0;
280 char magic_pkt;
281
282 if (vpninfo->dtls_need_reconnect) {
283 vpninfo->dtls_need_reconnect = 0;
284 dtls_reconnect(vpninfo);
285 return 1;
286 }
287
288 if (vpninfo->dtls_state == DTLS_CONNECTING) {
289 dtls_try_handshake(vpninfo);
290 return 0;
291 }
292
293 if (vpninfo->dtls_state == DTLS_SLEEPING) {
294 int when = vpninfo->new_dtls_started + vpninfo->dtls_attempt_period - time(NULL);
295
296 if (when <= 0) {
297 vpn_progress(vpninfo, PRG_DEBUG, _("Attempt new DTLS connection\n"));
298 if (connect_dtls_socket(vpninfo) < 0)
299 *timeout = 1000;
300 } else if ((when * 1000) < *timeout) {
301 *timeout = when * 1000;
302 }
303 return 0;
304 }
305
306 while (readable) {
307 int len = MAX(16384, vpninfo->ip_info.mtu);
308 unsigned char *buf;
309
310 if (!vpninfo->dtls_pkt) {
311 vpninfo->dtls_pkt = malloc(sizeof(struct pkt) + len);
312 if (!vpninfo->dtls_pkt) {
313 vpn_progress(vpninfo, PRG_ERR, _("Allocation failed\n"));
314 break;
315 }
316 }
317
318 buf = vpninfo->dtls_pkt->data - 1;
319 len = DTLS_RECV(vpninfo->dtls_ssl, buf, len + 1);
320 if (len <= 0)
321 break;
322
323 vpn_progress(vpninfo, PRG_TRACE,
324 _("Received DTLS packet 0x%02x of %d bytes\n"),
325 buf[0], len);
326
327 vpninfo->dtls_times.last_rx = time(NULL);
328
329 switch (buf[0]) {
330 case AC_PKT_DATA:
331 vpninfo->dtls_pkt->len = len - 1;
332 queue_packet(&vpninfo->incoming_queue, vpninfo->dtls_pkt);
333 vpninfo->dtls_pkt = NULL;
334 work_done = 1;
335 break;
336
337 case AC_PKT_DPD_OUT:
338 vpn_progress(vpninfo, PRG_DEBUG, _("Got DTLS DPD request\n"));
339
340 /* FIXME: What if the packet doesn't get through? */
341 magic_pkt = AC_PKT_DPD_RESP;
342 if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1)
343 vpn_progress(vpninfo, PRG_ERR,
344 _("Failed to send DPD response. Expect disconnect\n"));
345 continue;
346
347 case AC_PKT_DPD_RESP:
348 vpn_progress(vpninfo, PRG_DEBUG, _("Got DTLS DPD response\n"));
349 break;
350
351 case AC_PKT_KEEPALIVE:
352 vpn_progress(vpninfo, PRG_DEBUG, _("Got DTLS Keepalive\n"));
353 break;
354
355 case AC_PKT_COMPRESSED:
356 if (!vpninfo->dtls_compr) {
357 vpn_progress(vpninfo, PRG_ERR,
358 _("Compressed DTLS packet received when compression not enabled\n"));
359 goto unknown_pkt;
360 }
361 decompress_and_queue_packet(vpninfo, vpninfo->dtls_compr,
362 vpninfo->dtls_pkt->data, len - 1);
363 break;
364 default:
365 vpn_progress(vpninfo, PRG_ERR,
366 _("Unknown DTLS packet type %02x, len %d\n"),
367 buf[0], len);
368 if (1) {
369 /* Some versions of OpenSSL have bugs with receiving out-of-order
370 * packets. Not only do they wrongly decide to drop packets if
371 * two packets get swapped in transit, but they also _fail_ to
372 * drop the packet in non-blocking mode; instead they return
373 * the appropriate length of garbage. So don't abort... for now. */
374 break;
375 } else {
376 unknown_pkt:
377 vpninfo->quit_reason = "Unknown packet received";
378 return 1;
379 }
380
381 }
382 }
383
384 switch (keepalive_action(&vpninfo->dtls_times, timeout)) {
385 case KA_REKEY: {
386 int ret;
387
388 vpn_progress(vpninfo, PRG_INFO, _("DTLS rekey due\n"));
389
390 if (vpninfo->dtls_times.rekey_method == REKEY_SSL) {
391 time(&vpninfo->new_dtls_started);
392 vpninfo->dtls_state = DTLS_CONNECTING;
393 ret = dtls_try_handshake(vpninfo);
394 if (ret) {
395 vpn_progress(vpninfo, PRG_ERR, _("DTLS Rehandshake failed; reconnecting.\n"));
396 return connect_dtls_socket(vpninfo);
397 }
398 }
399
400 return 1;
401 }
402
403 case KA_DPD_DEAD:
404 vpn_progress(vpninfo, PRG_ERR, _("DTLS Dead Peer Detection detected dead peer!\n"));
405 /* Fall back to SSL, and start a new DTLS connection */
406 dtls_reconnect(vpninfo);
407 return 1;
408
409 case KA_DPD:
410 vpn_progress(vpninfo, PRG_DEBUG, _("Send DTLS DPD\n"));
411
412 magic_pkt = AC_PKT_DPD_OUT;
413 if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1)
414 vpn_progress(vpninfo, PRG_ERR,
415 _("Failed to send DPD request. Expect disconnect\n"));
416
417 /* last_dpd will just have been set */
418 vpninfo->dtls_times.last_tx = vpninfo->dtls_times.last_dpd;
419 work_done = 1;
420 break;
421
422 case KA_KEEPALIVE:
423 /* No need to send an explicit keepalive
424 if we have real data to send */
425 if (vpninfo->outgoing_queue.head)
426 break;
427
428 vpn_progress(vpninfo, PRG_DEBUG, _("Send DTLS Keepalive\n"));
429
430 magic_pkt = AC_PKT_KEEPALIVE;
431 if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1)
432 vpn_progress(vpninfo, PRG_ERR,
433 _("Failed to send keepalive request. Expect disconnect\n"));
434 time(&vpninfo->dtls_times.last_tx);
435 work_done = 1;
436 break;
437
438 case KA_NONE:
439 ;
440 }
441
442 /* Service outgoing packet queue */
443 unmonitor_write_fd(vpninfo, dtls);
444 while (vpninfo->outgoing_queue.head) {
445 struct pkt *this = dequeue_packet(&vpninfo->outgoing_queue);
446 struct pkt *send_pkt = this;
447 int ret;
448
449 /* If TOS optname is set, we want to copy the TOS/TCLASS header
450 to the outer UDP packet */
451 if (vpninfo->dtls_tos_optname)
452 udp_tos_update(vpninfo, this);
453
454 /* One byte of header */
455 this->cstp.hdr[7] = AC_PKT_DATA;
456
457 /* We can compress into vpninfo->deflate_pkt unless CSTP
458 * currently has a compressed packet pending — which it
459 * shouldn't if DTLS is active. */
460 if (vpninfo->dtls_compr &&
461 vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
462 !compress_packet(vpninfo, vpninfo->dtls_compr, this)) {
463 send_pkt = vpninfo->deflate_pkt;
464 send_pkt->cstp.hdr[7] = AC_PKT_COMPRESSED;
465 }
466
467 #ifdef OPENCONNECT_OPENSSL
468 ret = SSL_write(vpninfo->dtls_ssl, &send_pkt->cstp.hdr[7], send_pkt->len + 1);
469 if (ret <= 0) {
470 ret = SSL_get_error(vpninfo->dtls_ssl, ret);
471
472 if (ret == SSL_ERROR_WANT_WRITE) {
473 monitor_write_fd(vpninfo, dtls);
474 requeue_packet(&vpninfo->outgoing_queue, this);
475 } else if (ret != SSL_ERROR_WANT_READ) {
476 /* If it's a real error, kill the DTLS connection and
477 requeue the packet to be sent over SSL */
478 vpn_progress(vpninfo, PRG_ERR,
479 _("DTLS got write error %d. Falling back to SSL\n"),
480 ret);
481 openconnect_report_ssl_errors(vpninfo);
482 dtls_reconnect(vpninfo);
483 requeue_packet(&vpninfo->outgoing_queue, this);
484 work_done = 1;
485 }
486 return work_done;
487 }
488 #else /* GnuTLS */
489 ret = gnutls_record_send(vpninfo->dtls_ssl, &send_pkt->cstp.hdr[7], send_pkt->len + 1);
490 if (ret <= 0) {
491 if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) {
492 vpn_progress(vpninfo, PRG_ERR,
493 _("DTLS got write error: %s. Falling back to SSL\n"),
494 gnutls_strerror(ret));
495 dtls_reconnect(vpninfo);
496 work_done = 1;
497 } else {
498 /* Wake me up when it becomes writeable */
499 monitor_write_fd(vpninfo, dtls);
500 }
501
502 requeue_packet(&vpninfo->outgoing_queue, this);
503 return work_done;
504 }
505 #endif
506 time(&vpninfo->dtls_times.last_tx);
507 vpn_progress(vpninfo, PRG_TRACE,
508 _("Sent DTLS packet of %d bytes; DTLS send returned %d\n"),
509 this->len, ret);
510 free(this);
511 }
512
513 return work_done;
514 }
515
516 /* This symbol is missing in glibc < 2.22 (bug 18643). */
517 #if defined(__linux__) && !defined(HAVE_IPV6_PATHMTU)
518 # define HAVE_IPV6_PATHMTU 1
519 # define IPV6_PATHMTU 61
520 #endif
521
522 #define PKT_INTERVAL_MS 50
523
524 /* Performs a binary search to detect MTU.
525 * @buf: is preallocated with MTU size
526 *
527 * Returns: new MTU or 0
528 */
probe_mtu(struct openconnect_info * vpninfo,unsigned char * buf)529 static int probe_mtu(struct openconnect_info *vpninfo, unsigned char *buf)
530 {
531 int max, min, cur, ret, absolute_min, last;
532 int tries = 0; /* Number of loops in bin search - includes resends */
533 uint32_t id, id_len;
534 struct timeval start_tv, now_tv, last_tv;
535
536 absolute_min = 576;
537 if (vpninfo->ip_info.addr6)
538 absolute_min = 1280;
539
540 /* We'll assume that it is at least functional, and permits the bare
541 * minimum MTU for the protocol(s) it transports. All else is mad. */
542 min = absolute_min;
543
544 /* First send a probe at the configured maximum. Most of the time, this
545 one will probably work. */
546 last = cur = max = vpninfo->ip_info.mtu;
547
548 if (max <= min)
549 goto fail;
550
551 /* Generate unique ID */
552 if (openconnect_random(&id, sizeof(id)) < 0)
553 goto fail;
554
555 vpn_progress(vpninfo, PRG_DEBUG,
556 _("Initiating MTU detection (min=%d, max=%d)\n"), min, max);
557
558 gettimeofday(&start_tv, NULL);
559 last_tv = start_tv;
560
561 while (1) {
562 int wait_ms;
563
564 #ifdef HAVE_IPV6_PATHMTU
565 if (vpninfo->peer_addr->sa_family == AF_INET6) {
566 struct ip6_mtuinfo mtuinfo;
567 socklen_t len = sizeof(mtuinfo);
568 int newmax;
569
570 if (getsockopt(vpninfo->dtls_fd, IPPROTO_IPV6, IPV6_PATHMTU, &mtuinfo, &len) >= 0) {
571 newmax = mtuinfo.ip6m_mtu;
572 if (newmax > 0) {
573 newmax = dtls_set_mtu(vpninfo, newmax) - /*ipv6*/40 - /*udp*/20 - /*oc dtls*/1;
574 if (absolute_min > newmax)
575 goto fail;
576 if (max > newmax)
577 max = newmax;
578 if (cur > newmax)
579 cur = newmax;
580 }
581 }
582 }
583 #endif
584
585 buf[0] = AC_PKT_DPD_OUT;
586 id_len = id + cur;
587 memcpy(&buf[1], &id_len, sizeof(id_len));
588
589 vpn_progress(vpninfo, PRG_TRACE,
590 _("Sending MTU DPD probe (%u bytes)\n"), cur);
591 ret = openconnect_dtls_write(vpninfo, buf, cur + 1);
592 if (ret != cur + 1) {
593 vpn_progress(vpninfo, PRG_ERR,
594 _("Failed to send DPD request (%d %d)\n"), cur, ret);
595 if (cur == max) {
596 max = --cur;
597 if (cur >= absolute_min)
598 continue;
599 }
600 goto fail;
601 }
602 if (last == cur)
603 tries++;
604 else {
605 tries = 0;
606 last = cur;
607 }
608
609 memset(buf, 0, sizeof(id)+1);
610 keep_waiting:
611 gettimeofday(&now_tv, NULL);
612
613 if (now_tv.tv_sec > start_tv.tv_sec + 10) {
614 if (absolute_min == min) {
615 /* Hm, we never got *anything* back successfully? */
616 vpn_progress(vpninfo, PRG_ERR,
617 _("Too long time in MTU detect loop; assuming negotiated MTU.\n"));
618 goto fail;
619 } else {
620 vpn_progress(vpninfo, PRG_ERR,
621 _("Too long time in MTU detect loop; MTU set to %d.\n"), min);
622 ret = min;
623 goto out;
624 }
625 }
626
627
628 wait_ms = PKT_INTERVAL_MS -
629 ((now_tv.tv_sec - last_tv.tv_sec) * 1000) -
630 ((now_tv.tv_usec - last_tv.tv_usec) / 1000);
631 if (wait_ms < 0 || wait_ms > PKT_INTERVAL_MS)
632 wait_ms = PKT_INTERVAL_MS;
633
634 ret = openconnect_dtls_read(vpninfo, buf, max+1, wait_ms);
635 if (ret > 0 && (buf[0] != AC_PKT_DPD_RESP || !memcpy(&id_len, &buf[1], sizeof(id_len)) ||
636 id_len != id + ret - 1)) {
637 vpn_progress(vpninfo, PRG_DEBUG,
638 _("Received unexpected packet (%.2x) in MTU detection; skipping.\n"), (unsigned)buf[0]);
639 goto keep_waiting;
640 }
641
642 if (ret == -ETIMEDOUT) {
643 if (tries >= 6) {
644 vpn_progress(vpninfo, PRG_DEBUG,
645 _("No response to size %u after %d tries; declare MTU is %u\n"),
646 last, tries, min);
647 ret = min;
648 goto out;
649 }
650 } else if (ret < 0) {
651 vpn_progress(vpninfo, PRG_ERR,
652 _("Failed to recv DPD request (%d)\n"), ret);
653 goto fail;
654 } else if (ret > 0) {
655 vpn_progress(vpninfo, PRG_TRACE,
656 _("Received MTU DPD probe (%u bytes)\n"), ret - 1);
657 ret--;
658 tries = 0;
659 }
660
661 if (ret == max)
662 goto out;
663
664 if (ret > min) {
665 min = ret;
666 if (min >= last) {
667 cur = (min + max + 1) / 2;
668 } else {
669 cur = (min + last + 1) / 2;
670 }
671 } else {
672 cur = (min + last + 1) / 2;
673 }
674 }
675 fail:
676 ret = 0;
677 out:
678 return ret;
679 }
680
681
682
dtls_detect_mtu(struct openconnect_info * vpninfo)683 void dtls_detect_mtu(struct openconnect_info *vpninfo)
684 {
685 int mtu = vpninfo->ip_info.mtu;
686 int prev_mtu = vpninfo->ip_info.mtu;
687 unsigned char *buf;
688
689 if (vpninfo->ip_info.mtu < 1 + sizeof(uint32_t))
690 return;
691
692 /* detect MTU */
693 buf = calloc(1, 1 + vpninfo->ip_info.mtu);
694 if (!buf) {
695 vpn_progress(vpninfo, PRG_ERR, _("Allocation failed\n"));
696 return;
697 }
698
699 mtu = probe_mtu(vpninfo, buf);
700 if (mtu == 0)
701 goto skip_mtu;
702
703 vpninfo->ip_info.mtu = mtu;
704 if (prev_mtu != vpninfo->ip_info.mtu) {
705 vpn_progress(vpninfo, PRG_INFO,
706 _("Detected MTU of %d bytes (was %d)\n"), vpninfo->ip_info.mtu, prev_mtu);
707 } else {
708 vpn_progress(vpninfo, PRG_DEBUG,
709 _("No change in MTU after detection (was %d)\n"), prev_mtu);
710 }
711
712 skip_mtu:
713 free(buf);
714 }
715
716