예제 #1
0
int
pfkey_msg_parse(struct sadb_msg *pfkey_msg,
		struct pf_key_ext_parsers_def *ext_parsers[],
		struct sadb_ext *extensions[],
		int dir)
{
	int error = 0;
	int remain;
	struct sadb_ext *pfkey_ext;
	int extensions_seen = 0;
	
	DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
		  "pfkey_msg_parse: "
		  "parsing message ver=%d, type=%d(%s), errno=%d, satype=%d(%s), len=%d, res=%d, seq=%d, pid=%d.\n", 
		  pfkey_msg->sadb_msg_version,
		  pfkey_msg->sadb_msg_type,
		  pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type),
		  pfkey_msg->sadb_msg_errno,
		  pfkey_msg->sadb_msg_satype,
		  satype2name(pfkey_msg->sadb_msg_satype),
		  pfkey_msg->sadb_msg_len,
		  pfkey_msg->sadb_msg_reserved,
		  pfkey_msg->sadb_msg_seq,
		  pfkey_msg->sadb_msg_pid);
	
	if(ext_parsers == NULL) ext_parsers = ext_default_parsers;
	
	pfkey_extensions_init(extensions);
	
	remain = pfkey_msg->sadb_msg_len;
	remain -= sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN;
	
	pfkey_ext = (struct sadb_ext*)((char*)pfkey_msg +
				       sizeof(struct sadb_msg));
	
	extensions[0] = (struct sadb_ext *) pfkey_msg;
	
	
	if(pfkey_msg->sadb_msg_version != PF_KEY_V2) {
		DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
			"pfkey_msg_parse: "
			"not PF_KEY_V2 msg, found %d, should be %d.\n",
			pfkey_msg->sadb_msg_version,
			PF_KEY_V2);
		SENDERR(EINVAL);
	}

	if(!pfkey_msg->sadb_msg_type) {
		DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
			"pfkey_msg_parse: "
			"msg type not set, must be non-zero..\n");
		SENDERR(EINVAL);
	}

	if(pfkey_msg->sadb_msg_type > SADB_MAX) {
		DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
			"pfkey_msg_parse: "
			"msg type=%d > max=%d.\n",
			pfkey_msg->sadb_msg_type,
			SADB_MAX);
		SENDERR(EINVAL);
	}

	switch(pfkey_msg->sadb_msg_type) {
	case SADB_GETSPI:
	case SADB_UPDATE:
	case SADB_ADD:
	case SADB_DELETE:
	case SADB_GET:
	case SADB_X_GRPSA:
	case SADB_X_ADDFLOW:
		if(!satype2proto(pfkey_msg->sadb_msg_satype)) {
			DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
				  "pfkey_msg_parse: "
				  "satype %d conversion to proto failed for msg_type %d (%s).\n",
				  pfkey_msg->sadb_msg_satype,
				  pfkey_msg->sadb_msg_type,
				  pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type));
			SENDERR(EINVAL);
		} else {
			DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
				  "pfkey_msg_parse: "
				  "satype %d(%s) conversion to proto gives %d for msg_type %d(%s).\n",
				  pfkey_msg->sadb_msg_satype,
				  satype2name(pfkey_msg->sadb_msg_satype),
				  satype2proto(pfkey_msg->sadb_msg_satype),
				  pfkey_msg->sadb_msg_type,
				  pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type));
		}
	case SADB_ACQUIRE:
	case SADB_REGISTER:
	case SADB_EXPIRE:
		if(!pfkey_msg->sadb_msg_satype) {
			DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
				  "pfkey_msg_parse: "
				  "satype is zero, must be non-zero for msg_type %d(%s).\n",
				  pfkey_msg->sadb_msg_type,
				  pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type));
			SENDERR(EINVAL);
		}
	default:
	}
	
	/* errno must not be set in downward messages */
	/* this is not entirely true... a response to an ACQUIRE could return an error */
	if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type != SADB_ACQUIRE) && pfkey_msg->sadb_msg_errno) {
		DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
			    "pfkey_msg_parse: "
			    "errno set to %d.\n",
			    pfkey_msg->sadb_msg_errno);
		SENDERR(EINVAL);
	}

	DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
		  "pfkey_msg_parse: "
		  "remain=%d, ext_type=%d(%s), ext_len=%d.\n", 
		  remain,
		  pfkey_ext->sadb_ext_type,
		  pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type),
		  pfkey_ext->sadb_ext_len);
	
	DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
		"pfkey_msg_parse: "
		"extensions permitted=%08x, required=%08x.\n",
		extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type],
		extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]);
	
	extensions_seen = 1;
	
	while( (remain * IPSEC_PFKEYv2_ALIGN) >= sizeof(struct sadb_ext) ) {
		/* Is there enough message left to support another extension header? */
		if(remain < pfkey_ext->sadb_ext_len) {
			DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
				"pfkey_msg_parse: "
				"remain %d less than ext len %d.\n", 
				remain, pfkey_ext->sadb_ext_len);
			SENDERR(EINVAL);
		}
		
		DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
			"pfkey_msg_parse: "
			"parsing ext type=%d remain=%d.\n",
			pfkey_ext->sadb_ext_type,
			remain);
		
		/* Is the extension header type valid? */
		if((pfkey_ext->sadb_ext_type > SADB_EXT_MAX) || (!pfkey_ext->sadb_ext_type)) {
			DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
				"pfkey_msg_parse: "
				"ext type %d invalid, SADB_EXT_MAX=%d.\n", 
				pfkey_ext->sadb_ext_type, SADB_EXT_MAX);
			SENDERR(EINVAL);
		}
		
		/* Have we already seen this type of extension? */
		if((extensions_seen & ( 1 << pfkey_ext->sadb_ext_type )) != 0)
		{
			DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
				"pfkey_msg_parse: "
				"ext type %d already seen.\n", 
				pfkey_ext->sadb_ext_type);
			SENDERR(EINVAL);
		}

		/* Do I even know about this type of extension? */
		if(ext_parsers[pfkey_ext->sadb_ext_type]==NULL) {
			DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
				"pfkey_msg_parse: "
				"ext type %d unknown, ignoring.\n", 
				pfkey_ext->sadb_ext_type);
			goto next_ext;
		}

		/* Is this type of extension permitted for this type of message? */
		if(!(extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type] &
		     1<<pfkey_ext->sadb_ext_type)) {
			DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
				"pfkey_msg_parse: "
				"ext type %d not permitted, exts_perm_in=%08x, 1<<type=%08x\n", 
				pfkey_ext->sadb_ext_type, 
				extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type],
				1<<pfkey_ext->sadb_ext_type);
			SENDERR(EINVAL);
		}

		DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
			"pfkey_msg_parse: "
			"About to parse extension %d %p with parser %s.\n",
			pfkey_ext->sadb_ext_type,
			pfkey_ext,
			ext_parsers[pfkey_ext->sadb_ext_type]->parser_name);
		/* Parse the extension */
		if((error =
		    (*ext_parsers[pfkey_ext->sadb_ext_type]->parser)(pfkey_ext))) {
			DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
				"pfkey_msg_parse: "
				"extension parsing for type %d failed with error %d.\n",
				pfkey_ext->sadb_ext_type, error); 
			SENDERR(-error);
		}
		DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
			"pfkey_msg_parse: "
			"Extension %d parsed.\n",
			pfkey_ext->sadb_ext_type);
		
		/* Mark that we have seen this extension and remember the header location */
		extensions_seen |= ( 1 << pfkey_ext->sadb_ext_type );
		extensions[pfkey_ext->sadb_ext_type] = pfkey_ext;

	next_ext:		
		/* Calculate how much message remains */
		remain -= pfkey_ext->sadb_ext_len;

		if(!remain) {
			break;
		}
		/* Find the next extension header */
		pfkey_ext = (struct sadb_ext*)((char*)pfkey_ext +
			pfkey_ext->sadb_ext_len * IPSEC_PFKEYv2_ALIGN);
	}

	if(remain) {
		DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
			"pfkey_msg_parse: "
			"unexpected remainder of %d.\n", 
			remain);
		/* why is there still something remaining? */
		SENDERR(EINVAL);
	}

	/* check required extensions */
	DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT,
		"pfkey_msg_parse: "
		"extensions permitted=%08x, seen=%08x, required=%08x.\n",
		extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type],
		extensions_seen,
		extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]);

	/* don't check further if it is an error return message since it
	   may not have a body */
	if(pfkey_msg->sadb_msg_errno) {
		SENDERR(-error);
	}

	if((extensions_seen &
	    extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) !=
	   extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) {
		DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
			"pfkey_msg_parse: "
			"required extensions missing:%08x.\n",
			extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type] -
			(extensions_seen &
			 extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]));
		SENDERR(EINVAL);
	}
	
	if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type == SADB_X_DELFLOW)
	   && ((extensions_seen	& SADB_X_EXT_ADDRESS_DELFLOW)
	       != SADB_X_EXT_ADDRESS_DELFLOW)
	   && (((extensions_seen & (1<<SADB_EXT_SA)) != (1<<SADB_EXT_SA))
	   || ((((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_flags
		& SADB_X_SAFLAGS_CLEARFLOW)
	       != SADB_X_SAFLAGS_CLEARFLOW))) {
		DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
			"pfkey_msg_parse: "
			"required SADB_X_DELFLOW extensions missing: either %08x must be present or %08x must be present with SADB_X_SAFLAGS_CLEARFLOW set.\n",
			SADB_X_EXT_ADDRESS_DELFLOW
			- (extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW),
			(1<<SADB_EXT_SA) - (extensions_seen & (1<<SADB_EXT_SA)));
		SENDERR(EINVAL);
	}
	
	switch(pfkey_msg->sadb_msg_type) {
	case SADB_ADD:
	case SADB_UPDATE:
		/* check maturity */
		if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state !=
		   SADB_SASTATE_MATURE) {
			DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
				"pfkey_msg_parse: "
				"state=%d for add or update should be MATURE=%d.\n",
				((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state,
				SADB_SASTATE_MATURE);
			SENDERR(EINVAL);
		}
		
		/* check AH and ESP */
		switch(((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype) {
		case SADB_SATYPE_AH:
			if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) &&
			     ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_auth !=
			     SADB_AALG_NONE)) {
				DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
					"pfkey_msg_parse: "
					"auth alg is zero, must be non-zero for AH SAs.\n");
				SENDERR(EINVAL);
			}
			if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt !=
			   SADB_EALG_NONE) {
				DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
					"pfkey_msg_parse: "
					"AH handed encalg=%d, must be zero.\n",
					((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt);
				SENDERR(EINVAL);
			}
			break;
		case SADB_SATYPE_ESP:
			if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) &&
			     ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt !=
			     SADB_EALG_NONE)) {
				DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
					"pfkey_msg_parse: "
					"encrypt alg=%d is zero, must be non-zero for ESP=%d SAs.\n",
					((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt,
					((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype);
				SENDERR(EINVAL);
			}
			if((((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt ==
			    SADB_EALG_NULL) &&
			   (((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth ==
			    SADB_AALG_NONE) ) {
				DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
					"pfkey_msg_parse: "
					"ESP handed encNULL+authNONE, illegal combination.\n");
				SENDERR(EINVAL);
			}
			break;
		case SADB_X_SATYPE_COMP:
			if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) &&
			     ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt !=
			     SADB_EALG_NONE)) {
				DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
					"pfkey_msg_parse: "
					"encrypt alg=%d is zero, must be non-zero for COMP=%d SAs.\n",
					((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt,
					((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype);
				SENDERR(EINVAL);
			}
			if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth !=
			   SADB_AALG_NONE) {
				DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
					"pfkey_msg_parse: "
					"COMP handed auth=%d, must be zero.\n",
					((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth);
				SENDERR(EINVAL);
			}
			break;
		default:
		}
		if(ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi) <= 255) {
			DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM,
				"pfkey_msg_parse: "
				"spi=%08lx must be > 255.\n",
				ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi));
			SENDERR(EINVAL);
		}
	default:	
	}
errlab:

	return error;
}
예제 #2
0
파일: pf_key.c 프로젝트: hydromet/libreswan
int main(int argc, char *argv[])
{
	int opt;
	ssize_t readlen;
	unsigned char pfkey_buf[256];
	struct sadb_msg *msg;
	int fork_after_register;
	char *pidfilename;
	char *infilename;
	char *outfilename;

	static int ah_register;
	static int esp_register;
	static int ipip_register;
	static int ipcomp_register;

	static struct option long_options[] =
	{
		{ "help",        no_argument, 0, 'h' },
		{ "daemon",      required_argument, 0, 'f' },
		{ "dumpfile",    required_argument, 0, 'd' },
		{ "encodefile",  required_argument, 0, 'e' },
		{ "ah",          no_argument, &ah_register, 1 },
		{ "esp",         no_argument, &esp_register, 1 },
		{ "ipip",        no_argument, &ipip_register, 1 },
		{ "ipcomp",      no_argument, &ipcomp_register, 1 },
	};

	ah_register   = 0;
	esp_register  = 0;
	ipip_register = 0;
	ipcomp_register = 0;
	dienow = 0;
	fork_after_register = 0;

	pidfilename = NULL;
	infilename  = NULL;
	outfilename = NULL;

	progname = argv[0];
	if (strrchr(progname, '/'))
		progname = strrchr(progname, '/') + 1;

	while ((opt = getopt_long(argc, argv, "hd:e:f:",
				  long_options, NULL)) !=  EOF) {
		switch (opt) {
		case 'f':
			pidfilename = optarg;
			fork_after_register = 1;
			break;

		case 'd':
			infilename = optarg;
			break;

		case 'e':
			outfilename = optarg;
			break;

		case 'h':
			Usage();
			break;
		case '0':
			/* it was a long option with a flag */
			break;
		}
	}

	if (infilename  == NULL &&
	    outfilename == NULL) {
		if ((pfkey_sock =
			     safe_socket(PF_KEY, SOCK_RAW, PF_KEY_V2) ) < 0) {
			fprintf(stderr,
				"%s: failed to open PF_KEY family socket: %s\n",
				progname, strerror(errno));
			exit(1);
		}

		if (ah_register == 0 &&
		    esp_register == 0 &&
		    ipip_register == 0 &&
		    ipcomp_register == 0) {
			ah_register = 1;
			esp_register = 1;
			ipip_register = 1;
			ipcomp_register = 1;
		}

		if (ah_register)
			pfkey_register(K_SADB_SATYPE_AH);
		if (esp_register)
			pfkey_register(K_SADB_SATYPE_ESP);
		if (ipip_register)
			pfkey_register(K_SADB_X_SATYPE_IPIP);
		if (ipcomp_register)
			pfkey_register(K_SADB_X_SATYPE_COMP);

		if (fork_after_register) {
			/*
			 * to aid in regression testing, we offer to register
			 * everything first, and then we fork. As part of this
			 * we write the PID of the new process to a file
			 * provided.
			 */
			int pid;
			FILE *pidfile;

			fflush(stdout);
			fflush(stderr);

			pid = fork();
			if (pid != 0) {
				/* in parent! */
				exit(0);
			}

			if ((pidfile = fopen(pidfilename, "w")) == NULL) {
				perror(pidfilename);
			} else {
				fprintf(pidfile, "%d", getpid());
				fclose(pidfile);
			}
		}

	} else if (infilename != NULL) {
		pfkey_sock = open(infilename, O_RDONLY);
		if (pfkey_sock < 0) {
			fprintf(stderr, "%s: failed to open %s: %s\n",
				progname, infilename, strerror(errno));
			exit(1);
		}
	} else if (outfilename != NULL) {
		/* call encoder */
		exit(1);
	}

	signal(SIGINT,  controlC);
	signal(SIGTERM, controlC);

	while ((readlen =
			read(pfkey_sock, pfkey_buf, sizeof(pfkey_buf))) > 0) {
		msg = (struct sadb_msg *)pfkey_buf;

		/* first, see if we got enough for an sadb_msg */
		if ((size_t)readlen < sizeof(struct sadb_msg)) {
			printf("%s: runt packet of size: %d (<%lu)\n",
			       progname, (int)readlen,
			       (unsigned long)sizeof(struct sadb_msg));
			continue;
		}

		/* okay, we got enough for a message, print it out */
		printf(
			"\npfkey v%d msg. type=%d(%s) seq=%d len=%d pid=%d errno=%d satype=%d(%s)\n",
			msg->sadb_msg_version,
			msg->sadb_msg_type,
			pfkey_v2_sadb_type_string(msg->sadb_msg_type),
			msg->sadb_msg_seq,
			msg->sadb_msg_len,
			msg->sadb_msg_pid,
			msg->sadb_msg_errno,
			msg->sadb_msg_satype,
			satype2name(msg->sadb_msg_satype));

		if ((size_t)readlen != msg->sadb_msg_len *
		    IPSEC_PFKEYv2_ALIGN) {
			printf(
				"%s: packet size read from socket=%d doesn't equal sadb_msg_len %d * %u; message not decoded\n",
				progname,
				(int) readlen,
				msg->sadb_msg_len,
				(int) IPSEC_PFKEYv2_ALIGN);
			continue;
		}

		pfkey_print(msg, stdout);
	}
	printf("%s: exited normally\n", progname);
	exit(0);
}
예제 #3
0
파일: spi.c 프로젝트: intliang/libreswan
int main(int argc, char *argv[])
{
	__u32 spi = 0;
	int c;
	ip_said said;
	const char *error_s;
	char ipsaid_txt[SATOT_BUF];

	int outif = 0;
	int error = 0;
	ssize_t io_error;
	int argcount = argc;
	pid_t mypid;
	int listenreply = 0;

	unsigned char authalg, encryptalg;
	struct sadb_ext *extensions[K_SADB_EXT_MAX + 1];
	struct sadb_msg *pfkey_msg;
	char *edst_opt, *spi_opt, *proto_opt, *af_opt, *said_opt, *dst_opt,
	*src_opt;
	u_int32_t natt;
	u_int16_t sport, dport;
	uint32_t life[life_maxsever][life_maxtype];
	char *life_opt[life_maxsever][life_maxtype];
	struct stat sts;
	struct sadb_builds sab;

	progname = argv[0];
	mypid = getpid();
	natt = 0;
	sport = 0;
	dport = 0;

	tool_init_log();

	zero(&said);	/* OK: no pointer fields */
	edst_opt = spi_opt = proto_opt = af_opt = said_opt = dst_opt =
		src_opt = NULL;
	{
		int i, j;

		for (i = 0; i < life_maxsever; i++) {
			for (j = 0; j < life_maxtype; j++) {
				life_opt[i][j] = NULL;
				life[i][j] = 0;
			}
		}
	}

	while ((c = getopt_long(argc, argv,
				"" /*"H:P:Z:46dcA:E:e:s:a:w:i:D:S:hvgl:+:f:"*/,
				longopts, 0)) != EOF) {
		unsigned long u;
		err_t ugh;

		switch (c) {
		case 'g':
			debug = TRUE;
			pfkey_lib_debug = PF_KEY_DEBUG_PARSE_MAX;
			/* paul: this is a plutoism? cur_debugging = 0xffffffff; */
			argcount--;
			break;

		case 'R':
			listenreply = 1;
			argcount--;
			break;

		case 'r':
			dumpsaref = 1;
			argcount--;
			break;

		case 'b':  /* set the SAref to use */
			ugh = ttoulb(optarg, 0, 0, INT_MAX, &u);
			if (ugh != NULL) {
				fprintf(stderr,
					"%s: Invalid SAREFi parameter \"%s\": %s\n",
					progname, optarg, ugh);
				exit(1);
			}
			saref_me = u;
			argcount--;
			break;

		case 'B':  /* set the SAref to use for outgoing packets */
			ugh = ttoulb(optarg, 0, 0, INT_MAX, &u);
			if (ugh != NULL) {
				fprintf(stderr,
					"%s: Invalid SAREFo parameter \"%s\": %s\n",
					progname, optarg, ugh);
				exit(1);
			}
			saref_him = u;
			argcount--;
			break;

		case 'O':  /* set interface from which packet should arrive */
			ugh = ttoulb(optarg, 0, 0, INT_MAX, &u);
			if (ugh != NULL) {
				fprintf(stderr,
					"%s: Invalid outif parameter \"%s\": %s\n",
					progname, optarg, ugh);
				exit(1);
			}
			outif = u;
			argcount--;
			break;

		case 'l':
		{
			static const char combine_fmt[] = "%s --label %s";
			size_t room = strlen(argv[0]) +
					  sizeof(combine_fmt) +
					  strlen(optarg);

			progname = malloc(room);
			snprintf(progname, room, combine_fmt,
				argv[0],
				optarg);
			tool_close_log();
			tool_init_log();

			argcount -= 2;
			break;
		}
		case 'H':
			if (alg) {
				fprintf(stderr,
					"%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear'  options permitted.\n",
					progname);
				exit(1);
			}
			if (streq(optarg, "hmac-md5-96")) {
				alg = XF_AHHMACMD5;
			} else if (streq(optarg, "hmac-sha1-96")) {
				alg = XF_AHHMACSHA1;
			} else {
				fprintf(stderr,
					"%s: Unknown authentication algorithm '%s' follows '--ah' option.\n",
					progname, optarg);
				exit(1);
			}
			if (debug) {
				fprintf(stdout, "%s: Algorithm %d selected.\n",
					progname,
					alg);
			}
			break;

		case 'P':
			if (alg) {
				fprintf(stderr,
					"%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear'  options permitted.\n",
					progname);
				exit(1);
			}

			alg = decode_esp(optarg);

			if (debug) {
				fprintf(stdout, "%s: Algorithm %d selected.\n",
					progname,
					alg);
			}
			break;

		case 'Z':
			if (alg) {
				fprintf(stderr,
					"%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear'  options permitted.\n",
					progname);
				exit(1);
			}
			if (streq(optarg, "deflate")) {
				alg = XF_COMPDEFLATE;
			} else if (streq(optarg, "lzs")) {
				alg = XF_COMPLZS;
			} else {
				fprintf(stderr,
					"%s: Unknown compression algorithm '%s' follows '--comp' option.\n",
					progname, optarg);
				exit(1);
			}
			if (debug) {
				fprintf(stdout, "%s: Algorithm %d selected.\n",
					progname,
					alg);
			}
			break;

		case '4':
			if (alg) {
				fprintf(stderr,
					"%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear' options permitted.\n",
					progname);
				exit(1);
			}
			alg = XF_IP4;
			address_family = AF_INET;
			if (debug) {
				fprintf(stdout, "%s: Algorithm %d selected.\n",
					progname,
					alg);
			}
			break;

		case '6':
			if (alg) {
				fprintf(stderr,
					"%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear' options permitted.\n",
					progname);
				exit(1);
			}
			alg = XF_IP6;
			address_family = AF_INET6;
			if (debug) {
				fprintf(stdout, "%s: Algorithm %d selected.\n",
					progname,
					alg);
			}
			break;

		case 'd':
			if (alg) {
				fprintf(stderr,
					"%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear'  options permitted.\n",
					progname);
				exit(1);
			}
			alg = XF_DEL;
			if (debug) {
				fprintf(stdout, "%s: Algorithm %d selected.\n",
					progname,
					alg);
			}
			break;

		case 'c':
			if (alg) {
				fprintf(stderr,
					"%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear'  options permitted.\n",
					progname);
				exit(1);
			}
			alg = XF_CLR;
			if (debug) {
				fprintf(stdout, "%s: Algorithm %d selected.\n",
					progname,
					alg);
			}
			break;

		case 'e':
			if (said_opt) {
				fprintf(stderr,
					"%s: Error, EDST parameter redefined:%s, already defined in SA:%s\n",
					progname, optarg, said_opt);
				exit(1);
			}
			if (edst_opt) {
				fprintf(stderr,
					"%s: Error, EDST parameter redefined:%s, already defined as:%s\n",
					progname, optarg, edst_opt);
				exit(1);
			}
			error_s = ttoaddr(optarg, 0, address_family, &edst);
			if (error_s != NULL) {
				if (error_s) {
					fprintf(stderr,
						"%s: Error, %s converting --edst argument:%s\n",
						progname, error_s, optarg);
					exit(1);
				}
			}
			edst_opt = optarg;
			if (debug) {
				ipstr_buf b;

				fprintf(stdout, "%s: edst=%s.\n",
					progname,
					ipstr(&edst, &b));
			}
			break;

		case 's':
			if (said_opt != NULL) {
				fprintf(stderr,
					"%s: Error, SPI parameter redefined:%s, already defined in SA:%s\n",
					progname, optarg, said_opt);
				exit(1);
			}
			if (spi_opt != NULL) {
				fprintf(stderr,
					"%s: Error, SPI parameter redefined:%s, already defined as:%s\n",
					progname, optarg, spi_opt);
				exit(1);
			}
			ugh = ttoulb(optarg, 0, 0, 0xFFFFFFFFul, &u);
			if (ugh == NULL && u < 0x100)
				ugh = "0 - 0xFF are reserved";
			if (ugh != NULL) {
				fprintf(stderr,
					"%s: Invalid SPI parameter \"%s\": %s\n",
					progname, optarg, ugh);
				exit(1);
			}
			spi = u;
			spi_opt = optarg;
			break;

		case 'p':
			if (said_opt != NULL) {
				fprintf(stderr,
					"%s: Error, PROTO parameter redefined:%s, already defined in SA:%s\n",
					progname, optarg, said_opt);
				exit(1);
			}
			if (proto_opt != NULL) {
				fprintf(stderr,
					"%s: Error, PROTO parameter redefined:%s, already defined as:%s\n",
					progname, optarg, proto_opt);
				exit(1);
			}
			if (streq(optarg, "ah")) {
				proto = SA_AH;
			} else if (streq(optarg, "esp")) {
				proto = SA_ESP;
			} else if (streq(optarg, "tun")) {
				proto = SA_IPIP;
			} else if (streq(optarg, "comp")) {
				proto = SA_COMP;
			} else {
				fprintf(stderr,
					"%s: Invalid PROTO parameter: %s\n",
					progname, optarg);
				exit(1);
			}
			proto_opt = optarg;
			break;

		case 'a':
			if (said_opt) {
				fprintf(stderr,
					"%s: Error, ADDRESS FAMILY parameter redefined:%s, already defined in SA:%s\n",
					progname, optarg, said_opt);
				exit(1);
			}
			if (af_opt) {
				fprintf(stderr,
					"%s: Error, ADDRESS FAMILY parameter redefined:%s, already defined as:%s\n",
					progname, optarg, af_opt);
				exit(1);
			}
			if (streq(optarg, "inet")) {
				address_family = AF_INET;
				/* currently we ensure that all addresses belong to the same address family */
				anyaddr(address_family, &dst);
				anyaddr(address_family, &edst);
				anyaddr(address_family, &src);
			} else if (streq(optarg, "inet6")) {
				address_family = AF_INET6;
				/* currently we ensure that all addresses belong to the same address family */
				anyaddr(address_family, &dst);
				anyaddr(address_family, &edst);
				anyaddr(address_family, &src);
			} else {
				fprintf(stderr,
					"%s: Invalid ADDRESS FAMILY parameter: %s.\n",
					progname, optarg);
				exit(1);
			}
			af_opt = optarg;
			break;

		case 'I':
			if (said_opt) {
				fprintf(stderr,
					"%s: Error, SAID parameter redefined:%s, already defined in SA:%s\n",
					progname, optarg, said_opt);
				exit(1);
			}
			if (proto_opt) {
				fprintf(stderr,
					"%s: Error, PROTO parameter redefined in SA:%s, already defined as:%s\n",
					progname, optarg, proto_opt);
				exit(1);
			}
			if (edst_opt) {
				fprintf(stderr,
					"%s: Error, EDST parameter redefined in SA:%s, already defined as:%s\n",
					progname, optarg, edst_opt);
				exit(1);
			}
			if (spi_opt) {
				fprintf(stderr,
					"%s: Error, SPI parameter redefined in SA:%s, already defined as:%s\n",
					progname, optarg, spi_opt);
				exit(1);
			}
			error_s = ttosa(optarg, 0, &said);
			if (error_s != NULL) {
				fprintf(stderr,
					"%s: Error, %s converting --sa argument:%s\n",
					progname, error_s, optarg);
				exit(1);
			}
			if (debug) {
				satot(&said, 0, ipsaid_txt,
				      sizeof(ipsaid_txt));
				fprintf(stdout, "%s: said=%s.\n",
					progname,
					ipsaid_txt);
			}
			/* init the src and dst with the same address family */
			if (address_family == 0) {
				address_family = addrtypeof(&said.dst);
			} else if (address_family != addrtypeof(&said.dst)) {
				fprintf(stderr,
					"%s: Error, specified address family (%d) is different that of SAID: %s\n",
					progname, address_family, optarg);
				exit(1);
			}
			anyaddr(address_family, &dst);
			anyaddr(address_family, &edst);
			anyaddr(address_family, &src);
			said_opt = optarg;
			break;

		case 'A':
			decode_blob(optarg, "Authentication Key", &authkey, &authkeylen);
			break;

		case 'E':
			decode_blob(optarg, "Encryption Key", &enckey, &enckeylen);
			break;

		case 'w':
		{
			err_t ugh = ttoul(optarg, 0, 0, &replay_window);

			if (ugh != NULL) {
				fprintf(stderr,
					"%s: Invalid replay_window parameter: %s\n",
					progname, ugh);
				exit(1);
			}
			if (!(1 <= replay_window && replay_window <= 64)) {
				fprintf(stderr,
					"%s: Failed -- Illegal window size: arg=%s, replay_window=%lu, must be 1 <= size <= 64.\n",
					progname, optarg, replay_window);
				exit(1);
			}
		}
			break;

		case 'i':
			decode_blob(optarg, "IV", &iv, &ivlen);
			break;

		case 'D':
			if (dst_opt) {
				fprintf(stderr,
					"%s: Error, DST parameter redefined:%s, already defined as:%s\n",
					progname, optarg, dst_opt);
				exit(1);
			}
			error_s = ttoaddr(optarg, 0, address_family, &dst);
			if (error_s != NULL) {
				fprintf(stderr,
					"%s: Error, %s converting --dst argument:%s\n",
					progname, error_s, optarg);
				exit(1);
			}
			dst_opt = optarg;
			if (debug) {
				ipstr_buf b;

				fprintf(stdout, "%s: dst=%s.\n",
					progname,
					ipstr(&dst, &b));
			}
			break;

		case 'F':  /* src port */
			{
				unsigned long u;
				err_t ugh = ttoulb(optarg, 0, 0, 0xFFFF, &u);

				if (ugh != NULL) {
					fprintf(stderr,
						"%s: Invalid source port parameter \"%s\": %s\n",
						progname, optarg, ugh);
					exit(1);
				}
				sport = u;
			}
			break;

		case 'G':  /* dst port */
			{
				unsigned long u;
				err_t ugh = ttoulb(optarg, 0, 0, 0xFFFF, &u);

				if (ugh != NULL) {
					fprintf(stderr,
						"%s: Invalid destination port parameter \"%s\": %s\n",
						progname, optarg, ugh);
					exit(1);
				}
				dport = u;
			}
			break;

		case 'N':  /* nat-type */
			if (strcaseeq(optarg, "nonesp")) {
				natt = ESPINUDP_WITH_NON_ESP;
			} else if (strcaseeq(optarg, "none")) {
				natt = 0;
			} else {
				/* ??? what does this do?  Where is it documented? */
				unsigned long u;
				err_t ugh = ttoulb(optarg, 0, 0, 0xFFFFFFFFul, &u);

				if (ugh != NULL) {
					fprintf(stderr,
						"%s: Invalid character in natt parameter \"%s\": %s\n",
						progname, optarg, ugh);
					exit(1);
				}
				natt = u;
			}
			break;

		case 'S':
			if (src_opt) {
				fprintf(stderr,
					"%s: Error, SRC parameter redefined:%s, already defined as:%s\n",
					progname, optarg, src_opt);
				exit(1);
			}
			error_s = ttoaddr(optarg, 0, address_family, &src);
			if (error_s != NULL) {
				fprintf(stderr,
					"%s: Error, %s converting --src argument:%s\n",
					progname, error_s, optarg);
				exit(1);
			}
			src_opt = optarg;
			if (debug) {
				ipstr_buf b;

				fprintf(stdout, "%s: src=%s.\n",
					progname,
					ipstr(&src, &b));
			}
			break;

		case 'h':
			usage(progname, stdout);
			exit(0);

		case '?':
			usage(progname, stderr);
			exit(1);

		case 'v':
			fprintf(stdout, "%s, %s\n", progname,
				ipsec_version_code());
			exit(1);

		case 'f':
			if (parse_life_options(life,
					       life_opt,
					       optarg) != 0)
				exit(1);
			break;

		default:
			fprintf(stderr,
				"%s: unrecognized option '%c', update option processing.\n",
				progname, c);
			exit(1);
		}
	}
	if (debug) {
		fprintf(stdout, "%s: All options processed.\n",
			progname);
	}

	if (stat("/proc/net/pfkey", &sts) == 0) {
		fprintf(stderr,
			"%s: NETKEY does not use the ipsec spi command. Use 'ip xfrm' instead.\n",
			progname);
		exit(1);
	}

	if (argcount == 1) {
		int ret = 1;

		if ((stat("/proc/net/ipsec_spi", &sts)) != 0) {
			fprintf(stderr,
				"%s: No spi - no IPsec support in kernel (are the modules loaded?)\n",
				progname);
		} else {
			ret = system("cat /proc/net/ipsec_spi");
			ret = ret != -1 &&
			      WIFEXITED(ret) ? WEXITSTATUS(ret) : 1;
		}
		exit(ret);
	}

	switch (alg) {
	case XF_OTHER_ALG:
		/* validate keysizes */
		if (proc_read_ok) {
			const struct sadb_alg *alg_p;
			size_t keylen, minbits, maxbits;
			alg_p = kernel_alg_sadb_alg_get(SADB_SATYPE_ESP,
							SADB_EXT_SUPPORTED_ENCRYPT,
							esp_info->encryptalg);
			assert(alg_p != NULL);
			keylen = enckeylen * 8;

			minbits = alg_p->sadb_alg_minbits;
			maxbits = alg_p->sadb_alg_maxbits;
			/*
			 * if explicit keylen told in encrypt algo, eg "aes128"
			 * check actual keylen "equality"
			 */
			if (esp_info->enckeylen &&
			    esp_info->enckeylen != keylen) {
				fprintf(stderr, "%s: invalid encryption keylen=%d, "
					"required %d by encrypt algo string=\"%s\"\n",
					progname,
					(int)keylen,
					(int)esp_info->enckeylen,
					alg_string);
				exit(1);

			}
			/* thanks DES for this sh*t */

			if (minbits > keylen || maxbits < keylen) {
				fprintf(stderr, "%s: invalid encryption keylen=%d, "
					"must be between %d and %d bits\n",
					progname,
					(int)keylen,
					(int)minbits,
					(int)maxbits);
				exit(1);
			}
			alg_p = kernel_alg_sadb_alg_get(SADB_SATYPE_ESP,
							SADB_EXT_SUPPORTED_AUTH,
							esp_info->authalg);
			assert(alg_p);
			keylen = authkeylen * 8;
			minbits = alg_p->sadb_alg_minbits;
			maxbits = alg_p->sadb_alg_maxbits;
			if (minbits > keylen || maxbits < keylen) {
				fprintf(stderr, "%s: invalid auth keylen=%d, "
					"must be between %d and %d bits\n",
					progname,
					(int)keylen,
					(int)minbits,
					(int)maxbits);
				exit(1);
			}
		}
		/*
		 * ??? this break was added in a2791fda77a5cfcc6bc992fbc5019f4448112f88
		 * It is likely correct, but we're not sure.
		 * Luckily this code is probably never used.
		 */
		break;
	case XF_IP4:
	case XF_IP6:
	case XF_DEL:
	case XF_COMPDEFLATE:
	case XF_COMPLZS:
		if (!said_opt) {
			if (isanyaddr(&edst)) {
				fprintf(stderr,
					"%s: SA destination not specified.\n",
					progname);
				exit(1);
			}
			if (!spi) {
				fprintf(stderr, "%s: SA SPI not specified.\n",
					progname);
				exit(1);
			}
			if (!proto) {
				fprintf(stderr,
					"%s: SA PROTO not specified.\n",
					progname);
				exit(1);
			}
			initsaid(&edst, htonl(spi), proto, &said);
		} else {
			proto = said.proto;
			spi = ntohl(said.spi);
			edst = said.dst;
		}
		if ((address_family != 0) &&
		    (address_family != addrtypeof(&said.dst))) {
			fprintf(stderr,
				"%s: Defined address family and address family of SA missmatch.\n",
				progname);
			exit(1);
		}

		if (debug) {
			fprintf(stdout, "%s: SA valid.\n",
				progname);
		}
		break;
	case XF_CLR:
		break;
	default:
		fprintf(stderr,
			"%s: No action chosen.  See '%s --help' for usage.\n",
			progname, progname);
		exit(1);
	}

	switch (alg) {
	case XF_CLR:
	case XF_DEL:
	case XF_IP4:
	case XF_IP6:
	case XF_COMPDEFLATE:
	case XF_COMPLZS:
	case XF_OTHER_ALG:
		break;
	default:
		fprintf(stderr,
			"%s: No action chosen.  See '%s --help' for usage.\n",
			progname, progname);
		exit(1);
	}
	if (debug) {
		fprintf(stdout, "%s: Algorithm ok.\n",
			progname);
	}

	pfkey_sock = pfkey_open_sock_with_error();
	if (pfkey_sock < 0)
		exit(1);

	/* Build an SADB_ADD message to send down. */
	/* It needs <base, SA, address(SD), key(AE)> minimum. */
	/*   Lifetime(HS) could be added before addresses. */
	pfkey_extensions_init(extensions);

	error = pfkey_msg_hdr_build(&extensions[0],
				    alg == XF_DEL ? SADB_DELETE :
					alg == XF_CLR ? SADB_FLUSH :
					SADB_ADD,
				    proto2satype(proto),
				    0,
				    ++pfkey_seq,
				    mypid);
	if (error != 0) {
		fprintf(stderr,
			"%s: Trouble building message header, error=%d.\n",
			progname, error);
		pfkey_extensions_free(extensions);
		exit(1);
	}

	switch (alg) {
	case XF_OTHER_ALG:
		authalg = esp_info->authalg;
		if (debug) {
			fprintf(stdout, "%s: debug: authalg=%d\n",
				progname, authalg);
		}
		break;
	default:
		authalg = SADB_AALG_NONE;
	}
	switch (alg) {
	case XF_COMPDEFLATE:
		encryptalg = SADB_X_CALG_DEFLATE;
		break;
	case XF_COMPLZS:
		encryptalg = SADB_X_CALG_LZS;
		break;
	case XF_OTHER_ALG:
		encryptalg = esp_info->encryptalg;
		if (debug) {
			fprintf(stdout, "%s: debug: encryptalg=%d\n",
				progname, encryptalg);
		}
		break;
	default:
		encryptalg = SADB_EALG_NONE;
	}
	/* IE: pfkey_msg->sadb_msg_type == SADB_FLUSH */
	if (!(alg == XF_CLR)) {
		sab.sa_base.sadb_sa_len        = 0;
		sab.sa_base.sadb_sa_exttype    = SADB_EXT_SA;
		sab.sa_base.sadb_sa_spi        = htonl(spi);
		sab.sa_base.sadb_sa_replay     = replay_window;
		sab.sa_base.sadb_sa_state      = K_SADB_SASTATE_MATURE;
		sab.sa_base.sadb_sa_auth       = authalg;
		sab.sa_base.sadb_sa_encrypt    = encryptalg;
		sab.sa_base.sadb_sa_flags      = 0;
		sab.sa_base.sadb_x_sa_ref      = IPSEC_SAREF_NULL;
		sab.sa_base.sadb_x_reserved[0] = 0;
		sab.sa_base.sadb_x_reserved[1] = 0;
		sab.sa_base.sadb_x_reserved[2] = 0;
		sab.sa_base.sadb_x_reserved[3] = 0;

		error = pfkey_sa_builds(&extensions[SADB_EXT_SA], sab);
		if (error != 0) {
			fprintf(stderr,
				"%s: Trouble building sa extension, error=%d.\n",
				progname, error);
			pfkey_extensions_free(extensions);
			exit(1);
		}

		if (saref_me || saref_him) {
			error = pfkey_saref_build(&extensions[
							  K_SADB_X_EXT_SAREF],
						  saref_me, saref_him);
			if (error) {
				fprintf(stderr,
					"%s: Trouble building saref extension, error=%d.\n",
					progname, error);
				pfkey_extensions_free(extensions);
				exit(1);
			}
		}

		if (outif != 0) {
			error = pfkey_outif_build(&extensions[
							   SADB_X_EXT_PLUMBIF],
						  outif);
			if (error != 0) {
				fprintf(stderr,
					"%s: Trouble building outif extension, error=%d.\n",
					progname, error);
				pfkey_extensions_free(extensions);
				exit(1);
			}
		}

		if (debug) {
			fprintf(stdout,
				"%s: extensions[0]=0p%p previously set with msg_hdr.\n",
				progname,
				extensions[0]);
		}
		if (debug) {
			fprintf(stdout,
				"%s: assembled SA extension, pfkey msg authalg=%d encalg=%d.\n",
				progname,
				authalg,
				encryptalg);
		}

		if (debug) {
			int i, j;

			for (i = 0; i < life_maxsever; i++) {
				for (j = 0; j < life_maxtype; j++) {
					fprintf(stdout,
						"%s: i=%d, j=%d, life_opt[%d][%d]=0p%p, life[%d][%d]=%d\n",
						progname,
						i, j, i, j, life_opt[i][j], i, j,
						life[i][j]);
				}
			}
		}

		emit_lifetime("lifetime_s", SADB_EXT_LIFETIME_SOFT, extensions, life_opt[life_soft], life[life_soft]);
		emit_lifetime("lifetime_h", SADB_EXT_LIFETIME_HARD, extensions, life_opt[life_hard], life[life_hard]);

		if (debug) {
			ipstr_buf b;

			fprintf(stdout,
				"%s: assembling address_s extension (%s).\n",
				progname, ipstr(&src, &b));
		}

		error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC],
					    SADB_EXT_ADDRESS_SRC,
					    0,
					    0,
					    sockaddrof(&src));
		if (error != 0) {
			ipstr_buf b;

			fprintf(stderr,
				"%s: Trouble building address_s extension (%s), error=%d.\n",
				progname, ipstr(&src, &b), error);
			pfkey_extensions_free(extensions);
			exit(1);
		}

		error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST],
					    SADB_EXT_ADDRESS_DST,
					    0,
					    0,
					    sockaddrof(&edst));
		if (error != 0) {
			ipstr_buf b;

			fprintf(stderr,
				"%s: Trouble building address_d extension (%s), error=%d.\n",
				progname, ipstr(&edst, &b), error);
			pfkey_extensions_free(extensions);
			exit(1);
		}

		switch (alg) {
		/*	Allow no auth ... after all is local root decision 8)  */
		case XF_OTHER_ALG:
			if (!authalg)
				break;
			error = pfkey_key_build(&extensions[SADB_EXT_KEY_AUTH],
						SADB_EXT_KEY_AUTH,
						authkeylen * 8,
						authkey);
			if (error != 0) {
				fprintf(stderr,
					"%s: Trouble building key_a extension, error=%d.\n",
					progname, error);
				pfkey_extensions_free(extensions);
				exit(1);
			}
			if (debug) {
				fprintf(stdout,
					"%s: key_a extension assembled.\n",
					progname);
			}
			break;
		default:
			break;
		}

		switch (alg) {
		case XF_OTHER_ALG:
			if (enckeylen == 0) {
				if (debug)
					fprintf(stdout, "%s: key not provided (NULL alg?).\n",
						progname);
				break;

			}
			error = pfkey_key_build(&extensions[SADB_EXT_KEY_ENCRYPT],
						SADB_EXT_KEY_ENCRYPT,
						enckeylen * 8,
						enckey);
			if (error != 0) {
				fprintf(stderr,
					"%s: Trouble building key_e extension, error=%d.\n",
					progname, error);
				pfkey_extensions_free(extensions);
				exit(1);
			}
			if (debug) {
				fprintf(stdout,
					"%s: key_e extension assembled.\n",
					progname);
			}
			break;
		default:
			break;
		}

	}

	if (natt != 0) {
		bool success;

		int err;

		err = pfkey_x_nat_t_type_build(&extensions[
							 K_SADB_X_EXT_NAT_T_TYPE],
					       natt);
		success = pfkey_build(err,
				      "pfkey_nat_t_type Add ESP SA",
				      ipsaid_txt, extensions);
		if (!success)
			return FALSE;

		if (debug)
			fprintf(stderr, "setting natt_type to %d\n", natt);

		if (sport != 0) {
			err = pfkey_x_nat_t_port_build(
					&extensions[K_SADB_X_EXT_NAT_T_SPORT],
					K_SADB_X_EXT_NAT_T_SPORT,
					sport);
			success = pfkey_build(err,
					      "pfkey_nat_t_sport Add ESP SA",
					      ipsaid_txt, extensions);
			if (debug)
				fprintf(stderr, "setting natt_sport to %d\n",
					sport);
			if (!success)
				return FALSE;
		}

		if (dport != 0) {
			err = pfkey_x_nat_t_port_build(
					&extensions[K_SADB_X_EXT_NAT_T_DPORT],
					K_SADB_X_EXT_NAT_T_DPORT,
					dport);
			success = pfkey_build(err,
					      "pfkey_nat_t_dport Add ESP SA",
					      ipsaid_txt, extensions);
			if (debug)
				fprintf(stderr, "setting natt_dport to %d\n",
					dport);
			if (!success)
				return FALSE;
		}

#if 0
		/* not yet implemented */
		if (natt != 0 && !isanyaddr(&natt_oa)) {
			ip_str_buf b;

			success = pfkeyext_address(SADB_X_EXT_NAT_T_OA,
						   &natt_oa,
						   "pfkey_nat_t_oa Add ESP SA",
						   ipsaid_txt, extensions);
			if (debug)
				fprintf(stderr, "setting nat_oa to %s\n",
					ipstr(&natt_oa, &b));
			if (!success)
				return FALSE;
		}
#endif
	}

	if (debug) {
		fprintf(stdout, "%s: assembling pfkey msg....\n",
			progname);
	}
	error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_IN);
	if (error != 0) {
		fprintf(stderr,
			"%s: Trouble building pfkey message, error=%d.\n",
			progname, error);
		pfkey_extensions_free(extensions);
		pfkey_msg_free(&pfkey_msg);
		exit(1);
	}
	if (debug) {
		fprintf(stdout, "%s: assembled.\n",
			progname);
	}
	if (debug) {
		fprintf(stdout, "%s: writing pfkey msg.\n",
			progname);
	}
	io_error = write(pfkey_sock,
			 pfkey_msg,
			 pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
	if (io_error < 0) {
		fprintf(stderr, "%s: pfkey write failed (errno=%d): ",
			progname, errno);
		pfkey_extensions_free(extensions);
		pfkey_msg_free(&pfkey_msg);
		switch (errno) {
		case EACCES:
			fprintf(stderr, "access denied.  ");
			if (getuid() == 0)
				fprintf(stderr,
					"Check permissions.  Should be 600.\n");


			else
				fprintf(stderr,
					"You must be root to open this file.\n");


			break;
		case EUNATCH:
			fprintf(stderr,
				"Netlink not enabled OR KLIPS not loaded.\n");
			break;
		case EBUSY:
			fprintf(stderr,
				"KLIPS is busy.  Most likely a serious internal error occured in a previous command.  Please report as much detail as possible to development team.\n");
			break;
		case EINVAL:
			fprintf(stderr,
				"Invalid argument, check kernel log messages for specifics.\n");
			break;
		case ENODEV:
			fprintf(stderr, "KLIPS not loaded or enabled.\n");
			fprintf(stderr, "No device?!?\n");
			break;
		case ENOBUFS:
			fprintf(stderr, "No kernel memory to allocate SA.\n");
			break;
		case ESOCKTNOSUPPORT:
			fprintf(stderr,
				"Algorithm support not available in the kernel.  Please compile in support.\n");
			break;
		case EEXIST:
			fprintf(stderr,
				"SA already in use.  Delete old one first.\n");
			break;
		case ENOENT:
			fprintf(stderr,
				"device does not exist.  See Libreswan installation procedure.\n");
			break;
		case ENXIO:
		case ESRCH:
			fprintf(stderr,
				"SA does not exist.  Cannot delete.\n");
			break;
		case ENOSPC:
			fprintf(stderr,
				"no room in kernel SAref table.  Cannot process request.\n");
			break;
		case ESPIPE:
			fprintf(stderr,
				"kernel SAref table internal error.  Cannot process request.\n");
			break;
		default:
			fprintf(stderr,
				"Unknown socket write error %d (%s).  Please report as much detail as possible to development team.\n",
				errno, strerror(errno));
		}
		exit(1);
	} else if (io_error !=
		   (ssize_t)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN)) {
		fprintf(stderr, "%s: pfkey write truncated to %d bytes\n",
			progname, (int)io_error);
		pfkey_extensions_free(extensions);
		pfkey_msg_free(&pfkey_msg);
		exit(1);
	}

	if (debug) {
		fprintf(stdout, "%s: pfkey command written to socket.\n",
			progname);
	}

	if (pfkey_msg != NULL) {
		pfkey_extensions_free(extensions);
		pfkey_msg_free(&pfkey_msg);
	}
	if (debug) {
		fprintf(stdout, "%s: pfkey message buffer freed.\n",
			progname);
	}
	if (authkey != NULL) {
		memset(authkey, 0, authkeylen);
		free(authkey);
	}
	if (enckey != NULL) {
		memset(enckey, 0, enckeylen);
		free(enckey);
	}
	if (iv != NULL) {
		memset(iv, 0, ivlen);
		free(iv);
	}

	if (listenreply || saref_me || dumpsaref) {
		ssize_t readlen;
		unsigned char pfkey_buf[PFKEYv2_MAX_MSGSIZE];

		while ((readlen = read(pfkey_sock, pfkey_buf,
				     sizeof(pfkey_buf))) > 0) {
			struct sadb_ext *extensions[K_SADB_EXT_MAX + 1];
			pfkey_extensions_init(extensions);
			pfkey_msg = (struct sadb_msg *)pfkey_buf;

			/* first, see if we got enough for an sadb_msg */
			if ((size_t)readlen < sizeof(struct sadb_msg)) {
				if (debug) {
					printf("%s: runt packet of size: %ld (<%lu)\n",
						progname, (long)readlen,
						(unsigned long)sizeof(struct
								      sadb_msg));
				}
				continue;
			}

			/* okay, we got enough for a message, print it out */
			if (debug) {
				printf("%s: pfkey v%d msg received. type=%d(%s) seq=%d len=%d pid=%d errno=%d satype=%d(%s)\n",
					progname,
					pfkey_msg->sadb_msg_version,
					pfkey_msg->sadb_msg_type,
					pfkey_v2_sadb_type_string(pfkey_msg->
								  sadb_msg_type),
					pfkey_msg->sadb_msg_seq,
					pfkey_msg->sadb_msg_len,
					pfkey_msg->sadb_msg_pid,
					pfkey_msg->sadb_msg_errno,
					pfkey_msg->sadb_msg_satype,
					satype2name(pfkey_msg->sadb_msg_satype));
			}

			if (readlen !=
			    (ssize_t)(pfkey_msg->sadb_msg_len *
				      IPSEC_PFKEYv2_ALIGN)) {
				if (debug) {
					printf("%s: packet size read from socket=%d doesn't equal sadb_msg_len %u * %u; message not decoded\n",
						progname,
						(int)readlen,
						(unsigned)pfkey_msg->sadb_msg_len,
						(unsigned)IPSEC_PFKEYv2_ALIGN);
				}
				continue;
			}

			if (pfkey_msg_parse(pfkey_msg, NULL, extensions,
					    EXT_BITS_OUT)) {
				if (debug) {
					printf("%s: unparseable PF_KEY message.\n",
						progname);
				}
				continue;
			}

			if (debug) {
				printf("%s: parseable PF_KEY message.\n",
					progname);
			}
			if ((pid_t)pfkey_msg->sadb_msg_pid == mypid) {
				if (saref_me || dumpsaref) {
					struct sadb_x_saref *s =
						(struct sadb_x_saref *)
						extensions[
							K_SADB_X_EXT_SAREF];

					if (s != NULL) {
						printf("%s: saref=%d/%d\n",
						       progname,
						       s->sadb_x_saref_me,
						       s->sadb_x_saref_him);
					}
				}
				break;
			}
		}
	}
	(void) close(pfkey_sock);  /* close the socket */
	if (debug || listenreply)
		printf("%s: exited normally\n", progname);
	exit(0);
}