agentSaveState.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1999-2001 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Functions used to save and read the binding table for persistence across
* mipagent reboots.
* Saving should be invoked by sending mipagent the proper signal.
* After the signal is trapped, the functions here are invoked and save
* the state into the binary file:
* "/var/inet/mipagent_state"
* The file
* "/var/inet/mipagent_state.lock"
* is used as a mutex.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <thread.h>
#include <unistd.h>
#include <fcntl.h>
#include "impl.h"
#include "agent.h"
#include "setup.h"
#include "hash.h"
#include "conflib.h"
#define MAXLINELENGTH 200
/* file to save all mobility bindings to capture state across reboots */
#define SAVE_BINDINGS_FILE "/var/inet/mipagent_state"
#ifdef LOCKFILE
#define SAVE_BINDINGS_LOCK_FILE "/var/inet/mipagent_state.lock"
#endif /* LOCKFILE */
#define ENVVAR "MIPAGENT_SAVE_STATE"
/* Controls verbosity of debug messages when compiled w/ "-D MIP_DEBUG" */
extern int logVerbosity;
/*
* Counters maintained by Home Agents
*/
extern HomeAgentCounters haCounters;
/* Home Agent specific data structures. */
extern HashTable haMobileNodeHash;
extern HashTable mipSecAssocHash;
extern HashTable mipAgentHash;
extern int installIPsecPolicy(char *);
#ifdef LOCKFILE
static int lockFile; /* lock file file descriptor */
#endif /* LOCKFILE */
static int postFileAccess(void);
static void removeStateFile(void);
extern char *validIPsecAction[];
/*
* Function: saveAgentState
*
* Arguments: None.
*
* Description: Save mobile node binding entries. ret 1 if ok, 0 otherwise.
*
* Note: This routine must be called only when everything
* is quiescent. Otherwise inconsistent state may result
* if new security associations, mobile node entries
* or binding entries are being created as the state
* is being saved.
*
* Returns: 1 if ok, 0 otherwise.
*/
int
saveAgentState(void)
{
int EntriesSaved, numBindings;
int i;
/* Prepare for security association file access. */
mipverbose(("Could not prepare state file for writing.\n"));
return (0);
}
/*
* Only save the *dynamic* security associations.
* First: count them, and write that number.
* Then: do the actual saves.
*/
EntriesSaved = 0;
for (i = 0; i < HASH_TBL_SIZE; i++) {
if (mipSecAssocHash.buckets[i]) {
continue;
if (! sae->mipSecIsEntryDynamic)
continue;
++EntriesSaved;
}
}
}
/*
* First item to write out: the number of dynamic security
* associations which follow.
*/
mipverbose(("Problem saving the number of dynamic of"
"security associations.\n"));
(void) postFileAccess();
return (0);
}
/*
* Now: write out the actual dynamic security
* associations.
*/
for (i = 0; (EntriesSaved > 0) && (i < HASH_TBL_SIZE); i++) {
if (mipSecAssocHash.buckets[i]) {
continue;
/*
* If the entry is static there is
* no need to save it, as it will
* get restored out of the configuration
* file.
*/
if (! sae->mipSecIsEntryDynamic)
continue;
/*
* Now it's safe to write out the sec
* association entry.
*/
if (saveOneEntry((void *) sae,
sizeof (MipSecAssocEntry)) != 1) {
mipverbose(("Problem saving a mobile "
"node entry.\n"));
(void) postFileAccess();
return (0);
}
--EntriesSaved;
}
}
}
/*
* Now we'll save the agent-peer entries.
* First: count them, and write that number.
* Then: do the actual saves.
*/
EntriesSaved = 0;
for (i = 0; i < HASH_TBL_SIZE; i++) {
if (mipAgentHash.buckets[i]) {
continue;
++EntriesSaved;
}
}
}
/*
* Now write out the number of agent-peer entries which follow
*/
mipverbose(("Problem saving the number of "
"agent-peer entries.\n"));
(void) postFileAccess();
return (0);
}
/*
* Now: write out the actual agent-peer entries.
*/
for (i = 0; (EntriesSaved > 0) && (i < HASH_TBL_SIZE); i++) {
if (mipAgentHash.buckets[i]) {
continue;
/*
* Write out the agent-peer entry
*/
if (saveOneEntry((void *) mae,
sizeof (MobilityAgentEntry)) != 1) {
mipverbose(("Problem saving an agent-"
"peer entry.\n"));
(void) postFileAccess();
return (0);
}
--EntriesSaved;
}
}
}
/*
* Now save each mobile node entry.
*/
for (i = 0; i < HASH_TBL_SIZE; i++) {
if (haMobileNodeHash.buckets[i]) {
/*
* This should have been != NULL, instead
* of == NULL. The issue is that we would
* never properly reload existing registrations
* if the agent was re-started.
*/
/*
* Instead of trusting it to really
* know how many bindings it has, i'll
* go ahead and count them just to really
* make sure. Otherwise we could end up with
* null pointers, core dumps, etc... it's
* one more traversal, but most of the time
* there's zero or one binding, and the max
* is small anyway... Little extra price to pay
* to be able to sleep at night...
* This way, when we read the mn entry we
* know we can trust the number of bindings to
* know how many binding entries to read after
* that.
*/
for (numBindings = 0,
(numBindings <
/*
* finished counting the number of
* bindings...
*/
mipverbose(("Miscounted number of "
"bindings! (fixing...)\n"));
}
if (numBindings != 0) {
/*
* Now it's safe to write out the
* mobile node entry.
*/
if (saveOneEntry((void *) hamne,
sizeof (HaMobileNodeEntry)) != 1) {
mipverbose(("Problem saving "
"a mobile node entry.\n"));
(void) postFileAccess();
return (0);
}
#ifdef RADIUS_ENABLED
/*
* Save any haRadiusState as
* len:radiusState
*/
if (MobileNodeEntry->haRadiusState !=
NULL) {
sizeof (size_t), 1,
agentStateFile)) {
"Problem saving "
"length %d of "
"radius state"
"%s.\n", len,
hamne->
(void) postFileAccess();
return (0);
}
sizeof (char),
len, agentStateFile)) {
"Problem saving "
"radius state %s."
"\n",
hamne->
(void) postFileAccess();
return (0);
}
}
#endif /* RADIUS_ENABLED */
/*
* 2nd traversal of the bindings chain:
* save each binding
*/
if (saveOneEntry((void *) habe,
sizeof (HaBindingEntry))
!= 1) {
mipverbose(("Problem "
"saving a binding"
"entry.\n"));
(void) postFileAccess();
return (0);
}
}
}
}
}
}
if (postFileAccess() == -1)
return (0);
return (1);
} /* saveAgentState(void) */
/* returns 1 if ok, 0 otherwise. */
static int
{
int EntriesSaved = 0;
int i;
mipverbose(("Could not prepare state file for reading.\n"));
return (0);
}
/*
* First: how many dynamic security assoc's are there?
*/
!= 1) {
mipverbose(("Problem reading the number of dynamic sa's.\n"));
(void) postFileAccess();
return (0);
}
for (i = 0; i < EntriesSaved; i++) {
if ((int)readOneEntry((void *) &sa,
sizeof (MipSecAssocEntry)) != 1) {
mipverbose(("Problem reading a security assoc.\n"));
(void) postFileAccess();
return (0);
}
/*
* Create the relevant security association
* (locked upon return).
* NOTE: Must rewrite the key lifetime as
* the create routing clobbers the previous
* value.
*/
mipverbose(("Unable to create dynamic SA Entry\n"));
(void) postFileAccess();
return (0);
}
mipverbose(("Created a dynamic Mobile Node Entry\n"));
/*
* The Create function ends up locking the sa, so
* we need to free it.
*/
}
/*
* the next entry should indicate the number of agent-peer entries.
*/
!= 1) {
mipverbose(("Problem reading the number of agent-peers.\n"));
(void) postFileAccess();
return (0);
}
/* now restore that many agent-peer entries */
for (i = 0; i < EntriesSaved; i++) {
if ((int)readOneEntry((void *) &ma,
sizeof (MobilityAgentEntry)) != 1) {
mipverbose(("Problem reading an agent-peer entry.\n"));
(void) postFileAccess();
return (0);
}
/*
* Agent entries are read from the config file BEFORE this
* function is called. Therefore, any we find here that
* aren't in the config file must have been deleted, and
* hence we don't have a SA with them any longer. Conversely
* if there are new agent's configured, any SA that's
* configued with them is obviously not currently active.
*/
/*
* This is the former case - we saved this agent,
* but it hasn't been read from the config file,
* and so was delted. We no longer have to worry
* about this agent-peer, but let the user know
* in case the deletion was accidental.
*/
char peerAddr[IPv4_ADDR_LEN];
mipverbose(("agent-peer entry for %s "
"no longer configured\n", peerAddr));
continue;
}
/* Restore the active IPsec Policies */
/*
* The Create function ends up locking the map, so
* we need to free it.
*/
}
/*
* Read the mobile node entries till eof.
*/
/* CONSTCOND */
while (_B_TRUE) {
if ((int)readOneEntry((void *) &hamne,
sizeof (HaMobileNodeEntry)) != 1) {
if (ferror(agentStateFile)) {
"Problem reading a mobile node entry.\n"));
(void) postFileAccess();
return (0);
} else if (feof(agentStateFile)) {
mipverbose(("Done restoring agent state.\n"));
}
}
/*
* if its a dynamic type, we must explicitly create it here
* Otherwise, it was created upon initialization.
* Note: creation produces a *locked* node.
*/
if (hamne.haMnIsEntryDynamic) {
/* First, create the mobile node. */
NULL,
"Unable to create dynamic MN Entry\n"));
(void) postFileAccess();
return (0);
}
mipverbose(("Created a dynamic Mobile Node Entry\n"));
} else {
/*
* Search for the MobileNodeEntry based on the
* NAI.
*/
NULL, 0, 0, 0);
mipverbose(("Unable to find a "
"static MN Entry\n"));
(void) postFileAccess();
return (0);
}
}
mipverbose(("Found a static Mobile Node Entry\n"));
}
/*
* Copy all the fields to the (locked) mobile node entry.
* Must make sure the lock info does not get clobbered.
* Also, make sure we explicitly clobber the bindingEntries
* pointer, as it has no significance until we restore those
* entries one by one.
* Must also clobber the haMnBindingCnt and let restoreHABE
* increment that upon successful restoration of each binding
* entry.
*/
sizeof (HaMobileNodeEntry));
mnEntry->haMnBindingCnt = 0;
#if 0
/* This is not persistent friendly yet. */
/* read the radiusState string if needed. */
agentStateFile)) {
"Problem reading radius state length.\n"));
(void) postFileAccess();
return (0);
}
/* allocate some space for the radius state */
sizeof (char), len, agentStateFile)) {
"Problem reading radius state.\n"));
(void) postFileAccess();
return (0);
}
}
#endif
/*
* Read all binding entries for this
* mn entry.
*/
for (i = 0; i < (int)hamne.haMnBindingCnt; i++) {
if ((int)readOneEntry((void *) &habe,
sizeof (HaBindingEntry)) != 1) {
mipverbose(("Problem reading a "
"binding entry.\n"));
(void) postFileAccess();
return (0);
}
/*
* Figure out when the entry should expire.
*/
/* create the relevant binding entry */
&existing, &sessionLifetime);
}
}
/*
* Done with this mn entry, but before
* moving on to the next, let's unlock it.
*/
}
return (0);
} /* restoreAllMobilityBindings(void) */
int
restoreAgentState(void)
{
(void) restoreAllMobilityBindings();
return (0);
}
/* ------------------------------------------------------------ */
/* FILE FUNCTIONS */
/* ------------------------------------------------------------ */
/*
* Prepares the file to write out the state.
*/
#ifdef LOCKFILE
#endif /* LOCKFILE */
/* returns -1 if error, 0 if ok */
static int
{
char *envres;
/* find out which files we should be using */
#ifdef LOCKFILE
#endif /* LOCKFILE */
} else {
#ifdef LOCKFILE
#endif /* LOCKFILE */
}
#ifdef LOCKFILE
/* open the lock file */
mipverbose(("preFileAccess: cannot open lock file\n"));
return (-1);
}
/* lock it; block if already locked */
mipverbose(("preFileAccess: cannot lock %s\n",
lock_file));
return (-1);
}
#endif /* LOCKFILE */
/*
* Must first set umask so as not to allow anybody else
* to read these files. They may have keyeing material.
*/
== NULL) {
mipverbose(("preFileAccess: can't open %s for %s\n",
#ifdef LOCKFILE
mipverbose(("preFileAccess: can't unlock %s\n",
lock_file));
}
#endif /* LOCKFILE */
return (-1);
}
return (0);
} /* preFileAccess() */
/*
* Called after file access.
* Returns 0 if ok, -1 if error.
*/
static int
postFileAccess(void)
{
int ret = 0;
(void) fclose(agentStateFile);
#ifdef LOCKFILE
/* unlock the lock file */
mipverbose(("postFileAccess can't unlock %s\n",
lock_file));
ret = -1;
}
#endif /* LOCKFILE */
return (ret);
} /* postFileAccess() */
/*
* Delete the state and lock file after restoring.
*/
static void
removeStateFile(void)
{
#ifdef LOCKFILE
#endif /* LOCKFILE */
} /* removeStateFile() */
/* returns 0 if error, otherwise 1 (nitems) */
static size_t
{
if (nitems != 1) {
if (feof(agentStateFile)) {
mipverbose(("saveOneEntry: eof! \n"));
(void) postFileAccess();
return (0);
}
if (ferror(agentStateFile)) {
mipverbose(("saveOneEntry: error! \n"));
(void) postFileAccess();
return (0);
}
}
return (nitems);
}
/* returns 0 if error, otherwise 1 (nitems) */
static size_t
{
if (nitems != 1) {
if (feof(agentStateFile)) {
mipverbose(("readOneEntry: eof! \n"));
(void) postFileAccess();
return (0);
}
if (ferror(agentStateFile)) {
mipverbose(("readOneEntry: error! \n"));
(void) postFileAccess();
return (0);
}
}
return (nitems);
}
/*
* Function: restoreIPsecPolicies()
*
* Arguments: new - Pointer to the new MobilityAgentEntry info.
* This is what we've just read from the config file.
* saved - Pointer to what we read as our exit config.
* This is how we were configured, and what IPsec
* policies were in place when we were told to shutdown.
*
* Description: Compares what was parsed from the config file with how we were
* configured when we exited. Config file settings, and hence
* IPsec policies can change between executions. We want to
* restore those that were installed with the new settings,
* informing the user when policies have changed, and which have
* been restored - especially when those that were in place have
* been removed.
*
* Returns: 1 if OK, 0 if not (like the others called from
* restoreAllMobilityBindings()).
*/
int
{
/*
* Check the new policies, and restore any that were installed. We're
* really just doing this as a convenience for the user. Those that
* were installed because of bound mobile nodes will again be installed
* even if the policies are changed (we do NOT second-guess that sort
* of thing). However, since this sort of thing can be confusing
* when it fails, we'll log the new policies so the user has a place
* to start if there are problems.
*/
int action;
char peerAddr[IPv4_ADDR_LEN];
/*
* maPeerFlags will be restored when we restore the MN entries.
*
* We don't copy maIPsecFlags because the tunnel flags will be restored
* when the MN entries are restored, and the registration flags will
* be set as we [re]install each one.
*
* The maIPsecSAFlags[] aren't overwritten since they've been parsed
* based on the potentially new settings in conf-land!
*
* For readability, we first run through the policies, and see what's
* changed, then restore whatever was installed.
*/
for (action = FIRST_IPSEC_ACTION;
action++) {
/* check all for IPSEC_APPLY, then IPSEC_PERMIT */
sizeof (ipsec_req_t)) != 0) {
/* SA has changed, tell the user. */
mipverbose(("IPsecRequest %s policy for %s changed.\n",
/* was the policy active? */
/* A problem if the user *unconfigured* it! */
/* tell the user we know */
mipverbose(("new IPsecRequest %s policy"
" will be installed.\n",
}
}
sizeof (ipsec_req_t)) != 0) {
/* SA has changed, tell the user. */
mipverbose(("IPsecReply %s policy for %s changed.\n",
/* was the policy active? */
/* A problem if the user *unconfigured* it! */
/* tell the user we know */
mipverbose(("new IPsecReply %s policy "
"will be installed.\n",
}
}
sizeof (ipsec_req_t)) != 0) {
/* SA has changed, tell the user. */
mipverbose(("IPsecTunnel %s policy for %s changed.\n",
/* was the policy active? */
/* A problem if the user *unconfigured* it! */
if (!IPSEC_TUNNEL_ANY(
/* tell the user we know */
mipverbose(("new IPsecTunnel %s policy "
"will be installed.\n",
}
}
}
sizeof (ipsec_req_t)) != 0) {
/* SA has changed, tell the user. */
"IPsecReverseTunnel %s policy for %s changed.\n",
/* was the policy active? */
/* A problem if the user *unconfigured* it! */
if (!IPSEC_REVERSE_TUNNEL_ANY(
/* tell the user we know. */
"new IPsecReverseTunnel %s policy "
"will be installed.\n",
}
}
}
/*
* Restore those that were installed. Warn the user if any of these
* went away!
*/
for (action = FIRST_IPSEC_ACTION;
action++) {
/* restore for IPSEC_APPLY, then IPSEC_PERMIT */
/*
* note: there is a potential problem with restoring REQUESTs.
* Before we restore, we parse the config file, which is when
* IPSEC_REQUEST_PERMIT policies need to be installed. That
* means if this is what we're restoring, it's been done (and
* it's the new = correct policy).
*/
if ((action != IPSEC_PERMIT) &&
/* restore, IFF still configured */
if (installIPsecPolicy(
/* we wont be able to communicate */
"Can't restore %s's ipsec %s"
"registration request policy.\n",
peerAddr));
return (0);
}
/* set the installed flag */
} else {
/* one WAS installed, but isn't configured */
mipverbose(("WARNING: IPsecRequest %s policy "
"for %s was installed but is no longer "
"configured! Registration request will be "
peerAddr));
return (0);
}
}
/* restore, IFF still configured */
if (installIPsecPolicy(
/* we wont be able to communicate */
"Can't restore %s's ipsec %s"
"registration reply policy.\n",
peerAddr));
return (0);
}
/* set the installed flag */
} else {
/* one WAS installed, but isn't configured */
mipverbose(("WARNING: IPsecReply %s policy "
"for %s was installed but is no longer "
"configured! Registration reply will be "
peerAddr));
return (0);
}
}
/*
* Note:
* tunnel entries are added when we restore the MNs still
* registered with us. An existing MN causes addHABE() to be
* called, which calls encapadd() where the correct ipsec_req_t
* is passed to ioctl() via settaddr() - exactly as if the MN
* registered. At this time, warn the user if any policies
* went away, yet we're likely to restore the policy with a MN!
*/
/* tell the user the config's gone */
"WARNING: IPsecTunnel %s policy for "
" %s was installed, but is no longer "
"configured. Restoring with no policy!",
}
if (!IPSEC_REVERSE_TUNNEL_ANY(
/* tell the user the config's gone */
"WARNING: IPsecReverseTunnel %s policy for "
" %s was installed, but is no longer "
"configured. Restoring with no policy!",
}
}
return (1);
}