xref: /openbsd/sbin/ipsecctl/pfkey.c (revision df69c215)
1*df69c215Sderaadt /*	$OpenBSD: pfkey.c,v 1.61 2019/06/28 13:32:44 deraadt 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 *,
52375db29dShshoexer 		    struct ipsec_transforms *, struct ipsec_key *,
53a38d220fShshoexer 		    struct ipsec_key *, u_int8_t);
54a6bcba92Sbluhm static int	pfkey_sabundle(int, u_int8_t, u_int8_t, u_int8_t,
558065703bShshoexer 		    struct ipsec_addr_wrap *, u_int32_t,
568065703bShshoexer 		    struct ipsec_addr_wrap *, u_int32_t);
578a87fca6Smsf static int	pfkey_reply(int, u_int8_t **, ssize_t *);
58e225c210Shshoexer int		pfkey_parse(struct sadb_msg *, struct ipsec_rule *);
59f484f2cfShshoexer int		pfkey_ipsec_flush(void);
60356121f6Shshoexer int		pfkey_ipsec_establish(int, struct ipsec_rule *);
61f484f2cfShshoexer int		pfkey_init(void);
62f484f2cfShshoexer 
63f484f2cfShshoexer static int
64f484f2cfShshoexer pfkey_flow(int sd, u_int8_t satype, u_int8_t action, u_int8_t direction,
6557f58d0dSnaddy     u_int8_t proto, struct ipsec_addr_wrap *src, u_int16_t sport,
6657f58d0dSnaddy     struct ipsec_addr_wrap *dst, u_int16_t dport,
67435bb41eSmarkus     struct ipsec_addr_wrap *local, struct ipsec_addr_wrap *peer,
68435bb41eSmarkus     struct ipsec_auth *auth, u_int8_t flowtype)
69f484f2cfShshoexer {
70f484f2cfShshoexer 	struct sadb_msg		 smsg;
71435bb41eSmarkus 	struct sadb_address	 sa_src, sa_dst, sa_local, sa_peer, sa_smask,
72435bb41eSmarkus 				 sa_dmask;
73f484f2cfShshoexer 	struct sadb_protocol	 sa_flowtype, sa_protocol;
74f484f2cfShshoexer 	struct sadb_ident	*sa_srcid, *sa_dstid;
75435bb41eSmarkus 	struct sockaddr_storage	 ssrc, sdst, slocal, speer, smask, dmask;
76f484f2cfShshoexer 	struct iovec		 iov[IOV_CNT];
77f484f2cfShshoexer 	ssize_t			 n;
78f484f2cfShshoexer 	int			 iov_cnt, len, ret = 0;
79f484f2cfShshoexer 
80f484f2cfShshoexer 	sa_srcid = sa_dstid = NULL;
81f484f2cfShshoexer 
82f484f2cfShshoexer 	bzero(&ssrc, sizeof(ssrc));
83f484f2cfShshoexer 	bzero(&smask, sizeof(smask));
842099bcdfStodd 	ssrc.ss_family = smask.ss_family = src->af;
85f484f2cfShshoexer 	switch (src->af) {
86f484f2cfShshoexer 	case AF_INET:
87712e78baShshoexer 		((struct sockaddr_in *)&ssrc)->sin_addr = src->address.v4;
88f484f2cfShshoexer 		ssrc.ss_len = sizeof(struct sockaddr_in);
89712e78baShshoexer 		((struct sockaddr_in *)&smask)->sin_addr = src->mask.v4;
9057f58d0dSnaddy 		if (sport) {
9157f58d0dSnaddy 			((struct sockaddr_in *)&ssrc)->sin_port = sport;
9257f58d0dSnaddy 			((struct sockaddr_in *)&smask)->sin_port = 0xffff;
9357f58d0dSnaddy 		}
94f484f2cfShshoexer 		break;
95f484f2cfShshoexer 	case AF_INET6:
962099bcdfStodd 		((struct sockaddr_in6 *)&ssrc)->sin6_addr = src->address.v6;
972099bcdfStodd 		ssrc.ss_len = sizeof(struct sockaddr_in6);
982099bcdfStodd 		((struct sockaddr_in6 *)&smask)->sin6_addr = src->mask.v6;
9957f58d0dSnaddy 		if (sport) {
10057f58d0dSnaddy 			((struct sockaddr_in6 *)&ssrc)->sin6_port = sport;
10157f58d0dSnaddy 			((struct sockaddr_in6 *)&smask)->sin6_port = 0xffff;
10257f58d0dSnaddy 		}
1032099bcdfStodd 		break;
104f484f2cfShshoexer 	default:
105f484f2cfShshoexer 		warnx("unsupported address family %d", src->af);
106f484f2cfShshoexer 		return -1;
107f484f2cfShshoexer 	}
108f484f2cfShshoexer 	smask.ss_len = ssrc.ss_len;
109f484f2cfShshoexer 
110f484f2cfShshoexer 	bzero(&sdst, sizeof(sdst));
111f484f2cfShshoexer 	bzero(&dmask, sizeof(dmask));
1122099bcdfStodd 	sdst.ss_family = dmask.ss_family = dst->af;
113f484f2cfShshoexer 	switch (dst->af) {
114f484f2cfShshoexer 	case AF_INET:
115712e78baShshoexer 		((struct sockaddr_in *)&sdst)->sin_addr = dst->address.v4;
116f484f2cfShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in);
117712e78baShshoexer 		((struct sockaddr_in *)&dmask)->sin_addr = dst->mask.v4;
11857f58d0dSnaddy 		if (dport) {
11957f58d0dSnaddy 			((struct sockaddr_in *)&sdst)->sin_port = dport;
12057f58d0dSnaddy 			((struct sockaddr_in *)&dmask)->sin_port = 0xffff;
12157f58d0dSnaddy 		}
122f484f2cfShshoexer 		break;
123f484f2cfShshoexer 	case AF_INET6:
1242099bcdfStodd 		((struct sockaddr_in6 *)&sdst)->sin6_addr = dst->address.v6;
1252099bcdfStodd 		sdst.ss_len = sizeof(struct sockaddr_in6);
1262099bcdfStodd 		((struct sockaddr_in6 *)&dmask)->sin6_addr = dst->mask.v6;
12757f58d0dSnaddy 		if (dport) {
12857f58d0dSnaddy 			((struct sockaddr_in6 *)&sdst)->sin6_port = dport;
12957f58d0dSnaddy 			((struct sockaddr_in6 *)&dmask)->sin6_port = 0xffff;
13057f58d0dSnaddy 		}
1312099bcdfStodd 		break;
132f484f2cfShshoexer 	default:
133f484f2cfShshoexer 		warnx("unsupported address family %d", dst->af);
134f484f2cfShshoexer 		return -1;
135f484f2cfShshoexer 	}
136f484f2cfShshoexer 	dmask.ss_len = sdst.ss_len;
137f484f2cfShshoexer 
138435bb41eSmarkus 	bzero(&slocal, sizeof(slocal));
139435bb41eSmarkus 	if (local) {
1402099bcdfStodd 		slocal.ss_family = local->af;
141435bb41eSmarkus 		switch (local->af) {
142435bb41eSmarkus 		case AF_INET:
143435bb41eSmarkus 			((struct sockaddr_in *)&slocal)->sin_addr =
144435bb41eSmarkus 			    local->address.v4;
145435bb41eSmarkus 			slocal.ss_len = sizeof(struct sockaddr_in);
146435bb41eSmarkus 			break;
147435bb41eSmarkus 		case AF_INET6:
1482099bcdfStodd 			((struct sockaddr_in6 *)&slocal)->sin6_addr =
1492099bcdfStodd 			    local->address.v6;
1502099bcdfStodd 			slocal.ss_len = sizeof(struct sockaddr_in6);
1512099bcdfStodd 			break;
152435bb41eSmarkus 		default:
153435bb41eSmarkus 			warnx("unsupported address family %d", local->af);
154435bb41eSmarkus 			return -1;
155435bb41eSmarkus 		}
156435bb41eSmarkus 	}
157435bb41eSmarkus 
158f484f2cfShshoexer 	bzero(&speer, sizeof(speer));
15922a29ad6Shshoexer 	if (peer) {
1602099bcdfStodd 		speer.ss_family = peer->af;
161f484f2cfShshoexer 		switch (peer->af) {
162f484f2cfShshoexer 		case AF_INET:
163712e78baShshoexer 			((struct sockaddr_in *)&speer)->sin_addr =
164712e78baShshoexer 			    peer->address.v4;
165f484f2cfShshoexer 			speer.ss_len = sizeof(struct sockaddr_in);
166f484f2cfShshoexer 			break;
167f484f2cfShshoexer 		case AF_INET6:
1682099bcdfStodd 			((struct sockaddr_in6 *)&speer)->sin6_addr =
1692099bcdfStodd 			    peer->address.v6;
1702099bcdfStodd 			speer.ss_len = sizeof(struct sockaddr_in6);
1712099bcdfStodd 			break;
172f484f2cfShshoexer 		default:
173f484f2cfShshoexer 			warnx("unsupported address family %d", peer->af);
174f484f2cfShshoexer 			return -1;
175f484f2cfShshoexer 		}
17622a29ad6Shshoexer 	}
177f484f2cfShshoexer 
178f484f2cfShshoexer 	bzero(&smsg, sizeof(smsg));
179f484f2cfShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
180f484f2cfShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
181f484f2cfShshoexer 	smsg.sadb_msg_pid = getpid();
182f484f2cfShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
183f484f2cfShshoexer 	smsg.sadb_msg_type = action;
184f484f2cfShshoexer 	smsg.sadb_msg_satype = satype;
185f484f2cfShshoexer 
186f484f2cfShshoexer 	bzero(&sa_flowtype, sizeof(sa_flowtype));
187f484f2cfShshoexer 	sa_flowtype.sadb_protocol_exttype = SADB_X_EXT_FLOW_TYPE;
188f484f2cfShshoexer 	sa_flowtype.sadb_protocol_len = sizeof(sa_flowtype) / 8;
189f484f2cfShshoexer 	sa_flowtype.sadb_protocol_direction = direction;
19026df514dShshoexer 
191e1ffbfafShshoexer 	switch (flowtype) {
192e1ffbfafShshoexer 	case TYPE_USE:
19326df514dShshoexer 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_USE;
194e1ffbfafShshoexer 		break;
195b7a16601Shshoexer 	case TYPE_ACQUIRE:
196b7a16601Shshoexer 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_ACQUIRE;
197b7a16601Shshoexer 		break;
198e1ffbfafShshoexer 	case TYPE_REQUIRE:
199f484f2cfShshoexer 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_REQUIRE;
200e1ffbfafShshoexer 		break;
2017c7fb9e5Sreyk 	case TYPE_DENY:
2027c7fb9e5Sreyk 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_DENY;
2037c7fb9e5Sreyk 		break;
2047c7fb9e5Sreyk 	case TYPE_BYPASS:
2057c7fb9e5Sreyk 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_BYPASS;
2067c7fb9e5Sreyk 		break;
207b7a16601Shshoexer 	case TYPE_DONTACQ:
208b7a16601Shshoexer 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_DONTACQ;
209b7a16601Shshoexer 		break;
210e1ffbfafShshoexer 	default:
211e1ffbfafShshoexer 		warnx("unsupported flowtype %d", flowtype);
212e1ffbfafShshoexer 		return -1;
213e1ffbfafShshoexer 	}
214f484f2cfShshoexer 
215f484f2cfShshoexer 	bzero(&sa_protocol, sizeof(sa_protocol));
216f484f2cfShshoexer 	sa_protocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
217f484f2cfShshoexer 	sa_protocol.sadb_protocol_len = sizeof(sa_protocol) / 8;
218f484f2cfShshoexer 	sa_protocol.sadb_protocol_direction = 0;
2199182219dSmarkus 	sa_protocol.sadb_protocol_proto = proto;
220f484f2cfShshoexer 
221f484f2cfShshoexer 	bzero(&sa_src, sizeof(sa_src));
222f484f2cfShshoexer 	sa_src.sadb_address_exttype = SADB_X_EXT_SRC_FLOW;
223f484f2cfShshoexer 	sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
224f484f2cfShshoexer 
225f484f2cfShshoexer 	bzero(&sa_smask, sizeof(sa_smask));
226f484f2cfShshoexer 	sa_smask.sadb_address_exttype = SADB_X_EXT_SRC_MASK;
227f484f2cfShshoexer 	sa_smask.sadb_address_len =
228f484f2cfShshoexer 	    (sizeof(sa_smask) + ROUNDUP(smask.ss_len)) / 8;
229f484f2cfShshoexer 
230f484f2cfShshoexer 	bzero(&sa_dst, sizeof(sa_dst));
231f484f2cfShshoexer 	sa_dst.sadb_address_exttype = SADB_X_EXT_DST_FLOW;
232f484f2cfShshoexer 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
233f484f2cfShshoexer 
234f484f2cfShshoexer 	bzero(&sa_dmask, sizeof(sa_dmask));
235f484f2cfShshoexer 	sa_dmask.sadb_address_exttype = SADB_X_EXT_DST_MASK;
236f484f2cfShshoexer 	sa_dmask.sadb_address_len =
237f484f2cfShshoexer 	    (sizeof(sa_dmask) + ROUNDUP(dmask.ss_len)) / 8;
238f484f2cfShshoexer 
239435bb41eSmarkus 	if (local) {
240435bb41eSmarkus 		bzero(&sa_local, sizeof(sa_local));
241435bb41eSmarkus 		sa_local.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
242435bb41eSmarkus 		sa_local.sadb_address_len =
243435bb41eSmarkus 		    (sizeof(sa_local) + ROUNDUP(slocal.ss_len)) / 8;
244435bb41eSmarkus 	}
245435bb41eSmarkus 	if (peer) {
246f484f2cfShshoexer 		bzero(&sa_peer, sizeof(sa_peer));
247f484f2cfShshoexer 		sa_peer.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
248f484f2cfShshoexer 		sa_peer.sadb_address_len =
249f484f2cfShshoexer 		    (sizeof(sa_peer) + ROUNDUP(speer.ss_len)) / 8;
250435bb41eSmarkus 	}
251f484f2cfShshoexer 
252abe65127Shshoexer 	if (auth && auth->srcid) {
253abe65127Shshoexer 		len = ROUNDUP(strlen(auth->srcid) + 1) + sizeof(*sa_srcid);
254f484f2cfShshoexer 
255f484f2cfShshoexer 		sa_srcid = calloc(len, sizeof(u_int8_t));
256f484f2cfShshoexer 		if (sa_srcid == NULL)
257bd828a90Shshoexer 			err(1, "pfkey_flow: calloc");
258f484f2cfShshoexer 
2592281ca6dSmarkus 		sa_srcid->sadb_ident_type = auth->srcid_type;
260f484f2cfShshoexer 		sa_srcid->sadb_ident_len = len / 8;
261f484f2cfShshoexer 		sa_srcid->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
262f484f2cfShshoexer 
263abe65127Shshoexer 		strlcpy((char *)(sa_srcid + 1), auth->srcid,
264abe65127Shshoexer 		    ROUNDUP(strlen(auth->srcid) + 1));
265f484f2cfShshoexer 	}
266abe65127Shshoexer 	if (auth && auth->dstid) {
267abe65127Shshoexer 		len = ROUNDUP(strlen(auth->dstid) + 1) + sizeof(*sa_dstid);
268f484f2cfShshoexer 
269f484f2cfShshoexer 		sa_dstid = calloc(len, sizeof(u_int8_t));
270f484f2cfShshoexer 		if (sa_dstid == NULL)
271bd828a90Shshoexer 			err(1, "pfkey_flow: calloc");
272f484f2cfShshoexer 
2732281ca6dSmarkus 		sa_dstid->sadb_ident_type = auth->dstid_type;
274f484f2cfShshoexer 		sa_dstid->sadb_ident_len = len / 8;
275f484f2cfShshoexer 		sa_dstid->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
276f484f2cfShshoexer 
277abe65127Shshoexer 		strlcpy((char *)(sa_dstid + 1), auth->dstid,
278abe65127Shshoexer 		    ROUNDUP(strlen(auth->dstid) + 1));
279f484f2cfShshoexer 	}
280f484f2cfShshoexer 
281f484f2cfShshoexer 	iov_cnt = 0;
282f484f2cfShshoexer 
283f484f2cfShshoexer 	/* header */
284f484f2cfShshoexer 	iov[iov_cnt].iov_base = &smsg;
285f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
286f484f2cfShshoexer 	iov_cnt++;
287f484f2cfShshoexer 
28889ad8c34Shshoexer 	/* add flow type */
28989ad8c34Shshoexer 	iov[iov_cnt].iov_base = &sa_flowtype;
29089ad8c34Shshoexer 	iov[iov_cnt].iov_len = sizeof(sa_flowtype);
29189ad8c34Shshoexer 	smsg.sadb_msg_len += sa_flowtype.sadb_protocol_len;
29289ad8c34Shshoexer 	iov_cnt++;
29389ad8c34Shshoexer 
294435bb41eSmarkus 	/* local ip */
295435bb41eSmarkus 	if (local) {
296435bb41eSmarkus 		iov[iov_cnt].iov_base = &sa_local;
297435bb41eSmarkus 		iov[iov_cnt].iov_len = sizeof(sa_local);
298435bb41eSmarkus 		iov_cnt++;
299435bb41eSmarkus 		iov[iov_cnt].iov_base = &slocal;
300435bb41eSmarkus 		iov[iov_cnt].iov_len = ROUNDUP(slocal.ss_len);
301435bb41eSmarkus 		smsg.sadb_msg_len += sa_local.sadb_address_len;
302435bb41eSmarkus 		iov_cnt++;
303435bb41eSmarkus 	}
304435bb41eSmarkus 
305f484f2cfShshoexer 	/* remote peer */
30622a29ad6Shshoexer 	if (peer) {
307f484f2cfShshoexer 		iov[iov_cnt].iov_base = &sa_peer;
308f484f2cfShshoexer 		iov[iov_cnt].iov_len = sizeof(sa_peer);
309f484f2cfShshoexer 		iov_cnt++;
310f484f2cfShshoexer 		iov[iov_cnt].iov_base = &speer;
311f484f2cfShshoexer 		iov[iov_cnt].iov_len = ROUNDUP(speer.ss_len);
312f484f2cfShshoexer 		smsg.sadb_msg_len += sa_peer.sadb_address_len;
313f484f2cfShshoexer 		iov_cnt++;
31422a29ad6Shshoexer 	}
315f484f2cfShshoexer 
31689ad8c34Shshoexer 	/* src addr */
31789ad8c34Shshoexer 	iov[iov_cnt].iov_base = &sa_src;
31889ad8c34Shshoexer 	iov[iov_cnt].iov_len = sizeof(sa_src);
31989ad8c34Shshoexer 	iov_cnt++;
32089ad8c34Shshoexer 	iov[iov_cnt].iov_base = &ssrc;
32189ad8c34Shshoexer 	iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len);
32289ad8c34Shshoexer 	smsg.sadb_msg_len += sa_src.sadb_address_len;
323f484f2cfShshoexer 	iov_cnt++;
324f484f2cfShshoexer 
32589ad8c34Shshoexer 	/* src mask */
326f484f2cfShshoexer 	iov[iov_cnt].iov_base = &sa_smask;
327f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_smask);
328f484f2cfShshoexer 	iov_cnt++;
329f484f2cfShshoexer 	iov[iov_cnt].iov_base = &smask;
330f484f2cfShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(smask.ss_len);
331f484f2cfShshoexer 	smsg.sadb_msg_len += sa_smask.sadb_address_len;
332f484f2cfShshoexer 	iov_cnt++;
333f484f2cfShshoexer 
334f484f2cfShshoexer 	/* dest addr */
335f484f2cfShshoexer 	iov[iov_cnt].iov_base = &sa_dst;
336f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst);
337f484f2cfShshoexer 	iov_cnt++;
338f484f2cfShshoexer 	iov[iov_cnt].iov_base = &sdst;
339f484f2cfShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
340f484f2cfShshoexer 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
341f484f2cfShshoexer 	iov_cnt++;
342f484f2cfShshoexer 
34389ad8c34Shshoexer 	/* dst mask */
34489ad8c34Shshoexer 	iov[iov_cnt].iov_base = &sa_dmask;
34589ad8c34Shshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dmask);
346f484f2cfShshoexer 	iov_cnt++;
34789ad8c34Shshoexer 	iov[iov_cnt].iov_base = &dmask;
34889ad8c34Shshoexer 	iov[iov_cnt].iov_len = ROUNDUP(dmask.ss_len);
34989ad8c34Shshoexer 	smsg.sadb_msg_len += sa_dmask.sadb_address_len;
35089ad8c34Shshoexer 	iov_cnt++;
35189ad8c34Shshoexer 
35289ad8c34Shshoexer 	/* add protocol */
35389ad8c34Shshoexer 	iov[iov_cnt].iov_base = &sa_protocol;
35489ad8c34Shshoexer 	iov[iov_cnt].iov_len = sizeof(sa_protocol);
35589ad8c34Shshoexer 	smsg.sadb_msg_len += sa_protocol.sadb_protocol_len;
356f484f2cfShshoexer 	iov_cnt++;
357f484f2cfShshoexer 
358f484f2cfShshoexer 	if (sa_srcid) {
359f484f2cfShshoexer 		/* src identity */
360f484f2cfShshoexer 		iov[iov_cnt].iov_base = sa_srcid;
361f484f2cfShshoexer 		iov[iov_cnt].iov_len = sa_srcid->sadb_ident_len * 8;
362f484f2cfShshoexer 		smsg.sadb_msg_len += sa_srcid->sadb_ident_len;
363f484f2cfShshoexer 		iov_cnt++;
364f484f2cfShshoexer 	}
365f484f2cfShshoexer 	if (sa_dstid) {
366f484f2cfShshoexer 		/* dst identity */
367f484f2cfShshoexer 		iov[iov_cnt].iov_base = sa_dstid;
368f484f2cfShshoexer 		iov[iov_cnt].iov_len = sa_dstid->sadb_ident_len * 8;
369f484f2cfShshoexer 		smsg.sadb_msg_len += sa_dstid->sadb_ident_len;
370f484f2cfShshoexer 		iov_cnt++;
371f484f2cfShshoexer 	}
372f484f2cfShshoexer 	len = smsg.sadb_msg_len * 8;
37389d271caShenning 
37489d271caShenning 	do {
37589d271caShenning 		n = writev(sd, iov, iov_cnt);
37689d271caShenning 	} while (n == -1 && (errno == EAGAIN || errno == EINTR));
37789d271caShenning 	if (n == -1) {
378f484f2cfShshoexer 		warn("writev failed");
379f484f2cfShshoexer 		ret = -1;
380f484f2cfShshoexer 	}
381f484f2cfShshoexer 
382f484f2cfShshoexer 	free(sa_srcid);
383f484f2cfShshoexer 	free(sa_dstid);
384f484f2cfShshoexer 
385f484f2cfShshoexer 	return ret;
386f484f2cfShshoexer }
387f484f2cfShshoexer 
388f484f2cfShshoexer static int
38991f765ddShshoexer pfkey_sa(int sd, u_int8_t satype, u_int8_t action, u_int32_t spi,
39091f765ddShshoexer     struct ipsec_addr_wrap *src, struct ipsec_addr_wrap *dst,
39191f765ddShshoexer     struct ipsec_transforms *xfs, struct ipsec_key *authkey,
392a38d220fShshoexer     struct ipsec_key *enckey, u_int8_t tmode)
393f032086dShshoexer {
394f032086dShshoexer 	struct sadb_msg		smsg;
395f032086dShshoexer 	struct sadb_sa		sa;
396f032086dShshoexer 	struct sadb_address	sa_src, sa_dst;
397881e2068Shshoexer 	struct sadb_key		sa_authkey, sa_enckey;
398f032086dShshoexer 	struct sockaddr_storage	ssrc, sdst;
399f032086dShshoexer 	struct iovec		iov[IOV_CNT];
400f032086dShshoexer 	ssize_t			n;
401f032086dShshoexer 	int			iov_cnt, len, ret = 0;
402f032086dShshoexer 
403f032086dShshoexer 	bzero(&ssrc, sizeof(ssrc));
4042099bcdfStodd 	ssrc.ss_family = src->af;
405f032086dShshoexer 	switch (src->af) {
406f032086dShshoexer 	case AF_INET:
407712e78baShshoexer 		((struct sockaddr_in *)&ssrc)->sin_addr = src->address.v4;
408f032086dShshoexer 		ssrc.ss_len = sizeof(struct sockaddr_in);
409f032086dShshoexer 		break;
410f032086dShshoexer 	case AF_INET6:
4112099bcdfStodd 		((struct sockaddr_in6 *)&ssrc)->sin6_addr = src->address.v6;
4122099bcdfStodd 		ssrc.ss_len = sizeof(struct sockaddr_in6);
4132099bcdfStodd 		break;
414f032086dShshoexer 	default:
415f032086dShshoexer 		warnx("unsupported address family %d", src->af);
416f032086dShshoexer 		return -1;
417f032086dShshoexer 	}
418f032086dShshoexer 
419f032086dShshoexer 	bzero(&sdst, sizeof(sdst));
4202099bcdfStodd 	sdst.ss_family = dst->af;
421f032086dShshoexer 	switch (dst->af) {
422f032086dShshoexer 	case AF_INET:
423712e78baShshoexer 		((struct sockaddr_in *)&sdst)->sin_addr = dst->address.v4;
424f032086dShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in);
425f032086dShshoexer 		break;
426f032086dShshoexer 	case AF_INET6:
4272099bcdfStodd 		((struct sockaddr_in6 *)&sdst)->sin6_addr = dst->address.v6;
4282099bcdfStodd 		sdst.ss_len = sizeof(struct sockaddr_in6);
4292099bcdfStodd 		break;
430f032086dShshoexer 	default:
431f032086dShshoexer 		warnx("unsupported address family %d", dst->af);
432f032086dShshoexer 		return -1;
433f032086dShshoexer 	}
434f032086dShshoexer 
435f032086dShshoexer 	bzero(&smsg, sizeof(smsg));
436f032086dShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
437f032086dShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
438f032086dShshoexer 	smsg.sadb_msg_pid = getpid();
439f032086dShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
440f032086dShshoexer 	smsg.sadb_msg_type = action;
441f032086dShshoexer 	smsg.sadb_msg_satype = satype;
442f032086dShshoexer 
443f032086dShshoexer 	bzero(&sa, sizeof(sa));
444f032086dShshoexer 	sa.sadb_sa_len = sizeof(sa) / 8;
445f032086dShshoexer 	sa.sadb_sa_exttype = SADB_EXT_SA;
446f032086dShshoexer 	sa.sadb_sa_spi = htonl(spi);
447f032086dShshoexer 	sa.sadb_sa_state = SADB_SASTATE_MATURE;
448f032086dShshoexer 
44988a8cceeSmarkus 	if (satype != SADB_X_SATYPE_IPIP && tmode == IPSEC_TUNNEL)
450a38d220fShshoexer 		sa.sadb_sa_flags |= SADB_X_SAFLAGS_TUNNEL;
451a38d220fShshoexer 
452375db29dShshoexer 	if (xfs && xfs->authxf) {
453375db29dShshoexer 		switch (xfs->authxf->id) {
454881e2068Shshoexer 		case AUTHXF_NONE:
455881e2068Shshoexer 			break;
456881e2068Shshoexer 		case AUTHXF_HMAC_MD5:
457881e2068Shshoexer 			sa.sadb_sa_auth = SADB_AALG_MD5HMAC;
458881e2068Shshoexer 			break;
459881e2068Shshoexer 		case AUTHXF_HMAC_RIPEMD160:
460881e2068Shshoexer 			sa.sadb_sa_auth = SADB_X_AALG_RIPEMD160HMAC;
461881e2068Shshoexer 			break;
462881e2068Shshoexer 		case AUTHXF_HMAC_SHA1:
463881e2068Shshoexer 			sa.sadb_sa_auth = SADB_AALG_SHA1HMAC;
464881e2068Shshoexer 			break;
465881e2068Shshoexer 		case AUTHXF_HMAC_SHA2_256:
466881e2068Shshoexer 			sa.sadb_sa_auth = SADB_X_AALG_SHA2_256;
467881e2068Shshoexer 			break;
468881e2068Shshoexer 		case AUTHXF_HMAC_SHA2_384:
469881e2068Shshoexer 			sa.sadb_sa_auth = SADB_X_AALG_SHA2_384;
470881e2068Shshoexer 			break;
471881e2068Shshoexer 		case AUTHXF_HMAC_SHA2_512:
472881e2068Shshoexer 			sa.sadb_sa_auth = SADB_X_AALG_SHA2_512;
473881e2068Shshoexer 			break;
474881e2068Shshoexer 		default:
475881e2068Shshoexer 			warnx("unsupported authentication algorithm %d",
476375db29dShshoexer 			    xfs->authxf->id);
477881e2068Shshoexer 		}
478881e2068Shshoexer 	}
479375db29dShshoexer 	if (xfs && xfs->encxf) {
480375db29dShshoexer 		switch (xfs->encxf->id) {
481881e2068Shshoexer 		case ENCXF_NONE:
482881e2068Shshoexer 			break;
483881e2068Shshoexer 		case ENCXF_3DES_CBC:
484881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_EALG_3DESCBC;
485881e2068Shshoexer 			break;
486881e2068Shshoexer 		case ENCXF_AES:
487783454c1Smikeb 		case ENCXF_AES_128:
488783454c1Smikeb 		case ENCXF_AES_192:
489783454c1Smikeb 		case ENCXF_AES_256:
490881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_AES;
491881e2068Shshoexer 			break;
492881e2068Shshoexer 		case ENCXF_AESCTR:
4935f649d51Snaddy 		case ENCXF_AES_128_CTR:
4945f649d51Snaddy 		case ENCXF_AES_192_CTR:
4955f649d51Snaddy 		case ENCXF_AES_256_CTR:
496881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_AESCTR;
497881e2068Shshoexer 			break;
498783454c1Smikeb 		case ENCXF_AES_128_GCM:
499783454c1Smikeb 		case ENCXF_AES_192_GCM:
500783454c1Smikeb 		case ENCXF_AES_256_GCM:
501783454c1Smikeb 			sa.sadb_sa_encrypt = SADB_X_EALG_AESGCM16;
502783454c1Smikeb 			break;
503783454c1Smikeb 		case ENCXF_AES_128_GMAC:
504783454c1Smikeb 		case ENCXF_AES_192_GMAC:
505783454c1Smikeb 		case ENCXF_AES_256_GMAC:
506783454c1Smikeb 			sa.sadb_sa_encrypt = SADB_X_EALG_AESGMAC;
507783454c1Smikeb 			break;
508881e2068Shshoexer 		case ENCXF_BLOWFISH:
509881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_BLF;
510881e2068Shshoexer 			break;
511881e2068Shshoexer 		case ENCXF_CAST128:
512881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_CAST;
513881e2068Shshoexer 			break;
514881e2068Shshoexer 		case ENCXF_NULL:
515881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_EALG_NULL;
516881e2068Shshoexer 			break;
517881e2068Shshoexer 		default:
518375db29dShshoexer 			warnx("unsupported encryption algorithm %d",
519375db29dShshoexer 			    xfs->encxf->id);
520881e2068Shshoexer 		}
521881e2068Shshoexer 	}
52272e25333Shshoexer 	if (xfs && xfs->compxf) {
52372e25333Shshoexer 		switch (xfs->compxf->id) {
52472e25333Shshoexer 		case COMPXF_DEFLATE:
52572e25333Shshoexer 			sa.sadb_sa_encrypt = SADB_X_CALG_DEFLATE;
52672e25333Shshoexer 			break;
52772e25333Shshoexer 		case COMPXF_LZS:
52872e25333Shshoexer 			sa.sadb_sa_encrypt = SADB_X_CALG_LZS;
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 
54472e25333Shshoexer 	if (action == SADB_ADD && !authkey && !enckey && satype !=
54588a8cceeSmarkus 	    SADB_X_SATYPE_IPCOMP && satype != SADB_X_SATYPE_IPIP) { /* XXX ENCNULL */
5460813ab45Shshoexer 		warnx("no key specified");
5470813ab45Shshoexer 		return -1;
5480813ab45Shshoexer 	}
549881e2068Shshoexer 	if (authkey) {
550881e2068Shshoexer 		bzero(&sa_authkey, sizeof(sa_authkey));
551881e2068Shshoexer 		sa_authkey.sadb_key_len = (sizeof(sa_authkey) +
552881e2068Shshoexer 		    ((authkey->len + 7) / 8) * 8) / 8;
553881e2068Shshoexer 		sa_authkey.sadb_key_exttype = SADB_EXT_KEY_AUTH;
554881e2068Shshoexer 		sa_authkey.sadb_key_bits = 8 * authkey->len;
555881e2068Shshoexer 	}
556881e2068Shshoexer 	if (enckey) {
557881e2068Shshoexer 		bzero(&sa_enckey, sizeof(sa_enckey));
558881e2068Shshoexer 		sa_enckey.sadb_key_len = (sizeof(sa_enckey) +
559881e2068Shshoexer 		    ((enckey->len + 7) / 8) * 8) / 8;
560881e2068Shshoexer 		sa_enckey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
561881e2068Shshoexer 		sa_enckey.sadb_key_bits = 8 * enckey->len;
562f032086dShshoexer 	}
563f032086dShshoexer 
564f032086dShshoexer 	iov_cnt = 0;
565f032086dShshoexer 
566f032086dShshoexer 	/* header */
567f032086dShshoexer 	iov[iov_cnt].iov_base = &smsg;
568f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
569f032086dShshoexer 	iov_cnt++;
570f032086dShshoexer 
571f032086dShshoexer 	/* sa */
572f032086dShshoexer 	iov[iov_cnt].iov_base = &sa;
573f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(sa);
574f032086dShshoexer 	smsg.sadb_msg_len += sa.sadb_sa_len;
575f032086dShshoexer 	iov_cnt++;
576f032086dShshoexer 
577f032086dShshoexer 	/* src addr */
578f032086dShshoexer 	iov[iov_cnt].iov_base = &sa_src;
579f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_src);
580f032086dShshoexer 	iov_cnt++;
581f032086dShshoexer 	iov[iov_cnt].iov_base = &ssrc;
582f032086dShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len);
583f032086dShshoexer 	smsg.sadb_msg_len += sa_src.sadb_address_len;
584f032086dShshoexer 	iov_cnt++;
585f032086dShshoexer 
586f032086dShshoexer 	/* dst addr */
587f032086dShshoexer 	iov[iov_cnt].iov_base = &sa_dst;
588f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst);
589f032086dShshoexer 	iov_cnt++;
590f032086dShshoexer 	iov[iov_cnt].iov_base = &sdst;
591f032086dShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
592f032086dShshoexer 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
593f032086dShshoexer 	iov_cnt++;
594f032086dShshoexer 
595881e2068Shshoexer 	if (authkey) {
596881e2068Shshoexer 		/* authentication key */
597881e2068Shshoexer 		iov[iov_cnt].iov_base = &sa_authkey;
598881e2068Shshoexer 		iov[iov_cnt].iov_len = sizeof(sa_authkey);
599f032086dShshoexer 		iov_cnt++;
600881e2068Shshoexer 		iov[iov_cnt].iov_base = authkey->data;
601881e2068Shshoexer 		iov[iov_cnt].iov_len = ((authkey->len + 7) / 8) * 8;
602881e2068Shshoexer 		smsg.sadb_msg_len += sa_authkey.sadb_key_len;
603881e2068Shshoexer 		iov_cnt++;
604881e2068Shshoexer 	}
605881e2068Shshoexer 	if (enckey) {
606881e2068Shshoexer 		/* encryption key */
607881e2068Shshoexer 		iov[iov_cnt].iov_base = &sa_enckey;
608881e2068Shshoexer 		iov[iov_cnt].iov_len = sizeof(sa_enckey);
609881e2068Shshoexer 		iov_cnt++;
610881e2068Shshoexer 		iov[iov_cnt].iov_base = enckey->data;
611881e2068Shshoexer 		iov[iov_cnt].iov_len = ((enckey->len + 7) / 8) * 8;
612881e2068Shshoexer 		smsg.sadb_msg_len += sa_enckey.sadb_key_len;
613f032086dShshoexer 		iov_cnt++;
614f032086dShshoexer 	}
615f032086dShshoexer 
616f032086dShshoexer 	len = smsg.sadb_msg_len * 8;
617f032086dShshoexer 	if ((n = writev(sd, iov, iov_cnt)) == -1) {
618f032086dShshoexer 		warn("writev failed");
619f032086dShshoexer 		ret = -1;
620f032086dShshoexer 	} else if (n != len) {
621f032086dShshoexer 		warnx("short write");
622f032086dShshoexer 		ret = -1;
623f032086dShshoexer 	}
624f032086dShshoexer 
625f032086dShshoexer 	return ret;
626f032086dShshoexer }
627f032086dShshoexer 
628f032086dShshoexer static int
629a6bcba92Sbluhm pfkey_sabundle(int sd, u_int8_t satype, u_int8_t satype2, u_int8_t action,
6308065703bShshoexer     struct ipsec_addr_wrap *dst, u_int32_t spi, struct ipsec_addr_wrap *dst2,
6318065703bShshoexer     u_int32_t spi2)
6328f2109caShshoexer {
6338f2109caShshoexer 	struct sadb_msg		smsg;
6348f2109caShshoexer 	struct sadb_sa		sa1, sa2;
6358f2109caShshoexer 	struct sadb_address	sa_dst, sa_dst2;
6368f2109caShshoexer 	struct sockaddr_storage	sdst, sdst2;
6378f2109caShshoexer 	struct sadb_protocol	sa_proto;
6388f2109caShshoexer 	struct iovec		iov[IOV_CNT];
6398f2109caShshoexer 	ssize_t			n;
6408f2109caShshoexer 	int			iov_cnt, len, ret = 0;
6418f2109caShshoexer 
6428f2109caShshoexer 	bzero(&sdst, sizeof(sdst));
6438f2109caShshoexer 	sdst.ss_family = dst->af;
6448f2109caShshoexer 	switch (dst->af) {
6458f2109caShshoexer 	case AF_INET:
6468f2109caShshoexer 		((struct sockaddr_in *)&sdst)->sin_addr = dst->address.v4;
6478f2109caShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in);
6488f2109caShshoexer 		break;
6498f2109caShshoexer 	case AF_INET6:
6508f2109caShshoexer 		((struct sockaddr_in6 *)&sdst)->sin6_addr = dst->address.v6;
6518f2109caShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in6);
6528f2109caShshoexer 		break;
6538f2109caShshoexer 	default:
6548f2109caShshoexer 		warnx("unsupported address family %d", dst->af);
6558f2109caShshoexer 		return -1;
6568f2109caShshoexer 	}
6578f2109caShshoexer 
6588f2109caShshoexer 	bzero(&sdst2, sizeof(sdst2));
6598f2109caShshoexer 	sdst2.ss_family = dst2->af;
6608f2109caShshoexer 	switch (dst2->af) {
6618f2109caShshoexer 	case AF_INET:
6628f2109caShshoexer 		((struct sockaddr_in *)&sdst2)->sin_addr = dst2->address.v4;
6638f2109caShshoexer 		sdst2.ss_len = sizeof(struct sockaddr_in);
6648f2109caShshoexer 		break;
6658f2109caShshoexer 	case AF_INET6:
6668f2109caShshoexer 		((struct sockaddr_in6 *)&sdst2)->sin6_addr = dst2->address.v6;
6678f2109caShshoexer 		sdst2.ss_len = sizeof(struct sockaddr_in6);
6688f2109caShshoexer 		break;
6698f2109caShshoexer 	default:
6708f2109caShshoexer 		warnx("unsupported address family %d", dst2->af);
6718f2109caShshoexer 		return -1;
6728f2109caShshoexer 	}
6738f2109caShshoexer 
6748f2109caShshoexer 	bzero(&smsg, sizeof(smsg));
6758f2109caShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
6768f2109caShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
6778f2109caShshoexer 	smsg.sadb_msg_pid = getpid();
6788f2109caShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
6798f2109caShshoexer 	smsg.sadb_msg_type = action;
6808f2109caShshoexer 	smsg.sadb_msg_satype = satype;
6818f2109caShshoexer 
6828f2109caShshoexer 	bzero(&sa1, sizeof(sa1));
6838f2109caShshoexer 	sa1.sadb_sa_len = sizeof(sa1) / 8;
6848f2109caShshoexer 	sa1.sadb_sa_exttype = SADB_EXT_SA;
6858f2109caShshoexer 	sa1.sadb_sa_spi = htonl(spi);
6868f2109caShshoexer 	sa1.sadb_sa_state = SADB_SASTATE_MATURE;
6878f2109caShshoexer 
6888f2109caShshoexer 	bzero(&sa2, sizeof(sa2));
6898f2109caShshoexer 	sa2.sadb_sa_len = sizeof(sa2) / 8;
6908f2109caShshoexer 	sa2.sadb_sa_exttype = SADB_X_EXT_SA2;
6918f2109caShshoexer 	sa2.sadb_sa_spi = htonl(spi2);
6928f2109caShshoexer 	sa2.sadb_sa_state = SADB_SASTATE_MATURE;
6938f2109caShshoexer 	iov_cnt = 0;
6948f2109caShshoexer 
6958f2109caShshoexer 	bzero(&sa_dst, sizeof(sa_dst));
6968f2109caShshoexer 	sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
6978f2109caShshoexer 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
6988f2109caShshoexer 
6998f2109caShshoexer 	bzero(&sa_dst2, sizeof(sa_dst2));
7008f2109caShshoexer 	sa_dst2.sadb_address_exttype = SADB_X_EXT_DST2;
7018f2109caShshoexer 	sa_dst2.sadb_address_len = (sizeof(sa_dst2) + ROUNDUP(sdst2.ss_len)) / 8;
7028f2109caShshoexer 
7038f2109caShshoexer 	bzero(&sa_proto, sizeof(sa_proto));
704d44c51a1Sbluhm 	sa_proto.sadb_protocol_exttype = SADB_X_EXT_SATYPE2;
7058f2109caShshoexer 	sa_proto.sadb_protocol_len = sizeof(sa_proto) / 8;
7068f2109caShshoexer 	sa_proto.sadb_protocol_direction = 0;
7078065703bShshoexer 	sa_proto.sadb_protocol_proto = satype2;
7088f2109caShshoexer 
7098f2109caShshoexer 	/* header */
7108f2109caShshoexer 	iov[iov_cnt].iov_base = &smsg;
7118f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
7128f2109caShshoexer 	iov_cnt++;
7138f2109caShshoexer 
7148f2109caShshoexer 	/* sa */
7158f2109caShshoexer 	iov[iov_cnt].iov_base = &sa1;
7168f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa1);
7178f2109caShshoexer 	smsg.sadb_msg_len += sa1.sadb_sa_len;
7188f2109caShshoexer 	iov_cnt++;
7198f2109caShshoexer 
7208f2109caShshoexer 	/* dst addr */
7218f2109caShshoexer 	iov[iov_cnt].iov_base = &sa_dst;
7228f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst);
7238f2109caShshoexer 	iov_cnt++;
7248f2109caShshoexer 	iov[iov_cnt].iov_base = &sdst;
7258f2109caShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
7268f2109caShshoexer 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
7278f2109caShshoexer 	iov_cnt++;
7288f2109caShshoexer 
7298f2109caShshoexer 	/* second sa */
7308f2109caShshoexer 	iov[iov_cnt].iov_base = &sa2;
7318f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa2);
7328f2109caShshoexer 	smsg.sadb_msg_len += sa2.sadb_sa_len;
7338f2109caShshoexer 	iov_cnt++;
7348f2109caShshoexer 
7358f2109caShshoexer 	/* second dst addr */
7368f2109caShshoexer 	iov[iov_cnt].iov_base = &sa_dst2;
7378f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst2);
7388f2109caShshoexer 	iov_cnt++;
7398f2109caShshoexer 	iov[iov_cnt].iov_base = &sdst2;
7408f2109caShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst2.ss_len);
7418f2109caShshoexer 	smsg.sadb_msg_len += sa_dst2.sadb_address_len;
7428f2109caShshoexer 	iov_cnt++;
7438f2109caShshoexer 
7448f2109caShshoexer 	/* SA type */
7458f2109caShshoexer 	iov[iov_cnt].iov_base = &sa_proto;
7468f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_proto);
7478f2109caShshoexer 	smsg.sadb_msg_len += sa_proto.sadb_protocol_len;
7488f2109caShshoexer 	iov_cnt++;
7498f2109caShshoexer 
7508f2109caShshoexer 	len = smsg.sadb_msg_len * 8;
7518f2109caShshoexer 	if ((n = writev(sd, iov, iov_cnt)) == -1) {
7528f2109caShshoexer 		warn("writev failed");
7538f2109caShshoexer 		ret = -1;
7548f2109caShshoexer 	} else if (n != len) {
7558f2109caShshoexer 		warnx("short write");
7568f2109caShshoexer 		ret = -1;
7578f2109caShshoexer 	}
7588f2109caShshoexer 
7598f2109caShshoexer 	return (ret);
7608f2109caShshoexer }
7618f2109caShshoexer 
7628f2109caShshoexer static int
7638a87fca6Smsf pfkey_reply(int sd, u_int8_t **datap, ssize_t *lenp)
764f484f2cfShshoexer {
765f484f2cfShshoexer 	struct sadb_msg	 hdr;
766f484f2cfShshoexer 	ssize_t		 len;
767f484f2cfShshoexer 	u_int8_t	*data;
768f484f2cfShshoexer 
769f484f2cfShshoexer 	if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
770f484f2cfShshoexer 		warnx("short read");
771f484f2cfShshoexer 		return -1;
772f484f2cfShshoexer 	}
773f484f2cfShshoexer 	len = hdr.sadb_msg_len * PFKEYV2_CHUNK;
774f484f2cfShshoexer 	if ((data = malloc(len)) == NULL)
775bd828a90Shshoexer 		err(1, "pfkey_reply: malloc");
776f484f2cfShshoexer 	if (read(sd, data, len) != len) {
777f484f2cfShshoexer 		warn("PF_KEY short read");
7781e82d711Sderaadt 		freezero(data, len);
779f484f2cfShshoexer 		return -1;
780f484f2cfShshoexer 	}
7818a87fca6Smsf 	if (datap) {
7828a87fca6Smsf 		*datap = data;
7838a87fca6Smsf 		if (lenp)
7848a87fca6Smsf 			*lenp = len;
7858a87fca6Smsf 	} else {
7861e82d711Sderaadt 		freezero(data, len);
7878a87fca6Smsf 	}
788011122f7Smarkus 	if (datap == NULL && hdr.sadb_msg_errno != 0) {
789011122f7Smarkus 		errno = hdr.sadb_msg_errno;
79092270c32Shshoexer 		if (errno != EEXIST) {
791011122f7Smarkus 			warn("PF_KEY failed");
792011122f7Smarkus 			return -1;
793011122f7Smarkus 		}
79492270c32Shshoexer 	}
795f484f2cfShshoexer 	return 0;
796f484f2cfShshoexer }
797f484f2cfShshoexer 
798f484f2cfShshoexer int
7991edc1b9aShshoexer pfkey_parse(struct sadb_msg *msg, struct ipsec_rule *rule)
8001edc1b9aShshoexer {
8011edc1b9aShshoexer 	struct sadb_ext		*ext;
8021edc1b9aShshoexer 	struct sadb_address	*saddr;
8031edc1b9aShshoexer 	struct sadb_protocol	*sproto;
8041edc1b9aShshoexer 	struct sadb_ident	*sident;
8051edc1b9aShshoexer 	struct sockaddr		*sa;
806712e78baShshoexer 	struct sockaddr_in	*sa_in;
8072099bcdfStodd 	struct sockaddr_in6	*sa_in6;
8081edc1b9aShshoexer 	int			 len;
8091edc1b9aShshoexer 
8101edc1b9aShshoexer 	switch (msg->sadb_msg_satype) {
811b26a6cb5Shshoexer 	case SADB_SATYPE_ESP:
8129182219dSmarkus 		rule->satype = IPSEC_ESP;
8131edc1b9aShshoexer 		break;
814b26a6cb5Shshoexer 	case SADB_SATYPE_AH:
8159182219dSmarkus 		rule->satype = IPSEC_AH;
8161edc1b9aShshoexer 		break;
817b26a6cb5Shshoexer 	case SADB_X_SATYPE_IPCOMP:
8189182219dSmarkus 		rule->satype = IPSEC_IPCOMP;
819a29da9d0Shshoexer 		break;
82088a8cceeSmarkus 	case SADB_X_SATYPE_IPIP:
8219182219dSmarkus 		rule->satype = IPSEC_IPIP;
82288a8cceeSmarkus 		break;
8231edc1b9aShshoexer 	default:
8241edc1b9aShshoexer 		return (1);
8251edc1b9aShshoexer 	}
8261edc1b9aShshoexer 
8271edc1b9aShshoexer 	for (ext = (struct sadb_ext *)(msg + 1);
8281edc1b9aShshoexer 	    (size_t)((u_int8_t *)ext - (u_int8_t *)msg) <
829d5d1799eShshoexer 	    msg->sadb_msg_len * PFKEYV2_CHUNK && ext->sadb_ext_len > 0;
8301edc1b9aShshoexer 	    ext = (struct sadb_ext *)((u_int8_t *)ext +
8311edc1b9aShshoexer 	    ext->sadb_ext_len * PFKEYV2_CHUNK)) {
8321edc1b9aShshoexer 		switch (ext->sadb_ext_type) {
8331edc1b9aShshoexer 		case SADB_EXT_ADDRESS_SRC:
8341edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
8351edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
8361edc1b9aShshoexer 
83791f765ddShshoexer 			rule->local = calloc(1, sizeof(struct ipsec_addr_wrap));
838d5d1799eShshoexer 			if (rule->local == NULL)
839a1059f6aStodd 				err(1, "pfkey_parse: calloc");
8401edc1b9aShshoexer 
8412099bcdfStodd 			rule->local->af = sa->sa_family;
8421edc1b9aShshoexer 			switch (sa->sa_family) {
8431edc1b9aShshoexer 			case AF_INET:
8441edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
84591f765ddShshoexer 				    &rule->local->address.v4,
846712e78baShshoexer 				    sizeof(struct in_addr));
8472099bcdfStodd 				set_ipmask(rule->local, 32);
8482099bcdfStodd 				break;
8492099bcdfStodd 			case AF_INET6:
8502099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
8512099bcdfStodd 				    &rule->local->address.v6,
8522099bcdfStodd 				    sizeof(struct in6_addr));
8532099bcdfStodd 				set_ipmask(rule->local, 128);
8541edc1b9aShshoexer 				break;
8551edc1b9aShshoexer 			default:
8561edc1b9aShshoexer 				return (1);
8571edc1b9aShshoexer 			}
8581edc1b9aShshoexer 			break;
8591edc1b9aShshoexer 
8601edc1b9aShshoexer 
8611edc1b9aShshoexer 		case SADB_EXT_ADDRESS_DST:
8621edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
8631edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
8641edc1b9aShshoexer 
86591f765ddShshoexer 			rule->peer = calloc(1, sizeof(struct ipsec_addr_wrap));
8661edc1b9aShshoexer 			if (rule->peer == NULL)
867a1059f6aStodd 				err(1, "pfkey_parse: calloc");
8681edc1b9aShshoexer 
8692099bcdfStodd 			rule->peer->af = sa->sa_family;
8701edc1b9aShshoexer 			switch (sa->sa_family) {
8711edc1b9aShshoexer 			case AF_INET:
8721edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
873712e78baShshoexer 				    &rule->peer->address.v4,
874712e78baShshoexer 				    sizeof(struct in_addr));
8752099bcdfStodd 				set_ipmask(rule->peer, 32);
8762099bcdfStodd 				break;
8772099bcdfStodd 			case AF_INET6:
8782099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
8792099bcdfStodd 				    &rule->peer->address.v6,
8802099bcdfStodd 				    sizeof(struct in6_addr));
8812099bcdfStodd 				set_ipmask(rule->peer, 128);
8821edc1b9aShshoexer 				break;
8831edc1b9aShshoexer 			default:
8841edc1b9aShshoexer 				return (1);
8851edc1b9aShshoexer 			}
8861edc1b9aShshoexer 			break;
8871edc1b9aShshoexer 
8881edc1b9aShshoexer 		case SADB_EXT_IDENTITY_SRC:
8891edc1b9aShshoexer 			sident = (struct sadb_ident *)ext;
8901edc1b9aShshoexer 			len = (sident->sadb_ident_len * sizeof(uint64_t)) -
8911edc1b9aShshoexer 			    sizeof(struct sadb_ident);
8921edc1b9aShshoexer 
8939382ed8fShshoexer 			if (rule->auth == NULL) {
8949382ed8fShshoexer 				rule->auth = calloc(1, sizeof(struct
8959382ed8fShshoexer 				    ipsec_auth));
8969382ed8fShshoexer 				if (rule->auth == NULL)
897bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
8989382ed8fShshoexer 			}
8999382ed8fShshoexer 
900abe65127Shshoexer 			rule->auth->srcid = calloc(1, len);
901abe65127Shshoexer 			if (rule->auth->srcid == NULL)
902bd828a90Shshoexer 				err(1, "pfkey_parse: calloc");
9031edc1b9aShshoexer 
904abe65127Shshoexer 			strlcpy(rule->auth->srcid, (char *)(sident + 1), len);
9051edc1b9aShshoexer 			break;
9061edc1b9aShshoexer 
9071edc1b9aShshoexer 		case SADB_EXT_IDENTITY_DST:
9081edc1b9aShshoexer 			sident = (struct sadb_ident *)ext;
9091edc1b9aShshoexer 			len = (sident->sadb_ident_len * sizeof(uint64_t)) -
9101edc1b9aShshoexer 			    sizeof(struct sadb_ident);
9111edc1b9aShshoexer 
9129382ed8fShshoexer 			if (rule->auth == NULL) {
9139382ed8fShshoexer 				rule->auth = calloc(1, sizeof(struct
9149382ed8fShshoexer 				    ipsec_auth));
9159382ed8fShshoexer 				if (rule->auth == NULL)
916bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
9179382ed8fShshoexer 			}
9189382ed8fShshoexer 
919abe65127Shshoexer 			rule->auth->dstid = calloc(1, len);
920abe65127Shshoexer 			if (rule->auth->dstid == NULL)
921bd828a90Shshoexer 				err(1, "pfkey_parse: calloc");
9221edc1b9aShshoexer 
923abe65127Shshoexer 			strlcpy(rule->auth->dstid, (char *)(sident + 1), len);
9241edc1b9aShshoexer 			break;
9251edc1b9aShshoexer 
9261edc1b9aShshoexer 		case SADB_X_EXT_PROTOCOL:
9279182219dSmarkus 			sproto = (struct sadb_protocol *)ext;
9289182219dSmarkus 			if (sproto->sadb_protocol_direction == 0)
9299182219dSmarkus 				rule->proto = sproto->sadb_protocol_proto;
9301edc1b9aShshoexer 			break;
9311edc1b9aShshoexer 
9321edc1b9aShshoexer 		case SADB_X_EXT_FLOW_TYPE:
9331edc1b9aShshoexer 			sproto = (struct sadb_protocol *)ext;
9341edc1b9aShshoexer 
9351edc1b9aShshoexer 			switch (sproto->sadb_protocol_direction) {
9361edc1b9aShshoexer 			case IPSP_DIRECTION_IN:
9371edc1b9aShshoexer 				rule->direction = IPSEC_IN;
9381edc1b9aShshoexer 				break;
9391edc1b9aShshoexer 			case IPSP_DIRECTION_OUT:
9401edc1b9aShshoexer 				rule->direction = IPSEC_OUT;
9411edc1b9aShshoexer 				break;
9421edc1b9aShshoexer 			default:
9431edc1b9aShshoexer 				return (1);
9441edc1b9aShshoexer 			}
9456122c05eShshoexer 			switch (sproto->sadb_protocol_proto) {
9466122c05eShshoexer 			case SADB_X_FLOW_TYPE_USE:
9471a3f035aShshoexer 				rule->flowtype = TYPE_USE;
9486122c05eShshoexer 				break;
9496122c05eShshoexer 			case SADB_X_FLOW_TYPE_ACQUIRE:
9501a3f035aShshoexer 				rule->flowtype = TYPE_ACQUIRE;
9516122c05eShshoexer 				break;
9526122c05eShshoexer 			case SADB_X_FLOW_TYPE_REQUIRE:
9531a3f035aShshoexer 				rule->flowtype = TYPE_REQUIRE;
9546122c05eShshoexer 				break;
9556122c05eShshoexer 			case SADB_X_FLOW_TYPE_DENY:
9561a3f035aShshoexer 				rule->flowtype = TYPE_DENY;
9576122c05eShshoexer 				break;
9586122c05eShshoexer 			case SADB_X_FLOW_TYPE_BYPASS:
9591a3f035aShshoexer 				rule->flowtype = TYPE_BYPASS;
9606122c05eShshoexer 				break;
9616122c05eShshoexer 			case SADB_X_FLOW_TYPE_DONTACQ:
9621a3f035aShshoexer 				rule->flowtype = TYPE_DONTACQ;
9636122c05eShshoexer 				break;
9646122c05eShshoexer 			default:
9651a3f035aShshoexer 				rule->flowtype = TYPE_UNKNOWN;
9666122c05eShshoexer 				break;
9676122c05eShshoexer 			}
9681edc1b9aShshoexer 			break;
9691edc1b9aShshoexer 
9701edc1b9aShshoexer 		case SADB_X_EXT_SRC_FLOW:
9711edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
9721edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
9731edc1b9aShshoexer 
9741edc1b9aShshoexer 			if (rule->src == NULL) {
9751edc1b9aShshoexer 				rule->src = calloc(1,
97691f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
9771edc1b9aShshoexer 				if (rule->src == NULL)
978bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
9791edc1b9aShshoexer 			}
9801edc1b9aShshoexer 
9812099bcdfStodd 			rule->src->af = sa->sa_family;
9821edc1b9aShshoexer 			switch (sa->sa_family) {
9831edc1b9aShshoexer 			case AF_INET:
9841edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
985712e78baShshoexer 				    &rule->src->address.v4,
986712e78baShshoexer 				    sizeof(struct in_addr));
98757f58d0dSnaddy 				rule->sport =
98857f58d0dSnaddy 				    ((struct sockaddr_in *)sa)->sin_port;
9892099bcdfStodd 				break;
9902099bcdfStodd 			case AF_INET6:
9912099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
9922099bcdfStodd 				    &rule->src->address.v6,
9932099bcdfStodd 				    sizeof(struct in6_addr));
99457f58d0dSnaddy 				rule->sport =
99557f58d0dSnaddy 				    ((struct sockaddr_in6 *)sa)->sin6_port;
9961edc1b9aShshoexer 				break;
9971edc1b9aShshoexer 			default:
9981edc1b9aShshoexer 				return (1);
9991edc1b9aShshoexer 			}
10001edc1b9aShshoexer 			break;
10011edc1b9aShshoexer 
10021edc1b9aShshoexer 		case SADB_X_EXT_DST_FLOW:
10031edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
10041edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
10051edc1b9aShshoexer 
10061edc1b9aShshoexer 			if (rule->dst == NULL) {
10071edc1b9aShshoexer 				rule->dst = calloc(1,
100891f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
10091edc1b9aShshoexer 				if (rule->dst == NULL)
1010bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
10111edc1b9aShshoexer 			}
10121edc1b9aShshoexer 
10132099bcdfStodd 			rule->dst->af = sa->sa_family;
10141edc1b9aShshoexer 			switch (sa->sa_family) {
10151edc1b9aShshoexer 			case AF_INET:
10161edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
1017712e78baShshoexer 				    &rule->dst->address.v4,
1018712e78baShshoexer 				    sizeof(struct in_addr));
101957f58d0dSnaddy 				rule->dport =
102057f58d0dSnaddy 				    ((struct sockaddr_in *)sa)->sin_port;
10211edc1b9aShshoexer 				break;
10222099bcdfStodd 			case AF_INET6:
10232099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
10242099bcdfStodd 				    &rule->dst->address.v6,
10252099bcdfStodd 				    sizeof(struct in6_addr));
102657f58d0dSnaddy 				rule->dport =
102757f58d0dSnaddy 				    ((struct sockaddr_in6 *)sa)->sin6_port;
10282099bcdfStodd 				break;
10291edc1b9aShshoexer 			default:
10301edc1b9aShshoexer 				return (1);
10311edc1b9aShshoexer 			}
10321edc1b9aShshoexer 			break;
10331edc1b9aShshoexer 
10341edc1b9aShshoexer 
10351edc1b9aShshoexer 		case SADB_X_EXT_SRC_MASK:
10361edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
10371edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
10381edc1b9aShshoexer 
10391edc1b9aShshoexer 			if (rule->src == NULL) {
10401edc1b9aShshoexer 				rule->src = calloc(1,
104191f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
10421edc1b9aShshoexer 				if (rule->src == NULL)
1043bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
10441edc1b9aShshoexer 			}
10451edc1b9aShshoexer 
10462099bcdfStodd 			rule->src->af = sa->sa_family;
10471edc1b9aShshoexer 			switch (sa->sa_family) {
10481edc1b9aShshoexer 			case AF_INET:
1049712e78baShshoexer 				sa_in = (struct sockaddr_in *)sa;
1050712e78baShshoexer 				bcopy(&sa_in->sin_addr, &rule->src->mask.v4,
10511edc1b9aShshoexer 				    sizeof(struct in_addr));
10522099bcdfStodd 				break;
10532099bcdfStodd 			case AF_INET6:
10542099bcdfStodd 				sa_in6 = (struct sockaddr_in6 *)sa;
10552099bcdfStodd 				bcopy(&sa_in6->sin6_addr, &rule->src->mask.v6,
10562099bcdfStodd 				    sizeof(struct in6_addr));
10571edc1b9aShshoexer 				break;
10581edc1b9aShshoexer 
10591edc1b9aShshoexer 			default:
10601edc1b9aShshoexer 				return (1);
10611edc1b9aShshoexer 			}
10621edc1b9aShshoexer 			break;
10631edc1b9aShshoexer 
10641edc1b9aShshoexer 		case SADB_X_EXT_DST_MASK:
10651edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
10661edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
10671edc1b9aShshoexer 
10681edc1b9aShshoexer 			if (rule->dst == NULL) {
10691edc1b9aShshoexer 				rule->dst = calloc(1,
107091f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
10711edc1b9aShshoexer 				if (rule->dst == NULL)
1072bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
10731edc1b9aShshoexer 			}
10741edc1b9aShshoexer 
10752099bcdfStodd 			rule->dst->af = sa->sa_family;
10761edc1b9aShshoexer 			switch (sa->sa_family) {
10771edc1b9aShshoexer 			case AF_INET:
1078712e78baShshoexer 				sa_in = (struct sockaddr_in *)sa;
1079712e78baShshoexer 				bcopy(&sa_in->sin_addr, &rule->dst->mask.v4,
10801edc1b9aShshoexer 				    sizeof(struct in_addr));
10811edc1b9aShshoexer 				break;
10822099bcdfStodd 			case AF_INET6:
10832099bcdfStodd 				sa_in6 = (struct sockaddr_in6 *)sa;
10842099bcdfStodd 				bcopy(&sa_in6->sin6_addr, &rule->dst->mask.v6,
10852099bcdfStodd 				    sizeof(struct in6_addr));
10862099bcdfStodd 				break;
10871edc1b9aShshoexer 			default:
10881edc1b9aShshoexer 				return (1);
10891edc1b9aShshoexer 			}
10901edc1b9aShshoexer 			break;
10911edc1b9aShshoexer 
10921edc1b9aShshoexer 		default:
10931edc1b9aShshoexer 			return (1);
10941edc1b9aShshoexer 		}
10951edc1b9aShshoexer 	}
10961edc1b9aShshoexer 
10971edc1b9aShshoexer 	return (0);
10981edc1b9aShshoexer }
10991edc1b9aShshoexer 
11001edc1b9aShshoexer int
1101356121f6Shshoexer pfkey_ipsec_establish(int action, struct ipsec_rule *r)
1102f484f2cfShshoexer {
110322a29ad6Shshoexer 	int		ret;
11048065703bShshoexer 	u_int8_t	satype, satype2, direction;
1105f484f2cfShshoexer 
1106f032086dShshoexer 	if (r->type == RULE_FLOW) {
11079182219dSmarkus 		switch (r->satype) {
1108f484f2cfShshoexer 		case IPSEC_ESP:
1109f484f2cfShshoexer 			satype = SADB_SATYPE_ESP;
1110f484f2cfShshoexer 			break;
1111f484f2cfShshoexer 		case IPSEC_AH:
1112f484f2cfShshoexer 			satype = SADB_SATYPE_AH;
1113f484f2cfShshoexer 			break;
111472e25333Shshoexer 		case IPSEC_IPCOMP:
111572e25333Shshoexer 			satype = SADB_X_SATYPE_IPCOMP;
111672e25333Shshoexer 			break;
111788a8cceeSmarkus 		case IPSEC_IPIP:
111888a8cceeSmarkus 			satype = SADB_X_SATYPE_IPIP;
111988a8cceeSmarkus 			break;
1120f484f2cfShshoexer 		default:
1121f484f2cfShshoexer 			return -1;
1122f484f2cfShshoexer 		}
1123f484f2cfShshoexer 
1124f484f2cfShshoexer 		switch (r->direction) {
1125f484f2cfShshoexer 		case IPSEC_IN:
1126f484f2cfShshoexer 			direction = IPSP_DIRECTION_IN;
1127f484f2cfShshoexer 			break;
1128f484f2cfShshoexer 		case IPSEC_OUT:
1129f484f2cfShshoexer 			direction = IPSP_DIRECTION_OUT;
1130f484f2cfShshoexer 			break;
1131f484f2cfShshoexer 		default:
1132f484f2cfShshoexer 			return -1;
1133f484f2cfShshoexer 		}
1134f484f2cfShshoexer 
113522a29ad6Shshoexer 		switch (action) {
113690bd57a7Shshoexer 		case ACTION_ADD:
1137f032086dShshoexer 			ret = pfkey_flow(fd, satype, SADB_X_ADDFLOW, direction,
113857f58d0dSnaddy 			    r->proto, r->src, r->sport, r->dst, r->dport,
113957f58d0dSnaddy 			    r->local, r->peer, r->auth, r->flowtype);
114022a29ad6Shshoexer 			break;
114190bd57a7Shshoexer 		case ACTION_DELETE:
114222a29ad6Shshoexer 			/* No peer for flow deletion. */
1143f032086dShshoexer 			ret = pfkey_flow(fd, satype, SADB_X_DELFLOW, direction,
114457f58d0dSnaddy 			    r->proto, r->src, r->sport, r->dst, r->dport,
114557f58d0dSnaddy 			    NULL, NULL, NULL, r->flowtype);
114622a29ad6Shshoexer 			break;
114722a29ad6Shshoexer 		default:
114822a29ad6Shshoexer 			return -1;
114922a29ad6Shshoexer 		}
1150f032086dShshoexer 	} else if (r->type == RULE_SA) {
11519182219dSmarkus 		switch (r->satype) {
1152881e2068Shshoexer 		case IPSEC_AH:
1153881e2068Shshoexer 			satype = SADB_SATYPE_AH;
1154881e2068Shshoexer 			break;
1155881e2068Shshoexer 		case IPSEC_ESP:
1156881e2068Shshoexer 			satype = SADB_SATYPE_ESP;
1157881e2068Shshoexer 			break;
115872e25333Shshoexer 		case IPSEC_IPCOMP:
115972e25333Shshoexer 			satype = SADB_X_SATYPE_IPCOMP;
116072e25333Shshoexer 			break;
1161381a2422Shshoexer 		case IPSEC_TCPMD5:
1162f032086dShshoexer 			satype = SADB_X_SATYPE_TCPSIGNATURE;
1163381a2422Shshoexer 			break;
116488a8cceeSmarkus 		case IPSEC_IPIP:
116588a8cceeSmarkus 			satype = SADB_X_SATYPE_IPIP;
116688a8cceeSmarkus 			break;
1167381a2422Shshoexer 		default:
1168381a2422Shshoexer 			return -1;
1169381a2422Shshoexer 		}
1170f032086dShshoexer 		switch (action) {
117190bd57a7Shshoexer 		case ACTION_ADD:
1172f032086dShshoexer 			ret = pfkey_sa(fd, satype, SADB_ADD, r->spi,
1173a38d220fShshoexer 			    r->src, r->dst, r->xfs, r->authkey, r->enckey,
1174a38d220fShshoexer 			    r->tmode);
1175f032086dShshoexer 			break;
117690bd57a7Shshoexer 		case ACTION_DELETE:
1177f032086dShshoexer 			ret = pfkey_sa(fd, satype, SADB_DELETE, r->spi,
1178a38d220fShshoexer 			    r->src, r->dst, r->xfs, NULL, NULL, r->tmode);
1179f032086dShshoexer 			break;
1180f032086dShshoexer 		default:
1181f032086dShshoexer 			return -1;
1182f032086dShshoexer 		}
1183a6bcba92Sbluhm 	} else if (r->type == RULE_BUNDLE) {
11848f2109caShshoexer 		switch (r->satype) {
11858f2109caShshoexer 		case IPSEC_AH:
11868f2109caShshoexer 			satype = SADB_SATYPE_AH;
11878f2109caShshoexer 			break;
11888f2109caShshoexer 		case IPSEC_ESP:
11898f2109caShshoexer 			satype = SADB_SATYPE_ESP;
11908f2109caShshoexer 			break;
11918f2109caShshoexer 		case IPSEC_IPCOMP:
11928f2109caShshoexer 			satype = SADB_X_SATYPE_IPCOMP;
11938f2109caShshoexer 			break;
11948f2109caShshoexer 		case IPSEC_TCPMD5:
11958f2109caShshoexer 			satype = SADB_X_SATYPE_TCPSIGNATURE;
11968f2109caShshoexer 			break;
11978f2109caShshoexer 		case IPSEC_IPIP:
11988f2109caShshoexer 			satype = SADB_X_SATYPE_IPIP;
11998f2109caShshoexer 			break;
12008f2109caShshoexer 		default:
12018f2109caShshoexer 			return -1;
12028f2109caShshoexer 		}
12038065703bShshoexer 		switch (r->proto2) {
12048065703bShshoexer 		case IPSEC_AH:
12058065703bShshoexer 			satype2 = SADB_SATYPE_AH;
12068065703bShshoexer 			break;
12078065703bShshoexer 		case IPSEC_ESP:
12088065703bShshoexer 			satype2 = SADB_SATYPE_ESP;
12098065703bShshoexer 			break;
12108065703bShshoexer 		case IPSEC_IPCOMP:
12118065703bShshoexer 			satype2 = SADB_X_SATYPE_IPCOMP;
12128065703bShshoexer 			break;
12138065703bShshoexer 		case IPSEC_TCPMD5:
12148065703bShshoexer 			satype2 = SADB_X_SATYPE_TCPSIGNATURE;
12158065703bShshoexer 			break;
12168065703bShshoexer 		case IPSEC_IPIP:
12178065703bShshoexer 			satype2 = SADB_X_SATYPE_IPIP;
12188065703bShshoexer 			break;
12198065703bShshoexer 		default:
12208065703bShshoexer 			return -1;
12218065703bShshoexer 		}
12228f2109caShshoexer 		switch (action) {
12238f2109caShshoexer 		case ACTION_ADD:
1224a6bcba92Sbluhm 			ret = pfkey_sabundle(fd, satype, satype2,
12258065703bShshoexer 			    SADB_X_GRPSPIS, r->dst, r->spi, r->dst2, r->spi2);
12268f2109caShshoexer 			break;
12278f2109caShshoexer 		case ACTION_DELETE:
12288f2109caShshoexer 			return 0;
12298f2109caShshoexer 		default:
12308f2109caShshoexer 			return -1;
12318f2109caShshoexer 		}
1232f032086dShshoexer 	} else
1233f032086dShshoexer 		return -1;
1234f032086dShshoexer 
123522a29ad6Shshoexer 	if (ret < 0)
1236f484f2cfShshoexer 		return -1;
12378a87fca6Smsf 	if (pfkey_reply(fd, NULL, NULL) < 0)
1238f484f2cfShshoexer 		return -1;
1239f484f2cfShshoexer 
1240f484f2cfShshoexer 	return 0;
1241f484f2cfShshoexer }
1242f484f2cfShshoexer 
1243f484f2cfShshoexer int
1244f484f2cfShshoexer pfkey_ipsec_flush(void)
1245f484f2cfShshoexer {
1246f484f2cfShshoexer 	struct sadb_msg smsg;
1247f484f2cfShshoexer 	struct iovec	iov[IOV_CNT];
1248f484f2cfShshoexer 	ssize_t		n;
1249f484f2cfShshoexer 	int		iov_cnt, len;
1250f484f2cfShshoexer 
1251f484f2cfShshoexer 	bzero(&smsg, sizeof(smsg));
1252f484f2cfShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
1253f484f2cfShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
1254f484f2cfShshoexer 	smsg.sadb_msg_pid = getpid();
1255f484f2cfShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
1256f484f2cfShshoexer 	smsg.sadb_msg_type = SADB_FLUSH;
1257f484f2cfShshoexer 	smsg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
1258f484f2cfShshoexer 
1259f484f2cfShshoexer 	iov_cnt = 0;
1260f484f2cfShshoexer 
1261f484f2cfShshoexer 	iov[iov_cnt].iov_base = &smsg;
1262f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
1263f484f2cfShshoexer 	iov_cnt++;
1264f484f2cfShshoexer 
1265f484f2cfShshoexer 	len = smsg.sadb_msg_len * 8;
1266f484f2cfShshoexer 	if ((n = writev(fd, iov, iov_cnt)) == -1) {
1267f484f2cfShshoexer 		warn("writev failed");
1268f484f2cfShshoexer 		return -1;
1269f484f2cfShshoexer 	}
1270f484f2cfShshoexer 	if (n != len) {
1271f484f2cfShshoexer 		warnx("short write");
1272f484f2cfShshoexer 		return -1;
1273f484f2cfShshoexer 	}
12748a87fca6Smsf 	if (pfkey_reply(fd, NULL, NULL) < 0)
1275f484f2cfShshoexer 		return -1;
1276f484f2cfShshoexer 
1277f484f2cfShshoexer 	return 0;
1278f484f2cfShshoexer }
1279f484f2cfShshoexer 
12808a87fca6Smsf static int
12818a87fca6Smsf pfkey_promisc(void)
12828a87fca6Smsf {
12838a87fca6Smsf 	struct sadb_msg msg;
12848a87fca6Smsf 
12858a87fca6Smsf 	memset(&msg, 0, sizeof(msg));
12868a87fca6Smsf 	msg.sadb_msg_version = PF_KEY_V2;
12878a87fca6Smsf 	msg.sadb_msg_seq = sadb_msg_seq++;
12888a87fca6Smsf 	msg.sadb_msg_pid = getpid();
12898a87fca6Smsf 	msg.sadb_msg_len = sizeof(msg) / PFKEYV2_CHUNK;
12908a87fca6Smsf 	msg.sadb_msg_type = SADB_X_PROMISC;
12918a87fca6Smsf 	msg.sadb_msg_satype = 1;	/* enable */
12928a87fca6Smsf 	if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) {
12938a87fca6Smsf 		warn("pfkey_promisc: write failed");
12948a87fca6Smsf 		return -1;
12958a87fca6Smsf 	}
12968a87fca6Smsf 	if (pfkey_reply(fd, NULL, NULL) < 0)
12978a87fca6Smsf 		return -1;
12988a87fca6Smsf 	return 0;
12998a87fca6Smsf }
13008a87fca6Smsf 
13018a87fca6Smsf int
13028a87fca6Smsf pfkey_monitor(int opts)
13038a87fca6Smsf {
130456232324Sderaadt 	struct pollfd pfd[1];
13058a87fca6Smsf 	struct sadb_msg *msg;
130656232324Sderaadt 	u_int8_t *data;
130756232324Sderaadt 	ssize_t len;
13088a87fca6Smsf 	int n;
13098a87fca6Smsf 
13108a87fca6Smsf 	if (pfkey_init() < 0)
13118a87fca6Smsf 		return -1;
13128a87fca6Smsf 	if (pfkey_promisc() < 0)
13138a87fca6Smsf 		return -1;
13148a87fca6Smsf 
131556232324Sderaadt 	pfd[0].fd = fd;
131656232324Sderaadt 	pfd[0].events = POLLIN;
13178a87fca6Smsf 	for (;;) {
1318*df69c215Sderaadt 		if ((n = poll(pfd, 1, -1)) == -1)
131956232324Sderaadt 			err(2, "poll");
13208a87fca6Smsf 		if (n == 0)
13218a87fca6Smsf 			break;
132256232324Sderaadt 		if ((pfd[0].revents & POLLIN) == 0)
13238a87fca6Smsf 			continue;
13248a87fca6Smsf 		if (pfkey_reply(fd, &data, &len) < 0)
13258a87fca6Smsf 			continue;
13268a87fca6Smsf 		msg = (struct sadb_msg *)data;
13278a87fca6Smsf 		if (msg->sadb_msg_type == SADB_X_PROMISC) {
13288a87fca6Smsf 			/* remove extra header from promisc messages */
13298a87fca6Smsf 			if ((msg->sadb_msg_len * PFKEYV2_CHUNK) >=
13308a87fca6Smsf 			    2 * sizeof(struct sadb_msg)) {
13318a87fca6Smsf 				msg++;
13328a87fca6Smsf 			}
13338a87fca6Smsf 		}
13348a87fca6Smsf 		pfkey_monitor_sa(msg, opts);
13358a87fca6Smsf 		if (opts & IPSECCTL_OPT_VERBOSE)
13368a87fca6Smsf 			pfkey_print_raw(data, len);
13371e82d711Sderaadt 		freezero(data, len);
13388a87fca6Smsf 	}
13398a87fca6Smsf 	close(fd);
13408a87fca6Smsf 	return 0;
13418a87fca6Smsf }
13428a87fca6Smsf 
1343f484f2cfShshoexer int
1344f484f2cfShshoexer pfkey_init(void)
1345f484f2cfShshoexer {
1346f484f2cfShshoexer 	if ((fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) == -1)
1347bd828a90Shshoexer 		err(1, "pfkey_init: failed to open PF_KEY socket");
1348f484f2cfShshoexer 
1349f484f2cfShshoexer 	return 0;
1350f484f2cfShshoexer }
1351