Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Status-file option like in OpenVPN #831

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ const struct vpn_config invalid_cfg = {
.user_agent = NULL,
.hostcheck = NULL,
.check_virtual_desktop = NULL,
.status_file = {'\0'},
.status_interval = 0
};

/*
Expand Down Expand Up @@ -458,6 +460,12 @@ int load_config(struct vpn_config *cfg, const char *filename)
} else if (strcmp(key, "check-virtual-desktop") == 0) {
free(cfg->check_virtual_desktop);
cfg->check_virtual_desktop = strdup(val);
} else if (strcmp(key, "status-file") == 0) {
strcpy(cfg->status_file, val);
} else if (strcmp(key, "status-interval") == 0) {
unsigned long status_interval = strtoul(val, NULL, 0);

cfg->status_interval = status_interval;
} else {
log_warn("Bad key in config file: \"%s\".\n", key);
goto err_free;
Expand Down Expand Up @@ -614,4 +622,8 @@ void merge_config(struct vpn_config *dst, struct vpn_config *src)
dst->hostcheck = src->hostcheck;
if (src->check_virtual_desktop != invalid_cfg.check_virtual_desktop)
dst->check_virtual_desktop = src->check_virtual_desktop;
if (src->status_file[0])
strcpy(dst->status_file, src->status_file);
if (src->status_interval != invalid_cfg.status_interval)
dst->status_interval = src->status_interval;
}
3 changes: 3 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ struct x509_digest {
#define OTP_SIZE 64
#define REALM_SIZE 63
#define PEM_PASSPHRASE_SIZE 31
#define STATUS_FILE_SIZE 253

/*
* RFC 6265 does not limit the size of cookies:
Expand Down Expand Up @@ -96,6 +97,8 @@ struct vpn_config {
char *pinentry;
char iface_name[IF_NAMESIZE];
char realm[REALM_SIZE + 1];
char status_file[STATUS_FILE_SIZE + 1];
unsigned int status_interval;

int set_routes;
int set_dns;
Expand Down
129 changes: 125 additions & 4 deletions src/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ static void destroy_ssl_locks(void)
// global variable to pass signal out of its handler
volatile sig_atomic_t sig_received = 0;

enum tunnel_state *current_state;
static uint64_t total_received_bytes;
static uint64_t total_transmitted_bytes;
static struct in_addr ip_addr;
static struct in_addr ns1_addr;
static struct in_addr ns2_addr;
static char dns_suffix[MAX_DOMAIN_LENGTH];
static const char *status_file;

int get_sig_received(void)
{
return (int)sig_received;
Expand Down Expand Up @@ -261,6 +270,8 @@ static void *pppd_read(void *arg)

log_debug("%s ---> gateway (%lu bytes)\n", PPP_DAEMON,
packet->len);
total_transmitted_bytes += packet->len;
current_state = &tunnel->state;
#if HAVE_USR_SBIN_PPPD
log_packet("pppd: ", packet->len, pkt_data(packet));
#else
Expand Down Expand Up @@ -292,6 +303,7 @@ static void *pppd_write(void *arg)
struct tunnel *tunnel = (struct tunnel *) arg;
fd_set write_fd;

current_state = &tunnel->state;
FD_ZERO(&write_fd);
FD_SET(tunnel->pppd_pty, &write_fd);

Expand Down Expand Up @@ -489,6 +501,7 @@ static void *ssl_read(void *arg)
}

log_debug("gateway ---> %s (%lu bytes)\n", PPP_DAEMON, packet->len);
total_received_bytes += packet->len;
log_packet("gtw: ", packet->len, pkt_data(packet));
pool_push(&tunnel->ssl_to_pty_pool, packet);

Expand All @@ -510,6 +523,14 @@ static void *ssl_read(void *arg)
}
strcat(line, "]");
log_info("Got addresses: %s\n", line);
memcpy(&ip_addr, &tunnel->ipv4.ip_addr,
sizeof(struct in_addr));
memcpy(&ns1_addr, &tunnel->ipv4.ns1_addr,
sizeof(struct in_addr));
memcpy(&ns2_addr, &tunnel->ipv4.ns2_addr,
sizeof(struct in_addr));
memcpy(&dns_suffix, tunnel->ipv4.dns_suffix,
MAX_DOMAIN_LENGTH);
}
if (packet_is_end_negociation(packet)) {
log_info("Negotiation complete.\n");
Expand Down Expand Up @@ -604,11 +625,87 @@ static void *if_config(void *arg)
return NULL;
}

static void save_stats(void)
{
FILE *fp;
const char *state_str;

if (current_state != NULL) {
switch (*current_state) {
case STATE_DOWN: {
state_str = "State: disconnected";
break;
}
case STATE_CONNECTING: {
state_str = "State: connecting";
break;
}
case STATE_UP: {
state_str = "State: connected";
break;
}
case STATE_DISCONNECTING: {
state_str = "State: disconnecting";
break;
}
}
}
if (status_file[0] != '\0') {
fp = fopen(status_file, "w");
if (fp != NULL) {
fprintf(fp, "State: %s\n", state_str);
fprintf(fp, "Received bytes: %lu\n", total_received_bytes);
fprintf(fp, "Transmitted bytes: %lu\n", total_transmitted_bytes);
fprintf(fp, "IP Address: %s\n", inet_ntoa(ip_addr));
fprintf(fp, "NSS1 Address: %s\n", inet_ntoa(ns1_addr));
fprintf(fp, "NSS2 Address: %s\n", inet_ntoa(ns2_addr));
fprintf(fp, "Domain: %s\n", dns_suffix);
fclose(fp);
} else {
log_error(" Cannot create status file!\n");
}
}
log_info("State: %s\n", state_str);
log_info("Received bytes: %lu\n", total_received_bytes);
log_info("Transmitted bytes: %lu\n", total_transmitted_bytes);
log_info("IP Address: %s\n", inet_ntoa(ip_addr));
log_info("NSS1 Address: %s\n", inet_ntoa(ns1_addr));
log_info("NSS2 Address: %s\n", inet_ntoa(ns2_addr));
log_info("Domain: %s\n", dns_suffix);
}

/*
* Thread to print statistics
*/
static void *status_thread(void *arg)
{
struct tunnel *tunnel = (struct tunnel *) arg;

log_debug("%s thread\n", __func__);

// Wait for the right moment to configure IP interface
SEM_WAIT(&sem_if_config);
if (tunnel->config->status_file[0] != '\0') {
while (1) {
current_state = &tunnel->state;
save_stats();
log_debug("Sleeping statistics thread for %u seconds...",
tunnel->config->status_interval);
sleep(tunnel->config->status_interval);
}
}
return NULL;
}

static void sig_handler(int signo)
{
sig_received = signo;
if (signo == SIGINT || signo == SIGTERM)
SEM_POST(&sem_stop_io);
if (signo == SIGUSR1) {
save_stats();
} else {
sig_received = signo;
if (signo == SIGINT || signo == SIGTERM)
SEM_POST(&sem_stop_io);
}
}

int io_loop(struct tunnel *tunnel)
Expand All @@ -622,6 +719,7 @@ int io_loop(struct tunnel *tunnel)
pthread_t ssl_read_thread;
pthread_t ssl_write_thread;
pthread_t if_config_thread;
pthread_t stats_thread;

SEM_INIT(&sem_pppd_ready, 0, 0);
SEM_INIT(&sem_if_config, 0, 0);
Expand All @@ -633,6 +731,7 @@ int io_loop(struct tunnel *tunnel)
init_ssl_locks();

init_hdlc();
status_file = tunnel->config->status_file;

/*
* I noticed that using TCP_NODELAY (i.e. disabling Nagle's algorithm)
Expand All @@ -659,12 +758,14 @@ int io_loop(struct tunnel *tunnel)
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
sigaddset(&sigset, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &sigset, &oldset);
#endif

// Set signal handler
if (signal(SIGINT, sig_handler) == SIG_ERR ||
signal(SIGTERM, sig_handler) == SIG_ERR)
signal(SIGTERM, sig_handler) == SIG_ERR ||
signal(SIGUSR1, sig_handler) == SIG_ERR)
goto err_signal;

// Ignore SIGHUP
Expand Down Expand Up @@ -702,6 +803,14 @@ int io_loop(struct tunnel *tunnel)
goto err_thread;
}

if (status_file[0] != '\0' && tunnel->config->status_interval != 0) {
ret = pthread_create(&stats_thread, NULL, status_thread, tunnel);
if (ret != 0) {
log_debug("Error creating status_thread: %s\n", strerror(ret));
goto err_thread;
}
}

#if !HAVE_MACH_MACH_H
// Restore the signal for the main thread
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
Expand All @@ -715,6 +824,11 @@ int io_loop(struct tunnel *tunnel)
ret = pthread_cancel(if_config_thread);
if (ret != 0)
log_debug("Error canceling if_config_thread: %s\n", strerror(ret));
if (status_file[0] != '\0') {
ret = pthread_cancel(stats_thread);
if (ret != 0)
log_debug("Error canceling stats_thread: %s\n", strerror(ret));
}

ret = pthread_cancel(ssl_write_thread);
if (ret != 0)
Expand All @@ -734,6 +848,13 @@ int io_loop(struct tunnel *tunnel)

log_info("Cleanup, joining threads...\n");
// failure to clean is a possible zombie thread, consider it fatal
if (status_file[0] != '\0' && tunnel->config->status_interval != 0) {
ret = pthread_join(stats_thread, NULL);
if (ret != 0) {
log_debug("Error joining stats_thread: %s\n", strerror(ret));
fatal = 1;
}
}
ret = pthread_join(if_config_thread, NULL);
if (ret != 0) {
log_debug("Error joining if_config_thread: %s\n", strerror(ret));
Expand Down
34 changes: 33 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,11 @@ PPPD_USAGE \
" certificate will be matched against this value.\n" \
" <digest> is the X509 certificate's sha256 sum.\n" \
" This option can be used multiple times to trust\n" \
" several certificates.\n"
" several certificates.\n" \
" --status Create a status file with statistics.\n" \
" --status-interval Interval between updating status file. \n " \
" If status file is not set, then print it on log.\n" \
" Printing is disabled by default.\n"

#define help_options_part2 \
" --insecure-ssl Do not disable insecure SSL protocols/ciphers.\n" \
Expand Down Expand Up @@ -208,6 +212,8 @@ int main(int argc, char **argv)
.use_syslog = 0,
.half_internet_routes = 0,
.persistent = 0,
.status_file = {'\0'},
.status_interval = 0,
#if HAVE_RESOLVCONF
.use_resolvconf = USE_RESOLVCONF,
#endif
Expand Down Expand Up @@ -270,6 +276,8 @@ int main(int argc, char **argv)
{"cipher-list", required_argument, NULL, 0},
{"min-tls", required_argument, NULL, 0},
{"seclevel-1", no_argument, &cli_cfg.seclevel_1, 1},
{"status-file", required_argument, NULL, 0},
{"status-interval", required_argument, NULL, 0},
#if HAVE_USR_SBIN_PPPD
{"pppd-use-peerdns", required_argument, NULL, 0},
{"pppd-no-peerdns", no_argument, &cli_cfg.pppd_use_peerdns, 0},
Expand Down Expand Up @@ -307,6 +315,7 @@ int main(int argc, char **argv)
/* If this option set a flag, do nothing else now. */
if (long_options[option_index].flag != 0)
break;
log_debug("%s\n", long_options[option_index].name);
if (strcmp(long_options[option_index].name,
"version") == 0) {
printf(VERSION "\n");
Expand Down Expand Up @@ -509,6 +518,26 @@ int main(int argc, char **argv)
cli_cfg.set_dns = set_dns;
break;
}
if (strcmp(long_options[option_index].name,
"status-file") == 0) {
log_debug(" COpying %s\n", optarg);
strncpy(cli_cfg.status_file, optarg, STATUS_FILE_SIZE);
cli_cfg.status_file[STATUS_FILE_SIZE] = '\0';
log_debug(" COpying %s\n", cli_cfg.status_file);
break;
}
if (strcmp(long_options[option_index].name,
"status-interval") == 0) {
long status_interval = strtol(optarg, NULL, 0);

if (status_interval < 0 || status_interval > UINT_MAX) {
log_warn("Bad status_interval option: \"%s\"\n",
optarg);
break;
}
cli_cfg.status_interval = status_interval;
break;
}
goto user_error;
case 'h':
printf("%s%s%s%s%s%s%s", usage, summary,
Expand Down Expand Up @@ -625,6 +654,9 @@ int main(int argc, char **argv)
log_debug_all("Config password = \"%s\"\n", cfg.password);
if (cfg.otp[0] != '\0')
log_debug("One-time password = \"%s\"\n", cfg.otp);
if (cfg.status_file[0] != '\0')
log_debug("Config status-file = \"%s\"\n", cfg.status_file);
log_debug("Config status-interval = \"%u\"\n", cfg.status_interval);

if (geteuid() != 0) {
log_error("This process was not spawned with root privileges, which are required.\n");
Expand Down