Edit

IABSD.fr/src/sbin/ipsecctl/parse.y

Branch :

  • Show log

    Commit

  • Author : dlg
    Date : 2023-08-07 04:10:08
    Hash : 8fd570bd
    Message : add support route based ipsec vpn negotiation with sec(4) via isakmpd. this adds "interface secX" to the grammar that you can use instead of specifying tunnel/transport modes and traffic selectors. if you have config like "ike interface sec0 local ... peer ...", ipsecctl will generate the right config for isakmpd to negotiate esp tunnels for all traffic between 0.0.0.0/0 and 0.0.0.0/0. however, this also specifies that they should be set up as interface SAs in the kernel for use with sec(4). this supports route-based instead of policy based ipsec encapsulation, and allows us to more easily operate with other vendors and products that also offer route-based vpns with opinions about the negotiated policy that doesnt fit with the SPD. support from many including markus@ tobhe@ claudio@ sthen@ patrick@ now is a good time deraadt@

  • sbin/ipsecctl/parse.y
  • /*	$OpenBSD: parse.y,v 1.183 2023/08/07 04:10:08 dlg Exp $	*/
    
    /*
     * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
     * Copyright (c) 2001 Markus Friedl.  All rights reserved.
     * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
     * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
     * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org>
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    %{
    #include <sys/types.h>
    #include <sys/ioctl.h>
    #include <sys/queue.h>
    #include <sys/socket.h>
    #include <sys/stat.h>
    #include <net/if.h>
    #include <netinet/in.h>
    #include <netinet/ip_ipsp.h>
    #include <arpa/inet.h>
    
    #include <ctype.h>
    #include <err.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <ifaddrs.h>
    #include <limits.h>
    #include <netdb.h>
    #include <stdarg.h>
    #include <stdio.h>
    #include <string.h>
    #include <syslog.h>
    #include <unistd.h>
    
    #include "ipsecctl.h"
    
    TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
    static struct file {
    	TAILQ_ENTRY(file)	 entry;
    	FILE			*stream;
    	char			*name;
    	int			 lineno;
    	int			 errors;
    } *file, *topfile;
    struct file	*pushfile(const char *, int);
    int		 popfile(void);
    int		 check_file_secrecy(int, const char *);
    int		 yyparse(void);
    int		 yylex(void);
    int		 yyerror(const char *, ...)
        __attribute__((__format__ (printf, 1, 2)))
        __attribute__((__nonnull__ (1)));
    int		 yywarn(const char *, ...)
        __attribute__((__format__ (printf, 1, 2)))
        __attribute__((__nonnull__ (1)));
    int		 kw_cmp(const void *, const void *);
    int		 lookup(char *);
    int		 lgetc(int);
    int		 lungetc(int);
    int		 findeol(void);
    
    TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
    struct sym {
    	TAILQ_ENTRY(sym)	 entry;
    	int			 used;
    	int			 persist;
    	char			*nam;
    	char			*val;
    };
    int		 symset(const char *, const char *, int);
    char		*symget(const char *);
    int		 cmdline_symset(char *);
    
    #define KEYSIZE_LIMIT	1024
    
    static struct ipsecctl	*ipsec = NULL;
    static int		 debug = 0;
    
    const struct ipsec_xf authxfs[] = {
    	{ "unknown",		AUTHXF_UNKNOWN,		0,	0 },
    	{ "none",		AUTHXF_NONE,		0,	0 },
    	{ "hmac-md5",		AUTHXF_HMAC_MD5,	16,	0 },
    	{ "hmac-ripemd160",	AUTHXF_HMAC_RIPEMD160,	20,	0 },
    	{ "hmac-sha1",		AUTHXF_HMAC_SHA1,	20,	0 },
    	{ "hmac-sha2-256",	AUTHXF_HMAC_SHA2_256,	32,	0 },
    	{ "hmac-sha2-384",	AUTHXF_HMAC_SHA2_384,	48,	0 },
    	{ "hmac-sha2-512",	AUTHXF_HMAC_SHA2_512,	64,	0 },
    	{ NULL,			0,			0,	0 },
    };
    
    const struct ipsec_xf encxfs[] = {
    	{ "unknown",		ENCXF_UNKNOWN,		0,	0,	0, 0 },
    	{ "none",		ENCXF_NONE,		0,	0,	0, 0 },
    	{ "3des-cbc",		ENCXF_3DES_CBC,		24,	24,	0, 0 },
    	{ "aes",		ENCXF_AES,		16,	32,	0, 0 },
    	{ "aes-128",		ENCXF_AES_128,		16,	16,	0, 0 },
    	{ "aes-192",		ENCXF_AES_192,		24,	24,	0, 0 },
    	{ "aes-256",		ENCXF_AES_256,		32,	32,	0, 0 },
    	{ "aesctr",		ENCXF_AESCTR,		16+4,	32+4,	0, 1 },
    	{ "aes-128-ctr",	ENCXF_AES_128_CTR,	16+4,	16+4,	0, 1 },
    	{ "aes-192-ctr",	ENCXF_AES_192_CTR,	24+4,	24+4,	0, 1 },
    	{ "aes-256-ctr",	ENCXF_AES_256_CTR,	32+4,	32+4,	0, 1 },
    	{ "aes-128-gcm",	ENCXF_AES_128_GCM,	16+4,	16+4,	1, 1 },
    	{ "aes-192-gcm",	ENCXF_AES_192_GCM,	24+4,	24+4,	1, 1 },
    	{ "aes-256-gcm",	ENCXF_AES_256_GCM,	32+4,	32+4,	1, 1 },
    	{ "aes-128-gmac",	ENCXF_AES_128_GMAC,	16+4,	16+4,	1, 1 },
    	{ "aes-192-gmac",	ENCXF_AES_192_GMAC,	24+4,	24+4,	1, 1 },
    	{ "aes-256-gmac",	ENCXF_AES_256_GMAC,	32+4,	32+4,	1, 1 },
    	{ "blowfish",		ENCXF_BLOWFISH,		5,	56,	0, 0 },
    	{ "cast128",		ENCXF_CAST128,		5,	16,	0, 0 },
    	{ "chacha20-poly1305",	ENCXF_CHACHA20_POLY1305, 32+4,	32+4,	1, 1 },
    	{ "null",		ENCXF_NULL,		0,	0,	0, 0 },
    	{ NULL,			0,			0,	0,	0, 0 },
    };
    
    const struct ipsec_xf compxfs[] = {
    	{ "unknown",		COMPXF_UNKNOWN,		0,	0 },
    	{ "deflate",		COMPXF_DEFLATE,		0,	0 },
    	{ NULL,			0,			0,	0 },
    };
    
    const struct ipsec_xf groupxfs[] = {
    	{ "unknown",		GROUPXF_UNKNOWN,	0,	0 },
    	{ "none",		GROUPXF_NONE,		0,	0 },
    	{ "modp768",		GROUPXF_1,		768,	0 },
    	{ "grp1",		GROUPXF_1,		768,	0 },
    	{ "modp1024",		GROUPXF_2,		1024,	0 },
    	{ "grp2",		GROUPXF_2,		1024,	0 },
    	{ "modp1536",		GROUPXF_5,		1536,	0 },
    	{ "grp5",		GROUPXF_5,		1536,	0 },
    	{ "modp2048",		GROUPXF_14,		2048,	0 },
    	{ "grp14",		GROUPXF_14,		2048,	0 },
    	{ "modp3072",		GROUPXF_15,		3072,	0 },
    	{ "grp15",		GROUPXF_15,		3072,	0 },
    	{ "modp4096",		GROUPXF_16,		4096,	0 },
    	{ "grp16",		GROUPXF_16,		4096,	0 },
    	{ "modp6144",		GROUPXF_17,		6144,	0 },
    	{ "grp17",		GROUPXF_17,		6144,	0 },
    	{ "modp8192",		GROUPXF_18,		8192,	0 },
    	{ "grp18",		GROUPXF_18,		8192,	0 },
    	{ "ecp256",		GROUPXF_19,		256,	0 },
    	{ "grp19",		GROUPXF_19,		256,	0 },
    	{ "ecp384",		GROUPXF_20,		384,	0 },
    	{ "grp20",		GROUPXF_20,		384,	0 },
    	{ "ecp521",		GROUPXF_21,		521,	0 },
    	{ "grp21",		GROUPXF_21,		521,	0 },
    	{ "ecp192",		GROUPXF_25,		192,	0 },
    	{ "grp25",		GROUPXF_25,		192,	0 },
    	{ "ecp224",		GROUPXF_26,		224,	0 },
    	{ "grp26",		GROUPXF_26,		224,	0 },
    	{ "bp224",		GROUPXF_27,		224,	0 },
    	{ "grp27",		GROUPXF_27,		224,	0 },
    	{ "bp256",		GROUPXF_28,		256,	0 },
    	{ "grp28",		GROUPXF_28,		256,	0 },
    	{ "bp384",		GROUPXF_29,		384,	0 },
    	{ "grp29",		GROUPXF_29,		384,	0 },
    	{ "bp512",		GROUPXF_30,		512,	0 },
    	{ "grp30",		GROUPXF_30,		512,	0 },
    	{ NULL,			0,			0,	0 },
    };
    
    int			 atoul(char *, u_long *);
    int			 atospi(char *, u_int32_t *);
    u_int8_t		 x2i(unsigned char *);
    struct ipsec_key	*parsekey(unsigned char *, size_t);
    struct ipsec_key	*parsekeyfile(char *);
    struct ipsec_addr_wrap	*host(const char *);
    struct ipsec_addr_wrap	*host_v6(const char *, int);
    struct ipsec_addr_wrap	*host_v4(const char *, int);
    struct ipsec_addr_wrap	*host_dns(const char *, int);
    struct ipsec_addr_wrap	*host_if(const char *, int);
    struct ipsec_addr_wrap	*host_any(void);
    void			 ifa_load(void);
    int			 ifa_exists(const char *);
    struct ipsec_addr_wrap	*ifa_lookup(const char *ifa_name);
    struct ipsec_addr_wrap	*ifa_grouplookup(const char *);
    void			 set_ipmask(struct ipsec_addr_wrap *, u_int8_t);
    const struct ipsec_xf	*parse_xf(const char *, const struct ipsec_xf *);
    struct ipsec_lifetime	*parse_life(const char *);
    struct ipsec_transforms *copytransforms(const struct ipsec_transforms *);
    struct ipsec_lifetime	*copylife(const struct ipsec_lifetime *);
    struct ipsec_auth	*copyipsecauth(const struct ipsec_auth *);
    struct ike_auth		*copyikeauth(const struct ike_auth *);
    struct ipsec_key	*copykey(struct ipsec_key *);
    struct ipsec_addr_wrap	*copyhost(const struct ipsec_addr_wrap *);
    char			*copytag(const char *);
    struct ipsec_rule	*copyrule(struct ipsec_rule *);
    int			 validate_af(struct ipsec_addr_wrap *,
    			     struct ipsec_addr_wrap *);
    int			 validate_sa(u_int32_t, u_int8_t,
    			     struct ipsec_transforms *, struct ipsec_key *,
    			     struct ipsec_key *, u_int8_t);
    struct ipsec_rule	*create_sa(u_int8_t, u_int8_t, struct ipsec_hosts *,
    			     u_int32_t, u_int8_t, u_int16_t,
    			     struct ipsec_transforms *,
    			     struct ipsec_key *, struct ipsec_key *);
    struct ipsec_rule	*reverse_sa(struct ipsec_rule *, u_int32_t,
    			     struct ipsec_key *, struct ipsec_key *);
    struct ipsec_rule	*create_sabundle(struct ipsec_addr_wrap *, u_int8_t,
    			     u_int32_t, struct ipsec_addr_wrap *, u_int8_t,
    			     u_int32_t);
    struct ipsec_rule	*create_flow(u_int8_t, u_int8_t, struct ipsec_hosts *,
    			     u_int8_t, char *, char *, u_int8_t);
    int			 set_rule_peers(struct ipsec_rule *r,
    			     struct ipsec_hosts *peers);
    void			 expand_any(struct ipsec_addr_wrap *);
    int			 expand_rule(struct ipsec_rule *, struct ipsec_hosts *,
    			     u_int8_t, u_int32_t, struct ipsec_key *,
    			     struct ipsec_key *, char *);
    struct ipsec_rule	*reverse_rule(struct ipsec_rule *);
    struct ipsec_rule	*create_ike(u_int8_t, struct ipsec_hosts *,
    			     struct ike_mode *, struct ike_mode *, u_int8_t,
    			     u_int8_t, u_int8_t, char *, char *,
    			     struct ike_auth *, char *);
    int			 add_sabundle(struct ipsec_rule *, char *);
    int			 get_id_type(char *);
    
    struct ipsec_transforms *ipsec_transforms;
    
    typedef struct {
    	union {
    		int64_t	 	 number;
    		uint32_t	 unit;
    		u_int8_t	 ikemode;
    		u_int8_t	 dir;
    		u_int8_t	 satype;	/* encapsulating prococol */
    		u_int8_t	 proto;		/* encapsulated protocol */
    		u_int8_t	 tmode;
    		char		*string;
    		u_int16_t	 port;
    		struct ipsec_hosts hosts;
    		struct ipsec_hosts peers;
    		struct ipsec_addr_wrap *anyhost;
    		struct ipsec_addr_wrap *singlehost;
    		struct ipsec_addr_wrap *host;
    		struct {
    			char *srcid;
    			char *dstid;
    		} ids;
    		char		*id;
    		u_int8_t	 type;
    		struct ike_auth	 ikeauth;
    		struct {
    			u_int32_t	spiout;
    			u_int32_t	spiin;
    		} spis;
    		struct {
    			u_int8_t	encap;
    			u_int16_t	port;
    		} udpencap;
    		struct {
    			struct ipsec_key *keyout;
    			struct ipsec_key *keyin;
    		} authkeys;
    		struct {
    			struct ipsec_key *keyout;
    			struct ipsec_key *keyin;
    		} enckeys;
    		struct {
    			struct ipsec_key *keyout;
    			struct ipsec_key *keyin;
    		} keys;
    		struct ipsec_transforms *transforms;
    		struct ipsec_lifetime	*life;
    		struct ike_mode		*mode;
    	} v;
    	int lineno;
    } YYSTYPE;
    
    %}
    
    %token	FLOW FROM ESP AH IN PEER ON OUT TO SRCID DSTID RSA PSK TCPMD5 SPI
    %token	AUTHKEY ENCKEY FILENAME AUTHXF ENCXF ERROR IKE MAIN QUICK AGGRESSIVE
    %token	PASSIVE ACTIVE ANY IPIP IPCOMP COMPXF TUNNEL TRANSPORT DYNAMIC LIFETIME
    %token	TYPE DENY BYPASS LOCAL PROTO USE ACQUIRE REQUIRE DONTACQ GROUP PORT TAG
    %token	INCLUDE BUNDLE UDPENCAP INTERFACE
    %token	<v.string>		STRING
    %token	<v.number>		NUMBER
    %type	<v.unit>		iface
    %type	<v.string>		string
    %type	<v.dir>			dir
    %type	<v.satype>		satype
    %type	<v.proto>		proto
    %type	<v.number>		protoval
    %type	<v.tmode>		tmode
    %type	<v.hosts>		hosts
    %type	<v.port>		port
    %type	<v.number>		portval
    %type	<v.peers>		peers
    %type	<v.anyhost>		anyhost
    %type	<v.singlehost>		singlehost
    %type	<v.host>		host host_list host_spec
    %type	<v.ids>			ids
    %type	<v.id>			id
    %type	<v.spis>		spispec
    %type	<v.udpencap>		udpencap
    %type	<v.authkeys>		authkeyspec
    %type	<v.enckeys>		enckeyspec
    %type	<v.string>		bundlestring
    %type	<v.keys>		keyspec
    %type	<v.transforms>		transforms
    %type	<v.ikemode>		ikemode
    %type	<v.ikeauth>		ikeauth
    %type	<v.type>		type
    %type	<v.life>		lifetime
    %type	<v.mode>		phase1mode phase2mode
    %type	<v.string>		tag
    %%
    
    grammar		: /* empty */
    		| grammar include '\n'
    		| grammar '\n'
    		| grammar ikerule '\n'
    		| grammar flowrule '\n'
    		| grammar sarule '\n'
    		| grammar tcpmd5rule '\n'
    		| grammar varset '\n'
    		| grammar error '\n'		{ file->errors++; }
    		;
    
    comma		: ','
    		| /* empty */
    		;
    
    include		: INCLUDE STRING		{
    			struct file	*nfile;
    
    			if ((nfile = pushfile($2, 0)) == NULL) {
    				yyerror("failed to include file %s", $2);
    				free($2);
    				YYERROR;
    			}
    			free($2);
    
    			file = nfile;
    			lungetc('\n');
    		}
    		;
    
    tcpmd5rule	: TCPMD5 hosts spispec authkeyspec	{
    			struct ipsec_rule	*r;
    
    			r = create_sa(IPSEC_TCPMD5, IPSEC_TRANSPORT, &$2,
    			    $3.spiout, 0, 0, NULL, $4.keyout, NULL);
    			if (r == NULL)
    				YYERROR;
    
    			if (expand_rule(r, NULL, 0, $3.spiin, $4.keyin, NULL,
    			    NULL))
    				errx(1, "tcpmd5rule: expand_rule");
    		}
    		;
    
    sarule		: satype tmode hosts spispec udpencap transforms authkeyspec
    		    enckeyspec bundlestring {
    			struct ipsec_rule	*r;
    
    			r = create_sa($1, $2, &$3, $4.spiout, $5.encap, $5.port,
    			    $6, $7.keyout, $8.keyout);
    			if (r == NULL)
    				YYERROR;
    
    			if (expand_rule(r, NULL, 0, $4.spiin, $7.keyin,
    			    $8.keyin, $9))
    				errx(1, "sarule: expand_rule");
    		}
    		;
    
    flowrule	: FLOW satype dir proto hosts peers ids type {
    			struct ipsec_rule	*r;
    
    			r = create_flow($3, $4, &$5, $2, $7.srcid,
    			    $7.dstid, $8);
    			if (r == NULL)
    				YYERROR;
    
    			if (expand_rule(r, &$6, $3, 0, NULL, NULL, NULL))
    				errx(1, "flowrule: expand_rule");
    		}
    		;
    
    ikerule		: IKE ikemode satype tmode proto hosts peers
    		    phase1mode phase2mode ids ikeauth tag {
    			struct ipsec_rule	*r;
    
    			r = create_ike($5, &$6, $8, $9, $3, $4, $2,
    			    $10.srcid, $10.dstid, &$11, $12);
    			if (r == NULL)
    				YYERROR;
    
    			if (expand_rule(r, &$7, 0, 0, NULL, NULL, NULL))
    				errx(1, "ikerule: expand_rule");
    		}
    
    		/* ike interface sec0 local $h_self peer $h_s2s1 ... */
    		| IKE ikemode iface peers
    		    phase1mode phase2mode ids ikeauth {
    			uint8_t			 proto = 0; // IPPROTO_IPIP;
    			struct ipsec_hosts	 hosts;
    			struct ike_mode		*phase1mode = $5;
    			struct ike_mode		*phase2mode = $6;
    			uint8_t			 satype = IPSEC_ESP;
    			uint8_t			 tmode = IPSEC_TUNNEL;
    			uint8_t			 mode = $2;
    			struct ike_auth		*authtype = &$8;
    			char			*tag = NULL;
    
    			struct ipsec_rule	*r;
    
    			hosts.src = host_v4("0.0.0.0/0", 1);
    			hosts.sport = htons(0);
    			hosts.dst = host_v4("0.0.0.0/0", 1);
    			hosts.dport = htons(0);
    
    			r = create_ike(proto, &hosts, phase1mode, phase2mode,
    			    satype, tmode, mode, $7.srcid, $7.dstid,
    			    authtype, tag);
    			if (r == NULL) {
    				YYERROR;
    			}
    
    			r->flags |= IPSEC_RULE_F_IFACE;
    			r->iface = $3;
    
    			if (expand_rule(r, &$4, 0, 0, NULL, NULL, NULL))
    				errx(1, "ikerule: expand interface rule");
    
    		}
    		;
    
    satype		: /* empty */			{ $$ = IPSEC_ESP; }
    		| ESP				{ $$ = IPSEC_ESP; }
    		| AH				{ $$ = IPSEC_AH; }
    		| IPCOMP			{ $$ = IPSEC_IPCOMP; }
    		| IPIP				{ $$ = IPSEC_IPIP; }
    		;
    
    proto		: /* empty */			{ $$ = 0; }
    		| PROTO protoval		{ $$ = $2; }
    		| PROTO ESP 			{ $$ = IPPROTO_ESP; }
    		| PROTO AH			{ $$ = IPPROTO_AH; }
    		;
    
    protoval	: STRING			{
    			struct protoent *p;
    
    			p = getprotobyname($1);
    			if (p == NULL) {
    				yyerror("unknown protocol: %s", $1);
    				YYERROR;
    			}
    			$$ = p->p_proto;
    			free($1);
    		}
    		| NUMBER			{
    			if ($1 > 255 || $1 < 0) {
    				yyerror("protocol outside range");
    				YYERROR;
    			}
    		}
    		;
    
    tmode		: /* empty */			{ $$ = IPSEC_TUNNEL; }
    		| TUNNEL			{ $$ = IPSEC_TUNNEL; }
    		| TRANSPORT			{ $$ = IPSEC_TRANSPORT; }
    		;
    
    dir		: /* empty */			{ $$ = IPSEC_INOUT; }
    		| IN				{ $$ = IPSEC_IN; }
    		| OUT				{ $$ = IPSEC_OUT; }
    		;
    
    hosts		: FROM host port TO host port		{
    			struct ipsec_addr_wrap *ipa;
    			for (ipa = $5; ipa; ipa = ipa->next) {
    				if (ipa->srcnat) {
    					yyerror("no flow NAT support for"
    					    " destination network: %s", ipa->name);
    					YYERROR;
    				}
    			}
    			$$.src = $2;
    			$$.sport = $3;
    			$$.dst = $5;
    			$$.dport = $6;
    		}
    		| TO host port FROM host port		{
    			struct ipsec_addr_wrap *ipa;
    			for (ipa = $2; ipa; ipa = ipa->next) {
    				if (ipa->srcnat) {
    					yyerror("no flow NAT support for"
    					    " destination network: %s", ipa->name);
    					YYERROR;
    				}
    			}
    			$$.src = $5;
    			$$.sport = $6;
    			$$.dst = $2;
    			$$.dport = $3;
    		}
    		;
    
    port		: /* empty */				{ $$ = 0; }
    		| PORT portval				{ $$ = $2; }
    		;
    
    portval		: STRING				{
    			struct servent *s;
    
    			if ((s = getservbyname($1, "tcp")) != NULL ||
    			    (s = getservbyname($1, "udp")) != NULL) {
    				$$ = s->s_port;
    			} else {
    				yyerror("unknown port: %s", $1);
    				YYERROR;
    			}
    		}
    		| NUMBER				{
    			if ($1 > USHRT_MAX || $1 < 0) {
    				yyerror("port outside range");
    				YYERROR;
    			}
    			$$ = htons($1);
    		}
    		;
    
    peers		: /* empty */				{
    			$$.dst = NULL;
    			$$.src = NULL;
    		}
    		| PEER anyhost LOCAL singlehost		{
    			$$.dst = $2;
    			$$.src = $4;
    		}
    		| LOCAL singlehost PEER anyhost		{
    			$$.dst = $4;
    			$$.src = $2;
    		}
    		| PEER anyhost				{
    			$$.dst = $2;
    			$$.src = NULL;
    		}
    		| LOCAL singlehost			{
    			$$.dst = NULL;
    			$$.src = $2;
    		}
    		;
    
    anyhost		: singlehost			{ $$ = $1; }
    		| ANY				{
    			$$ = host_any();
    		}
    
    singlehost	: /* empty */			{ $$ = NULL; }
    		| STRING			{
    			if (($$ = host($1)) == NULL) {
    				free($1);
    				yyerror("could not parse host specification");
    				YYERROR;
    			}
    			free($1);
    		}
    		;
    
    host_list	: host				{ $$ = $1; }
    		| host_list comma host		{
    			if ($3 == NULL)
    				$$ = $1;
    			else if ($1 == NULL)
    				$$ = $3;
    			else {
    				$1->tail->next = $3;
    				$1->tail = $3->tail;
    				$$ = $1;
    			}
    		}
    		;
    
    host_spec	: STRING			{
    			if (($$ = host($1)) == NULL) {
    				free($1);
    				yyerror("could not parse host specification");
    				YYERROR;
    			}
    			free($1);
    		}
    		| STRING '/' NUMBER		{
    			char	*buf;
    
    			if (asprintf(&buf, "%s/%lld", $1, $3) == -1)
    				err(1, "host: asprintf");
    			free($1);
    			if (($$ = host(buf)) == NULL)	{
    				free(buf);
    				yyerror("could not parse host specification");
    				YYERROR;
    			}
    			free(buf);
    		}
    		;
    
    host		: host_spec			{ $$ = $1; }
    		| host_spec '(' host_spec ')'   {
    			if ($3->af != $1->af) {
    				yyerror("Flow NAT address family mismatch");
    				YYERROR;
    			}
    			$$ = $1;
    			$$->srcnat = $3;
    		}
    		| ANY				{
    			$$ = host_any();
    		}
    		| '{' host_list '}'		{ $$ = $2; }
    		;
    
    ids		: /* empty */			{
    			$$.srcid = NULL;
    			$$.dstid = NULL;
    		}
    		| SRCID id DSTID id		{
    			$$.srcid = $2;
    			$$.dstid = $4;
    		}
    		| SRCID id			{
    			$$.srcid = $2;
    			$$.dstid = NULL;
    		}
    		| DSTID id			{
    			$$.srcid = NULL;
    			$$.dstid = $2;
    		}
    		;
    
    type		: /* empty */			{
    			$$ = TYPE_UNKNOWN;
    		}
    		| TYPE USE			{
    			$$ = TYPE_USE;
    		}
    		| TYPE ACQUIRE			{
    			$$ = TYPE_ACQUIRE;
    		}
    		| TYPE REQUIRE			{
    			$$ = TYPE_REQUIRE;
    		}
    		| TYPE DENY			{
    			$$ = TYPE_DENY;
    		}
    		| TYPE BYPASS			{
    			$$ = TYPE_BYPASS;
    		}
    		| TYPE DONTACQ			{
    			$$ = TYPE_DONTACQ;
    		}
    		;
    
    id		: STRING			{ $$ = $1; }
    		;
    
    spispec		: SPI STRING			{
    			u_int32_t	 spi;
    			char		*p = strchr($2, ':');
    
    			if (p != NULL) {
    				*p++ = 0;
    
    				if (atospi(p, &spi) == -1) {
    					free($2);
    					YYERROR;
    				}
    				$$.spiin = spi;
    			} else
    				$$.spiin = 0;
    
    			if (atospi($2, &spi) == -1) {
    				free($2);
    				YYERROR;
    			}
    			$$.spiout = spi;
    
    
    			free($2);
    		}
    		| SPI NUMBER			{
    			if ($2 > UINT_MAX || $2 < 0) {
    				yyerror("%lld not a valid spi", $2);
    				YYERROR;
    			}
    			if ($2 >= SPI_RESERVED_MIN && $2 <= SPI_RESERVED_MAX) {
    				yyerror("%lld within reserved spi range", $2);
    				YYERROR;
    			}
    
    			$$.spiin = 0;
    			$$.spiout = $2;
    		}
    		;
    
    udpencap	: /* empty */				{
    			$$.encap = 0;
    		}
    		| UDPENCAP				{
    			$$.encap = 1;
    			$$.port = 0;
    		}
    		| UDPENCAP PORT NUMBER			{
    			$$.encap = 1;
    			$$.port = $3;
    		}
    		;
    
    transforms	:					{
    			if ((ipsec_transforms = calloc(1,
    			    sizeof(struct ipsec_transforms))) == NULL)
    				err(1, "transforms: calloc");
    		}
    		    transforms_l
    			{ $$ = ipsec_transforms; }
    		| /* empty */				{
    			if (($$ = calloc(1,
    			    sizeof(struct ipsec_transforms))) == NULL)
    				err(1, "transforms: calloc");
    		}
    		;
    
    transforms_l	: transforms_l transform
    		| transform
    		;
    
    transform	: AUTHXF STRING			{
    			if (ipsec_transforms->authxf)
    				yyerror("auth already set");
    			else {
    				ipsec_transforms->authxf = parse_xf($2,
    				    authxfs);
    				if (!ipsec_transforms->authxf)
    					yyerror("%s not a valid transform", $2);
    			}
    		}
    		| ENCXF STRING			{
    			if (ipsec_transforms->encxf)
    				yyerror("enc already set");
    			else {
    				ipsec_transforms->encxf = parse_xf($2, encxfs);
    				if (!ipsec_transforms->encxf)
    					yyerror("%s not a valid transform", $2);
    			}
    		}
    		| COMPXF STRING			{
    			if (ipsec_transforms->compxf)
    				yyerror("comp already set");
    			else {
    				ipsec_transforms->compxf = parse_xf($2,
    				    compxfs);
    				if (!ipsec_transforms->compxf)
    					yyerror("%s not a valid transform", $2);
    			}
    		}
    		| GROUP STRING			{
    			if (ipsec_transforms->groupxf)
    				yyerror("group already set");
    			else {
    				ipsec_transforms->groupxf = parse_xf($2,
    				    groupxfs);
    				if (!ipsec_transforms->groupxf)
    					yyerror("%s not a valid transform", $2);
    			}
    		}
    		;
    
    phase1mode	: /* empty */	{
    			struct ike_mode		*p1;
    
    			/* We create just an empty main mode */
    			if ((p1 = calloc(1, sizeof(struct ike_mode))) == NULL)
    				err(1, "phase1mode: calloc");
    			p1->ike_exch = IKE_MM;
    			$$ = p1;
    		}
    		| MAIN transforms lifetime		{
    			struct ike_mode *p1;
    
    			if ((p1 = calloc(1, sizeof(struct ike_mode))) == NULL)
    				err(1, "phase1mode: calloc");
    			p1->xfs = $2;
    			p1->life = $3;
    			p1->ike_exch = IKE_MM;
    			$$ = p1;
    		}
    		| AGGRESSIVE transforms lifetime	{
    			struct ike_mode	*p1;
    
    			if ((p1 = calloc(1, sizeof(struct ike_mode))) == NULL)
    				err(1, "phase1mode: calloc");
    			p1->xfs = $2;
    			p1->life = $3;
    			p1->ike_exch = IKE_AM;
    			$$ = p1;
    		}
    		;
    
    phase2mode	: /* empty */	{
    			struct ike_mode		*p2;
    
    			/* We create just an empty quick mode */
    			if ((p2 = calloc(1, sizeof(struct ike_mode))) == NULL)
    				err(1, "phase2mode: calloc");
    			p2->ike_exch = IKE_QM;
    			$$ = p2;
    		}
    		| QUICK transforms lifetime	{
    			struct ike_mode	*p2;
    
    			if ((p2 = calloc(1, sizeof(struct ike_mode))) == NULL)
    				err(1, "phase2mode: calloc");
    			p2->xfs = $2;
    			p2->life = $3;
    			p2->ike_exch = IKE_QM;
    			$$ = p2;
    		}
    		;
    
    lifetime	: /* empty */			{
    			struct ipsec_lifetime *life;
    
    			/* We create just an empty transform */
    			if ((life = calloc(1, sizeof(struct ipsec_lifetime)))
    			    == NULL)
    				err(1, "life: calloc");
    			life->lt_seconds = -1;
    			life->lt_bytes = -1;
    			$$ = life;
    		}
    		| LIFETIME NUMBER		{
    			struct ipsec_lifetime *life;
    
    			if ((life = calloc(1, sizeof(struct ipsec_lifetime)))
    			    == NULL)
    				err(1, "life: calloc");
    			life->lt_seconds = $2;
    			life->lt_bytes = -1;
    			$$ = life;
    		}
    		| LIFETIME STRING		{
    			$$ = parse_life($2);
    		}
    		;
    
    authkeyspec	: /* empty */			{
    			$$.keyout = NULL;
    			$$.keyin = NULL;
    		}
    		| AUTHKEY keyspec		{
    			$$.keyout = $2.keyout;
    			$$.keyin = $2.keyin;
    		}
    		;
    
    enckeyspec	: /* empty */			{
    			$$.keyout = NULL;
    			$$.keyin = NULL;
    		}
    		| ENCKEY keyspec		{
    			$$.keyout = $2.keyout;
    			$$.keyin = $2.keyin;
    		}
    		;
    
    bundlestring	: /* empty */			{ $$ = NULL; }
    		| BUNDLE STRING			{ $$ = $2; }
    		;
    
    keyspec		: STRING			{
    			unsigned char	*hex;
    			unsigned char	*p = strchr($1, ':');
    
    			if (p != NULL ) {
    				*p++ = 0;
    
    				if (!strncmp(p, "0x", 2))
    					p += 2;
    				$$.keyin = parsekey(p, strlen(p));
    			} else
    				$$.keyin = NULL;
    
    			hex = $1;
    			if (!strncmp(hex, "0x", 2))
    				hex += 2;
    			$$.keyout = parsekey(hex, strlen(hex));
    
    			free($1);
    		}
    		| FILENAME STRING		{
    			unsigned char	*p = strchr($2, ':');
    
    			if (p != NULL) {
    				*p++ = 0;
    				$$.keyin = parsekeyfile(p);
    			}
    			$$.keyout = parsekeyfile($2);
    			free($2);
    		}
    		;
    
    ikemode		: /* empty */			{ $$ = IKE_ACTIVE; }
    		| PASSIVE			{ $$ = IKE_PASSIVE; }
    		| DYNAMIC			{ $$ = IKE_DYNAMIC; }
    		| ACTIVE			{ $$ = IKE_ACTIVE; }
    		;
    
    ikeauth		: /* empty */			{
    			$$.type = IKE_AUTH_RSA;
    			$$.string = NULL;
    		}
    		| RSA				{
    			$$.type = IKE_AUTH_RSA;
    			$$.string = NULL;
    		}
    		| PSK STRING			{
    			$$.type = IKE_AUTH_PSK;
    			if (($$.string = strdup($2)) == NULL)
    				err(1, "ikeauth: strdup");
    		}
    		;
    
    tag		: /* empty */
    		{
    			$$ = NULL;
    		}
    		| TAG STRING
    		{
    			$$ = $2;
    		}
    		;
    
    iface		: INTERFACE STRING		{
    			static const char prefix[] = "sec";
    			const char *errstr = NULL;
    			size_t len, plen;
    
    			plen = strlen(prefix);
    			len = strlen($2);
    
    			if (len <= plen || memcmp($2, prefix, plen) != 0) {
    				yyerror("invalid %s interface name", prefix);
    				free($2);
    				YYERROR;
    			}
    
    			$$ = strtonum($2 + plen, 0, UINT_MAX, &errstr);
    			free($2);
    			if (errstr != NULL) {
    				yyerror("invalid %s interface unit: %s",
    				    prefix, errstr);
    				YYERROR;
    			}
    		}
    		;
    
    string		: string STRING
    		{
    			if (asprintf(&$$, "%s %s", $1, $2) == -1)
    				err(1, "string: asprintf");
    			free($1);
    			free($2);
    		}
    		| STRING
    		;
    
    varset		: STRING '=' string
    		{
    			char *s = $1;
    			if (ipsec->opts & IPSECCTL_OPT_VERBOSE)
    				printf("%s = \"%s\"\n", $1, $3);
    			while (*s++) {
    				if (isspace((unsigned char)*s)) {
    					yyerror("macro name cannot contain "
    					    "whitespace");
    					free($1);
    					free($3);
    					YYERROR;
    				}
    			}
    			if (symset($1, $3, 0) == -1)
    				err(1, "cannot store variable");
    			free($1);
    			free($3);
    		}
    		;
    
    %%
    
    struct keywords {
    	const char	*k_name;
    	int		 k_val;
    };
    
    int
    yyerror(const char *fmt, ...)
    {
    	va_list		 ap;
    
    	file->errors++;
    	va_start(ap, fmt);
    	fprintf(stderr, "%s: %d: ", file->name, yylval.lineno);
    	vfprintf(stderr, fmt, ap);
    	fprintf(stderr, "\n");
    	va_end(ap);
    	return (0);
    }
    
    int
    yywarn(const char *fmt, ...)
    {
    	va_list		 ap;
    
    	va_start(ap, fmt);
    	fprintf(stderr, "%s: %d: ", file->name, yylval.lineno);
    	vfprintf(stderr, fmt, ap);
    	fprintf(stderr, "\n");
    	va_end(ap);
    	return (0);
    }
    
    int
    kw_cmp(const void *k, const void *e)
    {
    	return (strcmp(k, ((const struct keywords *)e)->k_name));
    }
    
    int
    lookup(char *s)
    {
    	/* this has to be sorted always */
    	static const struct keywords keywords[] = {
    		{ "acquire",		ACQUIRE },
    		{ "active",		ACTIVE },
    		{ "aggressive",		AGGRESSIVE },
    		{ "ah",			AH },
    		{ "any",		ANY },
    		{ "auth",		AUTHXF },
    		{ "authkey",		AUTHKEY },
    		{ "bundle",		BUNDLE },
    		{ "bypass",		BYPASS },
    		{ "comp",		COMPXF },
    		{ "deny",		DENY },
    		{ "dontacq",		DONTACQ },
    		{ "dstid",		DSTID },
    		{ "dynamic",		DYNAMIC },
    		{ "enc",		ENCXF },
    		{ "enckey",		ENCKEY },
    		{ "esp",		ESP },
    		{ "file",		FILENAME },
    		{ "flow",		FLOW },
    		{ "from",		FROM },
    		{ "group",		GROUP },
    		{ "ike",		IKE },
    		{ "in",			IN },
    		{ "include",		INCLUDE },
    		{ "interface",		INTERFACE },
    		{ "ipcomp",		IPCOMP },
    		{ "ipip",		IPIP },
    		{ "lifetime",		LIFETIME },
    		{ "local",		LOCAL },
    		{ "main",		MAIN },
    		{ "out",		OUT },
    		{ "passive",		PASSIVE },
    		{ "peer",		PEER },
    		{ "port",		PORT },
    		{ "proto",		PROTO },
    		{ "psk",		PSK },
    		{ "quick",		QUICK },
    		{ "require",		REQUIRE },
    		{ "rsa",		RSA },
    		{ "spi",		SPI },
    		{ "srcid",		SRCID },
    		{ "tag",		TAG },
    		{ "tcpmd5",		TCPMD5 },
    		{ "to",			TO },
    		{ "transport",		TRANSPORT },
    		{ "tunnel",		TUNNEL },
    		{ "type",		TYPE },
    		{ "udpencap",		UDPENCAP },
    		{ "use",		USE }
    	};
    	const struct keywords	*p;
    
    	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
    	    sizeof(keywords[0]), kw_cmp);
    
    	if (p) {
    		if (debug > 1)
    			fprintf(stderr, "%s: %d\n", s, p->k_val);
    		return (p->k_val);
    	} else {
    		if (debug > 1)
    			fprintf(stderr, "string: %s\n", s);
    		return (STRING);
    	}
    }
    
    #define MAXPUSHBACK	128
    
    char	*parsebuf;
    int	 parseindex;
    char	 pushback_buffer[MAXPUSHBACK];
    int	 pushback_index = 0;
    
    int
    lgetc(int quotec)
    {
    	int		c, next;
    
    	if (parsebuf) {
    		/* Read character from the parsebuffer instead of input. */
    		if (parseindex >= 0) {
    			c = (unsigned char)parsebuf[parseindex++];
    			if (c != '\0')
    				return (c);
    			parsebuf = NULL;
    		} else
    			parseindex++;
    	}
    
    	if (pushback_index)
    		return ((unsigned char)pushback_buffer[--pushback_index]);
    
    	if (quotec) {
    		if ((c = getc(file->stream)) == EOF) {
    			yyerror("reached end of file while parsing quoted string");
    			if (file == topfile || popfile() == EOF)
    				return (EOF);
    			return (quotec);
    		}
    		return (c);
    	}
    
    	while ((c = getc(file->stream)) == '\\') {
    		next = getc(file->stream);
    		if (next != '\n') {
    			c = next;
    			break;
    		}
    		yylval.lineno = file->lineno;
    		file->lineno++;
    	}
    
    	while (c == EOF) {
    		if (file == topfile || popfile() == EOF)
    			return (EOF);
    		c = getc(file->stream);
    	}
    	return (c);
    }
    
    int
    lungetc(int c)
    {
    	if (c == EOF)
    		return (EOF);
    	if (parsebuf) {
    		parseindex--;
    		if (parseindex >= 0)
    			return (c);
    	}
    	if (pushback_index + 1 >= MAXPUSHBACK)
    		return (EOF);
    	pushback_buffer[pushback_index++] = c;
    	return (c);
    }
    
    int
    findeol(void)
    {
    	int	c;
    
    	parsebuf = NULL;
    
    	/* skip to either EOF or the first real EOL */
    	while (1) {
    		if (pushback_index)
    			c = (unsigned char)pushback_buffer[--pushback_index];
    		else
    			c = lgetc(0);
    		if (c == '\n') {
    			file->lineno++;
    			break;
    		}
    		if (c == EOF)
    			break;
    	}
    	return (ERROR);
    }
    
    int
    yylex(void)
    {
    	char	 buf[8096];
    	char	*p, *val;
    	int	 quotec, next, c;
    	int	 token;
    
    top:
    	p = buf;
    	while ((c = lgetc(0)) == ' ' || c == '\t')
    		; /* nothing */
    
    	yylval.lineno = file->lineno;
    	if (c == '#')
    		while ((c = lgetc(0)) != '\n' && c != EOF)
    			; /* nothing */
    	if (c == '$' && parsebuf == NULL) {
    		while (1) {
    			if ((c = lgetc(0)) == EOF)
    				return (0);
    
    			if (p + 1 >= buf + sizeof(buf) - 1) {
    				yyerror("string too long");
    				return (findeol());
    			}
    			if (isalnum(c) || c == '_') {
    				*p++ = c;
    				continue;
    			}
    			*p = '\0';
    			lungetc(c);
    			break;
    		}
    		val = symget(buf);
    		if (val == NULL) {
    			yyerror("macro '%s' not defined", buf);
    			return (findeol());
    		}
    		parsebuf = val;
    		parseindex = 0;
    		goto top;
    	}
    
    	switch (c) {
    	case '\'':
    	case '"':
    		quotec = c;
    		while (1) {
    			if ((c = lgetc(quotec)) == EOF)
    				return (0);
    			if (c == '\n') {
    				file->lineno++;
    				continue;
    			} else if (c == '\\') {
    				if ((next = lgetc(quotec)) == EOF)
    					return (0);
    				if (next == quotec || next == ' ' ||
    				    next == '\t')
    					c = next;
    				else if (next == '\n') {
    					file->lineno++;
    					continue;
    				} else
    					lungetc(next);
    			} else if (c == quotec) {
    				*p = '\0';
    				break;
    			} else if (c == '\0') {
    				yyerror("syntax error");
    				return (findeol());
    			}
    			if (p + 1 >= buf + sizeof(buf) - 1) {
    				yyerror("string too long");
    				return (findeol());
    			}
    			*p++ = c;
    		}
    		yylval.v.string = strdup(buf);
    		if (yylval.v.string == NULL)
    			err(1, "%s", __func__);
    		return (STRING);
    	}
    
    #define allowed_to_end_number(x) \
    	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
    
    	if (c == '-' || isdigit(c)) {
    		do {
    			*p++ = c;
    			if ((size_t)(p-buf) >= sizeof(buf)) {
    				yyerror("string too long");
    				return (findeol());
    			}
    		} while ((c = lgetc(0)) != EOF && isdigit(c));
    		lungetc(c);
    		if (p == buf + 1 && buf[0] == '-')
    			goto nodigits;
    		if (c == EOF || allowed_to_end_number(c)) {
    			const char *errstr = NULL;
    
    			*p = '\0';
    			yylval.v.number = strtonum(buf, LLONG_MIN,
    			    LLONG_MAX, &errstr);
    			if (errstr) {
    				yyerror("\"%s\" invalid number: %s",
    				    buf, errstr);
    				return (findeol());
    			}
    			return (NUMBER);
    		} else {
    nodigits:
    			while (p > buf + 1)
    				lungetc((unsigned char)*--p);
    			c = (unsigned char)*--p;
    			if (c == '-')
    				return (c);
    		}
    	}
    
    #define allowed_in_string(x) \
    	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
    	x != '{' && x != '}' && x != '<' && x != '>' && \
    	x != '!' && x != '=' && x != '/' && x != '#' && \
    	x != ','))
    
    	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
    		do {
    			*p++ = c;
    			if ((size_t)(p-buf) >= sizeof(buf)) {
    				yyerror("string too long");
    				return (findeol());
    			}
    		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
    		lungetc(c);
    		*p = '\0';
    		if ((token = lookup(buf)) == STRING)
    			if ((yylval.v.string = strdup(buf)) == NULL)
    				err(1, "%s", __func__);
    		return (token);
    	}
    	if (c == '\n') {
    		yylval.lineno = file->lineno;
    		file->lineno++;
    	}
    	if (c == EOF)
    		return (0);
    	return (c);
    }
    
    int
    check_file_secrecy(int fd, const char *fname)
    {
    	struct stat	st;
    
    	if (fstat(fd, &st)) {
    		warn("cannot stat %s", fname);
    		return (-1);
    	}
    	if (st.st_uid != 0 && st.st_uid != getuid()) {
    		warnx("%s: owner not root or current user", fname);
    		return (-1);
    	}
    	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
    		warnx("%s: group writable or world read/writable", fname);
    		return (-1);
    	}
    	return (0);
    }
    
    struct file *
    pushfile(const char *name, int secret)
    {
    	struct file	*nfile;
    
    	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
    		warn("%s", __func__);
    		return (NULL);
    	}
    	if ((nfile->name = strdup(name)) == NULL) {
    		warn("%s", __func__);
    		free(nfile);
    		return (NULL);
    	}
    	if (TAILQ_FIRST(&files) == NULL && strcmp(nfile->name, "-") == 0) {
    		nfile->stream = stdin;
    		free(nfile->name);
    		if ((nfile->name = strdup("stdin")) == NULL) {
    			warn("%s", __func__);
    			free(nfile);
    			return (NULL);
    		}
    	} else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
    		warn("%s: %s", __func__, nfile->name);
    		free(nfile->name);
    		free(nfile);
    		return (NULL);
    	} else if (secret &&
    	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
    		fclose(nfile->stream);
    		free(nfile->name);
    		free(nfile);
    		return (NULL);
    	}
    	nfile->lineno = 1;
    	TAILQ_INSERT_TAIL(&files, nfile, entry);
    	return (nfile);
    }
    
    int
    popfile(void)
    {
    	struct file	*prev;
    
    	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
    		prev->errors += file->errors;
    
    	TAILQ_REMOVE(&files, file, entry);
    	fclose(file->stream);
    	free(file->name);
    	free(file);
    	file = prev;
    
    	return (file ? 0 : EOF);
    }
    
    int
    parse_rules(const char *filename, struct ipsecctl *ipsecx)
    {
    	struct sym	*sym;
    	int		 errors = 0;
    
    	ipsec = ipsecx;
    
    	if ((file = pushfile(filename, 1)) == NULL) {
    		return (-1);
    	}
    	topfile = file;
    
    	yyparse();
    	errors = file->errors;
    	popfile();
    
    	/* Free macros and check which have not been used. */
    	while ((sym = TAILQ_FIRST(&symhead))) {
    		if ((ipsec->opts & IPSECCTL_OPT_VERBOSE2) && !sym->used)
    			fprintf(stderr, "warning: macro '%s' not "
    			    "used\n", sym->nam);
    		free(sym->nam);
    		free(sym->val);
    		TAILQ_REMOVE(&symhead, sym, entry);
    		free(sym);
    	}
    
    	return (errors ? -1 : 0);
    }
    
    int
    symset(const char *nam, const char *val, int persist)
    {
    	struct sym	*sym;
    
    	TAILQ_FOREACH(sym, &symhead, entry) {
    		if (strcmp(nam, sym->nam) == 0)
    			break;
    	}
    
    	if (sym != NULL) {
    		if (sym->persist == 1)
    			return (0);
    		else {
    			free(sym->nam);
    			free(sym->val);
    			TAILQ_REMOVE(&symhead, sym, entry);
    			free(sym);
    		}
    	}
    	if ((sym = calloc(1, sizeof(*sym))) == NULL)
    		return (-1);
    
    	sym->nam = strdup(nam);
    	if (sym->nam == NULL) {
    		free(sym);
    		return (-1);
    	}
    	sym->val = strdup(val);
    	if (sym->val == NULL) {
    		free(sym->nam);
    		free(sym);
    		return (-1);
    	}
    	sym->used = 0;
    	sym->persist = persist;
    	TAILQ_INSERT_TAIL(&symhead, sym, entry);
    	return (0);
    }
    
    int
    cmdline_symset(char *s)
    {
    	char	*sym, *val;
    	int	ret;
    
    	if ((val = strrchr(s, '=')) == NULL)
    		return (-1);
    
    	sym = strndup(s, val - s);
    	if (sym == NULL)
    		err(1, "%s", __func__);
    	ret = symset(sym, val + 1, 1);
    	free(sym);
    
    	return (ret);
    }
    
    char *
    symget(const char *nam)
    {
    	struct sym	*sym;
    
    	TAILQ_FOREACH(sym, &symhead, entry) {
    		if (strcmp(nam, sym->nam) == 0) {
    			sym->used = 1;
    			return (sym->val);
    		}
    	}
    	return (NULL);
    }
    
    int
    atoul(char *s, u_long *ulvalp)
    {
    	u_long	 ulval;
    	char	*ep;
    
    	errno = 0;
    	ulval = strtoul(s, &ep, 0);
    	if (s[0] == '\0' || *ep != '\0')
    		return (-1);
    	if (errno == ERANGE && ulval == ULONG_MAX)
    		return (-1);
    	*ulvalp = ulval;
    	return (0);
    }
    
    int
    atospi(char *s, u_int32_t *spivalp)
    {
    	unsigned long	ulval;
    
    	if (atoul(s, &ulval) == -1)
    		return (-1);
    	if (ulval > UINT_MAX) {
    		yyerror("%lu not a valid spi", ulval);
    		return (-1);
    	}
    	if (ulval >= SPI_RESERVED_MIN && ulval <= SPI_RESERVED_MAX) {
    		yyerror("%lu within reserved spi range", ulval);
    		return (-1);
    	}
    	*spivalp = ulval;
    	return (0);
    }
    
    u_int8_t
    x2i(unsigned char *s)
    {
    	char	ss[3];
    
    	ss[0] = s[0];
    	ss[1] = s[1];
    	ss[2] = 0;
    
    	if (!isxdigit(s[0]) || !isxdigit(s[1])) {
    		yyerror("keys need to be specified in hex digits");
    		return (-1);
    	}
    	return ((u_int8_t)strtoul(ss, NULL, 16));
    }
    
    struct ipsec_key *
    parsekey(unsigned char *hexkey, size_t len)
    {
    	struct ipsec_key *key;
    	int		  i;
    
    	key = calloc(1, sizeof(struct ipsec_key));
    	if (key == NULL)
    		err(1, "%s", __func__);
    
    	key->len = len / 2;
    	key->data = calloc(key->len, sizeof(u_int8_t));
    	if (key->data == NULL)
    		err(1, "%s", __func__);
    
    	for (i = 0; i < (int)key->len; i++)
    		key->data[i] = x2i(hexkey + 2 * i);
    
    	return (key);
    }
    
    struct ipsec_key *
    parsekeyfile(char *filename)
    {
    	struct stat	 sb;
    	int		 fd;
    	unsigned char	*hex;
    
    	if ((fd = open(filename, O_RDONLY)) < 0)
    		err(1, "open %s", filename);
    	if (fstat(fd, &sb) < 0)
    		err(1, "parsekeyfile: stat %s", filename);
    	if ((sb.st_size > KEYSIZE_LIMIT) || (sb.st_size == 0))
    		errx(1, "%s: key too %s", filename, sb.st_size ? "large" :
    		    "small");
    	if ((hex = calloc(sb.st_size, sizeof(unsigned char))) == NULL)
    		err(1, "%s", __func__);
    	if (read(fd, hex, sb.st_size) < sb.st_size)
    		err(1, "parsekeyfile: read");
    	close(fd);
    	return (parsekey(hex, sb.st_size));
    }
    
    int
    get_id_type(char *string)
    {
    	struct in6_addr ia;
    
    	if (string == NULL)
    		return (ID_UNKNOWN);
    
    	if (inet_pton(AF_INET, string, &ia) == 1)
    		return (ID_IPV4);
    	else if (inet_pton(AF_INET6, string, &ia) == 1)
    		return (ID_IPV6);
    	else if (strchr(string, '@'))
    		return (ID_UFQDN);
    	else
    		return (ID_FQDN);
    }
    
    struct ipsec_addr_wrap *
    host(const char *s)
    {
    	struct ipsec_addr_wrap	*ipa = NULL;
    	int			 mask, cont = 1;
    	char			*p, *q, *ps;
    
    	if ((p = strrchr(s, '/')) != NULL) {
    		errno = 0;
    		mask = strtol(p + 1, &q, 0);
    		if (errno == ERANGE || !q || *q || mask > 128 || q == (p + 1))
    			errx(1, "host: invalid netmask '%s'", p);
    		if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
    			err(1, "%s", __func__);
    		strlcpy(ps, s, strlen(s) - strlen(p) + 1);
    	} else {
    		if ((ps = strdup(s)) == NULL)
    			err(1, "%s", __func__);
    		mask = -1;
    	}
    
    	/* Does interface with this name exist? */
    	if (cont && (ipa = host_if(ps, mask)) != NULL)
    		cont = 0;
    
    	/* IPv4 address? */
    	if (cont && (ipa = host_v4(s, mask == -1 ? 32 : mask)) != NULL)
    		cont = 0;
    
    	/* IPv6 address? */
    	if (cont && (ipa = host_v6(ps, mask == -1 ? 128 : mask)) != NULL)
    		cont = 0;
    
    	/* dns lookup */
    	if (cont && mask == -1 && (ipa = host_dns(s, mask)) != NULL)
    		cont = 0;
    	free(ps);
    
    	if (ipa == NULL || cont == 1) {
    		fprintf(stderr, "no IP address found for %s\n", s);
    		return (NULL);
    	}
    	return (ipa);
    }
    
    struct ipsec_addr_wrap *
    host_v6(const char *s, int prefixlen)
    {
    	struct ipsec_addr_wrap	*ipa = NULL;
    	struct addrinfo		 hints, *res;
    	char			 hbuf[NI_MAXHOST];
    
    	bzero(&hints, sizeof(struct addrinfo));
    	hints.ai_family = AF_INET6;
    	hints.ai_socktype = SOCK_STREAM;
    	hints.ai_flags = AI_NUMERICHOST;
    	if (getaddrinfo(s, NULL, &hints, &res))
    		return (NULL);
    	if (res->ai_next)
    		err(1, "host_v6: numeric hostname expanded to multiple item");
    
    	ipa = calloc(1, sizeof(struct ipsec_addr_wrap));
    	if (ipa == NULL)
    		err(1, "%s", __func__);
    	ipa->af = res->ai_family;
    	memcpy(&ipa->address.v6,
    	    &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
    	    sizeof(struct in6_addr));
    	if (prefixlen > 128)
    		prefixlen = 128;
    	ipa->next = NULL;
    	ipa->tail = ipa;
    
    	set_ipmask(ipa, prefixlen);
    	if (getnameinfo(res->ai_addr, res->ai_addrlen,
    	    hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) {
    		errx(1, "could not get a numeric hostname");
    	}
    
    	if (prefixlen != 128) {
    		ipa->netaddress = 1;
    		if (asprintf(&ipa->name, "%s/%d", hbuf, prefixlen) == -1)
    			err(1, "%s", __func__);
    	} else {
    		if ((ipa->name = strdup(hbuf)) == NULL)
    			err(1, "%s", __func__);
    	}
    
    	freeaddrinfo(res);
    
    	return (ipa);
    }
    
    struct ipsec_addr_wrap *
    host_v4(const char *s, int mask)
    {
    	struct ipsec_addr_wrap	*ipa = NULL;
    	struct in_addr		 ina;
    	int			 bits = 32;
    
    	bzero(&ina, sizeof(struct in_addr));
    	if (strrchr(s, '/') != NULL) {
    		if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1)
    			return (NULL);
    	} else {
    		if (inet_pton(AF_INET, s, &ina) != 1)
    			return (NULL);
    	}
    
    	ipa = calloc(1, sizeof(struct ipsec_addr_wrap));
    	if (ipa == NULL)
    		err(1, "%s", __func__);
    
    	ipa->address.v4 = ina;
    	ipa->name = strdup(s);
    	if (ipa->name == NULL)
    		err(1, "%s", __func__);
    	ipa->af = AF_INET;
    	ipa->next = NULL;
    	ipa->tail = ipa;
    
    	set_ipmask(ipa, bits);
    	if (strrchr(s, '/') != NULL)
    		ipa->netaddress = 1;
    
    	return (ipa);
    }
    
    struct ipsec_addr_wrap *
    host_dns(const char *s, int mask)
    {
    	struct ipsec_addr_wrap	*ipa = NULL, *head = NULL;
    	struct addrinfo		 hints, *res0, *res;
    	int			 error;
    	char			 hbuf[NI_MAXHOST];
    
    	bzero(&hints, sizeof(struct addrinfo));
    	hints.ai_family = PF_UNSPEC;
    	hints.ai_socktype = SOCK_STREAM;
    	error = getaddrinfo(s, NULL, &hints, &res0);
    	if (error)
    		return (NULL);
    
    	for (res = res0; res; res = res->ai_next) {
    		if (res->ai_family != AF_INET && res->ai_family != AF_INET6)
    			continue;
    
    		ipa = calloc(1, sizeof(struct ipsec_addr_wrap));
    		if (ipa == NULL)
    			err(1, "%s", __func__);
    		switch (res->ai_family) {
    		case AF_INET:
    			memcpy(&ipa->address.v4,
    			    &((struct sockaddr_in *)res->ai_addr)->sin_addr,
    			    sizeof(struct in_addr));
    			break;
    		case AF_INET6:
    			/* XXX we do not support scoped IPv6 address yet */
    			if (((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id) {
    				free(ipa);
    				continue;
    			}
    			memcpy(&ipa->address.v6,
    			    &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
    			    sizeof(struct in6_addr));
    			break;
    		}
    		error = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
    		    sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
    		if (error)
    			err(1, "host_dns: getnameinfo");
    		ipa->name = strdup(hbuf);
    		if (ipa->name == NULL)
    			err(1, "%s", __func__);
    		ipa->af = res->ai_family;
    		ipa->next = NULL;
    		ipa->tail = ipa;
    		if (head == NULL)
    			head = ipa;
    		else {
    			head->tail->next = ipa;
    			head->tail = ipa;
    		}
    
    		/*
    		 * XXX for now, no netmask support for IPv6.
    		 * but since there's no way to specify address family, once you
    		 * have IPv6 address on a host, you cannot use dns/netmask
    		 * syntax.
    		 */
    		if (ipa->af == AF_INET)
    			set_ipmask(ipa, mask == -1 ? 32 : mask);
    		else
    			if (mask != -1)
    				err(1, "host_dns: cannot apply netmask "
    				    "on non-IPv4 address");
    	}
    	freeaddrinfo(res0);
    
    	return (head);
    }
    
    struct ipsec_addr_wrap *
    host_if(const char *s, int mask)
    {
    	struct ipsec_addr_wrap *ipa = NULL;
    
    	if (ifa_exists(s))
    		ipa = ifa_lookup(s);
    
    	return (ipa);
    }
    
    struct ipsec_addr_wrap *
    host_any(void)
    {
    	struct ipsec_addr_wrap	*ipa;
    
    	ipa = calloc(1, sizeof(struct ipsec_addr_wrap));
    	if (ipa == NULL)
    		err(1, "%s", __func__);
    	ipa->af = AF_UNSPEC;
    	ipa->netaddress = 1;
    	ipa->tail = ipa;
    	return (ipa);
    }
    
    /* interface lookup routintes */
    
    struct ipsec_addr_wrap	*iftab;
    
    void
    ifa_load(void)
    {
    	struct ifaddrs		*ifap, *ifa;
    	struct ipsec_addr_wrap	*n = NULL, *h = NULL;
    
    	if (getifaddrs(&ifap) < 0)
    		err(1, "ifa_load: getifaddrs");
    
    	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
    		if (ifa->ifa_addr == NULL ||
    		    !(ifa->ifa_addr->sa_family == AF_INET ||
    		    ifa->ifa_addr->sa_family == AF_INET6 ||
    		    ifa->ifa_addr->sa_family == AF_LINK))
    			continue;
    		n = calloc(1, sizeof(struct ipsec_addr_wrap));
    		if (n == NULL)
    			err(1, "%s", __func__);
    		n->af = ifa->ifa_addr->sa_family;
    		if ((n->name = strdup(ifa->ifa_name)) == NULL)
    			err(1, "%s", __func__);
    		if (n->af == AF_INET) {
    			n->af = AF_INET;
    			memcpy(&n->address.v4, &((struct sockaddr_in *)
    			    ifa->ifa_addr)->sin_addr,
    			    sizeof(struct in_addr));
    			memcpy(&n->mask.v4, &((struct sockaddr_in *)
    			    ifa->ifa_netmask)->sin_addr,
    			    sizeof(struct in_addr));
    		} else if (n->af == AF_INET6) {
    			n->af = AF_INET6;
    			memcpy(&n->address.v6, &((struct sockaddr_in6 *)
    			    ifa->ifa_addr)->sin6_addr,
    			    sizeof(struct in6_addr));
    			memcpy(&n->mask.v6, &((struct sockaddr_in6 *)
    			    ifa->ifa_netmask)->sin6_addr,
    			    sizeof(struct in6_addr));
    		}
    		n->next = NULL;
    		n->tail = n;
    		if (h == NULL)
    			h = n;
    		else {
    			h->tail->next = n;
    			h->tail = n;
    		}
    	}
    
    	iftab = h;
    	freeifaddrs(ifap);
    }
    
    int
    ifa_exists(const char *ifa_name)
    {
    	struct ipsec_addr_wrap	*n;
    	struct ifgroupreq	 ifgr;
    	int			 s;
    
    	if (iftab == NULL)
    		ifa_load();
    
    	/* check whether this is a group */
    	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    		err(1, "ifa_exists: socket");
    	bzero(&ifgr, sizeof(ifgr));
    	strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name));
    	if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == 0) {
    		close(s);
    		return (1);
    	}
    	close(s);
    
    	for (n = iftab; n; n = n->next) {
    		if (n->af == AF_LINK && !strncmp(n->name, ifa_name,
    		    IFNAMSIZ))
    			return (1);
    	}
    
    	return (0);
    }
    
    struct ipsec_addr_wrap *
    ifa_grouplookup(const char *ifa_name)
    {
    	struct ifg_req		*ifg;
    	struct ifgroupreq	 ifgr;
    	int			 s;
    	size_t			 len;
    	struct ipsec_addr_wrap	*n, *h = NULL, *hn;
    
    	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    		err(1, "socket");
    	bzero(&ifgr, sizeof(ifgr));
    	strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name));
    	if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) {
    		close(s);
    		return (NULL);
    	}
    
    	len = ifgr.ifgr_len;
    	if ((ifgr.ifgr_groups = calloc(1, len)) == NULL)
    		err(1, "%s", __func__);
    	if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1)
    		err(1, "ioctl");
    
    	for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req);
    	    ifg++) {
    		len -= sizeof(struct ifg_req);
    		if ((n = ifa_lookup(ifg->ifgrq_member)) == NULL)
    			continue;
    		if (h == NULL)
    			h = n;
    		else {
    			for (hn = h; hn->next != NULL; hn = hn->next)
    				;	/* nothing */
    			hn->next = n;
    			n->tail = hn;
    		}
    	}
    	free(ifgr.ifgr_groups);
    	close(s);
    
    	return (h);
    }
    
    struct ipsec_addr_wrap *
    ifa_lookup(const char *ifa_name)
    {
    	struct ipsec_addr_wrap	*p = NULL, *h = NULL, *n = NULL;
    
    	if (iftab == NULL)
    		ifa_load();
    
    	if ((n = ifa_grouplookup(ifa_name)) != NULL)
    		return (n);
    
    	for (p = iftab; p; p = p->next) {
    		if (p->af != AF_INET && p->af != AF_INET6)
    			continue;
    		if (strncmp(p->name, ifa_name, IFNAMSIZ))
    			continue;
    		n = calloc(1, sizeof(struct ipsec_addr_wrap));
    		if (n == NULL)
    			err(1, "%s", __func__);
    		memcpy(n, p, sizeof(struct ipsec_addr_wrap));
    		if ((n->name = strdup(p->name)) == NULL)
    			err(1, "%s", __func__);
    		switch (n->af) {
    		case AF_INET:
    			set_ipmask(n, 32);
    			break;
    		case AF_INET6:
    			/* route/show.c and bgpd/util.c give KAME credit */
    			if (IN6_IS_ADDR_LINKLOCAL(&n->address.v6)) {
    				u_int16_t tmp16;
    				/* for now we can not handle link local,
    				 * therefore bail for now
    				 */
    				free(n);
    				continue;
    
    				memcpy(&tmp16, &n->address.v6.s6_addr[2],
    				    sizeof(tmp16));
    				/* use this when we support link-local
    				 * n->??.scopeid = ntohs(tmp16);
    				 */
    				n->address.v6.s6_addr[2] = 0;
    				n->address.v6.s6_addr[3] = 0;
    			}
    			set_ipmask(n, 128);
    			break;
    		}
    
    		n->next = NULL;
    		n->tail = n;
    		if (h == NULL)
    			h = n;
    		else {
    			h->tail->next = n;
    			h->tail = n;
    		}
    	}
    
    	return (h);
    }
    
    void
    set_ipmask(struct ipsec_addr_wrap *address, u_int8_t b)
    {
    	struct ipsec_addr	*ipa;
    	int			 i, j = 0;
    
    	ipa = &address->mask;
    	bzero(ipa, sizeof(struct ipsec_addr));
    
    	while (b >= 32) {
    		ipa->addr32[j++] = 0xffffffff;
    		b -= 32;
    	}
    	for (i = 31; i > 31 - b; --i)
    		ipa->addr32[j] |= (1 << i);
    	if (b)
    		ipa->addr32[j] = htonl(ipa->addr32[j]);
    }
    
    const struct ipsec_xf *
    parse_xf(const char *name, const struct ipsec_xf xfs[])
    {
    	int		i;
    
    	for (i = 0; xfs[i].name != NULL; i++) {
    		if (strncmp(name, xfs[i].name, strlen(name)))
    			continue;
    		return &xfs[i];
    	}
    	return (NULL);
    }
    
    struct ipsec_lifetime *
    parse_life(const char *value)
    {
    	struct ipsec_lifetime	*life;
    	int			ret;
    	int			seconds = 0;
    	char			unit = 0;
    
    	ret = sscanf(value, "%d%c", &seconds, &unit);
    	if (ret == 2) {
    		switch (tolower((unsigned char)unit)) {
    		case 'm':
    			seconds *= 60;
    			break;
    		case 'h':
    			seconds *= 60 * 60;
    			break;
    		default:
    			err(1, "invalid time unit");
    		}
    	} else if (ret != 1)
    		err(1, "invalid time specification: %s", value);
    
    	life = calloc(1, sizeof(struct ipsec_lifetime));
    	if (life == NULL)
    		err(1, "%s", __func__);
    
    	life->lt_seconds = seconds;
    	life->lt_bytes = -1;
    
    	return (life);
    }
    
    struct ipsec_transforms *
    copytransforms(const struct ipsec_transforms *xfs)
    {
    	struct ipsec_transforms *newxfs;
    
    	if (xfs == NULL)
    		return (NULL);
    
    	newxfs = calloc(1, sizeof(struct ipsec_transforms));
    	if (newxfs == NULL)
    		err(1, "%s", __func__);
    
    	memcpy(newxfs, xfs, sizeof(struct ipsec_transforms));
    	return (newxfs);
    }
    
    struct ipsec_lifetime *
    copylife(const struct ipsec_lifetime *life)
    {
    	struct ipsec_lifetime *newlife;
    
    	if (life == NULL)
    		return (NULL);
    
    	newlife = calloc(1, sizeof(struct ipsec_lifetime));
    	if (newlife == NULL)
    		err(1, "%s", __func__);
    
    	memcpy(newlife, life, sizeof(struct ipsec_lifetime));
    	return (newlife);
    }
    
    struct ipsec_auth *
    copyipsecauth(const struct ipsec_auth *auth)
    {
    	struct ipsec_auth	*newauth;
    
    	if (auth == NULL)
    		return (NULL);
    
    	if ((newauth = calloc(1, sizeof(struct ipsec_auth))) == NULL)
    		err(1, "%s", __func__);
    	if (auth->srcid &&
    	    asprintf(&newauth->srcid, "%s", auth->srcid) == -1)
    		err(1, "%s", __func__);
    	if (auth->dstid &&
    	    asprintf(&newauth->dstid, "%s", auth->dstid) == -1)
    		err(1, "%s", __func__);
    
    	newauth->srcid_type = auth->srcid_type;
    	newauth->dstid_type = auth->dstid_type;
    	newauth->type = auth->type;
    
    	return (newauth);
    }
    
    struct ike_auth *
    copyikeauth(const struct ike_auth *auth)
    {
    	struct ike_auth	*newauth;
    
    	if (auth == NULL)
    		return (NULL);
    
    	if ((newauth = calloc(1, sizeof(struct ike_auth))) == NULL)
    		err(1, "%s", __func__);
    	if (auth->string &&
    	    asprintf(&newauth->string, "%s", auth->string) == -1)
    		err(1, "%s", __func__);
    
    	newauth->type = auth->type;
    
    	return (newauth);
    }
    
    struct ipsec_key *
    copykey(struct ipsec_key *key)
    {
    	struct ipsec_key	*newkey;
    
    	if (key == NULL)
    		return (NULL);
    
    	if ((newkey = calloc(1, sizeof(struct ipsec_key))) == NULL)
    		err(1, "%s", __func__);
    	if ((newkey->data = calloc(key->len, sizeof(u_int8_t))) == NULL)
    		err(1, "%s", __func__);
    	memcpy(newkey->data, key->data, key->len);
    	newkey->len = key->len;
    
    	return (newkey);
    }
    
    struct ipsec_addr_wrap *
    copyhost(const struct ipsec_addr_wrap *src)
    {
    	struct ipsec_addr_wrap *dst;
    
    	if (src == NULL)
    		return (NULL);
    
    	dst = calloc(1, sizeof(struct ipsec_addr_wrap));
    	if (dst == NULL)
    		err(1, "%s", __func__);
    
    	memcpy(dst, src, sizeof(struct ipsec_addr_wrap));
    
    	if (src->name != NULL && (dst->name = strdup(src->name)) == NULL)
    		err(1, "%s", __func__);
    
    	return dst;
    }
    
    char *
    copytag(const char *src)
    {
    	char *tag;
    
    	if (src == NULL)
    		return (NULL);
    	if ((tag = strdup(src)) == NULL)
    		err(1, "%s", __func__);
    
    	return (tag);
    }
    
    struct ipsec_rule *
    copyrule(struct ipsec_rule *rule)
    {
    	struct ipsec_rule	*r;
    
    	if ((r = calloc(1, sizeof(struct ipsec_rule))) == NULL)
    		err(1, "%s", __func__);
    
    	r->src = copyhost(rule->src);
    	r->dst = copyhost(rule->dst);
    	r->local = copyhost(rule->local);
    	r->peer = copyhost(rule->peer);
    	r->auth = copyipsecauth(rule->auth);
    	r->ikeauth = copyikeauth(rule->ikeauth);
    	r->xfs = copytransforms(rule->xfs);
    	r->p1xfs = copytransforms(rule->p1xfs);
    	r->p2xfs = copytransforms(rule->p2xfs);
    	r->p1life = copylife(rule->p1life);
    	r->p2life = copylife(rule->p2life);
    	r->authkey = copykey(rule->authkey);
    	r->enckey = copykey(rule->enckey);
    	r->tag = copytag(rule->tag);
    
    	r->flags = rule->flags;
    	r->p1ie = rule->p1ie;
    	r->p2ie = rule->p2ie;
    	r->type = rule->type;
    	r->satype = rule->satype;
    	r->proto = rule->proto;
    	r->tmode = rule->tmode;
    	r->direction = rule->direction;
    	r->flowtype = rule->flowtype;
    	r->sport = rule->sport;
    	r->dport = rule->dport;
    	r->ikemode = rule->ikemode;
    	r->spi = rule->spi;
    	r->udpencap = rule->udpencap;
    	r->udpdport = rule->udpdport;
    	r->nr = rule->nr;
    	r->iface = rule->iface;
    
    	return (r);
    }
    
    int
    validate_af(struct ipsec_addr_wrap *src, struct ipsec_addr_wrap *dst)
    {
    	struct ipsec_addr_wrap *ta;
    	u_int8_t src_v4 = 0;
    	u_int8_t dst_v4 = 0;
    	u_int8_t src_v6 = 0;
    	u_int8_t dst_v6 = 0;
    
    	for (ta = src; ta; ta = ta->next) {
    		if (ta->af == AF_INET)
    			src_v4 = 1;
    		if (ta->af == AF_INET6)
    			src_v6 = 1;
    		if (ta->af == AF_UNSPEC)
    			return 0;
    		if (src_v4 && src_v6)
    			break;
    	}
    	for (ta = dst; ta; ta = ta->next) {
    		if (ta->af == AF_INET)
    			dst_v4 = 1;
    		if (ta->af == AF_INET6)
    			dst_v6 = 1;
    		if (ta->af == AF_UNSPEC)
    			return 0;
    		if (dst_v4 && dst_v6)
    			break;
    	}
    	if (src_v4 != dst_v4 && src_v6 != dst_v6)
    		return (1);
    
    	return (0);
    }
    
    
    int
    validate_sa(u_int32_t spi, u_int8_t satype, struct ipsec_transforms *xfs,
        struct ipsec_key *authkey, struct ipsec_key *enckey, u_int8_t tmode)
    {
    	/* Sanity checks */
    	if (spi == 0) {
    		yyerror("no SPI specified");
    		return (0);
    	}
    	if (satype == IPSEC_AH) {
    		if (!xfs) {
    			yyerror("no transforms specified");
    			return (0);
    		}
    		if (!xfs->authxf)
    			xfs->authxf = &authxfs[AUTHXF_HMAC_SHA2_256];
    		if (xfs->encxf) {
    			yyerror("ah does not provide encryption");
    			return (0);
    		}
    		if (xfs->compxf) {
    			yyerror("ah does not provide compression");
    			return (0);
    		}
    	}
    	if (satype == IPSEC_ESP) {
    		if (!xfs) {
    			yyerror("no transforms specified");
    			return (0);
    		}
    		if (xfs->compxf) {
    			yyerror("esp does not provide compression");
    			return (0);
    		}
    		if (!xfs->encxf)
    			xfs->encxf = &encxfs[ENCXF_AES];
    		if (xfs->encxf->nostatic) {
    			yyerror("%s is disallowed with static keys",
    			    xfs->encxf->name);
    			return 0;
    		}
    		if (xfs->encxf->noauth && xfs->authxf) {
    			yyerror("authentication is implicit for %s",
    			    xfs->encxf->name);
    			return (0);
    		} else if (!xfs->encxf->noauth && !xfs->authxf)
    			xfs->authxf = &authxfs[AUTHXF_HMAC_SHA2_256];
    	}
    	if (satype == IPSEC_IPCOMP) {
    		if (!xfs) {
    			yyerror("no transform specified");
    			return (0);
    		}
    		if (xfs->authxf || xfs->encxf) {
    			yyerror("no encryption or authentication with ipcomp");
    			return (0);
    		}
    		if (!xfs->compxf)
    			xfs->compxf = &compxfs[COMPXF_DEFLATE];
    	}
    	if (satype == IPSEC_IPIP) {
    		if (!xfs) {
    			yyerror("no transform specified");
    			return (0);
    		}
    		if (xfs->authxf || xfs->encxf || xfs->compxf) {
    			yyerror("no encryption, authentication or compression"
    			    " with ipip");
    			return (0);
    		}
    	}
    	if (satype == IPSEC_TCPMD5 && authkey == NULL && tmode !=
    	    IPSEC_TRANSPORT) {
    		yyerror("authentication key needed for tcpmd5");
    		return (0);
    	}
    	if (xfs && xfs->authxf) {
    		if (!authkey && xfs->authxf != &authxfs[AUTHXF_NONE]) {
    			yyerror("no authentication key specified");
    			return (0);
    		}
    		if (authkey && authkey->len != xfs->authxf->keymin) {
    			yyerror("wrong authentication key length, needs to be "
    			    "%zu bits", xfs->authxf->keymin * 8);
    			return (0);
    		}
    	}
    	if (xfs && xfs->encxf) {
    		if (!enckey && xfs->encxf != &encxfs[ENCXF_NULL]) {
    			yyerror("no encryption key specified");
    			return (0);
    		}
    		if (enckey) {
    			if (enckey->len < xfs->encxf->keymin) {
    				yyerror("encryption key too short (%zu bits), "
    				    "minimum %zu bits", enckey->len * 8,
    				    xfs->encxf->keymin * 8);
    				return (0);
    			}
    			if (xfs->encxf->keymax < enckey->len) {
    				yyerror("encryption key too long (%zu bits), "
    				    "maximum %zu bits", enckey->len * 8,
    				    xfs->encxf->keymax * 8);
    				return (0);
    			}
    		}
    	}
    
    	return 1;
    }
    
    int
    add_sabundle(struct ipsec_rule *r, char *bundle)
    {
    	struct ipsec_rule	*rp, *last, *sabundle;
    	int			 found = 0;
    
    	TAILQ_FOREACH(rp, &ipsec->bundle_queue, bundle_entry) {
    		if ((strcmp(rp->src->name, r->src->name) == 0) &&
    		    (strcmp(rp->dst->name, r->dst->name) == 0) &&
    		    (strcmp(rp->bundle, bundle) == 0)) {
    			found = 1;
    			break;
    		}
    	}
    	if (found) {
    		last = TAILQ_LAST(&rp->dst_bundle_queue, dst_bundle_queue);
    		TAILQ_INSERT_TAIL(&rp->dst_bundle_queue, r, dst_bundle_entry);
    
    		sabundle = create_sabundle(last->dst, last->satype, last->spi,
    		    r->dst, r->satype, r->spi);
    		if (sabundle == NULL)
    			return (1);
    		sabundle->nr = ipsec->rule_nr++;
    		if (ipsecctl_add_rule(ipsec, sabundle))
    			return (1);
    	} else {
    		TAILQ_INSERT_TAIL(&ipsec->bundle_queue, r, bundle_entry);
    		TAILQ_INIT(&r->dst_bundle_queue);
    		TAILQ_INSERT_TAIL(&r->dst_bundle_queue, r, dst_bundle_entry);
    		r->bundle = bundle;
    	}
    
    	return (0);
    }
    
    struct ipsec_rule *
    create_sa(u_int8_t satype, u_int8_t tmode, struct ipsec_hosts *hosts,
        u_int32_t spi, u_int8_t udpencap, u_int16_t udpdport,
        struct ipsec_transforms *xfs, struct ipsec_key *authkey, struct ipsec_key *enckey)
    {
    	struct ipsec_rule *r;
    
    	if (validate_sa(spi, satype, xfs, authkey, enckey, tmode) == 0)
    		return (NULL);
    
    	r = calloc(1, sizeof(struct ipsec_rule));
    	if (r == NULL)
    		err(1, "%s", __func__);
    
    	r->type |= RULE_SA;
    	r->satype = satype;
    	r->tmode = tmode;
    	r->src = hosts->src;
    	r->dst = hosts->dst;
    	r->spi = spi;
    	r->udpencap = udpencap;
    	r->udpdport = udpdport;
    	r->xfs = xfs;
    	r->authkey = authkey;
    	r->enckey = enckey;
    
    	return r;
    }
    
    struct ipsec_rule *
    reverse_sa(struct ipsec_rule *rule, u_int32_t spi, struct ipsec_key *authkey,
        struct ipsec_key *enckey)
    {
    	struct ipsec_rule *reverse;
    
    	if (validate_sa(spi, rule->satype, rule->xfs, authkey, enckey,
    	    rule->tmode) == 0)
    		return (NULL);
    
    	reverse = calloc(1, sizeof(struct ipsec_rule));
    	if (reverse == NULL)
    		err(1, "%s", __func__);
    
    	reverse->type |= RULE_SA;
    	reverse->satype = rule->satype;
    	reverse->tmode = rule->tmode;
    	reverse->src = copyhost(rule->dst);
    	reverse->dst = copyhost(rule->src);
    	reverse->spi = spi;
    	reverse->udpencap = rule->udpencap;
    	reverse->udpdport = rule->udpdport;
    	reverse->xfs = copytransforms(rule->xfs);
    	reverse->authkey = authkey;
    	reverse->enckey = enckey;
    
    	return (reverse);
    }
    
    struct ipsec_rule *
    create_sabundle(struct ipsec_addr_wrap *dst, u_int8_t proto, u_int32_t spi,
        struct ipsec_addr_wrap *dst2, u_int8_t proto2, u_int32_t spi2)
    {
    	struct ipsec_rule *r;
    
    	r = calloc(1, sizeof(struct ipsec_rule));
    	if (r == NULL)
    		err(1, "%s", __func__);
    
    	r->type |= RULE_BUNDLE;
    
    	r->dst = copyhost(dst);
    	r->dst2 = copyhost(dst2);
    	r->proto = proto;
    	r->proto2 = proto2;
    	r->spi = spi;
    	r->spi2 = spi2;
    	r->satype = proto;
    
    	return (r);
    }
    
    struct ipsec_rule *
    create_flow(u_int8_t dir, u_int8_t proto, struct ipsec_hosts *hosts,
        u_int8_t satype, char *srcid, char *dstid, u_int8_t type)
    {
    	struct ipsec_rule *r;
    
    	r = calloc(1, sizeof(struct ipsec_rule));
    	if (r == NULL)
    		err(1, "%s", __func__);
    
    	r->type |= RULE_FLOW;
    
    	if (dir == IPSEC_INOUT)
    		r->direction = IPSEC_OUT;
    	else
    		r->direction = dir;
    
    	r->satype = satype;
    	r->proto = proto;
    	r->src = hosts->src;
    	r->sport = hosts->sport;
    	r->dst = hosts->dst;
    	r->dport = hosts->dport;
    	if ((hosts->sport != 0 || hosts->dport != 0) &&
    	    (proto != IPPROTO_TCP && proto != IPPROTO_UDP)) {
    		yyerror("no protocol supplied with source/destination ports");
    		goto errout;
    	}
    
    	switch (satype) {
    	case IPSEC_IPCOMP:
    	case IPSEC_IPIP:
    		if (type == TYPE_UNKNOWN)
    			type = TYPE_USE;
    		break;
    	default:
    		if (type == TYPE_UNKNOWN)
    			type = TYPE_REQUIRE;
    		break;
    	}		
    
    	r->flowtype = type;
    	if (type == TYPE_DENY || type == TYPE_BYPASS)
    		return (r);
    
    	r->auth = calloc(1, sizeof(struct ipsec_auth));
    	if (r->auth == NULL)
    		err(1, "%s", __func__);
    	r->auth->srcid = srcid;
    	r->auth->dstid = dstid;
    	r->auth->srcid_type = get_id_type(srcid);
    	r->auth->dstid_type = get_id_type(dstid);
    	return r;
    
    errout:
    	free(r);
    	if (srcid)
    		free(srcid);
    	if (dstid)
    		free(dstid);
    	free(hosts->src);
    	hosts->src = NULL;
    	free(hosts->dst);
    	hosts->dst = NULL;
    
    	return NULL;
    }
    
    void
    expand_any(struct ipsec_addr_wrap *ipa_in)
    {
    	struct ipsec_addr_wrap *oldnext, *ipa;
    
    	for (ipa = ipa_in; ipa; ipa = ipa->next) {
    		if (ipa->af != AF_UNSPEC)
    			continue;
    		oldnext = ipa->next;
    
    		ipa->af = AF_INET;
    		ipa->netaddress = 1;
    		if ((ipa->name = strdup("0.0.0.0/0")) == NULL)
    			err(1, "%s", __func__);
    
    		ipa->next = calloc(1, sizeof(struct ipsec_addr_wrap));
    		if (ipa->next == NULL)
    			err(1, "%s", __func__);
    		ipa->next->af = AF_INET6;
    		ipa->next->netaddress = 1;
    		if ((ipa->next->name = strdup("::/0")) == NULL)
    			err(1, "%s", __func__);
    
    		ipa->next->next = oldnext;
    	}
    }
    	 
    int
    set_rule_peers(struct ipsec_rule *r, struct ipsec_hosts *peers)
    {
    	if (r->type == RULE_FLOW &&
    	    (r->flowtype == TYPE_DENY || r->flowtype == TYPE_BYPASS))
    		return (0);
    
    	r->local = copyhost(peers->src);
    	r->peer = copyhost(peers->dst);
    	if (r->peer == NULL) {
    		/* Set peer to remote host.  Must be a host address. */
    		if (r->direction == IPSEC_IN) {
    			if (!r->src->netaddress)
    				r->peer = copyhost(r->src);
    		} else {
    			if (!r->dst->netaddress)
    				r->peer = copyhost(r->dst);
    		}
    	}
    	if (r->type == RULE_FLOW && r->peer == NULL) {
    		yyerror("no peer specified for destination %s",
    		    r->dst->name);
    		return (1);
    	}
    	if (r->peer != NULL && r->peer->af == AF_UNSPEC) {
    		/* If peer has been specified as any, use the default peer. */
    		free(r->peer);
    		r->peer = NULL;
    	}
    	if (r->type == RULE_IKE && r->peer == NULL) {
    		/*
                     * Check if the default peer is consistent for all
                     * rules.  Only warn to avoid breaking existing configs.
    		 */
    		static struct ipsec_rule *pdr = NULL;
    
    		if (pdr == NULL) {
    			/* Remember first default peer rule for comparison. */
    			pdr = r;
    		} else {
    			/* The new default peer must create the same config. */
    			if ((pdr->local == NULL && r->local != NULL) ||
    			    (pdr->local != NULL && r->local == NULL) ||
    			    (pdr->local != NULL && r->local != NULL &&
    			    strcmp(pdr->local->name, r->local->name)))
    				yywarn("default peer local mismatch");
    			if (pdr->ikeauth->type != r->ikeauth->type)
    				yywarn("default peer phase 1 auth mismatch");
    			if (pdr->ikeauth->type == IKE_AUTH_PSK &&
    			    r->ikeauth->type == IKE_AUTH_PSK &&
    			    strcmp(pdr->ikeauth->string, r->ikeauth->string))
    				yywarn("default peer psk mismatch");
    			if (pdr->p1ie != r->p1ie)
    				yywarn("default peer phase 1 mode mismatch");
    			/*
    			 * Transforms have ADD insted of SET so they may be
    			 * different and are not checked here.
    			 */
    			if ((pdr->auth->srcid == NULL &&
    			    r->auth->srcid != NULL) ||
    			    (pdr->auth->srcid != NULL &&
    			    r->auth->srcid == NULL) ||
    			    (pdr->auth->srcid != NULL &&
    			    r->auth->srcid != NULL &&
    			    strcmp(pdr->auth->srcid, r->auth->srcid)))
    				yywarn("default peer srcid mismatch");
    			if ((pdr->auth->dstid == NULL &&
    			    r->auth->dstid != NULL) ||
    			    (pdr->auth->dstid != NULL &&
    			    r->auth->dstid == NULL) ||
    			    (pdr->auth->dstid != NULL &&
    			    r->auth->dstid != NULL &&
    			    strcmp(pdr->auth->dstid, r->auth->dstid)))
    				yywarn("default peer dstid mismatch");
    		}
    	}
    	return (0);
    }
    
    int
    expand_rule(struct ipsec_rule *rule, struct ipsec_hosts *peers,
        u_int8_t direction, u_int32_t spi, struct ipsec_key *authkey,
        struct ipsec_key *enckey, char *bundle)
    {
    	struct ipsec_rule	*r, *revr;
    	struct ipsec_addr_wrap	*src, *dst;
    	int added = 0, ret = 1;
    
    	if (validate_af(rule->src, rule->dst)) {
    		yyerror("source/destination address families do not match");
    		goto errout;
    	}
    	expand_any(rule->src);
    	expand_any(rule->dst);
    	for (src = rule->src; src; src = src->next) {
    		for (dst = rule->dst; dst; dst = dst->next) {
    			if (src->af != dst->af)
    				continue;
    			r = copyrule(rule);
    
    			r->src = copyhost(src);
    			r->dst = copyhost(dst);
    
    			if (peers && set_rule_peers(r, peers)) {
    				ipsecctl_free_rule(r);
    				goto errout;
    			}
    
    			r->nr = ipsec->rule_nr++;
    			if (ipsecctl_add_rule(ipsec, r))
    				goto out;
    			if (bundle && add_sabundle(r, bundle))
    				goto out;
    
    			if (direction == IPSEC_INOUT) {
    				/* Create and add reverse flow rule. */
    				revr = reverse_rule(r);
    				if (revr == NULL)
    					goto out;
    
    				revr->nr = ipsec->rule_nr++;
    				if (ipsecctl_add_rule(ipsec, revr))
    					goto out;
    				if (bundle && add_sabundle(revr, bundle))
    					goto out;
    			} else if (spi != 0 || authkey || enckey) {
    				/* Create and add reverse sa rule. */
    				revr = reverse_sa(r, spi, authkey, enckey);
    				if (revr == NULL)
    					goto out;
    
    				revr->nr = ipsec->rule_nr++;
    				if (ipsecctl_add_rule(ipsec, revr))
    					goto out;
    				if (bundle && add_sabundle(revr, bundle))
    					goto out;
    			}
    			added++;
    		}
    	}
    	if (!added)
    		yyerror("rule expands to no valid combination");
     errout:
    	ret = 0;
    	ipsecctl_free_rule(rule);
     out:
    	if (peers) {
    		if (peers->src)
    			free(peers->src);
    		if (peers->dst)
    			free(peers->dst);
    	}
    	return (ret);
    }
    
    struct ipsec_rule *
    reverse_rule(struct ipsec_rule *rule)
    {
    	struct ipsec_rule *reverse;
    
    	reverse = calloc(1, sizeof(struct ipsec_rule));
    	if (reverse == NULL)
    		err(1, "%s", __func__);
    
    	reverse->type |= RULE_FLOW;
    
    	/* Reverse direction */
    	if (rule->direction == (u_int8_t)IPSEC_OUT)
    		reverse->direction = (u_int8_t)IPSEC_IN;
    	else
    		reverse->direction = (u_int8_t)IPSEC_OUT;
    
    	reverse->flowtype = rule->flowtype;
    	reverse->src = copyhost(rule->dst);
    	reverse->dst = copyhost(rule->src);
    	reverse->sport = rule->dport;
    	reverse->dport = rule->sport;
    	if (rule->local)
    		reverse->local = copyhost(rule->local);
    	if (rule->peer)
    		reverse->peer = copyhost(rule->peer);
    	reverse->satype = rule->satype;
    	reverse->proto = rule->proto;
    
    	if (rule->auth) {
    		reverse->auth = calloc(1, sizeof(struct ipsec_auth));
    		if (reverse->auth == NULL)
    			err(1, "%s", __func__);
    		if (rule->auth->dstid && (reverse->auth->dstid =
    		    strdup(rule->auth->dstid)) == NULL)
    			err(1, "%s", __func__);
    		if (rule->auth->srcid && (reverse->auth->srcid =
    		    strdup(rule->auth->srcid)) == NULL)
    			err(1, "%s", __func__);
    		reverse->auth->srcid_type = rule->auth->srcid_type;
    		reverse->auth->dstid_type = rule->auth->dstid_type;
    		reverse->auth->type = rule->auth->type;
    	}
    
    	return reverse;
    }
    
    struct ipsec_rule *
    create_ike(u_int8_t proto, struct ipsec_hosts *hosts,
        struct ike_mode *phase1mode, struct ike_mode *phase2mode, u_int8_t satype,
        u_int8_t tmode, u_int8_t mode, char *srcid, char *dstid,
        struct ike_auth *authtype, char *tag)
    {
    	struct ipsec_rule *r;
    
    	r = calloc(1, sizeof(struct ipsec_rule));
    	if (r == NULL)
    		err(1, "%s", __func__);
    
    	r->type = RULE_IKE;
    
    	r->proto = proto;
    	r->src = hosts->src;
    	r->sport = hosts->sport;
    	r->dst = hosts->dst;
    	r->dport = hosts->dport;
    	if ((hosts->sport != 0 || hosts->dport != 0) &&
    	    (proto != IPPROTO_TCP && proto != IPPROTO_UDP)) {
    		yyerror("no protocol supplied with source/destination ports");
    		goto errout;
    	}
    
    	r->satype = satype;
    	r->tmode = tmode;
    	r->ikemode = mode;
    	if (phase1mode) {
    		r->p1xfs = phase1mode->xfs;
    		r->p1life = phase1mode->life;
    		r->p1ie = phase1mode->ike_exch;
    	} else {
    		r->p1ie = IKE_MM;
    	}
    	if (phase2mode) {
    		if (phase2mode->xfs && phase2mode->xfs->encxf &&
    		    phase2mode->xfs->encxf->noauth &&
    		    phase2mode->xfs->authxf) {
    			yyerror("authentication is implicit for %s",
    			    phase2mode->xfs->encxf->name);
    			goto errout;
    		}
    		r->p2xfs = phase2mode->xfs;
    		r->p2life = phase2mode->life;
    		r->p2ie = phase2mode->ike_exch;
    	} else {
    		r->p2ie = IKE_QM;
    	}
    
    	r->auth = calloc(1, sizeof(struct ipsec_auth));
    	if (r->auth == NULL)
    		err(1, "%s", __func__);
    	r->auth->srcid = srcid;
    	r->auth->dstid = dstid;
    	r->auth->srcid_type = get_id_type(srcid);
    	r->auth->dstid_type = get_id_type(dstid);
    	r->ikeauth = calloc(1, sizeof(struct ike_auth));
    	if (r->ikeauth == NULL)
    		err(1, "%s", __func__);
    	r->ikeauth->type = authtype->type;
    	r->ikeauth->string = authtype->string;
    	r->tag = tag;
    
    	return (r);
    
    errout:
    	free(r);
    	free(hosts->src);
    	hosts->src = NULL;
    	free(hosts->dst);
    	hosts->dst = NULL;
    	if (phase1mode) {
    		free(phase1mode->xfs);
    		phase1mode->xfs = NULL;
    		free(phase1mode->life);
    		phase1mode->life = NULL;
    	}
    	if (phase2mode) {
    		free(phase2mode->xfs);
    		phase2mode->xfs = NULL;
    		free(phase2mode->life);
    		phase2mode->life = NULL;
    	}
    	if (srcid)
    		free(srcid);
    	if (dstid)
    		free(dstid);
    	return NULL;
    }