xref: /openbsd/sbin/ipsecctl/pfkey.c (revision 202aab43)
1*202aab43Stobhe /*	$OpenBSD: pfkey.c,v 1.64 2023/10/09 15:32:14 tobhe Exp $	*/
2f484f2cfShshoexer /*
3f484f2cfShshoexer  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
4f484f2cfShshoexer  * Copyright (c) 2003, 2004 Markus Friedl <markus@openbsd.org>
5f484f2cfShshoexer  * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org>
6f484f2cfShshoexer  *
7f484f2cfShshoexer  * Permission to use, copy, modify, and distribute this software for any
8f484f2cfShshoexer  * purpose with or without fee is hereby granted, provided that the above
9f484f2cfShshoexer  * copyright notice and this permission notice appear in all copies.
10f484f2cfShshoexer  *
11f484f2cfShshoexer  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12f484f2cfShshoexer  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13f484f2cfShshoexer  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14f484f2cfShshoexer  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15f484f2cfShshoexer  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16f484f2cfShshoexer  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17f484f2cfShshoexer  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18f484f2cfShshoexer  */
19f484f2cfShshoexer 
20f484f2cfShshoexer #include <sys/types.h>
21f484f2cfShshoexer #include <sys/queue.h>
22f484f2cfShshoexer #include <sys/uio.h>
23f484f2cfShshoexer #include <sys/socket.h>
24f484f2cfShshoexer #include <netinet/in.h>
25f484f2cfShshoexer #include <netinet/ip_ipsp.h>
26f484f2cfShshoexer #include <net/pfkeyv2.h>
27f484f2cfShshoexer 
28f484f2cfShshoexer #include <err.h>
29f484f2cfShshoexer #include <errno.h>
30f484f2cfShshoexer #include <stdio.h>
31f484f2cfShshoexer #include <string.h>
32f484f2cfShshoexer #include <stdlib.h>
3356232324Sderaadt #include <poll.h>
34f484f2cfShshoexer #include <unistd.h>
35f484f2cfShshoexer 
36f484f2cfShshoexer #include "ipsecctl.h"
371edc1b9aShshoexer #include "pfkey.h"
38f484f2cfShshoexer 
39f484f2cfShshoexer #define ROUNDUP(x) (((x) + (PFKEYV2_CHUNK - 1)) & ~(PFKEYV2_CHUNK - 1))
40f484f2cfShshoexer #define IOV_CNT 20
41f484f2cfShshoexer 
42f484f2cfShshoexer static int	fd;
43f484f2cfShshoexer static u_int32_t sadb_msg_seq = 1;
44f484f2cfShshoexer 
459182219dSmarkus static int	pfkey_flow(int, u_int8_t, u_int8_t, u_int8_t, u_int8_t,
4657f58d0dSnaddy 		    struct ipsec_addr_wrap *, u_int16_t,
4757f58d0dSnaddy 		    struct ipsec_addr_wrap *, u_int16_t,
48435bb41eSmarkus 		    struct ipsec_addr_wrap *, struct ipsec_addr_wrap *,
49435bb41eSmarkus 		    struct ipsec_auth *, u_int8_t);
50f032086dShshoexer static int	pfkey_sa(int, u_int8_t, u_int8_t, u_int32_t,
5191f765ddShshoexer 		    struct ipsec_addr_wrap *, struct ipsec_addr_wrap *,
52a3b4c2abSbluhm 		    u_int8_t, u_int16_t,
53375db29dShshoexer 		    struct ipsec_transforms *, struct ipsec_key *,
54a38d220fShshoexer 		    struct ipsec_key *, u_int8_t);
55a6bcba92Sbluhm static int	pfkey_sabundle(int, u_int8_t, u_int8_t, u_int8_t,
568065703bShshoexer 		    struct ipsec_addr_wrap *, u_int32_t,
578065703bShshoexer 		    struct ipsec_addr_wrap *, u_int32_t);
588a87fca6Smsf static int	pfkey_reply(int, u_int8_t **, ssize_t *);
59e225c210Shshoexer int		pfkey_parse(struct sadb_msg *, struct ipsec_rule *);
60f484f2cfShshoexer int		pfkey_ipsec_flush(void);
61356121f6Shshoexer int		pfkey_ipsec_establish(int, struct ipsec_rule *);
62f484f2cfShshoexer int		pfkey_init(void);
63f484f2cfShshoexer 
64f484f2cfShshoexer static int
pfkey_flow(int sd,u_int8_t satype,u_int8_t action,u_int8_t direction,u_int8_t proto,struct ipsec_addr_wrap * src,u_int16_t sport,struct ipsec_addr_wrap * dst,u_int16_t dport,struct ipsec_addr_wrap * local,struct ipsec_addr_wrap * peer,struct ipsec_auth * auth,u_int8_t flowtype)65f484f2cfShshoexer pfkey_flow(int sd, u_int8_t satype, u_int8_t action, u_int8_t direction,
6657f58d0dSnaddy     u_int8_t proto, struct ipsec_addr_wrap *src, u_int16_t sport,
6757f58d0dSnaddy     struct ipsec_addr_wrap *dst, u_int16_t dport,
68435bb41eSmarkus     struct ipsec_addr_wrap *local, struct ipsec_addr_wrap *peer,
69435bb41eSmarkus     struct ipsec_auth *auth, u_int8_t flowtype)
70f484f2cfShshoexer {
71f484f2cfShshoexer 	struct sadb_msg		 smsg;
72435bb41eSmarkus 	struct sadb_address	 sa_src, sa_dst, sa_local, sa_peer, sa_smask,
73435bb41eSmarkus 				 sa_dmask;
74f484f2cfShshoexer 	struct sadb_protocol	 sa_flowtype, sa_protocol;
75f484f2cfShshoexer 	struct sadb_ident	*sa_srcid, *sa_dstid;
76435bb41eSmarkus 	struct sockaddr_storage	 ssrc, sdst, slocal, speer, smask, dmask;
77f484f2cfShshoexer 	struct iovec		 iov[IOV_CNT];
78f484f2cfShshoexer 	ssize_t			 n;
79f484f2cfShshoexer 	int			 iov_cnt, len, ret = 0;
80f484f2cfShshoexer 
81f484f2cfShshoexer 	sa_srcid = sa_dstid = NULL;
82f484f2cfShshoexer 
83f484f2cfShshoexer 	bzero(&ssrc, sizeof(ssrc));
84f484f2cfShshoexer 	bzero(&smask, sizeof(smask));
852099bcdfStodd 	ssrc.ss_family = smask.ss_family = src->af;
86f484f2cfShshoexer 	switch (src->af) {
87f484f2cfShshoexer 	case AF_INET:
88712e78baShshoexer 		((struct sockaddr_in *)&ssrc)->sin_addr = src->address.v4;
89f484f2cfShshoexer 		ssrc.ss_len = sizeof(struct sockaddr_in);
90712e78baShshoexer 		((struct sockaddr_in *)&smask)->sin_addr = src->mask.v4;
9157f58d0dSnaddy 		if (sport) {
9257f58d0dSnaddy 			((struct sockaddr_in *)&ssrc)->sin_port = sport;
9357f58d0dSnaddy 			((struct sockaddr_in *)&smask)->sin_port = 0xffff;
9457f58d0dSnaddy 		}
95f484f2cfShshoexer 		break;
96f484f2cfShshoexer 	case AF_INET6:
972099bcdfStodd 		((struct sockaddr_in6 *)&ssrc)->sin6_addr = src->address.v6;
982099bcdfStodd 		ssrc.ss_len = sizeof(struct sockaddr_in6);
992099bcdfStodd 		((struct sockaddr_in6 *)&smask)->sin6_addr = src->mask.v6;
10057f58d0dSnaddy 		if (sport) {
10157f58d0dSnaddy 			((struct sockaddr_in6 *)&ssrc)->sin6_port = sport;
10257f58d0dSnaddy 			((struct sockaddr_in6 *)&smask)->sin6_port = 0xffff;
10357f58d0dSnaddy 		}
1042099bcdfStodd 		break;
105f484f2cfShshoexer 	default:
106f484f2cfShshoexer 		warnx("unsupported address family %d", src->af);
107f484f2cfShshoexer 		return -1;
108f484f2cfShshoexer 	}
109f484f2cfShshoexer 	smask.ss_len = ssrc.ss_len;
110f484f2cfShshoexer 
111f484f2cfShshoexer 	bzero(&sdst, sizeof(sdst));
112f484f2cfShshoexer 	bzero(&dmask, sizeof(dmask));
1132099bcdfStodd 	sdst.ss_family = dmask.ss_family = dst->af;
114f484f2cfShshoexer 	switch (dst->af) {
115f484f2cfShshoexer 	case AF_INET:
116712e78baShshoexer 		((struct sockaddr_in *)&sdst)->sin_addr = dst->address.v4;
117f484f2cfShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in);
118712e78baShshoexer 		((struct sockaddr_in *)&dmask)->sin_addr = dst->mask.v4;
11957f58d0dSnaddy 		if (dport) {
12057f58d0dSnaddy 			((struct sockaddr_in *)&sdst)->sin_port = dport;
12157f58d0dSnaddy 			((struct sockaddr_in *)&dmask)->sin_port = 0xffff;
12257f58d0dSnaddy 		}
123f484f2cfShshoexer 		break;
124f484f2cfShshoexer 	case AF_INET6:
1252099bcdfStodd 		((struct sockaddr_in6 *)&sdst)->sin6_addr = dst->address.v6;
1262099bcdfStodd 		sdst.ss_len = sizeof(struct sockaddr_in6);
1272099bcdfStodd 		((struct sockaddr_in6 *)&dmask)->sin6_addr = dst->mask.v6;
12857f58d0dSnaddy 		if (dport) {
12957f58d0dSnaddy 			((struct sockaddr_in6 *)&sdst)->sin6_port = dport;
13057f58d0dSnaddy 			((struct sockaddr_in6 *)&dmask)->sin6_port = 0xffff;
13157f58d0dSnaddy 		}
1322099bcdfStodd 		break;
133f484f2cfShshoexer 	default:
134f484f2cfShshoexer 		warnx("unsupported address family %d", dst->af);
135f484f2cfShshoexer 		return -1;
136f484f2cfShshoexer 	}
137f484f2cfShshoexer 	dmask.ss_len = sdst.ss_len;
138f484f2cfShshoexer 
139435bb41eSmarkus 	bzero(&slocal, sizeof(slocal));
140435bb41eSmarkus 	if (local) {
1412099bcdfStodd 		slocal.ss_family = local->af;
142435bb41eSmarkus 		switch (local->af) {
143435bb41eSmarkus 		case AF_INET:
144435bb41eSmarkus 			((struct sockaddr_in *)&slocal)->sin_addr =
145435bb41eSmarkus 			    local->address.v4;
146435bb41eSmarkus 			slocal.ss_len = sizeof(struct sockaddr_in);
147435bb41eSmarkus 			break;
148435bb41eSmarkus 		case AF_INET6:
1492099bcdfStodd 			((struct sockaddr_in6 *)&slocal)->sin6_addr =
1502099bcdfStodd 			    local->address.v6;
1512099bcdfStodd 			slocal.ss_len = sizeof(struct sockaddr_in6);
1522099bcdfStodd 			break;
153435bb41eSmarkus 		default:
154435bb41eSmarkus 			warnx("unsupported address family %d", local->af);
155435bb41eSmarkus 			return -1;
156435bb41eSmarkus 		}
157435bb41eSmarkus 	}
158435bb41eSmarkus 
159f484f2cfShshoexer 	bzero(&speer, sizeof(speer));
16022a29ad6Shshoexer 	if (peer) {
1612099bcdfStodd 		speer.ss_family = peer->af;
162f484f2cfShshoexer 		switch (peer->af) {
163f484f2cfShshoexer 		case AF_INET:
164712e78baShshoexer 			((struct sockaddr_in *)&speer)->sin_addr =
165712e78baShshoexer 			    peer->address.v4;
166f484f2cfShshoexer 			speer.ss_len = sizeof(struct sockaddr_in);
167f484f2cfShshoexer 			break;
168f484f2cfShshoexer 		case AF_INET6:
1692099bcdfStodd 			((struct sockaddr_in6 *)&speer)->sin6_addr =
1702099bcdfStodd 			    peer->address.v6;
1712099bcdfStodd 			speer.ss_len = sizeof(struct sockaddr_in6);
1722099bcdfStodd 			break;
173f484f2cfShshoexer 		default:
174f484f2cfShshoexer 			warnx("unsupported address family %d", peer->af);
175f484f2cfShshoexer 			return -1;
176f484f2cfShshoexer 		}
17722a29ad6Shshoexer 	}
178f484f2cfShshoexer 
179f484f2cfShshoexer 	bzero(&smsg, sizeof(smsg));
180f484f2cfShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
181f484f2cfShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
182f484f2cfShshoexer 	smsg.sadb_msg_pid = getpid();
183f484f2cfShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
184f484f2cfShshoexer 	smsg.sadb_msg_type = action;
185f484f2cfShshoexer 	smsg.sadb_msg_satype = satype;
186f484f2cfShshoexer 
187f484f2cfShshoexer 	bzero(&sa_flowtype, sizeof(sa_flowtype));
188f484f2cfShshoexer 	sa_flowtype.sadb_protocol_exttype = SADB_X_EXT_FLOW_TYPE;
189f484f2cfShshoexer 	sa_flowtype.sadb_protocol_len = sizeof(sa_flowtype) / 8;
190f484f2cfShshoexer 	sa_flowtype.sadb_protocol_direction = direction;
19126df514dShshoexer 
192e1ffbfafShshoexer 	switch (flowtype) {
193e1ffbfafShshoexer 	case TYPE_USE:
19426df514dShshoexer 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_USE;
195e1ffbfafShshoexer 		break;
196b7a16601Shshoexer 	case TYPE_ACQUIRE:
197b7a16601Shshoexer 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_ACQUIRE;
198b7a16601Shshoexer 		break;
199e1ffbfafShshoexer 	case TYPE_REQUIRE:
200f484f2cfShshoexer 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_REQUIRE;
201e1ffbfafShshoexer 		break;
2027c7fb9e5Sreyk 	case TYPE_DENY:
2037c7fb9e5Sreyk 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_DENY;
2047c7fb9e5Sreyk 		break;
2057c7fb9e5Sreyk 	case TYPE_BYPASS:
2067c7fb9e5Sreyk 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_BYPASS;
2077c7fb9e5Sreyk 		break;
208b7a16601Shshoexer 	case TYPE_DONTACQ:
209b7a16601Shshoexer 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_DONTACQ;
210b7a16601Shshoexer 		break;
211e1ffbfafShshoexer 	default:
212e1ffbfafShshoexer 		warnx("unsupported flowtype %d", flowtype);
213e1ffbfafShshoexer 		return -1;
214e1ffbfafShshoexer 	}
215f484f2cfShshoexer 
216f484f2cfShshoexer 	bzero(&sa_protocol, sizeof(sa_protocol));
217f484f2cfShshoexer 	sa_protocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
218f484f2cfShshoexer 	sa_protocol.sadb_protocol_len = sizeof(sa_protocol) / 8;
219f484f2cfShshoexer 	sa_protocol.sadb_protocol_direction = 0;
2209182219dSmarkus 	sa_protocol.sadb_protocol_proto = proto;
221f484f2cfShshoexer 
222f484f2cfShshoexer 	bzero(&sa_src, sizeof(sa_src));
223f484f2cfShshoexer 	sa_src.sadb_address_exttype = SADB_X_EXT_SRC_FLOW;
224f484f2cfShshoexer 	sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
225f484f2cfShshoexer 
226f484f2cfShshoexer 	bzero(&sa_smask, sizeof(sa_smask));
227f484f2cfShshoexer 	sa_smask.sadb_address_exttype = SADB_X_EXT_SRC_MASK;
228f484f2cfShshoexer 	sa_smask.sadb_address_len =
229f484f2cfShshoexer 	    (sizeof(sa_smask) + ROUNDUP(smask.ss_len)) / 8;
230f484f2cfShshoexer 
231f484f2cfShshoexer 	bzero(&sa_dst, sizeof(sa_dst));
232f484f2cfShshoexer 	sa_dst.sadb_address_exttype = SADB_X_EXT_DST_FLOW;
233f484f2cfShshoexer 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
234f484f2cfShshoexer 
235f484f2cfShshoexer 	bzero(&sa_dmask, sizeof(sa_dmask));
236f484f2cfShshoexer 	sa_dmask.sadb_address_exttype = SADB_X_EXT_DST_MASK;
237f484f2cfShshoexer 	sa_dmask.sadb_address_len =
238f484f2cfShshoexer 	    (sizeof(sa_dmask) + ROUNDUP(dmask.ss_len)) / 8;
239f484f2cfShshoexer 
240435bb41eSmarkus 	if (local) {
241435bb41eSmarkus 		bzero(&sa_local, sizeof(sa_local));
242435bb41eSmarkus 		sa_local.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
243435bb41eSmarkus 		sa_local.sadb_address_len =
244435bb41eSmarkus 		    (sizeof(sa_local) + ROUNDUP(slocal.ss_len)) / 8;
245435bb41eSmarkus 	}
246435bb41eSmarkus 	if (peer) {
247f484f2cfShshoexer 		bzero(&sa_peer, sizeof(sa_peer));
248f484f2cfShshoexer 		sa_peer.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
249f484f2cfShshoexer 		sa_peer.sadb_address_len =
250f484f2cfShshoexer 		    (sizeof(sa_peer) + ROUNDUP(speer.ss_len)) / 8;
251435bb41eSmarkus 	}
252f484f2cfShshoexer 
253abe65127Shshoexer 	if (auth && auth->srcid) {
254abe65127Shshoexer 		len = ROUNDUP(strlen(auth->srcid) + 1) + sizeof(*sa_srcid);
255f484f2cfShshoexer 
256f484f2cfShshoexer 		sa_srcid = calloc(len, sizeof(u_int8_t));
257f484f2cfShshoexer 		if (sa_srcid == NULL)
258bd828a90Shshoexer 			err(1, "pfkey_flow: calloc");
259f484f2cfShshoexer 
2602281ca6dSmarkus 		sa_srcid->sadb_ident_type = auth->srcid_type;
261f484f2cfShshoexer 		sa_srcid->sadb_ident_len = len / 8;
262f484f2cfShshoexer 		sa_srcid->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
263f484f2cfShshoexer 
264abe65127Shshoexer 		strlcpy((char *)(sa_srcid + 1), auth->srcid,
265abe65127Shshoexer 		    ROUNDUP(strlen(auth->srcid) + 1));
266f484f2cfShshoexer 	}
267abe65127Shshoexer 	if (auth && auth->dstid) {
268abe65127Shshoexer 		len = ROUNDUP(strlen(auth->dstid) + 1) + sizeof(*sa_dstid);
269f484f2cfShshoexer 
270f484f2cfShshoexer 		sa_dstid = calloc(len, sizeof(u_int8_t));
271f484f2cfShshoexer 		if (sa_dstid == NULL)
272bd828a90Shshoexer 			err(1, "pfkey_flow: calloc");
273f484f2cfShshoexer 
2742281ca6dSmarkus 		sa_dstid->sadb_ident_type = auth->dstid_type;
275f484f2cfShshoexer 		sa_dstid->sadb_ident_len = len / 8;
276f484f2cfShshoexer 		sa_dstid->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
277f484f2cfShshoexer 
278abe65127Shshoexer 		strlcpy((char *)(sa_dstid + 1), auth->dstid,
279abe65127Shshoexer 		    ROUNDUP(strlen(auth->dstid) + 1));
280f484f2cfShshoexer 	}
281f484f2cfShshoexer 
282f484f2cfShshoexer 	iov_cnt = 0;
283f484f2cfShshoexer 
284f484f2cfShshoexer 	/* header */
285f484f2cfShshoexer 	iov[iov_cnt].iov_base = &smsg;
286f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
287f484f2cfShshoexer 	iov_cnt++;
288f484f2cfShshoexer 
28989ad8c34Shshoexer 	/* add flow type */
29089ad8c34Shshoexer 	iov[iov_cnt].iov_base = &sa_flowtype;
29189ad8c34Shshoexer 	iov[iov_cnt].iov_len = sizeof(sa_flowtype);
29289ad8c34Shshoexer 	smsg.sadb_msg_len += sa_flowtype.sadb_protocol_len;
29389ad8c34Shshoexer 	iov_cnt++;
29489ad8c34Shshoexer 
295435bb41eSmarkus 	/* local ip */
296435bb41eSmarkus 	if (local) {
297435bb41eSmarkus 		iov[iov_cnt].iov_base = &sa_local;
298435bb41eSmarkus 		iov[iov_cnt].iov_len = sizeof(sa_local);
299435bb41eSmarkus 		iov_cnt++;
300435bb41eSmarkus 		iov[iov_cnt].iov_base = &slocal;
301435bb41eSmarkus 		iov[iov_cnt].iov_len = ROUNDUP(slocal.ss_len);
302435bb41eSmarkus 		smsg.sadb_msg_len += sa_local.sadb_address_len;
303435bb41eSmarkus 		iov_cnt++;
304435bb41eSmarkus 	}
305435bb41eSmarkus 
306f484f2cfShshoexer 	/* remote peer */
30722a29ad6Shshoexer 	if (peer) {
308f484f2cfShshoexer 		iov[iov_cnt].iov_base = &sa_peer;
309f484f2cfShshoexer 		iov[iov_cnt].iov_len = sizeof(sa_peer);
310f484f2cfShshoexer 		iov_cnt++;
311f484f2cfShshoexer 		iov[iov_cnt].iov_base = &speer;
312f484f2cfShshoexer 		iov[iov_cnt].iov_len = ROUNDUP(speer.ss_len);
313f484f2cfShshoexer 		smsg.sadb_msg_len += sa_peer.sadb_address_len;
314f484f2cfShshoexer 		iov_cnt++;
31522a29ad6Shshoexer 	}
316f484f2cfShshoexer 
31789ad8c34Shshoexer 	/* src addr */
31889ad8c34Shshoexer 	iov[iov_cnt].iov_base = &sa_src;
31989ad8c34Shshoexer 	iov[iov_cnt].iov_len = sizeof(sa_src);
32089ad8c34Shshoexer 	iov_cnt++;
32189ad8c34Shshoexer 	iov[iov_cnt].iov_base = &ssrc;
32289ad8c34Shshoexer 	iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len);
32389ad8c34Shshoexer 	smsg.sadb_msg_len += sa_src.sadb_address_len;
324f484f2cfShshoexer 	iov_cnt++;
325f484f2cfShshoexer 
32689ad8c34Shshoexer 	/* src mask */
327f484f2cfShshoexer 	iov[iov_cnt].iov_base = &sa_smask;
328f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_smask);
329f484f2cfShshoexer 	iov_cnt++;
330f484f2cfShshoexer 	iov[iov_cnt].iov_base = &smask;
331f484f2cfShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(smask.ss_len);
332f484f2cfShshoexer 	smsg.sadb_msg_len += sa_smask.sadb_address_len;
333f484f2cfShshoexer 	iov_cnt++;
334f484f2cfShshoexer 
335f484f2cfShshoexer 	/* dest addr */
336f484f2cfShshoexer 	iov[iov_cnt].iov_base = &sa_dst;
337f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst);
338f484f2cfShshoexer 	iov_cnt++;
339f484f2cfShshoexer 	iov[iov_cnt].iov_base = &sdst;
340f484f2cfShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
341f484f2cfShshoexer 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
342f484f2cfShshoexer 	iov_cnt++;
343f484f2cfShshoexer 
34489ad8c34Shshoexer 	/* dst mask */
34589ad8c34Shshoexer 	iov[iov_cnt].iov_base = &sa_dmask;
34689ad8c34Shshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dmask);
347f484f2cfShshoexer 	iov_cnt++;
34889ad8c34Shshoexer 	iov[iov_cnt].iov_base = &dmask;
34989ad8c34Shshoexer 	iov[iov_cnt].iov_len = ROUNDUP(dmask.ss_len);
35089ad8c34Shshoexer 	smsg.sadb_msg_len += sa_dmask.sadb_address_len;
35189ad8c34Shshoexer 	iov_cnt++;
35289ad8c34Shshoexer 
35389ad8c34Shshoexer 	/* add protocol */
35489ad8c34Shshoexer 	iov[iov_cnt].iov_base = &sa_protocol;
35589ad8c34Shshoexer 	iov[iov_cnt].iov_len = sizeof(sa_protocol);
35689ad8c34Shshoexer 	smsg.sadb_msg_len += sa_protocol.sadb_protocol_len;
357f484f2cfShshoexer 	iov_cnt++;
358f484f2cfShshoexer 
359f484f2cfShshoexer 	if (sa_srcid) {
360f484f2cfShshoexer 		/* src identity */
361f484f2cfShshoexer 		iov[iov_cnt].iov_base = sa_srcid;
362f484f2cfShshoexer 		iov[iov_cnt].iov_len = sa_srcid->sadb_ident_len * 8;
363f484f2cfShshoexer 		smsg.sadb_msg_len += sa_srcid->sadb_ident_len;
364f484f2cfShshoexer 		iov_cnt++;
365f484f2cfShshoexer 	}
366f484f2cfShshoexer 	if (sa_dstid) {
367f484f2cfShshoexer 		/* dst identity */
368f484f2cfShshoexer 		iov[iov_cnt].iov_base = sa_dstid;
369f484f2cfShshoexer 		iov[iov_cnt].iov_len = sa_dstid->sadb_ident_len * 8;
370f484f2cfShshoexer 		smsg.sadb_msg_len += sa_dstid->sadb_ident_len;
371f484f2cfShshoexer 		iov_cnt++;
372f484f2cfShshoexer 	}
373f484f2cfShshoexer 	len = smsg.sadb_msg_len * 8;
37489d271caShenning 
37589d271caShenning 	do {
37689d271caShenning 		n = writev(sd, iov, iov_cnt);
37789d271caShenning 	} while (n == -1 && (errno == EAGAIN || errno == EINTR));
37889d271caShenning 	if (n == -1) {
379f484f2cfShshoexer 		warn("writev failed");
380f484f2cfShshoexer 		ret = -1;
381f484f2cfShshoexer 	}
382f484f2cfShshoexer 
383f484f2cfShshoexer 	free(sa_srcid);
384f484f2cfShshoexer 	free(sa_dstid);
385f484f2cfShshoexer 
386f484f2cfShshoexer 	return ret;
387f484f2cfShshoexer }
388f484f2cfShshoexer 
389f484f2cfShshoexer static int
pfkey_sa(int sd,u_int8_t satype,u_int8_t action,u_int32_t spi,struct ipsec_addr_wrap * src,struct ipsec_addr_wrap * dst,u_int8_t encap,u_int16_t dport,struct ipsec_transforms * xfs,struct ipsec_key * authkey,struct ipsec_key * enckey,u_int8_t tmode)39091f765ddShshoexer pfkey_sa(int sd, u_int8_t satype, u_int8_t action, u_int32_t spi,
39191f765ddShshoexer     struct ipsec_addr_wrap *src, struct ipsec_addr_wrap *dst,
392a3b4c2abSbluhm     u_int8_t encap, u_int16_t dport,
39391f765ddShshoexer     struct ipsec_transforms *xfs, struct ipsec_key *authkey,
394a38d220fShshoexer     struct ipsec_key *enckey, u_int8_t tmode)
395f032086dShshoexer {
396f032086dShshoexer 	struct sadb_msg		smsg;
397f032086dShshoexer 	struct sadb_sa		sa;
398f032086dShshoexer 	struct sadb_address	sa_src, sa_dst;
399881e2068Shshoexer 	struct sadb_key		sa_authkey, sa_enckey;
400a3b4c2abSbluhm 	struct sadb_x_udpencap	udpencap;
401f032086dShshoexer 	struct sockaddr_storage	ssrc, sdst;
402f032086dShshoexer 	struct iovec		iov[IOV_CNT];
403f032086dShshoexer 	ssize_t			n;
404f032086dShshoexer 	int			iov_cnt, len, ret = 0;
405f032086dShshoexer 
406f032086dShshoexer 	bzero(&ssrc, sizeof(ssrc));
4072099bcdfStodd 	ssrc.ss_family = src->af;
408f032086dShshoexer 	switch (src->af) {
409f032086dShshoexer 	case AF_INET:
410712e78baShshoexer 		((struct sockaddr_in *)&ssrc)->sin_addr = src->address.v4;
411f032086dShshoexer 		ssrc.ss_len = sizeof(struct sockaddr_in);
412f032086dShshoexer 		break;
413f032086dShshoexer 	case AF_INET6:
4142099bcdfStodd 		((struct sockaddr_in6 *)&ssrc)->sin6_addr = src->address.v6;
4152099bcdfStodd 		ssrc.ss_len = sizeof(struct sockaddr_in6);
4162099bcdfStodd 		break;
417f032086dShshoexer 	default:
418f032086dShshoexer 		warnx("unsupported address family %d", src->af);
419f032086dShshoexer 		return -1;
420f032086dShshoexer 	}
421f032086dShshoexer 
422f032086dShshoexer 	bzero(&sdst, sizeof(sdst));
4232099bcdfStodd 	sdst.ss_family = dst->af;
424f032086dShshoexer 	switch (dst->af) {
425f032086dShshoexer 	case AF_INET:
426712e78baShshoexer 		((struct sockaddr_in *)&sdst)->sin_addr = dst->address.v4;
427f032086dShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in);
428f032086dShshoexer 		break;
429f032086dShshoexer 	case AF_INET6:
4302099bcdfStodd 		((struct sockaddr_in6 *)&sdst)->sin6_addr = dst->address.v6;
4312099bcdfStodd 		sdst.ss_len = sizeof(struct sockaddr_in6);
4322099bcdfStodd 		break;
433f032086dShshoexer 	default:
434f032086dShshoexer 		warnx("unsupported address family %d", dst->af);
435f032086dShshoexer 		return -1;
436f032086dShshoexer 	}
437f032086dShshoexer 
438f032086dShshoexer 	bzero(&smsg, sizeof(smsg));
439f032086dShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
440f032086dShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
441f032086dShshoexer 	smsg.sadb_msg_pid = getpid();
442f032086dShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
443f032086dShshoexer 	smsg.sadb_msg_type = action;
444f032086dShshoexer 	smsg.sadb_msg_satype = satype;
445f032086dShshoexer 
446f032086dShshoexer 	bzero(&sa, sizeof(sa));
447f032086dShshoexer 	sa.sadb_sa_len = sizeof(sa) / 8;
448f032086dShshoexer 	sa.sadb_sa_exttype = SADB_EXT_SA;
449f032086dShshoexer 	sa.sadb_sa_spi = htonl(spi);
450f032086dShshoexer 	sa.sadb_sa_state = SADB_SASTATE_MATURE;
451f032086dShshoexer 
45288a8cceeSmarkus 	if (satype != SADB_X_SATYPE_IPIP && tmode == IPSEC_TUNNEL)
453a38d220fShshoexer 		sa.sadb_sa_flags |= SADB_X_SAFLAGS_TUNNEL;
454a38d220fShshoexer 
455375db29dShshoexer 	if (xfs && xfs->authxf) {
456375db29dShshoexer 		switch (xfs->authxf->id) {
457881e2068Shshoexer 		case AUTHXF_NONE:
458881e2068Shshoexer 			break;
459881e2068Shshoexer 		case AUTHXF_HMAC_MD5:
460881e2068Shshoexer 			sa.sadb_sa_auth = SADB_AALG_MD5HMAC;
461881e2068Shshoexer 			break;
462881e2068Shshoexer 		case AUTHXF_HMAC_RIPEMD160:
463881e2068Shshoexer 			sa.sadb_sa_auth = SADB_X_AALG_RIPEMD160HMAC;
464881e2068Shshoexer 			break;
465881e2068Shshoexer 		case AUTHXF_HMAC_SHA1:
466881e2068Shshoexer 			sa.sadb_sa_auth = SADB_AALG_SHA1HMAC;
467881e2068Shshoexer 			break;
468881e2068Shshoexer 		case AUTHXF_HMAC_SHA2_256:
469881e2068Shshoexer 			sa.sadb_sa_auth = SADB_X_AALG_SHA2_256;
470881e2068Shshoexer 			break;
471881e2068Shshoexer 		case AUTHXF_HMAC_SHA2_384:
472881e2068Shshoexer 			sa.sadb_sa_auth = SADB_X_AALG_SHA2_384;
473881e2068Shshoexer 			break;
474881e2068Shshoexer 		case AUTHXF_HMAC_SHA2_512:
475881e2068Shshoexer 			sa.sadb_sa_auth = SADB_X_AALG_SHA2_512;
476881e2068Shshoexer 			break;
477881e2068Shshoexer 		default:
478881e2068Shshoexer 			warnx("unsupported authentication algorithm %d",
479375db29dShshoexer 			    xfs->authxf->id);
480881e2068Shshoexer 		}
481881e2068Shshoexer 	}
482375db29dShshoexer 	if (xfs && xfs->encxf) {
483375db29dShshoexer 		switch (xfs->encxf->id) {
484881e2068Shshoexer 		case ENCXF_NONE:
485881e2068Shshoexer 			break;
486881e2068Shshoexer 		case ENCXF_3DES_CBC:
487881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_EALG_3DESCBC;
488881e2068Shshoexer 			break;
489881e2068Shshoexer 		case ENCXF_AES:
490783454c1Smikeb 		case ENCXF_AES_128:
491783454c1Smikeb 		case ENCXF_AES_192:
492783454c1Smikeb 		case ENCXF_AES_256:
493881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_AES;
494881e2068Shshoexer 			break;
495881e2068Shshoexer 		case ENCXF_AESCTR:
4965f649d51Snaddy 		case ENCXF_AES_128_CTR:
4975f649d51Snaddy 		case ENCXF_AES_192_CTR:
4985f649d51Snaddy 		case ENCXF_AES_256_CTR:
499881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_AESCTR;
500881e2068Shshoexer 			break;
501783454c1Smikeb 		case ENCXF_AES_128_GCM:
502783454c1Smikeb 		case ENCXF_AES_192_GCM:
503783454c1Smikeb 		case ENCXF_AES_256_GCM:
504783454c1Smikeb 			sa.sadb_sa_encrypt = SADB_X_EALG_AESGCM16;
505783454c1Smikeb 			break;
506783454c1Smikeb 		case ENCXF_AES_128_GMAC:
507783454c1Smikeb 		case ENCXF_AES_192_GMAC:
508783454c1Smikeb 		case ENCXF_AES_256_GMAC:
509783454c1Smikeb 			sa.sadb_sa_encrypt = SADB_X_EALG_AESGMAC;
510783454c1Smikeb 			break;
511881e2068Shshoexer 		case ENCXF_BLOWFISH:
512881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_BLF;
513881e2068Shshoexer 			break;
514881e2068Shshoexer 		case ENCXF_CAST128:
515881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_CAST;
516881e2068Shshoexer 			break;
517881e2068Shshoexer 		case ENCXF_NULL:
518881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_EALG_NULL;
519881e2068Shshoexer 			break;
520881e2068Shshoexer 		default:
521375db29dShshoexer 			warnx("unsupported encryption algorithm %d",
522375db29dShshoexer 			    xfs->encxf->id);
523881e2068Shshoexer 		}
524881e2068Shshoexer 	}
52572e25333Shshoexer 	if (xfs && xfs->compxf) {
52672e25333Shshoexer 		switch (xfs->compxf->id) {
52772e25333Shshoexer 		case COMPXF_DEFLATE:
52872e25333Shshoexer 			sa.sadb_sa_encrypt = SADB_X_CALG_DEFLATE;
52972e25333Shshoexer 			break;
53072e25333Shshoexer 		default:
53172e25333Shshoexer 			warnx("unsupported compression algorithm %d",
53272e25333Shshoexer 			    xfs->compxf->id);
53372e25333Shshoexer 		}
53472e25333Shshoexer 	}
535881e2068Shshoexer 
536f032086dShshoexer 	bzero(&sa_src, sizeof(sa_src));
537f032086dShshoexer 	sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
538f032086dShshoexer 	sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
539f032086dShshoexer 
540f032086dShshoexer 	bzero(&sa_dst, sizeof(sa_dst));
541f032086dShshoexer 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
542f032086dShshoexer 	sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
543f032086dShshoexer 
544a3b4c2abSbluhm 	if (encap) {
545a3b4c2abSbluhm 		sa.sadb_sa_flags |= SADB_X_SAFLAGS_UDPENCAP;
546a3b4c2abSbluhm 		udpencap.sadb_x_udpencap_exttype = SADB_X_EXT_UDPENCAP;
547a3b4c2abSbluhm 		udpencap.sadb_x_udpencap_len = sizeof(udpencap) / 8;
548a3b4c2abSbluhm 		udpencap.sadb_x_udpencap_port = htons(dport);
549a3b4c2abSbluhm 	}
55072e25333Shshoexer 	if (action == SADB_ADD && !authkey && !enckey && satype !=
55188a8cceeSmarkus 	    SADB_X_SATYPE_IPCOMP && satype != SADB_X_SATYPE_IPIP) { /* XXX ENCNULL */
5520813ab45Shshoexer 		warnx("no key specified");
5530813ab45Shshoexer 		return -1;
5540813ab45Shshoexer 	}
555881e2068Shshoexer 	if (authkey) {
556881e2068Shshoexer 		bzero(&sa_authkey, sizeof(sa_authkey));
557881e2068Shshoexer 		sa_authkey.sadb_key_len = (sizeof(sa_authkey) +
558881e2068Shshoexer 		    ((authkey->len + 7) / 8) * 8) / 8;
559881e2068Shshoexer 		sa_authkey.sadb_key_exttype = SADB_EXT_KEY_AUTH;
560881e2068Shshoexer 		sa_authkey.sadb_key_bits = 8 * authkey->len;
561881e2068Shshoexer 	}
562881e2068Shshoexer 	if (enckey) {
563881e2068Shshoexer 		bzero(&sa_enckey, sizeof(sa_enckey));
564881e2068Shshoexer 		sa_enckey.sadb_key_len = (sizeof(sa_enckey) +
565881e2068Shshoexer 		    ((enckey->len + 7) / 8) * 8) / 8;
566881e2068Shshoexer 		sa_enckey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
567881e2068Shshoexer 		sa_enckey.sadb_key_bits = 8 * enckey->len;
568f032086dShshoexer 	}
569f032086dShshoexer 
570f032086dShshoexer 	iov_cnt = 0;
571f032086dShshoexer 
572f032086dShshoexer 	/* header */
573f032086dShshoexer 	iov[iov_cnt].iov_base = &smsg;
574f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
575f032086dShshoexer 	iov_cnt++;
576f032086dShshoexer 
577f032086dShshoexer 	/* sa */
578f032086dShshoexer 	iov[iov_cnt].iov_base = &sa;
579f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(sa);
580f032086dShshoexer 	smsg.sadb_msg_len += sa.sadb_sa_len;
581f032086dShshoexer 	iov_cnt++;
582f032086dShshoexer 
583f032086dShshoexer 	/* src addr */
584f032086dShshoexer 	iov[iov_cnt].iov_base = &sa_src;
585f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_src);
586f032086dShshoexer 	iov_cnt++;
587f032086dShshoexer 	iov[iov_cnt].iov_base = &ssrc;
588f032086dShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len);
589f032086dShshoexer 	smsg.sadb_msg_len += sa_src.sadb_address_len;
590f032086dShshoexer 	iov_cnt++;
591f032086dShshoexer 
592f032086dShshoexer 	/* dst addr */
593f032086dShshoexer 	iov[iov_cnt].iov_base = &sa_dst;
594f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst);
595f032086dShshoexer 	iov_cnt++;
596f032086dShshoexer 	iov[iov_cnt].iov_base = &sdst;
597f032086dShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
598f032086dShshoexer 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
599f032086dShshoexer 	iov_cnt++;
600f032086dShshoexer 
601a3b4c2abSbluhm 	if (encap) {
602a3b4c2abSbluhm 		iov[iov_cnt].iov_base = &udpencap;
603a3b4c2abSbluhm 		iov[iov_cnt].iov_len = sizeof(udpencap);
604a3b4c2abSbluhm 		smsg.sadb_msg_len += udpencap.sadb_x_udpencap_len;
605a3b4c2abSbluhm 		iov_cnt++;
606a3b4c2abSbluhm 	}
607881e2068Shshoexer 	if (authkey) {
608881e2068Shshoexer 		/* authentication key */
609881e2068Shshoexer 		iov[iov_cnt].iov_base = &sa_authkey;
610881e2068Shshoexer 		iov[iov_cnt].iov_len = sizeof(sa_authkey);
611f032086dShshoexer 		iov_cnt++;
612881e2068Shshoexer 		iov[iov_cnt].iov_base = authkey->data;
613881e2068Shshoexer 		iov[iov_cnt].iov_len = ((authkey->len + 7) / 8) * 8;
614881e2068Shshoexer 		smsg.sadb_msg_len += sa_authkey.sadb_key_len;
615881e2068Shshoexer 		iov_cnt++;
616881e2068Shshoexer 	}
617881e2068Shshoexer 	if (enckey) {
618881e2068Shshoexer 		/* encryption key */
619881e2068Shshoexer 		iov[iov_cnt].iov_base = &sa_enckey;
620881e2068Shshoexer 		iov[iov_cnt].iov_len = sizeof(sa_enckey);
621881e2068Shshoexer 		iov_cnt++;
622881e2068Shshoexer 		iov[iov_cnt].iov_base = enckey->data;
623881e2068Shshoexer 		iov[iov_cnt].iov_len = ((enckey->len + 7) / 8) * 8;
624881e2068Shshoexer 		smsg.sadb_msg_len += sa_enckey.sadb_key_len;
625f032086dShshoexer 		iov_cnt++;
626f032086dShshoexer 	}
627f032086dShshoexer 
628f032086dShshoexer 	len = smsg.sadb_msg_len * 8;
629f032086dShshoexer 	if ((n = writev(sd, iov, iov_cnt)) == -1) {
630f032086dShshoexer 		warn("writev failed");
631f032086dShshoexer 		ret = -1;
632f032086dShshoexer 	} else if (n != len) {
633f032086dShshoexer 		warnx("short write");
634f032086dShshoexer 		ret = -1;
635f032086dShshoexer 	}
636f032086dShshoexer 
637f032086dShshoexer 	return ret;
638f032086dShshoexer }
639f032086dShshoexer 
640f032086dShshoexer static int
pfkey_sabundle(int sd,u_int8_t satype,u_int8_t satype2,u_int8_t action,struct ipsec_addr_wrap * dst,u_int32_t spi,struct ipsec_addr_wrap * dst2,u_int32_t spi2)641a6bcba92Sbluhm pfkey_sabundle(int sd, u_int8_t satype, u_int8_t satype2, u_int8_t action,
6428065703bShshoexer     struct ipsec_addr_wrap *dst, u_int32_t spi, struct ipsec_addr_wrap *dst2,
6438065703bShshoexer     u_int32_t spi2)
6448f2109caShshoexer {
6458f2109caShshoexer 	struct sadb_msg		smsg;
6468f2109caShshoexer 	struct sadb_sa		sa1, sa2;
6478f2109caShshoexer 	struct sadb_address	sa_dst, sa_dst2;
6488f2109caShshoexer 	struct sockaddr_storage	sdst, sdst2;
6498f2109caShshoexer 	struct sadb_protocol	sa_proto;
6508f2109caShshoexer 	struct iovec		iov[IOV_CNT];
6518f2109caShshoexer 	ssize_t			n;
6528f2109caShshoexer 	int			iov_cnt, len, ret = 0;
6538f2109caShshoexer 
6548f2109caShshoexer 	bzero(&sdst, sizeof(sdst));
6558f2109caShshoexer 	sdst.ss_family = dst->af;
6568f2109caShshoexer 	switch (dst->af) {
6578f2109caShshoexer 	case AF_INET:
6588f2109caShshoexer 		((struct sockaddr_in *)&sdst)->sin_addr = dst->address.v4;
6598f2109caShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in);
6608f2109caShshoexer 		break;
6618f2109caShshoexer 	case AF_INET6:
6628f2109caShshoexer 		((struct sockaddr_in6 *)&sdst)->sin6_addr = dst->address.v6;
6638f2109caShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in6);
6648f2109caShshoexer 		break;
6658f2109caShshoexer 	default:
6668f2109caShshoexer 		warnx("unsupported address family %d", dst->af);
6678f2109caShshoexer 		return -1;
6688f2109caShshoexer 	}
6698f2109caShshoexer 
6708f2109caShshoexer 	bzero(&sdst2, sizeof(sdst2));
6718f2109caShshoexer 	sdst2.ss_family = dst2->af;
6728f2109caShshoexer 	switch (dst2->af) {
6738f2109caShshoexer 	case AF_INET:
6748f2109caShshoexer 		((struct sockaddr_in *)&sdst2)->sin_addr = dst2->address.v4;
6758f2109caShshoexer 		sdst2.ss_len = sizeof(struct sockaddr_in);
6768f2109caShshoexer 		break;
6778f2109caShshoexer 	case AF_INET6:
6788f2109caShshoexer 		((struct sockaddr_in6 *)&sdst2)->sin6_addr = dst2->address.v6;
6798f2109caShshoexer 		sdst2.ss_len = sizeof(struct sockaddr_in6);
6808f2109caShshoexer 		break;
6818f2109caShshoexer 	default:
6828f2109caShshoexer 		warnx("unsupported address family %d", dst2->af);
6838f2109caShshoexer 		return -1;
6848f2109caShshoexer 	}
6858f2109caShshoexer 
6868f2109caShshoexer 	bzero(&smsg, sizeof(smsg));
6878f2109caShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
6888f2109caShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
6898f2109caShshoexer 	smsg.sadb_msg_pid = getpid();
6908f2109caShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
6918f2109caShshoexer 	smsg.sadb_msg_type = action;
6928f2109caShshoexer 	smsg.sadb_msg_satype = satype;
6938f2109caShshoexer 
6948f2109caShshoexer 	bzero(&sa1, sizeof(sa1));
6958f2109caShshoexer 	sa1.sadb_sa_len = sizeof(sa1) / 8;
6968f2109caShshoexer 	sa1.sadb_sa_exttype = SADB_EXT_SA;
6978f2109caShshoexer 	sa1.sadb_sa_spi = htonl(spi);
6988f2109caShshoexer 	sa1.sadb_sa_state = SADB_SASTATE_MATURE;
6998f2109caShshoexer 
7008f2109caShshoexer 	bzero(&sa2, sizeof(sa2));
7018f2109caShshoexer 	sa2.sadb_sa_len = sizeof(sa2) / 8;
7028f2109caShshoexer 	sa2.sadb_sa_exttype = SADB_X_EXT_SA2;
7038f2109caShshoexer 	sa2.sadb_sa_spi = htonl(spi2);
7048f2109caShshoexer 	sa2.sadb_sa_state = SADB_SASTATE_MATURE;
7058f2109caShshoexer 	iov_cnt = 0;
7068f2109caShshoexer 
7078f2109caShshoexer 	bzero(&sa_dst, sizeof(sa_dst));
7088f2109caShshoexer 	sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
7098f2109caShshoexer 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
7108f2109caShshoexer 
7118f2109caShshoexer 	bzero(&sa_dst2, sizeof(sa_dst2));
7128f2109caShshoexer 	sa_dst2.sadb_address_exttype = SADB_X_EXT_DST2;
7138f2109caShshoexer 	sa_dst2.sadb_address_len = (sizeof(sa_dst2) + ROUNDUP(sdst2.ss_len)) / 8;
7148f2109caShshoexer 
7158f2109caShshoexer 	bzero(&sa_proto, sizeof(sa_proto));
716d44c51a1Sbluhm 	sa_proto.sadb_protocol_exttype = SADB_X_EXT_SATYPE2;
7178f2109caShshoexer 	sa_proto.sadb_protocol_len = sizeof(sa_proto) / 8;
7188f2109caShshoexer 	sa_proto.sadb_protocol_direction = 0;
7198065703bShshoexer 	sa_proto.sadb_protocol_proto = satype2;
7208f2109caShshoexer 
7218f2109caShshoexer 	/* header */
7228f2109caShshoexer 	iov[iov_cnt].iov_base = &smsg;
7238f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
7248f2109caShshoexer 	iov_cnt++;
7258f2109caShshoexer 
7268f2109caShshoexer 	/* sa */
7278f2109caShshoexer 	iov[iov_cnt].iov_base = &sa1;
7288f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa1);
7298f2109caShshoexer 	smsg.sadb_msg_len += sa1.sadb_sa_len;
7308f2109caShshoexer 	iov_cnt++;
7318f2109caShshoexer 
7328f2109caShshoexer 	/* dst addr */
7338f2109caShshoexer 	iov[iov_cnt].iov_base = &sa_dst;
7348f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst);
7358f2109caShshoexer 	iov_cnt++;
7368f2109caShshoexer 	iov[iov_cnt].iov_base = &sdst;
7378f2109caShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
7388f2109caShshoexer 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
7398f2109caShshoexer 	iov_cnt++;
7408f2109caShshoexer 
7418f2109caShshoexer 	/* second sa */
7428f2109caShshoexer 	iov[iov_cnt].iov_base = &sa2;
7438f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa2);
7448f2109caShshoexer 	smsg.sadb_msg_len += sa2.sadb_sa_len;
7458f2109caShshoexer 	iov_cnt++;
7468f2109caShshoexer 
7478f2109caShshoexer 	/* second dst addr */
7488f2109caShshoexer 	iov[iov_cnt].iov_base = &sa_dst2;
7498f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst2);
7508f2109caShshoexer 	iov_cnt++;
7518f2109caShshoexer 	iov[iov_cnt].iov_base = &sdst2;
7528f2109caShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst2.ss_len);
7538f2109caShshoexer 	smsg.sadb_msg_len += sa_dst2.sadb_address_len;
7548f2109caShshoexer 	iov_cnt++;
7558f2109caShshoexer 
7568f2109caShshoexer 	/* SA type */
7578f2109caShshoexer 	iov[iov_cnt].iov_base = &sa_proto;
7588f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_proto);
7598f2109caShshoexer 	smsg.sadb_msg_len += sa_proto.sadb_protocol_len;
7608f2109caShshoexer 	iov_cnt++;
7618f2109caShshoexer 
7628f2109caShshoexer 	len = smsg.sadb_msg_len * 8;
7638f2109caShshoexer 	if ((n = writev(sd, iov, iov_cnt)) == -1) {
7648f2109caShshoexer 		warn("writev failed");
7658f2109caShshoexer 		ret = -1;
7668f2109caShshoexer 	} else if (n != len) {
7678f2109caShshoexer 		warnx("short write");
7688f2109caShshoexer 		ret = -1;
7698f2109caShshoexer 	}
7708f2109caShshoexer 
7718f2109caShshoexer 	return (ret);
7728f2109caShshoexer }
7738f2109caShshoexer 
7748f2109caShshoexer static int
pfkey_reply(int sd,u_int8_t ** datap,ssize_t * lenp)7758a87fca6Smsf pfkey_reply(int sd, u_int8_t **datap, ssize_t *lenp)
776f484f2cfShshoexer {
777f484f2cfShshoexer 	struct sadb_msg	 hdr;
778f484f2cfShshoexer 	ssize_t		 len;
779f484f2cfShshoexer 	u_int8_t	*data;
780f484f2cfShshoexer 
781f484f2cfShshoexer 	if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
782f484f2cfShshoexer 		warnx("short read");
783f484f2cfShshoexer 		return -1;
784f484f2cfShshoexer 	}
785f484f2cfShshoexer 	len = hdr.sadb_msg_len * PFKEYV2_CHUNK;
786f484f2cfShshoexer 	if ((data = malloc(len)) == NULL)
787bd828a90Shshoexer 		err(1, "pfkey_reply: malloc");
788f484f2cfShshoexer 	if (read(sd, data, len) != len) {
789f484f2cfShshoexer 		warn("PF_KEY short read");
7901e82d711Sderaadt 		freezero(data, len);
791f484f2cfShshoexer 		return -1;
792f484f2cfShshoexer 	}
7938a87fca6Smsf 	if (datap) {
7948a87fca6Smsf 		*datap = data;
7958a87fca6Smsf 		if (lenp)
7968a87fca6Smsf 			*lenp = len;
7978a87fca6Smsf 	} else {
7981e82d711Sderaadt 		freezero(data, len);
7998a87fca6Smsf 	}
800011122f7Smarkus 	if (datap == NULL && hdr.sadb_msg_errno != 0) {
801011122f7Smarkus 		errno = hdr.sadb_msg_errno;
80292270c32Shshoexer 		if (errno != EEXIST) {
803011122f7Smarkus 			warn("PF_KEY failed");
804011122f7Smarkus 			return -1;
805011122f7Smarkus 		}
80692270c32Shshoexer 	}
807f484f2cfShshoexer 	return 0;
808f484f2cfShshoexer }
809f484f2cfShshoexer 
810f484f2cfShshoexer int
pfkey_parse(struct sadb_msg * msg,struct ipsec_rule * rule)8111edc1b9aShshoexer pfkey_parse(struct sadb_msg *msg, struct ipsec_rule *rule)
8121edc1b9aShshoexer {
8131edc1b9aShshoexer 	struct sadb_ext		*ext;
8141edc1b9aShshoexer 	struct sadb_address	*saddr;
8151edc1b9aShshoexer 	struct sadb_protocol	*sproto;
8161edc1b9aShshoexer 	struct sadb_ident	*sident;
8171edc1b9aShshoexer 	struct sockaddr		*sa;
818712e78baShshoexer 	struct sockaddr_in	*sa_in;
8192099bcdfStodd 	struct sockaddr_in6	*sa_in6;
8201edc1b9aShshoexer 	int			 len;
8211edc1b9aShshoexer 
8221edc1b9aShshoexer 	switch (msg->sadb_msg_satype) {
823b26a6cb5Shshoexer 	case SADB_SATYPE_ESP:
8249182219dSmarkus 		rule->satype = IPSEC_ESP;
8251edc1b9aShshoexer 		break;
826b26a6cb5Shshoexer 	case SADB_SATYPE_AH:
8279182219dSmarkus 		rule->satype = IPSEC_AH;
8281edc1b9aShshoexer 		break;
829b26a6cb5Shshoexer 	case SADB_X_SATYPE_IPCOMP:
8309182219dSmarkus 		rule->satype = IPSEC_IPCOMP;
831a29da9d0Shshoexer 		break;
83288a8cceeSmarkus 	case SADB_X_SATYPE_IPIP:
8339182219dSmarkus 		rule->satype = IPSEC_IPIP;
83488a8cceeSmarkus 		break;
8351edc1b9aShshoexer 	default:
8361edc1b9aShshoexer 		return (1);
8371edc1b9aShshoexer 	}
8381edc1b9aShshoexer 
8391edc1b9aShshoexer 	for (ext = (struct sadb_ext *)(msg + 1);
8401edc1b9aShshoexer 	    (size_t)((u_int8_t *)ext - (u_int8_t *)msg) <
841d5d1799eShshoexer 	    msg->sadb_msg_len * PFKEYV2_CHUNK && ext->sadb_ext_len > 0;
8421edc1b9aShshoexer 	    ext = (struct sadb_ext *)((u_int8_t *)ext +
8431edc1b9aShshoexer 	    ext->sadb_ext_len * PFKEYV2_CHUNK)) {
8441edc1b9aShshoexer 		switch (ext->sadb_ext_type) {
8451edc1b9aShshoexer 		case SADB_EXT_ADDRESS_SRC:
8461edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
8471edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
8481edc1b9aShshoexer 
84991f765ddShshoexer 			rule->local = calloc(1, sizeof(struct ipsec_addr_wrap));
850d5d1799eShshoexer 			if (rule->local == NULL)
851a1059f6aStodd 				err(1, "pfkey_parse: calloc");
8521edc1b9aShshoexer 
8532099bcdfStodd 			rule->local->af = sa->sa_family;
8541edc1b9aShshoexer 			switch (sa->sa_family) {
8551edc1b9aShshoexer 			case AF_INET:
8561edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
85791f765ddShshoexer 				    &rule->local->address.v4,
858712e78baShshoexer 				    sizeof(struct in_addr));
8592099bcdfStodd 				set_ipmask(rule->local, 32);
8602099bcdfStodd 				break;
8612099bcdfStodd 			case AF_INET6:
8622099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
8632099bcdfStodd 				    &rule->local->address.v6,
8642099bcdfStodd 				    sizeof(struct in6_addr));
8652099bcdfStodd 				set_ipmask(rule->local, 128);
8661edc1b9aShshoexer 				break;
8671edc1b9aShshoexer 			default:
8681edc1b9aShshoexer 				return (1);
8691edc1b9aShshoexer 			}
8701edc1b9aShshoexer 			break;
8711edc1b9aShshoexer 
8721edc1b9aShshoexer 
8731edc1b9aShshoexer 		case SADB_EXT_ADDRESS_DST:
8741edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
8751edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
8761edc1b9aShshoexer 
87791f765ddShshoexer 			rule->peer = calloc(1, sizeof(struct ipsec_addr_wrap));
8781edc1b9aShshoexer 			if (rule->peer == NULL)
879a1059f6aStodd 				err(1, "pfkey_parse: calloc");
8801edc1b9aShshoexer 
8812099bcdfStodd 			rule->peer->af = sa->sa_family;
8821edc1b9aShshoexer 			switch (sa->sa_family) {
8831edc1b9aShshoexer 			case AF_INET:
8841edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
885712e78baShshoexer 				    &rule->peer->address.v4,
886712e78baShshoexer 				    sizeof(struct in_addr));
8872099bcdfStodd 				set_ipmask(rule->peer, 32);
8882099bcdfStodd 				break;
8892099bcdfStodd 			case AF_INET6:
8902099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
8912099bcdfStodd 				    &rule->peer->address.v6,
8922099bcdfStodd 				    sizeof(struct in6_addr));
8932099bcdfStodd 				set_ipmask(rule->peer, 128);
8941edc1b9aShshoexer 				break;
8951edc1b9aShshoexer 			default:
8961edc1b9aShshoexer 				return (1);
8971edc1b9aShshoexer 			}
8981edc1b9aShshoexer 			break;
8991edc1b9aShshoexer 
9001edc1b9aShshoexer 		case SADB_EXT_IDENTITY_SRC:
9011edc1b9aShshoexer 			sident = (struct sadb_ident *)ext;
9021edc1b9aShshoexer 			len = (sident->sadb_ident_len * sizeof(uint64_t)) -
9031edc1b9aShshoexer 			    sizeof(struct sadb_ident);
9041edc1b9aShshoexer 
9059382ed8fShshoexer 			if (rule->auth == NULL) {
9069382ed8fShshoexer 				rule->auth = calloc(1, sizeof(struct
9079382ed8fShshoexer 				    ipsec_auth));
9089382ed8fShshoexer 				if (rule->auth == NULL)
909bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
9109382ed8fShshoexer 			}
9119382ed8fShshoexer 
912abe65127Shshoexer 			rule->auth->srcid = calloc(1, len);
913abe65127Shshoexer 			if (rule->auth->srcid == NULL)
914bd828a90Shshoexer 				err(1, "pfkey_parse: calloc");
9151edc1b9aShshoexer 
916abe65127Shshoexer 			strlcpy(rule->auth->srcid, (char *)(sident + 1), len);
9171edc1b9aShshoexer 			break;
9181edc1b9aShshoexer 
9191edc1b9aShshoexer 		case SADB_EXT_IDENTITY_DST:
9201edc1b9aShshoexer 			sident = (struct sadb_ident *)ext;
9211edc1b9aShshoexer 			len = (sident->sadb_ident_len * sizeof(uint64_t)) -
9221edc1b9aShshoexer 			    sizeof(struct sadb_ident);
9231edc1b9aShshoexer 
9249382ed8fShshoexer 			if (rule->auth == NULL) {
9259382ed8fShshoexer 				rule->auth = calloc(1, sizeof(struct
9269382ed8fShshoexer 				    ipsec_auth));
9279382ed8fShshoexer 				if (rule->auth == NULL)
928bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
9299382ed8fShshoexer 			}
9309382ed8fShshoexer 
931abe65127Shshoexer 			rule->auth->dstid = calloc(1, len);
932abe65127Shshoexer 			if (rule->auth->dstid == NULL)
933bd828a90Shshoexer 				err(1, "pfkey_parse: calloc");
9341edc1b9aShshoexer 
935abe65127Shshoexer 			strlcpy(rule->auth->dstid, (char *)(sident + 1), len);
9361edc1b9aShshoexer 			break;
9371edc1b9aShshoexer 
9381edc1b9aShshoexer 		case SADB_X_EXT_PROTOCOL:
9399182219dSmarkus 			sproto = (struct sadb_protocol *)ext;
9409182219dSmarkus 			if (sproto->sadb_protocol_direction == 0)
9419182219dSmarkus 				rule->proto = sproto->sadb_protocol_proto;
9421edc1b9aShshoexer 			break;
9431edc1b9aShshoexer 
9441edc1b9aShshoexer 		case SADB_X_EXT_FLOW_TYPE:
9451edc1b9aShshoexer 			sproto = (struct sadb_protocol *)ext;
9461edc1b9aShshoexer 
9471edc1b9aShshoexer 			switch (sproto->sadb_protocol_direction) {
9481edc1b9aShshoexer 			case IPSP_DIRECTION_IN:
9491edc1b9aShshoexer 				rule->direction = IPSEC_IN;
9501edc1b9aShshoexer 				break;
9511edc1b9aShshoexer 			case IPSP_DIRECTION_OUT:
9521edc1b9aShshoexer 				rule->direction = IPSEC_OUT;
9531edc1b9aShshoexer 				break;
9541edc1b9aShshoexer 			default:
9551edc1b9aShshoexer 				return (1);
9561edc1b9aShshoexer 			}
9576122c05eShshoexer 			switch (sproto->sadb_protocol_proto) {
9586122c05eShshoexer 			case SADB_X_FLOW_TYPE_USE:
9591a3f035aShshoexer 				rule->flowtype = TYPE_USE;
9606122c05eShshoexer 				break;
9616122c05eShshoexer 			case SADB_X_FLOW_TYPE_ACQUIRE:
9621a3f035aShshoexer 				rule->flowtype = TYPE_ACQUIRE;
9636122c05eShshoexer 				break;
9646122c05eShshoexer 			case SADB_X_FLOW_TYPE_REQUIRE:
9651a3f035aShshoexer 				rule->flowtype = TYPE_REQUIRE;
9666122c05eShshoexer 				break;
9676122c05eShshoexer 			case SADB_X_FLOW_TYPE_DENY:
9681a3f035aShshoexer 				rule->flowtype = TYPE_DENY;
9696122c05eShshoexer 				break;
9706122c05eShshoexer 			case SADB_X_FLOW_TYPE_BYPASS:
9711a3f035aShshoexer 				rule->flowtype = TYPE_BYPASS;
9726122c05eShshoexer 				break;
9736122c05eShshoexer 			case SADB_X_FLOW_TYPE_DONTACQ:
9741a3f035aShshoexer 				rule->flowtype = TYPE_DONTACQ;
9756122c05eShshoexer 				break;
9766122c05eShshoexer 			default:
9771a3f035aShshoexer 				rule->flowtype = TYPE_UNKNOWN;
9786122c05eShshoexer 				break;
9796122c05eShshoexer 			}
9801edc1b9aShshoexer 			break;
9811edc1b9aShshoexer 
9821edc1b9aShshoexer 		case SADB_X_EXT_SRC_FLOW:
9831edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
9841edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
9851edc1b9aShshoexer 
9861edc1b9aShshoexer 			if (rule->src == NULL) {
9871edc1b9aShshoexer 				rule->src = calloc(1,
98891f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
9891edc1b9aShshoexer 				if (rule->src == NULL)
990bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
9911edc1b9aShshoexer 			}
9921edc1b9aShshoexer 
9932099bcdfStodd 			rule->src->af = sa->sa_family;
9941edc1b9aShshoexer 			switch (sa->sa_family) {
9951edc1b9aShshoexer 			case AF_INET:
9961edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
997712e78baShshoexer 				    &rule->src->address.v4,
998712e78baShshoexer 				    sizeof(struct in_addr));
99957f58d0dSnaddy 				rule->sport =
100057f58d0dSnaddy 				    ((struct sockaddr_in *)sa)->sin_port;
10012099bcdfStodd 				break;
10022099bcdfStodd 			case AF_INET6:
10032099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
10042099bcdfStodd 				    &rule->src->address.v6,
10052099bcdfStodd 				    sizeof(struct in6_addr));
100657f58d0dSnaddy 				rule->sport =
100757f58d0dSnaddy 				    ((struct sockaddr_in6 *)sa)->sin6_port;
10081edc1b9aShshoexer 				break;
10091edc1b9aShshoexer 			default:
10101edc1b9aShshoexer 				return (1);
10111edc1b9aShshoexer 			}
10121edc1b9aShshoexer 			break;
10131edc1b9aShshoexer 
10141edc1b9aShshoexer 		case SADB_X_EXT_DST_FLOW:
10151edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
10161edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
10171edc1b9aShshoexer 
10181edc1b9aShshoexer 			if (rule->dst == NULL) {
10191edc1b9aShshoexer 				rule->dst = calloc(1,
102091f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
10211edc1b9aShshoexer 				if (rule->dst == NULL)
1022bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
10231edc1b9aShshoexer 			}
10241edc1b9aShshoexer 
10252099bcdfStodd 			rule->dst->af = sa->sa_family;
10261edc1b9aShshoexer 			switch (sa->sa_family) {
10271edc1b9aShshoexer 			case AF_INET:
10281edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
1029712e78baShshoexer 				    &rule->dst->address.v4,
1030712e78baShshoexer 				    sizeof(struct in_addr));
103157f58d0dSnaddy 				rule->dport =
103257f58d0dSnaddy 				    ((struct sockaddr_in *)sa)->sin_port;
10331edc1b9aShshoexer 				break;
10342099bcdfStodd 			case AF_INET6:
10352099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
10362099bcdfStodd 				    &rule->dst->address.v6,
10372099bcdfStodd 				    sizeof(struct in6_addr));
103857f58d0dSnaddy 				rule->dport =
103957f58d0dSnaddy 				    ((struct sockaddr_in6 *)sa)->sin6_port;
10402099bcdfStodd 				break;
10411edc1b9aShshoexer 			default:
10421edc1b9aShshoexer 				return (1);
10431edc1b9aShshoexer 			}
10441edc1b9aShshoexer 			break;
10451edc1b9aShshoexer 
10461edc1b9aShshoexer 
10471edc1b9aShshoexer 		case SADB_X_EXT_SRC_MASK:
10481edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
10491edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
10501edc1b9aShshoexer 
10511edc1b9aShshoexer 			if (rule->src == NULL) {
10521edc1b9aShshoexer 				rule->src = calloc(1,
105391f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
10541edc1b9aShshoexer 				if (rule->src == NULL)
1055bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
10561edc1b9aShshoexer 			}
10571edc1b9aShshoexer 
10582099bcdfStodd 			rule->src->af = sa->sa_family;
10591edc1b9aShshoexer 			switch (sa->sa_family) {
10601edc1b9aShshoexer 			case AF_INET:
1061712e78baShshoexer 				sa_in = (struct sockaddr_in *)sa;
1062712e78baShshoexer 				bcopy(&sa_in->sin_addr, &rule->src->mask.v4,
10631edc1b9aShshoexer 				    sizeof(struct in_addr));
10642099bcdfStodd 				break;
10652099bcdfStodd 			case AF_INET6:
10662099bcdfStodd 				sa_in6 = (struct sockaddr_in6 *)sa;
10672099bcdfStodd 				bcopy(&sa_in6->sin6_addr, &rule->src->mask.v6,
10682099bcdfStodd 				    sizeof(struct in6_addr));
10691edc1b9aShshoexer 				break;
10701edc1b9aShshoexer 
10711edc1b9aShshoexer 			default:
10721edc1b9aShshoexer 				return (1);
10731edc1b9aShshoexer 			}
10741edc1b9aShshoexer 			break;
10751edc1b9aShshoexer 
10761edc1b9aShshoexer 		case SADB_X_EXT_DST_MASK:
10771edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
10781edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
10791edc1b9aShshoexer 
10801edc1b9aShshoexer 			if (rule->dst == NULL) {
10811edc1b9aShshoexer 				rule->dst = calloc(1,
108291f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
10831edc1b9aShshoexer 				if (rule->dst == NULL)
1084bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
10851edc1b9aShshoexer 			}
10861edc1b9aShshoexer 
10872099bcdfStodd 			rule->dst->af = sa->sa_family;
10881edc1b9aShshoexer 			switch (sa->sa_family) {
10891edc1b9aShshoexer 			case AF_INET:
1090712e78baShshoexer 				sa_in = (struct sockaddr_in *)sa;
1091712e78baShshoexer 				bcopy(&sa_in->sin_addr, &rule->dst->mask.v4,
10921edc1b9aShshoexer 				    sizeof(struct in_addr));
10931edc1b9aShshoexer 				break;
10942099bcdfStodd 			case AF_INET6:
10952099bcdfStodd 				sa_in6 = (struct sockaddr_in6 *)sa;
10962099bcdfStodd 				bcopy(&sa_in6->sin6_addr, &rule->dst->mask.v6,
10972099bcdfStodd 				    sizeof(struct in6_addr));
10982099bcdfStodd 				break;
10991edc1b9aShshoexer 			default:
11001edc1b9aShshoexer 				return (1);
11011edc1b9aShshoexer 			}
11021edc1b9aShshoexer 			break;
11031edc1b9aShshoexer 
11041edc1b9aShshoexer 		default:
11051edc1b9aShshoexer 			return (1);
11061edc1b9aShshoexer 		}
11071edc1b9aShshoexer 	}
11081edc1b9aShshoexer 
11091edc1b9aShshoexer 	return (0);
11101edc1b9aShshoexer }
11111edc1b9aShshoexer 
11121edc1b9aShshoexer int
pfkey_ipsec_establish(int action,struct ipsec_rule * r)1113356121f6Shshoexer pfkey_ipsec_establish(int action, struct ipsec_rule *r)
1114f484f2cfShshoexer {
111522a29ad6Shshoexer 	int		ret;
11168065703bShshoexer 	u_int8_t	satype, satype2, direction;
1117f484f2cfShshoexer 
1118f032086dShshoexer 	if (r->type == RULE_FLOW) {
11199182219dSmarkus 		switch (r->satype) {
1120f484f2cfShshoexer 		case IPSEC_ESP:
1121f484f2cfShshoexer 			satype = SADB_SATYPE_ESP;
1122f484f2cfShshoexer 			break;
1123f484f2cfShshoexer 		case IPSEC_AH:
1124f484f2cfShshoexer 			satype = SADB_SATYPE_AH;
1125f484f2cfShshoexer 			break;
112672e25333Shshoexer 		case IPSEC_IPCOMP:
112772e25333Shshoexer 			satype = SADB_X_SATYPE_IPCOMP;
112872e25333Shshoexer 			break;
112988a8cceeSmarkus 		case IPSEC_IPIP:
113088a8cceeSmarkus 			satype = SADB_X_SATYPE_IPIP;
113188a8cceeSmarkus 			break;
1132f484f2cfShshoexer 		default:
1133f484f2cfShshoexer 			return -1;
1134f484f2cfShshoexer 		}
1135f484f2cfShshoexer 
1136f484f2cfShshoexer 		switch (r->direction) {
1137f484f2cfShshoexer 		case IPSEC_IN:
1138f484f2cfShshoexer 			direction = IPSP_DIRECTION_IN;
1139f484f2cfShshoexer 			break;
1140f484f2cfShshoexer 		case IPSEC_OUT:
1141f484f2cfShshoexer 			direction = IPSP_DIRECTION_OUT;
1142f484f2cfShshoexer 			break;
1143f484f2cfShshoexer 		default:
1144f484f2cfShshoexer 			return -1;
1145f484f2cfShshoexer 		}
1146f484f2cfShshoexer 
114722a29ad6Shshoexer 		switch (action) {
114890bd57a7Shshoexer 		case ACTION_ADD:
1149f032086dShshoexer 			ret = pfkey_flow(fd, satype, SADB_X_ADDFLOW, direction,
115057f58d0dSnaddy 			    r->proto, r->src, r->sport, r->dst, r->dport,
115157f58d0dSnaddy 			    r->local, r->peer, r->auth, r->flowtype);
115222a29ad6Shshoexer 			break;
115390bd57a7Shshoexer 		case ACTION_DELETE:
115422a29ad6Shshoexer 			/* No peer for flow deletion. */
1155f032086dShshoexer 			ret = pfkey_flow(fd, satype, SADB_X_DELFLOW, direction,
115657f58d0dSnaddy 			    r->proto, r->src, r->sport, r->dst, r->dport,
115757f58d0dSnaddy 			    NULL, NULL, NULL, r->flowtype);
115822a29ad6Shshoexer 			break;
115922a29ad6Shshoexer 		default:
116022a29ad6Shshoexer 			return -1;
116122a29ad6Shshoexer 		}
1162f032086dShshoexer 	} else if (r->type == RULE_SA) {
11639182219dSmarkus 		switch (r->satype) {
1164881e2068Shshoexer 		case IPSEC_AH:
1165881e2068Shshoexer 			satype = SADB_SATYPE_AH;
1166881e2068Shshoexer 			break;
1167881e2068Shshoexer 		case IPSEC_ESP:
1168881e2068Shshoexer 			satype = SADB_SATYPE_ESP;
1169881e2068Shshoexer 			break;
117072e25333Shshoexer 		case IPSEC_IPCOMP:
117172e25333Shshoexer 			satype = SADB_X_SATYPE_IPCOMP;
117272e25333Shshoexer 			break;
1173381a2422Shshoexer 		case IPSEC_TCPMD5:
1174f032086dShshoexer 			satype = SADB_X_SATYPE_TCPSIGNATURE;
1175381a2422Shshoexer 			break;
117688a8cceeSmarkus 		case IPSEC_IPIP:
117788a8cceeSmarkus 			satype = SADB_X_SATYPE_IPIP;
117888a8cceeSmarkus 			break;
1179381a2422Shshoexer 		default:
1180381a2422Shshoexer 			return -1;
1181381a2422Shshoexer 		}
1182f032086dShshoexer 		switch (action) {
118390bd57a7Shshoexer 		case ACTION_ADD:
1184f032086dShshoexer 			ret = pfkey_sa(fd, satype, SADB_ADD, r->spi,
1185a3b4c2abSbluhm 			    r->src, r->dst, r->udpencap, r->udpdport,
1186a3b4c2abSbluhm 			    r->xfs, r->authkey, r->enckey, r->tmode);
1187f032086dShshoexer 			break;
118890bd57a7Shshoexer 		case ACTION_DELETE:
1189f032086dShshoexer 			ret = pfkey_sa(fd, satype, SADB_DELETE, r->spi,
1190a3b4c2abSbluhm 			    r->src, r->dst, 0, 0, r->xfs, NULL, NULL, r->tmode);
1191f032086dShshoexer 			break;
1192f032086dShshoexer 		default:
1193f032086dShshoexer 			return -1;
1194f032086dShshoexer 		}
1195a6bcba92Sbluhm 	} else if (r->type == RULE_BUNDLE) {
11968f2109caShshoexer 		switch (r->satype) {
11978f2109caShshoexer 		case IPSEC_AH:
11988f2109caShshoexer 			satype = SADB_SATYPE_AH;
11998f2109caShshoexer 			break;
12008f2109caShshoexer 		case IPSEC_ESP:
12018f2109caShshoexer 			satype = SADB_SATYPE_ESP;
12028f2109caShshoexer 			break;
12038f2109caShshoexer 		case IPSEC_IPCOMP:
12048f2109caShshoexer 			satype = SADB_X_SATYPE_IPCOMP;
12058f2109caShshoexer 			break;
12068f2109caShshoexer 		case IPSEC_TCPMD5:
12078f2109caShshoexer 			satype = SADB_X_SATYPE_TCPSIGNATURE;
12088f2109caShshoexer 			break;
12098f2109caShshoexer 		case IPSEC_IPIP:
12108f2109caShshoexer 			satype = SADB_X_SATYPE_IPIP;
12118f2109caShshoexer 			break;
12128f2109caShshoexer 		default:
12138f2109caShshoexer 			return -1;
12148f2109caShshoexer 		}
12158065703bShshoexer 		switch (r->proto2) {
12168065703bShshoexer 		case IPSEC_AH:
12178065703bShshoexer 			satype2 = SADB_SATYPE_AH;
12188065703bShshoexer 			break;
12198065703bShshoexer 		case IPSEC_ESP:
12208065703bShshoexer 			satype2 = SADB_SATYPE_ESP;
12218065703bShshoexer 			break;
12228065703bShshoexer 		case IPSEC_IPCOMP:
12238065703bShshoexer 			satype2 = SADB_X_SATYPE_IPCOMP;
12248065703bShshoexer 			break;
12258065703bShshoexer 		case IPSEC_TCPMD5:
12268065703bShshoexer 			satype2 = SADB_X_SATYPE_TCPSIGNATURE;
12278065703bShshoexer 			break;
12288065703bShshoexer 		case IPSEC_IPIP:
12298065703bShshoexer 			satype2 = SADB_X_SATYPE_IPIP;
12308065703bShshoexer 			break;
12318065703bShshoexer 		default:
12328065703bShshoexer 			return -1;
12338065703bShshoexer 		}
12348f2109caShshoexer 		switch (action) {
12358f2109caShshoexer 		case ACTION_ADD:
1236a6bcba92Sbluhm 			ret = pfkey_sabundle(fd, satype, satype2,
12378065703bShshoexer 			    SADB_X_GRPSPIS, r->dst, r->spi, r->dst2, r->spi2);
12388f2109caShshoexer 			break;
12398f2109caShshoexer 		case ACTION_DELETE:
12408f2109caShshoexer 			return 0;
12418f2109caShshoexer 		default:
12428f2109caShshoexer 			return -1;
12438f2109caShshoexer 		}
1244f032086dShshoexer 	} else
1245f032086dShshoexer 		return -1;
1246f032086dShshoexer 
124722a29ad6Shshoexer 	if (ret < 0)
1248f484f2cfShshoexer 		return -1;
12498a87fca6Smsf 	if (pfkey_reply(fd, NULL, NULL) < 0)
1250f484f2cfShshoexer 		return -1;
1251f484f2cfShshoexer 
1252f484f2cfShshoexer 	return 0;
1253f484f2cfShshoexer }
1254f484f2cfShshoexer 
1255f484f2cfShshoexer int
pfkey_ipsec_flush(void)1256f484f2cfShshoexer pfkey_ipsec_flush(void)
1257f484f2cfShshoexer {
1258f484f2cfShshoexer 	struct sadb_msg smsg;
1259f484f2cfShshoexer 	struct iovec	iov[IOV_CNT];
1260f484f2cfShshoexer 	ssize_t		n;
1261f484f2cfShshoexer 	int		iov_cnt, len;
1262f484f2cfShshoexer 
1263f484f2cfShshoexer 	bzero(&smsg, sizeof(smsg));
1264f484f2cfShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
1265f484f2cfShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
1266f484f2cfShshoexer 	smsg.sadb_msg_pid = getpid();
1267f484f2cfShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
1268f484f2cfShshoexer 	smsg.sadb_msg_type = SADB_FLUSH;
1269f484f2cfShshoexer 	smsg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
1270f484f2cfShshoexer 
1271f484f2cfShshoexer 	iov_cnt = 0;
1272f484f2cfShshoexer 
1273f484f2cfShshoexer 	iov[iov_cnt].iov_base = &smsg;
1274f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
1275f484f2cfShshoexer 	iov_cnt++;
1276f484f2cfShshoexer 
1277f484f2cfShshoexer 	len = smsg.sadb_msg_len * 8;
1278f484f2cfShshoexer 	if ((n = writev(fd, iov, iov_cnt)) == -1) {
1279f484f2cfShshoexer 		warn("writev failed");
1280f484f2cfShshoexer 		return -1;
1281f484f2cfShshoexer 	}
1282f484f2cfShshoexer 	if (n != len) {
1283f484f2cfShshoexer 		warnx("short write");
1284f484f2cfShshoexer 		return -1;
1285f484f2cfShshoexer 	}
12868a87fca6Smsf 	if (pfkey_reply(fd, NULL, NULL) < 0)
1287f484f2cfShshoexer 		return -1;
1288f484f2cfShshoexer 
1289f484f2cfShshoexer 	return 0;
1290f484f2cfShshoexer }
1291f484f2cfShshoexer 
12928a87fca6Smsf static int
pfkey_promisc(void)12938a87fca6Smsf pfkey_promisc(void)
12948a87fca6Smsf {
12958a87fca6Smsf 	struct sadb_msg msg;
12968a87fca6Smsf 
12978a87fca6Smsf 	memset(&msg, 0, sizeof(msg));
12988a87fca6Smsf 	msg.sadb_msg_version = PF_KEY_V2;
12998a87fca6Smsf 	msg.sadb_msg_seq = sadb_msg_seq++;
13008a87fca6Smsf 	msg.sadb_msg_pid = getpid();
13018a87fca6Smsf 	msg.sadb_msg_len = sizeof(msg) / PFKEYV2_CHUNK;
13028a87fca6Smsf 	msg.sadb_msg_type = SADB_X_PROMISC;
13038a87fca6Smsf 	msg.sadb_msg_satype = 1;	/* enable */
13048a87fca6Smsf 	if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) {
13058a87fca6Smsf 		warn("pfkey_promisc: write failed");
13068a87fca6Smsf 		return -1;
13078a87fca6Smsf 	}
13088a87fca6Smsf 	if (pfkey_reply(fd, NULL, NULL) < 0)
13098a87fca6Smsf 		return -1;
13108a87fca6Smsf 	return 0;
13118a87fca6Smsf }
13128a87fca6Smsf 
13138a87fca6Smsf int
pfkey_monitor(int opts)13148a87fca6Smsf pfkey_monitor(int opts)
13158a87fca6Smsf {
131656232324Sderaadt 	struct pollfd pfd[1];
13178a87fca6Smsf 	struct sadb_msg *msg;
131856232324Sderaadt 	u_int8_t *data;
131956232324Sderaadt 	ssize_t len;
13208a87fca6Smsf 	int n;
13218a87fca6Smsf 
13228a87fca6Smsf 	if (pfkey_init() < 0)
13238a87fca6Smsf 		return -1;
13248a87fca6Smsf 	if (pfkey_promisc() < 0)
13258a87fca6Smsf 		return -1;
13268a87fca6Smsf 
1327*202aab43Stobhe 	if (pledge("stdio", NULL) == -1)
1328*202aab43Stobhe 		err(1, "pledge");
1329*202aab43Stobhe 
133056232324Sderaadt 	pfd[0].fd = fd;
133156232324Sderaadt 	pfd[0].events = POLLIN;
13328a87fca6Smsf 	for (;;) {
1333df69c215Sderaadt 		if ((n = poll(pfd, 1, -1)) == -1)
133456232324Sderaadt 			err(2, "poll");
13358a87fca6Smsf 		if (n == 0)
13368a87fca6Smsf 			break;
133756232324Sderaadt 		if ((pfd[0].revents & POLLIN) == 0)
13388a87fca6Smsf 			continue;
13398a87fca6Smsf 		if (pfkey_reply(fd, &data, &len) < 0)
13408a87fca6Smsf 			continue;
13418a87fca6Smsf 		msg = (struct sadb_msg *)data;
13428a87fca6Smsf 		if (msg->sadb_msg_type == SADB_X_PROMISC) {
13438a87fca6Smsf 			/* remove extra header from promisc messages */
13448a87fca6Smsf 			if ((msg->sadb_msg_len * PFKEYV2_CHUNK) >=
13458a87fca6Smsf 			    2 * sizeof(struct sadb_msg)) {
13468a87fca6Smsf 				msg++;
13478a87fca6Smsf 			}
13488a87fca6Smsf 		}
13498a87fca6Smsf 		pfkey_monitor_sa(msg, opts);
13508a87fca6Smsf 		if (opts & IPSECCTL_OPT_VERBOSE)
13518a87fca6Smsf 			pfkey_print_raw(data, len);
13521e82d711Sderaadt 		freezero(data, len);
13538a87fca6Smsf 	}
13548a87fca6Smsf 	close(fd);
13558a87fca6Smsf 	return 0;
13568a87fca6Smsf }
13578a87fca6Smsf 
1358f484f2cfShshoexer int
pfkey_init(void)1359f484f2cfShshoexer pfkey_init(void)
1360f484f2cfShshoexer {
1361f484f2cfShshoexer 	if ((fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) == -1)
1362bd828a90Shshoexer 		err(1, "pfkey_init: failed to open PF_KEY socket");
1363f484f2cfShshoexer 
1364f484f2cfShshoexer 	return 0;
1365f484f2cfShshoexer }
1366