xref: /openbsd/sbin/ipsecctl/pfkey.c (revision 783454c1)
1*783454c1Smikeb /*	$OpenBSD: pfkey.c,v 1.50 2010/09/22 14:04:09 mikeb 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:
491*783454c1Smikeb 		case ENCXF_AES_128:
492*783454c1Smikeb 		case ENCXF_AES_192:
493*783454c1Smikeb 		case ENCXF_AES_256:
494881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_AES;
495881e2068Shshoexer 			break;
496881e2068Shshoexer 		case ENCXF_AESCTR:
497881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_AESCTR;
498881e2068Shshoexer 			break;
499*783454c1Smikeb 		case ENCXF_AES_128_GCM:
500*783454c1Smikeb 		case ENCXF_AES_192_GCM:
501*783454c1Smikeb 		case ENCXF_AES_256_GCM:
502*783454c1Smikeb 			sa.sadb_sa_encrypt = SADB_X_EALG_AESGCM16;
503*783454c1Smikeb 			break;
504*783454c1Smikeb 		case ENCXF_AES_128_GMAC:
505*783454c1Smikeb 		case ENCXF_AES_192_GMAC:
506*783454c1Smikeb 		case ENCXF_AES_256_GMAC:
507*783454c1Smikeb 			sa.sadb_sa_encrypt = SADB_X_EALG_AESGMAC;
508*783454c1Smikeb 			break;
509881e2068Shshoexer 		case ENCXF_BLOWFISH:
510881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_BLF;
511881e2068Shshoexer 			break;
512881e2068Shshoexer 		case ENCXF_CAST128:
513881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_CAST;
514881e2068Shshoexer 			break;
515881e2068Shshoexer 		case ENCXF_NULL:
516881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_EALG_NULL;
517881e2068Shshoexer 			break;
518881e2068Shshoexer 		case ENCXF_SKIPJACK:
519881e2068Shshoexer 			sa.sadb_sa_encrypt = SADB_X_EALG_SKIPJACK;
520881e2068Shshoexer 			break;
521881e2068Shshoexer 		default:
522375db29dShshoexer 			warnx("unsupported encryption algorithm %d",
523375db29dShshoexer 			    xfs->encxf->id);
524881e2068Shshoexer 		}
525881e2068Shshoexer 	}
52672e25333Shshoexer 	if (xfs && xfs->compxf) {
52772e25333Shshoexer 		switch (xfs->compxf->id) {
52872e25333Shshoexer 		case COMPXF_DEFLATE:
52972e25333Shshoexer 			sa.sadb_sa_encrypt = SADB_X_CALG_DEFLATE;
53072e25333Shshoexer 			break;
53172e25333Shshoexer 		case COMPXF_LZS:
53272e25333Shshoexer 			sa.sadb_sa_encrypt = SADB_X_CALG_LZS;
53372e25333Shshoexer 			break;
53472e25333Shshoexer 		default:
53572e25333Shshoexer 			warnx("unsupported compression algorithm %d",
53672e25333Shshoexer 			    xfs->compxf->id);
53772e25333Shshoexer 		}
53872e25333Shshoexer 	}
539881e2068Shshoexer 
540f032086dShshoexer 	bzero(&sa_src, sizeof(sa_src));
541f032086dShshoexer 	sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8;
542f032086dShshoexer 	sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
543f032086dShshoexer 
544f032086dShshoexer 	bzero(&sa_dst, sizeof(sa_dst));
545f032086dShshoexer 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
546f032086dShshoexer 	sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
547f032086dShshoexer 
54872e25333Shshoexer 	if (action == SADB_ADD && !authkey && !enckey && satype !=
54988a8cceeSmarkus 	    SADB_X_SATYPE_IPCOMP && satype != SADB_X_SATYPE_IPIP) { /* XXX ENCNULL */
5500813ab45Shshoexer 		warnx("no key specified");
5510813ab45Shshoexer 		return -1;
5520813ab45Shshoexer 	}
553881e2068Shshoexer 	if (authkey) {
554881e2068Shshoexer 		bzero(&sa_authkey, sizeof(sa_authkey));
555881e2068Shshoexer 		sa_authkey.sadb_key_len = (sizeof(sa_authkey) +
556881e2068Shshoexer 		    ((authkey->len + 7) / 8) * 8) / 8;
557881e2068Shshoexer 		sa_authkey.sadb_key_exttype = SADB_EXT_KEY_AUTH;
558881e2068Shshoexer 		sa_authkey.sadb_key_bits = 8 * authkey->len;
559881e2068Shshoexer 	}
560881e2068Shshoexer 	if (enckey) {
561881e2068Shshoexer 		bzero(&sa_enckey, sizeof(sa_enckey));
562881e2068Shshoexer 		sa_enckey.sadb_key_len = (sizeof(sa_enckey) +
563881e2068Shshoexer 		    ((enckey->len + 7) / 8) * 8) / 8;
564881e2068Shshoexer 		sa_enckey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
565881e2068Shshoexer 		sa_enckey.sadb_key_bits = 8 * enckey->len;
566f032086dShshoexer 	}
567f032086dShshoexer 
568f032086dShshoexer 	iov_cnt = 0;
569f032086dShshoexer 
570f032086dShshoexer 	/* header */
571f032086dShshoexer 	iov[iov_cnt].iov_base = &smsg;
572f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
573f032086dShshoexer 	iov_cnt++;
574f032086dShshoexer 
575f032086dShshoexer 	/* sa */
576f032086dShshoexer 	iov[iov_cnt].iov_base = &sa;
577f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(sa);
578f032086dShshoexer 	smsg.sadb_msg_len += sa.sadb_sa_len;
579f032086dShshoexer 	iov_cnt++;
580f032086dShshoexer 
581f032086dShshoexer 	/* src addr */
582f032086dShshoexer 	iov[iov_cnt].iov_base = &sa_src;
583f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_src);
584f032086dShshoexer 	iov_cnt++;
585f032086dShshoexer 	iov[iov_cnt].iov_base = &ssrc;
586f032086dShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len);
587f032086dShshoexer 	smsg.sadb_msg_len += sa_src.sadb_address_len;
588f032086dShshoexer 	iov_cnt++;
589f032086dShshoexer 
590f032086dShshoexer 	/* dst addr */
591f032086dShshoexer 	iov[iov_cnt].iov_base = &sa_dst;
592f032086dShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst);
593f032086dShshoexer 	iov_cnt++;
594f032086dShshoexer 	iov[iov_cnt].iov_base = &sdst;
595f032086dShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
596f032086dShshoexer 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
597f032086dShshoexer 	iov_cnt++;
598f032086dShshoexer 
599881e2068Shshoexer 	if (authkey) {
600881e2068Shshoexer 		/* authentication key */
601881e2068Shshoexer 		iov[iov_cnt].iov_base = &sa_authkey;
602881e2068Shshoexer 		iov[iov_cnt].iov_len = sizeof(sa_authkey);
603f032086dShshoexer 		iov_cnt++;
604881e2068Shshoexer 		iov[iov_cnt].iov_base = authkey->data;
605881e2068Shshoexer 		iov[iov_cnt].iov_len = ((authkey->len + 7) / 8) * 8;
606881e2068Shshoexer 		smsg.sadb_msg_len += sa_authkey.sadb_key_len;
607881e2068Shshoexer 		iov_cnt++;
608881e2068Shshoexer 	}
609881e2068Shshoexer 	if (enckey) {
610881e2068Shshoexer 		/* encryption key */
611881e2068Shshoexer 		iov[iov_cnt].iov_base = &sa_enckey;
612881e2068Shshoexer 		iov[iov_cnt].iov_len = sizeof(sa_enckey);
613881e2068Shshoexer 		iov_cnt++;
614881e2068Shshoexer 		iov[iov_cnt].iov_base = enckey->data;
615881e2068Shshoexer 		iov[iov_cnt].iov_len = ((enckey->len + 7) / 8) * 8;
616881e2068Shshoexer 		smsg.sadb_msg_len += sa_enckey.sadb_key_len;
617f032086dShshoexer 		iov_cnt++;
618f032086dShshoexer 	}
619f032086dShshoexer 
620f032086dShshoexer 	len = smsg.sadb_msg_len * 8;
621f032086dShshoexer 	if ((n = writev(sd, iov, iov_cnt)) == -1) {
622f032086dShshoexer 		warn("writev failed");
623f032086dShshoexer 		ret = -1;
624f032086dShshoexer 	} else if (n != len) {
625f032086dShshoexer 		warnx("short write");
626f032086dShshoexer 		ret = -1;
627f032086dShshoexer 	}
628f032086dShshoexer 
629f032086dShshoexer 	return ret;
630f032086dShshoexer }
631f032086dShshoexer 
632f032086dShshoexer static int
6338065703bShshoexer pfkey_sagroup(int sd, u_int8_t satype, u_int8_t satype2, u_int8_t action,
6348065703bShshoexer     struct ipsec_addr_wrap *dst, u_int32_t spi, struct ipsec_addr_wrap *dst2,
6358065703bShshoexer     u_int32_t spi2)
6368f2109caShshoexer {
6378f2109caShshoexer 	struct sadb_msg		smsg;
6388f2109caShshoexer 	struct sadb_sa		sa1, sa2;
6398f2109caShshoexer 	struct sadb_address	sa_dst, sa_dst2;
6408f2109caShshoexer 	struct sockaddr_storage	sdst, sdst2;
6418f2109caShshoexer 	struct sadb_protocol	sa_proto;
6428f2109caShshoexer 	struct iovec		iov[IOV_CNT];
6438f2109caShshoexer 	ssize_t			n;
6448f2109caShshoexer 	int			iov_cnt, len, ret = 0;
6458f2109caShshoexer 
6468f2109caShshoexer 	bzero(&sdst, sizeof(sdst));
6478f2109caShshoexer 	sdst.ss_family = dst->af;
6488f2109caShshoexer 	switch (dst->af) {
6498f2109caShshoexer 	case AF_INET:
6508f2109caShshoexer 		((struct sockaddr_in *)&sdst)->sin_addr = dst->address.v4;
6518f2109caShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in);
6528f2109caShshoexer 		break;
6538f2109caShshoexer 	case AF_INET6:
6548f2109caShshoexer 		((struct sockaddr_in6 *)&sdst)->sin6_addr = dst->address.v6;
6558f2109caShshoexer 		sdst.ss_len = sizeof(struct sockaddr_in6);
6568f2109caShshoexer 		break;
6578f2109caShshoexer 	default:
6588f2109caShshoexer 		warnx("unsupported address family %d", dst->af);
6598f2109caShshoexer 		return -1;
6608f2109caShshoexer 	}
6618f2109caShshoexer 
6628f2109caShshoexer 	bzero(&sdst2, sizeof(sdst2));
6638f2109caShshoexer 	sdst2.ss_family = dst2->af;
6648f2109caShshoexer 	switch (dst2->af) {
6658f2109caShshoexer 	case AF_INET:
6668f2109caShshoexer 		((struct sockaddr_in *)&sdst2)->sin_addr = dst2->address.v4;
6678f2109caShshoexer 		sdst2.ss_len = sizeof(struct sockaddr_in);
6688f2109caShshoexer 		break;
6698f2109caShshoexer 	case AF_INET6:
6708f2109caShshoexer 		((struct sockaddr_in6 *)&sdst2)->sin6_addr = dst2->address.v6;
6718f2109caShshoexer 		sdst2.ss_len = sizeof(struct sockaddr_in6);
6728f2109caShshoexer 		break;
6738f2109caShshoexer 	default:
6748f2109caShshoexer 		warnx("unsupported address family %d", dst2->af);
6758f2109caShshoexer 		return -1;
6768f2109caShshoexer 	}
6778f2109caShshoexer 
6788f2109caShshoexer 	bzero(&smsg, sizeof(smsg));
6798f2109caShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
6808f2109caShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
6818f2109caShshoexer 	smsg.sadb_msg_pid = getpid();
6828f2109caShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
6838f2109caShshoexer 	smsg.sadb_msg_type = action;
6848f2109caShshoexer 	smsg.sadb_msg_satype = satype;
6858f2109caShshoexer 
6868f2109caShshoexer 	bzero(&sa1, sizeof(sa1));
6878f2109caShshoexer 	sa1.sadb_sa_len = sizeof(sa1) / 8;
6888f2109caShshoexer 	sa1.sadb_sa_exttype = SADB_EXT_SA;
6898f2109caShshoexer 	sa1.sadb_sa_spi = htonl(spi);
6908f2109caShshoexer 	sa1.sadb_sa_state = SADB_SASTATE_MATURE;
6918f2109caShshoexer 
6928f2109caShshoexer 	bzero(&sa2, sizeof(sa2));
6938f2109caShshoexer 	sa2.sadb_sa_len = sizeof(sa2) / 8;
6948f2109caShshoexer 	sa2.sadb_sa_exttype = SADB_X_EXT_SA2;
6958f2109caShshoexer 	sa2.sadb_sa_spi = htonl(spi2);
6968f2109caShshoexer 	sa2.sadb_sa_state = SADB_SASTATE_MATURE;
6978f2109caShshoexer 	iov_cnt = 0;
6988f2109caShshoexer 
6998f2109caShshoexer 	bzero(&sa_dst, sizeof(sa_dst));
7008f2109caShshoexer 	sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
7018f2109caShshoexer 	sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8;
7028f2109caShshoexer 
7038f2109caShshoexer 	bzero(&sa_dst2, sizeof(sa_dst2));
7048f2109caShshoexer 	sa_dst2.sadb_address_exttype = SADB_X_EXT_DST2;
7058f2109caShshoexer 	sa_dst2.sadb_address_len = (sizeof(sa_dst2) + ROUNDUP(sdst2.ss_len)) / 8;
7068f2109caShshoexer 
7078f2109caShshoexer 	bzero(&sa_proto, sizeof(sa_proto));
7088f2109caShshoexer 	sa_proto.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
7098f2109caShshoexer 	sa_proto.sadb_protocol_len = sizeof(sa_proto) / 8;
7108f2109caShshoexer 	sa_proto.sadb_protocol_direction = 0;
7118065703bShshoexer 	sa_proto.sadb_protocol_proto = satype2;
7128f2109caShshoexer 
7138f2109caShshoexer 	/* header */
7148f2109caShshoexer 	iov[iov_cnt].iov_base = &smsg;
7158f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
7168f2109caShshoexer 	iov_cnt++;
7178f2109caShshoexer 
7188f2109caShshoexer 	/* sa */
7198f2109caShshoexer 	iov[iov_cnt].iov_base = &sa1;
7208f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa1);
7218f2109caShshoexer 	smsg.sadb_msg_len += sa1.sadb_sa_len;
7228f2109caShshoexer 	iov_cnt++;
7238f2109caShshoexer 
7248f2109caShshoexer 	/* dst addr */
7258f2109caShshoexer 	iov[iov_cnt].iov_base = &sa_dst;
7268f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst);
7278f2109caShshoexer 	iov_cnt++;
7288f2109caShshoexer 	iov[iov_cnt].iov_base = &sdst;
7298f2109caShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len);
7308f2109caShshoexer 	smsg.sadb_msg_len += sa_dst.sadb_address_len;
7318f2109caShshoexer 	iov_cnt++;
7328f2109caShshoexer 
7338f2109caShshoexer 	/* second sa */
7348f2109caShshoexer 	iov[iov_cnt].iov_base = &sa2;
7358f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa2);
7368f2109caShshoexer 	smsg.sadb_msg_len += sa2.sadb_sa_len;
7378f2109caShshoexer 	iov_cnt++;
7388f2109caShshoexer 
7398f2109caShshoexer 	/* second dst addr */
7408f2109caShshoexer 	iov[iov_cnt].iov_base = &sa_dst2;
7418f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_dst2);
7428f2109caShshoexer 	iov_cnt++;
7438f2109caShshoexer 	iov[iov_cnt].iov_base = &sdst2;
7448f2109caShshoexer 	iov[iov_cnt].iov_len = ROUNDUP(sdst2.ss_len);
7458f2109caShshoexer 	smsg.sadb_msg_len += sa_dst2.sadb_address_len;
7468f2109caShshoexer 	iov_cnt++;
7478f2109caShshoexer 
7488f2109caShshoexer 	/* SA type */
7498f2109caShshoexer 	iov[iov_cnt].iov_base = &sa_proto;
7508f2109caShshoexer 	iov[iov_cnt].iov_len = sizeof(sa_proto);
7518f2109caShshoexer 	smsg.sadb_msg_len += sa_proto.sadb_protocol_len;
7528f2109caShshoexer 	iov_cnt++;
7538f2109caShshoexer 
7548f2109caShshoexer 	len = smsg.sadb_msg_len * 8;
7558f2109caShshoexer 	if ((n = writev(sd, iov, iov_cnt)) == -1) {
7568f2109caShshoexer 		warn("writev failed");
7578f2109caShshoexer 		ret = -1;
7588f2109caShshoexer 	} else if (n != len) {
7598f2109caShshoexer 		warnx("short write");
7608f2109caShshoexer 		ret = -1;
7618f2109caShshoexer 	}
7628f2109caShshoexer 
7638f2109caShshoexer 	return (ret);
7648f2109caShshoexer }
7658f2109caShshoexer 
7668f2109caShshoexer static int
7678a87fca6Smsf pfkey_reply(int sd, u_int8_t **datap, ssize_t *lenp)
768f484f2cfShshoexer {
769f484f2cfShshoexer 	struct sadb_msg	 hdr;
770f484f2cfShshoexer 	ssize_t		 len;
771f484f2cfShshoexer 	u_int8_t	*data;
772f484f2cfShshoexer 
773f484f2cfShshoexer 	if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
774f484f2cfShshoexer 		warnx("short read");
775f484f2cfShshoexer 		return -1;
776f484f2cfShshoexer 	}
777f484f2cfShshoexer 	len = hdr.sadb_msg_len * PFKEYV2_CHUNK;
778f484f2cfShshoexer 	if ((data = malloc(len)) == NULL)
779bd828a90Shshoexer 		err(1, "pfkey_reply: malloc");
780f484f2cfShshoexer 	if (read(sd, data, len) != len) {
781f484f2cfShshoexer 		warn("PF_KEY short read");
782f484f2cfShshoexer 		bzero(data, len);
783f484f2cfShshoexer 		free(data);
784f484f2cfShshoexer 		return -1;
785f484f2cfShshoexer 	}
7868a87fca6Smsf 	if (datap) {
7878a87fca6Smsf 		*datap = data;
7888a87fca6Smsf 		if (lenp)
7898a87fca6Smsf 			*lenp = len;
7908a87fca6Smsf 	} else {
791f484f2cfShshoexer 		bzero(data, len);
792f484f2cfShshoexer 		free(data);
7938a87fca6Smsf 	}
794011122f7Smarkus 	if (datap == NULL && hdr.sadb_msg_errno != 0) {
795011122f7Smarkus 		errno = hdr.sadb_msg_errno;
79692270c32Shshoexer 		if (errno != EEXIST) {
797011122f7Smarkus 			warn("PF_KEY failed");
798011122f7Smarkus 			return -1;
799011122f7Smarkus 		}
80092270c32Shshoexer 	}
801f484f2cfShshoexer 	return 0;
802f484f2cfShshoexer }
803f484f2cfShshoexer 
804f484f2cfShshoexer int
8051edc1b9aShshoexer pfkey_parse(struct sadb_msg *msg, struct ipsec_rule *rule)
8061edc1b9aShshoexer {
8071edc1b9aShshoexer 	struct sadb_ext		*ext;
8081edc1b9aShshoexer 	struct sadb_address	*saddr;
8091edc1b9aShshoexer 	struct sadb_protocol	*sproto;
8101edc1b9aShshoexer 	struct sadb_ident	*sident;
8111edc1b9aShshoexer 	struct sockaddr		*sa;
812712e78baShshoexer 	struct sockaddr_in	*sa_in;
8132099bcdfStodd 	struct sockaddr_in6	*sa_in6;
8141edc1b9aShshoexer 	int			 len;
8151edc1b9aShshoexer 
8161edc1b9aShshoexer 	switch (msg->sadb_msg_satype) {
817b26a6cb5Shshoexer 	case SADB_SATYPE_ESP:
8189182219dSmarkus 		rule->satype = IPSEC_ESP;
8191edc1b9aShshoexer 		break;
820b26a6cb5Shshoexer 	case SADB_SATYPE_AH:
8219182219dSmarkus 		rule->satype = IPSEC_AH;
8221edc1b9aShshoexer 		break;
823b26a6cb5Shshoexer 	case SADB_X_SATYPE_IPCOMP:
8249182219dSmarkus 		rule->satype = IPSEC_IPCOMP;
825a29da9d0Shshoexer 		break;
82688a8cceeSmarkus 	case SADB_X_SATYPE_IPIP:
8279182219dSmarkus 		rule->satype = IPSEC_IPIP;
82888a8cceeSmarkus 		break;
8291edc1b9aShshoexer 	default:
8301edc1b9aShshoexer 		return (1);
8311edc1b9aShshoexer 	}
8321edc1b9aShshoexer 
8331edc1b9aShshoexer 	for (ext = (struct sadb_ext *)(msg + 1);
8341edc1b9aShshoexer 	    (size_t)((u_int8_t *)ext - (u_int8_t *)msg) <
835d5d1799eShshoexer 	    msg->sadb_msg_len * PFKEYV2_CHUNK && ext->sadb_ext_len > 0;
8361edc1b9aShshoexer 	    ext = (struct sadb_ext *)((u_int8_t *)ext +
8371edc1b9aShshoexer 	    ext->sadb_ext_len * PFKEYV2_CHUNK)) {
8381edc1b9aShshoexer 		switch (ext->sadb_ext_type) {
8391edc1b9aShshoexer 		case SADB_EXT_ADDRESS_SRC:
8401edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
8411edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
8421edc1b9aShshoexer 
84391f765ddShshoexer 			rule->local = calloc(1, sizeof(struct ipsec_addr_wrap));
844d5d1799eShshoexer 			if (rule->local == NULL)
845a1059f6aStodd 				err(1, "pfkey_parse: calloc");
8461edc1b9aShshoexer 
8472099bcdfStodd 			rule->local->af = sa->sa_family;
8481edc1b9aShshoexer 			switch (sa->sa_family) {
8491edc1b9aShshoexer 			case AF_INET:
8501edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
85191f765ddShshoexer 				    &rule->local->address.v4,
852712e78baShshoexer 				    sizeof(struct in_addr));
8532099bcdfStodd 				set_ipmask(rule->local, 32);
8542099bcdfStodd 				break;
8552099bcdfStodd 			case AF_INET6:
8562099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
8572099bcdfStodd 				    &rule->local->address.v6,
8582099bcdfStodd 				    sizeof(struct in6_addr));
8592099bcdfStodd 				set_ipmask(rule->local, 128);
8601edc1b9aShshoexer 				break;
8611edc1b9aShshoexer 			default:
8621edc1b9aShshoexer 				return (1);
8631edc1b9aShshoexer 			}
8641edc1b9aShshoexer 			break;
8651edc1b9aShshoexer 
8661edc1b9aShshoexer 
8671edc1b9aShshoexer 		case SADB_EXT_ADDRESS_DST:
8681edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
8691edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
8701edc1b9aShshoexer 
87191f765ddShshoexer 			rule->peer = calloc(1, sizeof(struct ipsec_addr_wrap));
8721edc1b9aShshoexer 			if (rule->peer == NULL)
873a1059f6aStodd 				err(1, "pfkey_parse: calloc");
8741edc1b9aShshoexer 
8752099bcdfStodd 			rule->peer->af = sa->sa_family;
8761edc1b9aShshoexer 			switch (sa->sa_family) {
8771edc1b9aShshoexer 			case AF_INET:
8781edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
879712e78baShshoexer 				    &rule->peer->address.v4,
880712e78baShshoexer 				    sizeof(struct in_addr));
8812099bcdfStodd 				set_ipmask(rule->peer, 32);
8822099bcdfStodd 				break;
8832099bcdfStodd 			case AF_INET6:
8842099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
8852099bcdfStodd 				    &rule->peer->address.v6,
8862099bcdfStodd 				    sizeof(struct in6_addr));
8872099bcdfStodd 				set_ipmask(rule->peer, 128);
8881edc1b9aShshoexer 				break;
8891edc1b9aShshoexer 			default:
8901edc1b9aShshoexer 				return (1);
8911edc1b9aShshoexer 			}
8921edc1b9aShshoexer 			break;
8931edc1b9aShshoexer 
8941edc1b9aShshoexer 		case SADB_EXT_IDENTITY_SRC:
8951edc1b9aShshoexer 			sident = (struct sadb_ident *)ext;
8961edc1b9aShshoexer 			len = (sident->sadb_ident_len * sizeof(uint64_t)) -
8971edc1b9aShshoexer 			    sizeof(struct sadb_ident);
8981edc1b9aShshoexer 
8999382ed8fShshoexer 			if (rule->auth == NULL) {
9009382ed8fShshoexer 				rule->auth = calloc(1, sizeof(struct
9019382ed8fShshoexer 				    ipsec_auth));
9029382ed8fShshoexer 				if (rule->auth == NULL)
903bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
9049382ed8fShshoexer 			}
9059382ed8fShshoexer 
906abe65127Shshoexer 			rule->auth->srcid = calloc(1, len);
907abe65127Shshoexer 			if (rule->auth->srcid == NULL)
908bd828a90Shshoexer 				err(1, "pfkey_parse: calloc");
9091edc1b9aShshoexer 
910abe65127Shshoexer 			strlcpy(rule->auth->srcid, (char *)(sident + 1), len);
9111edc1b9aShshoexer 			break;
9121edc1b9aShshoexer 
9131edc1b9aShshoexer 		case SADB_EXT_IDENTITY_DST:
9141edc1b9aShshoexer 			sident = (struct sadb_ident *)ext;
9151edc1b9aShshoexer 			len = (sident->sadb_ident_len * sizeof(uint64_t)) -
9161edc1b9aShshoexer 			    sizeof(struct sadb_ident);
9171edc1b9aShshoexer 
9189382ed8fShshoexer 			if (rule->auth == NULL) {
9199382ed8fShshoexer 				rule->auth = calloc(1, sizeof(struct
9209382ed8fShshoexer 				    ipsec_auth));
9219382ed8fShshoexer 				if (rule->auth == NULL)
922bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
9239382ed8fShshoexer 			}
9249382ed8fShshoexer 
925abe65127Shshoexer 			rule->auth->dstid = calloc(1, len);
926abe65127Shshoexer 			if (rule->auth->dstid == NULL)
927bd828a90Shshoexer 				err(1, "pfkey_parse: calloc");
9281edc1b9aShshoexer 
929abe65127Shshoexer 			strlcpy(rule->auth->dstid, (char *)(sident + 1), len);
9301edc1b9aShshoexer 			break;
9311edc1b9aShshoexer 
9321edc1b9aShshoexer 		case SADB_X_EXT_PROTOCOL:
9339182219dSmarkus 			sproto = (struct sadb_protocol *)ext;
9349182219dSmarkus 			if (sproto->sadb_protocol_direction == 0)
9359182219dSmarkus 				rule->proto = sproto->sadb_protocol_proto;
9361edc1b9aShshoexer 			break;
9371edc1b9aShshoexer 
9381edc1b9aShshoexer 		case SADB_X_EXT_FLOW_TYPE:
9391edc1b9aShshoexer 			sproto = (struct sadb_protocol *)ext;
9401edc1b9aShshoexer 
9411edc1b9aShshoexer 			switch (sproto->sadb_protocol_direction) {
9421edc1b9aShshoexer 			case IPSP_DIRECTION_IN:
9431edc1b9aShshoexer 				rule->direction = IPSEC_IN;
9441edc1b9aShshoexer 				break;
9451edc1b9aShshoexer 			case IPSP_DIRECTION_OUT:
9461edc1b9aShshoexer 				rule->direction = IPSEC_OUT;
9471edc1b9aShshoexer 				break;
9481edc1b9aShshoexer 			default:
9491edc1b9aShshoexer 				return (1);
9501edc1b9aShshoexer 			}
9516122c05eShshoexer 			switch (sproto->sadb_protocol_proto) {
9526122c05eShshoexer 			case SADB_X_FLOW_TYPE_USE:
9531a3f035aShshoexer 				rule->flowtype = TYPE_USE;
9546122c05eShshoexer 				break;
9556122c05eShshoexer 			case SADB_X_FLOW_TYPE_ACQUIRE:
9561a3f035aShshoexer 				rule->flowtype = TYPE_ACQUIRE;
9576122c05eShshoexer 				break;
9586122c05eShshoexer 			case SADB_X_FLOW_TYPE_REQUIRE:
9591a3f035aShshoexer 				rule->flowtype = TYPE_REQUIRE;
9606122c05eShshoexer 				break;
9616122c05eShshoexer 			case SADB_X_FLOW_TYPE_DENY:
9621a3f035aShshoexer 				rule->flowtype = TYPE_DENY;
9636122c05eShshoexer 				break;
9646122c05eShshoexer 			case SADB_X_FLOW_TYPE_BYPASS:
9651a3f035aShshoexer 				rule->flowtype = TYPE_BYPASS;
9666122c05eShshoexer 				break;
9676122c05eShshoexer 			case SADB_X_FLOW_TYPE_DONTACQ:
9681a3f035aShshoexer 				rule->flowtype = TYPE_DONTACQ;
9696122c05eShshoexer 				break;
9706122c05eShshoexer 			default:
9711a3f035aShshoexer 				rule->flowtype = TYPE_UNKNOWN;
9726122c05eShshoexer 				break;
9736122c05eShshoexer 			}
9741edc1b9aShshoexer 			break;
9751edc1b9aShshoexer 
9761edc1b9aShshoexer 		case SADB_X_EXT_SRC_FLOW:
9771edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
9781edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
9791edc1b9aShshoexer 
9801edc1b9aShshoexer 			if (rule->src == NULL) {
9811edc1b9aShshoexer 				rule->src = calloc(1,
98291f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
9831edc1b9aShshoexer 				if (rule->src == NULL)
984bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
9851edc1b9aShshoexer 			}
9861edc1b9aShshoexer 
9872099bcdfStodd 			rule->src->af = sa->sa_family;
9881edc1b9aShshoexer 			switch (sa->sa_family) {
9891edc1b9aShshoexer 			case AF_INET:
9901edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
991712e78baShshoexer 				    &rule->src->address.v4,
992712e78baShshoexer 				    sizeof(struct in_addr));
99357f58d0dSnaddy 				rule->sport =
99457f58d0dSnaddy 				    ((struct sockaddr_in *)sa)->sin_port;
9952099bcdfStodd 				break;
9962099bcdfStodd 			case AF_INET6:
9972099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
9982099bcdfStodd 				    &rule->src->address.v6,
9992099bcdfStodd 				    sizeof(struct in6_addr));
100057f58d0dSnaddy 				rule->sport =
100157f58d0dSnaddy 				    ((struct sockaddr_in6 *)sa)->sin6_port;
10021edc1b9aShshoexer 				break;
10031edc1b9aShshoexer 			default:
10041edc1b9aShshoexer 				return (1);
10051edc1b9aShshoexer 			}
10061edc1b9aShshoexer 			break;
10071edc1b9aShshoexer 
10081edc1b9aShshoexer 		case SADB_X_EXT_DST_FLOW:
10091edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
10101edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
10111edc1b9aShshoexer 
10121edc1b9aShshoexer 			if (rule->dst == NULL) {
10131edc1b9aShshoexer 				rule->dst = calloc(1,
101491f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
10151edc1b9aShshoexer 				if (rule->dst == NULL)
1016bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
10171edc1b9aShshoexer 			}
10181edc1b9aShshoexer 
10192099bcdfStodd 			rule->dst->af = sa->sa_family;
10201edc1b9aShshoexer 			switch (sa->sa_family) {
10211edc1b9aShshoexer 			case AF_INET:
10221edc1b9aShshoexer 				bcopy(&((struct sockaddr_in *)sa)->sin_addr,
1023712e78baShshoexer 				    &rule->dst->address.v4,
1024712e78baShshoexer 				    sizeof(struct in_addr));
102557f58d0dSnaddy 				rule->dport =
102657f58d0dSnaddy 				    ((struct sockaddr_in *)sa)->sin_port;
10271edc1b9aShshoexer 				break;
10282099bcdfStodd 			case AF_INET6:
10292099bcdfStodd 				bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr,
10302099bcdfStodd 				    &rule->dst->address.v6,
10312099bcdfStodd 				    sizeof(struct in6_addr));
103257f58d0dSnaddy 				rule->dport =
103357f58d0dSnaddy 				    ((struct sockaddr_in6 *)sa)->sin6_port;
10342099bcdfStodd 				break;
10351edc1b9aShshoexer 			default:
10361edc1b9aShshoexer 				return (1);
10371edc1b9aShshoexer 			}
10381edc1b9aShshoexer 			break;
10391edc1b9aShshoexer 
10401edc1b9aShshoexer 
10411edc1b9aShshoexer 		case SADB_X_EXT_SRC_MASK:
10421edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
10431edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
10441edc1b9aShshoexer 
10451edc1b9aShshoexer 			if (rule->src == NULL) {
10461edc1b9aShshoexer 				rule->src = calloc(1,
104791f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
10481edc1b9aShshoexer 				if (rule->src == NULL)
1049bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
10501edc1b9aShshoexer 			}
10511edc1b9aShshoexer 
10522099bcdfStodd 			rule->src->af = sa->sa_family;
10531edc1b9aShshoexer 			switch (sa->sa_family) {
10541edc1b9aShshoexer 			case AF_INET:
1055712e78baShshoexer 				sa_in = (struct sockaddr_in *)sa;
1056712e78baShshoexer 				bcopy(&sa_in->sin_addr, &rule->src->mask.v4,
10571edc1b9aShshoexer 				    sizeof(struct in_addr));
10582099bcdfStodd 				break;
10592099bcdfStodd 			case AF_INET6:
10602099bcdfStodd 				sa_in6 = (struct sockaddr_in6 *)sa;
10612099bcdfStodd 				bcopy(&sa_in6->sin6_addr, &rule->src->mask.v6,
10622099bcdfStodd 				    sizeof(struct in6_addr));
10631edc1b9aShshoexer 				break;
10641edc1b9aShshoexer 
10651edc1b9aShshoexer 			default:
10661edc1b9aShshoexer 				return (1);
10671edc1b9aShshoexer 			}
10681edc1b9aShshoexer 			break;
10691edc1b9aShshoexer 
10701edc1b9aShshoexer 		case SADB_X_EXT_DST_MASK:
10711edc1b9aShshoexer 			saddr = (struct sadb_address *)ext;
10721edc1b9aShshoexer 			sa = (struct sockaddr *)(saddr + 1);
10731edc1b9aShshoexer 
10741edc1b9aShshoexer 			if (rule->dst == NULL) {
10751edc1b9aShshoexer 				rule->dst = calloc(1,
107691f765ddShshoexer 				    sizeof(struct ipsec_addr_wrap));
10771edc1b9aShshoexer 				if (rule->dst == NULL)
1078bd828a90Shshoexer 					err(1, "pfkey_parse: calloc");
10791edc1b9aShshoexer 			}
10801edc1b9aShshoexer 
10812099bcdfStodd 			rule->dst->af = sa->sa_family;
10821edc1b9aShshoexer 			switch (sa->sa_family) {
10831edc1b9aShshoexer 			case AF_INET:
1084712e78baShshoexer 				sa_in = (struct sockaddr_in *)sa;
1085712e78baShshoexer 				bcopy(&sa_in->sin_addr, &rule->dst->mask.v4,
10861edc1b9aShshoexer 				    sizeof(struct in_addr));
10871edc1b9aShshoexer 				break;
10882099bcdfStodd 			case AF_INET6:
10892099bcdfStodd 				sa_in6 = (struct sockaddr_in6 *)sa;
10902099bcdfStodd 				bcopy(&sa_in6->sin6_addr, &rule->dst->mask.v6,
10912099bcdfStodd 				    sizeof(struct in6_addr));
10922099bcdfStodd 				break;
10931edc1b9aShshoexer 			default:
10941edc1b9aShshoexer 				return (1);
10951edc1b9aShshoexer 			}
10961edc1b9aShshoexer 			break;
10971edc1b9aShshoexer 
10981edc1b9aShshoexer 		default:
10991edc1b9aShshoexer 			return (1);
11001edc1b9aShshoexer 		}
11011edc1b9aShshoexer 	}
11021edc1b9aShshoexer 
11031edc1b9aShshoexer 	return (0);
11041edc1b9aShshoexer }
11051edc1b9aShshoexer 
11061edc1b9aShshoexer int
1107356121f6Shshoexer pfkey_ipsec_establish(int action, struct ipsec_rule *r)
1108f484f2cfShshoexer {
110922a29ad6Shshoexer 	int		ret;
11108065703bShshoexer 	u_int8_t	satype, satype2, direction;
1111f484f2cfShshoexer 
1112f032086dShshoexer 	if (r->type == RULE_FLOW) {
11139182219dSmarkus 		switch (r->satype) {
1114f484f2cfShshoexer 		case IPSEC_ESP:
1115f484f2cfShshoexer 			satype = SADB_SATYPE_ESP;
1116f484f2cfShshoexer 			break;
1117f484f2cfShshoexer 		case IPSEC_AH:
1118f484f2cfShshoexer 			satype = SADB_SATYPE_AH;
1119f484f2cfShshoexer 			break;
112072e25333Shshoexer 		case IPSEC_IPCOMP:
112172e25333Shshoexer 			satype = SADB_X_SATYPE_IPCOMP;
112272e25333Shshoexer 			break;
112388a8cceeSmarkus 		case IPSEC_IPIP:
112488a8cceeSmarkus 			satype = SADB_X_SATYPE_IPIP;
112588a8cceeSmarkus 			break;
1126f484f2cfShshoexer 		default:
1127f484f2cfShshoexer 			return -1;
1128f484f2cfShshoexer 		}
1129f484f2cfShshoexer 
1130f484f2cfShshoexer 		switch (r->direction) {
1131f484f2cfShshoexer 		case IPSEC_IN:
1132f484f2cfShshoexer 			direction = IPSP_DIRECTION_IN;
1133f484f2cfShshoexer 			break;
1134f484f2cfShshoexer 		case IPSEC_OUT:
1135f484f2cfShshoexer 			direction = IPSP_DIRECTION_OUT;
1136f484f2cfShshoexer 			break;
1137f484f2cfShshoexer 		default:
1138f484f2cfShshoexer 			return -1;
1139f484f2cfShshoexer 		}
1140f484f2cfShshoexer 
114122a29ad6Shshoexer 		switch (action) {
114290bd57a7Shshoexer 		case ACTION_ADD:
1143f032086dShshoexer 			ret = pfkey_flow(fd, satype, SADB_X_ADDFLOW, direction,
114457f58d0dSnaddy 			    r->proto, r->src, r->sport, r->dst, r->dport,
114557f58d0dSnaddy 			    r->local, r->peer, r->auth, r->flowtype);
114622a29ad6Shshoexer 			break;
114790bd57a7Shshoexer 		case ACTION_DELETE:
114822a29ad6Shshoexer 			/* No peer for flow deletion. */
1149f032086dShshoexer 			ret = pfkey_flow(fd, satype, SADB_X_DELFLOW, direction,
115057f58d0dSnaddy 			    r->proto, r->src, r->sport, r->dst, r->dport,
115157f58d0dSnaddy 			    NULL, NULL, NULL, r->flowtype);
115222a29ad6Shshoexer 			break;
115322a29ad6Shshoexer 		default:
115422a29ad6Shshoexer 			return -1;
115522a29ad6Shshoexer 		}
1156f032086dShshoexer 	} else if (r->type == RULE_SA) {
11579182219dSmarkus 		switch (r->satype) {
1158881e2068Shshoexer 		case IPSEC_AH:
1159881e2068Shshoexer 			satype = SADB_SATYPE_AH;
1160881e2068Shshoexer 			break;
1161881e2068Shshoexer 		case IPSEC_ESP:
1162881e2068Shshoexer 			satype = SADB_SATYPE_ESP;
1163881e2068Shshoexer 			break;
116472e25333Shshoexer 		case IPSEC_IPCOMP:
116572e25333Shshoexer 			satype = SADB_X_SATYPE_IPCOMP;
116672e25333Shshoexer 			break;
1167381a2422Shshoexer 		case IPSEC_TCPMD5:
1168f032086dShshoexer 			satype = SADB_X_SATYPE_TCPSIGNATURE;
1169381a2422Shshoexer 			break;
117088a8cceeSmarkus 		case IPSEC_IPIP:
117188a8cceeSmarkus 			satype = SADB_X_SATYPE_IPIP;
117288a8cceeSmarkus 			break;
1173381a2422Shshoexer 		default:
1174381a2422Shshoexer 			return -1;
1175381a2422Shshoexer 		}
1176f032086dShshoexer 		switch (action) {
117790bd57a7Shshoexer 		case ACTION_ADD:
1178f032086dShshoexer 			ret = pfkey_sa(fd, satype, SADB_ADD, r->spi,
1179a38d220fShshoexer 			    r->src, r->dst, r->xfs, r->authkey, r->enckey,
1180a38d220fShshoexer 			    r->tmode);
1181f032086dShshoexer 			break;
118290bd57a7Shshoexer 		case ACTION_DELETE:
1183f032086dShshoexer 			ret = pfkey_sa(fd, satype, SADB_DELETE, r->spi,
1184a38d220fShshoexer 			    r->src, r->dst, r->xfs, NULL, NULL, r->tmode);
1185f032086dShshoexer 			break;
1186f032086dShshoexer 		default:
1187f032086dShshoexer 			return -1;
1188f032086dShshoexer 		}
11898f2109caShshoexer 	} else if (r->type == RULE_GROUP) {
11908f2109caShshoexer 		switch (r->satype) {
11918f2109caShshoexer 		case IPSEC_AH:
11928f2109caShshoexer 			satype = SADB_SATYPE_AH;
11938f2109caShshoexer 			break;
11948f2109caShshoexer 		case IPSEC_ESP:
11958f2109caShshoexer 			satype = SADB_SATYPE_ESP;
11968f2109caShshoexer 			break;
11978f2109caShshoexer 		case IPSEC_IPCOMP:
11988f2109caShshoexer 			satype = SADB_X_SATYPE_IPCOMP;
11998f2109caShshoexer 			break;
12008f2109caShshoexer 		case IPSEC_TCPMD5:
12018f2109caShshoexer 			satype = SADB_X_SATYPE_TCPSIGNATURE;
12028f2109caShshoexer 			break;
12038f2109caShshoexer 		case IPSEC_IPIP:
12048f2109caShshoexer 			satype = SADB_X_SATYPE_IPIP;
12058f2109caShshoexer 			break;
12068f2109caShshoexer 		default:
12078f2109caShshoexer 			return -1;
12088f2109caShshoexer 		}
12098065703bShshoexer 		switch (r->proto2) {
12108065703bShshoexer 		case IPSEC_AH:
12118065703bShshoexer 			satype2 = SADB_SATYPE_AH;
12128065703bShshoexer 			break;
12138065703bShshoexer 		case IPSEC_ESP:
12148065703bShshoexer 			satype2 = SADB_SATYPE_ESP;
12158065703bShshoexer 			break;
12168065703bShshoexer 		case IPSEC_IPCOMP:
12178065703bShshoexer 			satype2 = SADB_X_SATYPE_IPCOMP;
12188065703bShshoexer 			break;
12198065703bShshoexer 		case IPSEC_TCPMD5:
12208065703bShshoexer 			satype2 = SADB_X_SATYPE_TCPSIGNATURE;
12218065703bShshoexer 			break;
12228065703bShshoexer 		case IPSEC_IPIP:
12238065703bShshoexer 			satype2 = SADB_X_SATYPE_IPIP;
12248065703bShshoexer 			break;
12258065703bShshoexer 		default:
12268065703bShshoexer 			return -1;
12278065703bShshoexer 		}
12288f2109caShshoexer 		switch (action) {
12298f2109caShshoexer 		case ACTION_ADD:
12308065703bShshoexer 			ret = pfkey_sagroup(fd, satype, satype2,
12318065703bShshoexer 			    SADB_X_GRPSPIS, r->dst, r->spi, r->dst2, r->spi2);
12328f2109caShshoexer 			break;
12338f2109caShshoexer 		case ACTION_DELETE:
12348f2109caShshoexer 			return 0;
12358f2109caShshoexer 		default:
12368f2109caShshoexer 			return -1;
12378f2109caShshoexer 		}
1238f032086dShshoexer 	} else
1239f032086dShshoexer 		return -1;
1240f032086dShshoexer 
124122a29ad6Shshoexer 	if (ret < 0)
1242f484f2cfShshoexer 		return -1;
12438a87fca6Smsf 	if (pfkey_reply(fd, NULL, NULL) < 0)
1244f484f2cfShshoexer 		return -1;
1245f484f2cfShshoexer 
1246f484f2cfShshoexer 	return 0;
1247f484f2cfShshoexer }
1248f484f2cfShshoexer 
1249f484f2cfShshoexer int
1250f484f2cfShshoexer pfkey_ipsec_flush(void)
1251f484f2cfShshoexer {
1252f484f2cfShshoexer 	struct sadb_msg smsg;
1253f484f2cfShshoexer 	struct iovec	iov[IOV_CNT];
1254f484f2cfShshoexer 	ssize_t		n;
1255f484f2cfShshoexer 	int		iov_cnt, len;
1256f484f2cfShshoexer 
1257f484f2cfShshoexer 	bzero(&smsg, sizeof(smsg));
1258f484f2cfShshoexer 	smsg.sadb_msg_version = PF_KEY_V2;
1259f484f2cfShshoexer 	smsg.sadb_msg_seq = sadb_msg_seq++;
1260f484f2cfShshoexer 	smsg.sadb_msg_pid = getpid();
1261f484f2cfShshoexer 	smsg.sadb_msg_len = sizeof(smsg) / 8;
1262f484f2cfShshoexer 	smsg.sadb_msg_type = SADB_FLUSH;
1263f484f2cfShshoexer 	smsg.sadb_msg_satype = SADB_SATYPE_UNSPEC;
1264f484f2cfShshoexer 
1265f484f2cfShshoexer 	iov_cnt = 0;
1266f484f2cfShshoexer 
1267f484f2cfShshoexer 	iov[iov_cnt].iov_base = &smsg;
1268f484f2cfShshoexer 	iov[iov_cnt].iov_len = sizeof(smsg);
1269f484f2cfShshoexer 	iov_cnt++;
1270f484f2cfShshoexer 
1271f484f2cfShshoexer 	len = smsg.sadb_msg_len * 8;
1272f484f2cfShshoexer 	if ((n = writev(fd, iov, iov_cnt)) == -1) {
1273f484f2cfShshoexer 		warn("writev failed");
1274f484f2cfShshoexer 		return -1;
1275f484f2cfShshoexer 	}
1276f484f2cfShshoexer 	if (n != len) {
1277f484f2cfShshoexer 		warnx("short write");
1278f484f2cfShshoexer 		return -1;
1279f484f2cfShshoexer 	}
12808a87fca6Smsf 	if (pfkey_reply(fd, NULL, NULL) < 0)
1281f484f2cfShshoexer 		return -1;
1282f484f2cfShshoexer 
1283f484f2cfShshoexer 	return 0;
1284f484f2cfShshoexer }
1285f484f2cfShshoexer 
12868a87fca6Smsf static int
12878a87fca6Smsf pfkey_promisc(void)
12888a87fca6Smsf {
12898a87fca6Smsf 	struct sadb_msg msg;
12908a87fca6Smsf 
12918a87fca6Smsf 	memset(&msg, 0, sizeof(msg));
12928a87fca6Smsf 	msg.sadb_msg_version = PF_KEY_V2;
12938a87fca6Smsf 	msg.sadb_msg_seq = sadb_msg_seq++;
12948a87fca6Smsf 	msg.sadb_msg_pid = getpid();
12958a87fca6Smsf 	msg.sadb_msg_len = sizeof(msg) / PFKEYV2_CHUNK;
12968a87fca6Smsf 	msg.sadb_msg_type = SADB_X_PROMISC;
12978a87fca6Smsf 	msg.sadb_msg_satype = 1;	/* enable */
12988a87fca6Smsf 	if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) {
12998a87fca6Smsf 		warn("pfkey_promisc: write failed");
13008a87fca6Smsf 		return -1;
13018a87fca6Smsf 	}
13028a87fca6Smsf 	if (pfkey_reply(fd, NULL, NULL) < 0)
13038a87fca6Smsf 		return -1;
13048a87fca6Smsf 	return 0;
13058a87fca6Smsf }
13068a87fca6Smsf 
13078a87fca6Smsf int
13088a87fca6Smsf pfkey_monitor(int opts)
13098a87fca6Smsf {
13108a87fca6Smsf 	fd_set *rset;
13118a87fca6Smsf 	u_int8_t *data;
13128a87fca6Smsf 	struct sadb_msg *msg;
13138a87fca6Smsf 	ssize_t len, set_size;
13148a87fca6Smsf 	int n;
13158a87fca6Smsf 
13168a87fca6Smsf 	if (pfkey_init() < 0)
13178a87fca6Smsf 		return -1;
13188a87fca6Smsf 	if (pfkey_promisc() < 0)
13198a87fca6Smsf 		return -1;
13208a87fca6Smsf 
13218a87fca6Smsf 	set_size = howmany(fd + 1, NFDBITS) * sizeof(fd_mask);
13228a87fca6Smsf 	if ((rset = malloc(set_size)) == NULL) {
13238a87fca6Smsf 		warn("malloc");
13248a87fca6Smsf 		return -1;
13258a87fca6Smsf 	}
13268a87fca6Smsf 	for (;;) {
13278a87fca6Smsf 		memset(rset, 0, set_size);
13288a87fca6Smsf 		FD_SET(fd, rset);
13298a87fca6Smsf 		if ((n = select(fd+1, rset, NULL, NULL, NULL)) < 0)
13308a87fca6Smsf 			err(2, "select");
13318a87fca6Smsf 		if (n == 0)
13328a87fca6Smsf 			break;
13338a87fca6Smsf 		if (!FD_ISSET(fd, rset))
13348a87fca6Smsf 			continue;
13358a87fca6Smsf 		if (pfkey_reply(fd, &data, &len) < 0)
13368a87fca6Smsf 			continue;
13378a87fca6Smsf 		msg = (struct sadb_msg *)data;
13388a87fca6Smsf 		if (msg->sadb_msg_type == SADB_X_PROMISC) {
13398a87fca6Smsf 			/* remove extra header from promisc messages */
13408a87fca6Smsf 			if ((msg->sadb_msg_len * PFKEYV2_CHUNK) >=
13418a87fca6Smsf 			    2 * sizeof(struct sadb_msg)) {
13428a87fca6Smsf 				msg++;
13438a87fca6Smsf 			}
13448a87fca6Smsf 		}
13458a87fca6Smsf 		pfkey_monitor_sa(msg, opts);
13468a87fca6Smsf 		if (opts & IPSECCTL_OPT_VERBOSE)
13478a87fca6Smsf 			pfkey_print_raw(data, len);
13488a87fca6Smsf 		memset(data, 0, len);
13498a87fca6Smsf 		free(data);
13508a87fca6Smsf 	}
13518a87fca6Smsf 	close(fd);
13528a87fca6Smsf 	return 0;
13538a87fca6Smsf }
13548a87fca6Smsf 
1355f484f2cfShshoexer int
1356f484f2cfShshoexer pfkey_init(void)
1357f484f2cfShshoexer {
1358f484f2cfShshoexer 	if ((fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) == -1)
1359bd828a90Shshoexer 		err(1, "pfkey_init: failed to open PF_KEY socket");
1360f484f2cfShshoexer 
1361f484f2cfShshoexer 	return 0;
1362f484f2cfShshoexer }
1363