mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 16:57:00 +08:00
When receiving unexpected UDP packets, check if they match a known IP and forward them.
This will not work for many clients that come from a single IP but will work-around issues, when clients are behind a NAT that keeps their UDP port state for shorter time than DPD.
This commit is contained in:
@@ -138,7 +138,7 @@ message tun_mtu_msg
|
||||
/* UDP_FD */
|
||||
message udp_fd_msg
|
||||
{
|
||||
|
||||
required bool hello = 1 [default = true]; /* is that a client hello? */
|
||||
}
|
||||
|
||||
/* SESSION_INFO */
|
||||
|
||||
106
src/main.c
106
src/main.c
@@ -621,7 +621,7 @@ static int forward_udp_to_owner(main_server_st* s, struct listener_st *listener)
|
||||
{
|
||||
int ret, e;
|
||||
struct sockaddr_storage cli_addr;
|
||||
struct proc_st *ctmp = NULL;
|
||||
struct proc_st *ctmp = NULL, *proc_to_send = NULL;
|
||||
socklen_t cli_addr_size;
|
||||
uint8_t buffer[1024];
|
||||
char tbuf[64];
|
||||
@@ -629,6 +629,7 @@ uint8_t *session_id;
|
||||
int session_id_size;
|
||||
ssize_t buffer_size;
|
||||
int connected = 0;
|
||||
int match_ip_only = 0, matching_ips;
|
||||
time_t now;
|
||||
|
||||
/* first receive from the correct client and connect socket */
|
||||
@@ -650,6 +651,7 @@ time_t now;
|
||||
human_addr((struct sockaddr*)&cli_addr, cli_addr_size, tbuf, sizeof(tbuf)),
|
||||
(unsigned int)buffer[1], (unsigned int)buffer[2],
|
||||
(unsigned int)buffer[RECORD_PAYLOAD_POS], (unsigned int)buffer[RECORD_PAYLOAD_POS+1]);
|
||||
|
||||
if (buffer[1] != 254 && (buffer[1] != 1 && buffer[2] != 0) &&
|
||||
buffer[RECORD_PAYLOAD_POS] != 254 && (buffer[RECORD_PAYLOAD_POS] != 0 && buffer[RECORD_PAYLOAD_POS+1] != 0)) {
|
||||
mslog(s, NULL, LOG_INFO, "unknown DTLS version: %u.%u", (unsigned)buffer[1], (unsigned)buffer[2]);
|
||||
@@ -657,51 +659,77 @@ time_t now;
|
||||
}
|
||||
if (buffer[0] != 22) {
|
||||
mslog(s, NULL, LOG_INFO, "unexpected DTLS content type: %u", (unsigned int)buffer[0]);
|
||||
goto fail;
|
||||
/* Here we received a non-client hello packet. It may be that
|
||||
* the client's NAT changed it's UDP source port and the previous
|
||||
* connection is invalidated. Try to see if we can simply match
|
||||
* the IP address and forward the socket.
|
||||
*/
|
||||
match_ip_only = 1;
|
||||
} else {
|
||||
/* read session_id */
|
||||
session_id_size = buffer[RECORD_PAYLOAD_POS+HANDSHAKE_SESSION_ID_POS];
|
||||
session_id = &buffer[RECORD_PAYLOAD_POS+HANDSHAKE_SESSION_ID_POS+1];
|
||||
}
|
||||
|
||||
/* read session_id */
|
||||
session_id_size = buffer[RECORD_PAYLOAD_POS+HANDSHAKE_SESSION_ID_POS];
|
||||
session_id = &buffer[RECORD_PAYLOAD_POS+HANDSHAKE_SESSION_ID_POS+1];
|
||||
|
||||
/* search for the IP and the session ID in all procs */
|
||||
now = time(0);
|
||||
matching_ips = 0;
|
||||
|
||||
list_for_each(&s->proc_list.head, ctmp, list) {
|
||||
if (session_id_size == ctmp->dtls_session_id_size &&
|
||||
if (match_ip_only == 0 && session_id_size == ctmp->dtls_session_id_size &&
|
||||
memcmp(session_id, ctmp->dtls_session_id, session_id_size) == 0) {
|
||||
UdpFdMsg msg = UDP_FD_MSG__INIT;
|
||||
|
||||
if (now - ctmp->udp_fd_receive_time <= UDP_FD_RESEND_TIME) {
|
||||
mslog(s, ctmp, LOG_INFO, "received UDP connection too soon");
|
||||
break;
|
||||
}
|
||||
|
||||
ret = connect(listener->fd, (void*)&cli_addr, cli_addr_size);
|
||||
if (ret == -1) {
|
||||
e = errno;
|
||||
mslog(s, ctmp, LOG_ERR, "connect UDP socket: %s", strerror(e));
|
||||
break;
|
||||
}
|
||||
|
||||
ret = send_socket_msg_to_worker(s, ctmp, CMD_UDP_FD,
|
||||
listener->fd,
|
||||
&msg,
|
||||
(pack_size_func)udp_fd_msg__get_packed_size,
|
||||
(pack_func)udp_fd_msg__pack);
|
||||
if (ret < 0) {
|
||||
mslog(s, ctmp, LOG_ERR, "error passing UDP socket");
|
||||
break;
|
||||
}
|
||||
mslog(s, ctmp, LOG_DEBUG, "passed UDP socket");
|
||||
ctmp->udp_fd_receive_time = now;
|
||||
connected = 1;
|
||||
|
||||
reopen_udp_port(listener);
|
||||
proc_to_send = ctmp;
|
||||
break;
|
||||
} else if (match_ip_only != 0 && cli_addr_size == ctmp->remote_addr_len &&
|
||||
memcmp(SA_IN_P_GENERIC(&cli_addr, cli_addr_size),
|
||||
SA_IN_P_GENERIC(&ctmp->remote_addr, ctmp->remote_addr_len),
|
||||
SA_IN_SIZE(ctmp->remote_addr_len)) == 0) {
|
||||
matching_ips++;
|
||||
proc_to_send = ctmp;
|
||||
}
|
||||
}
|
||||
|
||||
if (proc_to_send != 0) {
|
||||
UdpFdMsg msg = UDP_FD_MSG__INIT;
|
||||
|
||||
if (matching_ips > 1) {
|
||||
mslog(s, proc_to_send, LOG_INFO, "cannot associate with a client; more than a single clients from this IP");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (now - proc_to_send->udp_fd_receive_time <= UDP_FD_RESEND_TIME) {
|
||||
mslog(s, proc_to_send, LOG_INFO, "received UDP connection too soon");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = connect(listener->fd, (void*)&cli_addr, cli_addr_size);
|
||||
if (ret == -1) {
|
||||
e = errno;
|
||||
mslog(s, proc_to_send, LOG_ERR, "connect UDP socket: %s", strerror(e));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (match_ip_only != 0) {
|
||||
msg.hello = 0;
|
||||
}
|
||||
|
||||
ret = send_socket_msg_to_worker(s, proc_to_send, CMD_UDP_FD,
|
||||
listener->fd,
|
||||
&msg,
|
||||
(pack_size_func)udp_fd_msg__get_packed_size,
|
||||
(pack_func)udp_fd_msg__pack);
|
||||
if (ret < 0) {
|
||||
mslog(s, proc_to_send, LOG_ERR, "error passing UDP socket");
|
||||
goto fail;
|
||||
}
|
||||
mslog(s, proc_to_send, LOG_DEBUG, "passed UDP socket");
|
||||
proc_to_send->udp_fd_receive_time = now;
|
||||
connected = 1;
|
||||
|
||||
reopen_udp_port(listener);
|
||||
}
|
||||
|
||||
fail:
|
||||
if (connected == 0) {
|
||||
/* received packet from unknown host. Ignore it. */
|
||||
@@ -709,7 +737,7 @@ fail:
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
@@ -771,14 +799,14 @@ unsigned total = 10;
|
||||
static int check_tcp_wrapper(int fd)
|
||||
{
|
||||
struct request_info req;
|
||||
|
||||
|
||||
if (request_init(&req, RQ_FILE, fd, RQ_DAEMON, PACKAGE_NAME, 0) == NULL)
|
||||
return -1;
|
||||
|
||||
|
||||
sock_host(&req);
|
||||
if (hosts_access(&req) == 0)
|
||||
return -1;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
@@ -831,7 +859,7 @@ int main(int argc, char** argv)
|
||||
|
||||
/* Initialize GnuTLS */
|
||||
tls_global_init(&s);
|
||||
|
||||
|
||||
ret = gnutls_rnd(GNUTLS_RND_RANDOM, s.cookie_key, sizeof(s.cookie_key));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error in cookie key generation\n");
|
||||
|
||||
@@ -50,10 +50,7 @@ int handle_worker_commands(struct worker_st *ws)
|
||||
uint16_t length;
|
||||
int e;
|
||||
struct msghdr hdr;
|
||||
union {
|
||||
char x[32];
|
||||
struct sockaddr_storage ss;
|
||||
} cmd_data;
|
||||
uint8_t cmd_data[32];
|
||||
union {
|
||||
struct cmsghdr cm;
|
||||
char control[CMSG_SPACE(sizeof(int))];
|
||||
@@ -70,7 +67,7 @@ int handle_worker_commands(struct worker_st *ws)
|
||||
iov[1].iov_base = &length;
|
||||
iov[1].iov_len = 2;
|
||||
|
||||
iov[2].iov_base = &cmd_data;
|
||||
iov[2].iov_base = cmd_data;
|
||||
iov[2].iov_len = sizeof(cmd_data);
|
||||
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
@@ -92,30 +89,57 @@ int handle_worker_commands(struct worker_st *ws)
|
||||
exit(0);
|
||||
}
|
||||
|
||||
oclog(ws, LOG_DEBUG, "worker received message %s of %u bytes\n", cmd_request_to_str(cmd), (unsigned)length);
|
||||
if (length > ret - 3) {
|
||||
oclog(ws, LOG_DEBUG, "worker received invalid message %s of %u bytes that claims to be %u\n", cmd_request_to_str(cmd), (unsigned)ret-3, (unsigned)length);
|
||||
exit(1);
|
||||
} else {
|
||||
oclog(ws, LOG_DEBUG, "worker received message %s of %u bytes\n", cmd_request_to_str(cmd), (unsigned)length);
|
||||
}
|
||||
|
||||
/*cmd_data_len = ret - 1;*/
|
||||
|
||||
switch(cmd) {
|
||||
case CMD_TERMINATE:
|
||||
exit(0);
|
||||
case CMD_UDP_FD:
|
||||
case CMD_UDP_FD: {
|
||||
UdpFdMsg *tmsg;
|
||||
unsigned hello = 1;
|
||||
int fd;
|
||||
|
||||
if (ws->udp_state != UP_WAIT_FD) {
|
||||
oclog(ws, LOG_INFO, "received another a UDP fd!");
|
||||
}
|
||||
|
||||
tmsg = udp_fd_msg__unpack(NULL, length, cmd_data);
|
||||
if (tmsg) {
|
||||
hello = tmsg->hello;
|
||||
udp_fd_msg__free_unpacked(tmsg, NULL);
|
||||
}
|
||||
|
||||
if ( (cmptr = CMSG_FIRSTHDR(&hdr)) != NULL && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
|
||||
if (cmptr->cmsg_level != SOL_SOCKET || cmptr->cmsg_type != SCM_RIGHTS) {
|
||||
oclog(ws, LOG_ERR, "received UDP fd message of wrong type");
|
||||
goto udp_fd_fail;
|
||||
}
|
||||
|
||||
memcpy(&fd, CMSG_DATA(cmptr), sizeof(int));
|
||||
|
||||
if (hello == 0) {
|
||||
/* only replace our session if we are inactive for more than 60 secs */
|
||||
if ((ws->udp_state != UP_ACTIVE && ws->udp_state != UP_INACTIVE) ||
|
||||
time(0) - ws->last_msg_udp < 60) {
|
||||
oclog(ws, LOG_INFO, "received UDP fd message but our session is active!");
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
} else { /* received client hello */
|
||||
ws->udp_state = UP_SETUP;
|
||||
}
|
||||
|
||||
if (ws->udp_fd != -1) {
|
||||
close(ws->udp_fd);
|
||||
}
|
||||
|
||||
memcpy(&ws->udp_fd, CMSG_DATA(cmptr), sizeof(int));
|
||||
ws->udp_state = UP_SETUP;
|
||||
ws->udp_fd = fd;
|
||||
|
||||
oclog(ws, LOG_DEBUG, "received new UDP fd and connected to peer");
|
||||
return 0;
|
||||
@@ -123,6 +147,8 @@ int handle_worker_commands(struct worker_st *ws)
|
||||
oclog(ws, LOG_ERR, "could not receive peer's UDP fd");
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
oclog(ws, LOG_ERR, "unknown CMD 0x%x", (unsigned)cmd);
|
||||
|
||||
Reference in New Issue
Block a user