#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <paths.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/kern_event.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <net/if_var.h>
#include <netinet/in_var.h>
#include <mach/mach_time.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include "vpnoptions.h"
#include "vpnplugins.h"
#include "vpnd.h"
#define VPN_ADDR_DELETE 0x1
struct vpn_address {
TAILQ_ENTRY(vpn_address) next;
int pid;
int flags;
char ip_address[16];
};
#define LB_MAX_SLAVE_AGE 10
#define LB_IPCONFIG_TIMEOUT 60
struct lb_slave {
TAILQ_ENTRY(lb_slave) next;
struct sockaddr_in server_address;
int age;
struct in_addr redirect_address;
u_int16_t max_connection;
u_int16_t cur_connection;
u_int32_t ratio;
};
enum {
HEALTH_UNKNOWN = -1,
HEALTH_OK,
HEALTH_SICK,
HEALTH_DEAD
};
static int listen_sockfd = -1;
static int lb_sockfd = -1;
static int evt_sockfd = -1;
static int health_sockfd = -1;
static int health_state = HEALTH_UNKNOWN;
static struct vpn_channel the_vpn_channel;
static double timeScaleSeconds;
static double timeScaleMicroSeconds;
int lb_is_started = 0; int lb_is_master = 0; struct sockaddr_in lb_master_address; TAILQ_HEAD(, lb_slave) lb_slaves_list; u_int16_t lb_cur_connections = 0; u_int16_t lb_max_connections = 0; struct lb_slave *lb_next_slave = 0; int lb_ipconfig_time = 0;
TAILQ_HEAD(, vpn_address) save_address_list;
TAILQ_HEAD(, vpn_address) free_address_list;
TAILQ_HEAD(, vpn_address) child_list;
extern int got_sig_chld(void);
extern int got_sig_hup(void);
extern int got_sig_usr1(void);
extern int got_terminate(void);
static pid_t fork_child(int fdSocket);
static int reap_children(void);
static int terminate_children(void);
static int getabsolutetime(struct timeval *timenow);
static void determine_next_slave(struct vpn_params* params);
int start_load_balancing(struct vpn_params *params, fd_set *out_fds, int *out_fdmax);
int stop_load_balancing(struct vpn_params *params, fd_set *out_fds);
void init_address_lists(void)
{
TAILQ_INIT(&free_address_list);
TAILQ_INIT(&save_address_list);
TAILQ_INIT(&child_list);
TAILQ_INIT(&lb_slaves_list);
lb_max_connections = 0;
lb_cur_connections = 0;
}
int add_address(char* ip_address)
{
struct vpn_address *address_slot;
int size;
if ((size = strlen(ip_address) + 1) > 16)
return -1;
address_slot = (struct vpn_address*)malloc(sizeof(struct vpn_address));
if (address_slot == 0)
return -1;
lb_max_connections++;
memcpy(address_slot->ip_address, ip_address, strlen(ip_address) + 1);
address_slot->flags = 0;
TAILQ_INSERT_TAIL(&free_address_list, address_slot, next);
return 0;
}
int add_address_range(char* ip_addr_start, char* ip_addr_end)
{
struct in_addr start_addr;
struct in_addr end_addr;
struct in_addr cur_addr;
char addr_str[16];
char *ip_addr;
if (!ip_addr_end)
return add_address(ip_addr_start);
if (inet_pton(AF_INET, ip_addr_start, &start_addr) < 1)
return -1;
if (inet_pton(AF_INET, ip_addr_end, &end_addr) < 1)
return -1;
start_addr.s_addr = ntohl(start_addr.s_addr);
end_addr.s_addr = ntohl(end_addr.s_addr);
if (start_addr.s_addr > end_addr.s_addr)
return -1;
if (start_addr.s_addr == end_addr.s_addr)
return add_address(ip_addr_start);
for (; start_addr.s_addr <= end_addr.s_addr; start_addr.s_addr++) {
cur_addr = start_addr;
cur_addr.s_addr = htonl(cur_addr.s_addr);
if (ip_addr = (char*)inet_ntop(AF_INET, &cur_addr, addr_str, 16)) {
if (add_address(ip_addr))
return -1;
} else
return -1;
}
return 0;
}
void begin_address_update(void)
{
struct vpn_address *address_slot;
while (address_slot = (struct vpn_address*)TAILQ_FIRST(&free_address_list)) {
TAILQ_REMOVE(&free_address_list, address_slot, next);
TAILQ_INSERT_TAIL(&save_address_list, address_slot, next);
}
}
void cancel_address_update(void)
{
struct vpn_address *address_slot;
while (address_slot = (struct vpn_address*)TAILQ_FIRST(&free_address_list)) {
TAILQ_REMOVE(&free_address_list, address_slot, next);
free(address_slot);
lb_max_connections--;
}
while (address_slot = (struct vpn_address*)TAILQ_FIRST(&save_address_list)) {
TAILQ_REMOVE(&save_address_list, address_slot, next);
TAILQ_INSERT_TAIL(&free_address_list, address_slot, next);
}
}
void apply_address_update(void)
{
struct vpn_address *address_slot;
struct vpn_address *child_address;
while (address_slot = (struct vpn_address*)TAILQ_FIRST(&save_address_list)) {
TAILQ_REMOVE(&save_address_list, address_slot, next);
free(address_slot);
lb_max_connections--;
}
TAILQ_FOREACH(child_address, &child_list, next) {
child_address->flags |= VPN_ADDR_DELETE;
TAILQ_FOREACH(address_slot, &free_address_list, next) {
if (!strcmp(child_address->ip_address, address_slot->ip_address)) {
TAILQ_REMOVE(&free_address_list, address_slot, next); free(address_slot);
lb_max_connections--;
child_address->flags &= ~VPN_ADDR_DELETE; break;
}
}
if (child_address->flags & VPN_ADDR_DELETE)
while (kill(child_address->pid, SIGTERM) < 0)
if (errno != EINTR) {
vpnlog(LOG_ERR, "VPND: error terminating child - err = %s\n", strerror(errno));
break;
}
}
vpnlog(LOG_DEBUG, "address list updated\n");
reap_children();
}
int address_avail(void)
{
return (TAILQ_FIRST(&free_address_list) != 0);
}
int init_plugin(struct vpn_params *params)
{
char path[MAXPATHLEN], name[MAXPATHLEN], *p;
CFBundleRef pluginbdl, bdl;
CFURLRef pluginurl, url;
int (*start)(struct vpn_channel*, CFBundleRef, CFBundleRef, int debug, int log_verbose) = 0;
bool isPPP;
int len, err = -1;
bzero(&the_vpn_channel, sizeof(struct vpn_channel));
if (params->plugin_path == 0) {
err = add_builtin_plugin(params, &the_vpn_channel);
if (err)
vpnlog(LOG_ERR, "Cannot initialize built-in channel\n");
return err;
}
len = strlen(params->plugin_path);
if (len > 4 && !strcmp(¶ms->plugin_path[len - 4], ".ppp"))
isPPP = 1;
else if (len > 4 && !strcmp(¶ms->plugin_path[len - 4], ".vpn"))
isPPP = 0;
else {
vpnlog(LOG_ERR, "Plugin ''%s' has an incorrect suffix (must end with '.ppp' or '.vpn')\n", params->plugin_path);
return -1;
}
if (params->plugin_path[0] == '/') {
strlcpy(path, params->plugin_path, sizeof(path));
for (p = ¶ms->plugin_path[len - 1]; *p != '/'; p--);
strncpy(name, p + 1, strlen(p) - 5);
*(name + (strlen(p) - 5)) = 0;
}
else {
strlcpy(path, PLUGINS_DIR, sizeof(path));
strlcat(path, params->plugin_path, sizeof(path));
strlcpy(name, params->plugin_path, sizeof(name) - 4); *(name + len - 4) = 0;
}
vpnlog(LOG_NOTICE, "Loading plugin %s\n", path);
if (url = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8 *)path, strlen(path), TRUE)) {
if (bdl = CFBundleCreate(NULL, url)) {
if (isPPP) {
if (pluginurl = CFBundleCopyBuiltInPlugInsURL(bdl)) {
strlcat(path, "/", sizeof(path));
CFURLGetFileSystemRepresentation(pluginurl, 0, (UInt8 *)(path+strlen(path)), MAXPATHLEN-strlen(path));
CFRelease(pluginurl);
strlcat(path, "/", sizeof(path));
strlcat(path, name, sizeof(path));
strlcat(path, ".vpn", sizeof(path));
if (pluginurl = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8 *)path, strlen(path), TRUE)) {
if (pluginbdl = CFBundleCreate(NULL, pluginurl)) {
if (CFBundleLoadExecutable(pluginbdl)
&& (start = CFBundleGetFunctionPointerForName(pluginbdl, CFSTR("start"))))
err = (*start)(&the_vpn_channel, pluginbdl, bdl, params->debug, params->log_verbose);
CFRelease(pluginbdl);
}
CFRelease(pluginurl);
}
}
}
else {
if (CFBundleLoadExecutable(bdl)
&& (start = CFBundleGetFunctionPointerForName(bdl, CFSTR("start"))))
err = (*start)(&the_vpn_channel, bdl, NULL, params->debug, params->log_verbose);
}
CFRelease(bdl);
}
CFRelease(url);
}
if (err) {
vpnlog(LOG_ERR, "Unable to load plugin (error = %d)\n", err);
return -1;
}
if (the_vpn_channel.listen == 0 || the_vpn_channel.accept == 0
|| the_vpn_channel.refuse == 0 || the_vpn_channel.close == 0) {
vpnlog(LOG_ERR, "Plugin channel not properly initialized\n");
return -1;
}
return 0;
}
int get_plugin_args(struct vpn_params* params, int reload)
{
if (the_vpn_channel.get_pppd_args)
if (the_vpn_channel.get_pppd_args(params, reload))
return -1;
if (params->lb_enable && !the_vpn_channel.lb_redirect) {
vpnlog(LOG_ERR, "Plugin does not support Load Balancing\n");
params->lb_enable = 0;
}
return 0;
}
int call_ipconfig(char *command, char *interface, struct in_addr *address, int timeout)
{
int pid, exitcode = -1, status;
char str[32], str1[32];
if ((pid = fork()) < 0)
return 1;
if (pid == 0) {
int i;
for (i = getdtablesize() - 1; i >= 0; i--) close(i);
open("/dev/null", O_RDWR, 0);
dup(0);
dup(0);
inet_ntop(AF_INET, address, str, sizeof(str));
snprintf(str1, sizeof(str1), "%d", timeout);
execle("/usr/sbin/ipconfig", "ipconfig", command, interface, "FAILOVER", str, "255.255.255.255", str1, (char *)0, (char *)0);
exit(1);
}
while (waitpid(pid, &status, 0) < 0) {
if (errno == EINTR)
continue;
return 1;
}
exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
if (exitcode)
vpnlog(LOG_ERR, "Unable to configure IP Failover Service (error %d)\n", exitcode);
return exitcode;
}
int configure_failover(char *interface, struct in_addr *address, int timeout)
{
int err = -1;
err = call_ipconfig("setservice", interface, address, timeout);
if (err)
vpnlog(LOG_ERR, "Unable to configure IP Failover Service (error %d)\n", err);
return err;
}
int unconfigure_failover(char *interface, struct in_addr *address)
{
int err = -1;
err = call_ipconfig("removeservice", interface, address, 0);
if (err)
vpnlog(LOG_ERR, "Unable to unconfigure IP Failover Service (error %d)\n", err);
return err;
}
int health_check(struct vpn_params *params, int event, fd_set *out_fds, int *out_fdmax)
{
int fd = health_sockfd, err, ret = 0;
err = the_vpn_channel.health_check(&fd, event);
if (fd != health_sockfd) {
if (health_sockfd != -1)
FD_CLR(health_sockfd, out_fds);
health_sockfd = fd;
if (health_sockfd != -1) {
FD_SET(health_sockfd, out_fds);
if (*out_fdmax <= health_sockfd)
*out_fdmax = health_sockfd + 1;
}
}
switch (err) {
case 0:
if (health_state == HEALTH_SICK) {
vpnlog(LOG_ERR, "Health control check: server is back to normal...\n");
if (params->lb_enable) {
if (start_load_balancing(params, out_fds, out_fdmax) < 0)
ret = -1;
}
}
health_state = HEALTH_OK;
break;
case -2:
if (health_state == HEALTH_OK) {
vpnlog(LOG_ERR, "Health control check: server is sick...\n");
}
health_state = HEALTH_SICK;
if (lb_is_started)
stop_load_balancing(params, out_fds);
break;
default:
case -1:
health_state = HEALTH_DEAD;
vpnlog(LOG_ERR, "Health control check: server is dead...\n");
ret = -1;
break;
}
return ret;
}
int start_load_balancing(struct vpn_params *params, fd_set *out_fds, int *out_fdmax)
{
struct sockaddr_in listen_addr;
fd_set fds = *out_fds;
int fdmax = *out_fdmax;
struct kev_request kev_req;
if (lb_is_started)
return 0;
if (params->lb_enable && !the_vpn_channel.lb_redirect) {
vpnlog(LOG_ERR, "Plugin does not support Load Balancing\n");
params->lb_enable = 0;
}
evt_sockfd = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT);
if (evt_sockfd < 0) {
vpnlog(LOG_ERR, "Unable to create event socket (errno = %d)\n", errno);
goto fail;
}
FD_SET(evt_sockfd, &fds);
if (fdmax <= evt_sockfd)
fdmax = evt_sockfd + 1;
kev_req.vendor_code = KEV_VENDOR_APPLE;
kev_req.kev_class = KEV_NETWORK_CLASS;
kev_req.kev_subclass = KEV_INET_SUBCLASS;
ioctl(evt_sockfd, SIOCSKEVFILT, &kev_req);
lb_sockfd = socket(PF_INET, SOCK_DGRAM, 0);
if (lb_sockfd < 0) {
vpnlog(LOG_ERR, "Unable to create load balancing socket (errno = %d)\n", errno);
goto fail;
}
FD_SET(lb_sockfd, &fds);
if (fdmax <= lb_sockfd)
fdmax = lb_sockfd + 1;
bzero(&listen_addr, sizeof(listen_addr));
listen_addr.sin_family = PF_INET;
listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
listen_addr.sin_port = params->lb_port;
if (bind(lb_sockfd, (struct sockaddr*)&listen_addr, sizeof(listen_addr)) < 0 ){
vpnlog(LOG_ERR, "Unable to bind load balancing socket (errno = %d)\n", errno);
goto fail;
}
bzero(&lb_master_address, sizeof(lb_master_address));
lb_master_address.sin_family = AF_INET;
lb_master_address.sin_len = sizeof(lb_master_address);
lb_master_address.sin_addr = params->lb_cluster_address;
lb_master_address.sin_port = params->lb_port;
configure_failover(params->lb_interface, ¶ms->lb_cluster_address, LB_IPCONFIG_TIMEOUT);
lb_ipconfig_time = LB_IPCONFIG_TIMEOUT - 10;
lb_is_master = find_address(&lb_master_address, params->lb_interface);
*out_fds = fds;
*out_fdmax = fdmax;
lb_is_started = 1;
lb_next_slave = 0;
vpnlog(LOG_NOTICE, "Load Balancing: Started\n");
return 0;
fail:
if (evt_sockfd >= 0) {
close(evt_sockfd);
evt_sockfd = -1;
}
if (lb_sockfd >= 0) {
close(lb_sockfd);
lb_sockfd = -1;
}
return -1;
}
int stop_load_balancing(struct vpn_params *params, fd_set *out_fds)
{
if (!lb_is_started)
return 0;
if (evt_sockfd >= 0) {
FD_CLR(evt_sockfd, out_fds);
close(evt_sockfd);
evt_sockfd = -1;
}
if (lb_sockfd >= 0) {
FD_CLR(lb_sockfd, out_fds);
close(lb_sockfd);
lb_sockfd = -1;
}
unconfigure_failover(params->lb_interface, ¶ms->lb_cluster_address);
lb_is_master = 0;
lb_is_started = 0;
lb_next_slave = 0;
vpnlog(LOG_NOTICE, "Load Balancing: Stopped\n");
return 0;
}
static void determine_next_slave(struct vpn_params *params)
{
struct lb_slave *slave, *oldslave;
u_int32_t a;
oldslave = lb_next_slave;
lb_next_slave = TAILQ_FIRST(&lb_slaves_list);
TAILQ_FOREACH(slave, &lb_slaves_list, next) {
if (slave->ratio < lb_next_slave->ratio)
lb_next_slave = slave;
}
if (lb_next_slave && lb_next_slave != oldslave) {
a = ntohl(lb_next_slave->redirect_address.s_addr);
vpnlog(LOG_NOTICE, "Load Balancing: Next call will be redirected to slave with IP address %d.%d.%d.%d. Current slave load %d/%d.\n",
a >> 24 & 0xFF, a >> 16 & 0xFF, a >> 8 & 0xFF, a & 0xFF, lb_next_slave->cur_connection, lb_next_slave->max_connection);
if (the_vpn_channel.lb_redirect) {
the_vpn_channel.lb_redirect(¶ms->lb_cluster_address, &lb_next_slave->redirect_address);
}
}
}
#define LB_MSG_TYPE_UPDATE 1
struct lb_message {
u_int16_t type;
u_int16_t len;
u_int8_t version;
u_int8_t reserved1;
u_int16_t reserved2;
u_int32_t redirect_address; u_int16_t max_connection; u_int16_t cur_connection; u_int32_t reserved3;
u_int32_t reserved4;
};
void accept_connections(struct vpn_params* params)
{
pid_t pid_child;
fd_set fds, fds_save;
char addr_str[32];
int i, child_sockfd, fdmax, err, hastimeout;
struct vpn_address *address_slot;
struct sockaddr_in addr;
socklen_t addrlen;
size_t datalen;
char data[1000];
struct timeval timenow, timeout, timeend;
struct lb_message *lbmsg;
u_int16_t lbmsgtype;
struct lb_slave *slave;
u_int32_t a;
FD_ZERO(&fds_save);
fdmax = 0;
listen_sockfd = 0;
if (the_vpn_channel.listen)
listen_sockfd = the_vpn_channel.listen();
if (listen_sockfd < 0) {
vpnlog(LOG_ERR, "Unable to initialize vpn plugin\n");
goto fail;
}
if (listen_sockfd) {
FD_SET(listen_sockfd, &fds_save);
if (fdmax <= listen_sockfd)
fdmax = listen_sockfd + 1;
}
health_sockfd = -1;
if (params->lb_enable) {
if (start_load_balancing(params, &fds_save, &fdmax) < 0) {
goto fail;
}
}
hastimeout = lb_is_started || the_vpn_channel.health_check;
if (hastimeout) {
getabsolutetime(&timeend);
timeend.tv_sec += 1; }
while (!got_terminate()) {
if (hastimeout) {
getabsolutetime(&timenow);
timeout.tv_sec = timeend.tv_sec > timenow.tv_sec ? timeend.tv_sec - timenow.tv_sec : 0;
timeout.tv_usec = timeend.tv_usec - timenow.tv_usec;
if (timeout.tv_usec < 0) {
timeout.tv_usec += 1000000;
timeout.tv_sec -= 1;
}
if (timeout.tv_sec < 0)
timeout.tv_sec = timeout.tv_usec = 0;
}
fds = fds_save;
i = select(fdmax, &fds, NULL, NULL, hastimeout ? &timeout : 0);
if (i > 0) {
if (lb_is_started && FD_ISSET (evt_sockfd, &fds)) {
char buf[256];
struct kern_event_msg *ev_msg;
struct kev_in_data *inetdata;
if (recv(evt_sockfd, &buf, sizeof(buf), 0) != -1) {
ev_msg = (struct kern_event_msg *) &buf;
switch (ev_msg->event_code) {
case KEV_INET_NEW_ADDR:
inetdata = (struct kev_in_data *) &ev_msg->event_data[0];
if (inetdata->ia_addr.s_addr == params->lb_cluster_address.s_addr) {
vpnlog(LOG_NOTICE, "Load Balancing: Cluster address assigned. Server is becoming master...\n");
lb_is_master = 1;
}
break;
case KEV_INET_ADDR_DELETED:
inetdata = (struct kev_in_data *) &ev_msg->event_data[0];
if (inetdata->ia_addr.s_addr == params->lb_cluster_address.s_addr) {
vpnlog(LOG_NOTICE, "Load Balancing: Cluster address deleted. Server is no longer master...\n");
lb_is_master = 0;
while (slave = TAILQ_FIRST(&lb_slaves_list)) {
TAILQ_REMOVE(&lb_slaves_list, slave, next);
free(slave);
}
}
break;
}
}
}
if (lb_is_started && FD_ISSET (lb_sockfd, &fds)) {
addrlen = sizeof(addr);
datalen = recvfrom (lb_sockfd, data, sizeof(data) - 1, 0, (struct sockaddr*)&addr, &addrlen);
if (datalen > 0) {
data[datalen] = 0;
lbmsg = (struct lb_message *)data;
lbmsgtype = ntohs(lbmsg->type);
if (lb_is_master && lbmsgtype == LB_MSG_TYPE_UPDATE) {
TAILQ_FOREACH(slave, &lb_slaves_list, next) {
if (slave->server_address.sin_addr.s_addr == addr.sin_addr.s_addr)
break;
}
if (!slave) {
slave = malloc(sizeof(struct lb_slave));
if (slave == 0) {
vpnlog(LOG_ERR, "cannot allocate memory for slave server.\n");
break;
}
a = ntohl(addr.sin_addr.s_addr);
vpnlog(LOG_NOTICE, "Load Balancing: Slave server appeared with IP address %d.%d.%d.%d\n", a >> 24 & 0xFF, a >> 16 & 0xFF, a >> 8 & 0xFF, a & 0xFF);
bcopy(&addr, &slave->server_address, sizeof(addr));
TAILQ_INSERT_TAIL(&lb_slaves_list, slave, next);
}
slave->age = LB_MAX_SLAVE_AGE;
slave->redirect_address.s_addr = lbmsg->redirect_address;
slave->max_connection = ntohs(lbmsg->max_connection);
slave->cur_connection = ntohs(lbmsg->cur_connection);
slave->ratio = (lbmsg->cur_connection < lbmsg->max_connection) ? (lbmsg->cur_connection * 1000)/lbmsg->max_connection : 1000;
a = ntohl(lbmsg->redirect_address);
determine_next_slave(params);
}
}
}
if (health_sockfd != -1 && FD_ISSET (health_sockfd, &fds)) {
err = health_check(params, 1, &fds_save, &fdmax);
if (err < 0)
goto fail;
}
if (FD_ISSET (listen_sockfd, &fds)) {
address_slot = (struct vpn_address*)TAILQ_FIRST(&free_address_list);
if (address_slot == 0) {
if ((child_sockfd = the_vpn_channel.refuse()) < 0) {
vpnlog(LOG_ERR, "Error while refusing incoming call %s\n", strerror(errno));
continue;
}
} else {
if ((child_sockfd = the_vpn_channel.accept()) < 0) {
vpnlog(LOG_ERR, "Error accepting incoming call %s\n", strerror(errno));
continue;
}
}
if (child_sockfd == 0)
continue;
while ((pid_child = fork_child(child_sockfd)) < 0) {
if (errno != EINTR) {
vpnlog(LOG_ERR, "Error during fork = %s\n", strerror(errno));
goto fail;
} else if (got_terminate())
goto fail;
}
if (pid_child && address_slot) { vpnlog(LOG_NOTICE, "Incoming call... Address given to client = %s\n", address_slot->ip_address);
TAILQ_REMOVE(&free_address_list, address_slot, next);
address_slot->pid = pid_child;
TAILQ_INSERT_TAIL(&child_list, address_slot, next);
lb_cur_connections++;
} else if (address_slot) {
snprintf(addr_str, sizeof(addr_str), ":%s", address_slot->ip_address);
params->exec_args[params->next_arg_index] = addr_str; params->exec_args[params->next_arg_index + 1] = 0; execve(PATH_PPPD, params->exec_args, NULL);
vpnlog(LOG_ERR, "execve failed during exec of /usr/sbin/pppd\nARGUMENTS\n");
for (i = 1; i < MAXARG && i < params->next_arg_index; i++) {
if (params->exec_args[i])
vpnlog(LOG_DEBUG, "%d : %s\n", i, params->exec_args[i]);
}
vpnlog(LOG_DEBUG, "\n");
exit(1);
}
}
}
else if (i == 0) {
if (the_vpn_channel.health_check) {
err = health_check(params, 0, &fds_save, &fdmax);
if (err < 0)
goto fail;
}
if (lb_is_started) {
lbmsg = (struct lb_message *)data;
bzero(lbmsg, sizeof(struct lb_message));
lbmsg->type = htons(LB_MSG_TYPE_UPDATE);
lbmsg->len = htons(sizeof(*lbmsg));
lbmsg->redirect_address = params->lb_redirect_address.s_addr;
lbmsg->max_connection = htons(lb_max_connections);
lbmsg->cur_connection = htons(lb_cur_connections);
a = ntohl(params->lb_redirect_address.s_addr);
if (sendto(lb_sockfd, data, sizeof(*lbmsg), 0, (struct sockaddr*)&lb_master_address, sizeof(lb_master_address)) < 0) {
vpnlog(LOG_ERR, "Load balancing: failed to send update (%s)", strerror(errno));
}
if (lb_is_master) {
TAILQ_FOREACH(slave, &lb_slaves_list, next) {
if (--slave->age == 0) {
a = ntohl(slave->server_address.sin_addr.s_addr);
vpnlog(LOG_NOTICE, "Load Balancing: Slave server with IP address %d.%d.%d.%d disappeared\n", a >> 24 & 0xFF, a >> 16 & 0xFF, a >> 8 & 0xFF, a & 0xFF);
TAILQ_REMOVE(&lb_slaves_list, slave, next);
if (slave == lb_next_slave) {
lb_next_slave = 0;
determine_next_slave(params);
}
free(slave);
}
}
}
lb_ipconfig_time--;
if (lb_ipconfig_time <= 0) {
configure_failover(params->lb_interface, ¶ms->lb_cluster_address, LB_IPCONFIG_TIMEOUT);
lb_ipconfig_time = LB_IPCONFIG_TIMEOUT - 10;
}
}
getabsolutetime(&timeend);
timeend.tv_sec += 1;
}
else {
if (errno != EINTR) {
vpnlog(LOG_ERR, "Unexpected result from select - err = %s\n", strerror(errno));
goto fail;
}
}
if (got_sig_chld())
reap_children();
if (got_sig_usr1())
toggle_debug();
if (got_sig_hup()) {
if (lb_is_started)
stop_load_balancing(params, &fds_save);
update_prefs();
if (params->lb_enable) {
if (start_load_balancing(params, &fds_save, &fdmax) < 0)
goto fail;
}
}
}
fail:
if (lb_is_started)
stop_load_balancing(params, &fds_save);
if (the_vpn_channel.close)
the_vpn_channel.close();
terminate_children();
}
static pid_t fork_child(int fdSocket)
{
register pid_t pidChild = 0 ;
int i;
errno = 0 ;
switch (pidChild = fork ()) {
case 0: break ;
case -1: syslog(LOG_ERR, "fork() failed: %m") ;
default: close (fdSocket) ;
return pidChild ;
}
for (i = getdtablesize() - 1; i >= 0; i--)
if (i != fdSocket)
close(i);
dup2 (fdSocket, STDIN_FILENO) ;
open("/dev/null", O_RDWR, 0);
open("/dev/null", O_RDWR, 0);
close (fdSocket) ;
return pidChild ;
}
static int reap_children(void)
{
int pid, status;
struct vpn_address *address_slot;
if (!TAILQ_FIRST(&child_list))
return 0;
while ((pid = waitpid(-1, &status, WNOHANG)) != -1 && pid != 0) {
TAILQ_FOREACH(address_slot, &child_list, next) {
if (address_slot->pid == pid) {
vpnlog(LOG_NOTICE, " --> Client with address = %s has hungup\n", address_slot->ip_address);
TAILQ_REMOVE(&child_list, address_slot, next);
lb_cur_connections--;
if (address_slot->flags & VPN_ADDR_DELETE) { free(address_slot);
lb_max_connections--;
}
else {
TAILQ_INSERT_TAIL(&free_address_list, address_slot, next);
}
if (WIFSIGNALED(status))
vpnlog(LOG_WARNING, "Child process (pid %d) terminated with signal %d", pid, WTERMSIG(status));
break;
}
}
}
if (pid == -1)
if (errno != EINTR) {
return -1;
}
return 0;
}
static int terminate_children(void)
{
struct vpn_address *child;
while (child = (struct vpn_address*)TAILQ_FIRST(&child_list)) {
while (kill(child->pid, SIGTERM) < 0)
if (errno != EINTR) {
vpnlog(LOG_ERR, "Error terminating child - err = %s\n", strerror(errno));
break;
}
TAILQ_REMOVE(&child_list, child, next);
lb_cur_connections--;
TAILQ_INSERT_TAIL(&free_address_list, child, next);
}
return 0;
}
int getabsolutetime(struct timeval *timenow)
{
double now;
if (timeScaleSeconds == 0) {
mach_timebase_info_data_t timebaseInfo;
if (mach_timebase_info(&timebaseInfo) == KERN_SUCCESS) { timeScaleMicroSeconds = ((double) timebaseInfo.numer / (double) timebaseInfo.denom) / 1000;
timeScaleSeconds = timeScaleMicroSeconds / 1000000;
}
else
return -1;
}
now = mach_absolute_time();
timenow->tv_sec = now * timeScaleSeconds;
timenow->tv_usec = (now * timeScaleMicroSeconds) - ((double)timenow->tv_sec * 1000000);
return 0;
}