+/* $NetBSD: session.c,v 1.7.6.2 2007/08/01 11:52:22 vanhu Exp $ */
+
/* $KAME: session.c,v 1.32 2003/09/24 02:01:17 jinmei Exp $ */
/*
#include <sys/stat.h>
#include <paths.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include <resolv.h>
+#include <TargetConditionals.h>
+#include <vproc_priv.h>
+
#include "libpfkey.h"
#include "var.h"
#include "evt.h"
#include "cfparse_proto.h"
#include "isakmp_var.h"
+#include "isakmp_xauth.h"
+#include "isakmp_cfg.h"
#include "admin_var.h"
#include "admin.h"
#include "privsep.h"
#endif
#include "vpn_control_var.h"
#include "policy.h"
+#include "algorithm.h" /* XXX ??? */
+
+#include "sainfo.h"
+#include "power_mgmt.h"
+
+
extern pid_t racoon_pid;
+extern char logFileStr[];
+extern int launchedbylaunchd(void);
static void close_session __P((void));
static void check_rtsock __P((void *));
static void initfds __P((void));
static void init_signal __P((void));
-static int set_signal __P((int sig, RETSIGTYPE (*func) __P((int))));
+static int set_signal __P((int sig, RETSIGTYPE (*func) __P((int, siginfo_t *, void *))));
static void check_sigreq __P((void));
static void check_flushsa_stub __P((void *));
static void check_flushsa __P((void));
static volatile sig_atomic_t sigreq[NSIG + 1];
static int dying = 0;
static struct sched *check_rtsock_sched = NULL;
+int terminated = 0;
+
+#define HANDLE_TENTATIVE_INTF_FAILURES() do { \
+ if (tentative_failures) { \
+ plog(LLV_ERROR, LOCATION, NULL, \
+ "detected tentative interface/address issues: will retry later.\n"); \
+ if (check_rtsock_sched == NULL) { \
+ /* only schedule if not already done */ \
+ check_rtsock_sched = sched_new(5, check_rtsock, NULL); \
+ } \
+ } \
+ } while(0)
+
+static void
+reinit_socks (void)
+{
+ int tentative_failures;
+
+ isakmp_close();
+ close(lcconf->rtsock);
+ initmyaddr();
+ if (isakmp_open(&tentative_failures) < 0) {
+ plog(LLV_ERROR2, LOCATION, NULL,
+ "failed to reopen isakmp sockets\n");
+ }
+ initfds();
+ HANDLE_TENTATIVE_INTF_FAILURES();
+}
+
+static int64_t racoon_keepalive = -1;
+
+/*
+ * This is used to (manually) update racoon's launchd keepalive, which is needed because racoon is (mostly)
+ * launched on demand and for <rdar://problem/8768510> requires a keepalive on dirty/failure exits.
+ * The launchd plist can't be used for this because RunOnLoad is required to have keepalive on a failure exit.
+ */
+int64_t
+launchd_update_racoon_keepalive (Boolean enabled)
+{
+ if (launchedbylaunchd()) {
+ vproc_t vp = vprocmgr_lookup_vproc("com.apple.racoon");
+ if (vp) {
+ int64_t val = (__typeof__(val))enabled;
+ if (vproc_swap_integer(vp,
+ VPROC_GSK_BASIC_KEEPALIVE,
+ &val,
+ &racoon_keepalive)) {
+ plog(LLV_ERROR2, LOCATION, NULL,
+ "failed to swap launchd keepalive integer %d\n", enabled);
+ }
+ vproc_release(vp);
+ }
+ }
+ return racoon_keepalive;
+}
int
session(void)
char pid_file[MAXPATHLEN];
FILE *fp;
int i, update_fds;
+ int tentative_failures;
/* initialize schedular */
sched_init();
+ /* needs to be called after schedular */
+ if (init_power_mgmt() < 0) {
+ errx(1, "failed to initialize power-mgmt.");
+ }
+
initmyaddr();
- if (isakmp_init() < 0) {
+ if (isakmp_init(false, &tentative_failures) < 0) {
plog(LLV_ERROR2, LOCATION, NULL,
"failed to initialize isakmp");
exit(1);
}
-
+ HANDLE_TENTATIVE_INTF_FAILURES();
+
#ifdef ENABLE_ADMINPORT
if (admin_init() < 0) {
plog(LLV_ERROR2, LOCATION, NULL,
"failed to initialize vpn control port");
exit(1);
}
+
#endif
init_signal();
initfds();
-#ifndef __APPLE__
-#ifdef ENABLE_NATT
- natt_keepalive_init ();
-#endif
-#endif
-
+#ifdef HAVE_OPENSSL
if (privsep_init() != 0) {
plog(LLV_ERROR2, LOCATION, NULL,
"failed to initialize privsep");
exit(1);
}
-
+#endif
+
for (i = 0; i <= NSIG; i++)
sigreq[i] = 0;
if (!f_foreground) {
racoon_pid = getpid();
if (lcconf->pathinfo[LC_PATHTYPE_PIDFILE] == NULL)
- strlcpy(pid_file, _PATH_VARRUN "racoon.pid", MAXPATHLEN);
+ strlcpy(pid_file, _PATH_VARRUN "racoon.pid", sizeof(pid_file));
else if (lcconf->pathinfo[LC_PATHTYPE_PIDFILE][0] == '/')
- strlcpy(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], MAXPATHLEN);
+ strlcpy(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], sizeof(pid_file));
else {
- strlcat(pid_file, _PATH_VARRUN, MAXPATHLEN);
- strlcat(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], MAXPATHLEN);
+ strlcat(pid_file, _PATH_VARRUN, sizeof(pid_file));
+ strlcat(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], sizeof(pid_file));
}
fp = fopen(pid_file, "w");
if (fp) {
}
}
+#if !TARGET_OS_EMBEDDED
+ // enable keepalive for recovery (from crashes and bad exits... after init)
+ (void)launchd_update_racoon_keepalive(true);
+#endif // !TARGET_OS_EMBEDDED
+
while (1) {
+ if (!TAILQ_EMPTY(&lcconf->saved_msg_queue))
+ pfkey_post_handler();
update_fds = 0;
/*
* asynchronous requests via signal.
*/
check_sigreq();
+ check_power_mgmt();
+
/* scheduling */
timeout = schedular();
+ // <rdar://problem/7650111> Workaround: make sure timeout is playing nice
+ if (timeout) {
+ if (timeout->tv_usec < 0 || timeout->tv_usec > SELECT_USEC_MAX ) {
+ timeout->tv_sec += ((__typeof__(timeout->tv_sec))timeout->tv_usec)/SELECT_USEC_MAX;
+ timeout->tv_usec %= SELECT_USEC_MAX;
+ }
+ if (timeout->tv_sec > SELECT_SEC_MAX /* tv_sec is unsigned */) {
+ timeout->tv_sec = SELECT_SEC_MAX;
+ }
+ if (!timeout->tv_sec && !timeout->tv_usec) {
+ timeout->tv_sec = 1;
+ }
+ }
if (dying)
rfds = maskdying;
continue;
default:
plog(LLV_ERROR2, LOCATION, NULL,
- "failed select (%s)\n",
- strerror(errno));
- exit(1);
+ "failed select (%s) nfds %d\n",
+ strerror(errno), nfds);
+ reinit_socks();
+ update_fds = 0;
+ continue;
}
/*NOTREACHED*/
}
update_fds = 1; // socket closed by peer - update mask
}
}
-#endif
+#endif
for (p = lcconf->myaddrs; p; p = p->next) {
if (!p->addr)
continue;
- if (FD_ISSET(p->sock, &rfds))
+ if ((p->sock != -1) &&
+ (FD_ISSET(p->sock, &rfds)))
if ((error = isakmp_handler(p->sock)) == -2)
break;
}
if (error == -2) {
- if (lcconf->autograbaddr) {
- /* serious socket problem - close all listening sockets and re-open */
- isakmp_close();
- initfds();
- sched_new(5, check_rtsock, NULL);
- continue;
- } else {
- isakmp_close_sockets();
- isakmp_open();
- initfds();
- continue;
- }
+ plog(LLV_ERROR2, LOCATION, NULL,
+ "failed to process isakmp port\n");
+ reinit_socks();
+ update_fds = 0;
+ continue;
}
if (FD_ISSET(lcconf->sock_pfkey, &rfds))
if (lcconf->rtsock >= 0 && FD_ISSET(lcconf->rtsock, &rfds)) {
if (update_myaddrs() && lcconf->autograbaddr)
if (check_rtsock_sched == NULL) /* only schedule if not already done */
- check_rtsock_sched = sched_new(5, check_rtsock, NULL);
+ check_rtsock_sched = sched_new(1, check_rtsock, NULL);
+ else {
+ // force reinit if schedule is too far off (3 seconds or more)
+ time_t too_far = current_time() + 3;
+ if (check_rtsock_sched->dead ||
+ check_rtsock_sched->xtime >= too_far) {
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "forced reinit of addrs\n");
+ update_fds = 0;
+ check_rtsock(NULL);
+ }
+ }
// initfds(); //%%% BUG FIX - not needed here
}
if (update_fds) {
static void
close_session()
{
- flushph1();
+ if ( terminated )
+ flushph2(false);
+ flushph1(false);
close_sockets();
backupsa_clean();
+#if !TARGET_OS_EMBEDDED
+ // a clean exit, so disable launchd keepalive
+ (void)launchd_update_racoon_keepalive(false);
+#endif // !TARGET_OS_EMBEDDED
+
plog(LLV_INFO, LOCATION, NULL, "racoon shutdown\n");
exit(0);
}
check_rtsock(p)
void *p;
{
+ int tentative_failures;
check_rtsock_sched = NULL;
grab_myaddrs();
isakmp_close_unused();
autoconf_myaddrsport();
- isakmp_open();
+ isakmp_open(&tentative_failures);
/* initialize socket list again */
initfds();
+ HANDLE_TENTATIVE_INTF_FAILURES();
}
static void
}
}
}
-
#endif
if (lcconf->sock_pfkey >= FD_SETSIZE) {
FD_SET(lcconf->rtsock, &mask0);
nfds = (nfds > lcconf->rtsock ? nfds : lcconf->rtsock);
}
-
+
for (p = lcconf->myaddrs; p; p = p->next) {
if (!p->addr)
continue;
nfds++;
}
+
static int signals[] = {
SIGHUP,
SIGINT,
* main loop in session().
*/
RETSIGTYPE
-signal_handler(sig)
+signal_handler(sig, sigi, ctx)
int sig;
+ siginfo_t *sigi;
+ void *ctx;
{
+#if 0
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "%s received signal %d from pid %d uid %d\n\n",
+ __FUNCTION__, sig, sigi->si_pid, sigi->si_uid);
+#endif
+
/* Do not just set it to 1, because we may miss some signals by just setting
* values to 0/1
*/
sigreq[sig]++;
+ if ( sig == SIGTERM ){
+ terminated = 1;
+ }
}
static void
check_sigreq()
{
int sig;
+ int tentative_failures;
/*
* XXX We are not able to tell if we got
break;
#endif
+ case SIGUSR1:
case SIGHUP:
+#ifdef ENABLE_HYBRID
+ if ((isakmp_cfg_init(ISAKMP_CFG_INIT_WARM)) != 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "ISAKMP mode config structure reset failed, "
+ "not reloading\n");
+ return;
+ }
+#endif
+ if ( terminated )
+ break;
+
+ /*
+ * if we got a HUP... try graceful teardown of sessions before we close and reopen sockets...
+ * so that info-deletes notifications can make it to the peer.
+ */
+ if (sig == SIGHUP) {
+ flushph2(true);
+ flushph1(true);
+ }
/* Save old configuration, load new one... */
isakmp_close();
close(lcconf->rtsock);
- if (cfreparse()) {
+ if (cfreparse(sig)) {
plog(LLV_ERROR2, LOCATION, NULL,
"configuration read failed\n");
exit(1);
}
- if (lcconf->logfile_param == NULL)
+ if (lcconf->logfile_param == NULL && logFileStr[0] == 0)
plogreset(lcconf->pathinfo[LC_PATHTYPE_LOGFILE]);
initmyaddr();
isakmp_cleanup();
- isakmp_init();
+ isakmp_init(true, &tentative_failures);
+ HANDLE_TENTATIVE_INTF_FAILURES();
initfds();
+#if TARGET_OS_EMBEDDED
+ if (no_remote_configs(TRUE)) {
+ EVT_PUSH(NULL, NULL, EVTT_RACOON_QUIT, NULL);
+ pfkey_send_flush(lcconf->sock_pfkey, SADB_SATYPE_UNSPEC);
+#ifdef ENABLE_FASTQUIT
+ close_session();
+#else
+ sched_new(1, check_flushsa_stub, NULL);
+#endif
+ dying = 1;
+ }
+#endif
break;
case SIGINT:
EVT_PUSH(NULL, NULL, EVTT_RACOON_QUIT, NULL);
pfkey_send_flush(lcconf->sock_pfkey,
SADB_SATYPE_UNSPEC);
- sched_new(1, check_flushsa_stub, NULL);
- dying = 1;
+ if ( sig == SIGTERM ){
+ terminated = 1; /* in case if it hasn't been set yet */
+ close_session();
+ }
+ else
+ sched_new(1, check_flushsa_stub, NULL);
+
+ dying = 1;
break;
default:
}
close_session();
+#if !TARGET_OS_EMBEDDED
+ if (lcconf->vt)
+ vproc_transaction_end(NULL, lcconf->vt);
+#endif
}
void
auto_exit_do(void *p)
{
EVT_PUSH(NULL, NULL, EVTT_RACOON_QUIT, NULL);
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "performing auto exit\n");
pfkey_send_flush(lcconf->sock_pfkey, SADB_SATYPE_UNSPEC);
sched_new(1, check_flushsa_stub, NULL);
dying = 1;
void
check_auto_exit(void)
{
-
if (lcconf->auto_exit_sched != NULL) { /* exit scheduled? */
if (lcconf->auto_exit_state != LC_AUTOEXITSTATE_ENABLED
|| vpn_control_connected() /* vpn control connected */
- || policies_installed()) /* policies installed in kernel */
+ || policies_installed() /* policies installed in kernel */
+ || !no_remote_configs(FALSE)) /* remote or anonymous configs */
SCHED_KILL(lcconf->auto_exit_sched);
} else { /* exit not scheduled */
if (lcconf->auto_exit_state == LC_AUTOEXITSTATE_ENABLED
- && !vpn_control_connected()
- && !policies_installed())
+ && !vpn_control_connected()
+ && !policies_installed()
+ && no_remote_configs(FALSE))
if (lcconf->auto_exit_delay == 0)
auto_exit_do(NULL); /* immediate exit */
else
static int
set_signal(sig, func)
int sig;
- RETSIGTYPE (*func) __P((int));
+ RETSIGTYPE (*func) __P((int, siginfo_t *, void *));
{
struct sigaction sa;
memset((caddr_t)&sa, 0, sizeof(sa));
sa.sa_handler = func;
- sa.sa_flags = SA_RESTART;
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigemptyset(&sa.sa_mask) < 0)
return -1;
return 0;
}
+