Use a single UDP port in the server.

Several modifications to use a single UDP port in the server. This
is currently done using a hack, i.e., pass the UDP socket to worker,
close it on the main server and then re-open it (using REUSEADDR).

Also several updates in TUN handling to allow more than one clients connecting.
This commit is contained in:
Nikos Mavrogiannopoulos
2013-02-07 17:48:16 +01:00
parent dd31208d4a
commit 1cb7ab38e9
18 changed files with 547 additions and 363 deletions

1
TODO
View File

@@ -1,4 +1,3 @@
* Use a single UDP port
* Add a simple username/password back-end
* Certificate authentication to the main process (can it be done without
moving the TLS handshake over the main thread?)

View File

@@ -125,6 +125,7 @@ unsigned j;
READ_STRING("listen-host", config->name, 0);
READ_NUMERIC("tcp-port", config->port, 1);
READ_NUMERIC("udp-port", config->udp_port, 0);
READ_NUMERIC("keepalive", config->keepalive, 0);
READ_STRING("server-cert", config->cert, 1);
@@ -199,6 +200,9 @@ static void check_cfg( struct cfg_st *config)
if (config->keepalive == 0)
config->keepalive = 30;
if (config->udp_port == 0)
config->udp_port = config->port;
if (config->priorities == NULL)
config->priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT";

View File

@@ -18,6 +18,7 @@ typedef enum {
RESUME_DELETE_REQ,
RESUME_FETCH_REQ,
RESUME_FETCH_REP,
CMD_UDP_FD,
CMD_TERMINATE,
} cmd_request_t;

View File

@@ -92,3 +92,35 @@ void __attribute__ ((format(printf, 3, 4)))
return;
}
/* proc is optional */
void __attribute__ ((format(printf, 4, 5)))
mslog(const main_server_st * s, const struct proc_st* proc,
int priority, const char *fmt, ...)
{
char buf[1024];
char ipbuf[128];
const char* ip = NULL;
va_list args;
if (priority == LOG_DEBUG && s->config->debug == 0)
return;
if (proc) {
ip = human_addr((void*)&proc->remote_addr, proc->remote_addr_len,
ipbuf, sizeof(ipbuf));
}
buf[1023] = 0;
va_start(args, fmt);
vsnprintf(buf, 1023, fmt, args);
va_end(args);
if (ip)
syslog(priority, "%s %s", ip, buf);
else
syslog(priority, "%s", buf);
return;
}

View File

@@ -73,7 +73,7 @@ static int send_auth_reply(main_server_st* s, struct proc_st* proc,
hdr.msg_iovlen++;
iov[2].iov_base = proc->session_id;
iov[2].iov_len = sizeof(proc->session_id);
iov[2].iov_len = proc->session_id_size;
hdr.msg_iovlen++;
iov[3].iov_base = lease->name;
@@ -114,8 +114,9 @@ struct stored_cookie_st sc;
memcpy(proc->cookie, req->cookie, sizeof(proc->cookie));
memcpy(proc->username, sc.username, sizeof(proc->username));
memcpy(proc->session_id, sc.session_id, sizeof(proc->session_id));
proc->session_id_size = sizeof(proc->session_id);
ret = open_tun(s->config, s->tun, lease);
ret = open_tun(s, lease);
if (ret < 0)
ret = -1; /* sorry */
@@ -134,6 +135,7 @@ struct stored_cookie_st *sc;
ret = gnutls_rnd(GNUTLS_RND_NONCE, proc->session_id, sizeof(proc->session_id));
if (ret < 0)
return -2;
proc->session_id_size = sizeof( proc->session_id);
sc = calloc(1, sizeof(*sc));
if (sc == NULL)
@@ -180,7 +182,7 @@ unsigned username_set = 0;
memcpy(proc->username, req->cert_user, MAX_USERNAME_SIZE);
else {
if (strcmp(proc->username, req->cert_user) != 0) {
syslog(LOG_INFO, "User '%s' presented a certificate from user '%s'", proc->username, req->cert_user);
mslog(s, proc, LOG_INFO, "User '%s' presented a certificate from user '%s'", proc->username, req->cert_user);
ret = -1;
}
}
@@ -190,7 +192,7 @@ unsigned username_set = 0;
if (req->hostname[0] != 0)
memcpy(proc->hostname, req->hostname, MAX_HOSTNAME_SIZE);
ret = open_tun(s->config, s->tun, lease);
ret = open_tun(s, lease);
if (ret < 0)
ret = -1; /* sorry */
}
@@ -232,7 +234,7 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
ret = recvmsg( proc->fd, &hdr, 0);
if (ret == -1) {
e = errno;
syslog(LOG_ERR, "Cannot obtain data from command socket (pid: %d, peer: %s): %s", proc->pid, peer_ip, strerror(e));
mslog(s, proc, LOG_ERR, "Cannot obtain data from command socket (pid: %d, peer: %s): %s", proc->pid, peer_ip, strerror(e));
return -1;
}
@@ -245,24 +247,24 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
switch(cmd) {
case RESUME_STORE_REQ:
if (cmd_data_len <= sizeof(cmd_data.sresume)-MAX_SESSION_DATA_SIZE) {
syslog(LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip);
return -2;
}
ret = handle_resume_store_req(s, proc, &cmd_data.sresume);
if (ret < 0) {
syslog(LOG_DEBUG, "Could not store resumption data (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_DEBUG, "Could not store resumption data (pid: %d, peer: %s).", proc->pid, peer_ip);
}
break;
case RESUME_DELETE_REQ:
if (cmd_data_len != sizeof(cmd_data.fresume)) {
syslog(LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip);
return -2;
}
ret = handle_resume_delete_req(s, proc, &cmd_data.fresume);
if (ret < 0) {
syslog(LOG_DEBUG, "Could not delete resumption data (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_DEBUG, "Could not delete resumption data (pid: %d, peer: %s).", proc->pid, peer_ip);
}
break;
@@ -270,19 +272,19 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
struct cmd_resume_fetch_reply_st reply;
if (cmd_data_len != sizeof(cmd_data.fresume)) {
syslog(LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip);
return -2;
}
ret = handle_resume_fetch_req(s, proc, &cmd_data.fresume, &reply);
if (ret < 0) {
syslog(LOG_DEBUG, "Could not fetch resumption data (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_DEBUG, "Could not fetch resumption data (pid: %d, peer: %s).", proc->pid, peer_ip);
ret = send_resume_fetch_reply(s, proc, REP_RESUME_FAILED, NULL);
} else
ret = send_resume_fetch_reply(s, proc, REP_RESUME_OK, &reply);
}
if (ret < 0) {
syslog(LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip);
return -2;
}
@@ -293,14 +295,14 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
if (cmd == AUTH_REQ) {
if (cmd_data_len != sizeof(cmd_data.auth)) {
syslog(LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip);
return -2;
}
ret = handle_auth_req(s, proc, &cmd_data.auth, &lease);
} else {
if (cmd_data_len != sizeof(cmd_data.cauth)) {
syslog(LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "Error in received message length (pid: %d, peer: %s).", proc->pid, peer_ip);
return -2;
}
@@ -310,7 +312,7 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
if (ret == 0) {
ret = user_connected(s, proc, lease);
if (ret < 0) {
syslog(LOG_INFO, "User '%s' disconnected due to script", proc->username);
mslog(s, proc, LOG_INFO, "User '%s' disconnected due to script", proc->username);
}
}
@@ -323,10 +325,10 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
}
syslog(LOG_INFO, "User '%s' authenticated", proc->username);
mslog(s, proc, LOG_INFO, "User '%s' authenticated", proc->username);
ret = send_auth_reply(s, proc, REP_AUTH_OK, lease);
if (ret < 0) {
syslog(LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip);
return -2;
}
@@ -336,17 +338,17 @@ int handle_commands(main_server_st *s, struct proc_st* proc)
close(lease->fd);
lease->fd = -1;
} else {
syslog(LOG_INFO, "Failed authentication attempt for user '%s'", proc->username);
mslog(s, proc, LOG_INFO, "Failed authentication attempt for user '%s'", proc->username);
ret = send_auth_reply( s, proc, REP_AUTH_FAILED, NULL);
if (ret < 0) {
syslog(LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "Could not send reply cmd (pid: %d, peer: %s).", proc->pid, peer_ip);
return -2;
}
}
break;
default:
syslog(LOG_ERR, "Unknown CMD 0x%x (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
mslog(s, proc, LOG_ERR, "Unknown CMD 0x%x (pid: %d, peer: %s).", (unsigned)cmd, proc->pid, peer_ip);
return -2;
}

View File

@@ -63,6 +63,42 @@ int send_resume_fetch_reply(main_server_st* s, struct proc_st * proc,
return(sendmsg(proc->fd, &hdr, 0));
}
int send_udp_fd(main_server_st* s, struct proc_st * proc,
void* cli_addr, socklen_t cli_addr_size, int fd)
{
struct iovec iov[2];
uint8_t cmd = CMD_UDP_FD;
struct msghdr hdr;
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
memset(&hdr, 0, sizeof(hdr));
iov[0].iov_base = &cmd;
iov[0].iov_len = 1;
hdr.msg_iovlen++;
iov[1].iov_base = cli_addr;
iov[1].iov_len = cli_addr_size;
hdr.msg_iovlen++;
hdr.msg_iov = iov;
hdr.msg_control = control_un.control;
hdr.msg_controllen = sizeof(control_un.control);
cmptr = CMSG_FIRSTHDR(&hdr);
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmptr), &fd, sizeof(int));
return(sendmsg(proc->fd, &hdr, 0));
}
int handle_resume_delete_req(main_server_st* s, struct proc_st * proc,
const struct cmd_resume_fetch_req_st * req)
{

View File

@@ -53,46 +53,31 @@ static void tls_log_func(int level, const char *str)
syslog(LOG_DEBUG, "TLS[<%d>]: %s", level, str);
}
/* Returns 0 on success or negative value on error.
*/
static int
listen_ports(struct cfg_st* config, struct listen_list_st *list, const char *node,
int listen_port, int socktype)
static
int _listen_ports(struct cfg_st* config, struct addrinfo *res, struct listen_list_st *list)
{
struct addrinfo hints, *res, *ptr;
char portname[6];
struct addrinfo *ptr;
int s, y;
const char* type = NULL;
char buf[512];
struct listener_st *tmp;
list_head_init(&list->head);
list->total = 0;
snprintf(portname, sizeof(portname), "%d", listen_port);
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = socktype;
hints.ai_flags = AI_PASSIVE
#ifdef AI_ADDRCONFIG
| AI_ADDRCONFIG
#endif
;
s = getaddrinfo(node, portname, &hints, &res);
if (s != 0) {
fprintf(stderr, "getaddrinfo() failed: %s\n",
gai_strerror(s));
return -1;
}
for (ptr = res; ptr != NULL; ptr = ptr->ai_next) {
#ifndef HAVE_IPV6
if (ptr->ai_family != AF_INET)
continue;
#endif
if (ptr->ai_socktype == SOCK_STREAM)
type = "TCP";
else if (ptr->ai_socktype == SOCK_DGRAM)
type = "UDP";
else
continue;
if (config->foreground != 0)
fprintf(stderr, "listening on %s...\n",
human_addr(ptr->ai_addr, ptr->ai_addrlen,
fprintf(stderr, "listening (%s) on %s...\n",
type, human_addr(ptr->ai_addr, ptr->ai_addrlen,
buf, sizeof(buf)));
s = socket(ptr->ai_family, ptr->ai_socktype,
@@ -113,7 +98,7 @@ listen_ports(struct cfg_st* config, struct listen_list_st *list, const char *nod
}
#endif
if (socktype == SOCK_STREAM) {
if (ptr->ai_socktype == SOCK_STREAM) {
y = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(const void *) &y, sizeof(y)) < 0) {
@@ -122,6 +107,8 @@ listen_ports(struct cfg_st* config, struct listen_list_st *list, const char *nod
continue;
}
} else {
y = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const void *) &y, sizeof(y));
#if defined(IP_DONTFRAG)
y = 1;
if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG,
@@ -143,25 +130,151 @@ listen_ports(struct cfg_st* config, struct listen_list_st *list, const char *nod
continue;
}
if (socktype == SOCK_STREAM) {
if (ptr->ai_socktype == SOCK_STREAM) {
if (listen(s, 10) < 0) {
perror("listen() failed");
exit(1);
return -1;
}
}
tmp = calloc(1, sizeof(struct listener_st));
tmp->fd = s;
tmp->family = ptr->ai_family;
tmp->socktype = ptr->ai_socktype;
tmp->protocol = ptr->ai_protocol;
tmp->addr_len = ptr->ai_addrlen;
memcpy(&tmp->addr, ptr->ai_addr, tmp->addr_len);
list_add(&list->head, &(tmp->list));
list->total++;
}
fflush(stderr);
return 0;
}
/* Returns 0 on success or negative value on error.
*/
static int
listen_ports(struct cfg_st* config, struct listen_list_st *list, const char *node)
{
struct addrinfo hints, *res;
char portname[6];
int ret;
list_head_init(&list->head);
list->total = 0;
snprintf(portname, sizeof(portname), "%d", config->port);
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE
#ifdef AI_ADDRCONFIG
| AI_ADDRCONFIG
#endif
;
ret = getaddrinfo(node, portname, &hints, &res);
if (ret != 0) {
fprintf(stderr, "getaddrinfo() failed: %s\n",
gai_strerror(ret));
return -1;
}
ret = _listen_ports(config, res, list);
if (ret < 0) {
return -1;
}
freeaddrinfo(res);
snprintf(portname, sizeof(portname), "%d", config->udp_port);
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE
#ifdef AI_ADDRCONFIG
| AI_ADDRCONFIG
#endif
;
ret = getaddrinfo(node, portname, &hints, &res);
if (ret != 0) {
fprintf(stderr, "getaddrinfo() failed: %s\n",
gai_strerror(ret));
return -1;
}
ret = _listen_ports(config, res, list);
if (ret < 0) {
return -1;
}
freeaddrinfo(res);
return 0;
}
/* This is a hack. I tried to use connect() on the worker
* and use connect() with unspec on the master process but all packets
* were received by master. Reopening the socket seems to resolve
* that.
*/
static
int reopen_udp_port(struct listener_st *l)
{
int s, y, e;
close(l->fd);
l->fd = -1;
s = socket(l->family, l->socktype, l->protocol);
if (s < 0) {
perror("socket() failed");
return -1;
}
#if defined(HAVE_IPV6) && !defined(_WIN32)
if (ptr->ai_family == AF_INET6) {
y = 1;
/* avoid listen on ipv6 addresses failing
* because already listening on ipv4 addresses: */
setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
(const void *) &y, sizeof(y));
}
#endif
y = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const void *) &y, sizeof(y));
#if defined(IP_DONTFRAG)
y = 1;
setsockopt(s, IPPROTO_IP, IP_DONTFRAG,
(const void *) &y, sizeof(y));
#elif defined(IP_MTU_DISCOVER)
y = IP_PMTUDISC_DO;
setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER,
(const void *) &y, sizeof(y));
#endif
set_cloexec_flag (s, 1);
if (bind(s, (void*)&l->addr, l->addr_len) < 0) {
e = errno;
syslog(LOG_ERR, "bind() failed: %s", strerror(e));
close(s);
return -1;
}
l->fd = s;
return 0;
}
static void cleanup_children(main_server_st *s)
{
int status;
@@ -171,7 +284,7 @@ pid_t pid;
if (WEXITSTATUS(status) != 0 ||
(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV)) {
if (WIFSIGNALED(status))
syslog(LOG_ERR, "Child %u died with sigsegv\n", (unsigned)pid);
mslog(s, NULL, LOG_ERR, "Child %u died with sigsegv\n", (unsigned)pid);
}
}
need_children_cleanup = 0;
@@ -248,36 +361,36 @@ static int verify_certificate_cb(gnutls_session_t session)
return 0;
}
static void drop_privileges(struct cfg_st *config)
static void drop_privileges(main_server_st* s)
{
int ret, e;
if (config->chroot_dir) {
ret = chroot(config->chroot_dir);
if (s->config->chroot_dir) {
ret = chroot(s->config->chroot_dir);
if (ret != 0) {
e = errno;
syslog(LOG_ERR, "Cannot chroot to %s: %s", config->chroot_dir, strerror(e));
mslog(s, NULL, LOG_ERR, "Cannot chroot to %s: %s", s->config->chroot_dir, strerror(e));
exit(1);
}
}
if (config->gid != -1 && (getgid() == 0 || getegid() == 0)) {
ret = setgid(config->gid);
if (s->config->gid != -1 && (getgid() == 0 || getegid() == 0)) {
ret = setgid(s->config->gid);
if (ret < 0) {
e = errno;
syslog(LOG_ERR, "Cannot set gid to %d: %s\n",
(int) config->gid, strerror(e));
mslog(s, NULL, LOG_ERR, "Cannot set gid to %d: %s\n",
(int) s->config->gid, strerror(e));
exit(1);
}
}
if (config->uid != -1 && (getuid() == 0 || geteuid() == 0)) {
ret = setuid(config->uid);
if (s->config->uid != -1 && (getuid() == 0 || geteuid() == 0)) {
ret = setuid(s->config->uid);
if (ret < 0) {
e = errno;
syslog(LOG_ERR, "Cannot set uid to %d: %s\n",
(int) config->uid, strerror(e));
mslog(s, NULL, LOG_ERR, "Cannot set uid to %d: %s\n",
(int) s->config->uid, strerror(e));
exit(1);
}
@@ -335,6 +448,76 @@ static void handle_term(int signo)
terminate = 1;
}
#define RECORD_PAYLOAD_POS 13
#define HANDSHAKE_SESSION_ID_POS 46
static int forward_udp_to_owner(main_server_st* s, struct listener_st *listener)
{
int ret;
struct sockaddr_storage cli_addr;
struct proc_st *ctmp;
socklen_t cli_addr_size;
uint8_t buffer[1024];
uint8_t *session_id;
int session_id_size;
ssize_t buffer_size;
int connected = 0;
/* first receive from the correct client and connect socket */
cli_addr_size = sizeof(cli_addr);
ret = recvfrom(listener->fd, buffer, sizeof(buffer), MSG_PEEK, (void*)&cli_addr, &cli_addr_size);
if (ret < 0) {
mslog(s, NULL, LOG_INFO, "Error receiving in UDP socket");
return -1;
}
buffer_size = ret;
/* obtain the session id */
if (buffer_size < RECORD_PAYLOAD_POS+HANDSHAKE_SESSION_ID_POS+GNUTLS_MAX_SESSION_ID+2)
goto fail;
/* check version */
if (buffer[1] != 254 && (buffer[1] != 1 && buffer[0] != 0)) {
mslog(s, NULL, LOG_ERR, "Unknown DTLS version: %u.%u", (unsigned)buffer[1], (unsigned)buffer[2]);
goto fail;
}
/* 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 */
list_for_each(&s->clist->head, ctmp, list) {
if (ctmp->udp_fd_received == 0 && session_id_size == ctmp->session_id_size &&
memcmp(session_id, ctmp->session_id, session_id_size) == 0) {
ret = send_udp_fd(s, ctmp, (void*)&cli_addr, cli_addr_size, listener->fd);
if (ret < 0) {
mslog(s, ctmp, LOG_ERR, "Error passing UDP socket");
return -1;
}
ctmp->udp_fd_received = 1;
connected = 1;
reopen_udp_port(listener);
break;
}
}
fail:
if (connected == 0) {
/* received packet from unknown host */
// mslog(s, NULL, LOG_ERR, "Received UDP packet from unexpected host; discarding it");
recv(listener->fd, buffer, buffer_size, 0);
return -1;
}
return 0;
}
int main(int argc, char** argv)
{
int fd, pid, e;
@@ -344,13 +527,14 @@ int main(int argc, char** argv)
struct listener_st *ltmp;
struct proc_st *ctmp, *cpos;
struct tun_st tun;
const char* perr;
fd_set rd;
int val, n = 0, ret;
struct timeval tv;
int cmd_fd[2];
struct worker_st ws;
struct cfg_st config;
unsigned active_clients = 0;
unsigned active_clients = 0, set;
main_server_st s;
list_head_init(&clist.head);
@@ -383,7 +567,7 @@ int main(int argc, char** argv)
s.clist = &clist;
/* Listen to network ports */
ret = listen_ports(&config, &llist, config.name, config.port, SOCK_STREAM);
ret = listen_ports(&config, &llist, config.name);
if (ret < 0) {
fprintf(stderr, "Cannot listen to specified ports\n");
exit(1);
@@ -413,35 +597,36 @@ int main(int argc, char** argv)
exit(1);
}
if (config.ca != NULL) {
ret =
gnutls_certificate_set_x509_trust_file(creds.xcred,
config.ca,
GNUTLS_X509_FMT_PEM);
if (ret < 0) {
fprintf(stderr, "Error setting the CA (%s) file.\n",
config.ca);
exit(1);
if (config.cert_req != GNUTLS_CERT_IGNORE) {
if (config.ca != NULL) {
ret =
gnutls_certificate_set_x509_trust_file(creds.xcred,
config.ca,
GNUTLS_X509_FMT_PEM);
if (ret < 0) {
fprintf(stderr, "Error setting the CA (%s) file.\n",
config.ca);
exit(1);
}
printf("Processed %d CA certificate(s).\n", ret);
}
printf("Processed %d CA certificate(s).\n", ret);
}
if (config.crl != NULL) {
ret =
gnutls_certificate_set_x509_crl_file(creds.xcred,
config.crl,
GNUTLS_X509_FMT_PEM);
GNUTLS_FATAL_ERR(ret);
}
if (config.crl != NULL) {
ret =
gnutls_certificate_set_x509_crl_file(creds.xcred,
config.crl,
GNUTLS_X509_FMT_PEM);
GNUTLS_FATAL_ERR(ret);
}
if (config.cert_req != GNUTLS_CERT_IGNORE) {
gnutls_certificate_set_verify_function(creds.xcred,
verify_certificate_cb);
}
ret = gnutls_priority_init(&creds.cprio, config.priorities, NULL);
ret = gnutls_priority_init(&creds.cprio, config.priorities, &perr);
if (ret == GNUTLS_E_PARSING_ERROR)
fprintf(stderr, "Error in TLS priority string: %s\n", perr);
GNUTLS_FATAL_ERR(ret);
memset(&ws, 0, sizeof(ws));
@@ -464,11 +649,14 @@ int main(int argc, char** argv)
FD_ZERO(&rd);
list_for_each(&llist.head, ltmp, list) {
if (ltmp->fd == -1) continue;
val = fcntl(ltmp->fd, F_GETFL, 0);
if ((val == -1)
|| (fcntl(ltmp->fd, F_SETFL, val | O_NONBLOCK) <
0)) {
perror("fcntl()");
e = errno;
mslog(&s, NULL, LOG_ERR, "fcntl() error: %s", strerror(e));
exit(1);
}
@@ -489,18 +677,20 @@ int main(int argc, char** argv)
if (ret < 0) {
e = errno;
syslog(LOG_ERR, "Error in select(): %s",
mslog(&s, NULL, LOG_ERR, "Error in select(): %s",
strerror(e));
exit(1);
}
/* Check for new connections to accept */
list_for_each(&llist.head, ltmp, list) {
if (FD_ISSET(ltmp->fd, &rd)) {
set = FD_ISSET(ltmp->fd, &rd);
if (set && ltmp->socktype == SOCK_STREAM) {
/* connection on TCP port */
ws.remote_addr_len = sizeof(ws.remote_addr);
fd = accept(ltmp->fd, (void*)&ws.remote_addr, &ws.remote_addr_len);
if (fd < 0) {
syslog(LOG_ERR,
mslog(&s, NULL, LOG_ERR,
"Error in accept(): %s",
strerror(errno));
continue;
@@ -509,14 +699,14 @@ int main(int argc, char** argv)
if (config.max_clients > 0 && active_clients >= config.max_clients) {
close(fd);
syslog(LOG_INFO, "Reached maximum client limit (active: %u)", active_clients);
mslog(&s, NULL, LOG_INFO, "Reached maximum client limit (active: %u)", active_clients);
break;
}
/* Create a command socket */
ret = socketpair(AF_UNIX, SOCK_STREAM, 0, cmd_fd);
if (ret < 0) {
syslog(LOG_ERR, "Error creating command socket");
mslog(&s, NULL, LOG_ERR, "Error creating command socket");
close(fd);
break;
}
@@ -525,7 +715,7 @@ int main(int argc, char** argv)
if (pid == 0) { /* child */
/* Drop privileges after this point */
drop_privileges(&config);
drop_privileges(&s);
/* close any open descriptors before
* running the server
@@ -536,6 +726,7 @@ int main(int argc, char** argv)
ws.config = &config;
ws.cmd_fd = cmd_fd[1];
ws.tun_fd = -1;
ws.udp_fd = -1;
ws.conn_fd = fd;
ws.creds = &creds;
@@ -562,6 +753,12 @@ fork_failed:
}
close(cmd_fd[1]);
close(fd);
} else if (set && ltmp->socktype == SOCK_DGRAM) {
/* connection on UDP port */
ret = forward_udp_to_owner(&s, ltmp);
if (ret < 0) {
mslog(&s, NULL, LOG_INFO, "Could not determine the owner of received UDP packet");
}
}
}
@@ -586,7 +783,7 @@ fork_failed:
need_maintainance = 0;
pid = fork();
if (pid == 0) { /* child */
syslog(LOG_INFO, "Performing maintainance");
mslog(&s, NULL, LOG_INFO, "Performing maintainance");
clear_lists(&s);
expire_cookies(&s);

View File

@@ -14,6 +14,12 @@ int cmd_parser (int argc, char **argv, struct cfg_st* config);
struct listener_st {
struct list_node list;
int fd;
int socktype;
struct sockaddr_storage addr; /* local socket address */
socklen_t addr_len;
int family;
int protocol;
};
struct listen_list_st {
@@ -30,7 +36,10 @@ struct proc_st {
char username[MAX_USERNAME_SIZE]; /* the owner */
char hostname[MAX_HOSTNAME_SIZE]; /* the requested hostname */
uint8_t cookie[COOKIE_SIZE]; /* the cookie associated with the session */
/* The DTLS session ID associated with the TLS session */
uint8_t session_id[GNUTLS_MAX_SESSION_ID];
unsigned session_id_size; /* would act as a flag if session_id is set */
unsigned udp_fd_received;
/* the tun lease this process has */
struct lease_st* lease;
@@ -63,6 +72,8 @@ void expire_tls_sessions(main_server_st *s);
int send_resume_fetch_reply(main_server_st* s, struct proc_st* proc,
cmd_resume_reply_t r, struct cmd_resume_fetch_reply_st * reply);
int send_udp_fd(main_server_st* s, struct proc_st* proc, void* cli_addr, socklen_t cli_addr_size, int fd);
int handle_resume_delete_req(main_server_st* s, struct proc_st* proc,
const struct cmd_resume_fetch_req_st * req);
@@ -75,4 +86,11 @@ int handle_resume_store_req(main_server_st* s, struct proc_st *proc,
void expire_cookies(main_server_st* s);
void
__attribute__ ((format(printf, 4, 5)))
mslog(const main_server_st * s, const struct proc_st* proc,
int priority, const char *fmt, ...);
int open_tun(main_server_st* s, struct lease_st** l);
#endif

View File

@@ -2,7 +2,7 @@
*
* DO NOT EDIT THIS FILE (ocserv-args.c)
*
* It has been AutoGen-ed February 7, 2013 at 12:56:29 AM by AutoGen 5.16
* It has been AutoGen-ed February 7, 2013 at 02:45:14 PM by AutoGen 5.16
* From the definitions ocserv-args.def
* and the template file options
*

View File

@@ -74,8 +74,9 @@ auth = "pam"
#max-clients = 1024
max-clients = 16
# TCP port number
# TCP and UDP port number
tcp-port = 3333
udp-port = 3333
# The key and the certificates of the server
server-cert = /path/to/cert.pem

View File

@@ -2,7 +2,7 @@
*
* DO NOT EDIT THIS FILE (ocserv-args.h)
*
* It has been AutoGen-ed February 7, 2013 at 12:56:29 AM by AutoGen 5.16
* It has been AutoGen-ed February 7, 2013 at 02:45:14 PM by AutoGen 5.16
* From the definitions ocserv-args.def
* and the template file options
*

View File

@@ -17,8 +17,9 @@ max-clients = 4
# to authentication
auth-timeout = 40
# TCP port number
# TCP and UDP port number
tcp-port = 3333
udp-port = 3333
# Keepalive in seconds
keepalive = 90
@@ -64,8 +65,8 @@ device = vpns
# IP-REAL is the remote IP of the client,
# IP-LOCAL is the local IP in the P-t-P connection and IP-REMOTE
# is the VPN client IP.
connect-script = /bin/echo
disconnect-script = /bin/echo
#connect-script = /bin/echo
#disconnect-script = /bin/echo
# The pool from which the VPN user IPs will be drawn from.
ipv4-network = 192.168.1.0

174
src/tun.c
View File

@@ -31,8 +31,11 @@
#include <errno.h>
#include <cloexec.h>
#include <netdb.h>
#include <vpn.h>
#include <tun.h>
#include <main.h>
#include <ccan/list/list.h>
static int bignum_add1 (uint8_t * num, unsigned size)
@@ -60,9 +63,7 @@ static int bignum_add1 (uint8_t * num, unsigned size)
static int get_avail_network_addresses(const struct cfg_st *config, const struct lease_st *last4,
const struct lease_st *last6, struct lease_st* lease)
{
struct sockaddr_storage tmp, mask;
struct sockaddr_in *t4;
struct sockaddr_in6 *t6;
struct sockaddr_storage tmp, mask, network;
unsigned i;
int ret;
@@ -73,13 +74,9 @@ static int get_avail_network_addresses(const struct cfg_st *config, const struct
memset(&tmp, 0, sizeof(tmp));
/* read the network */
if (last4 == NULL && (config->network.ipv4 && config->network.ipv4_netmask)) {
t4 = (void*)&tmp;
t4->sin_family = AF_INET;
if (config->network.ipv4 && config->network.ipv4_netmask) {
ret =
inet_pton(AF_INET, config->network.ipv4, SA_IN_P(t4));
inet_pton(AF_INET, config->network.ipv4, SA_IN_P(&network));
if (ret != 1) {
syslog(LOG_ERR, "Error reading IP: %s\n", config->network.ipv4);
@@ -93,40 +90,37 @@ static int get_avail_network_addresses(const struct cfg_st *config, const struct
syslog(LOG_ERR, "Error reading mask: %s\n", config->network.ipv4_netmask);
return -1;
}
/* mask the network */
/* mask the network (just in case it is wrong) */
for (i=0;i<sizeof(struct in_addr);i++)
SA_IN_U8_P(t4)[i] &= (SA_IN_U8_P(&mask)[i]);
SA_IN_U8_P(&network)[i] &= (SA_IN_U8_P(&mask)[i]);
memcpy(&tmp, &network, sizeof(tmp));
((struct sockaddr_in*)&tmp)->sin_family = AF_INET;
((struct sockaddr_in*)&network)->sin_family = AF_INET;
}
/* read the network */
if (last4 == NULL && (config->network.ipv4 && config->network.ipv4_netmask)) {
/* add one to get local IP */
i = sizeof(struct in_addr)-1;
SA_IN_U8_P(t4)[i]++;
SA_IN_U8_P(&tmp)[i]++;
lease->lip4_len = sizeof(struct sockaddr_in);
memcpy(&lease->lip4, t4, lease->lip4_len);
memcpy(&lease->lip4, &tmp, lease->lip4_len);
/* add one to get remote IP */
i = sizeof(struct in_addr)-1;
SA_IN_U8_P(t4)[i]++;
SA_IN_U8_P(&tmp)[i]++;
lease->rip4_len = sizeof(struct sockaddr_in);
memcpy(&lease->rip4, t4, lease->rip4_len);
memcpy(&lease->rip4, &tmp, lease->rip4_len);
} else if (last4 != NULL) {
t4 = (void*)&tmp;
t4->sin_family = AF_INET;
ret =
inet_pton(AF_INET, config->network.ipv4_netmask, SA_IN_P(&mask));
if (ret != 1) {
syslog(LOG_ERR, "Error reading mask: %s\n", config->network.ipv4_netmask);
return -1;
}
/* mask the network */
lease->lip4_len = last4->rip4_len;
memcpy(&lease->lip4, &last4->rip4, lease->rip4_len);
memcpy(&lease->lip4, &last4->rip4, last4->rip4_len);
bignum_add1(SA_IN_U8_P(&lease->lip4), sizeof(struct in_addr));
if (SA_IN_U8_P(&lease->lip4)[3] == 255) /* broadcast */
@@ -136,23 +130,21 @@ static int get_avail_network_addresses(const struct cfg_st *config, const struct
memcpy(&lease->rip4, &lease->lip4, lease->rip4_len);
bignum_add1(SA_IN_U8_P(&lease->rip4), sizeof(struct in_addr));
/* mask the last IP with the complement of netmask */
/* mask the last IP with the netmask */
memcpy(&tmp, &lease->rip4, lease->rip4_len);
for (i=0;i<sizeof(struct in_addr);i++)
SA_IN_U8_P(t4)[i] &= ~(SA_IN_U8_P(&mask)[i]);
if (memcmp(&tmp, &lease->rip4, lease->rip4_len) != 0) {
syslog(LOG_ERR, "Reached limit of maximum IPs.\n");
SA_IN_U8_P(&tmp)[i] &= (SA_IN_U8_P(&mask)[i]);
/* the result should match the network */
if (memcmp(SA_IN_U8_P(&network), SA_IN_U8_P(&tmp), sizeof(struct in_addr)) != 0) {
syslog(LOG_ERR, "Reached limit of maximum (v4) IPs.\n");
return -1;
}
}
if (last6 == NULL && (config->network.ipv6 && config->network.ipv6_netmask)) {
t6 = (void*)&tmp;
t6->sin6_family = AF_INET6;
if (config->network.ipv6 && config->network.ipv6_netmask) {
ret =
inet_pton(AF_INET6, config->network.ipv6, SA_IN6_P(t6));
inet_pton(AF_INET6, config->network.ipv6, SA_IN6_P(&network));
if (ret != 1) {
syslog(LOG_ERR, "Error reading IP: %s\n", config->network.ipv6);
@@ -169,49 +161,45 @@ static int get_avail_network_addresses(const struct cfg_st *config, const struct
/* mask the network */
for (i=0;i<sizeof(struct in6_addr);i++)
SA_IN6_U8_P(t6)[i] &= (SA_IN6_U8_P(&mask)[i]);
SA_IN6_U8_P(&network)[i] &= (SA_IN6_U8_P(&mask)[i]);
memcpy(&tmp, &network, sizeof(tmp));
((struct sockaddr_in6*)&tmp)->sin6_family = AF_INET6;
((struct sockaddr_in6*)&network)->sin6_family = AF_INET6;
}
if (last6 == NULL && (config->network.ipv6 && config->network.ipv6_netmask)) {
/* add one to get local IP */
i = sizeof(struct in6_addr)-1;
SA_IN6_U8_P(t6)[i]++;
SA_IN6_U8_P(&tmp)[i]++;
lease->lip6_len = sizeof(struct sockaddr_in6);
memcpy(&lease->lip6, t6, lease->lip6_len);
memcpy(&lease->lip6, &tmp, lease->lip6_len);
/* add one to get remote IP */
i = sizeof(struct in6_addr)-1;
SA_IN6_U8_P(t6)[i]++;
SA_IN6_U8_P(&tmp)[i]++;
lease->rip6_len = sizeof(struct sockaddr_in6);
memcpy(&lease->rip6, t6, lease->rip6_len);
memcpy(&lease->rip6, &tmp, lease->rip6_len);
} else if (last6 != NULL) {
t6 = (void*)&tmp;
t6->sin6_family = AF_INET6;
ret =
inet_pton(AF_INET6, config->network.ipv6_netmask, SA_IN6_P(&mask));
if (ret != 1) {
syslog(LOG_ERR, "Error reading mask: %s\n", config->network.ipv6_netmask);
return -1;
}
/* mask the network */
lease->lip6_len = last6->rip6_len;
memcpy(&lease->lip6, &last6->rip6, lease->rip6_len);
memcpy(&lease->lip6, &last6->rip6, last6->rip6_len);
bignum_add1(SA_IN6_U8_P(&lease->lip6), sizeof(struct in6_addr));
lease->rip6_len = last6->rip6_len;
memcpy(&lease->rip6, &lease->lip6, lease->rip6_len);
bignum_add1(SA_IN6_U8_P(&lease->rip6), sizeof(struct in6_addr));
/* mask the last IP with the complement of netmask */
/* mask the last IP with the netmask */
memcpy(&tmp, &lease->rip6, lease->rip6_len);
for (i=0;i<sizeof(struct in6_addr);i++)
SA_IN6_U8_P(t6)[i] &= ~(SA_IN6_U8_P(&mask)[i]);
SA_IN6_U8_P(&tmp)[i] &= (SA_IN6_U8_P(&mask)[i]);
if (memcmp(&tmp, &lease->rip6, lease->rip6_len) != 0) {
syslog(LOG_ERR, "Reached limit of maximum IPs.\n");
/* the result should match the network */
if (memcmp(SA_IN6_U8_P(&network), SA_IN6_U8_P(&tmp), sizeof(struct in6_addr)) != 0) {
syslog(LOG_ERR, "Reached limit of maximum (v6) IPs.\n");
return -1;
}
}
@@ -230,7 +218,7 @@ static int get_avail_network_addresses(const struct cfg_st *config, const struct
return 0;
}
static int set_network_info( const struct lease_st *lease)
static int set_network_info( main_server_st* s, const struct lease_st *lease)
{
struct ifreq ifr;
int fd, ret;
@@ -248,7 +236,7 @@ static int set_network_info( const struct lease_st *lease)
ret = ioctl(fd, SIOCSIFADDR, &ifr);
if (ret != 0) {
syslog(LOG_ERR, "%s: Error setting IPv4.\n", lease->name);
mslog(s, NULL, LOG_ERR, "%s: Error setting IPv4.\n", lease->name);
}
memset(&ifr, 0, sizeof(ifr));
@@ -258,7 +246,7 @@ static int set_network_info( const struct lease_st *lease)
ret = ioctl(fd, SIOCSIFDSTADDR, &ifr);
if (ret != 0) {
syslog(LOG_ERR, "%s: Error setting DST IPv4.\n", lease->name);
mslog(s, NULL, LOG_ERR, "%s: Error setting DST IPv4.\n", lease->name);
}
}
@@ -271,7 +259,7 @@ static int set_network_info( const struct lease_st *lease)
ret = ioctl(fd, SIOCSIFADDR, &ifr);
if (ret != 0) {
syslog(LOG_ERR, "%s: Error setting IPv6.\n", lease->name);
mslog(s, NULL, LOG_ERR, "%s: Error setting IPv6.\n", lease->name);
}
memset(&ifr, 0, sizeof(ifr));
@@ -281,12 +269,12 @@ static int set_network_info( const struct lease_st *lease)
ret = ioctl(fd, SIOCSIFDSTADDR, &ifr);
if (ret != 0) {
syslog(LOG_ERR, "%s: Error setting DST IPv6.\n", lease->name);
mslog(s, NULL, LOG_ERR, "%s: Error setting DST IPv6.\n", lease->name);
}
}
if (lease->lip6_len == 0 && lease->lip4_len == 0) {
syslog(LOG_ERR, "%s: Could not set any IP.\n", lease->name);
mslog(s, NULL, LOG_ERR, "%s: Could not set any IP.\n", lease->name);
ret = -1;
} else
ret = 0;
@@ -299,7 +287,7 @@ static int set_network_info( const struct lease_st *lease)
ret = ioctl(fd, SIOCSIFFLAGS, &ifr);
if (ret != 0) {
syslog(LOG_ERR, "%s: Could not bring up interface.\n", lease->name);
mslog(s, NULL, LOG_ERR, "%s: Could not bring up interface.\n", lease->name);
}
close(fd);
@@ -307,7 +295,7 @@ static int set_network_info( const struct lease_st *lease)
}
int open_tun(const struct cfg_st *config, struct tun_st* tun, struct lease_st** l)
int open_tun(main_server_st* s, struct lease_st** l)
{
int tunfd, ret, e;
struct ifreq ifr;
@@ -315,27 +303,28 @@ int open_tun(const struct cfg_st *config, struct tun_st* tun, struct lease_st**
struct lease_st *lease = NULL;
struct lease_st *last4, *tmp;
struct lease_st *last6;
unsigned current = s->tun->total;
if (list_empty(&tun->head)) {
if (list_empty(&s->tun->head)) {
lease = calloc(1, sizeof(*lease));
if (lease == NULL)
return -1;
/* find the first IP address */
ret = get_avail_network_addresses(config, NULL, NULL, lease);
ret = get_avail_network_addresses(s->config, NULL, NULL, lease);
if (ret < 0) {
free(lease);
return -1;
}
/* Add into the list */
list_add_tail( &tun->head, &lease->list);
tun->total++;
list_add_tail( &s->tun->head, &lease->list);
s->tun->total++;
} else {
last4 = last6 = NULL;
/* try to re-use an address */
list_for_each(&tun->head, tmp, list) {
list_for_each(&s->tun->head, tmp, list) {
if (tmp->in_use == 0) {
lease = tmp;
break;
@@ -347,26 +336,27 @@ int open_tun(const struct cfg_st *config, struct tun_st* tun, struct lease_st**
if (lease == NULL)
return -1;
list_for_each_rev(&tun->head, tmp, list) {
if (tmp->rip4_len > 0)
list_for_each_rev(&s->tun->head, tmp, list) {
if (last4 == NULL && tmp->rip4_len > 0)
last4 = tmp;
if (tmp->rip6_len > 0)
if (last6 == NULL && tmp->rip6_len > 0)
last6 = tmp;
if (last4 && last6)
break;
}
ret = get_avail_network_addresses(config, last4, last6, lease);
ret = get_avail_network_addresses(s->config, last4, last6, lease);
if (ret < 0) {
free(lease);
return -1;
}
/* Add into the list */
list_add_tail( &tun->head, &lease->list);
tun->total++;
list_add_tail( &s->tun->head, &lease->list);
s->tun->total++;
}
}
@@ -376,7 +366,7 @@ int open_tun(const struct cfg_st *config, struct tun_st* tun, struct lease_st**
tunfd = open("/dev/net/tun", O_RDWR);
if (tunfd < 0) {
int e = errno;
syslog(LOG_ERR, "Can't open /dev/net/tun: %s\n",
mslog(s, NULL, LOG_ERR, "Can't open /dev/net/tun: %s\n",
strerror(e));
return -1;
}
@@ -386,39 +376,39 @@ int open_tun(const struct cfg_st *config, struct tun_st* tun, struct lease_st**
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
snprintf(lease->name, sizeof(lease->name), "%s%u", config->network.name, 0);
snprintf(lease->name, sizeof(lease->name), "%s%u", s->config->network.name, current);
memcpy(ifr.ifr_name, lease->name, sizeof(ifr.ifr_name));
if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0) {
e = errno;
syslog(LOG_ERR, "TUNSETIFF: %s\n", strerror(e));
mslog(s, NULL, LOG_ERR, "%s: TUNSETIFF: %s\n", lease->name, strerror(e));
goto fail;
}
if (config->uid != -1) {
t = config->uid;
if (s->config->uid != -1) {
t = s->config->uid;
ret = ioctl(tunfd, TUNSETOWNER, t);
if (ret < 0) {
e = errno;
syslog(LOG_INFO, "TUNSETOWNER: %s\n",
strerror(e));
mslog(s, NULL, LOG_INFO, "%s: TUNSETOWNER: %s\n",
lease->name, strerror(e));
goto fail;
}
}
if (config->gid != -1) {
t = config->uid;
if (s->config->gid != -1) {
t = s->config->uid;
ret = ioctl(tunfd, TUNSETGROUP, t);
if (ret < 0) {
e = errno;
syslog(LOG_ERR, "TUNSETGROUP: %s\n",
strerror(e));
mslog(s, NULL, LOG_ERR, "%s: TUNSETGROUP: %s\n",
lease->name, strerror(e));
goto fail;
}
}
/* set IP/mask */
ret = set_network_info(lease);
ret = set_network_info(s, lease);
if (ret < 0) {
goto fail;
}

View File

@@ -48,6 +48,4 @@ inline static void tun_st_deinit(struct tun_st* ts)
}
}
int open_tun(const struct cfg_st *config, struct tun_st* tun, struct lease_st **lease);
#endif

View File

@@ -53,6 +53,7 @@ struct vpn_st {
struct cfg_st {
const char *name;
unsigned int port;
unsigned int udp_port;
const char *cert;
const char *key;
const char *ca;
@@ -102,4 +103,6 @@ const char *human_addr(const struct sockaddr *sa, socklen_t salen,
#define SA_IN_PORT(p) (((struct sockaddr_in *)(p))->sin_port)
#define SA_IN6_PORT(p) (((struct sockaddr_in6 *)(p))->sin6_port)
#define SA_IN_P_GENERIC(addr, size) ((size==sizeof(struct sockaddr_in))?SA_IN_U8_P(addr):SA_IN6_U8_P(addr))
#define SA_IN_SIZE(size) ((size==sizeof(struct sockaddr_in))?sizeof(struct in_addr):sizeof(struct in6_addr))
#endif

View File

@@ -157,8 +157,7 @@ static int recv_auth_reply(worker_st *ws)
uint8_t cmd = 0;
struct cmd_auth_reply_st resp;
struct msghdr hdr;
int ret;
int ret, cmdlen;
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
@@ -179,15 +178,25 @@ static int recv_auth_reply(worker_st *ws)
hdr.msg_controllen = sizeof(control_un.control);
ret = recvmsg( ws->cmd_fd, &hdr, 0);
if (ret < sizeof(resp)+1) {
oclog(ws, LOG_ERR, "Received incorrect data (%d, expected %d) from main", ret, (int)sizeof(resp)+1);
cmdlen = ret;
if (cmdlen < 2) {
oclog(ws, LOG_ERR, "Received incorrect data (%d, expected %d) from main", cmdlen, (int)2);
return -1;
}
if (cmd != AUTH_REP)
return -1;
cmdlen--;
switch(resp.reply) {
case REP_AUTH_OK:
if (cmdlen < sizeof(resp)) {
oclog(ws, LOG_ERR, "Received incorrect data (%d, expected %d) from main", ret, (int)sizeof(resp)+1);
return -1;
}
if ( (cmptr = CMSG_FIRSTHDR(&hdr)) != NULL && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
if (cmptr->cmsg_level != SOL_SOCKET)
return -1;

View File

@@ -237,57 +237,11 @@ char* tmp = malloc(length+1);
static int setup_dtls_connection(struct worker_st *ws)
{
int ret, e;
int ret;
gnutls_session_t session;
struct sockaddr_storage cli_addr;
socklen_t cli_addr_size;
uint8_t buffer[512];
ssize_t buffer_size;
gnutls_datum_t master = { ws->master_secret, sizeof(ws->master_secret) };
gnutls_datum_t sid = { ws->session_id, sizeof(ws->session_id) };
unsigned port;
/* first receive from the correct client and connect socket */
cli_addr_size = sizeof(cli_addr);
ret = recvfrom(ws->udp_fd, buffer, sizeof(buffer), MSG_PEEK, (void*)&cli_addr, &cli_addr_size);
if (ret < 0) {
oclog(ws, LOG_ERR, "Error receiving in UDP socket");
return -1;
}
if (cli_addr_size == sizeof(struct sockaddr_in6)) {
port = SA_IN6_PORT(&cli_addr);
} else {
port = SA_IN_PORT(&cli_addr);
}
port = ntohs(port);
buffer_size = ret;
if ( (ws->remote_addr_len == sizeof(struct sockaddr_in) && memcmp(SA_IN_P(&cli_addr),
SA_IN_P(&ws->remote_addr), sizeof(struct in_addr)) == 0) ||
(ws->remote_addr_len == sizeof(struct sockaddr_in6) && memcmp(SA_IN6_P(&cli_addr),
SA_IN6_P(&ws->remote_addr), sizeof(struct in6_addr)) == 0)) {
/* connect to host */
#if 1
oclog(ws, LOG_ERR, "Connecting to UDP port %u", port);
ret = connect(ws->udp_fd, (void*)&cli_addr, cli_addr_size);
if (ret < 0) {
e = errno;
oclog(ws, LOG_ERR, "Error connecting: %s", strerror(e));
return -1;
}
#endif
} else {
/* received packet from unknown host */
oclog(ws, LOG_ERR, "Received UDP packet from unexpected host; discarding it");
recv(ws->udp_fd, buffer, buffer_size, 0);
return 0;
}
/* DTLS cookie verified.
* Initialize session.
*/
@@ -354,6 +308,10 @@ void vpn_server(struct worker_st* ws)
syslog(LOG_INFO, "Accepted connection from %s",
human_addr((void*)&ws->remote_addr, ws->remote_addr_len,
buf, sizeof(buf)));
if (ws->remote_addr_len == sizeof(struct sockaddr_in))
ws->proto = AF_INET;
else
ws->proto = AF_INET6;
/* initialize the session */
ret = gnutls_init(&session, GNUTLS_SERVER);
@@ -631,103 +589,6 @@ fail:
return ret;
}
static int open_udp_port(worker_st *ws)
{
int s, e, ret;
struct sockaddr_storage si;
struct sockaddr_storage stcp;
socklen_t len;
int proto;
len = sizeof(stcp);
ret = getsockname(ws->conn_fd, (void*)&stcp, &len);
if (ret == -1) {
e = errno;
oclog(ws, LOG_ERR, "Error in getsockname: %s", strerror(e));
return -1;
}
proto = ((struct sockaddr*)&stcp)->sa_family;
s = socket(proto, SOCK_DGRAM, IPPROTO_UDP);
if (s == -1) {
e = errno;
oclog(ws, LOG_ERR, "Could not open a UDP socket: %s", strerror(e));
return -1;
}
/* listen on the same IP the client connected at */
memset(&si, 0, sizeof(si));
((struct sockaddr*)&si)->sa_family = proto;
if (proto == AF_INET) {
memcpy(SA_IN_P(&si), SA_IN_P(&stcp), sizeof(*SA_IN_P(&si)));
} else if (proto == AF_INET6) {
memcpy(SA_IN6_P(&si), SA_IN6_P(&stcp), sizeof(*SA_IN6_P(&si)));
} else {
oclog(ws, LOG_ERR, "Unknown protocol family: %d", proto);
goto fail;
}
/* make sure we don't fragment packets */
#if defined(IP_DONTFRAG)
ret = 1;
if (setsockopt (s, IPPROTO_IP, IP_DONTFRAG,
(const void *) &ret, sizeof (ret)) < 0)
if (ret < 0) {
e = errno;
oclog(ws, LOG_ERR, "Error in setsockopt (IP_DF): %s", strerror(e));
goto fail;
}
#elif defined(IP_MTU_DISCOVER)
ret = IP_PMTUDISC_DO;
if (setsockopt (s, IPPROTO_IP, IP_MTU_DISCOVER,
(const void *) &ret, sizeof (ret)) < 0)
if (ret < 0) {
e = errno;
oclog(ws, LOG_ERR, "Error in setsockopt (IP_MTU_DISCOVER): %s", strerror(e));
goto fail;
}
#endif
#ifdef SO_REUSEPORT
ret = 1;
ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &ret, sizeof(ret));
if (ret < 0) {
e = errno;
oclog(ws, LOG_ERR, "Error in setsockopt (SO_REUSEPORT): %s", strerror(e));
goto fail;
}
#endif
ret = bind(s, (void*)&si, len);
if (ret == -1) {
e = errno;
oclog(ws, LOG_ERR, "Could not bind on a UDP port: %s", strerror(e));
goto fail;
}
len = sizeof(si);
ret = getsockname(s, (void*)&si, &len);
if (ret == -1) {
e = errno;
oclog(ws, LOG_ERR, "Could not obtain UDP port number: %s", strerror(e));
goto fail;
}
if (proto == AF_INET) {
ws->udp_port = ntohs(SA_IN_PORT(&si));
} else {
ws->udp_port = ntohs(SA_IN6_PORT(&si));
}
ws->udp_port_proto = proto;
ws->udp_fd = s;
return 0;
fail:
close(s);
return -1;
}
static ssize_t tun_write(int sockfd, const void *buf, size_t len)
{
int left = len;
@@ -845,7 +706,7 @@ unsigned mtu_overhead, dtls_mtu = 0;
}
if (ws->config->network.name == NULL) {
oclog(ws, LOG_ERR, "No networks are configured. Rejecting client");
oclog(ws, LOG_ERR, "No networks are configured; rejecting client");
tls_puts(ws->session, "HTTP/1.1 503 Service Unavailable\r\n");
tls_puts(ws->session, "X-Reason: Server configuration error\r\n\r\n");
return -1;
@@ -869,12 +730,7 @@ unsigned mtu_overhead, dtls_mtu = 0;
ws->udp_state = UP_DISABLED;
if (req->master_secret_set != 0) {
memcpy(ws->master_secret, req->master_secret, TLS_MASTER_SIZE);
ret = open_udp_port(ws);
if (ret < 0) {
oclog(ws, LOG_NOTICE, "Could not open UDP port");
} else
ws->udp_state = UP_SETUP;
ws->udp_state = UP_SETUP;
}
@@ -914,12 +770,13 @@ unsigned mtu_overhead, dtls_mtu = 0;
}
tls_printf(ws->session, "X-DTLS-Session-ID: %s\r\n", buffer);
tls_printf(ws->session, "X-DTLS-Port: %u\r\n", ws->udp_port);
tls_printf(ws->session, "X-DTLS-Port: %u\r\n", ws->config->udp_port);
tls_puts(ws->session, "X-DTLS-ReKey-Time: 86400\r\n");
tls_printf(ws->session, "X-DTLS-Keepalive: %u\r\n", ws->config->keepalive);
tls_puts(ws->session, "X-DTLS-CipherSuite: "OPENSSL_CIPHERSUITE"\r\n");
if (ws->udp_port_proto == AF_INET)
/* assume that if IPv6 is used over TCP then the same would be used over UDP */
if (ws->proto == AF_INET)
mtu_overhead = 20+8;
else
mtu_overhead = 40+8;
@@ -940,7 +797,7 @@ unsigned mtu_overhead, dtls_mtu = 0;
max = MAX(ws->cmd_fd,ws->conn_fd);
max = MAX(max,ws->tun_fd);
if (ws->udp_state != UP_DISABLED) {
if (ws->udp_state != UP_DISABLED && ws->udp_fd != -1) {
FD_SET(ws->udp_fd, &rfds);
max = MAX(max,ws->udp_fd);
}
@@ -1046,7 +903,7 @@ unsigned mtu_overhead, dtls_mtu = 0;
}
}
if (ws->udp_state != UP_DISABLED && (FD_ISSET(ws->udp_fd, &rfds) || dtls_pending != 0)) {
if (ws->udp_fd != -1 && ws->udp_state != UP_DISABLED && (FD_ISSET(ws->udp_fd, &rfds) || dtls_pending != 0)) {
switch (ws->udp_state) {
case UP_ACTIVE:
@@ -1135,10 +992,16 @@ int handle_worker_commands(struct worker_st *ws)
uint8_t cmd;
struct msghdr hdr;
union {
char x[20];
char x[32];
struct sockaddr_storage ss;
} cmd_data;
int ret;
/*int cmd_data_len;*/
union {
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
int ret, e;
int cmd_data_len;
memset(&cmd_data, 0, sizeof(cmd_data));
@@ -1151,6 +1014,9 @@ int handle_worker_commands(struct worker_st *ws)
memset(&hdr, 0, sizeof(hdr));
hdr.msg_iov = iov;
hdr.msg_iovlen = 2;
hdr.msg_control = control_un.control;
hdr.msg_controllen = sizeof(control_un.control);
ret = recvmsg( ws->cmd_fd, &hdr, 0);
if (ret == -1) {
@@ -1162,11 +1028,39 @@ int handle_worker_commands(struct worker_st *ws)
exit(1);
}
/*cmd_data_len = ret - 1;*/
cmd_data_len = ret - 1;
switch(cmd) {
case CMD_TERMINATE:
exit(0);
case CMD_UDP_FD:
if ( (cmptr = CMSG_FIRSTHDR(&hdr)) != NULL && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
if (cmptr->cmsg_level != SOL_SOCKET)
return -1;
if (cmptr->cmsg_type != SCM_RIGHTS)
return -1;
memcpy(&ws->udp_fd, CMSG_DATA(cmptr), sizeof(int));
if (cmd_data_len > 0) {
ret = connect(ws->udp_fd, (void*)&cmd_data.ss, cmd_data_len);
if (ret == -1) {
e = errno;
oclog(ws, LOG_ERR, "connect(): %s", strerror(e));
goto udp_fd_fail;
}
} else {
oclog(ws, LOG_ERR, "Didn't receive peer's UDP address");
goto udp_fd_fail;
}
return 0;
udp_fd_fail:
close(ws->udp_fd);
ws->udp_fd = -1;
return -1;
} else {
return -1;
}
break;
default:
oclog(ws, LOG_ERR, "Unknown CMD 0x%x", (unsigned)cmd);
exit(1);

View File

@@ -44,12 +44,11 @@ typedef struct worker_st {
struct sockaddr_storage remote_addr; /* peer's address */
socklen_t remote_addr_len;
int proto; /* AF_INET or AF_INET6 */
/* set after authentication */
int udp_fd;
udp_port_state_t udp_state;
unsigned int udp_port;
int udp_port_proto;
/* for mtu trials */
unsigned last_good_mtu;