xref: /openbsd/sbin/ipsecctl/pfkey.c (revision 92270c32)
1*92270c32Shshoexer /*	$OpenBSD: pfkey.c,v 1.49 2008/12/22 17:00:37 hshoexer 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>
33f484f2cfShshoexer #include <unistd.h>
34f484f2cfShshoexer 
35f484f2cfShshoexer #include "ipsecctl.h"
361edc1b9aShshoexer #include "pfkey.h"
37f484f2cfShshoexer 
38f484f2cfShshoexer #define ROUNDUP(x) (((x) + (PFKEYV2_CHUNK - 1)) & ~(PFKEYV2_CHUNK - 1))
39f484f2cfShshoexer #define IOV_CNT 20
40f484f2cfShshoexer 
41f484f2cfShshoexer static int	fd;
42f484f2cfShshoexer static u_int32_t sadb_msg_seq = 1;
43f484f2cfShshoexer 
449182219dSmarkus static int	pfkey_flow(int, u_int8_t, u_int8_t, u_int8_t, u_int8_t,
4557f58d0dSnaddy 		    struct ipsec_addr_wrap *, u_int16_t,
4657f58d0dSnaddy 		    struct ipsec_addr_wrap *, u_int16_t,
47435bb41eSmarkus 		    struct ipsec_addr_wrap *, struct ipsec_addr_wrap *,
48435bb41eSmarkus 		    struct ipsec_auth *, u_int8_t);
49f032086dShshoexer static int	pfkey_sa(int, u_int8_t, u_int8_t, u_int32_t,
5091f765ddShshoexer 		    struct ipsec_addr_wrap *, struct ipsec_addr_wrap *,
51375db29dShshoexer 		    struct ipsec_transforms *, struct ipsec_key *,
52a38d220fShshoexer 		    struct ipsec_key *, u_int8_t);
538065703bShshoexer static int	pfkey_sagroup(int, u_int8_t, u_int8_t, u_int8_t,
548065703bShshoexer 		    struct ipsec_addr_wrap *, u_int32_t,
558065703bShshoexer 		    struct ipsec_addr_wrap *, u_int32_t);
568a87fca6Smsf static int	pfkey_reply(int, u_int8_t **, ssize_t *);
57e225c210Shshoexer int		pfkey_parse(struct sadb_msg *, struct ipsec_rule *);
58f484f2cfShshoexer int		pfkey_ipsec_flush(void);
59356121f6Shshoexer int		pfkey_ipsec_establish(int, struct ipsec_rule *);
60f484f2cfShshoexer int		pfkey_init(void);
61f484f2cfShshoexer 
62f484f2cfShshoexer static int
63f484f2cfShshoexer pfkey_flow(int sd, u_int8_t satype, u_int8_t action, u_int8_t direction,
6457f58d0dSnaddy     u_int8_t proto, struct ipsec_addr_wrap *src, u_int16_t sport,
6557f58d0dSnaddy     struct ipsec_addr_wrap *dst, u_int16_t dport,
66435bb41eSmarkus     struct ipsec_addr_wrap *local, struct ipsec_addr_wrap *peer,
67435bb41eSmarkus     struct ipsec_auth *auth, u_int8_t flowtype)
68f484f2cfShshoexer {
69f484f2cfShshoexer 	struct sadb_msg		 smsg;
70435bb41eSmarkus 	struct sadb_address	 sa_src, sa_dst, sa_local, sa_peer, sa_smask,
71435bb41eSmarkus 				 sa_dmask;
72f484f2cfShshoexer 	struct sadb_protocol	 sa_flowtype, sa_protocol;
73f484f2cfShshoexer 	struct sadb_ident	*sa_srcid, *sa_dstid;
74435bb41eSmarkus 	struct sockaddr_storage	 ssrc, sdst, slocal, speer, smask, dmask;
75f484f2cfShshoexer 	struct iovec		 iov[IOV_CNT];
76f484f2cfShshoexer 	ssize_t			 n;
77f484f2cfShshoexer 	int			 iov_cnt, len, ret = 0;
78f484f2cfShshoexer 
79f484f2cfShshoexer 	sa_srcid = sa_dstid = NULL;
80f484f2cfShshoexer 
81f484f2cfShshoexer 	bzero(&ssrc, sizeof(ssrc));
82f484f2cfShshoexer 	bzero(&smask, sizeof(smask));
832099bcdfStodd 	ssrc.ss_family = smask.ss_family = src->af;
84f484f2cfShshoexer 	switch (src->af) {
85f484f2cfShshoexer 	case AF_INET:
86712e78baShshoexer 		((struct sockaddr_in *)&ssrc)->sin_addr = src->address.v4;
87f484f2cfShshoexer 		ssrc.ss_len = sizeof(struct sockaddr_in);
88712e78baShshoexer 		((struct sockaddr_in *)&smask)->sin_addr = src->mask.v4;
8957f58d0dSnaddy 		if (sport) {
9057f58d0dSnaddy 			((struct sockaddr_in *)&ssrc)->sin_port = sport;
9157f58d0dSnaddy 			((struct sockaddr_in *)&smask)->sin_port = 0xffff;
9257f58d0dSnaddy 		}
93f484f2cfShshoexer 		break;
94f484f2cfShshoexer 	case AF_INET6:
952099bcdfStodd 		((struct sockaddr_in6 *)&ssrc)->sin6_addr = src->address.v6;
962099bcdfStodd 		ssrc.ss_len = sizeof(struct sockaddr_in6);
972099bcdfStodd 		((struct sockaddr_in6 *)&smask)->sin6_addr = src->mask.v6;
9857f58d0dSnaddy 		if (sport) {
9957f58d0dSnaddy 			((struct sockaddr_in6 *)&ssrc)->sin6_port = sport;
10057f58d0dSnaddy 			((struct sockaddr_in6 *)&smask)->sin6_port = 0xffff;
10157f58d0dSnaddy 		}
1022099bcdfStodd 		break;
103f484f2cfShshoexer 	default:
104f484f2cfShshoexer 		warnx("unsupported address family %d", src->af);
105f484f2cfShshoexer 		return -1;
106f484f2cfShshoexer 	}
107f484f2cfShshoexer 	smask.ss_len = ssrc.ss_len;
108f484f2cfShshoexer 
109f484f2cfShshoexer 	bzero(&sdst, sizeof(sdst));
110f484f2cfShshoexer 	bzero(&dmask, sizeof(dmask));
1112099bcdfStodd 	sdst.ss_family = dmask.ss_family = dst->af;
112f484f2cfShshoexer 	switch (dst->af) {
113f484f2cfShshoexer 	case AF_INET:
114712e78baShshoexer 		((struct sockaddr_in *)&sdst)->sin_addr = dst->address.v4;
115f484f2cfShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in);
116712e78baShshoexer 		((struct sockaddr_in *)&dmask)->sin_addr = dst->mask.v4;
11757f58d0dSnaddy 		if (dport) {
11857f58d0dSnaddy 			((struct sockaddr_in *)&sdst)->sin_port = dport;
11957f58d0dSnaddy 			((struct sockaddr_in *)&dmask)->sin_port = 0xffff;
12057f58d0dSnaddy 		}
121f484f2cfShshoexer 		break;
122f484f2cfShshoexer 	case AF_INET6:
1232099bcdfStodd 		((struct sockaddr_in6 *)&sdst)->sin6_addr = dst->address.v6;
1242099bcdfStodd 		sdst.ss_len = sizeof(struct sockaddr_in6);
1252099bcdfStodd 		((struct sockaddr_in6 *)&dmask)->sin6_addr = dst->mask.v6;
12657f58d0dSnaddy 		if (dport) {
12757f58d0dSnaddy 			((struct sockaddr_in6 *)&sdst)->sin6_port = dport;
12857f58d0dSnaddy 			((struct sockaddr_in6 *)&dmask)->sin6_port = 0xffff;
12957f58d0dSnaddy 		}
1302099bcdfStodd 		break;
131f484f2cfShshoexer 	default:
132f484f2cfShshoexer 		warnx("unsupported address family %d", dst->af);
133f484f2cfShshoexer 		return -1;
134f484f2cfShshoexer 	}
135f484f2cfShshoexer 	dmask.ss_len = sdst.ss_len;
136f484f2cfShshoexer 
137435bb41eSmarkus 	bzero(&slocal, sizeof(slocal));
138435bb41eSmarkus 	if (local) {
1392099bcdfStodd 		slocal.ss_family = local->af;
140435bb41eSmarkus 		switch (local->af) {
141435bb41eSmarkus 		case AF_INET:
142435bb41eSmarkus 			((struct sockaddr_in *)&slocal)->sin_addr =
143435bb41eSmarkus 			    local->address.v4;
144435bb41eSmarkus 			slocal.ss_len = sizeof(struct sockaddr_in);
145435bb41eSmarkus 			break;
146435bb41eSmarkus 		case AF_INET6:
1472099bcdfStodd 			((struct sockaddr_in6 *)&slocal)->sin6_addr =
1482099bcdfStodd 			    local->address.v6;
1492099bcdfStodd 			slocal.ss_len = sizeof(struct sockaddr_in6);
1502099bcdfStodd 			break;
151435bb41eSmarkus 		default:
152435bb41eSmarkus 			warnx("unsupported address family %d", local->af);
153435bb41eSmarkus 			return -1;
154435bb41eSmarkus 		}
155435bb41eSmarkus 	}
156435bb41eSmarkus 
157f484f2cfShshoexer 	bzero(&speer, sizeof(speer));
15822a29ad6Shshoexer 	if (peer) {
1592099bcdfStodd 		speer.ss_family = peer->af;
160f484f2cfShshoexer 		switch (peer->af) {
161f484f2cfShshoexer 		case AF_INET:
162712e78baShshoexer 			((struct sockaddr_in *)&speer)->sin_addr =
163712e78baShshoexer 			    peer->address.v4;
164f484f2cfShshoexer 			speer.ss_len = sizeof(struct sockaddr_in);
165f484f2cfShshoexer 			break;
166f484f2cfShshoexer 		case AF_INET6:
1672099bcdfStodd 			((struct sockaddr_in6 *)&speer)->sin6_addr =
1682099bcdfStodd 			    peer->address.v6;
1692099bcdfStodd 			speer.ss_len = sizeof(struct sockaddr_in6);
1702099bcdfStodd 			break;
171f484f2cfShshoexer 		default:
172f484f2cfShshoexer 			warnx("unsupported address family %d", peer->af);
173f484f2cfShshoexer 			return -1;
174f484f2cfShshoexer 		}
17522a29ad6Shshoexer 	}
176f484f2cfShshoexer 
177f484f2cfShshoexer 	bzero(&smsg, sizeof(smsg));
178f484f2cfShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
179f484f2cfShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
180f484f2cfShshoexer 	smsg.sadb_msg_pid = getpid();
181f484f2cfShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
182f484f2cfShshoexer 	smsg.sadb_msg_type = action;
183f484f2cfShshoexer 	smsg.sadb_msg_satype = satype;
184f484f2cfShshoexer 
185f484f2cfShshoexer 	bzero(&sa_flowtype, sizeof(sa_flowtype));
186f484f2cfShshoexer 	sa_flowtype.sadb_protocol_exttype = SADB_X_EXT_FLOW_TYPE;
187f484f2cfShshoexer 	sa_flowtype.sadb_protocol_len = sizeof(sa_flowtype) / 8;
188f484f2cfShshoexer 	sa_flowtype.sadb_protocol_direction = direction;
18926df514dShshoexer 
190e1ffbfafShshoexer 	switch (flowtype) {
191e1ffbfafShshoexer 	case TYPE_USE:
19226df514dShshoexer 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_USE;
193e1ffbfafShshoexer 		break;
194b7a16601Shshoexer 	case TYPE_ACQUIRE:
195b7a16601Shshoexer 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_ACQUIRE;
196b7a16601Shshoexer 		break;
197e1ffbfafShshoexer 	case TYPE_REQUIRE:
198f484f2cfShshoexer 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_REQUIRE;
199e1ffbfafShshoexer 		break;
2007c7fb9e5Sreyk 	case TYPE_DENY:
2017c7fb9e5Sreyk 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_DENY;
2027c7fb9e5Sreyk 		break;
2037c7fb9e5Sreyk 	case TYPE_BYPASS:
2047c7fb9e5Sreyk 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_BYPASS;
2057c7fb9e5Sreyk 		break;
206b7a16601Shshoexer 	case TYPE_DONTACQ:
207b7a16601Shshoexer 		sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_DONTACQ;
208b7a16601Shshoexer 		break;
209e1ffbfafShshoexer 	default:
210e1ffbfafShshoexer 		warnx("unsupported flowtype %d", flowtype);
211e1ffbfafShshoexer 		return -1;
212e1ffbfafShshoexer 	}
213f484f2cfShshoexer 
214f484f2cfShshoexer 	bzero(&sa_protocol, sizeof(sa_protocol));
215f484f2cfShshoexer 	sa_protocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
216f484f2cfShshoexer 	sa_protocol.sadb_protocol_len = sizeof(sa_protocol) / 8;
217f484f2cfShshoexer 	sa_protocol.sadb_protocol_direction = 0;
2189182219dSmarkus 	sa_protocol.sadb_protocol_proto = proto;
219f484f2cfShshoexer 
220f484f2cfShshoexer 	bzero(&sa_src, sizeof(sa_src));
221f484f2cfShshoexer 	sa_src.sadb_address_exttype = SADB_X_EXT_SRC_FLOW;
222f484f2cfShshoexer 	sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
223f484f2cfShshoexer 
224f484f2cfShshoexer 	bzero(&sa_smask, sizeof(sa_smask));
225f484f2cfShshoexer 	sa_smask.sadb_address_exttype = SADB_X_EXT_SRC_MASK;
226f484f2cfShshoexer 	sa_smask.sadb_address_len =
227f484f2cfShshoexer 	    (sizeof(sa_smask) + ROUNDUP(smask.ss_len)) / 8;
228f484f2cfShshoexer 
229f484f2cfShshoexer 	bzero(&sa_dst, sizeof(sa_dst));
230f484f2cfShshoexer 	sa_dst.sadb_address_exttype = SADB_X_EXT_DST_FLOW;
231f484f2cfShshoexer 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
232f484f2cfShshoexer 
233f484f2cfShshoexer 	bzero(&sa_dmask, sizeof(sa_dmask));
234f484f2cfShshoexer 	sa_dmask.sadb_address_exttype = SADB_X_EXT_DST_MASK;
235f484f2cfShshoexer 	sa_dmask.sadb_address_len =
236f484f2cfShshoexer 	    (sizeof(sa_dmask) + ROUNDUP(dmask.ss_len)) / 8;
237f484f2cfShshoexer 
238435bb41eSmarkus 	if (local) {
239435bb41eSmarkus 		bzero(&sa_local, sizeof(sa_local));
240435bb41eSmarkus 		sa_local.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
241435bb41eSmarkus 		sa_local.sadb_address_len =
242435bb41eSmarkus 		    (sizeof(sa_local) + ROUNDUP(slocal.ss_len)) / 8;
243435bb41eSmarkus 	}
244435bb41eSmarkus 	if (peer) {
245f484f2cfShshoexer 		bzero(&sa_peer, sizeof(sa_peer));
246f484f2cfShshoexer 		sa_peer.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
247f484f2cfShshoexer 		sa_peer.sadb_address_len =
248f484f2cfShshoexer 		    (sizeof(sa_peer) + ROUNDUP(speer.ss_len)) / 8;
249435bb41eSmarkus 	}
250f484f2cfShshoexer 
251abe65127Shshoexer 	if (auth && auth->srcid) {
252abe65127Shshoexer 		len = ROUNDUP(strlen(auth->srcid) + 1) + sizeof(*sa_srcid);
253f484f2cfShshoexer 
254f484f2cfShshoexer 		sa_srcid = calloc(len, sizeof(u_int8_t));
255f484f2cfShshoexer 		if (sa_srcid == NULL)
256bd828a90Shshoexer 			err(1, "pfkey_flow: calloc");
257f484f2cfShshoexer 
2582281ca6dSmarkus 		sa_srcid->sadb_ident_type = auth->srcid_type;
259f484f2cfShshoexer 		sa_srcid->sadb_ident_len = len / 8;
260f484f2cfShshoexer 		sa_srcid->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC;
261f484f2cfShshoexer 
262abe65127Shshoexer 		strlcpy((char *)(sa_srcid + 1), auth->srcid,
263abe65127Shshoexer 		    ROUNDUP(strlen(auth->srcid) + 1));
264f484f2cfShshoexer 	}
265abe65127Shshoexer 	if (auth && auth->dstid) {
266abe65127Shshoexer 		len = ROUNDUP(strlen(auth->dstid) + 1) + sizeof(*sa_dstid);
267f484f2cfShshoexer 
268f484f2cfShshoexer 		sa_dstid = calloc(len, sizeof(u_int8_t));
269f484f2cfShshoexer 		if (sa_dstid == NULL)
270bd828a90Shshoexer 			err(1, "pfkey_flow: calloc");
271f484f2cfShshoexer 
2722281ca6dSmarkus 		sa_dstid->sadb_ident_type = auth->dstid_type;
273f484f2cfShshoexer 		sa_dstid->sadb_ident_len = len / 8;
274f484f2cfShshoexer 		sa_dstid->sadb_ident_exttype = SADB_EXT_IDENTITY_DST;
275f484f2cfShshoexer 
276abe65127Shshoexer 		strlcpy((char *)(sa_dstid + 1), auth->dstid,
277abe65127Shshoexer 		    ROUNDUP(strlen(auth->dstid) + 1));
278f484f2cfShshoexer 	}
279f484f2cfShshoexer 
280f484f2cfShshoexer 	iov_cnt = 0;
281f484f2cfShshoexer 
282f484f2cfShshoexer 	/* header */
283f484f2cfShshoexer 	iov[iov_cnt].iov_base = &smsg;
284f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
285f484f2cfShshoexer 	iov_cnt++;
286f484f2cfShshoexer 
28789ad8c34Shshoexer 	/* add flow type */
28889ad8c34Shshoexer 	iov[iov_cnt].iov_base = &sa_flowtype;
28989ad8c34Shshoexer 	iov[iov_cnt].iov_len = sizeof(sa_flowtype);
29089ad8c34Shshoexer 	smsg.sadb_msg_len += sa_flowtype.sadb_protocol_len;
29189ad8c34Shshoexer 	iov_cnt++;
29289ad8c34Shshoexer 
293435bb41eSmarkus 	/* local ip */
294435bb41eSmarkus 	if (local) {
295435bb41eSmarkus 		iov[iov_cnt].iov_base = &sa_local;
296435bb41eSmarkus 		iov[iov_cnt].iov_len = sizeof(sa_local);
297435bb41eSmarkus 		iov_cnt++;
298435bb41eSmarkus 		iov[iov_cnt].iov_base = &slocal;
299435bb41eSmarkus 		iov[iov_cnt].iov_len = ROUNDUP(slocal.ss_len);
300435bb41eSmarkus 		smsg.sadb_msg_len += sa_local.sadb_address_len;
301435bb41eSmarkus 		iov_cnt++;
302435bb41eSmarkus 	}
303435bb41eSmarkus 
304f484f2cfShshoexer 	/* remote peer */
30522a29ad6Shshoexer 	if (peer) {
306f484f2cfShshoexer 		iov[iov_cnt].iov_base = &sa_peer;
307f484f2cfShshoexer 		iov[iov_cnt].iov_len = sizeof(sa_peer);
308f484f2cfShshoexer 		iov_cnt++;
309f484f2cfShshoexer 		iov[iov_cnt].iov_base = &speer;
310f484f2cfShshoexer 		iov[iov_cnt].iov_len = ROUNDUP(speer.ss_len);
311f484f2cfShshoexer 		smsg.sadb_msg_len += sa_peer.sadb_address_len;
312f484f2cfShshoexer 		iov_cnt++;
31322a29ad6Shshoexer 	}
314f484f2cfShshoexer 
31589ad8c34Shshoexer 	/* src addr */
31689ad8c34Shshoexer 	iov[iov_cnt].iov_base = &sa_src;
31789ad8c34Shshoexer 	iov[iov_cnt].iov_len = sizeof(sa_src);
31889ad8c34Shshoexer 	iov_cnt++;
31989ad8c34Shshoexer 	iov[iov_cnt].iov_base = &ssrc;
32089ad8c34Shshoexer 	iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len);
32189ad8c34Shshoexer 	smsg.sadb_msg_len += sa_src.sadb_address_len;
322f484f2cfShshoexer 	iov_cnt++;
323f484f2cfShshoexer 
32489ad8c34Shshoexer 	/* src mask */
325f484f2cfShshoexer 	iov[iov_cnt].iov_base = &sa_smask;
326f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_smask);
327f484f2cfShshoexer 	iov_cnt++;
328f484f2cfShshoexer 	iov[iov_cnt].iov_base = &smask;
329f484f2cfShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(smask.ss_len);
330f484f2cfShshoexer 	smsg.sadb_msg_len += sa_smask.sadb_address_len;
331f484f2cfShshoexer 	iov_cnt++;
332f484f2cfShshoexer 
333f484f2cfShshoexer 	/* dest addr */
334f484f2cfShshoexer 	iov[iov_cnt].iov_base = &sa_dst;
335f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst);
336f484f2cfShshoexer 	iov_cnt++;
337f484f2cfShshoexer 	iov[iov_cnt].iov_base = &sdst;
338f484f2cfShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
339f484f2cfShshoexer 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
340f484f2cfShshoexer 	iov_cnt++;
341f484f2cfShshoexer 
34289ad8c34Shshoexer 	/* dst mask */
34389ad8c34Shshoexer 	iov[iov_cnt].iov_base = &sa_dmask;
34489ad8c34Shshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dmask);
345f484f2cfShshoexer 	iov_cnt++;
34689ad8c34Shshoexer 	iov[iov_cnt].iov_base = &dmask;
34789ad8c34Shshoexer 	iov[iov_cnt].iov_len = ROUNDUP(dmask.ss_len);
34889ad8c34Shshoexer 	smsg.sadb_msg_len += sa_dmask.sadb_address_len;
34989ad8c34Shshoexer 	iov_cnt++;
35089ad8c34Shshoexer 
35189ad8c34Shshoexer 	/* add protocol */
35289ad8c34Shshoexer 	iov[iov_cnt].iov_base = &sa_protocol;
35389ad8c34Shshoexer 	iov[iov_cnt].iov_len = sizeof(sa_protocol);
35489ad8c34Shshoexer 	smsg.sadb_msg_len += sa_protocol.sadb_protocol_len;
355f484f2cfShshoexer 	iov_cnt++;
356f484f2cfShshoexer 
357f484f2cfShshoexer 	if (sa_srcid) {
358f484f2cfShshoexer 		/* src identity */
359f484f2cfShshoexer 		iov[iov_cnt].iov_base = sa_srcid;
360f484f2cfShshoexer 		iov[iov_cnt].iov_len = sa_srcid->sadb_ident_len * 8;
361f484f2cfShshoexer 		smsg.sadb_msg_len += sa_srcid->sadb_ident_len;
362f484f2cfShshoexer 		iov_cnt++;
363f484f2cfShshoexer 	}
364f484f2cfShshoexer 	if (sa_dstid) {
365f484f2cfShshoexer 		/* dst identity */
366f484f2cfShshoexer 		iov[iov_cnt].iov_base = sa_dstid;
367f484f2cfShshoexer 		iov[iov_cnt].iov_len = sa_dstid->sadb_ident_len * 8;
368f484f2cfShshoexer 		smsg.sadb_msg_len += sa_dstid->sadb_ident_len;
369f484f2cfShshoexer 		iov_cnt++;
370f484f2cfShshoexer 	}
371f484f2cfShshoexer 	len = smsg.sadb_msg_len * 8;
37289d271caShenning 
37389d271caShenning 	do {
37489d271caShenning 		n = writev(sd, iov, iov_cnt);
37589d271caShenning 	} while (n == -1 && (errno == EAGAIN || errno == EINTR));
37689d271caShenning 	if (n == -1) {
377f484f2cfShshoexer 		warn("writev failed");
378f484f2cfShshoexer 		ret = -1;
379f484f2cfShshoexer 	}
380f484f2cfShshoexer 
381f484f2cfShshoexer 	if (sa_srcid)
382f484f2cfShshoexer 		free(sa_srcid);
383f484f2cfShshoexer 	if (sa_dstid)
384f484f2cfShshoexer 		free(sa_dstid);
385f484f2cfShshoexer 
386f484f2cfShshoexer 	return ret;
387f484f2cfShshoexer }
388f484f2cfShshoexer 
389f484f2cfShshoexer static int
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,
39291f765ddShshoexer     struct ipsec_transforms *xfs, struct ipsec_key *authkey,
393a38d220fShshoexer     struct ipsec_key *enckey, u_int8_t tmode)
394f032086dShshoexer {
395f032086dShshoexer 	struct sadb_msg		smsg;
396f032086dShshoexer 	struct sadb_sa		sa;
397f032086dShshoexer 	struct sadb_address	sa_src, sa_dst;
398881e2068Shshoexer 	struct sadb_key		sa_authkey, sa_enckey;
399f032086dShshoexer 	struct sockaddr_storage	ssrc, sdst;
400f032086dShshoexer 	struct iovec		iov[IOV_CNT];
401f032086dShshoexer 	ssize_t			n;
402f032086dShshoexer 	int			iov_cnt, len, ret = 0;
403f032086dShshoexer 
404f032086dShshoexer 	bzero(&ssrc, sizeof(ssrc));
4052099bcdfStodd 	ssrc.ss_family = src->af;
406f032086dShshoexer 	switch (src->af) {
407f032086dShshoexer 	case AF_INET:
408712e78baShshoexer 		((struct sockaddr_in *)&ssrc)->sin_addr = src->address.v4;
409f032086dShshoexer 		ssrc.ss_len = sizeof(struct sockaddr_in);
410f032086dShshoexer 		break;
411f032086dShshoexer 	case AF_INET6:
4122099bcdfStodd 		((struct sockaddr_in6 *)&ssrc)->sin6_addr = src->address.v6;
4132099bcdfStodd 		ssrc.ss_len = sizeof(struct sockaddr_in6);
4142099bcdfStodd 		break;
415f032086dShshoexer 	default:
416f032086dShshoexer 		warnx("unsupported address family %d", src->af);
417f032086dShshoexer 		return -1;
418f032086dShshoexer 	}
419f032086dShshoexer 
420f032086dShshoexer 	bzero(&sdst, sizeof(sdst));
4212099bcdfStodd 	sdst.ss_family = dst->af;
422f032086dShshoexer 	switch (dst->af) {
423f032086dShshoexer 	case AF_INET:
424712e78baShshoexer 		((struct sockaddr_in *)&sdst)->sin_addr = dst->address.v4;
425f032086dShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in);
426f032086dShshoexer 		break;
427f032086dShshoexer 	case AF_INET6:
4282099bcdfStodd 		((struct sockaddr_in6 *)&sdst)->sin6_addr = dst->address.v6;
4292099bcdfStodd 		sdst.ss_len = sizeof(struct sockaddr_in6);
4302099bcdfStodd 		break;
431f032086dShshoexer 	default:
432f032086dShshoexer 		warnx("unsupported address family %d", dst->af);
433f032086dShshoexer 		return -1;
434f032086dShshoexer 	}
435f032086dShshoexer 
436f032086dShshoexer 	bzero(&smsg, sizeof(smsg));
437f032086dShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
438f032086dShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
439f032086dShshoexer 	smsg.sadb_msg_pid = getpid();
440f032086dShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
441f032086dShshoexer 	smsg.sadb_msg_type = action;
442f032086dShshoexer 	smsg.sadb_msg_satype = satype;
443f032086dShshoexer 
444f032086dShshoexer 	bzero(&sa, sizeof(sa));
445f032086dShshoexer 	sa.sadb_sa_len = sizeof(sa) / 8;
446f032086dShshoexer 	sa.sadb_sa_exttype = SADB_EXT_SA;
447f032086dShshoexer 	sa.sadb_sa_spi = htonl(spi);
448f032086dShshoexer 	sa.sadb_sa_state = SADB_SASTATE_MATURE;
449f032086dShshoexer 
45088a8cceeSmarkus 	if (satype != SADB_X_SATYPE_IPIP && tmode == IPSEC_TUNNEL)
451a38d220fShshoexer 		sa.sadb_sa_flags |= SADB_X_SAFLAGS_TUNNEL;
452a38d220fShshoexer 
453375db29dShshoexer 	if (xfs && xfs->authxf) {
454375db29dShshoexer 		switch (xfs->authxf->id) {
455881e2068Shshoexer 		case AUTHXF_NONE:
456881e2068Shshoexer 			break;
457881e2068Shshoexer 		case AUTHXF_HMAC_MD5:
458881e2068Shshoexer 			sa.sadb_sa_auth = SADB_AALG_MD5HMAC;
459881e2068Shshoexer 			break;
460881e2068Shshoexer 		case AUTHXF_HMAC_RIPEMD160:
461881e2068Shshoexer 			sa.sadb_sa_auth = SADB_X_AALG_RIPEMD160HMAC;
462881e2068Shshoexer 			break;
463881e2068Shshoexer 		case AUTHXF_HMAC_SHA1:
464881e2068Shshoexer 			sa.sadb_sa_auth = SADB_AALG_SHA1HMAC;
465881e2068Shshoexer 			break;
466881e2068Shshoexer 		case AUTHXF_HMAC_SHA2_256:
467881e2068Shshoexer 			sa.sadb_sa_auth = SADB_X_AALG_SHA2_256;
468881e2068Shshoexer 			break;
469881e2068Shshoexer 		case AUTHXF_HMAC_SHA2_384:
470881e2068Shshoexer 			sa.sadb_sa_auth = SADB_X_AALG_SHA2_384;
471881e2068Shshoexer 			break;
472881e2068Shshoexer 		case AUTHXF_HMAC_SHA2_512:
473881e2068Shshoexer 			sa.sadb_sa_auth = SADB_X_AALG_SHA2_512;
474881e2068Shshoexer 			break;
475881e2068Shshoexer 		default:
476881e2068Shshoexer 			warnx("unsupported authentication algorithm %d",
477375db29dShshoexer 			    xfs->authxf->id);
478881e2068Shshoexer 		}
479881e2068Shshoexer 	}
480375db29dShshoexer 	if (xfs && xfs->encxf) {
481375db29dShshoexer 		switch (xfs->encxf->id) {
482881e2068Shshoexer 		case ENCXF_NONE:
483881e2068Shshoexer 			break;
484881e2068Shshoexer 		case ENCXF_3DES_CBC:
485881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_EALG_3DESCBC;
486881e2068Shshoexer 			break;
487881e2068Shshoexer 		case ENCXF_DES_CBC:
488881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_EALG_DESCBC;
489881e2068Shshoexer 			break;
490881e2068Shshoexer 		case ENCXF_AES:
491881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_AES;
492881e2068Shshoexer 			break;
493881e2068Shshoexer 		case ENCXF_AESCTR:
494881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_AESCTR;
495881e2068Shshoexer 			break;
496881e2068Shshoexer 		case ENCXF_BLOWFISH:
497881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_BLF;
498881e2068Shshoexer 			break;
499881e2068Shshoexer 		case ENCXF_CAST128:
500881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_CAST;
501881e2068Shshoexer 			break;
502881e2068Shshoexer 		case ENCXF_NULL:
503881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_EALG_NULL;
504881e2068Shshoexer 			break;
505881e2068Shshoexer 		case ENCXF_SKIPJACK:
506881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_SKIPJACK;
507881e2068Shshoexer 			break;
508881e2068Shshoexer 		default:
509375db29dShshoexer 			warnx("unsupported encryption algorithm %d",
510375db29dShshoexer 			    xfs->encxf->id);
511881e2068Shshoexer 		}
512881e2068Shshoexer 	}
51372e25333Shshoexer 	if (xfs && xfs->compxf) {
51472e25333Shshoexer 		switch (xfs->compxf->id) {
51572e25333Shshoexer 		case COMPXF_DEFLATE:
51672e25333Shshoexer 			sa.sadb_sa_encrypt = SADB_X_CALG_DEFLATE;
51772e25333Shshoexer 			break;
51872e25333Shshoexer 		case COMPXF_LZS:
51972e25333Shshoexer 			sa.sadb_sa_encrypt = SADB_X_CALG_LZS;
52072e25333Shshoexer 			break;
52172e25333Shshoexer 		default:
52272e25333Shshoexer 			warnx("unsupported compression algorithm %d",
52372e25333Shshoexer 			    xfs->compxf->id);
52472e25333Shshoexer 		}
52572e25333Shshoexer 	}
526881e2068Shshoexer 
527f032086dShshoexer 	bzero(&sa_src, sizeof(sa_src));
528f032086dShshoexer 	sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
529f032086dShshoexer 	sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
530f032086dShshoexer 
531f032086dShshoexer 	bzero(&sa_dst, sizeof(sa_dst));
532f032086dShshoexer 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
533f032086dShshoexer 	sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
534f032086dShshoexer 
53572e25333Shshoexer 	if (action == SADB_ADD && !authkey && !enckey && satype !=
53688a8cceeSmarkus 	    SADB_X_SATYPE_IPCOMP && satype != SADB_X_SATYPE_IPIP) { /* XXX ENCNULL */
5370813ab45Shshoexer 		warnx("no key specified");
5380813ab45Shshoexer 		return -1;
5390813ab45Shshoexer 	}
540881e2068Shshoexer 	if (authkey) {
541881e2068Shshoexer 		bzero(&sa_authkey, sizeof(sa_authkey));
542881e2068Shshoexer 		sa_authkey.sadb_key_len = (sizeof(sa_authkey) +
543881e2068Shshoexer 		    ((authkey->len + 7) / 8) * 8) / 8;
544881e2068Shshoexer 		sa_authkey.sadb_key_exttype = SADB_EXT_KEY_AUTH;
545881e2068Shshoexer 		sa_authkey.sadb_key_bits = 8 * authkey->len;
546881e2068Shshoexer 	}
547881e2068Shshoexer 	if (enckey) {
548881e2068Shshoexer 		bzero(&sa_enckey, sizeof(sa_enckey));
549881e2068Shshoexer 		sa_enckey.sadb_key_len = (sizeof(sa_enckey) +
550881e2068Shshoexer 		    ((enckey->len + 7) / 8) * 8) / 8;
551881e2068Shshoexer 		sa_enckey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
552881e2068Shshoexer 		sa_enckey.sadb_key_bits = 8 * enckey->len;
553f032086dShshoexer 	}
554f032086dShshoexer 
555f032086dShshoexer 	iov_cnt = 0;
556f032086dShshoexer 
557f032086dShshoexer 	/* header */
558f032086dShshoexer 	iov[iov_cnt].iov_base = &smsg;
559f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
560f032086dShshoexer 	iov_cnt++;
561f032086dShshoexer 
562f032086dShshoexer 	/* sa */
563f032086dShshoexer 	iov[iov_cnt].iov_base = &sa;
564f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(sa);
565f032086dShshoexer 	smsg.sadb_msg_len += sa.sadb_sa_len;
566f032086dShshoexer 	iov_cnt++;
567f032086dShshoexer 
568f032086dShshoexer 	/* src addr */
569f032086dShshoexer 	iov[iov_cnt].iov_base = &sa_src;
570f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_src);
571f032086dShshoexer 	iov_cnt++;
572f032086dShshoexer 	iov[iov_cnt].iov_base = &ssrc;
573f032086dShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len);
574f032086dShshoexer 	smsg.sadb_msg_len += sa_src.sadb_address_len;
575f032086dShshoexer 	iov_cnt++;
576f032086dShshoexer 
577f032086dShshoexer 	/* dst addr */
578f032086dShshoexer 	iov[iov_cnt].iov_base = &sa_dst;
579f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst);
580f032086dShshoexer 	iov_cnt++;
581f032086dShshoexer 	iov[iov_cnt].iov_base = &sdst;
582f032086dShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
583f032086dShshoexer 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
584f032086dShshoexer 	iov_cnt++;
585f032086dShshoexer 
586881e2068Shshoexer 	if (authkey) {
587881e2068Shshoexer 		/* authentication key */
588881e2068Shshoexer 		iov[iov_cnt].iov_base = &sa_authkey;
589881e2068Shshoexer 		iov[iov_cnt].iov_len = sizeof(sa_authkey);
590f032086dShshoexer 		iov_cnt++;
591881e2068Shshoexer 		iov[iov_cnt].iov_base = authkey->data;
592881e2068Shshoexer 		iov[iov_cnt].iov_len = ((authkey->len + 7) / 8) * 8;
593881e2068Shshoexer 		smsg.sadb_msg_len += sa_authkey.sadb_key_len;
594881e2068Shshoexer 		iov_cnt++;
595881e2068Shshoexer 	}
596881e2068Shshoexer 	if (enckey) {
597881e2068Shshoexer 		/* encryption key */
598881e2068Shshoexer 		iov[iov_cnt].iov_base = &sa_enckey;
599881e2068Shshoexer 		iov[iov_cnt].iov_len = sizeof(sa_enckey);
600881e2068Shshoexer 		iov_cnt++;
601881e2068Shshoexer 		iov[iov_cnt].iov_base = enckey->data;
602881e2068Shshoexer 		iov[iov_cnt].iov_len = ((enckey->len + 7) / 8) * 8;
603881e2068Shshoexer 		smsg.sadb_msg_len += sa_enckey.sadb_key_len;
604f032086dShshoexer 		iov_cnt++;
605f032086dShshoexer 	}
606f032086dShshoexer 
607f032086dShshoexer 	len = smsg.sadb_msg_len * 8;
608f032086dShshoexer 	if ((n = writev(sd, iov, iov_cnt)) == -1) {
609f032086dShshoexer 		warn("writev failed");
610f032086dShshoexer 		ret = -1;
611f032086dShshoexer 	} else if (n != len) {
612f032086dShshoexer 		warnx("short write");
613f032086dShshoexer 		ret = -1;
614f032086dShshoexer 	}
615f032086dShshoexer 
616f032086dShshoexer 	return ret;
617f032086dShshoexer }
618f032086dShshoexer 
619f032086dShshoexer static int
6208065703bShshoexer pfkey_sagroup(int sd, u_int8_t satype, u_int8_t satype2, u_int8_t action,
6218065703bShshoexer     struct ipsec_addr_wrap *dst, u_int32_t spi, struct ipsec_addr_wrap *dst2,
6228065703bShshoexer     u_int32_t spi2)
6238f2109caShshoexer {
6248f2109caShshoexer 	struct sadb_msg		smsg;
6258f2109caShshoexer 	struct sadb_sa		sa1, sa2;
6268f2109caShshoexer 	struct sadb_address	sa_dst, sa_dst2;
6278f2109caShshoexer 	struct sockaddr_storage	sdst, sdst2;
6288f2109caShshoexer 	struct sadb_protocol	sa_proto;
6298f2109caShshoexer 	struct iovec		iov[IOV_CNT];
6308f2109caShshoexer 	ssize_t			n;
6318f2109caShshoexer 	int			iov_cnt, len, ret = 0;
6328f2109caShshoexer 
6338f2109caShshoexer 	bzero(&sdst, sizeof(sdst));
6348f2109caShshoexer 	sdst.ss_family = dst->af;
6358f2109caShshoexer 	switch (dst->af) {
6368f2109caShshoexer 	case AF_INET:
6378f2109caShshoexer 		((struct sockaddr_in *)&sdst)->sin_addr = dst->address.v4;
6388f2109caShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in);
6398f2109caShshoexer 		break;
6408f2109caShshoexer 	case AF_INET6:
6418f2109caShshoexer 		((struct sockaddr_in6 *)&sdst)->sin6_addr = dst->address.v6;
6428f2109caShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in6);
6438f2109caShshoexer 		break;
6448f2109caShshoexer 	default:
6458f2109caShshoexer 		warnx("unsupported address family %d", dst->af);
6468f2109caShshoexer 		return -1;
6478f2109caShshoexer 	}
6488f2109caShshoexer 
6498f2109caShshoexer 	bzero(&sdst2, sizeof(sdst2));
6508f2109caShshoexer 	sdst2.ss_family = dst2->af;
6518f2109caShshoexer 	switch (dst2->af) {
6528f2109caShshoexer 	case AF_INET:
6538f2109caShshoexer 		((struct sockaddr_in *)&sdst2)->sin_addr = dst2->address.v4;
6548f2109caShshoexer 		sdst2.ss_len = sizeof(struct sockaddr_in);
6558f2109caShshoexer 		break;
6568f2109caShshoexer 	case AF_INET6:
6578f2109caShshoexer 		((struct sockaddr_in6 *)&sdst2)->sin6_addr = dst2->address.v6;
6588f2109caShshoexer 		sdst2.ss_len = sizeof(struct sockaddr_in6);
6598f2109caShshoexer 		break;
6608f2109caShshoexer 	default:
6618f2109caShshoexer 		warnx("unsupported address family %d", dst2->af);
6628f2109caShshoexer 		return -1;
6638f2109caShshoexer 	}
6648f2109caShshoexer 
6658f2109caShshoexer 	bzero(&smsg, sizeof(smsg));
6668f2109caShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
6678f2109caShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
6688f2109caShshoexer 	smsg.sadb_msg_pid = getpid();
6698f2109caShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
6708f2109caShshoexer 	smsg.sadb_msg_type = action;
6718f2109caShshoexer 	smsg.sadb_msg_satype = satype;
6728f2109caShshoexer 
6738f2109caShshoexer 	bzero(&sa1, sizeof(sa1));
6748f2109caShshoexer 	sa1.sadb_sa_len = sizeof(sa1) / 8;
6758f2109caShshoexer 	sa1.sadb_sa_exttype = SADB_EXT_SA;
6768f2109caShshoexer 	sa1.sadb_sa_spi = htonl(spi);
6778f2109caShshoexer 	sa1.sadb_sa_state = SADB_SASTATE_MATURE;
6788f2109caShshoexer 
6798f2109caShshoexer 	bzero(&sa2, sizeof(sa2));
6808f2109caShshoexer 	sa2.sadb_sa_len = sizeof(sa2) / 8;
6818f2109caShshoexer 	sa2.sadb_sa_exttype = SADB_X_EXT_SA2;
6828f2109caShshoexer 	sa2.sadb_sa_spi = htonl(spi2);
6838f2109caShshoexer 	sa2.sadb_sa_state = SADB_SASTATE_MATURE;
6848f2109caShshoexer 	iov_cnt = 0;
6858f2109caShshoexer 
6868f2109caShshoexer 	bzero(&sa_dst, sizeof(sa_dst));
6878f2109caShshoexer 	sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
6888f2109caShshoexer 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
6898f2109caShshoexer 
6908f2109caShshoexer 	bzero(&sa_dst2, sizeof(sa_dst2));
6918f2109caShshoexer 	sa_dst2.sadb_address_exttype = SADB_X_EXT_DST2;
6928f2109caShshoexer 	sa_dst2.sadb_address_len = (sizeof(sa_dst2) + ROUNDUP(sdst2.ss_len)) / 8;
6938f2109caShshoexer 
6948f2109caShshoexer 	bzero(&sa_proto, sizeof(sa_proto));
6958f2109caShshoexer 	sa_proto.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
6968f2109caShshoexer 	sa_proto.sadb_protocol_len = sizeof(sa_proto) / 8;
6978f2109caShshoexer 	sa_proto.sadb_protocol_direction = 0;
6988065703bShshoexer 	sa_proto.sadb_protocol_proto = satype2;
6998f2109caShshoexer 
7008f2109caShshoexer 	/* header */
7018f2109caShshoexer 	iov[iov_cnt].iov_base = &smsg;
7028f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
7038f2109caShshoexer 	iov_cnt++;
7048f2109caShshoexer 
7058f2109caShshoexer 	/* sa */
7068f2109caShshoexer 	iov[iov_cnt].iov_base = &sa1;
7078f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa1);
7088f2109caShshoexer 	smsg.sadb_msg_len += sa1.sadb_sa_len;
7098f2109caShshoexer 	iov_cnt++;
7108f2109caShshoexer 
7118f2109caShshoexer 	/* dst addr */
7128f2109caShshoexer 	iov[iov_cnt].iov_base = &sa_dst;
7138f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst);
7148f2109caShshoexer 	iov_cnt++;
7158f2109caShshoexer 	iov[iov_cnt].iov_base = &sdst;
7168f2109caShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
7178f2109caShshoexer 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
7188f2109caShshoexer 	iov_cnt++;
7198f2109caShshoexer 
7208f2109caShshoexer 	/* second sa */
7218f2109caShshoexer 	iov[iov_cnt].iov_base = &sa2;
7228f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa2);
7238f2109caShshoexer 	smsg.sadb_msg_len += sa2.sadb_sa_len;
7248f2109caShshoexer 	iov_cnt++;
7258f2109caShshoexer 
7268f2109caShshoexer 	/* second dst addr */
7278f2109caShshoexer 	iov[iov_cnt].iov_base = &sa_dst2;
7288f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst2);
7298f2109caShshoexer 	iov_cnt++;
7308f2109caShshoexer 	iov[iov_cnt].iov_base = &sdst2;
7318f2109caShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst2.ss_len);
7328f2109caShshoexer 	smsg.sadb_msg_len += sa_dst2.sadb_address_len;
7338f2109caShshoexer 	iov_cnt++;
7348f2109caShshoexer 
7358f2109caShshoexer 	/* SA type */
7368f2109caShshoexer 	iov[iov_cnt].iov_base = &sa_proto;
7378f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_proto);
7388f2109caShshoexer 	smsg.sadb_msg_len += sa_proto.sadb_protocol_len;
7398f2109caShshoexer 	iov_cnt++;
7408f2109caShshoexer 
7418f2109caShshoexer 	len = smsg.sadb_msg_len * 8;
7428f2109caShshoexer 	if ((n = writev(sd, iov, iov_cnt)) == -1) {
7438f2109caShshoexer 		warn("writev failed");
7448f2109caShshoexer 		ret = -1;
7458f2109caShshoexer 	} else if (n != len) {
7468f2109caShshoexer 		warnx("short write");
7478f2109caShshoexer 		ret = -1;
7488f2109caShshoexer 	}
7498f2109caShshoexer 
7508f2109caShshoexer 	return (ret);
7518f2109caShshoexer }
7528f2109caShshoexer 
7538f2109caShshoexer static int
7548a87fca6Smsf pfkey_reply(int sd, u_int8_t **datap, ssize_t *lenp)
755f484f2cfShshoexer {
756f484f2cfShshoexer 	struct sadb_msg	 hdr;
757f484f2cfShshoexer 	ssize_t		 len;
758f484f2cfShshoexer 	u_int8_t	*data;
759f484f2cfShshoexer 
760f484f2cfShshoexer 	if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
761f484f2cfShshoexer 		warnx("short read");
762f484f2cfShshoexer 		return -1;
763f484f2cfShshoexer 	}
764f484f2cfShshoexer 	len = hdr.sadb_msg_len * PFKEYV2_CHUNK;
765f484f2cfShshoexer 	if ((data = malloc(len)) == NULL)
766bd828a90Shshoexer 		err(1, "pfkey_reply: malloc");
767f484f2cfShshoexer 	if (read(sd, data, len) != len) {
768f484f2cfShshoexer 		warn("PF_KEY short read");
769f484f2cfShshoexer 		bzero(data, len);
770f484f2cfShshoexer 		free(data);
771f484f2cfShshoexer 		return -1;
772f484f2cfShshoexer 	}
7738a87fca6Smsf 	if (datap) {
7748a87fca6Smsf 		*datap = data;
7758a87fca6Smsf 		if (lenp)
7768a87fca6Smsf 			*lenp = len;
7778a87fca6Smsf 	} else {
778f484f2cfShshoexer 		bzero(data, len);
779f484f2cfShshoexer 		free(data);
7808a87fca6Smsf 	}
781011122f7Smarkus 	if (datap == NULL && hdr.sadb_msg_errno != 0) {
782011122f7Smarkus 		errno = hdr.sadb_msg_errno;
783*92270c32Shshoexer 		if (errno != EEXIST) {
784011122f7Smarkus 			warn("PF_KEY failed");
785011122f7Smarkus 			return -1;
786011122f7Smarkus 		}
787*92270c32Shshoexer 	}
788f484f2cfShshoexer 	return 0;
789f484f2cfShshoexer }
790f484f2cfShshoexer 
791f484f2cfShshoexer int
7921edc1b9aShshoexer pfkey_parse(struct sadb_msg *msg, struct ipsec_rule *rule)
7931edc1b9aShshoexer {
7941edc1b9aShshoexer 	struct sadb_ext		*ext;
7951edc1b9aShshoexer 	struct sadb_address	*saddr;
7961edc1b9aShshoexer 	struct sadb_protocol	*sproto;
7971edc1b9aShshoexer 	struct sadb_ident	*sident;
7981edc1b9aShshoexer 	struct sockaddr		*sa;
799712e78baShshoexer 	struct sockaddr_in	*sa_in;
8002099bcdfStodd 	struct sockaddr_in6	*sa_in6;
8011edc1b9aShshoexer 	int			 len;
8021edc1b9aShshoexer 
8031edc1b9aShshoexer 	switch (msg->sadb_msg_satype) {
804b26a6cb5Shshoexer 	case SADB_SATYPE_ESP:
8059182219dSmarkus 		rule->satype = IPSEC_ESP;
8061edc1b9aShshoexer 		break;
807b26a6cb5Shshoexer 	case SADB_SATYPE_AH:
8089182219dSmarkus 		rule->satype = IPSEC_AH;
8091edc1b9aShshoexer 		break;
810b26a6cb5Shshoexer 	case SADB_X_SATYPE_IPCOMP:
8119182219dSmarkus 		rule->satype = IPSEC_IPCOMP;
812a29da9d0Shshoexer 		break;
81388a8cceeSmarkus 	case SADB_X_SATYPE_IPIP:
8149182219dSmarkus 		rule->satype = IPSEC_IPIP;
81588a8cceeSmarkus 		break;
8161edc1b9aShshoexer 	default:
8171edc1b9aShshoexer 		return (1);
8181edc1b9aShshoexer 	}
8191edc1b9aShshoexer 
8201edc1b9aShshoexer 	for (ext = (struct sadb_ext *)(msg + 1);
8211edc1b9aShshoexer 	    (size_t)((u_int8_t *)ext - (u_int8_t *)msg) <
822d5d1799eShshoexer 	    msg->sadb_msg_len * PFKEYV2_CHUNK && ext->sadb_ext_len > 0;
8231edc1b9aShshoexer 	    ext = (struct sadb_ext *)((u_int8_t *)ext +
8241edc1b9aShshoexer 	    ext->sadb_ext_len * PFKEYV2_CHUNK)) {
8251edc1b9aShshoexer 		switch (ext->sadb_ext_type) {
8261edc1b9aShshoexer 		case SADB_EXT_ADDRESS_SRC:
8271edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
8281edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
8291edc1b9aShshoexer 
83091f765ddShshoexer 			rule->local = calloc(1, sizeof(struct ipsec_addr_wrap));
831d5d1799eShshoexer 			if (rule->local == NULL)
832a1059f6aStodd 				err(1, "pfkey_parse: calloc");
8331edc1b9aShshoexer 
8342099bcdfStodd 			rule->local->af = sa->sa_family;
8351edc1b9aShshoexer 			switch (sa->sa_family) {
8361edc1b9aShshoexer 			case AF_INET:
8371edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
83891f765ddShshoexer 				    &rule->local->address.v4,
839712e78baShshoexer 				    sizeof(struct in_addr));
8402099bcdfStodd 				set_ipmask(rule->local, 32);
8412099bcdfStodd 				break;
8422099bcdfStodd 			case AF_INET6:
8432099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
8442099bcdfStodd 				    &rule->local->address.v6,
8452099bcdfStodd 				    sizeof(struct in6_addr));
8462099bcdfStodd 				set_ipmask(rule->local, 128);
8471edc1b9aShshoexer 				break;
8481edc1b9aShshoexer 			default:
8491edc1b9aShshoexer 				return (1);
8501edc1b9aShshoexer 			}
8511edc1b9aShshoexer 			break;
8521edc1b9aShshoexer 
8531edc1b9aShshoexer 
8541edc1b9aShshoexer 		case SADB_EXT_ADDRESS_DST:
8551edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
8561edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
8571edc1b9aShshoexer 
85891f765ddShshoexer 			rule->peer = calloc(1, sizeof(struct ipsec_addr_wrap));
8591edc1b9aShshoexer 			if (rule->peer == NULL)
860a1059f6aStodd 				err(1, "pfkey_parse: calloc");
8611edc1b9aShshoexer 
8622099bcdfStodd 			rule->peer->af = sa->sa_family;
8631edc1b9aShshoexer 			switch (sa->sa_family) {
8641edc1b9aShshoexer 			case AF_INET:
8651edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
866712e78baShshoexer 				    &rule->peer->address.v4,
867712e78baShshoexer 				    sizeof(struct in_addr));
8682099bcdfStodd 				set_ipmask(rule->peer, 32);
8692099bcdfStodd 				break;
8702099bcdfStodd 			case AF_INET6:
8712099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
8722099bcdfStodd 				    &rule->peer->address.v6,
8732099bcdfStodd 				    sizeof(struct in6_addr));
8742099bcdfStodd 				set_ipmask(rule->peer, 128);
8751edc1b9aShshoexer 				break;
8761edc1b9aShshoexer 			default:
8771edc1b9aShshoexer 				return (1);
8781edc1b9aShshoexer 			}
8791edc1b9aShshoexer 			break;
8801edc1b9aShshoexer 
8811edc1b9aShshoexer 		case SADB_EXT_IDENTITY_SRC:
8821edc1b9aShshoexer 			sident = (struct sadb_ident *)ext;
8831edc1b9aShshoexer 			len = (sident->sadb_ident_len * sizeof(uint64_t)) -
8841edc1b9aShshoexer 			    sizeof(struct sadb_ident);
8851edc1b9aShshoexer 
8869382ed8fShshoexer 			if (rule->auth == NULL) {
8879382ed8fShshoexer 				rule->auth = calloc(1, sizeof(struct
8889382ed8fShshoexer 				    ipsec_auth));
8899382ed8fShshoexer 				if (rule->auth == NULL)
890bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
8919382ed8fShshoexer 			}
8929382ed8fShshoexer 
893abe65127Shshoexer 			rule->auth->srcid = calloc(1, len);
894abe65127Shshoexer 			if (rule->auth->srcid == NULL)
895bd828a90Shshoexer 				err(1, "pfkey_parse: calloc");
8961edc1b9aShshoexer 
897abe65127Shshoexer 			strlcpy(rule->auth->srcid, (char *)(sident + 1), len);
8981edc1b9aShshoexer 			break;
8991edc1b9aShshoexer 
9001edc1b9aShshoexer 		case SADB_EXT_IDENTITY_DST:
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->dstid = calloc(1, len);
913abe65127Shshoexer 			if (rule->auth->dstid == NULL)
914bd828a90Shshoexer 				err(1, "pfkey_parse: calloc");
9151edc1b9aShshoexer 
916abe65127Shshoexer 			strlcpy(rule->auth->dstid, (char *)(sident + 1), len);
9171edc1b9aShshoexer 			break;
9181edc1b9aShshoexer 
9191edc1b9aShshoexer 		case SADB_X_EXT_PROTOCOL:
9209182219dSmarkus 			sproto = (struct sadb_protocol *)ext;
9219182219dSmarkus 			if (sproto->sadb_protocol_direction == 0)
9229182219dSmarkus 				rule->proto = sproto->sadb_protocol_proto;
9231edc1b9aShshoexer 			break;
9241edc1b9aShshoexer 
9251edc1b9aShshoexer 		case SADB_X_EXT_FLOW_TYPE:
9261edc1b9aShshoexer 			sproto = (struct sadb_protocol *)ext;
9271edc1b9aShshoexer 
9281edc1b9aShshoexer 			switch (sproto->sadb_protocol_direction) {
9291edc1b9aShshoexer 			case IPSP_DIRECTION_IN:
9301edc1b9aShshoexer 				rule->direction = IPSEC_IN;
9311edc1b9aShshoexer 				break;
9321edc1b9aShshoexer 			case IPSP_DIRECTION_OUT:
9331edc1b9aShshoexer 				rule->direction = IPSEC_OUT;
9341edc1b9aShshoexer 				break;
9351edc1b9aShshoexer 			default:
9361edc1b9aShshoexer 				return (1);
9371edc1b9aShshoexer 			}
9386122c05eShshoexer 			switch (sproto->sadb_protocol_proto) {
9396122c05eShshoexer 			case SADB_X_FLOW_TYPE_USE:
9401a3f035aShshoexer 				rule->flowtype = TYPE_USE;
9416122c05eShshoexer 				break;
9426122c05eShshoexer 			case SADB_X_FLOW_TYPE_ACQUIRE:
9431a3f035aShshoexer 				rule->flowtype = TYPE_ACQUIRE;
9446122c05eShshoexer 				break;
9456122c05eShshoexer 			case SADB_X_FLOW_TYPE_REQUIRE:
9461a3f035aShshoexer 				rule->flowtype = TYPE_REQUIRE;
9476122c05eShshoexer 				break;
9486122c05eShshoexer 			case SADB_X_FLOW_TYPE_DENY:
9491a3f035aShshoexer 				rule->flowtype = TYPE_DENY;
9506122c05eShshoexer 				break;
9516122c05eShshoexer 			case SADB_X_FLOW_TYPE_BYPASS:
9521a3f035aShshoexer 				rule->flowtype = TYPE_BYPASS;
9536122c05eShshoexer 				break;
9546122c05eShshoexer 			case SADB_X_FLOW_TYPE_DONTACQ:
9551a3f035aShshoexer 				rule->flowtype = TYPE_DONTACQ;
9566122c05eShshoexer 				break;
9576122c05eShshoexer 			default:
9581a3f035aShshoexer 				rule->flowtype = TYPE_UNKNOWN;
9596122c05eShshoexer 				break;
9606122c05eShshoexer 			}
9611edc1b9aShshoexer 			break;
9621edc1b9aShshoexer 
9631edc1b9aShshoexer 		case SADB_X_EXT_SRC_FLOW:
9641edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
9651edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
9661edc1b9aShshoexer 
9671edc1b9aShshoexer 			if (rule->src == NULL) {
9681edc1b9aShshoexer 				rule->src = calloc(1,
96991f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
9701edc1b9aShshoexer 				if (rule->src == NULL)
971bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
9721edc1b9aShshoexer 			}
9731edc1b9aShshoexer 
9742099bcdfStodd 			rule->src->af = sa->sa_family;
9751edc1b9aShshoexer 			switch (sa->sa_family) {
9761edc1b9aShshoexer 			case AF_INET:
9771edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
978712e78baShshoexer 				    &rule->src->address.v4,
979712e78baShshoexer 				    sizeof(struct in_addr));
98057f58d0dSnaddy 				rule->sport =
98157f58d0dSnaddy 				    ((struct sockaddr_in *)sa)->sin_port;
9822099bcdfStodd 				break;
9832099bcdfStodd 			case AF_INET6:
9842099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
9852099bcdfStodd 				    &rule->src->address.v6,
9862099bcdfStodd 				    sizeof(struct in6_addr));
98757f58d0dSnaddy 				rule->sport =
98857f58d0dSnaddy 				    ((struct sockaddr_in6 *)sa)->sin6_port;
9891edc1b9aShshoexer 				break;
9901edc1b9aShshoexer 			default:
9911edc1b9aShshoexer 				return (1);
9921edc1b9aShshoexer 			}
9931edc1b9aShshoexer 			break;
9941edc1b9aShshoexer 
9951edc1b9aShshoexer 		case SADB_X_EXT_DST_FLOW:
9961edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
9971edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
9981edc1b9aShshoexer 
9991edc1b9aShshoexer 			if (rule->dst == NULL) {
10001edc1b9aShshoexer 				rule->dst = calloc(1,
100191f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
10021edc1b9aShshoexer 				if (rule->dst == NULL)
1003bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
10041edc1b9aShshoexer 			}
10051edc1b9aShshoexer 
10062099bcdfStodd 			rule->dst->af = sa->sa_family;
10071edc1b9aShshoexer 			switch (sa->sa_family) {
10081edc1b9aShshoexer 			case AF_INET:
10091edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
1010712e78baShshoexer 				    &rule->dst->address.v4,
1011712e78baShshoexer 				    sizeof(struct in_addr));
101257f58d0dSnaddy 				rule->dport =
101357f58d0dSnaddy 				    ((struct sockaddr_in *)sa)->sin_port;
10141edc1b9aShshoexer 				break;
10152099bcdfStodd 			case AF_INET6:
10162099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
10172099bcdfStodd 				    &rule->dst->address.v6,
10182099bcdfStodd 				    sizeof(struct in6_addr));
101957f58d0dSnaddy 				rule->dport =
102057f58d0dSnaddy 				    ((struct sockaddr_in6 *)sa)->sin6_port;
10212099bcdfStodd 				break;
10221edc1b9aShshoexer 			default:
10231edc1b9aShshoexer 				return (1);
10241edc1b9aShshoexer 			}
10251edc1b9aShshoexer 			break;
10261edc1b9aShshoexer 
10271edc1b9aShshoexer 
10281edc1b9aShshoexer 		case SADB_X_EXT_SRC_MASK:
10291edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
10301edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
10311edc1b9aShshoexer 
10321edc1b9aShshoexer 			if (rule->src == NULL) {
10331edc1b9aShshoexer 				rule->src = calloc(1,
103491f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
10351edc1b9aShshoexer 				if (rule->src == NULL)
1036bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
10371edc1b9aShshoexer 			}
10381edc1b9aShshoexer 
10392099bcdfStodd 			rule->src->af = sa->sa_family;
10401edc1b9aShshoexer 			switch (sa->sa_family) {
10411edc1b9aShshoexer 			case AF_INET:
1042712e78baShshoexer 				sa_in = (struct sockaddr_in *)sa;
1043712e78baShshoexer 				bcopy(&sa_in->sin_addr, &rule->src->mask.v4,
10441edc1b9aShshoexer 				    sizeof(struct in_addr));
10452099bcdfStodd 				break;
10462099bcdfStodd 			case AF_INET6:
10472099bcdfStodd 				sa_in6 = (struct sockaddr_in6 *)sa;
10482099bcdfStodd 				bcopy(&sa_in6->sin6_addr, &rule->src->mask.v6,
10492099bcdfStodd 				    sizeof(struct in6_addr));
10501edc1b9aShshoexer 				break;
10511edc1b9aShshoexer 
10521edc1b9aShshoexer 			default:
10531edc1b9aShshoexer 				return (1);
10541edc1b9aShshoexer 			}
10551edc1b9aShshoexer 			break;
10561edc1b9aShshoexer 
10571edc1b9aShshoexer 		case SADB_X_EXT_DST_MASK:
10581edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
10591edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
10601edc1b9aShshoexer 
10611edc1b9aShshoexer 			if (rule->dst == NULL) {
10621edc1b9aShshoexer 				rule->dst = calloc(1,
106391f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
10641edc1b9aShshoexer 				if (rule->dst == NULL)
1065bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
10661edc1b9aShshoexer 			}
10671edc1b9aShshoexer 
10682099bcdfStodd 			rule->dst->af = sa->sa_family;
10691edc1b9aShshoexer 			switch (sa->sa_family) {
10701edc1b9aShshoexer 			case AF_INET:
1071712e78baShshoexer 				sa_in = (struct sockaddr_in *)sa;
1072712e78baShshoexer 				bcopy(&sa_in->sin_addr, &rule->dst->mask.v4,
10731edc1b9aShshoexer 				    sizeof(struct in_addr));
10741edc1b9aShshoexer 				break;
10752099bcdfStodd 			case AF_INET6:
10762099bcdfStodd 				sa_in6 = (struct sockaddr_in6 *)sa;
10772099bcdfStodd 				bcopy(&sa_in6->sin6_addr, &rule->dst->mask.v6,
10782099bcdfStodd 				    sizeof(struct in6_addr));
10792099bcdfStodd 				break;
10801edc1b9aShshoexer 			default:
10811edc1b9aShshoexer 				return (1);
10821edc1b9aShshoexer 			}
10831edc1b9aShshoexer 			break;
10841edc1b9aShshoexer 
10851edc1b9aShshoexer 		default:
10861edc1b9aShshoexer 			return (1);
10871edc1b9aShshoexer 		}
10881edc1b9aShshoexer 	}
10891edc1b9aShshoexer 
10901edc1b9aShshoexer 	return (0);
10911edc1b9aShshoexer }
10921edc1b9aShshoexer 
10931edc1b9aShshoexer int
1094356121f6Shshoexer pfkey_ipsec_establish(int action, struct ipsec_rule *r)
1095f484f2cfShshoexer {
109622a29ad6Shshoexer 	int		ret;
10978065703bShshoexer 	u_int8_t	satype, satype2, direction;
1098f484f2cfShshoexer 
1099f032086dShshoexer 	if (r->type == RULE_FLOW) {
11009182219dSmarkus 		switch (r->satype) {
1101f484f2cfShshoexer 		case IPSEC_ESP:
1102f484f2cfShshoexer 			satype = SADB_SATYPE_ESP;
1103f484f2cfShshoexer 			break;
1104f484f2cfShshoexer 		case IPSEC_AH:
1105f484f2cfShshoexer 			satype = SADB_SATYPE_AH;
1106f484f2cfShshoexer 			break;
110772e25333Shshoexer 		case IPSEC_IPCOMP:
110872e25333Shshoexer 			satype = SADB_X_SATYPE_IPCOMP;
110972e25333Shshoexer 			break;
111088a8cceeSmarkus 		case IPSEC_IPIP:
111188a8cceeSmarkus 			satype = SADB_X_SATYPE_IPIP;
111288a8cceeSmarkus 			break;
1113f484f2cfShshoexer 		default:
1114f484f2cfShshoexer 			return -1;
1115f484f2cfShshoexer 		}
1116f484f2cfShshoexer 
1117f484f2cfShshoexer 		switch (r->direction) {
1118f484f2cfShshoexer 		case IPSEC_IN:
1119f484f2cfShshoexer 			direction = IPSP_DIRECTION_IN;
1120f484f2cfShshoexer 			break;
1121f484f2cfShshoexer 		case IPSEC_OUT:
1122f484f2cfShshoexer 			direction = IPSP_DIRECTION_OUT;
1123f484f2cfShshoexer 			break;
1124f484f2cfShshoexer 		default:
1125f484f2cfShshoexer 			return -1;
1126f484f2cfShshoexer 		}
1127f484f2cfShshoexer 
112822a29ad6Shshoexer 		switch (action) {
112990bd57a7Shshoexer 		case ACTION_ADD:
1130f032086dShshoexer 			ret = pfkey_flow(fd, satype, SADB_X_ADDFLOW, direction,
113157f58d0dSnaddy 			    r->proto, r->src, r->sport, r->dst, r->dport,
113257f58d0dSnaddy 			    r->local, r->peer, r->auth, r->flowtype);
113322a29ad6Shshoexer 			break;
113490bd57a7Shshoexer 		case ACTION_DELETE:
113522a29ad6Shshoexer 			/* No peer for flow deletion. */
1136f032086dShshoexer 			ret = pfkey_flow(fd, satype, SADB_X_DELFLOW, direction,
113757f58d0dSnaddy 			    r->proto, r->src, r->sport, r->dst, r->dport,
113857f58d0dSnaddy 			    NULL, NULL, NULL, r->flowtype);
113922a29ad6Shshoexer 			break;
114022a29ad6Shshoexer 		default:
114122a29ad6Shshoexer 			return -1;
114222a29ad6Shshoexer 		}
1143f032086dShshoexer 	} else if (r->type == RULE_SA) {
11449182219dSmarkus 		switch (r->satype) {
1145881e2068Shshoexer 		case IPSEC_AH:
1146881e2068Shshoexer 			satype = SADB_SATYPE_AH;
1147881e2068Shshoexer 			break;
1148881e2068Shshoexer 		case IPSEC_ESP:
1149881e2068Shshoexer 			satype = SADB_SATYPE_ESP;
1150881e2068Shshoexer 			break;
115172e25333Shshoexer 		case IPSEC_IPCOMP:
115272e25333Shshoexer 			satype = SADB_X_SATYPE_IPCOMP;
115372e25333Shshoexer 			break;
1154381a2422Shshoexer 		case IPSEC_TCPMD5:
1155f032086dShshoexer 			satype = SADB_X_SATYPE_TCPSIGNATURE;
1156381a2422Shshoexer 			break;
115788a8cceeSmarkus 		case IPSEC_IPIP:
115888a8cceeSmarkus 			satype = SADB_X_SATYPE_IPIP;
115988a8cceeSmarkus 			break;
1160381a2422Shshoexer 		default:
1161381a2422Shshoexer 			return -1;
1162381a2422Shshoexer 		}
1163f032086dShshoexer 		switch (action) {
116490bd57a7Shshoexer 		case ACTION_ADD:
1165f032086dShshoexer 			ret = pfkey_sa(fd, satype, SADB_ADD, r->spi,
1166a38d220fShshoexer 			    r->src, r->dst, r->xfs, r->authkey, r->enckey,
1167a38d220fShshoexer 			    r->tmode);
1168f032086dShshoexer 			break;
116990bd57a7Shshoexer 		case ACTION_DELETE:
1170f032086dShshoexer 			ret = pfkey_sa(fd, satype, SADB_DELETE, r->spi,
1171a38d220fShshoexer 			    r->src, r->dst, r->xfs, NULL, NULL, r->tmode);
1172f032086dShshoexer 			break;
1173f032086dShshoexer 		default:
1174f032086dShshoexer 			return -1;
1175f032086dShshoexer 		}
11768f2109caShshoexer 	} else if (r->type == RULE_GROUP) {
11778f2109caShshoexer 		switch (r->satype) {
11788f2109caShshoexer 		case IPSEC_AH:
11798f2109caShshoexer 			satype = SADB_SATYPE_AH;
11808f2109caShshoexer 			break;
11818f2109caShshoexer 		case IPSEC_ESP:
11828f2109caShshoexer 			satype = SADB_SATYPE_ESP;
11838f2109caShshoexer 			break;
11848f2109caShshoexer 		case IPSEC_IPCOMP:
11858f2109caShshoexer 			satype = SADB_X_SATYPE_IPCOMP;
11868f2109caShshoexer 			break;
11878f2109caShshoexer 		case IPSEC_TCPMD5:
11888f2109caShshoexer 			satype = SADB_X_SATYPE_TCPSIGNATURE;
11898f2109caShshoexer 			break;
11908f2109caShshoexer 		case IPSEC_IPIP:
11918f2109caShshoexer 			satype = SADB_X_SATYPE_IPIP;
11928f2109caShshoexer 			break;
11938f2109caShshoexer 		default:
11948f2109caShshoexer 			return -1;
11958f2109caShshoexer 		}
11968065703bShshoexer 		switch (r->proto2) {
11978065703bShshoexer 		case IPSEC_AH:
11988065703bShshoexer 			satype2 = SADB_SATYPE_AH;
11998065703bShshoexer 			break;
12008065703bShshoexer 		case IPSEC_ESP:
12018065703bShshoexer 			satype2 = SADB_SATYPE_ESP;
12028065703bShshoexer 			break;
12038065703bShshoexer 		case IPSEC_IPCOMP:
12048065703bShshoexer 			satype2 = SADB_X_SATYPE_IPCOMP;
12058065703bShshoexer 			break;
12068065703bShshoexer 		case IPSEC_TCPMD5:
12078065703bShshoexer 			satype2 = SADB_X_SATYPE_TCPSIGNATURE;
12088065703bShshoexer 			break;
12098065703bShshoexer 		case IPSEC_IPIP:
12108065703bShshoexer 			satype2 = SADB_X_SATYPE_IPIP;
12118065703bShshoexer 			break;
12128065703bShshoexer 		default:
12138065703bShshoexer 			return -1;
12148065703bShshoexer 		}
12158f2109caShshoexer 		switch (action) {
12168f2109caShshoexer 		case ACTION_ADD:
12178065703bShshoexer 			ret = pfkey_sagroup(fd, satype, satype2,
12188065703bShshoexer 			    SADB_X_GRPSPIS, r->dst, r->spi, r->dst2, r->spi2);
12198f2109caShshoexer 			break;
12208f2109caShshoexer 		case ACTION_DELETE:
12218f2109caShshoexer 			return 0;
12228f2109caShshoexer 		default:
12238f2109caShshoexer 			return -1;
12248f2109caShshoexer 		}
1225f032086dShshoexer 	} else
1226f032086dShshoexer 		return -1;
1227f032086dShshoexer 
122822a29ad6Shshoexer 	if (ret < 0)
1229f484f2cfShshoexer 		return -1;
12308a87fca6Smsf 	if (pfkey_reply(fd, NULL, NULL) < 0)
1231f484f2cfShshoexer 		return -1;
1232f484f2cfShshoexer 
1233f484f2cfShshoexer 	return 0;
1234f484f2cfShshoexer }
1235f484f2cfShshoexer 
1236f484f2cfShshoexer int
1237f484f2cfShshoexer pfkey_ipsec_flush(void)
1238f484f2cfShshoexer {
1239f484f2cfShshoexer 	struct sadb_msg smsg;
1240f484f2cfShshoexer 	struct iovec	iov[IOV_CNT];
1241f484f2cfShshoexer 	ssize_t		n;
1242f484f2cfShshoexer 	int		iov_cnt, len;
1243f484f2cfShshoexer 
1244f484f2cfShshoexer 	bzero(&smsg, sizeof(smsg));
1245f484f2cfShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
1246f484f2cfShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
1247f484f2cfShshoexer 	smsg.sadb_msg_pid = getpid();
1248f484f2cfShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
1249f484f2cfShshoexer 	smsg.sadb_msg_type = SADB_FLUSH;
1250f484f2cfShshoexer 	smsg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
1251f484f2cfShshoexer 
1252f484f2cfShshoexer 	iov_cnt = 0;
1253f484f2cfShshoexer 
1254f484f2cfShshoexer 	iov[iov_cnt].iov_base = &smsg;
1255f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
1256f484f2cfShshoexer 	iov_cnt++;
1257f484f2cfShshoexer 
1258f484f2cfShshoexer 	len = smsg.sadb_msg_len * 8;
1259f484f2cfShshoexer 	if ((n = writev(fd, iov, iov_cnt)) == -1) {
1260f484f2cfShshoexer 		warn("writev failed");
1261f484f2cfShshoexer 		return -1;
1262f484f2cfShshoexer 	}
1263f484f2cfShshoexer 	if (n != len) {
1264f484f2cfShshoexer 		warnx("short write");
1265f484f2cfShshoexer 		return -1;
1266f484f2cfShshoexer 	}
12678a87fca6Smsf 	if (pfkey_reply(fd, NULL, NULL) < 0)
1268f484f2cfShshoexer 		return -1;
1269f484f2cfShshoexer 
1270f484f2cfShshoexer 	return 0;
1271f484f2cfShshoexer }
1272f484f2cfShshoexer 
12738a87fca6Smsf static int
12748a87fca6Smsf pfkey_promisc(void)
12758a87fca6Smsf {
12768a87fca6Smsf 	struct sadb_msg msg;
12778a87fca6Smsf 
12788a87fca6Smsf 	memset(&msg, 0, sizeof(msg));
12798a87fca6Smsf 	msg.sadb_msg_version = PF_KEY_V2;
12808a87fca6Smsf 	msg.sadb_msg_seq = sadb_msg_seq++;
12818a87fca6Smsf 	msg.sadb_msg_pid = getpid();
12828a87fca6Smsf 	msg.sadb_msg_len = sizeof(msg) / PFKEYV2_CHUNK;
12838a87fca6Smsf 	msg.sadb_msg_type = SADB_X_PROMISC;
12848a87fca6Smsf 	msg.sadb_msg_satype = 1;	/* enable */
12858a87fca6Smsf 	if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) {
12868a87fca6Smsf 		warn("pfkey_promisc: write failed");
12878a87fca6Smsf 		return -1;
12888a87fca6Smsf 	}
12898a87fca6Smsf 	if (pfkey_reply(fd, NULL, NULL) < 0)
12908a87fca6Smsf 		return -1;
12918a87fca6Smsf 	return 0;
12928a87fca6Smsf }
12938a87fca6Smsf 
12948a87fca6Smsf int
12958a87fca6Smsf pfkey_monitor(int opts)
12968a87fca6Smsf {
12978a87fca6Smsf 	fd_set *rset;
12988a87fca6Smsf 	u_int8_t *data;
12998a87fca6Smsf 	struct sadb_msg *msg;
13008a87fca6Smsf 	ssize_t len, set_size;
13018a87fca6Smsf 	int n;
13028a87fca6Smsf 
13038a87fca6Smsf 	if (pfkey_init() < 0)
13048a87fca6Smsf 		return -1;
13058a87fca6Smsf 	if (pfkey_promisc() < 0)
13068a87fca6Smsf 		return -1;
13078a87fca6Smsf 
13088a87fca6Smsf 	set_size = howmany(fd + 1, NFDBITS) * sizeof(fd_mask);
13098a87fca6Smsf 	if ((rset = malloc(set_size)) == NULL) {
13108a87fca6Smsf 		warn("malloc");
13118a87fca6Smsf 		return -1;
13128a87fca6Smsf 	}
13138a87fca6Smsf 	for (;;) {
13148a87fca6Smsf 		memset(rset, 0, set_size);
13158a87fca6Smsf 		FD_SET(fd, rset);
13168a87fca6Smsf 		if ((n = select(fd+1, rset, NULL, NULL, NULL)) < 0)
13178a87fca6Smsf 			err(2, "select");
13188a87fca6Smsf 		if (n == 0)
13198a87fca6Smsf 			break;
13208a87fca6Smsf 		if (!FD_ISSET(fd, rset))
13218a87fca6Smsf 			continue;
13228a87fca6Smsf 		if (pfkey_reply(fd, &data, &len) < 0)
13238a87fca6Smsf 			continue;
13248a87fca6Smsf 		msg = (struct sadb_msg *)data;
13258a87fca6Smsf 		if (msg->sadb_msg_type == SADB_X_PROMISC) {
13268a87fca6Smsf 			/* remove extra header from promisc messages */
13278a87fca6Smsf 			if ((msg->sadb_msg_len * PFKEYV2_CHUNK) >=
13288a87fca6Smsf 			    2 * sizeof(struct sadb_msg)) {
13298a87fca6Smsf 				msg++;
13308a87fca6Smsf 			}
13318a87fca6Smsf 		}
13328a87fca6Smsf 		pfkey_monitor_sa(msg, opts);
13338a87fca6Smsf 		if (opts & IPSECCTL_OPT_VERBOSE)
13348a87fca6Smsf 			pfkey_print_raw(data, len);
13358a87fca6Smsf 		memset(data, 0, len);
13368a87fca6Smsf 		free(data);
13378a87fca6Smsf 	}
13388a87fca6Smsf 	close(fd);
13398a87fca6Smsf 	return 0;
13408a87fca6Smsf }
13418a87fca6Smsf 
1342f484f2cfShshoexer int
1343f484f2cfShshoexer pfkey_init(void)
1344f484f2cfShshoexer {
1345f484f2cfShshoexer 	if ((fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) == -1)
1346bd828a90Shshoexer 		err(1, "pfkey_init: failed to open PF_KEY socket");
1347f484f2cfShshoexer 
1348f484f2cfShshoexer 	return 0;
1349f484f2cfShshoexer }
1350