xref: /openbsd-src/sbin/iked/pfkey.c (revision f36db9c4e75d6f4b6e40410c8005109158705028)
1*f36db9c4Syasuoka /*	$OpenBSD: pfkey.c,v 1.85 2024/07/13 12:22:46 yasuoka Exp $	*/
245ae9d61Sreyk 
345ae9d61Sreyk /*
4fcebd35dSreyk  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
545ae9d61Sreyk  * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org>
645ae9d61Sreyk  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
745ae9d61Sreyk  * Copyright (c) 2003, 2004 Markus Friedl <markus@openbsd.org>
845ae9d61Sreyk  *
945ae9d61Sreyk  * Permission to use, copy, modify, and distribute this software for any
1045ae9d61Sreyk  * purpose with or without fee is hereby granted, provided that the above
1145ae9d61Sreyk  * copyright notice and this permission notice appear in all copies.
1245ae9d61Sreyk  *
1345ae9d61Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1445ae9d61Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1545ae9d61Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1645ae9d61Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1745ae9d61Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1845ae9d61Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1945ae9d61Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2045ae9d61Sreyk  */
2145ae9d61Sreyk 
2245ae9d61Sreyk #include <sys/queue.h>
2345ae9d61Sreyk #include <sys/uio.h>
2445ae9d61Sreyk #include <sys/socket.h>
2545ae9d61Sreyk 
2645ae9d61Sreyk #include <netinet/in.h>
2745ae9d61Sreyk #include <netinet/ip_ipsp.h>
281c18b693Stobhe #include <net/if.h>
2945ae9d61Sreyk #include <net/pfkeyv2.h>
3045ae9d61Sreyk 
3145ae9d61Sreyk #include <err.h>
3245ae9d61Sreyk #include <errno.h>
3345ae9d61Sreyk #include <stdio.h>
34f0de6e10Sderaadt #include <poll.h>
3545ae9d61Sreyk #include <string.h>
3645ae9d61Sreyk #include <stdlib.h>
3745ae9d61Sreyk #include <unistd.h>
3845ae9d61Sreyk #include <event.h>
3945ae9d61Sreyk 
4045ae9d61Sreyk #include "iked.h"
4145ae9d61Sreyk #include "ikev2.h"
4245ae9d61Sreyk 
4345ae9d61Sreyk #define ROUNDUP(x) (((x) + (PFKEYV2_CHUNK - 1)) & ~(PFKEYV2_CHUNK - 1))
441c18b693Stobhe #define IOV_CNT 28
4545ae9d61Sreyk 
46d09d3a7dSreyk #define PFKEYV2_CHUNK sizeof(uint64_t)
473f3558dcSmarkus #define PFKEY_REPLY_TIMEOUT 1000
4845ae9d61Sreyk 
49c0b327e6Spatrick /* only used internally */
50c0b327e6Spatrick #define IKED_SADB_UPDATE_SA_ADDRESSES 0xff
51c0b327e6Spatrick 
52d09d3a7dSreyk static uint32_t sadb_msg_seq = 0;
53d09d3a7dSreyk static unsigned int sadb_decoupled = 0;
5445ae9d61Sreyk 
550347364bStobhe static int iked_rdomain = 0;
560347364bStobhe 
57d1ebe3d0Smikeb static struct event pfkey_timer_ev;
58d1ebe3d0Smikeb static struct timeval pfkey_timer_tv;
59d1ebe3d0Smikeb 
60d1ebe3d0Smikeb struct pfkey_message {
61d1ebe3d0Smikeb 	SIMPLEQ_ENTRY(pfkey_message)
62d1ebe3d0Smikeb 			 pm_entry;
63d09d3a7dSreyk 	uint8_t		*pm_data;
641c9ff2fdSblambert 	ssize_t		 pm_length;
65d1ebe3d0Smikeb };
666e1880a3Smarkus SIMPLEQ_HEAD(, pfkey_message) pfkey_retry, pfkey_postponed =
67d1ebe3d0Smikeb     SIMPLEQ_HEAD_INITIALIZER(pfkey_postponed);
68d1ebe3d0Smikeb 
6945ae9d61Sreyk struct pfkey_constmap {
70d09d3a7dSreyk 	uint8_t		 pfkey_id;
71d09d3a7dSreyk 	unsigned int	 pfkey_ikeid;
72d09d3a7dSreyk 	unsigned int	 pfkey_fixedkey;
7345ae9d61Sreyk };
7445ae9d61Sreyk 
7545ae9d61Sreyk static const struct pfkey_constmap pfkey_encr[] = {
7645ae9d61Sreyk 	{ SADB_EALG_3DESCBC,	IKEV2_XFORMENCR_3DES },
7745ae9d61Sreyk 	{ SADB_X_EALG_CAST,	IKEV2_XFORMENCR_CAST },
7845ae9d61Sreyk 	{ SADB_X_EALG_BLF,	IKEV2_XFORMENCR_BLOWFISH },
7945ae9d61Sreyk 	{ SADB_EALG_NULL,	IKEV2_XFORMENCR_NULL },
8045ae9d61Sreyk 	{ SADB_X_EALG_AES,	IKEV2_XFORMENCR_AES_CBC },
8145ae9d61Sreyk 	{ SADB_X_EALG_AESCTR,	IKEV2_XFORMENCR_AES_CTR },
823fa77592Smikeb 	{ SADB_X_EALG_AESGCM16,	IKEV2_XFORMENCR_AES_GCM_16 },
833fa77592Smikeb 	{ SADB_X_EALG_AESGMAC,	IKEV2_XFORMENCR_NULL_AES_GMAC },
84b3954071Smikeb 	{ SADB_X_EALG_CHACHA20POLY1305, IKEV2_XFORMENCR_CHACHA20_POLY1305 },
8545ae9d61Sreyk 	{ 0 }
8645ae9d61Sreyk };
8745ae9d61Sreyk 
8845ae9d61Sreyk static const struct pfkey_constmap pfkey_integr[] = {
8945ae9d61Sreyk 	{ SADB_AALG_MD5HMAC,	IKEV2_XFORMAUTH_HMAC_MD5_96 },
9045ae9d61Sreyk 	{ SADB_AALG_SHA1HMAC,	IKEV2_XFORMAUTH_HMAC_SHA1_96 },
9145ae9d61Sreyk 	{ SADB_X_AALG_SHA2_256,	IKEV2_XFORMAUTH_HMAC_SHA2_256_128 },
9245ae9d61Sreyk 	{ SADB_X_AALG_SHA2_384,	IKEV2_XFORMAUTH_HMAC_SHA2_384_192 },
9345ae9d61Sreyk 	{ SADB_X_AALG_SHA2_512,	IKEV2_XFORMAUTH_HMAC_SHA2_512_256 },
9445ae9d61Sreyk 	{ 0 }
9545ae9d61Sreyk };
9645ae9d61Sreyk 
9745ae9d61Sreyk static const struct pfkey_constmap pfkey_satype[] = {
9845ae9d61Sreyk 	{ SADB_SATYPE_AH,	IKEV2_SAPROTO_AH },
9945ae9d61Sreyk 	{ SADB_SATYPE_ESP,	IKEV2_SAPROTO_ESP },
10003f6ad09Smarkus 	{ SADB_X_SATYPE_IPCOMP,	IKEV2_SAPROTO_IPCOMP },
10145ae9d61Sreyk 	{ 0 }
10245ae9d61Sreyk };
10345ae9d61Sreyk 
104d09d3a7dSreyk int	pfkey_map(const struct pfkey_constmap *, uint16_t, uint8_t *);
1058f6f6c19Stobhe int	pfkey_flow(struct iked *, uint8_t, uint8_t, struct iked_flow *);
1068f6f6c19Stobhe int	pfkey_sa(struct iked *, uint8_t, uint8_t, struct iked_childsa *);
1078f6f6c19Stobhe int	pfkey_sa_getspi(struct iked *, uint8_t, struct iked_childsa *, uint32_t *);
1088f6f6c19Stobhe int	pfkey_sagroup(struct iked *, uint8_t, uint8_t,
10945ae9d61Sreyk 	    struct iked_childsa *, struct iked_childsa *);
110e7c3d382Stobhe int	pfkey_write(struct iked *, struct sadb_msg *, struct iovec *, int,
111d09d3a7dSreyk 	    uint8_t **, ssize_t *);
112d09d3a7dSreyk int	pfkey_reply(int, uint8_t **, ssize_t *);
1136417b90fSreyk void	pfkey_dispatch(int, short, void *);
114*f36db9c4Syasuoka int	pfkey_sa_lookup(struct iked *, struct iked_childsa *, uint64_t *,
115*f36db9c4Syasuoka 	    struct iked_sastats *);
1168f6f6c19Stobhe int	pfkey_sa_check_exists(struct iked *, struct iked_childsa *);
117*f36db9c4Syasuoka int	pfkey_sa_sastats(struct iked *, struct iked_childsa *,
118*f36db9c4Syasuoka 	    struct iked_sastats *);
1194292c78bSreyk 
12045ae9d61Sreyk struct sadb_ident *
121d09d3a7dSreyk 	pfkey_id2ident(struct iked_id *, unsigned int);
122d09d3a7dSreyk void	*pfkey_find_ext(uint8_t *, ssize_t, int);
123b0eeedd0Smikeb 
124d1ebe3d0Smikeb void	pfkey_timer_cb(int, short, void *);
1256e1880a3Smarkus int	pfkey_process(struct iked *, struct pfkey_message *);
12645ae9d61Sreyk 
12745ae9d61Sreyk int
pfkey_couple(struct iked * env,struct iked_sas * sas,int couple)1288f6f6c19Stobhe pfkey_couple(struct iked *env, struct iked_sas *sas, int couple)
129fc20f985Sreyk {
130fc20f985Sreyk 	struct iked_sa		*sa;
131fc20f985Sreyk 	struct iked_flow	*flow;
132e7fee6f8Stobhe 	struct iked_childsa	*csa, *ipcomp;
133fc20f985Sreyk 	const char		*mode[] = { "coupled", "decoupled" };
134fc20f985Sreyk 
135fc20f985Sreyk 	/* Socket is not ready */
1368f6f6c19Stobhe 	if (env->sc_pfkey == -1)
137fc20f985Sreyk 		return (-1);
138fc20f985Sreyk 
139af52b2a8Svgross 	if (sadb_decoupled == !couple)
140fc20f985Sreyk 		return (0);
141fc20f985Sreyk 
142fc20f985Sreyk 	log_debug("%s: kernel %s -> %s", __func__,
143af52b2a8Svgross 	    mode[sadb_decoupled], mode[!sadb_decoupled]);
144af52b2a8Svgross 
145af52b2a8Svgross 	/* Allow writes to the PF_KEY socket */
146af52b2a8Svgross 	sadb_decoupled = 0;
147fc20f985Sreyk 
148fc20f985Sreyk 	RB_FOREACH(sa, iked_sas, sas) {
149fc20f985Sreyk 		TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) {
150af52b2a8Svgross 			if (!csa->csa_loaded && couple)
1518f6f6c19Stobhe 				(void)pfkey_sa_add(env, csa, NULL);
152af52b2a8Svgross 			else if (csa->csa_loaded && !couple)
1538f6f6c19Stobhe 				(void)pfkey_sa_delete(env, csa);
154e7fee6f8Stobhe 			if ((ipcomp = csa->csa_bundled) != NULL) {
155e7fee6f8Stobhe 				if (!ipcomp->csa_loaded && couple)
1568f6f6c19Stobhe 					(void)pfkey_sa_add(env, ipcomp, csa);
157e7fee6f8Stobhe 				else if (ipcomp->csa_loaded && !couple)
1588f6f6c19Stobhe 					(void)pfkey_sa_delete(env, ipcomp);
159e7fee6f8Stobhe 			}
160fc20f985Sreyk 		}
161fc20f985Sreyk 		TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) {
162af52b2a8Svgross 			if (!flow->flow_loaded && couple)
1638f6f6c19Stobhe 				(void)pfkey_flow_add(env, flow);
164af52b2a8Svgross 			else if (flow->flow_loaded && !couple)
1658f6f6c19Stobhe 				(void)pfkey_flow_delete(env, flow);
166fc20f985Sreyk 		}
167fc20f985Sreyk 	}
168fc20f985Sreyk 
169af52b2a8Svgross 	sadb_decoupled = !couple;
170af52b2a8Svgross 
171fc20f985Sreyk 	return (0);
172fc20f985Sreyk }
173fc20f985Sreyk 
174fc20f985Sreyk int
pfkey_map(const struct pfkey_constmap * map,uint16_t alg,uint8_t * pfkalg)175d09d3a7dSreyk pfkey_map(const struct pfkey_constmap *map, uint16_t alg, uint8_t *pfkalg)
17645ae9d61Sreyk {
17745ae9d61Sreyk 	int	 i;
17845ae9d61Sreyk 
17945ae9d61Sreyk 	for (i = 0; map[i].pfkey_id != 0; i++)
18045ae9d61Sreyk 		if (map[i].pfkey_ikeid == alg) {
18145ae9d61Sreyk 			*pfkalg = map[i].pfkey_id;
18245ae9d61Sreyk 			return (0);
18345ae9d61Sreyk 		}
18445ae9d61Sreyk 	return (-1);
18545ae9d61Sreyk }
18645ae9d61Sreyk 
18745ae9d61Sreyk int
pfkey_flow(struct iked * env,uint8_t satype,uint8_t action,struct iked_flow * flow)1888f6f6c19Stobhe pfkey_flow(struct iked *env, uint8_t satype, uint8_t action, struct iked_flow *flow)
18945ae9d61Sreyk {
19045ae9d61Sreyk 	struct sadb_msg		 smsg;
1917cd0af1dSvgross 	struct iked_addr	*flow_src, *flow_dst;
19245ae9d61Sreyk 	struct sadb_address	 sa_src, sa_dst, sa_local, sa_peer, sa_smask,
19345ae9d61Sreyk 				 sa_dmask;
19445ae9d61Sreyk 	struct sadb_protocol	 sa_flowtype, sa_protocol;
1950347364bStobhe 	struct sadb_x_rdomain	 sa_rdomain;
19645ae9d61Sreyk 	struct sadb_ident	*sa_srcid, *sa_dstid;
19745ae9d61Sreyk 	struct sockaddr_storage	 ssrc, sdst, slocal, speer, smask, dmask;
19845ae9d61Sreyk 	struct iovec		 iov[IOV_CNT];
1994292c78bSreyk 	int			 iov_cnt, ret = -1;
200eab256a9Stobhe 	uint64_t		 pad = 0;
201eab256a9Stobhe 	size_t			 padlen;
20245ae9d61Sreyk 
20345ae9d61Sreyk 	sa_srcid = sa_dstid = NULL;
20445ae9d61Sreyk 
2057cd0af1dSvgross 	flow_src = &flow->flow_src;
2067cd0af1dSvgross 	flow_dst = &flow->flow_dst;
2077cd0af1dSvgross 
2087cd0af1dSvgross 	if (flow->flow_prenat.addr_af == flow_src->addr_af) {
2097cd0af1dSvgross 		if (flow->flow_dir == IPSP_DIRECTION_IN)
2107cd0af1dSvgross 			flow_dst = &flow->flow_prenat;
2117cd0af1dSvgross 		else
2127cd0af1dSvgross 			flow_src = &flow->flow_prenat;
2137cd0af1dSvgross 	}
2147cd0af1dSvgross 
21545ae9d61Sreyk 	bzero(&ssrc, sizeof(ssrc));
21645ae9d61Sreyk 	bzero(&smask, sizeof(smask));
2177cd0af1dSvgross 	memcpy(&ssrc, &flow_src->addr, sizeof(ssrc));
2187cd0af1dSvgross 	memcpy(&smask, &flow_src->addr, sizeof(smask));
2197cd0af1dSvgross 	socket_af((struct sockaddr *)&ssrc, flow_src->addr_port);
2207cd0af1dSvgross 	socket_af((struct sockaddr *)&smask, flow_src->addr_port ?
2219b76b366Sjsg 	    0xffff : 0);
22245ae9d61Sreyk 
2237cd0af1dSvgross 	switch (flow_src->addr_af) {
22445ae9d61Sreyk 	case AF_INET:
22545ae9d61Sreyk 		((struct sockaddr_in *)&smask)->sin_addr.s_addr =
2267cd0af1dSvgross 		    prefixlen2mask(flow_src->addr_net ?
2277cd0af1dSvgross 		    flow_src->addr_mask : 32);
22845ae9d61Sreyk 		break;
22945ae9d61Sreyk 	case AF_INET6:
2307cd0af1dSvgross 		prefixlen2mask6(flow_src->addr_net ?
2317cd0af1dSvgross 		    flow_src->addr_mask : 128,
232d09d3a7dSreyk 		    (uint32_t *)((struct sockaddr_in6 *)
23345ae9d61Sreyk 		    &smask)->sin6_addr.s6_addr);
23445ae9d61Sreyk 		break;
23545ae9d61Sreyk 	default:
23645ae9d61Sreyk 		log_warnx("%s: unsupported address family %d",
2377cd0af1dSvgross 		    __func__, flow_src->addr_af);
23845ae9d61Sreyk 		return (-1);
23945ae9d61Sreyk 	}
24045ae9d61Sreyk 	smask.ss_len = ssrc.ss_len;
24145ae9d61Sreyk 
24245ae9d61Sreyk 	bzero(&sdst, sizeof(sdst));
24345ae9d61Sreyk 	bzero(&dmask, sizeof(dmask));
2447cd0af1dSvgross 	memcpy(&sdst, &flow_dst->addr, sizeof(sdst));
2457cd0af1dSvgross 	memcpy(&dmask, &flow_dst->addr, sizeof(dmask));
2467cd0af1dSvgross 	socket_af((struct sockaddr *)&sdst, flow_dst->addr_port);
2477cd0af1dSvgross 	socket_af((struct sockaddr *)&dmask, flow_dst->addr_port ?
248a38650c7Smikeb 	    0xffff : 0);
24945ae9d61Sreyk 
2507cd0af1dSvgross 	switch (flow_dst->addr_af) {
25145ae9d61Sreyk 	case AF_INET:
25245ae9d61Sreyk 		((struct sockaddr_in *)&dmask)->sin_addr.s_addr =
2537cd0af1dSvgross 		    prefixlen2mask(flow_dst->addr_net ?
2547cd0af1dSvgross 		    flow_dst->addr_mask : 32);
25545ae9d61Sreyk 		break;
25645ae9d61Sreyk 	case AF_INET6:
2577cd0af1dSvgross 		prefixlen2mask6(flow_dst->addr_net ?
2587cd0af1dSvgross 		    flow_dst->addr_mask : 128,
259d09d3a7dSreyk 		    (uint32_t *)((struct sockaddr_in6 *)
26045ae9d61Sreyk 		    &dmask)->sin6_addr.s6_addr);
26145ae9d61Sreyk 		break;
26245ae9d61Sreyk 	default:
26345ae9d61Sreyk 		log_warnx("%s: unsupported address family %d",
2647cd0af1dSvgross 		    __func__, flow_dst->addr_af);
26545ae9d61Sreyk 		return (-1);
26645ae9d61Sreyk 	}
26745ae9d61Sreyk 	dmask.ss_len = sdst.ss_len;
26845ae9d61Sreyk 
26945ae9d61Sreyk 	bzero(&slocal, sizeof(slocal));
27045ae9d61Sreyk 	bzero(&speer, sizeof(speer));
2715342a326Sreyk 	if (action != SADB_X_DELFLOW && flow->flow_local != NULL) {
27245ae9d61Sreyk 		memcpy(&slocal, &flow->flow_local->addr, sizeof(slocal));
27345ae9d61Sreyk 		socket_af((struct sockaddr *)&slocal, 0);
27445ae9d61Sreyk 
27545ae9d61Sreyk 		memcpy(&speer, &flow->flow_peer->addr, sizeof(speer));
27645ae9d61Sreyk 		socket_af((struct sockaddr *)&speer, 0);
27745ae9d61Sreyk 	}
27845ae9d61Sreyk 
27945ae9d61Sreyk 	bzero(&smsg, sizeof(smsg));
28045ae9d61Sreyk 	smsg.sadb_msg_version = PF_KEY_V2;
281b0eeedd0Smikeb 	smsg.sadb_msg_seq = ++sadb_msg_seq;
28245ae9d61Sreyk 	smsg.sadb_msg_pid = getpid();
28345ae9d61Sreyk 	smsg.sadb_msg_len = sizeof(smsg) / 8;
28445ae9d61Sreyk 	smsg.sadb_msg_type = action;
28545ae9d61Sreyk 	smsg.sadb_msg_satype = satype;
28645ae9d61Sreyk 
28745ae9d61Sreyk 	bzero(&sa_flowtype, sizeof(sa_flowtype));
28845ae9d61Sreyk 	sa_flowtype.sadb_protocol_exttype = SADB_X_EXT_FLOW_TYPE;
28945ae9d61Sreyk 	sa_flowtype.sadb_protocol_len = sizeof(sa_flowtype) / 8;
29045ae9d61Sreyk 	sa_flowtype.sadb_protocol_direction = flow->flow_dir;
2910d71d9e6Stobhe 	sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_REQUIRE;
29245ae9d61Sreyk 
29345ae9d61Sreyk 	bzero(&sa_protocol, sizeof(sa_protocol));
29445ae9d61Sreyk 	sa_protocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
29545ae9d61Sreyk 	sa_protocol.sadb_protocol_len = sizeof(sa_protocol) / 8;
29645ae9d61Sreyk 	sa_protocol.sadb_protocol_direction = 0;
29745ae9d61Sreyk 	sa_protocol.sadb_protocol_proto = flow->flow_ipproto;
29845ae9d61Sreyk 
29945ae9d61Sreyk 	bzero(&sa_src, sizeof(sa_src));
30045ae9d61Sreyk 	sa_src.sadb_address_exttype = SADB_X_EXT_SRC_FLOW;
30145ae9d61Sreyk 	sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
30245ae9d61Sreyk 
30345ae9d61Sreyk 	bzero(&sa_smask, sizeof(sa_smask));
30445ae9d61Sreyk 	sa_smask.sadb_address_exttype = SADB_X_EXT_SRC_MASK;
30545ae9d61Sreyk 	sa_smask.sadb_address_len =
30645ae9d61Sreyk 	    (sizeof(sa_smask) + ROUNDUP(smask.ss_len)) / 8;
30745ae9d61Sreyk 
30845ae9d61Sreyk 	bzero(&sa_dst, sizeof(sa_dst));
30945ae9d61Sreyk 	sa_dst.sadb_address_exttype = SADB_X_EXT_DST_FLOW;
31045ae9d61Sreyk 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
31145ae9d61Sreyk 
31245ae9d61Sreyk 	bzero(&sa_dmask, sizeof(sa_dmask));
31345ae9d61Sreyk 	sa_dmask.sadb_address_exttype = SADB_X_EXT_DST_MASK;
31445ae9d61Sreyk 	sa_dmask.sadb_address_len =
31545ae9d61Sreyk 	    (sizeof(sa_dmask) + ROUNDUP(dmask.ss_len)) / 8;
31645ae9d61Sreyk 
3175342a326Sreyk 	if (action != SADB_X_DELFLOW && flow->flow_local != NULL) {
31845ae9d61Sreyk 		/* local address */
31945ae9d61Sreyk 		bzero(&sa_local, sizeof(sa_local));
32045ae9d61Sreyk 		sa_local.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
32145ae9d61Sreyk 		sa_local.sadb_address_len =
32245ae9d61Sreyk 		    (sizeof(sa_local) + ROUNDUP(slocal.ss_len)) / 8;
32345ae9d61Sreyk 
32445ae9d61Sreyk 		/* peer address */
32545ae9d61Sreyk 		bzero(&sa_peer, sizeof(sa_peer));
32645ae9d61Sreyk 		sa_peer.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
32745ae9d61Sreyk 		sa_peer.sadb_address_len =
32845ae9d61Sreyk 		    (sizeof(sa_peer) + ROUNDUP(speer.ss_len)) / 8;
32945ae9d61Sreyk 
33045ae9d61Sreyk 		/* local id */
331834f9deeSmarkus 		sa_srcid = pfkey_id2ident(IKESA_SRCID(flow->flow_ikesa),
33245ae9d61Sreyk 		    SADB_EXT_IDENTITY_SRC);
33345ae9d61Sreyk 
33445ae9d61Sreyk 		/* peer id */
335834f9deeSmarkus 		sa_dstid = pfkey_id2ident(IKESA_DSTID(flow->flow_ikesa),
33645ae9d61Sreyk 		    SADB_EXT_IDENTITY_DST);
33745ae9d61Sreyk 	}
33845ae9d61Sreyk 
3390347364bStobhe 	if (flow->flow_rdomain >= 0) {
3400347364bStobhe 		/* install flow in specific rdomain */
3410347364bStobhe 		bzero(&sa_rdomain, sizeof(sa_rdomain));
3420347364bStobhe 		sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN;
3430347364bStobhe 		sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8;
3440347364bStobhe 		sa_rdomain.sadb_x_rdomain_dom1 = flow->flow_rdomain;
3450347364bStobhe 	}
3460347364bStobhe 
347eab256a9Stobhe #define PAD(len)					\
348eab256a9Stobhe 	padlen = ROUNDUP((len)) - (len);		\
349eab256a9Stobhe 	if (padlen) {					\
350eab256a9Stobhe 		iov[iov_cnt].iov_base = &pad;		\
351eab256a9Stobhe 		iov[iov_cnt].iov_len = padlen;		\
352eab256a9Stobhe 		iov_cnt++;				\
353eab256a9Stobhe 	}
354eab256a9Stobhe 
35545ae9d61Sreyk 	iov_cnt = 0;
35645ae9d61Sreyk 
35745ae9d61Sreyk 	/* header */
35845ae9d61Sreyk 	iov[iov_cnt].iov_base = &smsg;
35945ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(smsg);
36045ae9d61Sreyk 	iov_cnt++;
36145ae9d61Sreyk 
36245ae9d61Sreyk 	/* add flow type */
36345ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_flowtype;
36445ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_flowtype);
36545ae9d61Sreyk 	smsg.sadb_msg_len += sa_flowtype.sadb_protocol_len;
36645ae9d61Sreyk 	iov_cnt++;
36745ae9d61Sreyk 
3685342a326Sreyk 	if (action != SADB_X_DELFLOW && flow->flow_local != NULL) {
36945ae9d61Sreyk 		/* remote peer */
37045ae9d61Sreyk 		iov[iov_cnt].iov_base = &sa_peer;
37145ae9d61Sreyk 		iov[iov_cnt].iov_len = sizeof(sa_peer);
37245ae9d61Sreyk 		iov_cnt++;
37345ae9d61Sreyk 		iov[iov_cnt].iov_base = &speer;
374eab256a9Stobhe 		iov[iov_cnt].iov_len = speer.ss_len;
37545ae9d61Sreyk 		smsg.sadb_msg_len += sa_peer.sadb_address_len;
37645ae9d61Sreyk 		iov_cnt++;
377eab256a9Stobhe 		PAD(speer.ss_len);
37845ae9d61Sreyk 	}
37945ae9d61Sreyk 
38045ae9d61Sreyk 	/* src addr */
38145ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_src;
38245ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_src);
38345ae9d61Sreyk 	iov_cnt++;
38445ae9d61Sreyk 	iov[iov_cnt].iov_base = &ssrc;
385eab256a9Stobhe 	iov[iov_cnt].iov_len = ssrc.ss_len;
38645ae9d61Sreyk 	smsg.sadb_msg_len += sa_src.sadb_address_len;
38745ae9d61Sreyk 	iov_cnt++;
388eab256a9Stobhe 	PAD(ssrc.ss_len);
38945ae9d61Sreyk 
39045ae9d61Sreyk 	/* src mask */
39145ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_smask;
39245ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_smask);
39345ae9d61Sreyk 	iov_cnt++;
39445ae9d61Sreyk 	iov[iov_cnt].iov_base = &smask;
395eab256a9Stobhe 	iov[iov_cnt].iov_len = smask.ss_len;
39645ae9d61Sreyk 	smsg.sadb_msg_len += sa_smask.sadb_address_len;
39745ae9d61Sreyk 	iov_cnt++;
398eab256a9Stobhe 	PAD(smask.ss_len);
39945ae9d61Sreyk 
40045ae9d61Sreyk 	/* dest addr */
40145ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_dst;
40245ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_dst);
40345ae9d61Sreyk 	iov_cnt++;
40445ae9d61Sreyk 	iov[iov_cnt].iov_base = &sdst;
405eab256a9Stobhe 	iov[iov_cnt].iov_len = sdst.ss_len;
40645ae9d61Sreyk 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
40745ae9d61Sreyk 	iov_cnt++;
408eab256a9Stobhe 	PAD(sdst.ss_len);
40945ae9d61Sreyk 
41045ae9d61Sreyk 	/* dst mask */
41145ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_dmask;
41245ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_dmask);
41345ae9d61Sreyk 	iov_cnt++;
41445ae9d61Sreyk 	iov[iov_cnt].iov_base = &dmask;
415eab256a9Stobhe 	iov[iov_cnt].iov_len = dmask.ss_len;
41645ae9d61Sreyk 	smsg.sadb_msg_len += sa_dmask.sadb_address_len;
41745ae9d61Sreyk 	iov_cnt++;
418eab256a9Stobhe 	PAD(dmask.ss_len);
41945ae9d61Sreyk 
42045ae9d61Sreyk 	/* add protocol */
42145ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_protocol;
42245ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_protocol);
42345ae9d61Sreyk 	smsg.sadb_msg_len += sa_protocol.sadb_protocol_len;
42445ae9d61Sreyk 	iov_cnt++;
42545ae9d61Sreyk 
42645ae9d61Sreyk 	if (sa_srcid) {
42745ae9d61Sreyk 		/* src identity */
42845ae9d61Sreyk 		iov[iov_cnt].iov_base = sa_srcid;
42945ae9d61Sreyk 		iov[iov_cnt].iov_len = sa_srcid->sadb_ident_len * 8;
43045ae9d61Sreyk 		smsg.sadb_msg_len += sa_srcid->sadb_ident_len;
43145ae9d61Sreyk 		iov_cnt++;
43245ae9d61Sreyk 	}
43345ae9d61Sreyk 	if (sa_dstid) {
43445ae9d61Sreyk 		/* dst identity */
43545ae9d61Sreyk 		iov[iov_cnt].iov_base = sa_dstid;
43645ae9d61Sreyk 		iov[iov_cnt].iov_len = sa_dstid->sadb_ident_len * 8;
43745ae9d61Sreyk 		smsg.sadb_msg_len += sa_dstid->sadb_ident_len;
43845ae9d61Sreyk 		iov_cnt++;
43945ae9d61Sreyk 	}
44045ae9d61Sreyk 
4410347364bStobhe 	if (flow->flow_rdomain >= 0) {
4420347364bStobhe 		iov[iov_cnt].iov_base = &sa_rdomain;
4430347364bStobhe 		iov[iov_cnt].iov_len = sizeof(sa_rdomain);
4440347364bStobhe 		smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len;
4450347364bStobhe 		iov_cnt++;
4460347364bStobhe 	}
447eab256a9Stobhe #undef PAD
4480347364bStobhe 
449e7c3d382Stobhe 	ret = pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL);
45045ae9d61Sreyk 
45145ae9d61Sreyk 	free(sa_srcid);
45245ae9d61Sreyk 	free(sa_dstid);
45345ae9d61Sreyk 
45445ae9d61Sreyk 	return (ret);
45545ae9d61Sreyk }
45645ae9d61Sreyk 
45745ae9d61Sreyk int
pfkey_sa(struct iked * env,uint8_t satype,uint8_t action,struct iked_childsa * sa)4588f6f6c19Stobhe pfkey_sa(struct iked *env, uint8_t satype, uint8_t action, struct iked_childsa *sa)
45945ae9d61Sreyk {
4601c18b693Stobhe 	char			 iface[IF_NAMESIZE];
46145ae9d61Sreyk 	struct sadb_msg		 smsg;
46245ae9d61Sreyk 	struct sadb_sa		 sadb;
463c0b327e6Spatrick 	struct sadb_address	 sa_src, sa_dst, sa_pxy;
46445ae9d61Sreyk 	struct sadb_key		 sa_authkey, sa_enckey;
465b0eeedd0Smikeb 	struct sadb_lifetime	 sa_ltime_hard, sa_ltime_soft;
46645ae9d61Sreyk 	struct sadb_x_udpencap	 udpencap;
46745ae9d61Sreyk 	struct sadb_x_tag	 sa_tag;
4681c18b693Stobhe 	struct sadb_x_iface	 sa_iface;
4690d1109d5Sreyk 	char			*tag = NULL;
470dd7958f8Sreyk 	struct sadb_x_tap	 sa_tap;
4710347364bStobhe 	struct sadb_x_rdomain	 sa_rdomain;
472c0b327e6Spatrick 	struct sockaddr_storage	 ssrc, sdst, spxy;
47345ae9d61Sreyk 	struct sadb_ident	*sa_srcid, *sa_dstid;
474b0eeedd0Smikeb 	struct iked_lifetime	*lt;
475dd7958f8Sreyk 	struct iked_policy	*pol;
476c0b327e6Spatrick 	struct iked_addr	*dst;
47745ae9d61Sreyk 	struct iovec		 iov[IOV_CNT];
4781c18b693Stobhe 	const char		*errstr = NULL;
4791c18b693Stobhe 	uint32_t		 ifminor;
480d09d3a7dSreyk 	uint32_t		 jitter;
4814292c78bSreyk 	int			 iov_cnt;
482489f3a3dSmpi 	int			 ret, dotap = 0;
483eab256a9Stobhe 	uint64_t		 pad = 0;
484eab256a9Stobhe 	size_t			 padlen;
48545ae9d61Sreyk 
48645ae9d61Sreyk 	sa_srcid = sa_dstid = NULL;
48745ae9d61Sreyk 
488dd7958f8Sreyk 	if (sa->csa_ikesa == NULL || sa->csa_ikesa->sa_policy == NULL) {
489dd7958f8Sreyk 		log_warn("%s: invalid SA and policy", __func__);
490dd7958f8Sreyk 		return (-1);
491dd7958f8Sreyk 	}
492dd7958f8Sreyk 	pol = sa->csa_ikesa->sa_policy;
493b0eeedd0Smikeb 	lt = &pol->pol_lifetime;
494dd7958f8Sreyk 
49545ae9d61Sreyk 	bzero(&ssrc, sizeof(ssrc));
49645ae9d61Sreyk 	memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc));
49745ae9d61Sreyk 	if (socket_af((struct sockaddr *)&ssrc, 0) == -1) {
49845ae9d61Sreyk 		log_warn("%s: invalid address", __func__);
49945ae9d61Sreyk 		return (-1);
50045ae9d61Sreyk 	}
50145ae9d61Sreyk 
502c0b327e6Spatrick 	dst = (action == IKED_SADB_UPDATE_SA_ADDRESSES &&
503c0b327e6Spatrick 	    sa->csa_dir == IPSP_DIRECTION_OUT) ?
504c0b327e6Spatrick 	    &sa->csa_ikesa->sa_peer_loaded :
505c0b327e6Spatrick 	    sa->csa_peer;
50645ae9d61Sreyk 	bzero(&sdst, sizeof(sdst));
507c0b327e6Spatrick 	memcpy(&sdst, &dst->addr, sizeof(sdst));
50845ae9d61Sreyk 	if (socket_af((struct sockaddr *)&sdst, 0) == -1) {
50945ae9d61Sreyk 		log_warn("%s: invalid address", __func__);
51045ae9d61Sreyk 		return (-1);
51145ae9d61Sreyk 	}
51245ae9d61Sreyk 
513c0b327e6Spatrick 	bzero(&spxy, sizeof(spxy));
514c0b327e6Spatrick 	if (dst != sa->csa_peer) {
515c0b327e6Spatrick 		memcpy(&spxy, &sa->csa_peer->addr, sizeof(spxy));
516c0b327e6Spatrick 		if (socket_af((struct sockaddr *)&spxy, 0) == -1) {
517c0b327e6Spatrick 			log_warn("%s: invalid address", __func__);
518c0b327e6Spatrick 			return (-1);
519c0b327e6Spatrick 		}
520c0b327e6Spatrick 	}
521c0b327e6Spatrick 
52245ae9d61Sreyk 	bzero(&smsg, sizeof(smsg));
52345ae9d61Sreyk 	smsg.sadb_msg_version = PF_KEY_V2;
524b0eeedd0Smikeb 	smsg.sadb_msg_seq = ++sadb_msg_seq;
52545ae9d61Sreyk 	smsg.sadb_msg_pid = getpid();
52645ae9d61Sreyk 	smsg.sadb_msg_len = sizeof(smsg) / 8;
52745ae9d61Sreyk 	smsg.sadb_msg_type = action;
52845ae9d61Sreyk 	smsg.sadb_msg_satype = satype;
52945ae9d61Sreyk 
53045ae9d61Sreyk 	bzero(&sadb, sizeof(sadb));
53145ae9d61Sreyk 	sadb.sadb_sa_len = sizeof(sadb) / 8;
53245ae9d61Sreyk 	sadb.sadb_sa_exttype = SADB_EXT_SA;
53345ae9d61Sreyk 	sadb.sadb_sa_spi = htonl(sa->csa_spi.spi);
53445ae9d61Sreyk 	sadb.sadb_sa_state = SADB_SASTATE_MATURE;
535d7243a54Smikeb 	sadb.sadb_sa_replay = 64;
53645ae9d61Sreyk 
53703f6ad09Smarkus 	if (!sa->csa_transport)
53845ae9d61Sreyk 		sadb.sadb_sa_flags |= SADB_X_SAFLAGS_TUNNEL;
53945ae9d61Sreyk 
540d7243a54Smikeb 	if (sa->csa_esn)
541d7243a54Smikeb 		sadb.sadb_sa_flags |= SADB_X_SAFLAGS_ESN;
542d7243a54Smikeb 
54345ae9d61Sreyk 	bzero(&sa_src, sizeof(sa_src));
54445ae9d61Sreyk 	sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
54545ae9d61Sreyk 	sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
54645ae9d61Sreyk 
54745ae9d61Sreyk 	bzero(&sa_dst, sizeof(sa_dst));
54845ae9d61Sreyk 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
54945ae9d61Sreyk 	sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
55045ae9d61Sreyk 
551c0b327e6Spatrick 	bzero(&sa_pxy, sizeof(sa_pxy));
552c0b327e6Spatrick 	sa_pxy.sadb_address_len = (sizeof(sa_pxy) + ROUNDUP(spxy.ss_len)) / 8;
553c0b327e6Spatrick 	sa_pxy.sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
554c0b327e6Spatrick 
55545ae9d61Sreyk 	bzero(&sa_authkey, sizeof(sa_authkey));
55645ae9d61Sreyk 	bzero(&sa_enckey, sizeof(sa_enckey));
55745ae9d61Sreyk 	bzero(&udpencap, sizeof udpencap);
558b0eeedd0Smikeb 	bzero(&sa_ltime_hard, sizeof(sa_ltime_hard));
559b0eeedd0Smikeb 	bzero(&sa_ltime_soft, sizeof(sa_ltime_soft));
5601c18b693Stobhe 	bzero(&sa_iface, sizeof(sa_iface));
56145ae9d61Sreyk 
5620347364bStobhe 	if (pol->pol_rdomain >= 0) {
5630347364bStobhe 		bzero(&sa_rdomain, sizeof(sa_rdomain));
5640347364bStobhe 		sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN;
5650347364bStobhe 		sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8;
5660347364bStobhe 		if (satype == SADB_X_SATYPE_IPCOMP) {
5670347364bStobhe 			/* IPCOMP SAs are always in the pol_rdomain */
5680347364bStobhe 			sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain;
5690347364bStobhe 			sa_rdomain.sadb_x_rdomain_dom2 = pol->pol_rdomain;
5700347364bStobhe 		} else if (sa->csa_dir == IPSP_DIRECTION_OUT) {
5710347364bStobhe 			/* switch rdomain on encrypt/decrypt */
5720347364bStobhe 			sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain;
5730347364bStobhe 			sa_rdomain.sadb_x_rdomain_dom2 = iked_rdomain;
5740347364bStobhe 		} else {
5750347364bStobhe 			sa_rdomain.sadb_x_rdomain_dom1 = iked_rdomain;
5760347364bStobhe 			sa_rdomain.sadb_x_rdomain_dom2 = pol->pol_rdomain;
5770347364bStobhe 		}
5780347364bStobhe 	}
5790347364bStobhe 
58045ae9d61Sreyk 	if (action == SADB_DELETE)
58145ae9d61Sreyk 		goto send;
58245ae9d61Sreyk 
583e3b1b16bSreyk 	if (satype == SADB_SATYPE_ESP &&
584e3b1b16bSreyk 	    sa->csa_ikesa->sa_udpencap && sa->csa_ikesa->sa_natt) {
585e3b1b16bSreyk 		sadb.sadb_sa_flags |= SADB_X_SAFLAGS_UDPENCAP;
586e3b1b16bSreyk 		udpencap.sadb_x_udpencap_exttype = SADB_X_EXT_UDPENCAP;
587e3b1b16bSreyk 		udpencap.sadb_x_udpencap_len = sizeof(udpencap) / 8;
588e3b1b16bSreyk 		udpencap.sadb_x_udpencap_port =
589e3b1b16bSreyk 		    sa->csa_ikesa->sa_peer.addr_port;
590e3b1b16bSreyk 
591e3b1b16bSreyk 		log_debug("%s: udpencap port %d", __func__,
592e3b1b16bSreyk 		    ntohs(udpencap.sadb_x_udpencap_port));
593e3b1b16bSreyk 	}
594e3b1b16bSreyk 
595c0b327e6Spatrick 	if (action == IKED_SADB_UPDATE_SA_ADDRESSES) {
596c0b327e6Spatrick 		smsg.sadb_msg_type = SADB_UPDATE;
597c0b327e6Spatrick 		goto send;
598c0b327e6Spatrick 	}
599c0b327e6Spatrick 
600b0eeedd0Smikeb 	if ((action == SADB_ADD || action == SADB_UPDATE) &&
601b0eeedd0Smikeb 	    !sa->csa_persistent && (lt->lt_bytes || lt->lt_seconds)) {
602b0eeedd0Smikeb 		sa_ltime_hard.sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
603b0eeedd0Smikeb 		sa_ltime_hard.sadb_lifetime_len = sizeof(sa_ltime_hard) / 8;
604b0eeedd0Smikeb 		sa_ltime_hard.sadb_lifetime_bytes = lt->lt_bytes;
605b0eeedd0Smikeb 		sa_ltime_hard.sadb_lifetime_addtime = lt->lt_seconds;
606b0eeedd0Smikeb 
6075b3564b9Sreyk 		/* double the lifetime for ipcomp; disable byte lifetime */
6081a394ec9Spatrick 		if (satype == SADB_X_SATYPE_IPCOMP) {
60965f3034eSmarkus 			sa_ltime_hard.sadb_lifetime_addtime *= 2;
6101a394ec9Spatrick 			sa_ltime_hard.sadb_lifetime_bytes = 0;
6111a394ec9Spatrick 		}
61265f3034eSmarkus 
613b0eeedd0Smikeb 		sa_ltime_soft.sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
614b0eeedd0Smikeb 		sa_ltime_soft.sadb_lifetime_len = sizeof(sa_ltime_soft) / 8;
615b0eeedd0Smikeb 		/* set randomly to 85-95% */
616b0eeedd0Smikeb 		jitter = 850 + arc4random_uniform(100);
617b0eeedd0Smikeb 		sa_ltime_soft.sadb_lifetime_bytes =
61865f3034eSmarkus 		    (sa_ltime_hard.sadb_lifetime_bytes * jitter) / 1000;
619b0eeedd0Smikeb 		sa_ltime_soft.sadb_lifetime_addtime =
62065f3034eSmarkus 		    (sa_ltime_hard.sadb_lifetime_addtime * jitter) / 1000;
621b0eeedd0Smikeb 	}
622b0eeedd0Smikeb 
62345ae9d61Sreyk 	/* XXX handle NULL encryption or NULL auth or combined encr/auth */
62445ae9d61Sreyk 	if (action == SADB_ADD &&
62545ae9d61Sreyk 	    !ibuf_length(sa->csa_integrkey) && !ibuf_length(sa->csa_encrkey) &&
62645ae9d61Sreyk 	    satype != SADB_X_SATYPE_IPCOMP && satype != SADB_X_SATYPE_IPIP) {
62745ae9d61Sreyk 		log_warnx("%s: no key specified", __func__);
62845ae9d61Sreyk 		return (-1);
62945ae9d61Sreyk 	}
63045ae9d61Sreyk 
63137ce25f4Smarkus 	if (sa->csa_integrid)
63245ae9d61Sreyk 		if (pfkey_map(pfkey_integr,
63337ce25f4Smarkus 		    sa->csa_integrid, &sadb.sadb_sa_auth) == -1) {
63445ae9d61Sreyk 			log_warnx("%s: unsupported integrity algorithm %s",
63537ce25f4Smarkus 			    __func__, print_map(sa->csa_integrid,
63645ae9d61Sreyk 			    ikev2_xformauth_map));
63745ae9d61Sreyk 			return (-1);
63845ae9d61Sreyk 		}
63945ae9d61Sreyk 
64037ce25f4Smarkus 	if (sa->csa_encrid)
64145ae9d61Sreyk 		if (pfkey_map(pfkey_encr,
64237ce25f4Smarkus 		    sa->csa_encrid, &sadb.sadb_sa_encrypt) == -1) {
64345ae9d61Sreyk 			log_warnx("%s: unsupported encryption algorithm %s",
64437ce25f4Smarkus 			    __func__, print_map(sa->csa_encrid,
64545ae9d61Sreyk 			    ikev2_xformencr_map));
64645ae9d61Sreyk 			return (-1);
64745ae9d61Sreyk 		}
64845ae9d61Sreyk 
64945ae9d61Sreyk 	if (ibuf_length(sa->csa_integrkey)) {
65045ae9d61Sreyk 		sa_authkey.sadb_key_len = (sizeof(sa_authkey) +
651eab256a9Stobhe 		    ROUNDUP(ibuf_size(sa->csa_integrkey))) / 8;
65245ae9d61Sreyk 		sa_authkey.sadb_key_exttype = SADB_EXT_KEY_AUTH;
65345ae9d61Sreyk 		sa_authkey.sadb_key_bits =
65445ae9d61Sreyk 		    8 * ibuf_size(sa->csa_integrkey);
65545ae9d61Sreyk 	}
65645ae9d61Sreyk 
65745ae9d61Sreyk 	if (ibuf_length(sa->csa_encrkey)) {
65845ae9d61Sreyk 		sa_enckey.sadb_key_len = (sizeof(sa_enckey) +
659eab256a9Stobhe 		    ROUNDUP(ibuf_size(sa->csa_encrkey))) / 8;
66045ae9d61Sreyk 		sa_enckey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
66145ae9d61Sreyk 		sa_enckey.sadb_key_bits =
66245ae9d61Sreyk 		    8 * ibuf_size(sa->csa_encrkey);
66345ae9d61Sreyk 	}
66445ae9d61Sreyk 
66503f6ad09Smarkus 	/* we only support deflate */
66603f6ad09Smarkus 	if (satype == SADB_X_SATYPE_IPCOMP)
66703f6ad09Smarkus 		sadb.sadb_sa_encrypt = SADB_X_CALG_DEFLATE;
66803f6ad09Smarkus 
669834f9deeSmarkus 	/* Note that we need to swap the IDs for incoming SAs (SADB_UPDATE) */
670834f9deeSmarkus 	if (action != SADB_UPDATE) {
671834f9deeSmarkus 		sa_srcid = pfkey_id2ident(
672834f9deeSmarkus 		    IKESA_SRCID(sa->csa_ikesa), SADB_EXT_IDENTITY_SRC);
673834f9deeSmarkus 		sa_dstid = pfkey_id2ident(
674834f9deeSmarkus 		    IKESA_DSTID(sa->csa_ikesa), SADB_EXT_IDENTITY_DST);
675834f9deeSmarkus 	} else {
676834f9deeSmarkus 		sa_srcid = pfkey_id2ident(
677834f9deeSmarkus 		    IKESA_DSTID(sa->csa_ikesa), SADB_EXT_IDENTITY_SRC);
678834f9deeSmarkus 		sa_dstid = pfkey_id2ident(
679834f9deeSmarkus 		    IKESA_SRCID(sa->csa_ikesa), SADB_EXT_IDENTITY_DST);
680834f9deeSmarkus 	}
68145ae9d61Sreyk 
68245ae9d61Sreyk 	tag = sa->csa_ikesa->sa_tag;
68345ae9d61Sreyk 	if (tag != NULL && *tag != '\0') {
68445ae9d61Sreyk 		bzero(&sa_tag, sizeof(sa_tag));
68545ae9d61Sreyk 		sa_tag.sadb_x_tag_exttype = SADB_X_EXT_TAG;
68645ae9d61Sreyk 		sa_tag.sadb_x_tag_len =
68745ae9d61Sreyk 		    (ROUNDUP(strlen(tag) + 1) + sizeof(sa_tag)) / 8;
68845ae9d61Sreyk 		sa_tag.sadb_x_tag_taglen = strlen(tag) + 1;
68945ae9d61Sreyk 	} else
69045ae9d61Sreyk 		tag = NULL;
69145ae9d61Sreyk 
692dd7958f8Sreyk 	if (pol->pol_tap != 0) {
693489f3a3dSmpi 		dotap = 1;
694dd7958f8Sreyk 		bzero(&sa_tap, sizeof(sa_tap));
695dd7958f8Sreyk 		sa_tap.sadb_x_tap_exttype = SADB_X_EXT_TAP;
696dd7958f8Sreyk 		sa_tap.sadb_x_tap_len = sizeof(sa_tap) / 8;
697dd7958f8Sreyk 		sa_tap.sadb_x_tap_unit = pol->pol_tap;
698dd7958f8Sreyk 	}
699dd7958f8Sreyk 
7001c18b693Stobhe 	if (pol->pol_flags & IKED_POLICY_ROUTING) {
7011c18b693Stobhe 		sa_iface.sadb_x_iface_exttype = SADB_X_EXT_IFACE;
7021c18b693Stobhe 		sa_iface.sadb_x_iface_len = sizeof(sa_iface) / 8;
7033cd1d0b5Stobhe 		if (if_indextoname(pol->pol_iface, iface) == 0) {
7043cd1d0b5Stobhe 			log_warn("%s: unsupported interface %d",
7053cd1d0b5Stobhe 			    __func__, pol->pol_iface);
7061c18b693Stobhe 			return (-1);
7071c18b693Stobhe 		}
7081c18b693Stobhe 		ifminor = strtonum(iface + strlen("sec"), 0, UINT_MAX, &errstr);
7091c18b693Stobhe 		if (errstr != NULL) {
7101c18b693Stobhe 			log_warnx("%s: unsupported interface %s",
7111c18b693Stobhe 			    __func__, iface);
7121c18b693Stobhe 			return (-1);
7131c18b693Stobhe 		}
7141c18b693Stobhe 		sa_iface.sadb_x_iface_unit = ifminor;
7151c18b693Stobhe 		sa_iface.sadb_x_iface_direction = sa->csa_dir;
7161c18b693Stobhe 	}
7171c18b693Stobhe 
71845ae9d61Sreyk  send:
719eab256a9Stobhe 
720eab256a9Stobhe #define PAD(len)					\
721eab256a9Stobhe 	padlen = ROUNDUP((len)) - (len);		\
722eab256a9Stobhe 	if (padlen) {					\
723eab256a9Stobhe 		iov[iov_cnt].iov_base = &pad;		\
724eab256a9Stobhe 		iov[iov_cnt].iov_len = padlen;		\
725eab256a9Stobhe 		iov_cnt++;				\
726eab256a9Stobhe 	}
727eab256a9Stobhe 
72845ae9d61Sreyk 	iov_cnt = 0;
72945ae9d61Sreyk 
73045ae9d61Sreyk 	/* header */
73145ae9d61Sreyk 	iov[iov_cnt].iov_base = &smsg;
73245ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(smsg);
73345ae9d61Sreyk 	iov_cnt++;
73445ae9d61Sreyk 
73545ae9d61Sreyk 	/* sa */
73645ae9d61Sreyk 	iov[iov_cnt].iov_base = &sadb;
73745ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sadb);
73845ae9d61Sreyk 	smsg.sadb_msg_len += sadb.sadb_sa_len;
73945ae9d61Sreyk 	iov_cnt++;
74045ae9d61Sreyk 
74145ae9d61Sreyk 	/* src addr */
74245ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_src;
74345ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_src);
74445ae9d61Sreyk 	iov_cnt++;
74545ae9d61Sreyk 	iov[iov_cnt].iov_base = &ssrc;
746eab256a9Stobhe 	iov[iov_cnt].iov_len = ssrc.ss_len;
74745ae9d61Sreyk 	smsg.sadb_msg_len += sa_src.sadb_address_len;
74845ae9d61Sreyk 	iov_cnt++;
749eab256a9Stobhe 	PAD(ssrc.ss_len);
75045ae9d61Sreyk 
75145ae9d61Sreyk 	/* dst addr */
75245ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_dst;
75345ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_dst);
75445ae9d61Sreyk 	iov_cnt++;
75545ae9d61Sreyk 	iov[iov_cnt].iov_base = &sdst;
756eab256a9Stobhe 	iov[iov_cnt].iov_len = sdst.ss_len;
75745ae9d61Sreyk 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
75845ae9d61Sreyk 	iov_cnt++;
759eab256a9Stobhe 	PAD(sdst.ss_len);
76045ae9d61Sreyk 
761c0b327e6Spatrick 	if (spxy.ss_len) {
762c0b327e6Spatrick 		/* pxy addr */
763c0b327e6Spatrick 		iov[iov_cnt].iov_base = &sa_pxy;
764c0b327e6Spatrick 		iov[iov_cnt].iov_len = sizeof(sa_pxy);
765c0b327e6Spatrick 		iov_cnt++;
766c0b327e6Spatrick 		iov[iov_cnt].iov_base = &spxy;
767eab256a9Stobhe 		iov[iov_cnt].iov_len = spxy.ss_len;
768c0b327e6Spatrick 		smsg.sadb_msg_len += sa_pxy.sadb_address_len;
769c0b327e6Spatrick 		iov_cnt++;
770eab256a9Stobhe 		PAD(spxy.ss_len);
771c0b327e6Spatrick 	}
772c0b327e6Spatrick 
773b0eeedd0Smikeb 	if (sa_ltime_soft.sadb_lifetime_len) {
774b0eeedd0Smikeb 		/* soft lifetime */
775b0eeedd0Smikeb 		iov[iov_cnt].iov_base = &sa_ltime_soft;
776b0eeedd0Smikeb 		iov[iov_cnt].iov_len = sizeof(sa_ltime_soft);
777b0eeedd0Smikeb 		smsg.sadb_msg_len += sa_ltime_soft.sadb_lifetime_len;
778b0eeedd0Smikeb 		iov_cnt++;
779b0eeedd0Smikeb 	}
780b0eeedd0Smikeb 
781b0eeedd0Smikeb 	if (sa_ltime_hard.sadb_lifetime_len) {
782b0eeedd0Smikeb 		/* hard lifetime */
783b0eeedd0Smikeb 		iov[iov_cnt].iov_base = &sa_ltime_hard;
784b0eeedd0Smikeb 		iov[iov_cnt].iov_len = sizeof(sa_ltime_hard);
785b0eeedd0Smikeb 		smsg.sadb_msg_len += sa_ltime_hard.sadb_lifetime_len;
786b0eeedd0Smikeb 		iov_cnt++;
787b0eeedd0Smikeb 	}
788b0eeedd0Smikeb 
78945ae9d61Sreyk 	if (udpencap.sadb_x_udpencap_len) {
79045ae9d61Sreyk 		iov[iov_cnt].iov_base = &udpencap;
79145ae9d61Sreyk 		iov[iov_cnt].iov_len = sizeof(udpencap);
79245ae9d61Sreyk 		smsg.sadb_msg_len += udpencap.sadb_x_udpencap_len;
79345ae9d61Sreyk 		iov_cnt++;
79445ae9d61Sreyk 	}
79545ae9d61Sreyk 
79645ae9d61Sreyk 	if (sa_enckey.sadb_key_len) {
79745ae9d61Sreyk 		/* encryption key */
79845ae9d61Sreyk 		iov[iov_cnt].iov_base = &sa_enckey;
79945ae9d61Sreyk 		iov[iov_cnt].iov_len = sizeof(sa_enckey);
80045ae9d61Sreyk 		iov_cnt++;
80145ae9d61Sreyk 		iov[iov_cnt].iov_base = ibuf_data(sa->csa_encrkey);
802eab256a9Stobhe 		iov[iov_cnt].iov_len = ibuf_size(sa->csa_encrkey);
80345ae9d61Sreyk 		smsg.sadb_msg_len += sa_enckey.sadb_key_len;
80445ae9d61Sreyk 		iov_cnt++;
805eab256a9Stobhe 		PAD(ibuf_size(sa->csa_encrkey));
80645ae9d61Sreyk 	}
80745ae9d61Sreyk 	if (sa_authkey.sadb_key_len) {
80845ae9d61Sreyk 		/* authentication key */
80945ae9d61Sreyk 		iov[iov_cnt].iov_base = &sa_authkey;
81045ae9d61Sreyk 		iov[iov_cnt].iov_len = sizeof(sa_authkey);
81145ae9d61Sreyk 		iov_cnt++;
81245ae9d61Sreyk 		iov[iov_cnt].iov_base = ibuf_data(sa->csa_integrkey);
813eab256a9Stobhe 		iov[iov_cnt].iov_len = ibuf_size(sa->csa_integrkey);
81445ae9d61Sreyk 		smsg.sadb_msg_len += sa_authkey.sadb_key_len;
81545ae9d61Sreyk 		iov_cnt++;
816eab256a9Stobhe 		PAD(ibuf_size(sa->csa_integrkey));
81745ae9d61Sreyk 	}
81845ae9d61Sreyk 
81945ae9d61Sreyk 	if (sa_srcid) {
82045ae9d61Sreyk 		/* src identity */
82145ae9d61Sreyk 		iov[iov_cnt].iov_base = sa_srcid;
82245ae9d61Sreyk 		iov[iov_cnt].iov_len = sa_srcid->sadb_ident_len * 8;
82345ae9d61Sreyk 		smsg.sadb_msg_len += sa_srcid->sadb_ident_len;
82445ae9d61Sreyk 		iov_cnt++;
82545ae9d61Sreyk 	}
82645ae9d61Sreyk 	if (sa_dstid) {
82745ae9d61Sreyk 		/* dst identity */
82845ae9d61Sreyk 		iov[iov_cnt].iov_base = sa_dstid;
82945ae9d61Sreyk 		iov[iov_cnt].iov_len = sa_dstid->sadb_ident_len * 8;
83045ae9d61Sreyk 		smsg.sadb_msg_len += sa_dstid->sadb_ident_len;
83145ae9d61Sreyk 		iov_cnt++;
83245ae9d61Sreyk 	}
83345ae9d61Sreyk 
83445ae9d61Sreyk 	if (tag != NULL) {
83545ae9d61Sreyk 		/* tag identity */
83645ae9d61Sreyk 		iov[iov_cnt].iov_base = &sa_tag;
83745ae9d61Sreyk 		iov[iov_cnt].iov_len = sizeof(sa_tag);
83845ae9d61Sreyk 		iov_cnt++;
83945ae9d61Sreyk 		iov[iov_cnt].iov_base = tag;
840eab256a9Stobhe 		iov[iov_cnt].iov_len = strlen(tag) + 1;
84145ae9d61Sreyk 		smsg.sadb_msg_len += sa_tag.sadb_x_tag_len;
84245ae9d61Sreyk 		iov_cnt++;
843eab256a9Stobhe 		PAD(strlen(tag) + 1);
84445ae9d61Sreyk 	}
84545ae9d61Sreyk 
8461c18b693Stobhe 	if (sa_iface.sadb_x_iface_len) {
8471c18b693Stobhe 		iov[iov_cnt].iov_base = &sa_iface;
8481c18b693Stobhe 		iov[iov_cnt].iov_len = sa_iface.sadb_x_iface_len * 8;
8491c18b693Stobhe 		smsg.sadb_msg_len += sa_iface.sadb_x_iface_len;
8501c18b693Stobhe 		iov_cnt++;
8511c18b693Stobhe 	}
8521c18b693Stobhe 
853489f3a3dSmpi 	if (dotap != 0) {
854dd7958f8Sreyk 		/* enc(4) device tap unit */
855dd7958f8Sreyk 		iov[iov_cnt].iov_base = &sa_tap;
856dd7958f8Sreyk 		iov[iov_cnt].iov_len = sizeof(sa_tap);
857dd7958f8Sreyk 		smsg.sadb_msg_len += sa_tap.sadb_x_tap_len;
858dd7958f8Sreyk 		iov_cnt++;
859dd7958f8Sreyk 	}
860dd7958f8Sreyk 
8610347364bStobhe 	if (pol->pol_rdomain >= 0) {
8620347364bStobhe 		iov[iov_cnt].iov_base = &sa_rdomain;
8630347364bStobhe 		iov[iov_cnt].iov_len = sizeof(sa_rdomain);
8640347364bStobhe 		smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len;
8650347364bStobhe 		iov_cnt++;
8660347364bStobhe 	}
867eab256a9Stobhe #undef PAD
8680347364bStobhe 
869e7c3d382Stobhe 	ret = pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL);
870160b250fSreyk 
871160b250fSreyk 	free(sa_srcid);
872160b250fSreyk 	free(sa_dstid);
873160b250fSreyk 
874160b250fSreyk 	return ret;
87545ae9d61Sreyk }
87645ae9d61Sreyk 
87745ae9d61Sreyk int
pfkey_sa_lookup(struct iked * env,struct iked_childsa * sa,uint64_t * last_used,struct iked_sastats * stats)878*f36db9c4Syasuoka pfkey_sa_lookup(struct iked *env, struct iked_childsa *sa, uint64_t *last_used,
879*f36db9c4Syasuoka     struct iked_sastats *stats)
880131966b1Smarkus {
8810347364bStobhe 	struct iked_policy	*pol = sa->csa_ikesa->sa_policy;
882131966b1Smarkus 	struct sadb_msg		*msg, smsg;
883131966b1Smarkus 	struct sadb_address	 sa_src, sa_dst;
884131966b1Smarkus 	struct sadb_sa		 sadb;
8850347364bStobhe 	struct sadb_x_rdomain	 sa_rdomain;
886131966b1Smarkus 	struct sadb_lifetime	*sa_life;
887*f36db9c4Syasuoka 	struct sadb_x_counter	*sa_counter;
888131966b1Smarkus 	struct sockaddr_storage	 ssrc, sdst;
889131966b1Smarkus 	struct iovec		 iov[IOV_CNT];
890eab256a9Stobhe 	uint64_t		 pad = 0;
891eab256a9Stobhe 	size_t			 padlen;
892d09d3a7dSreyk 	uint8_t			*data;
893131966b1Smarkus 	ssize_t			 n;
8940347364bStobhe 	int			 iov_cnt, ret = -1, rdomain;
895d09d3a7dSreyk 	uint8_t			 satype;
896131966b1Smarkus 
8978389fa3fStobhe 	if (last_used)
898131966b1Smarkus 		*last_used = 0;
899131966b1Smarkus 
900131966b1Smarkus 	if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
901131966b1Smarkus 		return (-1);
902131966b1Smarkus 
903131966b1Smarkus 	bzero(&ssrc, sizeof(ssrc));
904131966b1Smarkus 	memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc));
905131966b1Smarkus 	if (socket_af((struct sockaddr *)&ssrc, 0) == -1) {
906131966b1Smarkus 		log_warn("%s: invalid address", __func__);
907131966b1Smarkus 		return (-1);
908131966b1Smarkus 	}
909131966b1Smarkus 
910131966b1Smarkus 	bzero(&sdst, sizeof(sdst));
911131966b1Smarkus 	memcpy(&sdst, &sa->csa_peer->addr, sizeof(sdst));
912131966b1Smarkus 	if (socket_af((struct sockaddr *)&sdst, 0) == -1) {
913131966b1Smarkus 		log_warn("%s: invalid address", __func__);
914131966b1Smarkus 		return (-1);
915131966b1Smarkus 	}
916131966b1Smarkus 
917131966b1Smarkus 	bzero(&smsg, sizeof(smsg));
918131966b1Smarkus 	smsg.sadb_msg_version = PF_KEY_V2;
919131966b1Smarkus 	smsg.sadb_msg_seq = ++sadb_msg_seq;
920131966b1Smarkus 	smsg.sadb_msg_pid = getpid();
921131966b1Smarkus 	smsg.sadb_msg_len = sizeof(smsg) / 8;
922131966b1Smarkus 	smsg.sadb_msg_type = SADB_GET;
923131966b1Smarkus 	smsg.sadb_msg_satype = satype;
924131966b1Smarkus 
925131966b1Smarkus 	bzero(&sadb, sizeof(sadb));
926131966b1Smarkus 	sadb.sadb_sa_len = sizeof(sadb) / 8;
927131966b1Smarkus 	sadb.sadb_sa_exttype = SADB_EXT_SA;
928131966b1Smarkus 	sadb.sadb_sa_spi = htonl(sa->csa_spi.spi);
929131966b1Smarkus 	sadb.sadb_sa_state = SADB_SASTATE_MATURE;
930131966b1Smarkus 	sadb.sadb_sa_replay = 64;
931131966b1Smarkus 
9320347364bStobhe 	if (pol->pol_rdomain >= 0) {
9330347364bStobhe 		rdomain = (sa->csa_dir == IPSP_DIRECTION_IN) ?
9340347364bStobhe 		    iked_rdomain : pol->pol_rdomain;
9350347364bStobhe 		bzero(&sa_rdomain, sizeof(sa_rdomain));
9360347364bStobhe 		sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN;
9370347364bStobhe 		sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8;
9380347364bStobhe 		sa_rdomain.sadb_x_rdomain_dom1 = rdomain;
9390347364bStobhe 	}
9400347364bStobhe 
941131966b1Smarkus 	bzero(&sa_src, sizeof(sa_src));
942131966b1Smarkus 	sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
943131966b1Smarkus 	sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
944131966b1Smarkus 
945131966b1Smarkus 	bzero(&sa_dst, sizeof(sa_dst));
946131966b1Smarkus 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
947131966b1Smarkus 	sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
948131966b1Smarkus 
949131966b1Smarkus 	iov_cnt = 0;
950131966b1Smarkus 
951eab256a9Stobhe #define PAD(len)					\
952eab256a9Stobhe 	padlen = ROUNDUP((len)) - (len);		\
953eab256a9Stobhe 	if (padlen) {					\
954eab256a9Stobhe 		iov[iov_cnt].iov_base = &pad;		\
955eab256a9Stobhe 		iov[iov_cnt].iov_len = padlen;		\
956eab256a9Stobhe 		iov_cnt++;				\
957eab256a9Stobhe 	}
958eab256a9Stobhe 
959131966b1Smarkus 	/* header */
960131966b1Smarkus 	iov[iov_cnt].iov_base = &smsg;
961131966b1Smarkus 	iov[iov_cnt].iov_len = sizeof(smsg);
962131966b1Smarkus 	iov_cnt++;
963131966b1Smarkus 
964131966b1Smarkus 	/* sa */
965131966b1Smarkus 	iov[iov_cnt].iov_base = &sadb;
966131966b1Smarkus 	iov[iov_cnt].iov_len = sizeof(sadb);
967131966b1Smarkus 	smsg.sadb_msg_len += sadb.sadb_sa_len;
968131966b1Smarkus 	iov_cnt++;
969131966b1Smarkus 
970131966b1Smarkus 	/* src addr */
971131966b1Smarkus 	iov[iov_cnt].iov_base = &sa_src;
972131966b1Smarkus 	iov[iov_cnt].iov_len = sizeof(sa_src);
973131966b1Smarkus 	iov_cnt++;
974131966b1Smarkus 	iov[iov_cnt].iov_base = &ssrc;
975eab256a9Stobhe 	iov[iov_cnt].iov_len = ssrc.ss_len;
976131966b1Smarkus 	smsg.sadb_msg_len += sa_src.sadb_address_len;
977131966b1Smarkus 	iov_cnt++;
978eab256a9Stobhe 	PAD(ssrc.ss_len);
979131966b1Smarkus 
980131966b1Smarkus 	/* dst addr */
981131966b1Smarkus 	iov[iov_cnt].iov_base = &sa_dst;
982131966b1Smarkus 	iov[iov_cnt].iov_len = sizeof(sa_dst);
983131966b1Smarkus 	iov_cnt++;
984131966b1Smarkus 	iov[iov_cnt].iov_base = &sdst;
985eab256a9Stobhe 	iov[iov_cnt].iov_len = sdst.ss_len;
986131966b1Smarkus 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
987131966b1Smarkus 	iov_cnt++;
988eab256a9Stobhe 	PAD(sdst.ss_len);
989131966b1Smarkus 
9900347364bStobhe 	if (pol->pol_rdomain >= 0) {
9910347364bStobhe 		iov[iov_cnt].iov_base = &sa_rdomain;
9920347364bStobhe 		iov[iov_cnt].iov_len = sizeof(sa_rdomain);
9930347364bStobhe 		smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len;
9940347364bStobhe 		iov_cnt++;
9950347364bStobhe 	}
9960347364bStobhe 
997e7c3d382Stobhe 	if ((ret = pfkey_write(env, &smsg, iov, iov_cnt, &data, &n)) != 0)
998131966b1Smarkus 		return (-1);
999131966b1Smarkus 
1000131966b1Smarkus 	msg = (struct sadb_msg *)data;
1001131966b1Smarkus 	if (msg->sadb_msg_errno != 0) {
1002131966b1Smarkus 		errno = msg->sadb_msg_errno;
1003131966b1Smarkus 		ret = -1;
100433e4f7e3Stobhe 		if (errno == ESRCH)
100533e4f7e3Stobhe 			log_debug("%s: not found", __func__);
100633e4f7e3Stobhe 		else
1007131966b1Smarkus 			log_warn("%s: message", __func__);
1008131966b1Smarkus 		goto done;
1009131966b1Smarkus 	}
10108389fa3fStobhe 	if (last_used) {
10118389fa3fStobhe 		if ((sa_life = pfkey_find_ext(data, n,
10128389fa3fStobhe 		    SADB_X_EXT_LIFETIME_LASTUSE)) == NULL) {
1013bb108424Smarkus 			/* has never been used */
1014131966b1Smarkus 			ret = -1;
1015131966b1Smarkus 			goto done;
1016131966b1Smarkus 		}
1017131966b1Smarkus 		*last_used = sa_life->sadb_lifetime_usetime;
1018131966b1Smarkus 		log_debug("%s: last_used %llu", __func__, *last_used);
10198389fa3fStobhe 	}
1020*f36db9c4Syasuoka 	if (stats) {
1021*f36db9c4Syasuoka 		if ((sa_counter = pfkey_find_ext(data, n,
1022*f36db9c4Syasuoka 		    SADB_X_EXT_COUNTER)) == NULL) {
1023*f36db9c4Syasuoka 			/* has never been used */
1024*f36db9c4Syasuoka 			ret = -1;
1025*f36db9c4Syasuoka 			goto done;
1026*f36db9c4Syasuoka 		}
1027*f36db9c4Syasuoka 		stats->sas_ibytes = sa_counter->sadb_x_counter_ibytes;
1028*f36db9c4Syasuoka 		stats->sas_obytes = sa_counter->sadb_x_counter_obytes;
1029*f36db9c4Syasuoka 		stats->sas_ipackets = sa_counter->sadb_x_counter_ipackets;
1030*f36db9c4Syasuoka 		stats->sas_opackets = sa_counter->sadb_x_counter_opackets;
1031*f36db9c4Syasuoka 		stats->sas_idrops = sa_counter->sadb_x_counter_idrops;
1032*f36db9c4Syasuoka 		stats->sas_odrops = sa_counter->sadb_x_counter_odrops;
1033*f36db9c4Syasuoka 	}
1034131966b1Smarkus 
1035eab256a9Stobhe #undef PAD
1036131966b1Smarkus done:
10371e82d711Sderaadt 	freezero(data, n);
1038131966b1Smarkus 	return (ret);
1039131966b1Smarkus }
1040131966b1Smarkus 
1041131966b1Smarkus int
pfkey_sa_last_used(struct iked * env,struct iked_childsa * sa,uint64_t * last_used)10428f6f6c19Stobhe pfkey_sa_last_used(struct iked *env, struct iked_childsa *sa, uint64_t *last_used)
10438389fa3fStobhe {
1044*f36db9c4Syasuoka 	return pfkey_sa_lookup(env, sa, last_used, NULL);
10458389fa3fStobhe }
10468389fa3fStobhe 
10478389fa3fStobhe int
pfkey_sa_check_exists(struct iked * env,struct iked_childsa * sa)10488f6f6c19Stobhe pfkey_sa_check_exists(struct iked *env, struct iked_childsa *sa)
10498389fa3fStobhe {
1050*f36db9c4Syasuoka 	return pfkey_sa_lookup(env, sa, NULL, NULL);
1051*f36db9c4Syasuoka }
1052*f36db9c4Syasuoka 
1053*f36db9c4Syasuoka int
pfkey_sa_sastats(struct iked * env,struct iked_childsa * sa,struct iked_sastats * stats)1054*f36db9c4Syasuoka pfkey_sa_sastats(struct iked *env, struct iked_childsa *sa,
1055*f36db9c4Syasuoka     struct iked_sastats *stats)
1056*f36db9c4Syasuoka {
1057*f36db9c4Syasuoka 	return pfkey_sa_lookup(env, sa, NULL, stats);
10588389fa3fStobhe }
10598389fa3fStobhe 
10608389fa3fStobhe int
pfkey_sa_getspi(struct iked * env,uint8_t satype,struct iked_childsa * sa,uint32_t * spip)10618f6f6c19Stobhe pfkey_sa_getspi(struct iked *env, uint8_t satype, struct iked_childsa *sa,
1062d09d3a7dSreyk     uint32_t *spip)
106345ae9d61Sreyk {
106445ae9d61Sreyk 	struct sadb_msg		*msg, smsg;
106545ae9d61Sreyk 	struct sadb_address	 sa_src, sa_dst;
106645ae9d61Sreyk 	struct sadb_sa		*sa_ext;
106745ae9d61Sreyk 	struct sadb_spirange	 sa_spirange;
106845ae9d61Sreyk 	struct sockaddr_storage	 ssrc, sdst;
106945ae9d61Sreyk 	struct iovec		 iov[IOV_CNT];
1070eab256a9Stobhe 	uint64_t		 pad = 0;
1071eab256a9Stobhe 	size_t			 padlen;
1072d09d3a7dSreyk 	uint8_t			*data;
107345ae9d61Sreyk 	ssize_t			 n;
1074b0eeedd0Smikeb 	int			 iov_cnt, ret = -1;
107545ae9d61Sreyk 
107645ae9d61Sreyk 	bzero(&ssrc, sizeof(ssrc));
107745ae9d61Sreyk 	memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc));
107845ae9d61Sreyk 	if (socket_af((struct sockaddr *)&ssrc, 0) == -1) {
107945ae9d61Sreyk 		log_warn("%s: invalid address", __func__);
108045ae9d61Sreyk 		return (-1);
108145ae9d61Sreyk 	}
108245ae9d61Sreyk 
108345ae9d61Sreyk 	bzero(&sdst, sizeof(sdst));
108445ae9d61Sreyk 	memcpy(&sdst, &sa->csa_peer->addr, sizeof(sdst));
108545ae9d61Sreyk 	if (socket_af((struct sockaddr *)&sdst, 0) == -1) {
108645ae9d61Sreyk 		log_warn("%s: invalid address", __func__);
108745ae9d61Sreyk 		return (-1);
108845ae9d61Sreyk 	}
108945ae9d61Sreyk 
109045ae9d61Sreyk 	bzero(&smsg, sizeof(smsg));
109145ae9d61Sreyk 	smsg.sadb_msg_version = PF_KEY_V2;
1092b0eeedd0Smikeb 	smsg.sadb_msg_seq = ++sadb_msg_seq;
109345ae9d61Sreyk 	smsg.sadb_msg_pid = getpid();
109445ae9d61Sreyk 	smsg.sadb_msg_len = sizeof(smsg) / 8;
109545ae9d61Sreyk 	smsg.sadb_msg_type = SADB_GETSPI;
109645ae9d61Sreyk 	smsg.sadb_msg_satype = satype;
109745ae9d61Sreyk 
109845ae9d61Sreyk 	bzero(&sa_spirange, sizeof(sa_spirange));
109945ae9d61Sreyk 	sa_spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE;
110045ae9d61Sreyk 	sa_spirange.sadb_spirange_len = sizeof(sa_spirange) / 8;
110145ae9d61Sreyk 	sa_spirange.sadb_spirange_min = 0x100;
110203f6ad09Smarkus 	sa_spirange.sadb_spirange_max = (satype == SADB_X_SATYPE_IPCOMP) ?
110303f6ad09Smarkus 	    (CPI_PRIVATE_MIN - 1) : 0xffffffff;
110445ae9d61Sreyk 	sa_spirange.sadb_spirange_reserved = 0;
110545ae9d61Sreyk 
110645ae9d61Sreyk 	bzero(&sa_src, sizeof(sa_src));
110745ae9d61Sreyk 	sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
110845ae9d61Sreyk 	sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
110945ae9d61Sreyk 
111045ae9d61Sreyk 	bzero(&sa_dst, sizeof(sa_dst));
111145ae9d61Sreyk 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
111245ae9d61Sreyk 	sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
111345ae9d61Sreyk 
1114eab256a9Stobhe #define PAD(len)					\
1115eab256a9Stobhe 	padlen = ROUNDUP((len)) - (len);		\
1116eab256a9Stobhe 	if (padlen) {					\
1117eab256a9Stobhe 		iov[iov_cnt].iov_base = &pad;		\
1118eab256a9Stobhe 		iov[iov_cnt].iov_len = padlen;		\
1119eab256a9Stobhe 		iov_cnt++;				\
1120eab256a9Stobhe 	}
1121eab256a9Stobhe 
112245ae9d61Sreyk 	iov_cnt = 0;
112345ae9d61Sreyk 
112445ae9d61Sreyk 	/* header */
112545ae9d61Sreyk 	iov[iov_cnt].iov_base = &smsg;
112645ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(smsg);
112745ae9d61Sreyk 	iov_cnt++;
112845ae9d61Sreyk 
112945ae9d61Sreyk 	/* SPI range */
113045ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_spirange;
113145ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_spirange);
113245ae9d61Sreyk 	smsg.sadb_msg_len += sa_spirange.sadb_spirange_len;
113345ae9d61Sreyk 	iov_cnt++;
113445ae9d61Sreyk 
113545ae9d61Sreyk 	/* src addr */
113645ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_src;
113745ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_src);
113845ae9d61Sreyk 	iov_cnt++;
113945ae9d61Sreyk 	iov[iov_cnt].iov_base = &ssrc;
1140eab256a9Stobhe 	iov[iov_cnt].iov_len = ssrc.ss_len;
114145ae9d61Sreyk 	smsg.sadb_msg_len += sa_src.sadb_address_len;
114245ae9d61Sreyk 	iov_cnt++;
1143eab256a9Stobhe 	PAD(ssrc.ss_len);
114445ae9d61Sreyk 
114545ae9d61Sreyk 	/* dst addr */
114645ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_dst;
114745ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_dst);
114845ae9d61Sreyk 	iov_cnt++;
114945ae9d61Sreyk 	iov[iov_cnt].iov_base = &sdst;
1150eab256a9Stobhe 	iov[iov_cnt].iov_len = sdst.ss_len;
115145ae9d61Sreyk 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
115245ae9d61Sreyk 	iov_cnt++;
1153eab256a9Stobhe 	PAD(sdst.ss_len);
115445ae9d61Sreyk 
115545ae9d61Sreyk 	*spip = 0;
115645ae9d61Sreyk 
1157e7c3d382Stobhe 	if ((ret = pfkey_write(env, &smsg, iov, iov_cnt, &data, &n)) != 0)
115845ae9d61Sreyk 		return (-1);
115945ae9d61Sreyk 
116045ae9d61Sreyk 	msg = (struct sadb_msg *)data;
1161b0eeedd0Smikeb 	if (msg->sadb_msg_errno != 0) {
1162b0eeedd0Smikeb 		errno = msg->sadb_msg_errno;
1163b0eeedd0Smikeb 		log_warn("%s: message", __func__);
1164b0eeedd0Smikeb 		goto done;
1165b0eeedd0Smikeb 	}
1166b0eeedd0Smikeb 	if ((sa_ext = pfkey_find_ext(data, n, SADB_EXT_SA)) == NULL) {
1167fc01d564Stobhe 		log_debug("%s: erroneous reply", __func__);
1168b0eeedd0Smikeb 		goto done;
1169b0eeedd0Smikeb 	}
1170b0eeedd0Smikeb 
117145ae9d61Sreyk 	*spip = ntohl(sa_ext->sadb_sa_spi);
117245ae9d61Sreyk 	log_debug("%s: spi 0x%08x", __func__, *spip);
117345ae9d61Sreyk 
1174eab256a9Stobhe #undef PAD
1175eab256a9Stobhe 
1176b0eeedd0Smikeb done:
11771e82d711Sderaadt 	freezero(data, n);
117845ae9d61Sreyk 	return (ret);
117945ae9d61Sreyk }
118045ae9d61Sreyk 
118145ae9d61Sreyk int
pfkey_sagroup(struct iked * env,uint8_t satype1,uint8_t action,struct iked_childsa * sa1,struct iked_childsa * sa2)11828f6f6c19Stobhe pfkey_sagroup(struct iked *env, uint8_t satype1, uint8_t action,
118345ae9d61Sreyk     struct iked_childsa *sa1, struct iked_childsa *sa2)
118445ae9d61Sreyk {
118545ae9d61Sreyk 	struct sadb_msg		smsg;
118645ae9d61Sreyk 	struct sadb_sa		sadb1, sadb2;
118745ae9d61Sreyk 	struct sadb_address	sa_dst1, sa_dst2;
118845ae9d61Sreyk 	struct sockaddr_storage	sdst1, sdst2;
118945ae9d61Sreyk 	struct sadb_protocol	sa_proto;
11900347364bStobhe 	struct sadb_x_rdomain	sa_rdomain;
11910347364bStobhe 	struct iked_policy	*pol;
119245ae9d61Sreyk 	struct iovec		iov[IOV_CNT];
1193eab256a9Stobhe 	uint64_t		pad = 0;
1194eab256a9Stobhe 	size_t			padlen;
11954292c78bSreyk 	int			iov_cnt;
11960347364bStobhe 	int			group_rdomain;
1197d09d3a7dSreyk 	uint8_t			satype2;
119845ae9d61Sreyk 
119945ae9d61Sreyk 	if (pfkey_map(pfkey_satype, sa2->csa_saproto, &satype2) == -1)
120045ae9d61Sreyk 		return (-1);
120145ae9d61Sreyk 
120245ae9d61Sreyk 	bzero(&sdst1, sizeof(sdst1));
120345ae9d61Sreyk 	memcpy(&sdst1, &sa1->csa_peer->addr, sizeof(sdst1));
120445ae9d61Sreyk 	if (socket_af((struct sockaddr *)&sdst1, 0) == -1) {
120545ae9d61Sreyk 		log_warnx("%s: unsupported address family %d",
120645ae9d61Sreyk 		    __func__, sdst1.ss_family);
120745ae9d61Sreyk 		return (-1);
120845ae9d61Sreyk 	}
120945ae9d61Sreyk 
121045ae9d61Sreyk 	bzero(&sdst2, sizeof(sdst2));
121145ae9d61Sreyk 	memcpy(&sdst2, &sa2->csa_peer->addr, sizeof(sdst2));
121245ae9d61Sreyk 	if (socket_af((struct sockaddr *)&sdst2, 0) == -1) {
121345ae9d61Sreyk 		log_warnx("%s: unsupported address family %d",
121445ae9d61Sreyk 		    __func__, sdst2.ss_family);
121545ae9d61Sreyk 		return (-1);
121645ae9d61Sreyk 	}
121745ae9d61Sreyk 
121845ae9d61Sreyk 	bzero(&smsg, sizeof(smsg));
121945ae9d61Sreyk 	smsg.sadb_msg_version = PF_KEY_V2;
1220b0eeedd0Smikeb 	smsg.sadb_msg_seq = ++sadb_msg_seq;
122145ae9d61Sreyk 	smsg.sadb_msg_pid = getpid();
122245ae9d61Sreyk 	smsg.sadb_msg_len = sizeof(smsg) / 8;
122345ae9d61Sreyk 	smsg.sadb_msg_type = action;
122445ae9d61Sreyk 	smsg.sadb_msg_satype = satype1;
122545ae9d61Sreyk 
122645ae9d61Sreyk 	bzero(&sadb1, sizeof(sadb1));
122745ae9d61Sreyk 	sadb1.sadb_sa_len = sizeof(sadb1) / 8;
122845ae9d61Sreyk 	sadb1.sadb_sa_exttype = SADB_EXT_SA;
122945ae9d61Sreyk 	sadb1.sadb_sa_spi = htonl(sa1->csa_spi.spi);
123045ae9d61Sreyk 	sadb1.sadb_sa_state = SADB_SASTATE_MATURE;
123145ae9d61Sreyk 
123245ae9d61Sreyk 	bzero(&sadb2, sizeof(sadb2));
123345ae9d61Sreyk 	sadb2.sadb_sa_len = sizeof(sadb2) / 8;
123445ae9d61Sreyk 	sadb2.sadb_sa_exttype = SADB_X_EXT_SA2;
123545ae9d61Sreyk 	sadb2.sadb_sa_spi = htonl(sa2->csa_spi.spi);
123645ae9d61Sreyk 	sadb2.sadb_sa_state = SADB_SASTATE_MATURE;
12370347364bStobhe 
12380347364bStobhe 	/* Incoming SA1 (IPCOMP) and SA2 (ESP) are in different/other rdomain */
12390347364bStobhe 	group_rdomain =
12400347364bStobhe 	    (pol = sa1->csa_ikesa->sa_policy) != NULL &&
12410347364bStobhe 	    pol->pol_rdomain >= 0 &&
12420347364bStobhe 	    satype1 == SADB_X_SATYPE_IPCOMP &&
12430347364bStobhe 	    satype2 == SADB_SATYPE_ESP;
12440347364bStobhe 	if (group_rdomain) {
12450347364bStobhe 		bzero(&sa_rdomain, sizeof(sa_rdomain));
12460347364bStobhe 		sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN;
12470347364bStobhe 		sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8;
12480347364bStobhe 		if (sa1->csa_dir == IPSP_DIRECTION_IN) {
12490347364bStobhe 			/* only ESP SA is iked's rdomain */
12500347364bStobhe 			sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain;
12510347364bStobhe 			sa_rdomain.sadb_x_rdomain_dom2 = iked_rdomain;
12520347364bStobhe 		} else {
12530347364bStobhe 			/* both SAs are in pol_rdomain */
12540347364bStobhe 			sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain;
12550347364bStobhe 			sa_rdomain.sadb_x_rdomain_dom2 = pol->pol_rdomain;
12560347364bStobhe 		}
12570347364bStobhe 	}
12580347364bStobhe 
125945ae9d61Sreyk 	iov_cnt = 0;
126045ae9d61Sreyk 
126145ae9d61Sreyk 	bzero(&sa_dst1, sizeof(sa_dst1));
126245ae9d61Sreyk 	sa_dst1.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
126345ae9d61Sreyk 	sa_dst1.sadb_address_len =
126445ae9d61Sreyk 	    (sizeof(sa_dst1) + ROUNDUP(sdst1.ss_len)) / 8;
126545ae9d61Sreyk 
126645ae9d61Sreyk 	bzero(&sa_dst2, sizeof(sa_dst2));
126745ae9d61Sreyk 	sa_dst2.sadb_address_exttype = SADB_X_EXT_DST2;
126845ae9d61Sreyk 	sa_dst2.sadb_address_len =
126945ae9d61Sreyk 	    (sizeof(sa_dst2) + ROUNDUP(sdst2.ss_len)) / 8;
127045ae9d61Sreyk 
127145ae9d61Sreyk 	bzero(&sa_proto, sizeof(sa_proto));
1272d44c51a1Sbluhm 	sa_proto.sadb_protocol_exttype = SADB_X_EXT_SATYPE2;
127345ae9d61Sreyk 	sa_proto.sadb_protocol_len = sizeof(sa_proto) / 8;
127445ae9d61Sreyk 	sa_proto.sadb_protocol_direction = 0;
127545ae9d61Sreyk 	sa_proto.sadb_protocol_proto = satype2;
127645ae9d61Sreyk 
1277eab256a9Stobhe #define PAD(len)					\
1278eab256a9Stobhe 	padlen = ROUNDUP((len)) - (len);		\
1279eab256a9Stobhe 	if (padlen) {					\
1280eab256a9Stobhe 		iov[iov_cnt].iov_base = &pad;		\
1281eab256a9Stobhe 		iov[iov_cnt].iov_len = padlen;		\
1282eab256a9Stobhe 		iov_cnt++;				\
1283eab256a9Stobhe 	}
1284eab256a9Stobhe 
128545ae9d61Sreyk 	/* header */
128645ae9d61Sreyk 	iov[iov_cnt].iov_base = &smsg;
128745ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(smsg);
128845ae9d61Sreyk 	iov_cnt++;
128945ae9d61Sreyk 
129045ae9d61Sreyk 	/* sa */
129145ae9d61Sreyk 	iov[iov_cnt].iov_base = &sadb1;
129245ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sadb1);
129345ae9d61Sreyk 	smsg.sadb_msg_len += sadb1.sadb_sa_len;
129445ae9d61Sreyk 	iov_cnt++;
129545ae9d61Sreyk 
129645ae9d61Sreyk 	/* dst addr */
129745ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_dst1;
129845ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_dst1);
129945ae9d61Sreyk 	iov_cnt++;
130045ae9d61Sreyk 	iov[iov_cnt].iov_base = &sdst1;
1301eab256a9Stobhe 	iov[iov_cnt].iov_len = sdst1.ss_len;
130245ae9d61Sreyk 	smsg.sadb_msg_len += sa_dst1.sadb_address_len;
130345ae9d61Sreyk 	iov_cnt++;
1304eab256a9Stobhe 	PAD(sdst1.ss_len);
130545ae9d61Sreyk 
130645ae9d61Sreyk 	/* second sa */
130745ae9d61Sreyk 	iov[iov_cnt].iov_base = &sadb2;
130845ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sadb2);
130945ae9d61Sreyk 	smsg.sadb_msg_len += sadb2.sadb_sa_len;
131045ae9d61Sreyk 	iov_cnt++;
131145ae9d61Sreyk 
131245ae9d61Sreyk 	/* second dst addr */
131345ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_dst2;
131445ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_dst2);
131545ae9d61Sreyk 	iov_cnt++;
131645ae9d61Sreyk 	iov[iov_cnt].iov_base = &sdst2;
1317eab256a9Stobhe 	iov[iov_cnt].iov_len = sdst2.ss_len;
131845ae9d61Sreyk 	smsg.sadb_msg_len += sa_dst2.sadb_address_len;
131945ae9d61Sreyk 	iov_cnt++;
1320eab256a9Stobhe 	PAD(sdst2.ss_len);
132145ae9d61Sreyk 
132245ae9d61Sreyk 	/* SA type */
132345ae9d61Sreyk 	iov[iov_cnt].iov_base = &sa_proto;
132445ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(sa_proto);
132545ae9d61Sreyk 	smsg.sadb_msg_len += sa_proto.sadb_protocol_len;
132645ae9d61Sreyk 	iov_cnt++;
132745ae9d61Sreyk 
13280347364bStobhe 	/* SA1 and SA2 are from different rdomains */
13290347364bStobhe 	if (group_rdomain) {
13300347364bStobhe 		iov[iov_cnt].iov_base = &sa_rdomain;
13310347364bStobhe 		iov[iov_cnt].iov_len = sizeof(sa_rdomain);
13320347364bStobhe 		smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len;
13330347364bStobhe 		iov_cnt++;
13340347364bStobhe 	}
13350347364bStobhe 
1336eab256a9Stobhe #undef PAD
1337eab256a9Stobhe 
1338e7c3d382Stobhe 	return (pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL));
13394292c78bSreyk }
13404292c78bSreyk 
13414292c78bSreyk int
pfkey_write(struct iked * env,struct sadb_msg * smsg,struct iovec * iov,int iov_cnt,uint8_t ** datap,ssize_t * lenp)1342e7c3d382Stobhe pfkey_write(struct iked *env, struct sadb_msg *smsg, struct iovec *iov, int iov_cnt,
1343d09d3a7dSreyk     uint8_t **datap, ssize_t *lenp)
13444292c78bSreyk {
13454292c78bSreyk 	ssize_t n, len = smsg->sadb_msg_len * 8;
1346e7c3d382Stobhe 	int ret = -1;
13474292c78bSreyk 
1348fc20f985Sreyk 	if (sadb_decoupled) {
1349fc20f985Sreyk 		switch (smsg->sadb_msg_type) {
1350fc20f985Sreyk 		case SADB_GETSPI:
1351fc20f985Sreyk 			/* we need to get a new SPI from the kernel */
1352fc20f985Sreyk 			break;
1353fc20f985Sreyk 		default:
1354fc20f985Sreyk 			if (datap || lenp) {
1355fc20f985Sreyk 				log_warnx("%s: pfkey not coupled", __func__);
1356fc20f985Sreyk 				return (-1);
1357fc20f985Sreyk 			}
1358fc20f985Sreyk 			/* ignore request */
1359fc20f985Sreyk 			return (0);
1360fc20f985Sreyk 		}
1361fc20f985Sreyk 	}
1362fc20f985Sreyk 
1363e7c3d382Stobhe 	/* Delete event to poll() in pfkey_reply() */
1364e7c3d382Stobhe 	event_del(&env->sc_pfkeyev);
1365e7c3d382Stobhe 
1366e7c3d382Stobhe 	if ((n = writev(env->sc_pfkey, iov, iov_cnt)) == -1) {
13679ae8a051Stobhe 		log_warn("%s: writev failed: type %u len %zd",
13689ae8a051Stobhe 		    __func__, smsg->sadb_msg_type, len);
1369e7c3d382Stobhe 		goto done;
137045ae9d61Sreyk 	} else if (n != len) {
13714292c78bSreyk 		log_warn("%s: short write", __func__);
1372e7c3d382Stobhe 		goto done;
137345ae9d61Sreyk 	}
137445ae9d61Sreyk 
1375e7c3d382Stobhe 	ret = pfkey_reply(env->sc_pfkey, datap, lenp);
1376e7c3d382Stobhe  done:
1377e7c3d382Stobhe 	event_add(&env->sc_pfkeyev, NULL);
1378e7c3d382Stobhe 	return (ret);
137945ae9d61Sreyk }
138045ae9d61Sreyk 
13818389fa3fStobhe /* wait for pfkey response and returns 0 for ok, -1 for error, -2 for timeout */
138245ae9d61Sreyk int
pfkey_reply(int fd,uint8_t ** datap,ssize_t * lenp)13838f6f6c19Stobhe pfkey_reply(int fd, uint8_t **datap, ssize_t *lenp)
138445ae9d61Sreyk {
1385d1ebe3d0Smikeb 	struct pfkey_message	*pm;
138645ae9d61Sreyk 	struct sadb_msg		 hdr;
138745ae9d61Sreyk 	ssize_t			 len;
1388d09d3a7dSreyk 	uint8_t			*data;
1389f0de6e10Sderaadt 	struct pollfd		pfd[1];
13903f3558dcSmarkus 	int			 n;
139145ae9d61Sreyk 
13928f6f6c19Stobhe 	pfd[0].fd = fd;
1393f0de6e10Sderaadt 	pfd[0].events = POLLIN;
1394f0de6e10Sderaadt 
1395b0eeedd0Smikeb 	for (;;) {
13963f3558dcSmarkus 		/*
13973f3558dcSmarkus 		 * We should actually expect the reply to get lost
13983f3558dcSmarkus 		 * as PF_KEY is an unreliable service per the specs.
13993f3558dcSmarkus 		 * Currently we do this by setting a short timeout,
14003f3558dcSmarkus 		 * and if it is not readable in that time, we fail
14013f3558dcSmarkus 		 * the read.
14023f3558dcSmarkus 		 */
1403f0de6e10Sderaadt 		n = poll(pfd, 1, PFKEY_REPLY_TIMEOUT / 1000);
14043f3558dcSmarkus 		if (n == -1) {
1405f0de6e10Sderaadt 			log_warn("%s: poll() failed", __func__);
14063f3558dcSmarkus 			return (-1);
14073f3558dcSmarkus 		}
14083f3558dcSmarkus 		if (n == 0) {
14093f3558dcSmarkus 			log_warnx("%s: no reply from PF_KEY", __func__);
14108389fa3fStobhe 			return (-2);	/* retry */
14113f3558dcSmarkus 		}
14123f3558dcSmarkus 
14138f6f6c19Stobhe 		if (recv(fd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
1414b0eeedd0Smikeb 			log_warn("%s: short recv", __func__);
14154292c78bSreyk 			return (-1);
141645ae9d61Sreyk 		}
1417b0eeedd0Smikeb 
1418b0eeedd0Smikeb 		if (hdr.sadb_msg_version != PF_KEY_V2) {
1419b0eeedd0Smikeb 			log_warnx("%s: wrong pfkey version", __func__);
1420b0eeedd0Smikeb 			return (-1);
1421b0eeedd0Smikeb 		}
1422b0eeedd0Smikeb 
1423ce03dfbeSdoug 		if ((data = reallocarray(NULL, hdr.sadb_msg_len,
1424ce03dfbeSdoug 		    PFKEYV2_CHUNK)) == NULL) {
14254292c78bSreyk 			log_warn("%s: malloc", __func__);
14264292c78bSreyk 			return (-1);
14274292c78bSreyk 		}
1428ce03dfbeSdoug 		len = hdr.sadb_msg_len * PFKEYV2_CHUNK;
1429ce03dfbeSdoug 
14308f6f6c19Stobhe 		if (read(fd, data, len) != len) {
14314292c78bSreyk 			log_warnx("%s: short read", __func__);
143245ae9d61Sreyk 			free(data);
14334292c78bSreyk 			return (-1);
143445ae9d61Sreyk 		}
1435d1ebe3d0Smikeb 
1436d1ebe3d0Smikeb 		/* XXX: Only one message can be outstanding. */
1437d1ebe3d0Smikeb 		if (hdr.sadb_msg_seq == sadb_msg_seq &&
1438d09d3a7dSreyk 		    hdr.sadb_msg_pid == (uint32_t)getpid())
1439d1ebe3d0Smikeb 			break;
1440d1ebe3d0Smikeb 
1441596c707eSmarkus 		/* ignore messages for other processes */
1442596c707eSmarkus 		if (hdr.sadb_msg_pid != 0 &&
1443d09d3a7dSreyk 		    hdr.sadb_msg_pid != (uint32_t)getpid()) {
144474214e59Smarkus 			free(data);
1445596c707eSmarkus 			continue;
144674214e59Smarkus 		}
1447596c707eSmarkus 
1448d1ebe3d0Smikeb 		/* not the reply, enqueue */
1449d1ebe3d0Smikeb 		if ((pm = malloc(sizeof(*pm))) == NULL) {
1450596c707eSmarkus 			log_warn("%s: malloc", __func__);
1451d1ebe3d0Smikeb 			free(data);
1452d1ebe3d0Smikeb 			return (-1);
1453d1ebe3d0Smikeb 		}
1454d1ebe3d0Smikeb 		pm->pm_data = data;
14551c9ff2fdSblambert 		pm->pm_length = len;
1456d1ebe3d0Smikeb 		SIMPLEQ_INSERT_TAIL(&pfkey_postponed, pm, pm_entry);
1457d1ebe3d0Smikeb 		evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv);
1458d1ebe3d0Smikeb 	}
1459d1ebe3d0Smikeb 
146045ae9d61Sreyk 	if (datap) {
146145ae9d61Sreyk 		*datap = data;
146245ae9d61Sreyk 		if (lenp)
146345ae9d61Sreyk 			*lenp = len;
1464d1ebe3d0Smikeb 	} else
146545ae9d61Sreyk 		free(data);
1466d1ebe3d0Smikeb 
146745ae9d61Sreyk 	if (datap == NULL && hdr.sadb_msg_errno != 0) {
146845ae9d61Sreyk 		errno = hdr.sadb_msg_errno;
146945ae9d61Sreyk 		if (errno != EEXIST) {
147033e4f7e3Stobhe 			if (errno == ESRCH)
147133e4f7e3Stobhe 				log_debug("%s: not found", __func__);
147233e4f7e3Stobhe 			else
14734292c78bSreyk 				log_warn("%s: message", __func__);
14744292c78bSreyk 			return (-1);
147545ae9d61Sreyk 		}
147645ae9d61Sreyk 	}
14774292c78bSreyk 	return (0);
147845ae9d61Sreyk }
147945ae9d61Sreyk 
148045ae9d61Sreyk int
pfkey_flow_add(struct iked * env,struct iked_flow * flow)14818f6f6c19Stobhe pfkey_flow_add(struct iked *env, struct iked_flow *flow)
148245ae9d61Sreyk {
1483d09d3a7dSreyk 	uint8_t		 satype;
148445ae9d61Sreyk 
148545ae9d61Sreyk 	if (flow->flow_loaded)
148645ae9d61Sreyk 		return (0);
148745ae9d61Sreyk 
148845ae9d61Sreyk 	if (pfkey_map(pfkey_satype, flow->flow_saproto, &satype) == -1)
148945ae9d61Sreyk 		return (-1);
149045ae9d61Sreyk 
14918f6f6c19Stobhe 	if (pfkey_flow(env, satype, SADB_X_ADDFLOW, flow) == -1)
149245ae9d61Sreyk 		return (-1);
149345ae9d61Sreyk 
149445ae9d61Sreyk 	flow->flow_loaded = 1;
14955342a326Sreyk 
149645ae9d61Sreyk 	return (0);
149745ae9d61Sreyk }
149845ae9d61Sreyk 
149945ae9d61Sreyk int
pfkey_flow_delete(struct iked * env,struct iked_flow * flow)15008f6f6c19Stobhe pfkey_flow_delete(struct iked *env, struct iked_flow *flow)
150145ae9d61Sreyk {
1502d09d3a7dSreyk 	uint8_t		satype;
150345ae9d61Sreyk 
150445ae9d61Sreyk 	if (!flow->flow_loaded)
150545ae9d61Sreyk 		return (0);
150645ae9d61Sreyk 
150745ae9d61Sreyk 	if (pfkey_map(pfkey_satype, flow->flow_saproto, &satype) == -1)
150845ae9d61Sreyk 		return (-1);
150945ae9d61Sreyk 
15108f6f6c19Stobhe 	if (pfkey_flow(env, satype, SADB_X_DELFLOW, flow) == -1)
151145ae9d61Sreyk 		return (-1);
151245ae9d61Sreyk 
151345ae9d61Sreyk 	flow->flow_loaded = 0;
15145342a326Sreyk 
151545ae9d61Sreyk 	return (0);
151645ae9d61Sreyk }
151745ae9d61Sreyk 
151845ae9d61Sreyk int
pfkey_sa_init(struct iked * env,struct iked_childsa * sa,uint32_t * spi)15198f6f6c19Stobhe pfkey_sa_init(struct iked *env, struct iked_childsa *sa, uint32_t *spi)
152045ae9d61Sreyk {
1521d09d3a7dSreyk 	uint8_t		 satype;
152245ae9d61Sreyk 
152345ae9d61Sreyk 	if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
152445ae9d61Sreyk 		return (-1);
152545ae9d61Sreyk 
15268f6f6c19Stobhe 	if (pfkey_sa_getspi(env, satype, sa, spi) == -1)
152745ae9d61Sreyk 		return (-1);
152845ae9d61Sreyk 
152945ae9d61Sreyk 	log_debug("%s: new spi 0x%08x", __func__, *spi);
153045ae9d61Sreyk 
153145ae9d61Sreyk 	return (0);
153245ae9d61Sreyk }
153345ae9d61Sreyk 
153445ae9d61Sreyk int
pfkey_sa_add(struct iked * env,struct iked_childsa * sa,struct iked_childsa * last)15358f6f6c19Stobhe pfkey_sa_add(struct iked *env, struct iked_childsa *sa, struct iked_childsa *last)
153645ae9d61Sreyk {
1537d09d3a7dSreyk 	uint8_t		 satype;
1538d09d3a7dSreyk 	unsigned int	 cmd;
15398389fa3fStobhe 	int		 rval;
154045ae9d61Sreyk 
154145ae9d61Sreyk 	if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
154245ae9d61Sreyk 		return (-1);
154345ae9d61Sreyk 
1544d4614e98Sreyk 	if (sa->csa_allocated || sa->csa_loaded)
154545ae9d61Sreyk 		cmd = SADB_UPDATE;
154645ae9d61Sreyk 	else
154745ae9d61Sreyk 		cmd = SADB_ADD;
154845ae9d61Sreyk 
154945ae9d61Sreyk 	log_debug("%s: %s spi %s", __func__, cmd == SADB_ADD ? "add": "update",
155045ae9d61Sreyk 	    print_spi(sa->csa_spi.spi, 4));
155145ae9d61Sreyk 
15528f6f6c19Stobhe 	rval = pfkey_sa(env, satype, cmd, sa);
15538389fa3fStobhe 	if (rval != 0) {
1554af52b2a8Svgross 		if (cmd == SADB_ADD) {
15558389fa3fStobhe 			if (rval == -2) {
15568389fa3fStobhe 				/* timeout: check for existence */
15578f6f6c19Stobhe 				if (pfkey_sa_check_exists(env, sa) == 0) {
15588389fa3fStobhe 					log_debug("%s: SA exists after timeout",
15598389fa3fStobhe 					    __func__);
15608389fa3fStobhe 					goto loaded;
15618389fa3fStobhe 				}
15628389fa3fStobhe 			}
15638f6f6c19Stobhe 			(void)pfkey_sa_delete(env, sa);
156445ae9d61Sreyk 			return (-1);
156545ae9d61Sreyk 		}
1566af52b2a8Svgross 		if (sa->csa_allocated && !sa->csa_loaded && errno == ESRCH) {
1567af52b2a8Svgross 			/* Needed for recoupling local SAs */
1568af52b2a8Svgross 			log_debug("%s: SADB_UPDATE on local SA returned ESRCH,"
1569af52b2a8Svgross 			    " trying SADB_ADD", __func__);
15708f6f6c19Stobhe 			if (pfkey_sa(env, satype, SADB_ADD, sa) == -1)
1571af52b2a8Svgross 				return (-1);
1572af52b2a8Svgross 		} else {
1573af52b2a8Svgross 			return (-1);
1574af52b2a8Svgross 		}
1575af52b2a8Svgross 	}
157645ae9d61Sreyk 
15778389fa3fStobhe  loaded:
1578e7fee6f8Stobhe 	if (last != NULL) {
15798f6f6c19Stobhe 		if (pfkey_sagroup(env, satype,
158045ae9d61Sreyk 		    SADB_X_GRPSPIS, sa, last) == -1) {
15818f6f6c19Stobhe 			(void)pfkey_sa_delete(env, sa);
158245ae9d61Sreyk 			return (-1);
158345ae9d61Sreyk 		}
158445ae9d61Sreyk 	}
158545ae9d61Sreyk 
158645ae9d61Sreyk 	sa->csa_loaded = 1;
158745ae9d61Sreyk 	return (0);
158845ae9d61Sreyk }
158945ae9d61Sreyk 
159045ae9d61Sreyk int
pfkey_sa_update_addresses(struct iked * env,struct iked_childsa * sa)15918f6f6c19Stobhe pfkey_sa_update_addresses(struct iked *env, struct iked_childsa *sa)
1592c0b327e6Spatrick {
1593c0b327e6Spatrick 	uint8_t		 satype;
1594c0b327e6Spatrick 
1595c0b327e6Spatrick 	if (!sa->csa_ikesa)
1596c0b327e6Spatrick 		return (-1);
1597c0b327e6Spatrick 	/* check if peer has changed */
1598c0b327e6Spatrick 	if (sa->csa_ikesa->sa_peer_loaded.addr.ss_family == AF_UNSPEC ||
1599c0b327e6Spatrick 	    memcmp(&sa->csa_ikesa->sa_peer_loaded, &sa->csa_ikesa->sa_peer,
1600c0b327e6Spatrick 	    sizeof(sa->csa_ikesa->sa_peer_loaded)) == 0)
1601c0b327e6Spatrick 		return (0);
1602c0b327e6Spatrick 	if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
1603c0b327e6Spatrick 		return (-1);
1604c0b327e6Spatrick 	log_debug("%s: spi %s", __func__, print_spi(sa->csa_spi.spi, 4));
16058f6f6c19Stobhe 	return pfkey_sa(env, satype, IKED_SADB_UPDATE_SA_ADDRESSES, sa);
1606c0b327e6Spatrick }
1607c0b327e6Spatrick 
1608c0b327e6Spatrick int
pfkey_sa_delete(struct iked * env,struct iked_childsa * sa)16098f6f6c19Stobhe pfkey_sa_delete(struct iked *env, struct iked_childsa *sa)
161045ae9d61Sreyk {
1611d09d3a7dSreyk 	uint8_t			satype;
1612*f36db9c4Syasuoka 	struct iked_sastats	sas;
161345ae9d61Sreyk 
161445ae9d61Sreyk 	if (!sa->csa_loaded || sa->csa_spi.spi == 0)
161545ae9d61Sreyk 		return (0);
161645ae9d61Sreyk 
161745ae9d61Sreyk 	if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1)
161845ae9d61Sreyk 		return (-1);
161945ae9d61Sreyk 
1620*f36db9c4Syasuoka 	/* preserve the statistics */
1621*f36db9c4Syasuoka 	memset(&sas, 0, sizeof(sas));
1622*f36db9c4Syasuoka 	pfkey_sa_sastats(env, sa, &sas);
1623*f36db9c4Syasuoka 
16248f6f6c19Stobhe 	if (pfkey_sa(env, satype, SADB_DELETE, sa) == -1 &&
16258f6f6c19Stobhe 	    pfkey_sa_check_exists(env, sa) == 0)
162645ae9d61Sreyk 		return (-1);
162745ae9d61Sreyk 
162845ae9d61Sreyk 	sa->csa_loaded = 0;
1629*f36db9c4Syasuoka 
1630*f36db9c4Syasuoka 	sa->csa_ikesa->sa_stats.sas_ipackets += sas.sas_ipackets;
1631*f36db9c4Syasuoka 	sa->csa_ikesa->sa_stats.sas_opackets += sas.sas_opackets;
1632*f36db9c4Syasuoka 	sa->csa_ikesa->sa_stats.sas_ibytes += sas.sas_ibytes;
1633*f36db9c4Syasuoka 	sa->csa_ikesa->sa_stats.sas_obytes += sas.sas_obytes;
1634*f36db9c4Syasuoka 	sa->csa_ikesa->sa_stats.sas_idrops += sas.sas_idrops;
1635*f36db9c4Syasuoka 	sa->csa_ikesa->sa_stats.sas_odrops += sas.sas_odrops;
1636*f36db9c4Syasuoka 
163745ae9d61Sreyk 	return (0);
163845ae9d61Sreyk }
163945ae9d61Sreyk 
164045ae9d61Sreyk int
pfkey_flush(struct iked * env)1641e7c3d382Stobhe pfkey_flush(struct iked *env)
164245ae9d61Sreyk {
164345ae9d61Sreyk 	struct sadb_msg smsg;
164445ae9d61Sreyk 	struct iovec	iov[IOV_CNT];
16454292c78bSreyk 	int		iov_cnt;
164645ae9d61Sreyk 
164745ae9d61Sreyk 	bzero(&smsg, sizeof(smsg));
164845ae9d61Sreyk 	smsg.sadb_msg_version = PF_KEY_V2;
1649b0eeedd0Smikeb 	smsg.sadb_msg_seq = ++sadb_msg_seq;
165045ae9d61Sreyk 	smsg.sadb_msg_pid = getpid();
165145ae9d61Sreyk 	smsg.sadb_msg_len = sizeof(smsg) / 8;
165245ae9d61Sreyk 	smsg.sadb_msg_type = SADB_FLUSH;
165345ae9d61Sreyk 	smsg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
165445ae9d61Sreyk 
165545ae9d61Sreyk 	iov_cnt = 0;
165645ae9d61Sreyk 
165745ae9d61Sreyk 	iov[iov_cnt].iov_base = &smsg;
165845ae9d61Sreyk 	iov[iov_cnt].iov_len = sizeof(smsg);
165945ae9d61Sreyk 	iov_cnt++;
166045ae9d61Sreyk 
1661e7c3d382Stobhe 	return (pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL));
166245ae9d61Sreyk }
166345ae9d61Sreyk 
166445ae9d61Sreyk struct sadb_ident *
pfkey_id2ident(struct iked_id * id,unsigned int exttype)1665d09d3a7dSreyk pfkey_id2ident(struct iked_id *id, unsigned int exttype)
166645ae9d61Sreyk {
166745ae9d61Sreyk 	char			 idstr[IKED_ID_SIZE];
1668d09d3a7dSreyk 	unsigned int		 type;
166945ae9d61Sreyk 	size_t			 len;
167045ae9d61Sreyk 	struct sadb_ident	*sa_id;
167145ae9d61Sreyk 
167245ae9d61Sreyk 	switch (id->id_type) {
167345ae9d61Sreyk 	case IKEV2_ID_FQDN:
167445ae9d61Sreyk 		type = SADB_IDENTTYPE_FQDN;
167545ae9d61Sreyk 		break;
1676435ab53fSreyk 	case IKEV2_ID_UFQDN:
167745ae9d61Sreyk 		type = SADB_IDENTTYPE_USERFQDN;
167845ae9d61Sreyk 		break;
1679435ab53fSreyk 	case IKEV2_ID_IPV4:
1680435ab53fSreyk 	case IKEV2_ID_IPV6:
168145ae9d61Sreyk 		type = SADB_IDENTTYPE_PREFIX;
168245ae9d61Sreyk 		break;
1683435ab53fSreyk 	case IKEV2_ID_ASN1_DN:
1684d8131d03Sphessler 		type = SADB_IDENTTYPE_ASN1_DN;
1685d8131d03Sphessler 		break;
1686435ab53fSreyk 	case IKEV2_ID_ASN1_GN:
168745ae9d61Sreyk 	case IKEV2_ID_KEY_ID:
168845ae9d61Sreyk 	case IKEV2_ID_NONE:
168945ae9d61Sreyk 	default:
169045ae9d61Sreyk 		/* XXX not implemented/supported by PFKEY */
169145ae9d61Sreyk 		return (NULL);
169245ae9d61Sreyk 	}
169345ae9d61Sreyk 
169445ae9d61Sreyk 	bzero(&idstr, sizeof(idstr));
169545ae9d61Sreyk 
169608246d98Sreyk 	if (ikev2_print_id(id, idstr, sizeof(idstr)) == -1)
169745ae9d61Sreyk 		return (NULL);
169845ae9d61Sreyk 
169945ae9d61Sreyk 	len = ROUNDUP(strlen(idstr) + 1) + sizeof(*sa_id);
170045ae9d61Sreyk 	if ((sa_id = calloc(1, len)) == NULL)
170145ae9d61Sreyk 		return (NULL);
170245ae9d61Sreyk 
170345ae9d61Sreyk 	strlcpy((char *)(sa_id + 1), idstr, ROUNDUP(strlen(idstr) + 1));
170445ae9d61Sreyk 	sa_id->sadb_ident_type = type;
170545ae9d61Sreyk 	sa_id->sadb_ident_len = len / 8;
170645ae9d61Sreyk 	sa_id->sadb_ident_exttype = exttype;
170745ae9d61Sreyk 
170845ae9d61Sreyk 	return (sa_id);
170945ae9d61Sreyk }
171045ae9d61Sreyk 
171145ae9d61Sreyk int
pfkey_socket(struct iked * env)17128f6f6c19Stobhe pfkey_socket(struct iked *env)
17136417b90fSreyk {
17146417b90fSreyk 	int	 fd;
17156417b90fSreyk 
1716f2f2a684Sreyk 	if (privsep_process != PROC_PARENT)
17176417b90fSreyk 		fatal("pfkey_socket: called from unprivileged process");
17186417b90fSreyk 
17196417b90fSreyk 	if ((fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) == -1)
17206417b90fSreyk 		fatal("pfkey_socket: failed to open PF_KEY socket");
17216417b90fSreyk 
17226417b90fSreyk 	return (fd);
17236417b90fSreyk }
17246417b90fSreyk 
17256417b90fSreyk void
pfkey_init(struct iked * env,int fd)17266417b90fSreyk pfkey_init(struct iked *env, int fd)
172745ae9d61Sreyk {
17280927abadSmikeb 	struct sadb_msg		smsg;
17290927abadSmikeb 	struct iovec		iov;
173045ae9d61Sreyk 
17310347364bStobhe 	iked_rdomain = getrtable();
17320347364bStobhe 
17330aa0bcefSmarkus 	/* Set up a timer to process messages deferred by the pfkey_reply */
17340aa0bcefSmarkus 	pfkey_timer_tv.tv_sec = 1;
17350aa0bcefSmarkus 	pfkey_timer_tv.tv_usec = 0;
17360aa0bcefSmarkus 	evtimer_set(&pfkey_timer_ev, pfkey_timer_cb, env);
17370aa0bcefSmarkus 
17386417b90fSreyk 	/* Register the pfkey socket event handler */
17396417b90fSreyk 	env->sc_pfkey = fd;
17406417b90fSreyk 	event_set(&env->sc_pfkeyev, env->sc_pfkey,
17416417b90fSreyk 	    EV_READ|EV_PERSIST, pfkey_dispatch, env);
17426417b90fSreyk 	event_add(&env->sc_pfkeyev, NULL);
174345ae9d61Sreyk 
1744e7c3d382Stobhe 	pfkey_flush(env);
1745e7c3d382Stobhe 
17460927abadSmikeb 	/* Register it to get ESP and AH acquires from the kernel */
17470927abadSmikeb 	bzero(&smsg, sizeof(smsg));
17480927abadSmikeb 	smsg.sadb_msg_version = PF_KEY_V2;
17490927abadSmikeb 	smsg.sadb_msg_seq = ++sadb_msg_seq;
17500927abadSmikeb 	smsg.sadb_msg_pid = getpid();
17510927abadSmikeb 	smsg.sadb_msg_len = sizeof(smsg) / 8;
17520927abadSmikeb 	smsg.sadb_msg_type = SADB_REGISTER;
17530927abadSmikeb 	smsg.sadb_msg_satype = SADB_SATYPE_ESP;
17540927abadSmikeb 
17550927abadSmikeb 	iov.iov_base = &smsg;
17560927abadSmikeb 	iov.iov_len = sizeof(smsg);
17570927abadSmikeb 
1758e7c3d382Stobhe 	if (pfkey_write(env, &smsg, &iov, 1, NULL, NULL))
17590927abadSmikeb 		fatal("pfkey_init: failed to set up ESP acquires");
17600927abadSmikeb 
17610927abadSmikeb 	bzero(&smsg, sizeof(smsg));
17620927abadSmikeb 	smsg.sadb_msg_version = PF_KEY_V2;
17630927abadSmikeb 	smsg.sadb_msg_seq = ++sadb_msg_seq;
17640927abadSmikeb 	smsg.sadb_msg_pid = getpid();
17650927abadSmikeb 	smsg.sadb_msg_len = sizeof(smsg) / 8;
17660927abadSmikeb 	smsg.sadb_msg_type = SADB_REGISTER;
17670927abadSmikeb 	smsg.sadb_msg_satype = SADB_SATYPE_AH;
17680927abadSmikeb 
17690927abadSmikeb 	iov.iov_base = &smsg;
17700927abadSmikeb 	iov.iov_len = sizeof(smsg);
17710927abadSmikeb 
1772e7c3d382Stobhe 	if (pfkey_write(env, &smsg, &iov, 1, NULL, NULL))
17730927abadSmikeb 		fatal("pfkey_init: failed to set up AH acquires");
177445ae9d61Sreyk }
1775b0eeedd0Smikeb 
1776b0eeedd0Smikeb void *
pfkey_find_ext(uint8_t * data,ssize_t len,int type)1777d09d3a7dSreyk pfkey_find_ext(uint8_t *data, ssize_t len, int type)
1778b0eeedd0Smikeb {
1779b0eeedd0Smikeb 	struct sadb_ext	*ext = (struct sadb_ext *)(data +
1780b0eeedd0Smikeb 	    sizeof(struct sadb_msg));
1781b0eeedd0Smikeb 
1782d09d3a7dSreyk 	while (ext && ((uint8_t *)ext - data < len)) {
1783b0eeedd0Smikeb 		if (ext->sadb_ext_type == type)
1784b0eeedd0Smikeb 			return (ext);
1785d09d3a7dSreyk 		ext = (struct sadb_ext *)((uint8_t *)ext +
1786b0eeedd0Smikeb 		    ext->sadb_ext_len * PFKEYV2_CHUNK);
1787b0eeedd0Smikeb 	}
1788b0eeedd0Smikeb 
1789b0eeedd0Smikeb 	return (NULL);
1790b0eeedd0Smikeb }
1791b0eeedd0Smikeb 
1792b0eeedd0Smikeb void
pfkey_dispatch(int fd,short event,void * arg)17938f6f6c19Stobhe pfkey_dispatch(int fd, short event, void *arg)
1794b0eeedd0Smikeb {
1795b0eeedd0Smikeb 	struct iked		*env = (struct iked *)arg;
17966e1880a3Smarkus 	struct pfkey_message	 pm, *pmp;
1797b0eeedd0Smikeb 	struct sadb_msg		 hdr;
1798b0eeedd0Smikeb 	ssize_t			 len;
1799d09d3a7dSreyk 	uint8_t			*data;
1800b0eeedd0Smikeb 
18018f6f6c19Stobhe 	if (recv(fd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
1802b0eeedd0Smikeb 		log_warn("%s: short recv", __func__);
1803b0eeedd0Smikeb 		return;
1804b0eeedd0Smikeb 	}
1805b0eeedd0Smikeb 
1806b0eeedd0Smikeb 	if (hdr.sadb_msg_version != PF_KEY_V2) {
1807b0eeedd0Smikeb 		log_warnx("%s: wrong pfkey version", __func__);
1808b0eeedd0Smikeb 		return;
1809b0eeedd0Smikeb 	}
1810b0eeedd0Smikeb 
1811ce03dfbeSdoug 	if ((data = reallocarray(NULL, hdr.sadb_msg_len, PFKEYV2_CHUNK))
1812ce03dfbeSdoug 	    == NULL) {
1813b0eeedd0Smikeb 		log_warn("%s: malloc", __func__);
1814b0eeedd0Smikeb 		return;
1815b0eeedd0Smikeb 	}
1816ce03dfbeSdoug 	len = hdr.sadb_msg_len * PFKEYV2_CHUNK;
1817ce03dfbeSdoug 
18188f6f6c19Stobhe 	if (read(fd, data, len) != len) {
1819b0eeedd0Smikeb 		log_warn("%s: short read", __func__);
1820b0eeedd0Smikeb 		free(data);
1821b0eeedd0Smikeb 		return;
1822b0eeedd0Smikeb 	}
1823b0eeedd0Smikeb 
182490ef7001Smarkus 	/* Try postponed requests first, so we do in-order processing */
182590ef7001Smarkus 	if (!SIMPLEQ_EMPTY(&pfkey_postponed))
182690ef7001Smarkus 		pfkey_timer_cb(0, 0, env);
182790ef7001Smarkus 
1828d1ebe3d0Smikeb 	pm.pm_data = data;
18291c9ff2fdSblambert 	pm.pm_length = len;
1830d1ebe3d0Smikeb 
18316e1880a3Smarkus 	if (pfkey_process(env, &pm) == -1 &&
18326e1880a3Smarkus 	    (pmp = calloc(1, sizeof(*pmp))) != NULL) {
18336e1880a3Smarkus 		pmp->pm_data = data;
18346e1880a3Smarkus 		pmp->pm_length = len;
18356e1880a3Smarkus 		log_debug("%s: pfkey_process is busy, retry later", __func__);
18366e1880a3Smarkus 		SIMPLEQ_INSERT_TAIL(&pfkey_postponed, pmp, pm_entry);
18376e1880a3Smarkus 		evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv);
18386e1880a3Smarkus 	} else {
1839d1ebe3d0Smikeb 		free(data);
1840d1ebe3d0Smikeb 	}
18416e1880a3Smarkus }
1842d1ebe3d0Smikeb 
1843d1ebe3d0Smikeb void
pfkey_timer_cb(int unused,short event,void * arg)1844d1ebe3d0Smikeb pfkey_timer_cb(int unused, short event, void *arg)
1845d1ebe3d0Smikeb {
1846d1ebe3d0Smikeb 	struct iked		*env = arg;
1847d1ebe3d0Smikeb 	struct pfkey_message	*pm;
1848d1ebe3d0Smikeb 
18496e1880a3Smarkus 	SIMPLEQ_INIT(&pfkey_retry);
1850d1ebe3d0Smikeb 	while (!SIMPLEQ_EMPTY(&pfkey_postponed)) {
1851d1ebe3d0Smikeb 		pm = SIMPLEQ_FIRST(&pfkey_postponed);
1852d1ebe3d0Smikeb 		SIMPLEQ_REMOVE_HEAD(&pfkey_postponed, pm_entry);
18536e1880a3Smarkus 		if (pfkey_process(env, pm) == -1) {
18546e1880a3Smarkus 			log_debug("%s: pfkey_process is busy, retry later",
18556e1880a3Smarkus 			    __func__);
18566e1880a3Smarkus 			SIMPLEQ_INSERT_TAIL(&pfkey_retry, pm, pm_entry);
18576e1880a3Smarkus 		} else {
1858d1ebe3d0Smikeb 			free(pm->pm_data);
1859d1ebe3d0Smikeb 			free(pm);
1860d1ebe3d0Smikeb 		}
1861d1ebe3d0Smikeb 	}
186290ef7001Smarkus 	/* move from retry to postponed */
1863b434f93eSbket 	SIMPLEQ_CONCAT(&pfkey_postponed, &pfkey_retry);
18646e1880a3Smarkus 	if (!SIMPLEQ_EMPTY(&pfkey_postponed))
18656e1880a3Smarkus 		evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv);
18666e1880a3Smarkus }
1867d1ebe3d0Smikeb 
18686e1880a3Smarkus /*
18696e1880a3Smarkus  * pfkey_process returns 0 if the message has been processed and -1 if
187064405961Smmcc  * the system is busy and the message should be passed again, later.
18716e1880a3Smarkus  */
18726e1880a3Smarkus int
pfkey_process(struct iked * env,struct pfkey_message * pm)1873d1ebe3d0Smikeb pfkey_process(struct iked *env, struct pfkey_message *pm)
1874d1ebe3d0Smikeb {
1875d1ebe3d0Smikeb 	struct iked_spi		 spi;
1876d1ebe3d0Smikeb 	struct sadb_sa		*sa;
18770927abadSmikeb 	struct sadb_lifetime	*sa_ltime;
18780d1109d5Sreyk 	struct sadb_msg		*hdr;
18790d1109d5Sreyk 	struct sadb_msg		 smsg;
18800d1109d5Sreyk 	struct iked_addr	 peer;
18810d1109d5Sreyk 	struct iked_flow	 flow;
18820d1109d5Sreyk 	struct sadb_address	*sa_addr;
18830927abadSmikeb 	struct sadb_protocol	*sa_proto;
18840927abadSmikeb 	struct sadb_x_policy	 sa_pol;
188547d6a31cSmarkus 	struct sockaddr		*ssrc, *sdst, *smask, *dmask, *speer;
18860927abadSmikeb 	struct iovec		 iov[IOV_CNT];
18879e93d211Stobhe 	int			 ret = 0, iov_cnt;
1888d09d3a7dSreyk 	uint8_t			*reply;
18890d1109d5Sreyk 	ssize_t			 rlen;
18900927abadSmikeb 	const char		*errmsg = NULL;
1891d09d3a7dSreyk 	uint8_t			*data = pm->pm_data;
18921c9ff2fdSblambert 	ssize_t			 len = pm->pm_length;
189347d6a31cSmarkus 	size_t			 slen;
1894d1ebe3d0Smikeb 
1895d1ebe3d0Smikeb 	if (!env || !data || !len)
18966e1880a3Smarkus 		return (0);
1897d1ebe3d0Smikeb 
1898d1ebe3d0Smikeb 	hdr = (struct sadb_msg *)data;
1899d1ebe3d0Smikeb 
1900d1ebe3d0Smikeb 	switch (hdr->sadb_msg_type) {
19010927abadSmikeb 	case SADB_ACQUIRE:
19020927abadSmikeb 		bzero(&flow, sizeof(flow));
19030927abadSmikeb 		bzero(&peer, sizeof(peer));
19040927abadSmikeb 
19050927abadSmikeb 		if ((sa_addr = pfkey_find_ext(data, len,
19060927abadSmikeb 		    SADB_EXT_ADDRESS_DST)) == NULL) {
19070927abadSmikeb 			log_debug("%s: no peer address", __func__);
19086e1880a3Smarkus 			return (0);
1909b0eeedd0Smikeb 		}
191047d6a31cSmarkus 		speer = (struct sockaddr *)(sa_addr + 1);
191147d6a31cSmarkus 		peer.addr_af = speer->sa_family;
19120927abadSmikeb 		peer.addr_port = htons(socket_getport(speer));
191347d6a31cSmarkus 		if ((slen = speer->sa_len) > sizeof(peer.addr)) {
191447d6a31cSmarkus 			log_debug("%s: invalid peer address len", __func__);
19156e1880a3Smarkus 			return (0);
191647d6a31cSmarkus 		}
191747d6a31cSmarkus 		memcpy(&peer.addr, speer, slen);
19180927abadSmikeb 		if (socket_af((struct sockaddr *)&peer.addr,
19190927abadSmikeb 		    peer.addr_port) == -1) {
19200927abadSmikeb 			log_debug("%s: invalid address", __func__);
19216e1880a3Smarkus 			return (0);
19220927abadSmikeb 		}
19230927abadSmikeb 		flow.flow_peer = &peer;
19240927abadSmikeb 
19250927abadSmikeb 		log_debug("%s: acquire request (peer %s)", __func__,
192614e2a040Stb 		    print_addr(speer));
19270927abadSmikeb 
19280927abadSmikeb 		/* get the matching flow */
19290927abadSmikeb 		bzero(&smsg, sizeof(smsg));
19300927abadSmikeb 		smsg.sadb_msg_version = PF_KEY_V2;
19310927abadSmikeb 		smsg.sadb_msg_seq = ++sadb_msg_seq;
19320927abadSmikeb 		smsg.sadb_msg_pid = getpid();
19330927abadSmikeb 		smsg.sadb_msg_len = sizeof(smsg) / 8;
19340927abadSmikeb 		smsg.sadb_msg_type = SADB_X_ASKPOLICY;
19350927abadSmikeb 
19360927abadSmikeb 		iov_cnt = 0;
19370927abadSmikeb 
19380927abadSmikeb 		iov[iov_cnt].iov_base = &smsg;
19390927abadSmikeb 		iov[iov_cnt].iov_len = sizeof(smsg);
19400927abadSmikeb 		iov_cnt++;
19410927abadSmikeb 
19420927abadSmikeb 		bzero(&sa_pol, sizeof(sa_pol));
19430927abadSmikeb 		sa_pol.sadb_x_policy_exttype = SADB_X_EXT_POLICY;
19440927abadSmikeb 		sa_pol.sadb_x_policy_len = sizeof(sa_pol) / 8;
19450927abadSmikeb 		sa_pol.sadb_x_policy_seq = hdr->sadb_msg_seq;
19460927abadSmikeb 
19470927abadSmikeb 		iov[iov_cnt].iov_base = &sa_pol;
19480927abadSmikeb 		iov[iov_cnt].iov_len = sizeof(sa_pol);
19490927abadSmikeb 		smsg.sadb_msg_len += sizeof(sa_pol) / 8;
19500927abadSmikeb 		iov_cnt++;
19510927abadSmikeb 
1952e7c3d382Stobhe 		if (pfkey_write(env, &smsg, iov, iov_cnt, &reply, &rlen)) {
19530927abadSmikeb 			log_warnx("%s: failed to get a policy", __func__);
19546e1880a3Smarkus 			return (0);
19550927abadSmikeb 		}
19560927abadSmikeb 
19570927abadSmikeb 		if ((sa_addr = pfkey_find_ext(reply, rlen,
19580927abadSmikeb 		    SADB_X_EXT_SRC_FLOW)) == NULL) {
19590927abadSmikeb 			errmsg = "flow source address";
19600927abadSmikeb 			goto out;
19610927abadSmikeb 		}
196247d6a31cSmarkus 		ssrc = (struct sockaddr *)(sa_addr + 1);
196347d6a31cSmarkus 		flow.flow_src.addr_af = ssrc->sa_family;
19640927abadSmikeb 		flow.flow_src.addr_port = htons(socket_getport(ssrc));
196547d6a31cSmarkus 		if ((slen = ssrc->sa_len) > sizeof(flow.flow_src.addr)) {
196647d6a31cSmarkus 			log_debug("%s: invalid src address len", __func__);
19671c738e03Stobhe 			goto out;
196847d6a31cSmarkus 		}
196947d6a31cSmarkus 		memcpy(&flow.flow_src.addr, ssrc, slen);
19700927abadSmikeb 		if (socket_af((struct sockaddr *)&flow.flow_src.addr,
19710927abadSmikeb 		    flow.flow_src.addr_port) == -1) {
19720927abadSmikeb 			log_debug("%s: invalid address", __func__);
19731c738e03Stobhe 			goto out;
19740927abadSmikeb 		}
19750927abadSmikeb 
19760927abadSmikeb 		if ((sa_addr = pfkey_find_ext(reply, rlen,
19770927abadSmikeb 		    SADB_X_EXT_DST_FLOW)) == NULL) {
19780927abadSmikeb 			errmsg = "flow destination address";
19790927abadSmikeb 			goto out;
19800927abadSmikeb 		}
198147d6a31cSmarkus 		sdst = (struct sockaddr *)(sa_addr + 1);
198247d6a31cSmarkus 		flow.flow_dst.addr_af = sdst->sa_family;
19830927abadSmikeb 		flow.flow_dst.addr_port = htons(socket_getport(sdst));
198447d6a31cSmarkus 		if ((slen = sdst->sa_len) > sizeof(flow.flow_dst.addr)) {
198547d6a31cSmarkus 			log_debug("%s: invalid dst address len", __func__);
19861c738e03Stobhe 			goto out;
198747d6a31cSmarkus 		}
198847d6a31cSmarkus 		memcpy(&flow.flow_dst.addr, sdst, slen);
19890927abadSmikeb 		if (socket_af((struct sockaddr *)&flow.flow_dst.addr,
19900927abadSmikeb 		    flow.flow_dst.addr_port) == -1) {
19910927abadSmikeb 			log_debug("%s: invalid address", __func__);
19921c738e03Stobhe 			goto out;
19930927abadSmikeb 		}
19940927abadSmikeb 
19950927abadSmikeb 		if ((sa_addr = pfkey_find_ext(reply, rlen,
19960927abadSmikeb 		    SADB_X_EXT_SRC_MASK)) == NULL) {
19970927abadSmikeb 			errmsg = "flow source mask";
19980927abadSmikeb 			goto out;
19990927abadSmikeb 		}
200047d6a31cSmarkus 		smask = (struct sockaddr *)(sa_addr + 1);
200147d6a31cSmarkus 		switch (smask->sa_family) {
20020927abadSmikeb 		case AF_INET:
20030927abadSmikeb 			flow.flow_src.addr_mask =
20040927abadSmikeb 			    mask2prefixlen((struct sockaddr *)smask);
20050927abadSmikeb 			if (flow.flow_src.addr_mask != 32)
20060927abadSmikeb 				flow.flow_src.addr_net = 1;
20070927abadSmikeb 			break;
20080927abadSmikeb 		case AF_INET6:
20090927abadSmikeb 			flow.flow_src.addr_mask =
20100927abadSmikeb 			    mask2prefixlen6((struct sockaddr *)smask);
20110927abadSmikeb 			if (flow.flow_src.addr_mask != 128)
20120927abadSmikeb 				flow.flow_src.addr_net = 1;
20130927abadSmikeb 			break;
20140927abadSmikeb 		default:
20150927abadSmikeb 			log_debug("%s: bad address family", __func__);
20161c738e03Stobhe 			goto out;
20170927abadSmikeb 		}
20180927abadSmikeb 
20190927abadSmikeb 		if ((sa_addr = pfkey_find_ext(reply, rlen,
20200927abadSmikeb 		    SADB_X_EXT_DST_MASK)) == NULL) {
20210927abadSmikeb 			errmsg = "flow destination mask";
20220927abadSmikeb 			goto out;
20230927abadSmikeb 		}
202447d6a31cSmarkus 		dmask = (struct sockaddr *)(sa_addr + 1);
202547d6a31cSmarkus 		switch (dmask->sa_family) {
20260927abadSmikeb 		case AF_INET:
20270927abadSmikeb 			flow.flow_dst.addr_mask =
20280927abadSmikeb 			    mask2prefixlen((struct sockaddr *)dmask);
20290927abadSmikeb 			if (flow.flow_src.addr_mask != 32)
20300927abadSmikeb 				flow.flow_src.addr_net = 1;
20310927abadSmikeb 			break;
20320927abadSmikeb 		case AF_INET6:
20330927abadSmikeb 			flow.flow_dst.addr_mask =
20340927abadSmikeb 			    mask2prefixlen6((struct sockaddr *)dmask);
20350927abadSmikeb 			if (flow.flow_src.addr_mask != 128)
20360927abadSmikeb 				flow.flow_src.addr_net = 1;
20370927abadSmikeb 			break;
20380927abadSmikeb 		default:
20390927abadSmikeb 			log_debug("%s: bad address family", __func__);
20401c738e03Stobhe 			goto out;
20410927abadSmikeb 		}
20420927abadSmikeb 
20436946103dStobhe 		switch (hdr->sadb_msg_satype) {
20446946103dStobhe 		case SADB_SATYPE_AH:
20456946103dStobhe 			flow.flow_saproto = IKEV2_SAPROTO_AH;
20466946103dStobhe 			break;
20476946103dStobhe 		case SADB_SATYPE_ESP:
20486946103dStobhe 			flow.flow_saproto = IKEV2_SAPROTO_ESP;
20496946103dStobhe 			break;
20506946103dStobhe 		case SADB_X_SATYPE_IPCOMP:
20516946103dStobhe 			flow.flow_saproto = IKEV2_SAPROTO_IPCOMP;
20526946103dStobhe 			break;
20536946103dStobhe 		}
20546946103dStobhe 
20550927abadSmikeb 		if ((sa_proto = pfkey_find_ext(reply, rlen,
20560927abadSmikeb 		    SADB_X_EXT_FLOW_TYPE)) == NULL) {
20570927abadSmikeb 			errmsg = "flow protocol";
20580927abadSmikeb 			goto out;
20590927abadSmikeb 		}
20600927abadSmikeb 		flow.flow_dir = sa_proto->sadb_protocol_direction;
20610347364bStobhe 		flow.flow_rdomain = -1;	/* XXX get from kernel */
20620927abadSmikeb 
20630927abadSmikeb 		log_debug("%s: flow %s from %s/%s to %s/%s via %s", __func__,
20640927abadSmikeb 		    flow.flow_dir == IPSP_DIRECTION_IN ? "in" : "out",
206514e2a040Stb 		    print_addr(ssrc), print_addr(smask),
206614e2a040Stb 		    print_addr(sdst), print_addr(dmask),
206714e2a040Stb 		    print_addr(speer));
20680927abadSmikeb 
2069ff6390f7Stobhe 		ret = ikev2_child_sa_acquire(env, &flow);
20700927abadSmikeb 
20710927abadSmikeb out:
20720927abadSmikeb 		if (errmsg)
20730927abadSmikeb 			log_warnx("%s: %s wasn't found", __func__, errmsg);
20740927abadSmikeb 		free(reply);
20750927abadSmikeb 		break;
20760927abadSmikeb 
20770927abadSmikeb 	case SADB_EXPIRE:
20780927abadSmikeb 		if ((sa = pfkey_find_ext(data, len, SADB_EXT_SA)) == NULL) {
20790927abadSmikeb 			log_warnx("%s: SA extension wasn't found", __func__);
20806e1880a3Smarkus 			return (0);
20810927abadSmikeb 		}
20820927abadSmikeb 		if ((sa_ltime = pfkey_find_ext(data, len,
2083b0eeedd0Smikeb 			SADB_EXT_LIFETIME_SOFT)) == NULL &&
20840927abadSmikeb 		    (sa_ltime = pfkey_find_ext(data, len,
2085b0eeedd0Smikeb 			SADB_EXT_LIFETIME_HARD)) == NULL) {
20860927abadSmikeb 			log_warnx("%s: lifetime extension wasn't found",
2087b0eeedd0Smikeb 			    __func__);
20886e1880a3Smarkus 			return (0);
2089b0eeedd0Smikeb 		}
2090b0eeedd0Smikeb 		spi.spi = ntohl(sa->sadb_sa_spi);
2091b0eeedd0Smikeb 		spi.spi_size = 4;
2092d1ebe3d0Smikeb 		switch (hdr->sadb_msg_satype) {
2093b0eeedd0Smikeb 		case SADB_SATYPE_AH:
2094b0eeedd0Smikeb 			spi.spi_protoid = IKEV2_SAPROTO_AH;
2095b0eeedd0Smikeb 			break;
2096b0eeedd0Smikeb 		case SADB_SATYPE_ESP:
2097b0eeedd0Smikeb 			spi.spi_protoid = IKEV2_SAPROTO_ESP;
2098b0eeedd0Smikeb 			break;
209965f3034eSmarkus 		case SADB_X_SATYPE_IPCOMP:
210065f3034eSmarkus 			spi.spi_size = 2;
210165f3034eSmarkus 			spi.spi_protoid = IKEV2_SAPROTO_IPCOMP;
210265f3034eSmarkus 			break;
2103b0eeedd0Smikeb 		default:
210490ef7001Smarkus 			log_warnx("%s: unsupported SA type %d spi %s",
2105d1ebe3d0Smikeb 			    __func__, hdr->sadb_msg_satype,
2106b0eeedd0Smikeb 			    print_spi(spi.spi, spi.spi_size));
21076e1880a3Smarkus 			return (0);
2108b0eeedd0Smikeb 		}
2109b0eeedd0Smikeb 
2110b0eeedd0Smikeb 		log_debug("%s: SA %s is expired, pending %s", __func__,
2111b0eeedd0Smikeb 		    print_spi(spi.spi, spi.spi_size),
21120927abadSmikeb 		    sa_ltime->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT ?
2113b0eeedd0Smikeb 		    "rekeying" : "deletion");
2114b0eeedd0Smikeb 
21150927abadSmikeb 		if (sa_ltime->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT)
2116ff6390f7Stobhe 			ret = ikev2_child_sa_rekey(env, &spi);
2117b0eeedd0Smikeb 		else
2118ff6390f7Stobhe 			ret = ikev2_child_sa_drop(env, &spi);
2119b0eeedd0Smikeb 		break;
2120b0eeedd0Smikeb 	}
21216e1880a3Smarkus 	return (ret);
2122b0eeedd0Smikeb }
2123