ipseckey.c revision 07b569258a7f225101878792e6bfeedb2b35902c
/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* NOTE:I'm trying to use "struct sadb_foo" instead of "sadb_foo_t"
* as a maximal PF_KEY portability test.
*
* Also, this is a deliberately single-threaded app, also for portability
* to systems without POSIX threads.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <syslog.h>
#include <signal.h>
#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
#include <pwd.h>
#include <errno.h>
#include <libintl.h>
#include <locale.h>
#include <fcntl.h>
#include <strings.h>
#include <ctype.h>
#include <ipsec_util.h>
static int keysock;
#define MAX_GET_SIZE 1024
/* Defined as a uint64_t array for alignment purposes. */
/* local prototypes */
static const char *do_inet_ntop(const void *, char *, size_t);
static void printsatime(int64_t, const char *, const char *, const char *);
/*
* When something syntactically bad happens while reading commands,
* print it. For command line, exit. For reading from a file, exit, and
* print the offending line number. For interactive, just print the error
* and reset the program state with the longjmp().
*/
static void
usage(void)
{
if (readfile) {
}
if (!interactive) {
"ipseckey [ -nvp ] | cmd [sa_type] [extfield value]*\n"));
gettext("\tipseckey [ -nvp ] -f infile\n"));
gettext("\tipseckey [ -nvp ] -s outfile\n"));
exit(1);
} else {
}
}
/*
* Initialize a PF_KEY base message.
*/
static void
{
msg->sadb_msg_errno = 0;
/* For starters... */
msg->sadb_msg_reserved = 0;
}
/*
* parseXXX and rparseXXX commands parse input and convert them to PF_KEY
* field values, or do the reverse for the purposes of saving the SA tables.
* (See the save_XXX functions.)
*/
#define CMD_NONE 0
#define CMD_UPDATE 2
#define CMD_ADD 3
#define CMD_DELETE 4
#define CMD_GET 5
#define CMD_FLUSH 9
#define CMD_DUMP 10
#define CMD_MONITOR 11
#define CMD_PMONITOR 12
#define CMD_QUIT 13
#define CMD_SAVE 14
#define CMD_HELP 15
/*
* Parse the command.
*/
static int
{
static struct cmdtable {
char *cmd;
int token;
} table[] = {
/*
* Q: Do we want to do GETSPI?
* A: No, it's for automated key mgmt. only. Either that,
* or it isn't relevant until we support non IPsec SA types.
*/
{"update", CMD_UPDATE},
{"add", CMD_ADD},
{"delete", CMD_DELETE},
{"get", CMD_GET},
/*
* Q: And ACQUIRE and REGISTER and EXPIRE?
* A: not until we support non IPsec SA types.
*/
{"flush", CMD_FLUSH},
{"dump", CMD_DUMP},
{"monitor", CMD_MONITOR},
{"passive_monitor", CMD_PMONITOR},
{"pmonitor", CMD_PMONITOR},
{"quit", CMD_QUIT},
{"exit", CMD_QUIT},
{"save", CMD_SAVE},
{"help", CMD_HELP},
{"?", CMD_HELP},
};
ct++;
}
/*
* Convert a number from a command line. I picked "u_longlong_t" for the
* number because we need the largest number available. Also, the strto<num>
* calls don't deal in units of uintNN_t.
*/
static u_longlong_t
{
usage();
}
errno = 0;
if (bail) {
/* Errno message printed by warn(). */
usage();
} else {
/*
* -1, while not optimal, is sufficiently out of range
* for most of this function's applications when
* we don't just bail.
*/
return ((u_longlong_t)-1);
}
}
return (rc);
}
/*
* Parse and reverse parse a specific SA type (AH, ESP, etc.).
*/
static struct typetable {
char *type;
int token;
} type_table[] = {
{"all", SADB_SATYPE_UNSPEC},
{"ah", SADB_SATYPE_AH},
{"esp", SADB_SATYPE_ESP},
{NULL, 0} /* Token value is irrelevant for this entry. */
};
static char *
rparsesatype(int type)
{
tt++;
} else {
}
return (numprint);
}
static int
parsesatype(char *type)
{
return (SADB_SATYPE_UNSPEC);
tt++;
/*
* New SA types (including ones keysock maintains for user-land
* protocols) may be added, so parse a numeric value if possible.
*/
usage();
}
}
}
#define NEXTEOF 0
#define NEXTNONE 1
#define NEXTNUM 2
#define NEXTSTR 3
#define NEXTNUMSTR 4
#define NEXTADDR 5
#define NEXTHEX 6
#define NEXTIDENT 7
#define NEXTADDR4 8
#define NEXTADDR6 9
#define TOK_EOF 0
#define TOK_UNKNOWN 1
#define TOK_SPI 2
#define TOK_REPLAY 3
#define TOK_STATE 4
#define TOK_AUTHALG 5
#define TOK_ENCRALG 6
#define TOK_FLAGS 7
#define TOK_SOFT_ALLOC 8
#define TOK_SOFT_BYTES 9
#define TOK_SOFT_ADDTIME 10
#define TOK_SOFT_USETIME 11
#define TOK_HARD_ALLOC 12
#define TOK_HARD_BYTES 13
#define TOK_HARD_ADDTIME 14
#define TOK_HARD_USETIME 15
#define TOK_CURRENT_ALLOC 16
#define TOK_CURRENT_BYTES 17
#define TOK_CURRENT_ADDTIME 18
#define TOK_CURRENT_USETIME 19
#define TOK_SRCADDR 20
#define TOK_DSTADDR 21
#define TOK_PROXYADDR 22
#define TOK_AUTHKEY 23
#define TOK_ENCRKEY 24
#define TOK_SRCIDTYPE 25
#define TOK_DSTIDTYPE 26
#define TOK_DPD 27
#define TOK_SENS_LEVEL 28
#define TOK_SENS_MAP 29
#define TOK_INTEG_LEVEL 30
#define TOK_INTEG_MAP 31
#define TOK_SRCADDR6 32
#define TOK_DSTADDR6 33
#define TOK_PROXYADDR6 34
#define TOK_SRCPORT 35
#define TOK_DSTPORT 36
#define TOK_PROTO 37
#define TOK_ENCAP 38
#define TOK_NATLOC 39
#define TOK_NATREM 40
#define TOK_NATLPORT 41
#define TOK_NATRPORT 42
static struct toktable {
char *string;
int token;
int next;
} tokens[] = {
/* "String", token value, next arg is */
};
/*
* Q: Do I need stuff for proposals, combinations, supported algorithms,
* or SPI ranges?
*
* A: Probably not, but you never know.
*
* Parse out extension header type values.
*/
static int
{
return (TOK_EOF);
break;
/*
* Since the OS controls what extensions are available, we don't have
* to parse numeric values here.
*/
}
/*
* Parse possible state values.
*/
static uint8_t
parsestate(char *state)
{
struct states {
char *state;
} states[] = {
{"larval", SADB_SASTATE_LARVAL},
{"mature", SADB_SASTATE_MATURE},
{"dying", SADB_SASTATE_DYING},
{"dead", SADB_SASTATE_DEAD},
{NULL, 0}
};
usage();
}
}
usage();
/* NOTREACHED */
return (0);
}
/*
* Return a string containing the name of the specified numerical algorithm
* identifier.
*/
static char *
{
return (numprint);
}
}
/*
* Return the numerical algorithm identifier corresponding to the specified
* algorithm name.
*/
static uint8_t
{
struct ipsecalgent *algent;
usage();
}
return (alg_num);
}
/*
* Since algorithms can be loaded during kernel run-time, check for
* numeric algorithm values too. PF_KEY can catch bad ones with EINVAL.
*/
if (proto_num == IPSEC_PROTO_ESP)
else
alg);
usage();
/* NOTREACHED */
return (0);
}
/*
* Parse and reverse parse out a source/destination ID type.
*/
static struct idtypes {
char *idtype;
} idtypes[] = {
{"prefix", SADB_IDENTTYPE_PREFIX},
{"fqdn", SADB_IDENTTYPE_FQDN},
{"domain", SADB_IDENTTYPE_FQDN},
{"domainname", SADB_IDENTTYPE_FQDN},
{"user_fqdn", SADB_IDENTTYPE_USER_FQDN},
{"mailbox", SADB_IDENTTYPE_USER_FQDN},
{"der_dn", SADB_X_IDENTTYPE_DN},
{"der_gn", SADB_X_IDENTTYPE_GN},
{NULL, 0}
};
static char *
{
}
return (numprint);
}
static uint16_t
parseidtype(char *type)
{
/* Shouldn't reach here, see callers for why. */
usage();
}
}
/*
* Since identity types are almost arbitrary, check for numeric
* algorithm values too. PF_KEY can catch bad ones with EINVAL.
*/
usage();
/* NOTREACHED */
return (0);
}
/*
* Parse an address off the command line. Return length of sockaddr,
* and either return a hostent pointer (caller frees). The new
* getipnodebyname() call does the Right Thing (TM), even with
* raw addresses (colon-separated IPv6 or dotted decimal IPv4).
*/
static struct {
char *addtl[2];
} dummy;
static union {
} addr1;
static int
{
int hp_errno;
usage();
}
if (!nflag) {
/*
* Try name->address first. Assume AF_INET6, and
* get IPv4's, plus IPv6's if and only if IPv6 is configured.
* This means to add IPv6 SAs, you must have IPv6
* up-and-running. (AI_DEFAULT works here.)
*/
&hp_errno);
} else {
/*
* Try a normal address conversion only. Use "dummy"
* to construct a fake hostent. Caller will know not
* to free this one.
*/
/*
* Remape to AF_INET6 anyway.
*/
/*
* NOTE: If macro changes to disallow in-place
* conversion, rewhack this.
*/
} else {
}
}
usage();
}
/* Always return sockaddr_storage for now. */
return (sizeof (struct sockaddr_storage));
}
/*
* Parse a hex character for a key. A string will take the form:
* where
* xxxxxxxxx == a string of hex characters ([0-9][a-f][A-F])
* nn == an optional decimal "mask". If it is not present, it
* is assumed that the hex string will be rounded to the nearest
* byte, where odd nibbles, like 123 will become 0x0123.
*
* NOTE:Unlike the expression of IP addresses, I will not allow an
* excessive "mask". For example 2112/50 is very illegal.
* NOTE2: This key should be in canonical order. Consult your man
* pages per algorithm about said order.
*/
static struct sadb_key *
{
usage();
}
hexlen++;
if (input[i] == '\0') {
bits = 0;
} else {
/* Have /nn. */
input[i] = '\0';
(input + i + 1));
usage();
}
/* hexlen in nibbles */
usage();
}
/*
* Adjust hexlen down if user gave us too small of a bit
* count.
*/
}
}
/*
* Allocate. Remember, hexlen is in nibbles.
*/
Bail("malloc(parsekey)");
retval->sadb_key_reserved = 0;
if (bits == 0)
else
/*
* Read in nibbles. Read in odd-numbered as shifted high.
* (e.g. 123 becomes 0x1230).
*/
usage();
}
if (second)
else
break; /* out of for loop. */
key++;
}
/* bzero the remaining bits if we're a non-octet amount. */
if (bits & 0x7)
return (retval);
}
/*
* Expand the diagnostic code into a message.
*/
static void
{
/* Use two spaces so above strings can fit on the line. */
}
/*
* Prints the base PF_KEY message.
*/
static void
{
if (wallclock != 0)
switch (samsg->sadb_msg_type) {
case SADB_RESERVED:
break;
case SADB_GETSPI:
(void) printf("GETSPI");
break;
case SADB_UPDATE:
(void) printf("UPDATE");
break;
case SADB_ADD:
(void) printf("ADD");
break;
case SADB_DELETE:
(void) printf("DELETE");
break;
case SADB_GET:
(void) printf("GET");
break;
case SADB_ACQUIRE:
(void) printf("ACQUIRE");
break;
case SADB_REGISTER:
(void) printf("REGISTER");
break;
case SADB_EXPIRE:
(void) printf("EXPIRE");
break;
case SADB_FLUSH:
(void) printf("FLUSH");
break;
case SADB_DUMP:
(void) printf("DUMP");
break;
case SADB_X_PROMISC:
(void) printf("X_PROMISC");
break;
case SADB_X_INVERSE_ACQUIRE:
(void) printf("X_INVERSE_ACQUIRE");
break;
default:
break;
}
switch (samsg->sadb_msg_satype) {
case SADB_SATYPE_UNSPEC:
break;
case SADB_SATYPE_AH:
(void) printf("AH");
break;
case SADB_SATYPE_ESP:
(void) printf("ESP");
break;
case SADB_SATYPE_RSVP:
(void) printf("RSVP");
break;
case SADB_SATYPE_OSPFV2:
(void) printf("OSPFv2");
break;
case SADB_SATYPE_RIPV2:
(void) printf("RIPv2");
break;
case SADB_SATYPE_MIP:
break;
default:
break;
}
(void) printf(".\n");
if (samsg->sadb_msg_errno != 0) {
}
}
/*
* Print the SA extension for PF_KEY.
*/
static void
{
}
switch (assoc->sadb_sa_state) {
case SADB_SASTATE_LARVAL:
break;
case SADB_SASTATE_MATURE:
break;
case SADB_SASTATE_DYING:
break;
case SADB_SASTATE_DEAD:
break;
default:
}
prefix);
}
}
(void) printf("PFS ");
(void) printf("NOREPLAY ");
/* BEGIN Solaris-specific flags. */
(void) printf("X_USED ");
(void) printf("X_UNIQUE ");
(void) printf("X_AALG1 ");
(void) printf("X_AALG2 ");
(void) printf("X_EALG1 ");
(void) printf("X_EALG2 ");
(void) printf("X_NATT_LOC ");
(void) printf("X_NATT_REM ");
/* END Solaris-specific flags. */
(void) printf(">\n");
}
static void
{
if (t != lt) {
if (lt > 0)
t = LONG_MAX;
else
t = LONG_MIN;
}
}
/*
* Print the SA lifetime information. (An SADB_EXT_LIFETIME_* extension.)
*/
static void
{
"(%u) is bad."),
}
"extension length (%u) is bad."),
}
"extension length (%u) is bad."),
}
(void) printf(" LT: Lifetime information\n");
/* Express values as current values. */
"%s%llu bytes protected, %u allocations used.\n"),
gettext("%sSA added at time %s\n"),
if (current->sadb_lifetime_usetime != 0) {
gettext("%sSA first used at time %s\n"),
}
}
/* If possible, express values as time remaining. */
if (soft->sadb_lifetime_bytes != 0)
"%s%llu more bytes can be protected.\n"),
current->sadb_lifetime_bytes) : (0));
if (soft->sadb_lifetime_addtime != 0 ||
(soft->sadb_lifetime_usetime != 0 &&
current->sadb_lifetime_usetime != 0)) {
if (soft->sadb_lifetime_addtime != 0) {
adddelta =
} else {
}
if (soft->sadb_lifetime_usetime != 0 &&
current->sadb_lifetime_usetime != 0) {
usedelta =
} else {
}
if (scratch >= 0) {
"occurs in %lld seconds, "),
scratch);
} else {
"Soft expiration occurred "));
}
}
}
}
/* If possible, express values as time remaining. */
if (hard->sadb_lifetime_bytes != 0)
"%s%llu more bytes can be protected.\n"),
current->sadb_lifetime_bytes) : (0));
if (hard->sadb_lifetime_addtime != 0 ||
(hard->sadb_lifetime_usetime != 0 &&
current->sadb_lifetime_usetime != 0)) {
if (hard->sadb_lifetime_addtime != 0) {
adddelta =
} else {
}
if (hard->sadb_lifetime_usetime != 0 &&
current->sadb_lifetime_usetime != 0) {
usedelta =
} else {
}
if (scratch >= 0) {
"occurs in %lld seconds, "),
scratch);
} else {
"Hard expiration occured "));
}
}
}
}
}
/*
* Print an SADB_EXT_ADDRESS_* extension.
*/
static void
{
switch (addr->sadb_address_exttype) {
case SADB_EXT_ADDRESS_SRC:
break;
case SADB_EXT_ADDRESS_DST:
break;
case SADB_EXT_ADDRESS_PROXY:
break;
break;
break;
}
if (!nflag) {
if (addr->sadb_address_proto == 0) {
!= NULL) {
} else {
}
}
}
/*
* Print an SADB_EXT_KEY extension.
*/
static void
{
switch (key->sadb_key_exttype) {
case SADB_EXT_KEY_AUTH:
break;
case SADB_EXT_KEY_ENCRYPT:
break;
}
(void) putchar('\n');
}
/*
* Print an SADB_EXT_IDENTITY_* extension.
*/
static void
{
switch (id->sadb_ident_exttype) {
case SADB_EXT_IDENTITY_SRC:
break;
case SADB_EXT_IDENTITY_DST:
break;
}
if (canprint)
else
}
/*
* Print an SADB_SENSITIVITY extension.
*/
static void
{
int i;
(void) printf(
gettext("%sSensitivity DPD %d, sens level=%d, integ level=%d\n"),
(void) printf(
gettext("%s Sensitivity BM extended word %d 0x%llx\n"),
i, *bitmap);
(void) printf(
gettext("%s Integrity BM extended word %d 0x%llx\n"),
i, *bitmap);
}
/*
* Print an SADB_EXT_PROPOSAL extension.
*/
static void
{
int i, numcombs;
for (i = 0; i < numcombs; i++) {
}
}
if (combs[i].sadb_comb_hard_allocations)
if (combs[i].sadb_comb_hard_bytes)
if (combs[i].sadb_comb_hard_addtime)
if (combs[i].sadb_comb_hard_usetime)
if (combs[i].sadb_comb_soft_allocations)
if (combs[i].sadb_comb_soft_bytes)
if (combs[i].sadb_comb_soft_addtime)
if (combs[i].sadb_comb_soft_usetime)
(void) putchar('\n');
}
}
/*
* Print an extended proposal (SADB_X_EXT_EPROP).
*/
static void
{
struct sadb_x_ecomb *ecomb;
struct sadb_x_algdesc *algdesc;
int i, j;
for (i = 0; i < eprop->sadb_x_prop_numecombs; ) {
prefix, ++i);
for (j = 0; j < ecomb->sadb_x_ecomb_numalgs; ) {
switch (algdesc->sadb_x_algdesc_satype) {
case SADB_SATYPE_ESP:
break;
case SADB_SATYPE_AH:
break;
default:
}
switch (algdesc->sadb_x_algdesc_algtype) {
case SADB_X_ALGTYPE_CRYPT:
stdout);
break;
case SADB_X_ALGTYPE_AUTH:
stdout);
break;
default:
break;
}
}
}
}
/*
* Print an SADB_EXT_SUPPORTED extension.
*/
static void
{
int i, numalgs;
switch (supp->sadb_supported_exttype) {
case SADB_EXT_SUPPORTED_AUTH:
break;
break;
}
for (i = 0; i < numalgs; i++) {
switch (supp->sadb_supported_exttype) {
case SADB_EXT_SUPPORTED_AUTH:
break;
break;
}
algs[i].sadb_alg_ivlen);
}
}
/*
* Print an SADB_EXT_SPIRANGE extension.
*/
static void
{
}
/*
* Print an SADB_X_EXT_KM_COOKIE extension.
*/
static void
{
char *cookie_label;
NULL)
}
/*
* Take a PF_KEY message pointed to buffer and print it. Useful for DUMP
* and GET.
*/
static void
{
int i;
int lenbytes;
switch (ext->sadb_ext_type) {
case SADB_EXT_SA:
break;
/*
* Pluck out lifetimes and print them at the end. This is
* to show relative lifetimes.
*/
break;
case SADB_EXT_LIFETIME_HARD:
break;
case SADB_EXT_LIFETIME_SOFT:
break;
case SADB_EXT_ADDRESS_SRC:
(struct sadb_address *)current);
break;
case SADB_EXT_ADDRESS_DST:
(struct sadb_address *)current);
break;
case SADB_EXT_ADDRESS_PROXY:
(struct sadb_address *)current);
break;
case SADB_EXT_KEY_AUTH:
break;
case SADB_EXT_KEY_ENCRYPT:
break;
case SADB_EXT_IDENTITY_SRC:
(struct sadb_ident *)current);
break;
case SADB_EXT_IDENTITY_DST:
(struct sadb_ident *)current);
break;
case SADB_EXT_SENSITIVITY:
break;
case SADB_EXT_PROPOSAL:
break;
case SADB_EXT_SUPPORTED_AUTH:
(struct sadb_supported *)current);
break;
(struct sadb_supported *)current);
break;
case SADB_EXT_SPIRANGE:
(struct sadb_spirange *)current);
break;
case SADB_X_EXT_EPROP:
break;
case SADB_X_EXT_KM_COOKIE:
(struct sadb_x_kmc *)current);
break;
(struct sadb_address *)current);
break;
(struct sadb_address *)current);
break;
default:
"UNK: Unknown ext. %d, len %d.\n"),
for (i = 0; i < ext->sadb_ext_len; i++)
break;
}
}
/*
* Print lifetimes NOW.
*/
"space or corrupt message."));
}
}
/*
* Write a message to the PF_KEY socket. If verbose, print the message
* heading into the kernel.
*/
static int
{
if (vflag) {
(void) printf(
gettext("VERBOSE ON: Message to kernel looks like:\n"));
(void) printf("==========================================\n");
(void) printf("==========================================\n");
}
}
/*
* SIGALRM handler for time_critical_enter.
*/
static void
{
} else {
"PF_KEY reply message"), signal);
}
/* errx() calls exit. */
}
/*
* Enter a "time critical" section where key is waiting for a return message.
*/
static void
time_critical_enter(void)
{
(void) alarm(TIME_CRITICAL_TIME);
}
/*
* Exit the "time critical" section after getting an appropriate return
* message.
*/
static void
time_critical_exit(void)
{
(void) alarm(0);
}
/*
* Construct a PF_KEY FLUSH message for the SA type specified.
*/
static void
{
int rc;
if (rc == -1)
Bail("write() to PF_KEY socket failed (in doflush)");
do {
if (rc == -1)
Bail("read (in doflush)");
/*
* I should _never_ hit the following unless:
*
* 1. There is a kernel bug.
* 2. There is another process filling in its pid with mine, and
* issuing a different message that would cause a different result.
*/
gettext("doflush: Return message not of type SADB_FLUSH!"));
Bail("doflush: Return message not of type SADB_FLUSH!");
}
if (msg.sadb_msg_errno != 0) {
}
Bail("return message (in doflush)");
}
}
/*
* save_XXX functions are used when "saving" the SA tables to either a
* file or standard output. They use the dump_XXX functions where needed,
* but mostly they use the rparseXXX functions.
*/
/*
* Print save information for a lifetime extension.
*
* NOTE : It saves the lifetime in absolute terms. For example, if you
* had a hard_usetime of 60 seconds, you'll save it as 60 seconds, even though
* there may have been 59 seconds burned off the clock.
*/
static boolean_t
{
char *prefix;
"soft" : "hard";
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
/*
* Print save information for an address extension.
*/
static boolean_t
{
/*
* Address-family reality check.
*/
return (B_FALSE);
switch (addr->sadb_address_exttype) {
case SADB_EXT_ADDRESS_SRC:
prefix = "src";
pprefix = "sport";
break;
case SADB_EXT_ADDRESS_DST:
prefix = "dst";
pprefix = "dport";
break;
case SADB_EXT_ADDRESS_PROXY:
prefix = "proxy";
break;
prefix = "nat_loc ";
pprefix = "nat_lport";
break;
prefix = "nat_rem ";
pprefix = "nat_rport";
break;
}
return (B_FALSE);
/*
* Do not do address-to-name translation, given that we live in
* an age of names that explode into many addresses.
*/
if (printable_addr == NULL)
printable_addr = "<inet_ntop() failed>";
return (B_FALSE);
/*
* The port is in the same position for struct sockaddr_in and
* struct sockaddr_in6. We exploit that property here.
*/
return (B_TRUE);
}
/*
* Print save information for a key extension. Returns whether writing
* to the specified output file was successful or not.
*/
static boolean_t
{
char *prefix;
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
/*
* Print save information for an identity extension.
*/
static boolean_t
{
char *prefix;
return (B_FALSE);
"dst";
rparseidtype(ident->sadb_ident_type)) < 0)
return (B_FALSE);
if (ident->sadb_ident_type == SADB_X_IDENTTYPE_DN ||
ident->sadb_ident_type == SADB_X_IDENTTYPE_GN) {
return (B_FALSE);
} else {
return (B_FALSE);
}
return (B_TRUE);
}
/*
* "Save" a security association to an output file.
*
* NOTE the lack of calls to gettext() because I'm outputting parseable stuff.
* ALSO NOTE that if you change keywords (see parsecmd()), you'll have to
* change them here as well.
*/
static void
{
int seen_proto = 0;
struct sadb_address *addr;
#define bail2(s) do { \
int t = errno; \
errno = t; \
Bail(s); \
} while (B_FALSE) /* How do I lint-clean this? */
Bail("save_assoc: Opening comment of SA");
Bail("save_assoc: First line of SA");
/* LINTED E_CONST_COND */
savenl();
switch (ext->sadb_ext_type) {
case SADB_EXT_SA:
"or dead.\n") < 0) {
/* LINTED E_CONST_COND */
bail2("save_assoc: fprintf not mature");
}
}
/* LINTED E_CONST_COND */
bail2("save_assoc: fprintf spi");
IPSEC_PROTO_ESP)) < 0)
/* LINTED E_CONST_COND */
bail2("save_assoc: fprintf encrypt");
IPSEC_PROTO_AH)) < 0)
/* LINTED E_CONST_COND */
bail2("save_assoc: fprintf auth");
assoc->sadb_sa_replay) < 0)
/* LINTED E_CONST_COND */
bail2("save_assoc: fprintf replay");
/* LINTED E_CONST_COND */
bail2("save_assoc: fprintf encap");
}
/* LINTED E_CONST_COND */
savenl();
break;
case SADB_EXT_LIFETIME_HARD:
case SADB_EXT_LIFETIME_SOFT:
/* LINTED E_CONST_COND */
bail2("save_lifetime");
/* LINTED E_CONST_COND */
savenl();
break;
case SADB_EXT_ADDRESS_SRC:
case SADB_EXT_ADDRESS_DST:
case SADB_EXT_ADDRESS_PROXY:
/* LINTED E_CONST_COND */
savenl();
seen_proto = 1;
}
/* LINTED E_CONST_COND */
bail2("save_address");
/* LINTED E_CONST_COND */
savenl();
break;
case SADB_EXT_KEY_AUTH:
case SADB_EXT_KEY_ENCRYPT:
/* LINTED E_CONST_COND */
bail2("save_address");
/* LINTED E_CONST_COND */
savenl();
break;
case SADB_EXT_IDENTITY_SRC:
case SADB_EXT_IDENTITY_DST:
/* LINTED E_CONST_COND */
bail2("save_address");
/* LINTED E_CONST_COND */
savenl();
break;
case SADB_EXT_SENSITIVITY:
default:
/* Skip over irrelevant extensions. */
break;
}
}
/* LINTED E_CONST_COND */
bail2("save_assoc: last fputs");
}
/*
* Because "save" and "dump" both use the SADB_DUMP message, fold both
* into the same function.
*/
static void
{
int rc;
gettext("# This key file was generated by the"));
gettext(" ipseckey(1m) command's 'save' feature.\n\n"));
}
if (rc == -1)
Bail("write to PF_KEY socket failed (in dodump)");
do {
/*
* For DUMP, do only the read as a time critical section.
*/
if (rc == -1)
Bail("read (in dodump)");
msg->sadb_msg_seq != 0 &&
msg->sadb_msg_errno == 0) {
(void) putchar('\n');
} else {
}
}
if (msg->sadb_msg_errno == 0) {
(void) printf(
gettext("Dump succeeded for SA type %d.\n"),
satype);
} else {
Bail("Dump failed");
}
}
#define SCOPE_UNSPEC 0
#define SCOPE_LINKLOCAL 1
#define SCOPE_SITELOCAL 2
#define SCOPE_GLOBAL 3
#define SCOPE_V4COMPAT 4
static int
{
/* Don't return anything regarding multicast for now... */
if (IN6_IS_ADDR_UNSPECIFIED(addr))
return (SCOPE_UNSPEC);
if (IN6_IS_ADDR_LINKLOCAL(addr))
return (SCOPE_LINKLOCAL);
if (IN6_IS_ADDR_SITELOCAL(addr))
return (SCOPE_SITELOCAL);
if (IN6_IS_ADDR_V4COMPAT(addr))
return (SCOPE_V4COMPAT);
if (IN6_IS_ADDR_LOOPBACK(addr))
return (SCOPE_LOOPBACK);
/* For now, return global by default. */
return (SCOPE_GLOBAL);
}
/*
* doaddresses():
*
* Used by doaddup() and dodelget() to create new SA's based on the
* provided source and destination addresses hostent.
*
* sadb_msg_type: expected PF_KEY reply message type
* sadb_msg_satype: expected PF_KEY reply satype
* cmd: user command
* srchp: hostent for the source address(es)
* dsthp: hostent for the destination address(es)
* src: points to the SADB source address extension
* dst: points to the SADB destination address extension
* unspec_src: indicates an unspecified source address.
* buffer: pointer to the SADB buffer to use with PF_KEY
* buffer_size: size of buffer
* spi: spi for this message (set by caller)
* srcport: source port if specified
* dstport: destination port is specified
* proto: IP protocol number if specified
* NATT note: we are going to assume a semi-sane world where NAT
* boxen don't explode to multiple addresses.
*/
static void
{
struct sockaddr_in6 *sin6;
int i, rc;
char **walker; /* For the SRC and PROXY walking functions. */
char *first_match;
/*
* Okay, now we have "src", "dst", and maybe "proxy" reassigned
* to point into the buffer to be written to PF_KEY, we can do
* potentially several writes based on destination address.
*
* First, fill in port numbers and protocol in extensions.
*/
}
}
sizeof (struct in6_addr));
}
sizeof (struct in6_addr));
}
/*
* The rules for ADD, GET, and UPDATE: (NOTE: This assumes IPsec.
* If other consumers of PF_KEY happen, this will have to be
* rewhacked.):
*
* Do a message for every possible DST address.
*
* If a source or proxy address explodes, keep unspecified
* (and mention unspecified).
*
* If dsthp is == dummy.he, then go through the loop once.
* If any other hp is == dummy.he, then you don't have to apply any
* silly rules.
*
* DELETE is different, because you can leave either "src" or "dst"
* blank! You need to explode if one of them is full, and not assume
* that the other is set.
*/
/*
* No destination address specified.
* With extended diagnostics, we don't have to bail the
* non-DELETE cases here. The EINVAL diagnostics will be
* enough to inform the user(s) what happened.
*/
i = 0;
do {
/* Just to be sure... */
/* Degenerate case, h_addr_list[0] == NULL. */
Bail("Empty source address list");
/*
* Fill in the src sockaddr.
*/
sizeof (struct in6_addr));
}
/* Save off a copy for later writing... */
if (rc == -1)
Bail("write() to PF_KEY socket "
"(in doaddresses)");
do {
if (rc == -1)
Bail("read (in doaddresses)");
"doaddresses: Unexpected returned message "
Bail("doaddresses: Unexpected returned "
"message");
}
if (errno != 0) {
"values is incorrect."));
}
Bail("return message (in doaddresses)");
}
/* ...and then restore the saved buffer. */
return;
}
/* Just to be sure... */
} else {
/*
* Fill in the dst sockaddr.
*/
sizeof (struct in6_addr));
}
/*
* Try and assign src, if there's any ambiguity.
*/
/*
* IPv4 address. Find an IPv4 address, then
* keep looking for a second one. If a second
* exists, print a message, and fill in the
* unspecified address.
*/
first_match = NULL;
/* LINTED E_BAD_PTR_CAST_ALIGN */
if (IN6_IS_ADDR_V4MAPPED(
if (first_match != NULL)
break;
else
first_match = *walker;
}
}
if (first_match == NULL) {
/*
* No IPv4 hits. Is this a single
* dest?
*/
"No IPv4 source address "
if (single_dst) {
/* Error. */
usage();
} else {
/* Continue, but do I print? */
continue; /* for loop */
}
/* I should never reach here. */
}
/*
* Early loop exit. It must've been
* multiple hits...
*
* Issue a null-source warning?
*/
"Multiple IPv4 source addresses "
"for %s, using unspecified source "
} else {
/*
* If I reach here w/o hitting the
* previous if statements, I have a
* single source address for this
* destination.
*/
sizeof (struct in6_addr));
}
} else {
/*
* IPv6 address. Find an IPv6 address.
* Unlike IPv4 addresses, things can get a
* little more sticky with scopes, etc.
*/
first_match = NULL;
/* LINTED E_BAD_PTR_CAST_ALIGN */
if (!IN6_IS_ADDR_V4MAPPED(
/*
* Set first-match, etc.
* Take into account scopes,
* and other IPv6 thingies.
*/
/* LINTED E_BAD_PTR_CAST */
if (src_scope == SCOPE_UNSPEC ||
if (first_match !=
NULL)
break;
else
*walker;
}
}
}
if (first_match == NULL) {
/*
* No IPv6 hits. Is this a single
* dest?
*/
"No IPv6 source address of "
"matching scope for name %s."),
if (single_dst) {
/* Error. */
usage();
} else {
/* Continue, but do I print? */
continue; /* for loop */
}
/* I should never reach here. */
}
/*
* Early loop exit. Issue a
* null-source warning?
*/
"Multiple IPv6 source addresses "
"for %s of the same scope, using "
"unspecified source instead."),
} else {
/*
* If I reach here w/o hitting the
* previous if statements, I have a
* single source address for this
* destination.
*/
sizeof (struct in6_addr));
}
}
}
/* Save off a copy for later writing... */
if (rc == -1)
Bail("write() to PF_KEY socket (in doaddresses)");
/* Blank the key for paranoia's sake. */
do {
if (rc == -1)
Bail("read (in doaddresses)");
/*
* I should _never_ hit the following unless:
*
* 1. There is a kernel bug.
* 2. Another process is mistakenly using my pid in a PF_KEY
* message.
*/
"doaddresses: Unexpected returned message "
Bail("doaddresses: Unexpected returned message");
}
if (msgp->sadb_msg_errno != 0) {
char addrprint[INET6_ADDRSTRLEN];
int on_errno = 0;
char *on_errno_msg;
/*
* Print different error messages depending
* on the SADB message type being processed.
* messages, we report that the SA does not
* exist. If we get a EEXIST error for a
* SA already exists.
*/
if (sadb_msg_type == SADB_GET ||
sadb_msg_type == SADB_DELETE) {
on_errno_msg = "does not exist";
} else if (sadb_msg_type == SADB_ADD ||
sadb_msg_type == SADB_UPDATE) {
on_errno_msg = "already exists";
}
"with spi 0x%x and addr\n%s %s."),
continue;
} else {
"values is incorrect."));
}
Bail("return message (in doaddresses)");
}
}
"SA information bigger than %d bytes."),
}
}
/* ...and then restore the saved buffer. */
}
/* Degenerate case, h_addr_list[0] == NULL. */
if (i == 0)
Bail("Empty destination address list");
}
/*
* Perform an add or an update. ADD and UPDATE are similar in the extensions
* they need.
*/
static void
{
struct sockaddr_in6 *sin6;
/* MLS TODO: Need sensitivity eventually. */
char *thiscmd;
/* Assume last element in argv is set to NULL. */
do {
argv++;
switch (token) {
case TOK_EOF:
/* Do nothing, I'm done. */
break;
case TOK_UNKNOWN:
*(argv - 1));
usage(); /* Will exit program. */
break;
case TOK_SPI:
case TOK_REPLAY:
case TOK_STATE:
case TOK_AUTHALG:
case TOK_ENCRALG:
case TOK_ENCAP:
/*
* May want to place this chunk of code in a function.
*
* This code checks for duplicate entries on a command
* line.
*/
/* Allocate the SADB_EXT_SA extension. */
Bail("malloc(assoc)");
assoc->sadb_sa_len =
SADB_8TO64(sizeof (*assoc));
}
switch (token) {
case TOK_SPI:
/*
* can type in another SPI.
*/
if (assoc->sadb_sa_spi != 0) {
"single SPI value."));
usage();
}
/* Must convert SPI to network order! */
assoc->sadb_sa_spi =
break;
case TOK_REPLAY:
/*
* That same cretin can do the same with
* replay.
*/
if (assoc->sadb_sa_replay != 0) {
"single replay wsize."));
usage();
}
if (assoc->sadb_sa_replay != 0) {
"WARNING: Replay with manual"
" keying considered harmful."));
}
break;
case TOK_STATE:
/*
* 0 is an actual state value, LARVAL. This
* means that one can type in the larval state
* and then type in another state on the same
* command line.
*/
if (assoc->sadb_sa_state != 0) {
"single SA state."));
usage();
}
break;
case TOK_AUTHALG:
if (assoc->sadb_sa_auth != 0) {
"single auth algorithm."));
usage();
}
break;
case TOK_ENCRALG:
if (assoc->sadb_sa_encrypt != 0) {
" encryption algorithm."));
usage();
}
break;
case TOK_ENCAP:
if (use_natt) {
" encapsulation."));
usage();
}
" encapsulation."));
usage();
}
/* set assoc flags later */
break;
}
argv++;
break;
case TOK_SRCPORT:
if (srcport != 0) {
"single source port."));
usage();
}
argv++;
break;
case TOK_DSTPORT:
if (dstport != 0) {
"single destination port."));
usage();
}
argv++;
break;
case TOK_NATLPORT:
if (natt_lport != 0) {
"single natt local port."));
usage();
}
if (natt_rport != 0) {
"one of natt remote and local port."));
usage();
}
argv++;
break;
case TOK_NATRPORT:
if (natt_rport != 0) {
"single natt remote port."));
usage();
}
if (natt_lport != 0) {
"one of natt remote and local port."));
usage();
}
argv++;
break;
case TOK_PROTO:
if (proto != 0) {
"single protocol."));
usage();
}
argv++;
break;
case TOK_SRCADDR:
case TOK_SRCADDR6:
"single source address."));
usage();
}
(token == TOK_SRCADDR6));
argv++;
/*
* Round of the sockaddr length to an 8 byte
* boundary to make PF_KEY happy.
*/
Bail("malloc(src)");
src->sadb_address_reserved = 0;
src->sadb_address_prefixlen = 0;
src->sadb_address_proto = 0;
/*
* Single address with -n flag.
*/
sizeof (struct in6_addr));
}
break;
case TOK_DSTADDR:
case TOK_DSTADDR6:
"destination address."));
usage();
}
(token == TOK_DSTADDR6));
argv++;
Bail("malloc(dst)");
dst->sadb_address_reserved = 0;
dst->sadb_address_prefixlen = 0;
dst->sadb_address_proto = 0;
/*
* Single address with -n flag.
*/
sizeof (struct in6_addr));
}
break;
case TOK_PROXYADDR:
case TOK_PROXYADDR6:
"proxy address."));
usage();
}
(token == TOK_PROXYADDR6));
argv++;
Bail("malloc(proxy)");
proxy->sadb_address_reserved = 0;
proxy->sadb_address_prefixlen = 0;
proxy->sadb_address_proto = 0;
/*
* Single address with -n flag or single name.
*/
sizeof (struct in6_addr));
} else {
/*
* If the proxy address is vague, don't bother.
*/
}
break;
case TOK_NATLOC:
if (natt_local != NULL) {
"single natt local address."));
usage();
}
argv++;
/*
* Round of the sockaddr length to an 8 byte
* boundary to make PF_KEY happy.
*/
if (natt_local == NULL)
Bail("malloc(natt_local)");
natt_local->sadb_address_proto = 0;
/*
* Single address with -n flag.
*/
}
break;
case TOK_NATREM:
if (natt_remote != NULL) {
"single natt remote address."));
usage();
}
argv++;
/*
* Round of the sockaddr length to an 8 byte
* boundary to make PF_KEY happy.
*/
if (natt_remote == NULL)
Bail("malloc(natt_remote)");
/*
* Single address with -n flag.
*/
}
break;
case TOK_ENCRKEY:
"single encryption key."));
usage();
}
argv++;
break;
case TOK_AUTHKEY:
" authentication key."));
usage();
}
argv++;
break;
case TOK_SRCIDTYPE:
"line."));
usage();
}
" source certificate identity."));
usage();
}
Bail("malloc(srcid)");
argv++;
srcid->sadb_ident_reserved = 0;
/* Can use strcpy because I allocate my own memory. */
argv++;
break;
case TOK_DSTIDTYPE:
" line."));
usage();
}
"tion certificate identity."));
usage();
}
Bail("malloc(dstid)");
argv++;
dstid->sadb_ident_reserved = 0;
/* Can use strcpy because I allocate my own memory. */
argv++;
break;
case TOK_HARD_ALLOC:
case TOK_HARD_BYTES:
case TOK_HARD_ADDTIME:
case TOK_HARD_USETIME:
Bail("malloc(hard_lifetime)");
SADB_8TO64(sizeof (*hard));
}
switch (token) {
case TOK_HARD_ALLOC:
if (hard->sadb_lifetime_allocations != 0) {
" hard allocation limit."));
usage();
}
break;
case TOK_HARD_BYTES:
if (hard->sadb_lifetime_bytes != 0) {
"single hard byte limit."));
usage();
}
B_TRUE);
break;
case TOK_HARD_ADDTIME:
if (hard->sadb_lifetime_addtime != 0) {
"single past-add lifetime."));
usage();
}
B_TRUE);
break;
case TOK_HARD_USETIME:
if (hard->sadb_lifetime_usetime != 0) {
"single past-use lifetime."));
usage();
}
B_TRUE);
break;
}
argv++;
break;
case TOK_SOFT_ALLOC:
case TOK_SOFT_BYTES:
case TOK_SOFT_ADDTIME:
case TOK_SOFT_USETIME:
Bail("malloc(soft_lifetime)");
SADB_8TO64(sizeof (*soft));
}
switch (token) {
case TOK_SOFT_ALLOC:
if (soft->sadb_lifetime_allocations != 0) {
" soft allocation limit."));
usage();
}
break;
case TOK_SOFT_BYTES:
if (soft->sadb_lifetime_bytes != 0) {
" soft byte limit."));
usage();
}
B_TRUE);
break;
case TOK_SOFT_ADDTIME:
if (soft->sadb_lifetime_addtime != 0) {
" past-add lifetime."));
usage();
}
B_TRUE);
break;
case TOK_SOFT_USETIME:
if (soft->sadb_lifetime_usetime != 0) {
" past-use lifetime."));
usage();
}
B_TRUE);
break;
}
argv++;
break;
default:
*(argv - 1));
usage();
break;
}
/*
* Okay, so now I have all of the potential extensions!
* Allocate a single contiguous buffer. Keep in mind that it'll
* be enough because the key itself will be yanked.
*/
/*
* Set explicit unspecified source address.
*/
unspec_src = B_TRUE;
Bail("malloc(implicit src)");
/* Confusing, but we're copying from DST to SRC. :) */
}
if (assoc->sadb_sa_spi == 0) {
"the association you wish to %s."), thiscmd);
usage();
}
"for this add."));
usage();
}
/* Hack to let user specify NULL ESP implicitly. */
assoc->sadb_sa_encrypt == 0)
/* 0 is an actual value. Print a warning if it was entered. */
if (assoc->sadb_sa_state == 0) {
if (readstate)
"WARNING: Cannot set LARVAL SA state."));
}
if (use_natt) {
if (natt_remote != NULL)
if (natt_local != NULL)
}
/* Save the SPI for the case of an error. */
} else {
usage();
}
}
}
usage();
}
}
}
}
}
} else {
usage();
}
if (use_natt) {
"Must specify natt remote or local address "
"for UDP encapsulation."));
usage();
}
"local address must also be specified."));
usage();
}
"remote address must also be specified."));
usage();
}
if (natt_remote != NULL) {
}
if (natt_local != NULL) {
}
}
/*
* PF_KEY requires a source address extension, even if the source
* address itself is unspecified. (See "Set explicit unspecified..."
* code fragment above. Destination reality check was above.)
*/
}
}
/*
* DELETE and GET are similar, in that they only need the extensions
* required to _find_ an SA, and then either delete it or obtain its
* information.
*/
static void
{
char *thiscmd;
struct sockaddr_in6 *sin6;
/* Set the first extension header to right past the base message. */
/* Assume last element in argv is set to NULL. */
do {
argv++;
switch (token) {
case TOK_EOF:
/* Do nothing, I'm done. */
break;
case TOK_UNKNOWN:
*(argv - 1));
usage(); /* Will exit program. */
break;
case TOK_SPI:
"Can only specify single SPI value."));
usage();
}
B_TRUE));
argv++;
break;
case TOK_SRCPORT:
if (srcport != 0) {
"Can only specify single source port."));
usage();
}
argv++;
break;
case TOK_DSTPORT:
if (dstport != 0) {
"specify single destination port."));
usage();
}
argv++;
break;
case TOK_PROTO:
if (proto != 0) {
"Can only specify single protocol."));
usage();
}
argv++;
break;
case TOK_SRCADDR:
case TOK_SRCADDR6:
"Can only specify single source addr."));
usage();
}
(token == TOK_SRCADDR6));
argv++;
/*
* Single address with -n flag.
*/
sizeof (struct in6_addr));
}
/* The rest is pre-bzeroed for us. */
break;
case TOK_DSTADDR:
case TOK_DSTADDR6:
"addr."));
usage();
}
(token == TOK_SRCADDR6));
argv++;
/*
* Single address with -n flag.
*/
sizeof (struct in6_addr));
}
/* The rest is pre-bzeroed for us. */
break;
default:
usage(); /* Will exit program. */
break;
}
}
}
/* So I have enough of the message to send it down! */
}
/*
* "ipseckey monitor" should exit very gracefully if ^C is tapped.
*/
static void
monitor_catch(int signal)
{
}
/*
* Loop forever, listening on PF_KEY messages.
*/
static void
{
int rc;
/* Catch ^C. */
if (!passive) {
if (rc == -1)
Bail("write (SADB_X_PROMISC)");
} else {
}
for (; ; ) {
/*
* I assume that read() is non-blocking, and will never
* return 0.
*/
if (rc == -1)
Bail("read (in domonitor)");
/*
* Q: Should I use the same method of printing as GET does?
* A: For now, yes.
*/
(void) putchar('\n');
}
}
/*
* Open the output file for the "save" command.
*/
static FILE *
opensavefile(char *filename)
{
int fd;
/*
* If the user specifies "-" or doesn't give a filename, then
* dump to stdout. Make sure to document the dangers of files
* that are NFS, directing your output to strange places, etc.
*/
return (stdout);
/*
* open the file with the create bits set. Since I check for
* real UID == root in main(), I won't worry about the ownership
* problem.
*/
if (fd == -1) {
if (fd == -1)
}
"keying material."));
}
}
/* Okay, we have an FD. Assign it to a stdio FILE pointer. */
}
return (retval);
}
/*
* Either mask or unmask all relevant signals.
*/
static void
{
if (unmask) {
} else {
(void) sigfillset(&set);
}
}
/*
* Wrapper for inet_ntop(3SOCKET). Expects AF_INET6 address.
* Process the address as a AF_INET address if it is a IPv4 mapped
* address.
*/
static const char *
{
}
}
/*
* Assorted functions to print help text.
*/
static void
{
int i;
puts_tr("\nSA attributes:");
if (i%3 == 0)
(void) printf("\n");
}
(void) printf("\n");
}
static void
{
int cmd;
doattrhelp();
return;
}
switch (cmd) {
case CMD_UPDATE:
puts_tr("update - Update an existing SA");
break;
case CMD_ADD:
puts_tr("add - Add a new security association (SA)");
break;
case CMD_DELETE:
puts_tr("delete - Delete an SA");
break;
case CMD_GET:
puts_tr("get - Display an SA");
break;
case CMD_FLUSH:
puts_tr("flush - Delete all SAs");
break;
case CMD_DUMP:
puts_tr("dump - Display all SAs");
break;
case CMD_MONITOR:
puts_tr("monitor - Monitor all PF_KEY reply messages.");
break;
case CMD_PMONITOR:
"pmonitor, passive_monitor - Monitor PF_KEY messages that");
" reply to all PF_KEY sockets.");
break;
case CMD_QUIT:
puts_tr("quit, exit - Exit the program");
break;
case CMD_SAVE:
puts_tr("save - Saves all SAs to a file");
break;
case CMD_HELP:
puts_tr("help - Display list of commands");
puts_tr("help <cmd> - Display help for command");
puts_tr("help attr - Display possible SA attributes");
break;
default:
break;
}
}
static void
{
return;
}
puts_tr("Commands");
puts_tr("--------");
puts_tr("?, help - Display this list");
puts_tr("help <cmd> - Display help for command");
puts_tr("help attr - Display possible SA attributes");
puts_tr("quit, exit - Exit the program");
puts_tr("monitor - Monitor all PF_KEY reply messages.");
puts_tr("pmonitor, passive_monitor - Monitor PF_KEY messages that");
puts_tr(" reply to all PF_KEY sockets.");
puts_tr("");
puts_tr("The following commands are of the form:");
puts_tr(" <command> {SA type} {attribute value}*");
puts_tr("");
puts_tr("add (interactive only) - Add a new security association (SA)");
puts_tr("update (interactive only) - Update an existing SA");
puts_tr("delete - Delete an SA");
puts_tr("get - Display an SA");
puts_tr("flush - Delete all SAs");
puts_tr("dump - Display all SAs");
puts_tr("save - Saves all SAs to a file");
}
/*
* "Parse" a command line from argv.
*/
static void
{
if (argc == 0)
return;
switch (cmd) {
case CMD_HELP:
return;
case CMD_MONITOR:
break;
case CMD_PMONITOR:
break;
case CMD_QUIT:
exit(0);
}
if (satype != SADB_SATYPE_UNSPEC) {
argv++;
} else {
/*
* You must specify either "all" or a specific SA type
* for the "save" command.
*/
"SA type for save."));
usage();
} else {
argv++;
}
}
switch (cmd) {
case CMD_FLUSH:
break;
case CMD_ADD:
case CMD_UPDATE:
/*
* NOTE: Shouldn't allow ADDs or UPDATEs with keying material
* from the command line.
*/
if (!interactive) {
"can't do ADD or UPDATE from the command line."));
}
if (satype == SADB_SATYPE_UNSPEC) {
usage();
/* NOTREACHED */
}
/* Parse for extensions, including keying material. */
break;
case CMD_DELETE:
case CMD_GET:
if (satype == SADB_SATYPE_UNSPEC) {
usage();
/* NOTREACHED */
}
/* Parse for bare minimum to locate an SA. */
break;
case CMD_DUMP:
break;
case CMD_SAVE:
break;
default:
usage();
}
}
int
{
int ch;
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
if (getuid() != 0) {
}
/* umask me to paranoid, I only want to create files read-only */
switch (ch) {
case 'p':
break;
case 'n':
break;
case 'v':
break;
case 'f':
if (dosave)
usage();
break;
case 's':
if (readfile)
usage();
break;
default:
usage();
}
if (keysock == -1)
Bail("Opening PF_KEY socket");
if (dosave) {
exit(0);
}
/* Go into interactive mode here. */
}
return (0);
}