mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2024-09-21 10:27:19 +00:00
Allow setup of new DTLS session while processing on old session
Resolves: #359 Signed-off-by: Alan Jowett alan.jowett@microsoft.com
This commit is contained in:
parent
c5d3e4f321
commit
3436705a9c
1
NEWS
1
NEWS
@ -5,6 +5,7 @@
|
|||||||
- Don't attempt TLS if the client closes the connection with zero data
|
- Don't attempt TLS if the client closes the connection with zero data
|
||||||
sent (#357)
|
sent (#357)
|
||||||
- Increased the maximum configuration line (#364)
|
- Increased the maximum configuration line (#364)
|
||||||
|
- Allow setup of new DTLS session concurrent with old session.
|
||||||
|
|
||||||
|
|
||||||
* Version 1.1.1 (released 2020-09-21)
|
* Version 1.1.1 (released 2020-09-21)
|
||||||
|
@ -72,6 +72,7 @@ gnutls-bin / gnutls-utils
|
|||||||
iproute2 / iproute
|
iproute2 / iproute
|
||||||
yajl-tools / yajl
|
yajl-tools / yajl
|
||||||
iproute2 / iproute
|
iproute2 / iproute
|
||||||
|
tcpdump / tcpdump
|
||||||
```
|
```
|
||||||
|
|
||||||
See [README-radius](doc/README-radius.md) for more information on Radius
|
See [README-radius](doc/README-radius.md) for more information on Radius
|
||||||
|
@ -1172,7 +1172,6 @@ static void listen_watcher_cb (EV_P_ ev_io *w, int revents)
|
|||||||
|
|
||||||
ws->cmd_fd = cmd_fd[1];
|
ws->cmd_fd = cmd_fd[1];
|
||||||
ws->tun_fd = -1;
|
ws->tun_fd = -1;
|
||||||
ws->dtls_tptr.fd = -1;
|
|
||||||
set_cloexec_flag(fd, false);
|
set_cloexec_flag(fd, false);
|
||||||
ws->conn_fd = fd;
|
ws->conn_fd = fd;
|
||||||
ws->conn_type = stype;
|
ws->conn_type = stype;
|
||||||
|
19
src/tlslib.c
19
src/tlslib.c
@ -339,13 +339,13 @@ void cstp_fatal_close(worker_st *ws,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t dtls_recv_packet(worker_st *ws, gnutls_datum_t *data, void **p)
|
ssize_t dtls_recv_packet(struct dtls_st *dtls, gnutls_datum_t *data, void **p)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
#ifdef ZERO_COPY
|
#ifdef ZERO_COPY
|
||||||
gnutls_packet_t packet = NULL;
|
gnutls_packet_t packet = NULL;
|
||||||
|
|
||||||
ret = gnutls_record_recv_packet(ws->dtls_session, &packet);
|
ret = gnutls_record_recv_packet(dtls->dtls_session, &packet);
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
gnutls_packet_get(packet, data, NULL);
|
gnutls_packet_get(packet, data, NULL);
|
||||||
*p = packet;
|
*p = packet;
|
||||||
@ -354,7 +354,7 @@ ssize_t dtls_recv_packet(worker_st *ws, gnutls_datum_t *data, void **p)
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
ret =
|
ret =
|
||||||
gnutls_record_recv(ws->dtls_session, ws->buffer, ws->buffer_size);
|
gnutls_record_recv(dtls->dtls_session, ws->buffer, ws->buffer_size);
|
||||||
data->data = ws->buffer;
|
data->data = ws->buffer;
|
||||||
data->size = ret;
|
data->size = ret;
|
||||||
#endif
|
#endif
|
||||||
@ -362,7 +362,7 @@ ssize_t dtls_recv_packet(worker_st *ws, gnutls_datum_t *data, void **p)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t dtls_send(worker_st *ws, const void *data,
|
ssize_t dtls_send(struct dtls_st *dtls, const void *data,
|
||||||
size_t data_size)
|
size_t data_size)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -370,7 +370,7 @@ ssize_t dtls_send(worker_st *ws, const void *data,
|
|||||||
const uint8_t* p = data;
|
const uint8_t* p = data;
|
||||||
|
|
||||||
while(left > 0) {
|
while(left > 0) {
|
||||||
ret = gnutls_record_send(ws->dtls_session, p, data_size);
|
ret = gnutls_record_send(dtls->dtls_session, p, data_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) {
|
if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED) {
|
||||||
return ret;
|
return ret;
|
||||||
@ -389,10 +389,10 @@ ssize_t dtls_send(worker_st *ws, const void *data,
|
|||||||
return data_size;
|
return data_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dtls_close(worker_st *ws)
|
void dtls_close(struct dtls_st *dtls)
|
||||||
{
|
{
|
||||||
gnutls_bye(ws->dtls_session, GNUTLS_SHUT_WR);
|
gnutls_bye(dtls->dtls_session, GNUTLS_SHUT_WR);
|
||||||
gnutls_deinit(ws->dtls_session);
|
gnutls_deinit(dtls->dtls_session);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t rehash(const void *_e, void *unused)
|
static size_t rehash(const void *_e, void *unused)
|
||||||
@ -467,7 +467,8 @@ static int verify_certificate_cb(gnutls_session_t session)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session == ws->dtls_session) {
|
if ((session == DTLS_ACTIVE(ws)->dtls_session) ||
|
||||||
|
(session == DTLS_INACTIVE(ws)->dtls_session)) {
|
||||||
oclog(ws, LOG_ERR, "unexpected issue; client shouldn't have offered a certificate in DTLS");
|
oclog(ws, LOG_ERR, "unexpected issue; client shouldn't have offered a certificate in DTLS");
|
||||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -144,8 +144,8 @@ void cstp_cork(struct worker_st *ws);
|
|||||||
int cstp_uncork(struct worker_st *ws);
|
int cstp_uncork(struct worker_st *ws);
|
||||||
|
|
||||||
/* DTLS API */
|
/* DTLS API */
|
||||||
void dtls_close(struct worker_st *ws);
|
void dtls_close(struct dtls_st *dtls);
|
||||||
ssize_t dtls_send(struct worker_st *ws, const void *data, size_t data_size);
|
ssize_t dtls_send(struct dtls_st *dtls, const void *data, size_t data_size);
|
||||||
|
|
||||||
/* packet API */
|
/* packet API */
|
||||||
inline static void packet_deinit(void *p)
|
inline static void packet_deinit(void *p)
|
||||||
@ -158,7 +158,7 @@ inline static void packet_deinit(void *p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ssize_t cstp_recv_packet(struct worker_st *ws, gnutls_datum_t *data, void **p);
|
ssize_t cstp_recv_packet(struct worker_st *ws, gnutls_datum_t *data, void **p);
|
||||||
ssize_t dtls_recv_packet(struct worker_st *ws, gnutls_datum_t *data, void **p);
|
ssize_t dtls_recv_packet(struct dtls_st *dtls, gnutls_datum_t *data, void **p);
|
||||||
|
|
||||||
/* Helper functions */
|
/* Helper functions */
|
||||||
unsigned need_file_reload(const char *file, time_t last_access);
|
unsigned need_file_reload(const char *file, time_t last_access);
|
||||||
|
@ -416,6 +416,7 @@ typedef struct attic_entry_st {
|
|||||||
/* generic thing to stop complaints */
|
/* generic thing to stop complaints */
|
||||||
struct worker_st;
|
struct worker_st;
|
||||||
struct main_server_st;
|
struct main_server_st;
|
||||||
|
struct dtls_st;
|
||||||
|
|
||||||
#define MAX_BANNER_SIZE 256
|
#define MAX_BANNER_SIZE 256
|
||||||
#define MAX_USERNAME_SIZE 64
|
#define MAX_USERNAME_SIZE 64
|
||||||
|
@ -49,22 +49,22 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* recv from the new file descriptor and make sure we have a valid packet */
|
/* recv from the new file descriptor and make sure we have a valid packet */
|
||||||
static unsigned recv_from_new_fd(struct worker_st *ws, int fd, UdpFdMsg **tmsg)
|
static unsigned recv_from_new_fd(struct worker_st * ws, struct dtls_st *dtls, int fd, UdpFdMsg **tmsg)
|
||||||
{
|
{
|
||||||
int saved_fd, ret;
|
int saved_fd, ret;
|
||||||
UdpFdMsg *saved_tmsg;
|
UdpFdMsg *saved_tmsg;
|
||||||
|
|
||||||
/* don't bother with anything if we are on uninitialized state */
|
/* don't bother with anything if we are on uninitialized state */
|
||||||
if (ws->dtls_session == NULL || ws->udp_state != UP_ACTIVE)
|
if (dtls->dtls_session == NULL || dtls->udp_state != UP_ACTIVE)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
saved_fd = ws->dtls_tptr.fd;
|
saved_fd = dtls->dtls_tptr.fd;
|
||||||
saved_tmsg = ws->dtls_tptr.msg;
|
saved_tmsg = dtls->dtls_tptr.msg;
|
||||||
|
|
||||||
ws->dtls_tptr.msg = *tmsg;
|
dtls->dtls_tptr.msg = *tmsg;
|
||||||
ws->dtls_tptr.fd = fd;
|
dtls->dtls_tptr.fd = fd;
|
||||||
|
|
||||||
ret = gnutls_record_recv(ws->dtls_session, ws->buffer, ws->buffer_size);
|
ret = gnutls_record_recv(dtls->dtls_session, ws->buffer, ws->buffer_size);
|
||||||
/* we receive GNUTLS_E_AGAIN in case the packet was discarded */
|
/* we receive GNUTLS_E_AGAIN in case the packet was discarded */
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
@ -73,9 +73,9 @@ static unsigned recv_from_new_fd(struct worker_st *ws, int fd, UdpFdMsg **tmsg)
|
|||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
revert:
|
revert:
|
||||||
*tmsg = ws->dtls_tptr.msg;
|
*tmsg = dtls->dtls_tptr.msg;
|
||||||
ws->dtls_tptr.fd = saved_fd;
|
dtls->dtls_tptr.fd = saved_fd;
|
||||||
ws->dtls_tptr.msg = saved_tmsg;
|
dtls->dtls_tptr.msg = saved_tmsg;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +86,7 @@ int handle_commands_from_main(struct worker_st *ws)
|
|||||||
UdpFdMsg *tmsg = NULL;
|
UdpFdMsg *tmsg = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
|
struct dtls_st * dtls = NULL;
|
||||||
/*int cmd_data_len;*/
|
/*int cmd_data_len;*/
|
||||||
|
|
||||||
memset(&ws->buffer, 0, sizeof(ws->buffer));
|
memset(&ws->buffer, 0, sizeof(ws->buffer));
|
||||||
@ -113,7 +114,7 @@ int handle_commands_from_main(struct worker_st *ws)
|
|||||||
case CMD_UDP_FD: {
|
case CMD_UDP_FD: {
|
||||||
unsigned has_hello = 1;
|
unsigned has_hello = 1;
|
||||||
|
|
||||||
if (ws->udp_state != UP_WAIT_FD) {
|
if (DTLS_ACTIVE(ws)->udp_state != UP_WAIT_FD) {
|
||||||
oclog(ws, LOG_DEBUG, "received another a UDP fd!");
|
oclog(ws, LOG_DEBUG, "received another a UDP fd!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,24 +132,27 @@ int handle_commands_from_main(struct worker_st *ws)
|
|||||||
if (has_hello == 0) {
|
if (has_hello == 0) {
|
||||||
/* check if the first packet received is a valid one -
|
/* check if the first packet received is a valid one -
|
||||||
* if not discard the new fd */
|
* if not discard the new fd */
|
||||||
if (!recv_from_new_fd(ws, fd, &tmsg)) {
|
if (!recv_from_new_fd(ws, DTLS_ACTIVE(ws), fd, &tmsg)) {
|
||||||
oclog(ws, LOG_INFO, "received UDP fd message but its session has invalid data!");
|
oclog(ws, LOG_INFO, "received UDP fd message but its session has invalid data!");
|
||||||
if (tmsg)
|
if (tmsg)
|
||||||
udp_fd_msg__free_unpacked(tmsg, NULL);
|
udp_fd_msg__free_unpacked(tmsg, NULL);
|
||||||
close(fd);
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
dtls = DTLS_ACTIVE(ws);
|
||||||
} else { /* received client hello */
|
} else { /* received client hello */
|
||||||
ws->udp_state = UP_SETUP;
|
dtls = DTLS_INACTIVE(ws);
|
||||||
|
dtls->udp_state = UP_SETUP;
|
||||||
|
oclog(ws, LOG_DEBUG, "Starting DTLS session %d", ws->dtls_active_session ^ 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ws->dtls_tptr.fd != -1)
|
if (dtls->dtls_tptr.fd != -1)
|
||||||
close(ws->dtls_tptr.fd);
|
close(dtls->dtls_tptr.fd);
|
||||||
if (ws->dtls_tptr.msg != NULL)
|
if (dtls->dtls_tptr.msg != NULL)
|
||||||
udp_fd_msg__free_unpacked(ws->dtls_tptr.msg, NULL);
|
udp_fd_msg__free_unpacked(dtls->dtls_tptr.msg, NULL);
|
||||||
|
|
||||||
ws->dtls_tptr.msg = tmsg;
|
dtls->dtls_tptr.msg = tmsg;
|
||||||
ws->dtls_tptr.fd = fd;
|
dtls->dtls_tptr.fd = fd;
|
||||||
|
|
||||||
if (WSCONFIG(ws)->try_mtu == 0)
|
if (WSCONFIG(ws)->try_mtu == 0)
|
||||||
set_mtu_disc(fd, ws->proto, 0);
|
set_mtu_disc(fd, ws->proto, 0);
|
||||||
@ -170,8 +174,8 @@ int handle_commands_from_main(struct worker_st *ws)
|
|||||||
udp_fd_fail:
|
udp_fd_fail:
|
||||||
if (tmsg)
|
if (tmsg)
|
||||||
udp_fd_msg__free_unpacked(tmsg, NULL);
|
udp_fd_msg__free_unpacked(tmsg, NULL);
|
||||||
if (ws->dtls_tptr.fd == -1)
|
if (dtls && dtls->dtls_tptr.fd == -1)
|
||||||
ws->udp_state = UP_DISABLED;
|
dtls->udp_state = UP_DISABLED;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,7 @@ int disable_system_calls(struct worker_st *ws)
|
|||||||
ADD_SYSCALL(getsockopt, 0);
|
ADD_SYSCALL(getsockopt, 0);
|
||||||
ADD_SYSCALL(setsockopt, 0);
|
ADD_SYSCALL(setsockopt, 0);
|
||||||
|
|
||||||
|
|
||||||
#ifdef ANYCONNECT_CLIENT_COMPAT
|
#ifdef ANYCONNECT_CLIENT_COMPAT
|
||||||
/* we need to open files when we have an xml_config_file setup on any vhost */
|
/* we need to open files when we have an xml_config_file setup on any vhost */
|
||||||
list_for_each(ws->vconfig, vhost, list) {
|
list_for_each(ws->vconfig, vhost, list) {
|
||||||
@ -185,6 +186,13 @@ int disable_system_calls(struct worker_st *ws)
|
|||||||
* the TUN device */
|
* the TUN device */
|
||||||
ADD_SYSCALL(ioctl, 1, SCMP_A1(SCMP_CMP_EQ, (int)SIOCGIFMTU));
|
ADD_SYSCALL(ioctl, 1, SCMP_A1(SCMP_CMP_EQ, (int)SIOCGIFMTU));
|
||||||
|
|
||||||
|
// Add calls to support libev
|
||||||
|
ADD_SYSCALL(epoll_wait, 0);
|
||||||
|
ADD_SYSCALL(epoll_create1, 0);
|
||||||
|
ADD_SYSCALL(epoll_ctl, 0);
|
||||||
|
ADD_SYSCALL(rt_sigaction, 0);
|
||||||
|
ADD_SYSCALL(eventfd2, 0);
|
||||||
|
|
||||||
ret = seccomp_load(ctx);
|
ret = seccomp_load(ctx);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
oclog(ws, LOG_DEBUG, "could not load seccomp filter");
|
oclog(ws, LOG_DEBUG, "could not load seccomp filter");
|
||||||
|
593
src/worker-vpn.c
593
src/worker-vpn.c
@ -48,6 +48,7 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <ev.h>
|
||||||
|
|
||||||
#if defined(__linux__) && !defined(IPV6_PATHMTU)
|
#if defined(__linux__) && !defined(IPV6_PATHMTU)
|
||||||
# define IPV6_PATHMTU 61
|
# define IPV6_PATHMTU 61
|
||||||
@ -87,11 +88,26 @@
|
|||||||
|
|
||||||
#define MSS_ADJUST(x) x += TCP_HEADER_SIZE + ((ws->proto == AF_INET)?(IP_HEADER_SIZE):(IPV6_HEADER_SIZE))
|
#define MSS_ADJUST(x) x += TCP_HEADER_SIZE + ((ws->proto == AF_INET)?(IP_HEADER_SIZE):(IPV6_HEADER_SIZE))
|
||||||
|
|
||||||
|
#define WORKER_MAINTENANCE_TIME (10.)
|
||||||
|
|
||||||
struct worker_st *global_ws = NULL;
|
struct worker_st *global_ws = NULL;
|
||||||
|
|
||||||
static int terminate = 0;
|
static int terminate = 0;
|
||||||
static int terminate_reason = REASON_SERVER_DISCONNECT;
|
static int terminate_reason = REASON_SERVER_DISCONNECT;
|
||||||
|
|
||||||
|
static struct ev_loop *loop = NULL;
|
||||||
|
ev_io command_watcher;
|
||||||
|
ev_io tls_watcher;
|
||||||
|
ev_io tun_watcher;
|
||||||
|
ev_timer period_check_watcher;
|
||||||
|
ev_signal term_sig_watcher;
|
||||||
|
ev_signal int_sig_watcher;
|
||||||
|
ev_signal alarm_sig_watcher;
|
||||||
|
|
||||||
|
static void term_sig_watcher_cb(struct ev_loop *loop, ev_signal *w, int revents);
|
||||||
|
|
||||||
|
static int worker_event_loop(struct worker_st * ws);
|
||||||
|
|
||||||
static int parse_cstp_data(struct worker_st *ws, uint8_t * buf, size_t buf_size,
|
static int parse_cstp_data(struct worker_st *ws, uint8_t * buf, size_t buf_size,
|
||||||
time_t);
|
time_t);
|
||||||
static int parse_dtls_data(struct worker_st *ws, uint8_t * buf, size_t buf_size,
|
static int parse_dtls_data(struct worker_st *ws, uint8_t * buf, size_t buf_size,
|
||||||
@ -101,10 +117,12 @@ static void session_info_send(worker_st * ws);
|
|||||||
static void set_net_priority(worker_st * ws, int fd, int priority);
|
static void set_net_priority(worker_st * ws, int fd, int priority);
|
||||||
static void set_socket_timeout(worker_st * ws, int fd);
|
static void set_socket_timeout(worker_st * ws, int fd);
|
||||||
|
|
||||||
static void link_mtu_set(worker_st * ws, unsigned mtu);
|
static void link_mtu_set(worker_st * ws, struct dtls_st * dtls, unsigned mtu);
|
||||||
|
|
||||||
static int test_for_tcp_health_probe(struct worker_st *ws);
|
static int test_for_tcp_health_probe(struct worker_st *ws);
|
||||||
|
|
||||||
|
static void dtls_watcher_cb (EV_P_ ev_io * w, int revents);
|
||||||
|
|
||||||
static void handle_alarm(int signo)
|
static void handle_alarm(int signo)
|
||||||
{
|
{
|
||||||
if (global_ws)
|
if (global_ws)
|
||||||
@ -115,9 +133,9 @@ static void handle_alarm(int signo)
|
|||||||
|
|
||||||
static void handle_term(int signo)
|
static void handle_term(int signo)
|
||||||
{
|
{
|
||||||
terminate = 1;
|
terminate = 1;
|
||||||
terminate_reason = REASON_SERVER_DISCONNECT;
|
terminate_reason = REASON_SERVER_DISCONNECT;
|
||||||
alarm(2); /* force exit by SIGALRM */
|
alarm(2); /* force exit by SIGALRM */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we override that function to force gnutls use poll()
|
/* we override that function to force gnutls use poll()
|
||||||
@ -334,7 +352,7 @@ static int setup_legacy_dtls_keys(gnutls_session_t session, struct worker_st *ws
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int setup_dtls_connection(struct worker_st *ws)
|
static int setup_dtls_connection(struct worker_st *ws, struct dtls_st * dtls)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
gnutls_session_t session;
|
gnutls_session_t session;
|
||||||
@ -377,17 +395,17 @@ static int setup_dtls_connection(struct worker_st *ws)
|
|||||||
gnutls_transport_set_pull_function(session, dtls_pull);
|
gnutls_transport_set_pull_function(session, dtls_pull);
|
||||||
#endif
|
#endif
|
||||||
gnutls_transport_set_pull_timeout_function(session, dtls_pull_timeout);
|
gnutls_transport_set_pull_timeout_function(session, dtls_pull_timeout);
|
||||||
gnutls_transport_set_ptr(session, &ws->dtls_tptr);
|
gnutls_transport_set_ptr(session, &dtls->dtls_tptr);
|
||||||
|
|
||||||
/* we decrease the default retransmission timeout to bring
|
/* we decrease the default retransmission timeout to bring
|
||||||
* our DTLS support in par with the DTLS1.3 recommendations.
|
* our DTLS support in par with the DTLS1.3 recommendations.
|
||||||
*/
|
*/
|
||||||
gnutls_dtls_set_timeouts(session, 400, 60*1000);
|
gnutls_dtls_set_timeouts(session, 400, 60*1000);
|
||||||
|
|
||||||
ws->udp_state = UP_HANDSHAKE;
|
dtls->udp_state = UP_HANDSHAKE;
|
||||||
|
|
||||||
#if defined(CAPTURE_LATENCY_SUPPORT)
|
#if defined(CAPTURE_LATENCY_SUPPORT)
|
||||||
ret = setsockopt(ws->dtls_tptr.fd, SOL_SOCKET, SO_TIMESTAMPING, &ts_socket_opt, sizeof(ts_socket_opt));
|
ret = setsockopt(dtls->dtls_tptr.fd, SOL_SOCKET, SO_TIMESTAMPING, &ts_socket_opt, sizeof(ts_socket_opt));
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
oclog(ws, LOG_DEBUG, "setsockopt(UDP, SO_TIMESTAMPING), failed.");
|
oclog(ws, LOG_DEBUG, "setsockopt(UDP, SO_TIMESTAMPING), failed.");
|
||||||
#endif
|
#endif
|
||||||
@ -395,24 +413,28 @@ static int setup_dtls_connection(struct worker_st *ws)
|
|||||||
/* Setup the fd settings */
|
/* Setup the fd settings */
|
||||||
if (WSCONFIG(ws)->output_buffer > 0) {
|
if (WSCONFIG(ws)->output_buffer > 0) {
|
||||||
int t = MIN(2048, ws->link_mtu * WSCONFIG(ws)->output_buffer);
|
int t = MIN(2048, ws->link_mtu * WSCONFIG(ws)->output_buffer);
|
||||||
ret = setsockopt(ws->dtls_tptr.fd, SOL_SOCKET, SO_SNDBUF, &t,
|
ret = setsockopt(dtls->dtls_tptr.fd, SOL_SOCKET, SO_SNDBUF, &t,
|
||||||
sizeof(t));
|
sizeof(t));
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
oclog(ws, LOG_DEBUG,
|
oclog(ws, LOG_DEBUG,
|
||||||
"setsockopt(UDP, SO_SNDBUF) to %u, failed.",
|
"setsockopt(UDP, SO_SNDBUF) to %u, failed.",
|
||||||
t);
|
t);
|
||||||
}
|
}
|
||||||
set_net_priority(ws, ws->dtls_tptr.fd, ws->user_config->net_priority);
|
set_net_priority(ws, dtls->dtls_tptr.fd, ws->user_config->net_priority);
|
||||||
set_socket_timeout(ws, ws->dtls_tptr.fd);
|
set_socket_timeout(ws, dtls->dtls_tptr.fd);
|
||||||
|
|
||||||
/* reset MTU */
|
/* reset MTU */
|
||||||
link_mtu_set(ws, ws->adv_link_mtu);
|
link_mtu_set(ws, dtls, ws->adv_link_mtu);
|
||||||
|
|
||||||
if (ws->dtls_session != NULL) {
|
if (dtls->dtls_session != NULL) {
|
||||||
gnutls_deinit(ws->dtls_session);
|
gnutls_deinit(dtls->dtls_session);
|
||||||
}
|
}
|
||||||
|
|
||||||
ws->dtls_session = session;
|
dtls->dtls_session = session;
|
||||||
|
ev_init(&dtls->io, dtls_watcher_cb);
|
||||||
|
ev_io_set(&dtls->io, dtls->dtls_tptr.fd, EV_READ);
|
||||||
|
ev_io_start(loop, &dtls->io);
|
||||||
|
ev_invoke(loop, &dtls->io, EV_READ);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
fail:
|
fail:
|
||||||
@ -980,9 +1002,9 @@ void session_info_send(worker_st * ws)
|
|||||||
msg.cstp_compr = (char*)ws->cstp_selected_comp->name;
|
msg.cstp_compr = (char*)ws->cstp_selected_comp->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ws->udp_state != UP_DISABLED && ws->dtls_session) {
|
if (DTLS_ACTIVE(ws)->udp_state != UP_DISABLED && DTLS_ACTIVE(ws)->dtls_session) {
|
||||||
msg.dtls_ciphersuite =
|
msg.dtls_ciphersuite =
|
||||||
gnutls_session_get_desc(ws->dtls_session);
|
gnutls_session_get_desc(DTLS_ACTIVE(ws)->dtls_session);
|
||||||
if (ws->dtls_selected_comp)
|
if (ws->dtls_selected_comp)
|
||||||
msg.dtls_compr = (char*)ws->dtls_selected_comp->name;
|
msg.dtls_compr = (char*)ws->dtls_selected_comp->name;
|
||||||
}
|
}
|
||||||
@ -1010,7 +1032,7 @@ void session_info_send(worker_st * ws)
|
|||||||
* @mtu: the link MTU
|
* @mtu: the link MTU
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
void link_mtu_set(worker_st * ws, unsigned mtu)
|
void link_mtu_set(struct worker_st * ws, struct dtls_st * dtls, unsigned mtu)
|
||||||
{
|
{
|
||||||
if (ws->link_mtu == mtu || mtu > sizeof(ws->buffer))
|
if (ws->link_mtu == mtu || mtu > sizeof(ws->buffer))
|
||||||
return;
|
return;
|
||||||
@ -1018,8 +1040,8 @@ void link_mtu_set(worker_st * ws, unsigned mtu)
|
|||||||
ws->link_mtu = mtu;
|
ws->link_mtu = mtu;
|
||||||
|
|
||||||
oclog(ws, LOG_DEBUG, "setting connection link MTU to %u", mtu);
|
oclog(ws, LOG_DEBUG, "setting connection link MTU to %u", mtu);
|
||||||
if (ws->dtls_session)
|
if (dtls->dtls_session)
|
||||||
gnutls_dtls_set_mtu(ws->dtls_session,
|
gnutls_dtls_set_mtu(dtls->dtls_session,
|
||||||
ws->link_mtu - ws->dtls_proto_overhead);
|
ws->link_mtu - ws->dtls_proto_overhead);
|
||||||
|
|
||||||
data_mtu_send(ws, DATA_MTU(ws, ws->link_mtu));
|
data_mtu_send(ws, DATA_MTU(ws, ws->link_mtu));
|
||||||
@ -1031,25 +1053,25 @@ void link_mtu_set(worker_st * ws, unsigned mtu)
|
|||||||
* @mtu: the "plaintext" data MTU (not including the DTLS protocol byte)
|
* @mtu: the "plaintext" data MTU (not including the DTLS protocol byte)
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
void data_mtu_set(worker_st * ws, unsigned mtu)
|
void data_mtu_set(worker_st * ws, struct dtls_st * dtls, unsigned mtu)
|
||||||
{
|
{
|
||||||
if (ws->dtls_session) {
|
if (dtls->dtls_session) {
|
||||||
gnutls_dtls_set_data_mtu(ws->dtls_session, mtu+1);
|
gnutls_dtls_set_data_mtu(dtls->dtls_session, mtu+1);
|
||||||
|
|
||||||
mtu = gnutls_dtls_get_mtu(ws->dtls_session);
|
mtu = gnutls_dtls_get_mtu(dtls->dtls_session);
|
||||||
if (mtu <= 0 || mtu == ws->link_mtu)
|
if (mtu <= 0 || mtu == ws->link_mtu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mtu += ws->dtls_proto_overhead;
|
mtu += ws->dtls_proto_overhead;
|
||||||
link_mtu_set(ws, mtu);
|
link_mtu_set(ws, dtls, mtu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disable_mtu_disc(worker_st *ws)
|
static void disable_mtu_disc(worker_st *ws, struct dtls_st * dtls)
|
||||||
{
|
{
|
||||||
oclog(ws, LOG_DEBUG, "disabling MTU discovery on UDP socket");
|
oclog(ws, LOG_DEBUG, "disabling MTU discovery on UDP socket");
|
||||||
set_mtu_disc(ws->dtls_tptr.fd, ws->proto, 0);
|
set_mtu_disc(dtls->dtls_tptr.fd, ws->proto, 0);
|
||||||
link_mtu_set(ws, ws->adv_link_mtu);
|
link_mtu_set(ws, dtls, ws->adv_link_mtu);
|
||||||
WSCONFIG(ws)->try_mtu = 0;
|
WSCONFIG(ws)->try_mtu = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1059,9 +1081,9 @@ static void disable_mtu_disc(worker_st *ws)
|
|||||||
* Returns -1 on failure.
|
* Returns -1 on failure.
|
||||||
*/
|
*/
|
||||||
static
|
static
|
||||||
int mtu_not_ok(worker_st * ws)
|
int mtu_not_ok(worker_st * ws, struct dtls_st * dtls)
|
||||||
{
|
{
|
||||||
if (WSCONFIG(ws)->try_mtu == 0 || ws->dtls_session == NULL)
|
if (WSCONFIG(ws)->try_mtu == 0 || dtls->dtls_session == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (ws->proto == AF_INET) {
|
if (ws->proto == AF_INET) {
|
||||||
@ -1072,8 +1094,8 @@ int mtu_not_ok(worker_st * ws)
|
|||||||
if (ws->last_good_mtu == min) {
|
if (ws->last_good_mtu == min) {
|
||||||
oclog(ws, LOG_INFO,
|
oclog(ws, LOG_INFO,
|
||||||
"could not calculate a sufficient MTU; disabling MTU discovery");
|
"could not calculate a sufficient MTU; disabling MTU discovery");
|
||||||
disable_mtu_disc(ws);
|
disable_mtu_disc(ws, dtls);
|
||||||
link_mtu_set(ws, min);
|
link_mtu_set(ws, dtls, min);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1081,7 +1103,7 @@ int mtu_not_ok(worker_st * ws)
|
|||||||
ws->last_good_mtu = MAX(((2 * (ws->link_mtu)) / 3), min);
|
ws->last_good_mtu = MAX(((2 * (ws->link_mtu)) / 3), min);
|
||||||
}
|
}
|
||||||
|
|
||||||
link_mtu_set(ws, ws->last_good_mtu);
|
link_mtu_set(ws, dtls, ws->last_good_mtu);
|
||||||
oclog(ws, LOG_INFO, "MTU %u is too large, switching to %u",
|
oclog(ws, LOG_INFO, "MTU %u is too large, switching to %u",
|
||||||
ws->last_bad_mtu, ws->link_mtu);
|
ws->last_bad_mtu, ws->link_mtu);
|
||||||
} else if (ws->proto == AF_INET6) { /* IPv6 */
|
} else if (ws->proto == AF_INET6) { /* IPv6 */
|
||||||
@ -1089,16 +1111,16 @@ int mtu_not_ok(worker_st * ws)
|
|||||||
struct ip6_mtuinfo mtuinfo;
|
struct ip6_mtuinfo mtuinfo;
|
||||||
socklen_t len = sizeof(mtuinfo);
|
socklen_t len = sizeof(mtuinfo);
|
||||||
|
|
||||||
if (getsockopt(ws->dtls_tptr.fd, IPPROTO_IPV6, IPV6_PATHMTU, &mtuinfo, &len) < 0 || mtuinfo.ip6m_mtu < 1280) {
|
if (getsockopt(dtls->dtls_tptr.fd, IPPROTO_IPV6, IPV6_PATHMTU, &mtuinfo, &len) < 0 || mtuinfo.ip6m_mtu < 1280) {
|
||||||
oclog(ws, LOG_INFO, "cannot obtain IPv6 MTU (was %u); disabling MTU discovery",
|
oclog(ws, LOG_INFO, "cannot obtain IPv6 MTU (was %u); disabling MTU discovery",
|
||||||
ws->link_mtu);
|
ws->link_mtu);
|
||||||
disable_mtu_disc(ws);
|
disable_mtu_disc(ws, dtls);
|
||||||
link_mtu_set(ws, MIN_MTU(ws));
|
link_mtu_set(ws, dtls, MIN_MTU(ws));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
oclog(ws, LOG_DEBUG, "setting (via IPV6_PATHMTU) connection MTU to %u", mtuinfo.ip6m_mtu);
|
oclog(ws, LOG_DEBUG, "setting (via IPV6_PATHMTU) connection MTU to %u", mtuinfo.ip6m_mtu);
|
||||||
link_mtu_set(ws, mtuinfo.ip6m_mtu);
|
link_mtu_set(ws, dtls, mtuinfo.ip6m_mtu);
|
||||||
|
|
||||||
if (mtuinfo.ip6m_mtu > ws->adv_link_mtu) {
|
if (mtuinfo.ip6m_mtu > ws->adv_link_mtu) {
|
||||||
oclog(ws, LOG_INFO, "the discovered IPv6 MTU (%u) is larger than the advertised (%u); disabling MTU discovery",
|
oclog(ws, LOG_INFO, "the discovered IPv6 MTU (%u) is larger than the advertised (%u); disabling MTU discovery",
|
||||||
@ -1106,7 +1128,7 @@ int mtu_not_ok(worker_st * ws)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
link_mtu_set(ws, MIN_MTU(ws));
|
link_mtu_set(ws, dtls, MIN_MTU(ws));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1118,13 +1140,13 @@ int mtu_not_ok(worker_st * ws)
|
|||||||
* @ws: a worker structure
|
* @ws: a worker structure
|
||||||
* @mtu: the current "plaintext" data MTU
|
* @mtu: the current "plaintext" data MTU
|
||||||
*/
|
*/
|
||||||
static void mtu_discovery_init(worker_st * ws, unsigned mtu)
|
static void mtu_discovery_init(worker_st * ws, struct dtls_st * dtls, unsigned mtu)
|
||||||
{
|
{
|
||||||
const unsigned min = MIN_MTU(ws);
|
const unsigned min = MIN_MTU(ws);
|
||||||
if (mtu <= min) {
|
if (mtu <= min) {
|
||||||
oclog(ws, LOG_INFO,
|
oclog(ws, LOG_INFO,
|
||||||
"our initial MTU is too low; disabling MTU discovery");
|
"our initial MTU is too low; disabling MTU discovery");
|
||||||
disable_mtu_disc(ws);
|
disable_mtu_disc(ws, dtls);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!WSCONFIG(ws)->try_mtu)
|
if (!WSCONFIG(ws)->try_mtu)
|
||||||
@ -1136,7 +1158,7 @@ static void mtu_discovery_init(worker_st * ws, unsigned mtu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
void mtu_ok(worker_st * ws)
|
void mtu_ok(worker_st * ws, struct dtls_st * dtls)
|
||||||
{
|
{
|
||||||
unsigned int c;
|
unsigned int c;
|
||||||
|
|
||||||
@ -1150,7 +1172,7 @@ void mtu_ok(worker_st * ws)
|
|||||||
ws->last_good_mtu = ws->link_mtu;
|
ws->last_good_mtu = ws->link_mtu;
|
||||||
c = (ws->link_mtu + ws->last_bad_mtu) / 2;
|
c = (ws->link_mtu + ws->last_bad_mtu) / 2;
|
||||||
|
|
||||||
link_mtu_set(ws, c);
|
link_mtu_set(ws, dtls, c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1252,7 +1274,7 @@ int periodic_check(worker_st * ws, struct timespec *tnow, unsigned dpd)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* check DPD. Otherwise exit */
|
/* check DPD. Otherwise exit */
|
||||||
if (ws->udp_state == UP_ACTIVE &&
|
if (DTLS_ACTIVE(ws)->udp_state == UP_ACTIVE &&
|
||||||
now - ws->last_msg_udp > DPD_TRIES * dpd && dpd > 0) {
|
now - ws->last_msg_udp > DPD_TRIES * dpd && dpd > 0) {
|
||||||
unsigned data_mtu = DATA_MTU(ws, ws->link_mtu);
|
unsigned data_mtu = DATA_MTU(ws, ws->link_mtu);
|
||||||
oclog(ws, LOG_ERR,
|
oclog(ws, LOG_ERR,
|
||||||
@ -1262,13 +1284,13 @@ int periodic_check(worker_st * ws, struct timespec *tnow, unsigned dpd)
|
|||||||
memset(ws->buffer+1, 0, data_mtu);
|
memset(ws->buffer+1, 0, data_mtu);
|
||||||
ws->buffer[0] = AC_PKT_DPD_OUT;
|
ws->buffer[0] = AC_PKT_DPD_OUT;
|
||||||
|
|
||||||
ret = dtls_send(ws, ws->buffer, data_mtu+1);
|
ret = dtls_send(DTLS_ACTIVE(ws), ws->buffer, data_mtu+1);
|
||||||
DTLS_FATAL_ERR_CMD(ret, exit_worker_reason(ws, REASON_ERROR));
|
DTLS_FATAL_ERR_CMD(ret, exit_worker_reason(ws, REASON_ERROR));
|
||||||
|
|
||||||
if (now - ws->last_msg_udp > DPD_MAX_TRIES * dpd) {
|
if (now - ws->last_msg_udp > DPD_MAX_TRIES * dpd) {
|
||||||
oclog(ws, LOG_ERR,
|
oclog(ws, LOG_ERR,
|
||||||
"have not received UDP message or DPD for very long; disabling UDP port");
|
"have not received UDP message or DPD for very long; disabling UDP port");
|
||||||
ws->udp_state = UP_INACTIVE;
|
DTLS_ACTIVE(ws)->udp_state = UP_INACTIVE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dpd > 0 && now - ws->last_msg_tcp > DPD_TRIES * dpd) {
|
if (dpd > 0 && now - ws->last_msg_tcp > DPD_TRIES * dpd) {
|
||||||
@ -1294,12 +1316,12 @@ int periodic_check(worker_st * ws, struct timespec *tnow, unsigned dpd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ws->conn_type != SOCK_TYPE_UNIX && ws->udp_state != UP_DISABLED) {
|
if (ws->conn_type != SOCK_TYPE_UNIX && DTLS_ACTIVE(ws)->udp_state != UP_DISABLED) {
|
||||||
max = get_pmtu_approx(ws);
|
max = get_pmtu_approx(ws);
|
||||||
if (max > 0 && max < ws->link_mtu) {
|
if (max > 0 && max < ws->link_mtu) {
|
||||||
oclog(ws, LOG_DEBUG, "reducing MTU due to TCP/PMTU to %u",
|
oclog(ws, LOG_DEBUG, "reducing MTU due to TCP/PMTU to %u",
|
||||||
max);
|
max);
|
||||||
link_mtu_set(ws, max);
|
link_mtu_set(ws, DTLS_ACTIVE(ws), max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1359,16 +1381,16 @@ static void set_net_priority(worker_st * ws, int fd, int priority)
|
|||||||
|
|
||||||
#define SEND_ERR(x) if (x<0) goto send_error
|
#define SEND_ERR(x) if (x<0) goto send_error
|
||||||
|
|
||||||
static int dtls_mainloop(worker_st * ws, struct timespec *tnow)
|
static int dtls_mainloop(worker_st * ws, struct dtls_st * dtls, struct timespec *tnow)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
gnutls_datum_t data;
|
gnutls_datum_t data;
|
||||||
void *packet = NULL;
|
void *packet = NULL;
|
||||||
|
|
||||||
switch (ws->udp_state) {
|
switch (dtls->udp_state) {
|
||||||
case UP_ACTIVE:
|
case UP_ACTIVE:
|
||||||
case UP_INACTIVE:
|
case UP_INACTIVE:
|
||||||
ret = dtls_recv_packet(ws, &data, &packet);
|
ret = dtls_recv_packet(dtls, &data, &packet);
|
||||||
oclog(ws, LOG_TRANSFER_DEBUG,
|
oclog(ws, LOG_TRANSFER_DEBUG,
|
||||||
"received %d byte(s) (DTLS)", ret);
|
"received %d byte(s) (DTLS)", ret);
|
||||||
|
|
||||||
@ -1376,8 +1398,8 @@ static int dtls_mainloop(worker_st * ws, struct timespec *tnow)
|
|||||||
|
|
||||||
if (ret == GNUTLS_E_REHANDSHAKE) {
|
if (ret == GNUTLS_E_REHANDSHAKE) {
|
||||||
|
|
||||||
if (ws->last_dtls_rehandshake > 0 &&
|
if (dtls->last_dtls_rehandshake > 0 &&
|
||||||
tnow->tv_sec - ws->last_dtls_rehandshake <
|
tnow->tv_sec - dtls->last_dtls_rehandshake <
|
||||||
WSCONFIG(ws)->rekey_time / 2) {
|
WSCONFIG(ws)->rekey_time / 2) {
|
||||||
oclog(ws, LOG_INFO,
|
oclog(ws, LOG_INFO,
|
||||||
"client requested DTLS rehandshake too soon");
|
"client requested DTLS rehandshake too soon");
|
||||||
@ -1392,18 +1414,18 @@ static int dtls_mainloop(worker_st * ws, struct timespec *tnow)
|
|||||||
"client requested rehandshake on DTLS channel");
|
"client requested rehandshake on DTLS channel");
|
||||||
|
|
||||||
do {
|
do {
|
||||||
ret = gnutls_handshake(ws->dtls_session);
|
ret = gnutls_handshake(dtls->dtls_session);
|
||||||
} while (ret == GNUTLS_E_AGAIN
|
} while (ret == GNUTLS_E_AGAIN
|
||||||
|| ret == GNUTLS_E_INTERRUPTED);
|
|| ret == GNUTLS_E_INTERRUPTED);
|
||||||
|
|
||||||
DTLS_FATAL_ERR_CMD(ret, exit_worker_reason(ws, REASON_ERROR));
|
DTLS_FATAL_ERR_CMD(ret, exit_worker_reason(ws, REASON_ERROR));
|
||||||
oclog(ws, LOG_DEBUG, "DTLS rehandshake completed");
|
oclog(ws, LOG_DEBUG, "DTLS rehandshake completed");
|
||||||
|
|
||||||
ws->last_dtls_rehandshake = tnow->tv_sec;
|
dtls->last_dtls_rehandshake = tnow->tv_sec;
|
||||||
} else if (ret >= 1) {
|
} else if (ret >= 1) {
|
||||||
/* where we receive any DTLS UDP packet we reset the state
|
/* where we receive any DTLS UDP packet we reset the state
|
||||||
* to active */
|
* to active */
|
||||||
ws->udp_state = UP_ACTIVE;
|
dtls->udp_state = UP_ACTIVE;
|
||||||
|
|
||||||
if (bandwidth_update
|
if (bandwidth_update
|
||||||
(&ws->b_rx, data.size - CSTP_DTLS_OVERHEAD, tnow) != 0) {
|
(&ws->b_rx, data.size - CSTP_DTLS_OVERHEAD, tnow) != 0) {
|
||||||
@ -1423,50 +1445,53 @@ static int dtls_mainloop(worker_st * ws, struct timespec *tnow)
|
|||||||
ws->udp_recv_time = tnow->tv_sec;
|
ws->udp_recv_time = tnow->tv_sec;
|
||||||
break;
|
break;
|
||||||
case UP_SETUP:
|
case UP_SETUP:
|
||||||
ret = setup_dtls_connection(ws);
|
ret = setup_dtls_connection(ws, dtls);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ret = -1;
|
ret = -1;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
gnutls_dtls_set_mtu(ws->dtls_session, ws->link_mtu - ws->dtls_proto_overhead);
|
gnutls_dtls_set_mtu(dtls->dtls_session, ws->link_mtu - ws->dtls_proto_overhead);
|
||||||
mtu_discovery_init(ws, ws->link_mtu);
|
mtu_discovery_init(ws, dtls, ws->link_mtu);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UP_HANDSHAKE:
|
case UP_HANDSHAKE:
|
||||||
hsk_restart:
|
hsk_restart:
|
||||||
ret = gnutls_handshake(ws->dtls_session);
|
ret = gnutls_handshake(dtls->dtls_session);
|
||||||
if (ret < 0 && gnutls_error_is_fatal(ret) != 0) {
|
if (ret < 0 && gnutls_error_is_fatal(ret) != 0) {
|
||||||
if (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)
|
if (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)
|
||||||
oclog(ws, LOG_ERR,
|
oclog(ws, LOG_ERR,
|
||||||
"error in DTLS handshake: %s: %s\n",
|
"error in DTLS handshake: %s: %s\n",
|
||||||
gnutls_strerror(ret),
|
gnutls_strerror(ret),
|
||||||
gnutls_alert_get_name
|
gnutls_alert_get_name
|
||||||
(gnutls_alert_get(ws->dtls_session)));
|
(gnutls_alert_get(dtls->dtls_session)));
|
||||||
else
|
else
|
||||||
oclog(ws, LOG_ERR,
|
oclog(ws, LOG_ERR,
|
||||||
"error in DTLS handshake: %s\n",
|
"error in DTLS handshake: %s\n",
|
||||||
gnutls_strerror(ret));
|
gnutls_strerror(ret));
|
||||||
ws->udp_state = UP_DISABLED;
|
dtls->udp_state = UP_DISABLED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == GNUTLS_E_LARGE_PACKET) {
|
if (ret == GNUTLS_E_LARGE_PACKET) {
|
||||||
/* adjust mtu */
|
/* adjust mtu */
|
||||||
mtu_not_ok(ws);
|
mtu_not_ok(ws, dtls);
|
||||||
goto hsk_restart;
|
goto hsk_restart;
|
||||||
} else if (ret == 0) {
|
} else if (ret == 0) {
|
||||||
unsigned data_mtu;
|
unsigned data_mtu;
|
||||||
|
|
||||||
/* gnutls_dtls_get_data_mtu() already subtracts the crypto overhead */
|
/* gnutls_dtls_get_data_mtu() already subtracts the crypto overhead */
|
||||||
data_mtu =
|
data_mtu =
|
||||||
gnutls_dtls_get_data_mtu(ws->dtls_session) -
|
gnutls_dtls_get_data_mtu(dtls->dtls_session) -
|
||||||
CSTP_DTLS_OVERHEAD;
|
CSTP_DTLS_OVERHEAD;
|
||||||
|
|
||||||
ws->udp_state = UP_ACTIVE;
|
dtls->udp_state = UP_ACTIVE;
|
||||||
oclog(ws, LOG_DEBUG,
|
oclog(ws, LOG_DEBUG,
|
||||||
"DTLS handshake completed (link MTU: %u, data MTU: %u)\n",
|
"DTLS handshake completed (link MTU: %u, data MTU: %u)\n",
|
||||||
ws->link_mtu, data_mtu);
|
ws->link_mtu, data_mtu);
|
||||||
|
ws->dtls_active_session++;
|
||||||
|
oclog(ws, LOG_DEBUG,
|
||||||
|
"Maing DTLS session %d active", ws->dtls_active_session);
|
||||||
session_info_send(ws);
|
session_info_send(ws);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1510,11 +1535,11 @@ static int tls_mainloop(struct worker_st *ws, struct timespec *tnow)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret == AC_PKT_DATA || ret == AC_PKT_COMPRESSED) && ws->udp_state == UP_ACTIVE) {
|
if ((ret == AC_PKT_DATA || ret == AC_PKT_COMPRESSED) && DTLS_ACTIVE(ws)->udp_state == UP_ACTIVE) {
|
||||||
/* client switched to TLS for some reason */
|
/* client switched to TLS for some reason */
|
||||||
if (tnow->tv_sec - ws->udp_recv_time >
|
if (tnow->tv_sec - ws->udp_recv_time >
|
||||||
UDP_SWITCH_TIME)
|
UDP_SWITCH_TIME)
|
||||||
ws->udp_state = UP_INACTIVE;
|
DTLS_ACTIVE(ws)->udp_state = UP_INACTIVE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1582,15 +1607,15 @@ static int tun_mainloop(struct worker_st *ws, struct timespec *tnow)
|
|||||||
cstp_to_send.size = l;
|
cstp_to_send.size = l;
|
||||||
|
|
||||||
if (WSCONFIG(ws)->switch_to_tcp_timeout &&
|
if (WSCONFIG(ws)->switch_to_tcp_timeout &&
|
||||||
ws->udp_state == UP_ACTIVE &&
|
DTLS_ACTIVE(ws)->udp_state == UP_ACTIVE &&
|
||||||
tnow->tv_sec > ws->udp_recv_time + WSCONFIG(ws)->switch_to_tcp_timeout) {
|
tnow->tv_sec > ws->udp_recv_time + WSCONFIG(ws)->switch_to_tcp_timeout) {
|
||||||
oclog(ws, LOG_DEBUG, "No UDP data received for %li seconds, using TCP instead\n",
|
oclog(ws, LOG_DEBUG, "No UDP data received for %li seconds, using TCP instead\n",
|
||||||
tnow->tv_sec - ws->udp_recv_time);
|
tnow->tv_sec - ws->udp_recv_time);
|
||||||
ws->udp_state = UP_INACTIVE;
|
DTLS_ACTIVE(ws)->udp_state = UP_INACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_COMPRESSION
|
#ifdef ENABLE_COMPRESSION
|
||||||
if (ws->udp_state == UP_ACTIVE && ws->dtls_selected_comp != NULL && l > WSCONFIG(ws)->no_compress_limit) {
|
if (DTLS_ACTIVE(ws)->udp_state == UP_ACTIVE && ws->dtls_selected_comp != NULL && l > WSCONFIG(ws)->no_compress_limit) {
|
||||||
/* otherwise don't compress */
|
/* otherwise don't compress */
|
||||||
ret = ws->dtls_selected_comp->compress(ws->decomp+8, sizeof(ws->decomp)-8, ws->buffer+8, l);
|
ret = ws->dtls_selected_comp->compress(ws->decomp+8, sizeof(ws->decomp)-8, ws->buffer+8, l);
|
||||||
oclog(ws, LOG_TRANSFER_DEBUG, "compressed %d to %d\n", (int)l, ret);
|
oclog(ws, LOG_TRANSFER_DEBUG, "compressed %d to %d\n", (int)l, ret);
|
||||||
@ -1626,27 +1651,27 @@ static int tun_mainloop(struct worker_st *ws, struct timespec *tnow)
|
|||||||
|
|
||||||
oclog(ws, LOG_TRANSFER_DEBUG, "sending %d byte(s)\n", l);
|
oclog(ws, LOG_TRANSFER_DEBUG, "sending %d byte(s)\n", l);
|
||||||
|
|
||||||
if (ws->udp_state == UP_ACTIVE) {
|
if (DTLS_ACTIVE(ws)->udp_state == UP_ACTIVE) {
|
||||||
|
|
||||||
ws->tun_bytes_out += dtls_to_send.size;
|
ws->tun_bytes_out += dtls_to_send.size;
|
||||||
|
|
||||||
dtls_to_send.data[7] = dtls_type;
|
dtls_to_send.data[7] = dtls_type;
|
||||||
ret = dtls_send(ws, dtls_to_send.data + 7, dtls_to_send.size + 1);
|
ret = dtls_send(DTLS_ACTIVE(ws), dtls_to_send.data + 7, dtls_to_send.size + 1);
|
||||||
DTLS_FATAL_ERR_CMD(ret, exit_worker_reason(ws, REASON_ERROR));
|
DTLS_FATAL_ERR_CMD(ret, exit_worker_reason(ws, REASON_ERROR));
|
||||||
|
|
||||||
if (ret == GNUTLS_E_LARGE_PACKET) {
|
if (ret == GNUTLS_E_LARGE_PACKET) {
|
||||||
mtu_not_ok(ws);
|
mtu_not_ok(ws, DTLS_ACTIVE(ws));
|
||||||
|
|
||||||
oclog(ws, LOG_TRANSFER_DEBUG,
|
oclog(ws, LOG_TRANSFER_DEBUG,
|
||||||
"retrying (TLS) %d\n", l);
|
"retrying (TLS) %d\n", l);
|
||||||
tls_retry = 1;
|
tls_retry = 1;
|
||||||
} else if (ret >= 1+DATA_MTU(ws, ws->link_mtu) &&
|
} else if (ret >= 1+DATA_MTU(ws, ws->link_mtu) &&
|
||||||
WSCONFIG(ws)->try_mtu != 0) {
|
WSCONFIG(ws)->try_mtu != 0) {
|
||||||
mtu_ok(ws);
|
mtu_ok(ws, DTLS_ACTIVE(ws));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ws->udp_state != UP_ACTIVE || tls_retry != 0) {
|
if (DTLS_ACTIVE(ws)->udp_state != UP_ACTIVE || tls_retry != 0) {
|
||||||
cstp_to_send.data[0] = 'S';
|
cstp_to_send.data[0] = 'S';
|
||||||
cstp_to_send.data[1] = 'T';
|
cstp_to_send.data[1] = 'T';
|
||||||
cstp_to_send.data[2] = 'F';
|
cstp_to_send.data[2] = 'F';
|
||||||
@ -1787,7 +1812,7 @@ static void calc_mtu_values(worker_st * ws)
|
|||||||
/* link MTU is the device MTU */
|
/* link MTU is the device MTU */
|
||||||
ws->link_mtu = ws->vinfo.mtu;
|
ws->link_mtu = ws->vinfo.mtu;
|
||||||
|
|
||||||
if (ws->udp_state != UP_DISABLED) {
|
if (DTLS_ACTIVE(ws)->udp_state != UP_DISABLED) {
|
||||||
/* crypto overhead for DTLS */
|
/* crypto overhead for DTLS */
|
||||||
if (ws->req.use_psk) {
|
if (ws->req.use_psk) {
|
||||||
if (ws->session == NULL) {
|
if (ws->session == NULL) {
|
||||||
@ -1832,22 +1857,11 @@ static void calc_mtu_values(worker_st * ws)
|
|||||||
static int connect_handler(worker_st * ws)
|
static int connect_handler(worker_st * ws)
|
||||||
{
|
{
|
||||||
struct http_req_st *req = &ws->req;
|
struct http_req_st *req = &ws->req;
|
||||||
struct pollfd pfd[4];
|
|
||||||
unsigned pfd_size;
|
|
||||||
int max, ret, t;
|
int max, ret, t;
|
||||||
char *p;
|
char *p;
|
||||||
unsigned rnd;
|
unsigned rnd;
|
||||||
#ifdef HAVE_PPOLL
|
unsigned i;
|
||||||
struct timespec tv;
|
|
||||||
#endif
|
|
||||||
unsigned tls_pending, dtls_pending = 0, i;
|
|
||||||
struct timespec tnow;
|
|
||||||
unsigned ip6;
|
unsigned ip6;
|
||||||
sigset_t emptyset, blockset;
|
|
||||||
|
|
||||||
sigemptyset(&blockset);
|
|
||||||
sigemptyset(&emptyset);
|
|
||||||
sigaddset(&blockset, SIGTERM);
|
|
||||||
|
|
||||||
gnutls_rnd(GNUTLS_RND_NONCE, &rnd, sizeof(rnd));
|
gnutls_rnd(GNUTLS_RND_NONCE, &rnd, sizeof(rnd));
|
||||||
|
|
||||||
@ -1930,10 +1944,14 @@ static int connect_handler(worker_st * ws)
|
|||||||
SEND_ERR(ret);
|
SEND_ERR(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
ws->udp_state = UP_DISABLED;
|
ws->dtls_active_session = 0;
|
||||||
|
DTLS_ACTIVE(ws)->udp_state = UP_DISABLED;
|
||||||
|
DTLS_INACTIVE(ws)->udp_state = UP_DISABLED;
|
||||||
|
|
||||||
if (WSPCONFIG(ws)->udp_port != 0 && req->master_secret_set != 0) {
|
if (WSPCONFIG(ws)->udp_port != 0 && req->master_secret_set != 0) {
|
||||||
memcpy(ws->master_secret, req->master_secret, TLS_MASTER_SIZE);
|
memcpy(ws->master_secret, req->master_secret, TLS_MASTER_SIZE);
|
||||||
ws->udp_state = UP_WAIT_FD;
|
DTLS_ACTIVE(ws)->udp_state = UP_WAIT_FD;
|
||||||
|
DTLS_INACTIVE(ws)->udp_state = UP_WAIT_FD;
|
||||||
} else {
|
} else {
|
||||||
oclog(ws, LOG_DEBUG, "disabling UDP (DTLS) connection");
|
oclog(ws, LOG_DEBUG, "disabling UDP (DTLS) connection");
|
||||||
}
|
}
|
||||||
@ -1959,7 +1977,7 @@ static int connect_handler(worker_st * ws)
|
|||||||
if (max > 0 && max < ws->vinfo.mtu) {
|
if (max > 0 && max < ws->vinfo.mtu) {
|
||||||
oclog(ws, LOG_DEBUG, "reducing MTU due to TCP/PMTU to %u",
|
oclog(ws, LOG_DEBUG, "reducing MTU due to TCP/PMTU to %u",
|
||||||
max);
|
max);
|
||||||
link_mtu_set(ws, max);
|
link_mtu_set(ws, DTLS_ACTIVE(ws), max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2216,7 +2234,7 @@ static int connect_handler(worker_st * ws)
|
|||||||
set_net_priority(ws, ws->conn_fd, ws->user_config->net_priority);
|
set_net_priority(ws, ws->conn_fd, ws->user_config->net_priority);
|
||||||
set_no_delay(ws, ws->conn_fd);
|
set_no_delay(ws, ws->conn_fd);
|
||||||
|
|
||||||
if (ws->udp_state != UP_DISABLED) {
|
if (DTLS_ACTIVE(ws)->udp_state != UP_DISABLED) {
|
||||||
|
|
||||||
if (ws->user_config->dpd > 0) {
|
if (ws->user_config->dpd > 0) {
|
||||||
ret =
|
ret =
|
||||||
@ -2342,150 +2360,10 @@ static int connect_handler(worker_st * ws)
|
|||||||
ret = cstp_uncork(ws);
|
ret = cstp_uncork(ws);
|
||||||
SEND_ERR(ret);
|
SEND_ERR(ret);
|
||||||
|
|
||||||
/* start dead peer detection */
|
ret = worker_event_loop(ws);
|
||||||
gettime(&tnow);
|
if (ret != 0)
|
||||||
ws->last_msg_tcp = ws->last_msg_udp = ws->last_nc_msg = tnow.tv_sec;
|
{
|
||||||
|
goto exit;
|
||||||
bandwidth_init(&ws->b_rx, ws->user_config->rx_per_sec);
|
|
||||||
bandwidth_init(&ws->b_tx, ws->user_config->tx_per_sec);
|
|
||||||
|
|
||||||
sigprocmask(SIG_BLOCK, &blockset, NULL);
|
|
||||||
|
|
||||||
/* worker main loop */
|
|
||||||
for (;;) {
|
|
||||||
if (terminate != 0) {
|
|
||||||
terminate:
|
|
||||||
ws->buffer[0] = 'S';
|
|
||||||
ws->buffer[1] = 'T';
|
|
||||||
ws->buffer[2] = 'F';
|
|
||||||
ws->buffer[3] = 1;
|
|
||||||
ws->buffer[4] = 0;
|
|
||||||
ws->buffer[5] = 0;
|
|
||||||
ws->buffer[6] = AC_PKT_DISCONN;
|
|
||||||
ws->buffer[7] = 0;
|
|
||||||
|
|
||||||
oclog(ws, LOG_TRANSFER_DEBUG,
|
|
||||||
"sending disconnect message in TLS channel");
|
|
||||||
cstp_send(ws, ws->buffer, 8);
|
|
||||||
exit_worker_reason(ws, terminate_reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ws->session != NULL)
|
|
||||||
tls_pending = gnutls_record_check_pending(ws->session);
|
|
||||||
else
|
|
||||||
tls_pending = 0;
|
|
||||||
|
|
||||||
if (ws->udp_state > UP_WAIT_FD) {
|
|
||||||
dtls_pending = dtls_pull_buffer_non_empty(&ws->dtls_tptr);
|
|
||||||
if (ws->dtls_session != NULL)
|
|
||||||
dtls_pending +=
|
|
||||||
gnutls_record_check_pending(ws->dtls_session);
|
|
||||||
} else {
|
|
||||||
dtls_pending = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pfd[0].revents = 0;
|
|
||||||
pfd[1].revents = 0;
|
|
||||||
pfd[2].revents = 0;
|
|
||||||
pfd[3].revents = 0;
|
|
||||||
|
|
||||||
if (tls_pending == 0 && dtls_pending == 0) {
|
|
||||||
pfd[0].fd = ws->conn_fd;
|
|
||||||
pfd[0].events = POLLIN;
|
|
||||||
|
|
||||||
pfd[1].fd = ws->cmd_fd;
|
|
||||||
pfd[1].events = POLLIN;
|
|
||||||
|
|
||||||
pfd[2].fd = ws->tun_fd;
|
|
||||||
pfd[2].events = POLLIN;
|
|
||||||
|
|
||||||
pfd_size = 3;
|
|
||||||
|
|
||||||
if (ws->udp_state > UP_WAIT_FD) {
|
|
||||||
pfd[3].fd = ws->dtls_tptr.fd;
|
|
||||||
pfd[3].events = POLLIN;
|
|
||||||
pfd_size++;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_PPOLL
|
|
||||||
tv.tv_nsec = 0;
|
|
||||||
tv.tv_sec = 10;
|
|
||||||
ret = ppoll(pfd, pfd_size, &tv, &emptyset);
|
|
||||||
#else
|
|
||||||
sigprocmask(SIG_UNBLOCK, &blockset, NULL);
|
|
||||||
ret = poll(pfd, pfd_size, 10*1000);
|
|
||||||
sigprocmask(SIG_BLOCK, &blockset, NULL);
|
|
||||||
#endif
|
|
||||||
if (ret == -1) {
|
|
||||||
if (errno == EINTR || errno == EAGAIN)
|
|
||||||
continue;
|
|
||||||
terminate_reason = REASON_ERROR;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((pfd[0].revents | pfd[1].revents |
|
|
||||||
pfd[2].revents | pfd[3].revents) & POLLERR) {
|
|
||||||
terminate_reason = REASON_ERROR;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gettime(&tnow);
|
|
||||||
|
|
||||||
if (periodic_check(ws, &tnow, ws->user_config->dpd) < 0) {
|
|
||||||
terminate_reason = REASON_ERROR;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* send pending data from tun device */
|
|
||||||
if (pfd[2].revents & (POLLIN|POLLHUP)) {
|
|
||||||
ret = tun_mainloop(ws, &tnow);
|
|
||||||
if (ret < 0) {
|
|
||||||
terminate_reason = REASON_ERROR;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read pending data from TCP channel */
|
|
||||||
if ((pfd[0].revents & (POLLIN|POLLHUP)) || tls_pending != 0) {
|
|
||||||
ret = tls_mainloop(ws, &tnow);
|
|
||||||
if (ret < 0) {
|
|
||||||
terminate_reason = REASON_ERROR;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read data from UDP channel */
|
|
||||||
if (ws->udp_state > UP_WAIT_FD &&
|
|
||||||
((pfd[3].revents & (POLLIN|POLLHUP)) || dtls_pending != 0)) {
|
|
||||||
|
|
||||||
ret = dtls_mainloop(ws, &tnow);
|
|
||||||
if (ret < 0) {
|
|
||||||
terminate_reason = REASON_ERROR;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CAPTURE_LATENCY_SUPPORT)
|
|
||||||
if (ws->dtls_tptr.rx_time.tv_sec != 0) {
|
|
||||||
capture_latency_sample(ws, &ws->dtls_tptr.rx_time);
|
|
||||||
ws->dtls_tptr.rx_time.tv_sec = 0;
|
|
||||||
ws->dtls_tptr.rx_time.tv_nsec = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read commands from command fd */
|
|
||||||
if (pfd[1].revents & (POLLIN|POLLHUP)) {
|
|
||||||
ret = handle_commands_from_main(ws);
|
|
||||||
if (ret == ERR_NO_CMD_FD) {
|
|
||||||
terminate_reason = REASON_ERROR;
|
|
||||||
goto terminate;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
terminate_reason = REASON_ERROR;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -2493,9 +2371,11 @@ static int connect_handler(worker_st * ws)
|
|||||||
exit:
|
exit:
|
||||||
cstp_close(ws);
|
cstp_close(ws);
|
||||||
/*gnutls_deinit(ws->session); */
|
/*gnutls_deinit(ws->session); */
|
||||||
if (ws->udp_state == UP_ACTIVE && ws->dtls_session) {
|
if (DTLS_ACTIVE(ws)->udp_state == UP_ACTIVE && DTLS_ACTIVE(ws)->dtls_session) {
|
||||||
dtls_close(ws);
|
dtls_close(DTLS_ACTIVE(ws));
|
||||||
/*gnutls_deinit(ws->dtls_session); */
|
}
|
||||||
|
if (DTLS_INACTIVE(ws)->udp_state == UP_ACTIVE && DTLS_INACTIVE(ws)->dtls_session) {
|
||||||
|
dtls_close(DTLS_INACTIVE(ws));
|
||||||
}
|
}
|
||||||
|
|
||||||
exit_worker_reason(ws, terminate_reason);
|
exit_worker_reason(ws, terminate_reason);
|
||||||
@ -2551,15 +2431,15 @@ static int parse_data(struct worker_st *ws, uint8_t *buf, size_t buf_size,
|
|||||||
|
|
||||||
if (buf_size-CSTP_DTLS_OVERHEAD > DATA_MTU(ws, ws->link_mtu)) {
|
if (buf_size-CSTP_DTLS_OVERHEAD > DATA_MTU(ws, ws->link_mtu)) {
|
||||||
/* peer is doing MTU discovery */
|
/* peer is doing MTU discovery */
|
||||||
data_mtu_set(ws, buf_size-CSTP_DTLS_OVERHEAD);
|
data_mtu_set(ws, DTLS_ACTIVE(ws), buf_size-CSTP_DTLS_OVERHEAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = dtls_send(ws, buf, buf_size);
|
ret = dtls_send(DTLS_ACTIVE(ws), buf, buf_size);
|
||||||
if (ret == GNUTLS_E_LARGE_PACKET) {
|
if (ret == GNUTLS_E_LARGE_PACKET) {
|
||||||
oclog(ws, LOG_TRANSFER_DEBUG,
|
oclog(ws, LOG_TRANSFER_DEBUG,
|
||||||
"could not send DPD of %d bytes", (int)buf_size);
|
"could not send DPD of %d bytes", (int)buf_size);
|
||||||
mtu_not_ok(ws);
|
mtu_not_ok(ws, DTLS_ACTIVE(ws));
|
||||||
ret = dtls_send(ws, buf, 1);
|
ret = dtls_send(DTLS_ACTIVE(ws), buf, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
oclog(ws, LOG_TRANSFER_DEBUG,
|
oclog(ws, LOG_TRANSFER_DEBUG,
|
||||||
@ -2665,10 +2545,10 @@ static int parse_cstp_data(struct worker_st *ws,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf[6] == AC_PKT_DATA && ws->udp_state == UP_ACTIVE) {
|
if (buf[6] == AC_PKT_DATA && DTLS_ACTIVE(ws)->udp_state == UP_ACTIVE) {
|
||||||
/* if we received a data packet in the CSTP channel we assume that
|
/* if we received a data packet in the CSTP channel we assume that
|
||||||
* our peer wants to switch to it as the communication channel */
|
* our peer wants to switch to it as the communication channel */
|
||||||
ws->udp_state = UP_INACTIVE;
|
DTLS_ACTIVE(ws)->udp_state = UP_INACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = parse_data(ws, buf, buf_size, now, 0);
|
ret = parse_data(ws, buf, buf_size, now, 0);
|
||||||
@ -2709,3 +2589,228 @@ static int test_for_tcp_health_probe(struct worker_st *ws)
|
|||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void syserr_cb (const char *msg)
|
||||||
|
{
|
||||||
|
struct worker_st * ws = ev_userdata(loop);
|
||||||
|
int err = errno;
|
||||||
|
|
||||||
|
oclog(ws, LOG_ERR, "libev fatal error: %s / %s", msg, strerror(err));
|
||||||
|
|
||||||
|
terminate_reason = REASON_ERROR;
|
||||||
|
exit_worker_reason(ws, terminate_reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cstp_send_terminate(struct worker_st * ws)
|
||||||
|
{
|
||||||
|
ws->buffer[0] = 'S';
|
||||||
|
ws->buffer[1] = 'T';
|
||||||
|
ws->buffer[2] = 'F';
|
||||||
|
ws->buffer[3] = 1;
|
||||||
|
ws->buffer[4] = 0;
|
||||||
|
ws->buffer[5] = 0;
|
||||||
|
ws->buffer[6] = AC_PKT_DISCONN;
|
||||||
|
ws->buffer[7] = 0;
|
||||||
|
|
||||||
|
oclog(ws, LOG_TRANSFER_DEBUG,
|
||||||
|
"sending disconnect message in TLS channel");
|
||||||
|
cstp_send(ws, ws->buffer, 8);
|
||||||
|
exit_worker_reason(ws, terminate_reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void command_watcher_cb (EV_P_ ev_io *w, int revents)
|
||||||
|
{
|
||||||
|
struct worker_st *ws = ev_userdata(loop);
|
||||||
|
|
||||||
|
int ret = handle_commands_from_main(ws);
|
||||||
|
if (ret == ERR_NO_CMD_FD) {
|
||||||
|
terminate_reason = REASON_ERROR;
|
||||||
|
cstp_send_terminate(ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
terminate_reason = REASON_ERROR;
|
||||||
|
cstp_send_terminate(ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DTLS_ACTIVE(ws)->udp_state == UP_SETUP) {
|
||||||
|
ev_invoke(loop, &DTLS_ACTIVE(ws)->io, EV_READ);
|
||||||
|
}
|
||||||
|
if (DTLS_INACTIVE(ws)->udp_state == UP_SETUP) {
|
||||||
|
ev_invoke(loop, &DTLS_INACTIVE(ws)->io, EV_READ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tls_watcher_cb (EV_P_ ev_io * w, int revents)
|
||||||
|
{
|
||||||
|
struct timespec tnow;
|
||||||
|
struct worker_st *ws = ev_userdata(loop);
|
||||||
|
int ret;
|
||||||
|
gettime(&tnow);
|
||||||
|
|
||||||
|
ret = tls_mainloop(ws, &tnow);
|
||||||
|
if (ret < 0) {
|
||||||
|
oclog(ws, LOG_ERR, "tls_mainloop failed %d", ret);
|
||||||
|
terminate_reason = REASON_ERROR;
|
||||||
|
cstp_send_terminate(ws);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tun_watcher_cb (EV_P_ ev_io * w, int revents)
|
||||||
|
{
|
||||||
|
struct timespec tnow;
|
||||||
|
struct worker_st *ws = ev_userdata(loop);
|
||||||
|
int ret;
|
||||||
|
gettime(&tnow);
|
||||||
|
|
||||||
|
ret = tun_mainloop(ws, &tnow);
|
||||||
|
if (ret < 0) {
|
||||||
|
oclog(ws, LOG_ERR, "tun_mainloop failed %d", ret);
|
||||||
|
terminate_reason = REASON_ERROR;
|
||||||
|
cstp_send_terminate(ws);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dtls_watcher_cb (EV_P_ ev_io * w, int revents)
|
||||||
|
{
|
||||||
|
struct timespec tnow;
|
||||||
|
struct worker_st *ws = ev_userdata(loop);
|
||||||
|
struct dtls_st * dtls = (struct dtls_st*)w;
|
||||||
|
int ret;
|
||||||
|
gettime(&tnow);
|
||||||
|
|
||||||
|
ret = dtls_mainloop(ws, dtls, &tnow);
|
||||||
|
if (ret < 0) {
|
||||||
|
oclog(ws, LOG_ERR, "dtls_mainloop failed %d", ret);
|
||||||
|
terminate_reason = REASON_ERROR;
|
||||||
|
cstp_send_terminate(ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CAPTURE_LATENCY_SUPPORT)
|
||||||
|
if (dtls->dtls_tptr.rx_time.tv_sec != 0) {
|
||||||
|
capture_latency_sample(ws, &dtls->dtls_tptr.rx_time);
|
||||||
|
dtls->dtls_tptr.rx_time.tv_sec = 0;
|
||||||
|
dtls->dtls_tptr.rx_time.tv_nsec = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void term_sig_watcher_cb(struct ev_loop *loop, ev_signal *w, int revents)
|
||||||
|
{
|
||||||
|
struct worker_st *ws = ev_userdata(loop);
|
||||||
|
cstp_send_terminate(ws);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void invoke_dtls_if_needed(struct dtls_st * dtls)
|
||||||
|
{
|
||||||
|
if ((dtls->udp_state > UP_WAIT_FD) &&
|
||||||
|
(dtls->dtls_session != NULL) &&
|
||||||
|
(gnutls_record_check_pending(dtls->dtls_session))) {
|
||||||
|
ev_invoke(loop, &dtls->io, EV_READ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void periodic_check_watcher_cb(EV_P_ ev_timer *w, int revents)
|
||||||
|
{
|
||||||
|
struct worker_st *ws = ev_userdata(loop);
|
||||||
|
struct timespec tnow;
|
||||||
|
|
||||||
|
gettime(&tnow);
|
||||||
|
|
||||||
|
if (periodic_check(ws, &tnow, ws->user_config->dpd) < 0) {
|
||||||
|
terminate_reason = REASON_ERROR;
|
||||||
|
cstp_send_terminate(ws);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (terminate)
|
||||||
|
cstp_send_terminate(ws);
|
||||||
|
|
||||||
|
if (gnutls_record_check_pending(ws->session))
|
||||||
|
{
|
||||||
|
ev_invoke(loop, &tls_watcher, EV_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
invoke_dtls_if_needed(DTLS_ACTIVE(ws));
|
||||||
|
invoke_dtls_if_needed(DTLS_INACTIVE(ws));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int worker_event_loop(struct worker_st * ws)
|
||||||
|
{
|
||||||
|
struct timespec tnow;
|
||||||
|
|
||||||
|
#if defined(__linux__) && defined(HAVE_LIBSECCOMP)
|
||||||
|
loop = ev_default_loop(EVFLAG_NOENV|EVBACKEND_EPOLL);
|
||||||
|
#else
|
||||||
|
loop = EV_DEFAULT;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Restore the signal handlers
|
||||||
|
ocsignal(SIGTERM, SIG_DFL);
|
||||||
|
ocsignal(SIGINT, SIG_DFL);
|
||||||
|
ocsignal(SIGALRM, SIG_DFL);
|
||||||
|
|
||||||
|
ev_init(&alarm_sig_watcher, term_sig_watcher_cb);
|
||||||
|
ev_signal_set (&alarm_sig_watcher, SIGALRM);
|
||||||
|
ev_signal_start (loop, &alarm_sig_watcher);
|
||||||
|
|
||||||
|
ev_init (&int_sig_watcher, term_sig_watcher_cb);
|
||||||
|
ev_signal_set (&int_sig_watcher, SIGINT);
|
||||||
|
ev_signal_start (loop, &int_sig_watcher);
|
||||||
|
|
||||||
|
ev_init (&term_sig_watcher, term_sig_watcher_cb);
|
||||||
|
ev_signal_set (&term_sig_watcher, SIGTERM);
|
||||||
|
ev_signal_start (loop, &term_sig_watcher);
|
||||||
|
|
||||||
|
ev_set_userdata (loop, ws);
|
||||||
|
ev_set_syserr_cb(syserr_cb);
|
||||||
|
|
||||||
|
ev_init(&command_watcher, command_watcher_cb);
|
||||||
|
ev_io_set(&command_watcher, ws->cmd_fd, EV_READ);
|
||||||
|
ev_io_start(loop, &command_watcher);
|
||||||
|
|
||||||
|
ev_init(&tls_watcher, tls_watcher_cb);
|
||||||
|
ev_io_set(&tls_watcher, ws->conn_fd, EV_READ);
|
||||||
|
ev_io_start(loop, &tls_watcher);
|
||||||
|
|
||||||
|
ev_init(&DTLS_ACTIVE(ws)->io, dtls_watcher_cb);
|
||||||
|
ev_init(&DTLS_INACTIVE(ws)->io, dtls_watcher_cb);
|
||||||
|
|
||||||
|
ev_init(&tun_watcher, tun_watcher_cb);
|
||||||
|
ev_io_set(&tun_watcher, ws->tun_fd, EV_READ);
|
||||||
|
ev_io_start(loop, &tun_watcher);
|
||||||
|
|
||||||
|
ev_init (&period_check_watcher, periodic_check_watcher_cb);
|
||||||
|
ev_timer_set(&period_check_watcher, WORKER_MAINTENANCE_TIME, WORKER_MAINTENANCE_TIME);
|
||||||
|
ev_timer_start(loop, &period_check_watcher);
|
||||||
|
|
||||||
|
|
||||||
|
/* start dead peer detection */
|
||||||
|
gettime(&tnow);
|
||||||
|
ws->last_msg_tcp = ws->last_msg_udp = ws->last_nc_msg = tnow.tv_sec;
|
||||||
|
|
||||||
|
bandwidth_init(&ws->b_rx, ws->user_config->rx_per_sec);
|
||||||
|
bandwidth_init(&ws->b_tx, ws->user_config->tx_per_sec);
|
||||||
|
|
||||||
|
|
||||||
|
ev_run(loop, 0);
|
||||||
|
if (terminate != 0)
|
||||||
|
{
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
cstp_close(ws);
|
||||||
|
/*gnutls_deinit(ws->session); */
|
||||||
|
if (DTLS_ACTIVE(ws)->udp_state == UP_ACTIVE && DTLS_ACTIVE(ws)->dtls_session) {
|
||||||
|
dtls_close(DTLS_ACTIVE(ws));
|
||||||
|
}
|
||||||
|
if (DTLS_INACTIVE(ws)->udp_state == UP_ACTIVE && DTLS_INACTIVE(ws)->dtls_session) {
|
||||||
|
dtls_close(DTLS_INACTIVE(ws));
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_worker_reason(ws, terminate_reason);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
@ -189,7 +189,8 @@ int main(int argc, char **argv)
|
|||||||
ws->vconfig = s->vconfig;
|
ws->vconfig = s->vconfig;
|
||||||
|
|
||||||
ws->tun_fd = -1;
|
ws->tun_fd = -1;
|
||||||
ws->dtls_tptr.fd = -1;
|
DTLS_ACTIVE(ws)->dtls_tptr.fd = -1;
|
||||||
|
DTLS_INACTIVE(ws)->dtls_tptr.fd = -1;
|
||||||
|
|
||||||
/* Drop privileges after this point */
|
/* Drop privileges after this point */
|
||||||
drop_privileges(s);
|
drop_privileges(s);
|
||||||
|
17
src/worker.h
17
src/worker.h
@ -38,6 +38,7 @@
|
|||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
#include <hmac.h>
|
#include <hmac.h>
|
||||||
#include "vhost.h"
|
#include "vhost.h"
|
||||||
|
#include "ev.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UP_DISABLED,
|
UP_DISABLED,
|
||||||
@ -166,13 +167,20 @@ typedef struct dtls_transport_ptr {
|
|||||||
#endif
|
#endif
|
||||||
} dtls_transport_ptr;
|
} dtls_transport_ptr;
|
||||||
|
|
||||||
|
typedef struct dtls_st {
|
||||||
|
ev_io io;
|
||||||
|
dtls_transport_ptr dtls_tptr;
|
||||||
|
gnutls_session_t dtls_session;
|
||||||
|
udp_port_state_t udp_state;
|
||||||
|
time_t last_dtls_rehandshake;
|
||||||
|
} dtls_st;
|
||||||
|
|
||||||
/* Given a base MTU, this macro provides the DTLS plaintext data we can send;
|
/* Given a base MTU, this macro provides the DTLS plaintext data we can send;
|
||||||
* the output value does not include the DTLS header */
|
* the output value does not include the DTLS header */
|
||||||
#define DATA_MTU(ws,mtu) (mtu-ws->dtls_crypto_overhead-ws->dtls_proto_overhead)
|
#define DATA_MTU(ws,mtu) (mtu-ws->dtls_crypto_overhead-ws->dtls_proto_overhead)
|
||||||
|
|
||||||
typedef struct worker_st {
|
typedef struct worker_st {
|
||||||
gnutls_session_t session;
|
gnutls_session_t session;
|
||||||
gnutls_session_t dtls_session;
|
|
||||||
|
|
||||||
auth_struct_st *selected_auth;
|
auth_struct_st *selected_auth;
|
||||||
const compression_method_st *dtls_selected_comp;
|
const compression_method_st *dtls_selected_comp;
|
||||||
@ -224,13 +232,14 @@ typedef struct worker_st {
|
|||||||
time_t last_periodic_check;
|
time_t last_periodic_check;
|
||||||
|
|
||||||
/* set after authentication */
|
/* set after authentication */
|
||||||
dtls_transport_ptr dtls_tptr;
|
|
||||||
udp_port_state_t udp_state;
|
|
||||||
time_t udp_recv_time; /* time last udp packet was received */
|
time_t udp_recv_time; /* time last udp packet was received */
|
||||||
|
uint8_t dtls_active_session : 1;
|
||||||
|
dtls_st dtls[2];
|
||||||
|
#define DTLS_ACTIVE(ws) (&ws->dtls[ws->dtls_active_session])
|
||||||
|
#define DTLS_INACTIVE(ws) (&ws->dtls[ws->dtls_active_session ^ 1])
|
||||||
|
|
||||||
/* protection from multiple rehandshakes */
|
/* protection from multiple rehandshakes */
|
||||||
time_t last_tls_rehandshake;
|
time_t last_tls_rehandshake;
|
||||||
time_t last_dtls_rehandshake;
|
|
||||||
|
|
||||||
/* the time the last stats message was sent */
|
/* the time the last stats message was sent */
|
||||||
time_t last_stats_msg;
|
time_t last_stats_msg;
|
||||||
|
@ -180,6 +180,9 @@ check_PROGRAMS += gen_oidc_test_data
|
|||||||
dist_check_SCRIPTS += test-oidc
|
dist_check_SCRIPTS += test-oidc
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
dist_check_SCRIPTS += test-replay
|
||||||
|
|
||||||
TESTS = $(check_PROGRAMS) $(dist_check_SCRIPTS) $(xfail_scripts)
|
TESTS = $(check_PROGRAMS) $(dist_check_SCRIPTS) $(xfail_scripts)
|
||||||
|
|
||||||
XFAIL_TESTS = $(xfail_scripts)
|
XFAIL_TESTS = $(xfail_scripts)
|
||||||
|
@ -173,7 +173,7 @@ fi
|
|||||||
echo "Transferred ${OCTETS} bytes"
|
echo "Transferred ${OCTETS} bytes"
|
||||||
|
|
||||||
echo "Waiting for disconnection report"
|
echo "Waiting for disconnection report"
|
||||||
wait_file_contents ${RADIUSLOG} "Acct-Terminate-Cause" 70
|
wait_file_contents ${RADIUSLOG} "Acct-Terminate-Cause" 120
|
||||||
|
|
||||||
DISC=$(cat ${RADIUSLOG}|grep "Acct-Status-Type = Start"|tail -1)
|
DISC=$(cat ${RADIUSLOG}|grep "Acct-Status-Type = Start"|tail -1)
|
||||||
if test -z "$DISC";then
|
if test -z "$DISC";then
|
||||||
|
89
tests/test-replay
Executable file
89
tests/test-replay
Executable file
@ -0,0 +1,89 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Copyright (C) 2020 Microsoft Corporation
|
||||||
|
#
|
||||||
|
# This file is part of ocserv.
|
||||||
|
#
|
||||||
|
# ocserv is free software; you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License as published by the
|
||||||
|
# Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
# your option) any later version.
|
||||||
|
#
|
||||||
|
# ocserv is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with GnuTLS; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
SERV="${SERV:-../src/ocserv}"
|
||||||
|
srcdir=${srcdir:-.}
|
||||||
|
|
||||||
|
. `dirname $0`/common.sh
|
||||||
|
|
||||||
|
eval "${GETPORT}"
|
||||||
|
|
||||||
|
TCPDUMP=${TCPDUMP:-$(which tcpdump)}
|
||||||
|
|
||||||
|
if test -z "${TCPDUMP}" || ! test -x ${TCPDUMP};then
|
||||||
|
echo "You need tcpdump to run this test"
|
||||||
|
exit 77
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
echo "Testing whether replay of DTLS hello breaks client session... "
|
||||||
|
|
||||||
|
PIDFILE1="${srcdir}/ci$$-1.pid.tmp"
|
||||||
|
PIDFILE2="${srcdir}/ci$$-2.pid.tmp"
|
||||||
|
TCPDUMP_FILE="${srcdir}/dtls_hello.pcap"
|
||||||
|
rm -f "${PIDFILE1}" "${PIDFILE2}"
|
||||||
|
|
||||||
|
function finish {
|
||||||
|
set +e
|
||||||
|
echo " * Cleaning up..."
|
||||||
|
test -n "${PID}" && kill ${PID} >/dev/null 2>&1
|
||||||
|
test -f "${PIDFILE1}" && kill $(cat ${PIDFILE1}) >/dev/null 2>&1
|
||||||
|
test -f "${PIDFILE2}" && kill $(cat ${PIDFILE2}) >/dev/null 2>&1
|
||||||
|
test -n "${CONFIG}" && rm -f ${CONFIG} >/dev/null 2>&1
|
||||||
|
test -n "${TCPDUMP_FILE}" && rm -f "${TCPDUMP_FILE}" >/dev/null 2>&1
|
||||||
|
rm -f "${PIDFILE1}" "${PIDFILE2}" 2>&1
|
||||||
|
}
|
||||||
|
trap finish EXIT
|
||||||
|
|
||||||
|
# Catch the DTLS client hello
|
||||||
|
$TCPDUMP -c 1 -n -i lo "udp and port $PORT" -w $TCPDUMP_FILE > tcpdump.log 2>&1 &
|
||||||
|
|
||||||
|
update_config test-max-same-1.config
|
||||||
|
launch_server -d 9999 -f -c ${CONFIG} & PID=$!
|
||||||
|
wait_server $PID
|
||||||
|
|
||||||
|
echo "Connecting to obtain cookie... "
|
||||||
|
eval `echo "test" | $OPENCONNECT -q localhost:$PORT -u test --authenticate --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3`
|
||||||
|
|
||||||
|
if [ -z "$COOKIE" ];then
|
||||||
|
echo "Could not obtain cookie"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#echo "Cookie: $COOKIE"
|
||||||
|
|
||||||
|
echo "Connecting with cookie... "
|
||||||
|
echo "test" | $OPENCONNECT -q localhost:$PORT -u test -C "$COOKIE" --servercert=d66b507ae074d03b02eafca40d35f87dd81049d3 --verbose --pid-file "${PIDFILE1}" --background
|
||||||
|
|
||||||
|
sleep 4
|
||||||
|
|
||||||
|
# Extract the DTLS client hello
|
||||||
|
DTLS_HELLO=$($TCPDUMP -t -r $TCPDUMP_FILE -x | grep -v localhost | awk -F: '{print $2}' | tr -d '\n' | tr -d ' ' | cut -c 57- | sed 's/\(.\{2\}\)/\1 /g')
|
||||||
|
|
||||||
|
# Replay the DTLS client hello
|
||||||
|
echo Sending replayed DTLS Hello
|
||||||
|
echo $DTLS_HELLO | xxd -r -p | nc -w 1 -u localhost $PORT > /dev/null
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
ping -c 4 192.168.1.1 || fail $PID Tunnel was broken
|
||||||
|
|
||||||
|
|
||||||
|
exit 0
|
Loading…
Reference in New Issue
Block a user