xref: /openbsd-src/sbin/iked/policy.c (revision f36db9c4e75d6f4b6e40410c8005109158705028)
1*f36db9c4Syasuoka /*	$OpenBSD: policy.c,v 1.99 2024/07/13 12:22:46 yasuoka Exp $	*/
245ae9d61Sreyk 
345ae9d61Sreyk /*
4264f8b22Stobhe  * Copyright (c) 2020-2021 Tobias Heider <tobhe@openbsd.org>
5fcebd35dSreyk  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
6e2015428Sreyk  * Copyright (c) 2001 Daniel Hartmeier
745ae9d61Sreyk  *
845ae9d61Sreyk  * Permission to use, copy, modify, and distribute this software for any
945ae9d61Sreyk  * purpose with or without fee is hereby granted, provided that the above
1045ae9d61Sreyk  * copyright notice and this permission notice appear in all copies.
1145ae9d61Sreyk  *
1245ae9d61Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1345ae9d61Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1445ae9d61Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1545ae9d61Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1645ae9d61Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1745ae9d61Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1845ae9d61Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1945ae9d61Sreyk  */
2045ae9d61Sreyk 
2145ae9d61Sreyk #include <sys/queue.h>
2245ae9d61Sreyk #include <sys/socket.h>
2345ae9d61Sreyk #include <sys/uio.h>
2445ae9d61Sreyk #include <sys/tree.h>
2545ae9d61Sreyk 
26264f8b22Stobhe #include <netinet/in.h>
27264f8b22Stobhe 
2845ae9d61Sreyk #include <stdio.h>
2945ae9d61Sreyk #include <stdlib.h>
3045ae9d61Sreyk #include <unistd.h>
3145ae9d61Sreyk #include <string.h>
3245ae9d61Sreyk #include <errno.h>
3345ae9d61Sreyk #include <fcntl.h>
3445ae9d61Sreyk #include <event.h>
3545ae9d61Sreyk 
3645ae9d61Sreyk #include "iked.h"
3745ae9d61Sreyk #include "ikev2.h"
3845ae9d61Sreyk 
3945ae9d61Sreyk static __inline int
4045ae9d61Sreyk 	 sa_cmp(struct iked_sa *, struct iked_sa *);
4145ae9d61Sreyk static __inline int
42729f601bStobhe 	 sa_dstid_cmp(struct iked_sa *, struct iked_sa *);
43729f601bStobhe static __inline int
4445ae9d61Sreyk 	 user_cmp(struct iked_user *, struct iked_user *);
45b0eeedd0Smikeb static __inline int
46b0eeedd0Smikeb 	 childsa_cmp(struct iked_childsa *, struct iked_childsa *);
470927abadSmikeb static __inline int
480927abadSmikeb 	 flow_cmp(struct iked_flow *, struct iked_flow *);
491f864a9aStobhe static __inline int
501f864a9aStobhe 	 addr_cmp(struct iked_addr *, struct iked_addr *, int);
511f864a9aStobhe static __inline int
521f864a9aStobhe 	 ts_insert_unique(struct iked_addr *, struct iked_tss *, int);
530927abadSmikeb 
5486ddd613Stobhe static int	policy_test_flows(struct iked_policy *, struct iked_policy *);
55b0435d4dStobhe static int	proposals_match(struct iked_proposal *, struct iked_proposal *,
564ad5fa93Stobhe 		    struct iked_transform **, int, int);
5745ae9d61Sreyk 
5845ae9d61Sreyk void
policy_init(struct iked * env)5945ae9d61Sreyk policy_init(struct iked *env)
6045ae9d61Sreyk {
61e2015428Sreyk 	TAILQ_INIT(&env->sc_policies);
62ba3555e2Sreyk 	TAILQ_INIT(&env->sc_ocsp);
63*f36db9c4Syasuoka 	TAILQ_INIT(&env->sc_radauthservers);
64*f36db9c4Syasuoka 	TAILQ_INIT(&env->sc_radacctservers);
65*f36db9c4Syasuoka 	TAILQ_INIT(&env->sc_radcfgmaps);
66*f36db9c4Syasuoka 	TAILQ_INIT(&env->sc_raddaes);
67*f36db9c4Syasuoka 	TAILQ_INIT(&env->sc_raddaeclients);
6845ae9d61Sreyk 	RB_INIT(&env->sc_users);
6945ae9d61Sreyk 	RB_INIT(&env->sc_sas);
70729f601bStobhe 	RB_INIT(&env->sc_dstid_sas);
71856dba1dSmikeb 	RB_INIT(&env->sc_activesas);
72856dba1dSmikeb 	RB_INIT(&env->sc_activeflows);
7345ae9d61Sreyk }
7445ae9d61Sreyk 
75b0435d4dStobhe /*
76b0435d4dStobhe  * Lookup an iked policy matching the IKE_AUTH message msg
77b0435d4dStobhe  * and store a pointer to the found policy in msg.  If no policy
78b0435d4dStobhe  * matches a pointer to the default policy is stored in msg.
791b12ffc9Stobhe  * If 'proposals' is not NULL policy_lookup only returns policies
801b12ffc9Stobhe  * compatible with 'proposals'.
81b0435d4dStobhe  *
82b0435d4dStobhe  * Returns 0 on success and -1 if no matching policy was
83b0435d4dStobhe  * found and no default exists.
84b0435d4dStobhe  */
8545ae9d61Sreyk int
policy_lookup(struct iked * env,struct iked_message * msg,struct iked_proposals * proposals,struct iked_flows * flows,int nflows)861b12ffc9Stobhe policy_lookup(struct iked *env, struct iked_message *msg,
87a5265846Stobhe     struct iked_proposals *proposals, struct iked_flows *flows,
88a5265846Stobhe     int nflows)
8945ae9d61Sreyk {
9045ae9d61Sreyk 	struct iked_policy	 pol;
9195826121Smarkus 	char			*s, idstr[IKED_ID_SIZE];
9295826121Smarkus 
9345ae9d61Sreyk 
9445ae9d61Sreyk 	if (msg->msg_sa != NULL && msg->msg_sa->sa_policy != NULL) {
9545ae9d61Sreyk 		/* Existing SA with policy */
9645ae9d61Sreyk 		msg->msg_policy = msg->msg_sa->sa_policy;
977ff51f1dStobhe 		return (0);
9845ae9d61Sreyk 	}
9945ae9d61Sreyk 
10045ae9d61Sreyk 	bzero(&pol, sizeof(pol));
1011b12ffc9Stobhe 	if (proposals != NULL)
1021b12ffc9Stobhe 		pol.pol_proposals = *proposals;
103e2015428Sreyk 	pol.pol_af = msg->msg_peer.ss_family;
104a5265846Stobhe 	if (flows)
105a5265846Stobhe 		pol.pol_flows = *flows;
106a5265846Stobhe 	pol.pol_nflows = nflows;
1075d292c1aStobhe 	if (msg->msg_flags & IKED_MSG_FLAGS_USE_TRANSPORT)
1083ddf179cStobhe 		pol.pol_flags |= IKED_POLICY_TRANSPORT;
109e2015428Sreyk 	memcpy(&pol.pol_peer.addr, &msg->msg_peer, sizeof(msg->msg_peer));
110e2015428Sreyk 	memcpy(&pol.pol_local.addr, &msg->msg_local, sizeof(msg->msg_local));
1119ce164edStobhe 	if (msg->msg_peerid.id_type &&
1129ce164edStobhe 	    ikev2_print_id(&msg->msg_peerid, idstr, IKED_ID_SIZE) == 0 &&
11395826121Smarkus 	    (s = strchr(idstr, '/')) != NULL) {
1149ce164edStobhe 		pol.pol_peerid.id_type = msg->msg_peerid.id_type;
11595826121Smarkus 		pol.pol_peerid.id_length = strlen(s+1);
11695826121Smarkus 		strlcpy(pol.pol_peerid.id_data, s+1,
11795826121Smarkus 		    sizeof(pol.pol_peerid.id_data));
11895826121Smarkus 		log_debug("%s: peerid '%s'", __func__, s+1);
11995826121Smarkus 	}
120e3f5cf2eSpatrick 	if (msg->msg_localid.id_type &&
121e3f5cf2eSpatrick 	    ikev2_print_id(&msg->msg_localid, idstr, IKED_ID_SIZE) == 0 &&
122e3f5cf2eSpatrick 	    (s = strchr(idstr, '/')) != NULL) {
123e3f5cf2eSpatrick 		pol.pol_localid.id_type = msg->msg_localid.id_type;
124e3f5cf2eSpatrick 		pol.pol_localid.id_length = strlen(s+1);
125e3f5cf2eSpatrick 		strlcpy(pol.pol_localid.id_data, s+1,
126e3f5cf2eSpatrick 		    sizeof(pol.pol_localid.id_data));
127e3f5cf2eSpatrick 		log_debug("%s: localid '%s'", __func__, s+1);
128e3f5cf2eSpatrick 	}
12945ae9d61Sreyk 
13045ae9d61Sreyk 	/* Try to find a matching policy for this message */
1313ddf179cStobhe 	if ((msg->msg_policy = policy_test(env, &pol)) != NULL) {
1323ddf179cStobhe 		log_debug("%s: setting policy '%s'", __func__,
1333ddf179cStobhe 		    msg->msg_policy->pol_name);
1347ff51f1dStobhe 		return (0);
1353ddf179cStobhe 	}
13645ae9d61Sreyk 
13745ae9d61Sreyk 	/* No matching policy found, try the default */
13845ae9d61Sreyk 	if ((msg->msg_policy = env->sc_defaultcon) != NULL)
1397ff51f1dStobhe 		return (0);
14045ae9d61Sreyk 
14145ae9d61Sreyk 	/* No policy found */
14245ae9d61Sreyk 	return (-1);
14345ae9d61Sreyk }
14445ae9d61Sreyk 
145b0435d4dStobhe /*
14686ddd613Stobhe  * Lookup an iked policy matching the SA sa and store a pointer
14786ddd613Stobhe  * to the found policy in SA.
14886ddd613Stobhe  *
14986ddd613Stobhe  * Returns 0 on success and -1 if no matching policy was
15086ddd613Stobhe  * found
15186ddd613Stobhe  */
15286ddd613Stobhe int
policy_lookup_sa(struct iked * env,struct iked_sa * sa)15386ddd613Stobhe policy_lookup_sa(struct iked *env, struct iked_sa *sa)
15486ddd613Stobhe {
15586ddd613Stobhe 	struct iked_policy	 pol, *pol_found;
15686ddd613Stobhe 	struct iked_id		*lid, *pid;
15786ddd613Stobhe 	char			*s, idstr[IKED_ID_SIZE];
15886ddd613Stobhe 
15986ddd613Stobhe 	/*
16086ddd613Stobhe 	 * The SA should never be without policy. In the case of
16186ddd613Stobhe 	 * 'ikectl reload' the policy is no longer in sc_policies
16286ddd613Stobhe 	 * but is kept alive by the reference from the sa.
16386ddd613Stobhe 	 */
16486ddd613Stobhe 	if (sa->sa_policy == NULL) {
16586ddd613Stobhe 		log_warn("%s: missing SA policy.", SPI_SA(sa, __func__));
16686ddd613Stobhe 		return (-1);
16786ddd613Stobhe 	}
16886ddd613Stobhe 
16986ddd613Stobhe 	bzero(&pol, sizeof(pol));
17086ddd613Stobhe 	pol.pol_proposals = sa->sa_proposals;
17186ddd613Stobhe 	pol.pol_af = sa->sa_peer.addr_af;
17286ddd613Stobhe 	if (sa->sa_used_transport_mode)
17386ddd613Stobhe 		pol.pol_flags |= IKED_POLICY_TRANSPORT;
174136bc29aStobhe 	memcpy(&pol.pol_peer.addr, &sa->sa_peer.addr, sizeof(sa->sa_peer.addr));
175136bc29aStobhe 	memcpy(&pol.pol_local.addr, &sa->sa_local.addr, sizeof(sa->sa_local.addr));
17686ddd613Stobhe 	pol.pol_flows = sa->sa_policy->pol_flows;
17786ddd613Stobhe 	pol.pol_nflows = sa->sa_policy->pol_nflows;
17886ddd613Stobhe 
17986ddd613Stobhe 	if (sa->sa_hdr.sh_initiator) {
18086ddd613Stobhe 		lid = &sa->sa_iid;
18186ddd613Stobhe 		pid = &sa->sa_rid;
18286ddd613Stobhe 	} else {
18386ddd613Stobhe 		lid = &sa->sa_rid;
18486ddd613Stobhe 		pid = &sa->sa_iid;
18586ddd613Stobhe 	}
18686ddd613Stobhe 
18786ddd613Stobhe 	if (pid->id_type &&
18886ddd613Stobhe 	    ikev2_print_id(pid, idstr, IKED_ID_SIZE) == 0 &&
18986ddd613Stobhe 	    (s = strchr(idstr, '/')) != NULL) {
19086ddd613Stobhe 		pol.pol_peerid.id_type = pid->id_type;
19186ddd613Stobhe 		pol.pol_peerid.id_length = strlen(s+1);
19286ddd613Stobhe 		strlcpy(pol.pol_peerid.id_data, s+1,
19386ddd613Stobhe 		    sizeof(pol.pol_peerid.id_data));
19486ddd613Stobhe 		log_debug("%s: peerid '%s'", __func__, s+1);
19586ddd613Stobhe 	}
19686ddd613Stobhe 
19786ddd613Stobhe 	if (lid->id_type &&
19886ddd613Stobhe 	    ikev2_print_id(lid, idstr, IKED_ID_SIZE) == 0 &&
19986ddd613Stobhe 	    (s = strchr(idstr, '/')) != NULL) {
20086ddd613Stobhe 		pol.pol_localid.id_type = lid->id_type;
20186ddd613Stobhe 		pol.pol_localid.id_length = strlen(s+1);
20286ddd613Stobhe 		strlcpy(pol.pol_localid.id_data, s+1,
20386ddd613Stobhe 		    sizeof(pol.pol_localid.id_data));
20486ddd613Stobhe 		log_debug("%s: localid '%s'", __func__, s+1);
20586ddd613Stobhe 	}
20686ddd613Stobhe 
20786ddd613Stobhe 	/* Try to find a matching policy for this message */
20886ddd613Stobhe 	if ((pol_found = policy_test(env, &pol)) != NULL) {
20986ddd613Stobhe 		log_debug("%s: found policy '%s'", SPI_SA(sa, __func__),
21086ddd613Stobhe 		    pol_found->pol_name);
21186ddd613Stobhe 		sa->sa_policy = pol_found;
21286ddd613Stobhe 		return (0);
21386ddd613Stobhe 	}
21486ddd613Stobhe 
21586ddd613Stobhe 	/* No policy found */
21686ddd613Stobhe 	return (-1);
21786ddd613Stobhe }
21886ddd613Stobhe 
21986ddd613Stobhe /*
220b0435d4dStobhe  * Find a policy matching the query policy key in the global env.
221b0435d4dStobhe  * If multiple matching policies are found the policy with the highest
222b0435d4dStobhe  * priority is selected.
223b0435d4dStobhe  *
224b0435d4dStobhe  * Returns a pointer to a matching policy, or NULL if no policy matches.
225b0435d4dStobhe  */
226e2015428Sreyk struct iked_policy *
policy_test(struct iked * env,struct iked_policy * key)227e2015428Sreyk policy_test(struct iked *env, struct iked_policy *key)
22845ae9d61Sreyk {
229e2015428Sreyk 	struct iked_policy	*p = NULL, *pol = NULL;
23045ae9d61Sreyk 
231e2015428Sreyk 	p = TAILQ_FIRST(&env->sc_policies);
232e2015428Sreyk 	while (p != NULL) {
233e2015428Sreyk 		if (p->pol_flags & IKED_POLICY_SKIP)
234e2015428Sreyk 			p = p->pol_skip[IKED_SKIP_FLAGS];
235e2015428Sreyk 		else if (key->pol_af && p->pol_af &&
236e2015428Sreyk 		    key->pol_af != p->pol_af)
237e2015428Sreyk 			p = p->pol_skip[IKED_SKIP_AF];
238e2015428Sreyk 		else if (sockaddr_cmp((struct sockaddr *)&key->pol_peer.addr,
239e2015428Sreyk 		    (struct sockaddr *)&p->pol_peer.addr,
240e2015428Sreyk 		    p->pol_peer.addr_mask) != 0)
241e2015428Sreyk 			p = p->pol_skip[IKED_SKIP_DST_ADDR];
242e2015428Sreyk 		else if (sockaddr_cmp((struct sockaddr *)&key->pol_local.addr,
243e2015428Sreyk 		    (struct sockaddr *)&p->pol_local.addr,
244e2015428Sreyk 		    p->pol_local.addr_mask) != 0)
245e2015428Sreyk 			p = p->pol_skip[IKED_SKIP_SRC_ADDR];
246e2015428Sreyk 		else {
2479dbd37b8Sreyk 			/*
24886ddd613Stobhe 			 * Check if flows are requested and if they
24986ddd613Stobhe 			 * are compatible.
2509dbd37b8Sreyk 			 */
25186ddd613Stobhe 			if (key->pol_nflows && policy_test_flows(key, p)) {
2529dbd37b8Sreyk 				p = TAILQ_NEXT(p, pol_entry);
2539dbd37b8Sreyk 				continue;
2549dbd37b8Sreyk 			}
25595826121Smarkus 			/* make sure the peer ID matches */
25695826121Smarkus 			if (key->pol_peerid.id_type &&
2577ff51f1dStobhe 			    p->pol_peerid.id_type &&
25895826121Smarkus 			    (key->pol_peerid.id_type != p->pol_peerid.id_type ||
25995826121Smarkus 			    memcmp(key->pol_peerid.id_data,
26095826121Smarkus 			    p->pol_peerid.id_data,
26195826121Smarkus 			    sizeof(key->pol_peerid.id_data)) != 0)) {
26295826121Smarkus 				p = TAILQ_NEXT(p, pol_entry);
26395826121Smarkus 				continue;
26495826121Smarkus 			}
2659dbd37b8Sreyk 
26686ddd613Stobhe 			/* make sure the local ID matches */
26786ddd613Stobhe 			if (key->pol_localid.id_type &&
26886ddd613Stobhe 			    p->pol_localid.id_type &&
26986ddd613Stobhe 			    (key->pol_localid.id_type != p->pol_localid.id_type ||
27086ddd613Stobhe 			    memcmp(key->pol_localid.id_data,
27186ddd613Stobhe 			    p->pol_localid.id_data,
27286ddd613Stobhe 			    sizeof(key->pol_localid.id_data)) != 0)) {
27386ddd613Stobhe 				log_info("%s: localid mismatch", __func__);
27486ddd613Stobhe 				p = TAILQ_NEXT(p, pol_entry);
27586ddd613Stobhe 				continue;
27686ddd613Stobhe 			}
27786ddd613Stobhe 
2783ddf179cStobhe 			/* check transport mode */
2793ddf179cStobhe 			if ((key->pol_flags & IKED_POLICY_TRANSPORT) &&
2803ddf179cStobhe 			    !(p->pol_flags & IKED_POLICY_TRANSPORT)) {
2813ddf179cStobhe 				p = TAILQ_NEXT(p, pol_entry);
2823ddf179cStobhe 				continue;
2833ddf179cStobhe 			}
2843ddf179cStobhe 
285b0435d4dStobhe 			/* Make sure the proposals are compatible */
286b0435d4dStobhe 			if (TAILQ_FIRST(&key->pol_proposals) &&
287a738df56Stobhe 			    proposals_negotiate(NULL, &p->pol_proposals,
2884ad5fa93Stobhe 			    &key->pol_proposals, 0, -1) == -1) {
289b0435d4dStobhe 				p = TAILQ_NEXT(p, pol_entry);
290b0435d4dStobhe 				continue;
291b0435d4dStobhe 			}
292b0435d4dStobhe 
293e2015428Sreyk 			/* Policy matched */
294e2015428Sreyk 			pol = p;
29545ae9d61Sreyk 
296e2015428Sreyk 			if (pol->pol_flags & IKED_POLICY_QUICK)
297e2015428Sreyk 				break;
29845ae9d61Sreyk 
2999dbd37b8Sreyk 			/* Continue to find last matching policy */
300e2015428Sreyk 			p = TAILQ_NEXT(p, pol_entry);
301e2015428Sreyk 		}
302e2015428Sreyk 	}
303e2015428Sreyk 
304e2015428Sreyk 	return (pol);
305e2015428Sreyk }
306e2015428Sreyk 
30786ddd613Stobhe static int
policy_test_flows(struct iked_policy * key,struct iked_policy * p)30886ddd613Stobhe policy_test_flows(struct iked_policy *key, struct iked_policy *p)
30986ddd613Stobhe {
31086ddd613Stobhe 	struct iked_flow	*f;
31186ddd613Stobhe 
31286ddd613Stobhe 	for (f = RB_MIN(iked_flows, &key->pol_flows); f != NULL;
31386ddd613Stobhe 	    f = RB_NEXT(iked_flows, &key->pol_flows, f))
31486ddd613Stobhe 		if (RB_FIND(iked_flows, &p->pol_flows, f) == NULL)
31586ddd613Stobhe 			return (-1);
31686ddd613Stobhe 
31786ddd613Stobhe 	return (0);
31886ddd613Stobhe }
31986ddd613Stobhe 
320e2015428Sreyk #define	IKED_SET_SKIP_STEPS(i)						\
321e2015428Sreyk 	do {								\
322e2015428Sreyk 		while (head[i] != cur) {				\
323e2015428Sreyk 			head[i]->pol_skip[i] = cur;			\
324e2015428Sreyk 			head[i] = TAILQ_NEXT(head[i], pol_entry);	\
325e2015428Sreyk 		}							\
326e2015428Sreyk 	} while (0)
327e2015428Sreyk 
328e2015428Sreyk /* This code is derived from pf_calc_skip_steps() from pf.c */
329e2015428Sreyk void
policy_calc_skip_steps(struct iked_policies * policies)330e2015428Sreyk policy_calc_skip_steps(struct iked_policies *policies)
331e2015428Sreyk {
332e2015428Sreyk 	struct iked_policy	*head[IKED_SKIP_COUNT], *cur, *prev;
333e2015428Sreyk 	int			 i;
334e2015428Sreyk 
335e2015428Sreyk 	cur = TAILQ_FIRST(policies);
336e2015428Sreyk 	prev = cur;
337e2015428Sreyk 	for (i = 0; i < IKED_SKIP_COUNT; ++i)
338e2015428Sreyk 		head[i] = cur;
339e2015428Sreyk 	while (cur != NULL) {
340e2015428Sreyk 		if (cur->pol_flags & IKED_POLICY_SKIP)
341e2015428Sreyk 			IKED_SET_SKIP_STEPS(IKED_SKIP_FLAGS);
3424783d4f9Stobhe 		if (cur->pol_af != AF_UNSPEC &&
343e2015428Sreyk 		    prev->pol_af != AF_UNSPEC &&
344e2015428Sreyk 		    cur->pol_af != prev->pol_af)
345e2015428Sreyk 			IKED_SET_SKIP_STEPS(IKED_SKIP_AF);
3464783d4f9Stobhe 		if (IKED_ADDR_NEQ(&cur->pol_peer, &prev->pol_peer))
347e2015428Sreyk 			IKED_SET_SKIP_STEPS(IKED_SKIP_DST_ADDR);
3484783d4f9Stobhe 		if (IKED_ADDR_NEQ(&cur->pol_local, &prev->pol_local))
349e2015428Sreyk 			IKED_SET_SKIP_STEPS(IKED_SKIP_SRC_ADDR);
350e2015428Sreyk 
351e2015428Sreyk 		prev = cur;
352e2015428Sreyk 		cur = TAILQ_NEXT(cur, pol_entry);
353e2015428Sreyk 	}
354e2015428Sreyk 	for (i = 0; i < IKED_SKIP_COUNT; ++i)
355e2015428Sreyk 		IKED_SET_SKIP_STEPS(i);
35645ae9d61Sreyk }
35745ae9d61Sreyk 
35845ae9d61Sreyk void
policy_ref(struct iked * env,struct iked_policy * pol)35945ae9d61Sreyk policy_ref(struct iked *env, struct iked_policy *pol)
36045ae9d61Sreyk {
361ac3a6947Stobhe 	if (pol->pol_flags & IKED_POLICY_REFCNT)
36245ae9d61Sreyk 		pol->pol_refcnt++;
36345ae9d61Sreyk }
36445ae9d61Sreyk 
36545ae9d61Sreyk void
policy_unref(struct iked * env,struct iked_policy * pol)36645ae9d61Sreyk policy_unref(struct iked *env, struct iked_policy *pol)
36745ae9d61Sreyk {
36845ae9d61Sreyk 	if (pol == NULL || (pol->pol_flags & IKED_POLICY_REFCNT) == 0)
36945ae9d61Sreyk 		return;
37045ae9d61Sreyk 	if (--(pol->pol_refcnt) <= 0)
37145ae9d61Sreyk 		config_free_policy(env, pol);
37232b6fc39Smarkus 	else {
37332b6fc39Smarkus 		struct iked_sa		*tmp;
37432b6fc39Smarkus 		int			 count = 0;
37532b6fc39Smarkus 
37632b6fc39Smarkus 		TAILQ_FOREACH(tmp, &pol->pol_sapeers, sa_peer_entry)
37732b6fc39Smarkus 			count++;
37832b6fc39Smarkus 		if (count != pol->pol_refcnt)
37932b6fc39Smarkus 			log_warnx("%s: ERROR pol %p pol_refcnt %d != count %d",
38032b6fc39Smarkus 			    __func__, pol, pol->pol_refcnt, count);
38132b6fc39Smarkus 	}
38245ae9d61Sreyk }
38345ae9d61Sreyk 
38445ae9d61Sreyk void
sa_state(struct iked * env,struct iked_sa * sa,int state)38545ae9d61Sreyk sa_state(struct iked *env, struct iked_sa *sa, int state)
38645ae9d61Sreyk {
38745ae9d61Sreyk 	const char		*a;
38845ae9d61Sreyk 	const char		*b;
3894a986ab9Smarkus 	int			 ostate = sa->sa_state;
39045ae9d61Sreyk 
3914a986ab9Smarkus 	a = print_map(ostate, ikev2_state_map);
39245ae9d61Sreyk 	b = print_map(state, ikev2_state_map);
39345ae9d61Sreyk 
3944a986ab9Smarkus 	sa->sa_state = state;
3954a986ab9Smarkus 	if (ostate != IKEV2_STATE_INIT &&
3964a986ab9Smarkus 	    !sa_stateok(sa, state)) {
397230de460Stobhe 		log_debug("%s: cannot switch: %s -> %s",
398230de460Stobhe 		    SPI_SA(sa, __func__), a, b);
3994a986ab9Smarkus 		sa->sa_state = ostate;
4004a986ab9Smarkus 	} else if (ostate != sa->sa_state) {
401badf9456Sreyk 		switch (state) {
402badf9456Sreyk 		case IKEV2_STATE_ESTABLISHED:
403badf9456Sreyk 		case IKEV2_STATE_CLOSED:
404230de460Stobhe 			log_debug("%s: %s -> %s from %s to %s policy '%s'",
405ecea226bStobhe 			    SPI_SA(sa, __func__), a, b,
40614e2a040Stb 			    print_addr(&sa->sa_peer.addr),
40714e2a040Stb 			    print_addr(&sa->sa_local.addr),
4086e1880a3Smarkus 			    sa->sa_policy ? sa->sa_policy->pol_name :
4096e1880a3Smarkus 			    "<unknown>");
410badf9456Sreyk 			break;
411badf9456Sreyk 		default:
412230de460Stobhe 			log_debug("%s: %s -> %s",
413230de460Stobhe 			    SPI_SA(sa, __func__), a, b);
414badf9456Sreyk 			break;
415badf9456Sreyk 		}
416badf9456Sreyk 	}
417badf9456Sreyk 
418b41cc0c8Stobhe 	if (ostate != sa->sa_state) {
419b41cc0c8Stobhe 		switch (sa->sa_state) {
420b41cc0c8Stobhe 		case IKEV2_STATE_ESTABLISHED:
421b41cc0c8Stobhe 			ikestat_inc(env, ikes_sa_established_total);
422b41cc0c8Stobhe 			ikestat_inc(env, ikes_sa_established_current);
423b41cc0c8Stobhe 			break;
424b41cc0c8Stobhe 		case IKEV2_STATE_CLOSED:
425b41cc0c8Stobhe 		case IKEV2_STATE_CLOSING:
426b41cc0c8Stobhe 			switch (ostate) {
427b41cc0c8Stobhe 			case IKEV2_STATE_ESTABLISHED:
428b41cc0c8Stobhe 				ikestat_dec(env, ikes_sa_established_current);
429b41cc0c8Stobhe 				break;
430b41cc0c8Stobhe 			case IKEV2_STATE_CLOSED:
431b41cc0c8Stobhe 			case IKEV2_STATE_CLOSING:
432b41cc0c8Stobhe 				break;
433b41cc0c8Stobhe 			default:
434b41cc0c8Stobhe 				ikestat_inc(env, ikes_sa_established_failures);
435b41cc0c8Stobhe 				break;
436b41cc0c8Stobhe 			}
437b41cc0c8Stobhe 			break;
438b41cc0c8Stobhe 		}
439b41cc0c8Stobhe 	}
44045ae9d61Sreyk }
44145ae9d61Sreyk 
44245ae9d61Sreyk void
sa_stateflags(struct iked_sa * sa,unsigned int flags)443d09d3a7dSreyk sa_stateflags(struct iked_sa *sa, unsigned int flags)
44445ae9d61Sreyk {
445d09d3a7dSreyk 	unsigned int	require;
44658993c5cSreyk 
44758993c5cSreyk 	if (sa->sa_state > IKEV2_STATE_SA_INIT)
44858993c5cSreyk 		require = sa->sa_statevalid;
44958993c5cSreyk 	else
45058993c5cSreyk 		require = sa->sa_stateinit;
45158993c5cSreyk 
452a2749d89Sreyk 	log_debug("%s: 0x%04x -> 0x%04x %s (required 0x%04x %s)", __func__,
45345ae9d61Sreyk 	    sa->sa_stateflags, sa->sa_stateflags | flags,
4549ff1927fSreyk 	    print_bits(sa->sa_stateflags | flags, IKED_REQ_BITS), require,
4559ff1927fSreyk 	    print_bits(require, IKED_REQ_BITS));
45645ae9d61Sreyk 
45745ae9d61Sreyk 	sa->sa_stateflags |= flags;
45845ae9d61Sreyk }
45945ae9d61Sreyk 
46045ae9d61Sreyk int
sa_stateok(const struct iked_sa * sa,int state)461d571373cStobhe sa_stateok(const struct iked_sa *sa, int state)
46245ae9d61Sreyk {
463d09d3a7dSreyk 	unsigned int	 require;
46458993c5cSreyk 
46545ae9d61Sreyk 	if (sa->sa_state < state)
46645ae9d61Sreyk 		return (0);
46745ae9d61Sreyk 
46858993c5cSreyk 	if (state == IKEV2_STATE_SA_INIT)
46958993c5cSreyk 		require = sa->sa_stateinit;
47058993c5cSreyk 	else
47158993c5cSreyk 		require = sa->sa_statevalid;
47258993c5cSreyk 
47358993c5cSreyk 	if (state == IKEV2_STATE_SA_INIT ||
47458993c5cSreyk 	    state == IKEV2_STATE_VALID ||
475c26c395bSmikeb 	    state == IKEV2_STATE_EAP_VALID) {
476a2749d89Sreyk 		log_debug("%s: %s flags 0x%04x, require 0x%04x %s", __func__,
47758993c5cSreyk 		    print_map(state, ikev2_state_map),
47858993c5cSreyk 		    (sa->sa_stateflags & require), require,
47958993c5cSreyk 		    print_bits(require, IKED_REQ_BITS));
48058993c5cSreyk 
48158993c5cSreyk 		if ((sa->sa_stateflags & require) != require)
48245ae9d61Sreyk 			return (0);	/* not ready, ignore */
48345ae9d61Sreyk 	}
48445ae9d61Sreyk 	return (1);
48545ae9d61Sreyk }
48645ae9d61Sreyk 
48745ae9d61Sreyk struct iked_sa *
sa_new(struct iked * env,uint64_t ispi,uint64_t rspi,unsigned int initiator,struct iked_policy * pol)488d09d3a7dSreyk sa_new(struct iked *env, uint64_t ispi, uint64_t rspi,
489d09d3a7dSreyk     unsigned int initiator, struct iked_policy *pol)
49045ae9d61Sreyk {
49145ae9d61Sreyk 	struct iked_sa	*sa;
4921a459e1eSmarkus 	struct iked_sa	*old;
4931b0d4946Sreyk 	struct iked_id	*localid;
494d09d3a7dSreyk 	unsigned int	 diff;
49545ae9d61Sreyk 
496ae494144Sreyk 	if ((ispi == 0 && rspi == 0) ||
497ae494144Sreyk 	    (sa = sa_lookup(env, ispi, rspi, initiator)) == NULL) {
49845ae9d61Sreyk 		/* Create new SA */
4994697d2e3Smarkus 		if (!initiator && ispi == 0) {
5004697d2e3Smarkus 			log_debug("%s: cannot create responder IKE SA w/o ispi",
5014697d2e3Smarkus 			    __func__);
50245ae9d61Sreyk 			return (NULL);
50345ae9d61Sreyk 		}
5044697d2e3Smarkus 		sa = config_new_sa(env, initiator);
5054697d2e3Smarkus 		if (sa == NULL) {
5064697d2e3Smarkus 			log_debug("%s: failed to allocate IKE SA", __func__);
5074697d2e3Smarkus 			return (NULL);
5084697d2e3Smarkus 		}
5094697d2e3Smarkus 		if (!initiator)
5104697d2e3Smarkus 			sa->sa_hdr.sh_ispi = ispi;
5114697d2e3Smarkus 		old = RB_INSERT(iked_sas, &env->sc_sas, sa);
5124697d2e3Smarkus 		if (old && old != sa) {
5134697d2e3Smarkus 			log_warnx("%s: duplicate IKE SA", __func__);
5144697d2e3Smarkus 			config_free_sa(env, sa);
5154697d2e3Smarkus 			return (NULL);
5164697d2e3Smarkus 		}
5174697d2e3Smarkus 	}
5184697d2e3Smarkus 	/* Update rspi in the initator case */
5194697d2e3Smarkus 	if (initiator && sa->sa_hdr.sh_rspi == 0 && rspi)
5204697d2e3Smarkus 		sa->sa_hdr.sh_rspi = rspi;
5214697d2e3Smarkus 
522b0d5ea1dSpatrick 	if (pol == NULL && sa->sa_policy == NULL)
523b0d5ea1dSpatrick 		fatalx("%s: sa %p no policy", __func__, sa);
524b0d5ea1dSpatrick 	else if (sa->sa_policy == NULL) {
525b0d5ea1dSpatrick 		policy_ref(env, pol);
5261b0d4946Sreyk 		sa->sa_policy = pol;
52732b6fc39Smarkus 		TAILQ_INSERT_TAIL(&pol->pol_sapeers, sa, sa_peer_entry);
52832b6fc39Smarkus 	} else
5291b0d4946Sreyk 		pol = sa->sa_policy;
530ae494144Sreyk 
5314a986ab9Smarkus 	sa->sa_statevalid = IKED_REQ_AUTH|IKED_REQ_AUTHVALID|IKED_REQ_SA;
5321b0d4946Sreyk 	if (pol != NULL && pol->pol_auth.auth_eap) {
5334a986ab9Smarkus 		sa->sa_statevalid |= IKED_REQ_CERT|IKED_REQ_EAPVALID;
5341b0d4946Sreyk 	} else if (pol != NULL && pol->pol_auth.auth_method !=
535ae494144Sreyk 	    IKEV2_AUTH_SHARED_KEY_MIC) {
5364a986ab9Smarkus 		sa->sa_statevalid |= IKED_REQ_CERTVALID|IKED_REQ_CERT;
53745ae9d61Sreyk 	}
538d4614e98Sreyk 
53958993c5cSreyk 	if (initiator) {
54058993c5cSreyk 		localid = &sa->sa_iid;
5414a986ab9Smarkus 		diff = IKED_REQ_CERTVALID|IKED_REQ_AUTHVALID|IKED_REQ_SA|
5424a986ab9Smarkus 		    IKED_REQ_EAPVALID;
54358993c5cSreyk 		sa->sa_stateinit = sa->sa_statevalid & ~diff;
54458993c5cSreyk 		sa->sa_statevalid = sa->sa_statevalid & diff;
54558993c5cSreyk 	} else
54658993c5cSreyk 		localid = &sa->sa_rid;
54758993c5cSreyk 
548c45df278Stobhe 	if (pol != NULL &&
549d4614e98Sreyk 	    ikev2_policy2id(&pol->pol_localid, localid, 1) != 0) {
5501b0d4946Sreyk 		log_debug("%s: failed to get local id", __func__);
55184a9a21bStobhe 		ikev2_ike_sa_setreason(sa, "failed to get local id");
5521b0d4946Sreyk 		sa_free(env, sa);
5531b0d4946Sreyk 		return (NULL);
5541b0d4946Sreyk 	}
5551b0d4946Sreyk 
55645ae9d61Sreyk 	return (sa);
55745ae9d61Sreyk }
55845ae9d61Sreyk 
5591f864a9aStobhe int
policy_generate_ts(struct iked_policy * pol)5601f864a9aStobhe policy_generate_ts(struct iked_policy *pol)
5611f864a9aStobhe {
5621f864a9aStobhe 	struct iked_flow	*flow;
5631f864a9aStobhe 
5641f864a9aStobhe 	/* Generate list of traffic selectors from flows */
5651f864a9aStobhe 	RB_FOREACH(flow, iked_flows, &pol->pol_flows) {
5661f864a9aStobhe 		if (ts_insert_unique(&flow->flow_src, &pol->pol_tssrc,
5671f864a9aStobhe 		    flow->flow_ipproto) == 1)
5681f864a9aStobhe 			pol->pol_tssrc_count++;
5691f864a9aStobhe 		if (ts_insert_unique(&flow->flow_dst, &pol->pol_tsdst,
5701f864a9aStobhe 		    flow->flow_ipproto) == 1)
5711f864a9aStobhe 			pol->pol_tsdst_count++;
5721f864a9aStobhe 	}
5731f864a9aStobhe 	if (pol->pol_tssrc_count > IKEV2_MAXNUM_TSS ||
5741f864a9aStobhe 	    pol->pol_tsdst_count > IKEV2_MAXNUM_TSS)
5751f864a9aStobhe 		return (-1);
5761f864a9aStobhe 
5771f864a9aStobhe 	return (0);
5781f864a9aStobhe }
5791f864a9aStobhe 
5801f864a9aStobhe int
ts_insert_unique(struct iked_addr * addr,struct iked_tss * tss,int ipproto)5811f864a9aStobhe ts_insert_unique(struct iked_addr *addr, struct iked_tss *tss, int ipproto)
5821f864a9aStobhe {
5831f864a9aStobhe 	struct iked_ts		*ts;
5841f864a9aStobhe 
5851f864a9aStobhe 	/* Remove duplicates */
5861f864a9aStobhe 	TAILQ_FOREACH(ts, tss, ts_entry) {
5871f864a9aStobhe 		if (addr_cmp(addr, &ts->ts_addr, 1) == 0)
5881f864a9aStobhe 			return (0);
5891f864a9aStobhe 	}
5901f864a9aStobhe 
5911f864a9aStobhe 	if ((ts = calloc(1, sizeof(*ts))) == NULL)
5921f864a9aStobhe 		return (-1);
5931f864a9aStobhe 
5941f864a9aStobhe 	ts->ts_ipproto = ipproto;
5951f864a9aStobhe 	ts->ts_addr = *addr;
5961f864a9aStobhe 
5971f864a9aStobhe 	TAILQ_INSERT_TAIL(tss, ts, ts_entry);
5981f864a9aStobhe 	return (1);
5991f864a9aStobhe }
6001f864a9aStobhe 
60145ae9d61Sreyk void
sa_free(struct iked * env,struct iked_sa * sa)60245ae9d61Sreyk sa_free(struct iked *env, struct iked_sa *sa)
60345ae9d61Sreyk {
604ca2fa981Stobhe 	struct iked_sa	*osa;
605ca2fa981Stobhe 
60684a9a21bStobhe 	if (sa->sa_reason)
60784a9a21bStobhe 		log_info("%s: %s", SPI_SA(sa, __func__), sa->sa_reason);
60884a9a21bStobhe 	else
60984a9a21bStobhe 		log_debug("%s: ispi %s rspi %s", SPI_SA(sa, __func__),
61045ae9d61Sreyk 		    print_spi(sa->sa_hdr.sh_ispi, 8),
61145ae9d61Sreyk 		    print_spi(sa->sa_hdr.sh_rspi, 8));
61245ae9d61Sreyk 
613ca2fa981Stobhe 	/* IKE rekeying running? (old sa freed before new sa) */
61493415f65Smikeb 	if (sa->sa_nexti) {
61593415f65Smikeb 		RB_REMOVE(iked_sas, &env->sc_sas, sa->sa_nexti);
616729f601bStobhe 		if (sa->sa_nexti->sa_dstid_entry_valid) {
617729f601bStobhe 			log_info("%s: nexti established? %s",
618729f601bStobhe 			    SPI_SA(sa, __func__), SPI_SA(sa->sa_nexti, NULL));
619729f601bStobhe 			sa_dstid_remove(env, sa->sa_nexti);
620729f601bStobhe 		}
62193415f65Smikeb 		config_free_sa(env, sa->sa_nexti);
62293415f65Smikeb 	}
62393415f65Smikeb 	if (sa->sa_nextr) {
62493415f65Smikeb 		RB_REMOVE(iked_sas, &env->sc_sas, sa->sa_nextr);
625729f601bStobhe 		if (sa->sa_nextr->sa_dstid_entry_valid) {
626729f601bStobhe 			log_info("%s: nextr established? %s",
627729f601bStobhe 			    SPI_SA(sa, __func__), SPI_SA(sa->sa_nextr, NULL));
628729f601bStobhe 			sa_dstid_remove(env, sa->sa_nextr);
629729f601bStobhe 		}
63093415f65Smikeb 		config_free_sa(env, sa->sa_nextr);
6316e1880a3Smarkus 	}
632ca2fa981Stobhe 	/* reset matching backpointers (new sa freed before old sa) */
633ca2fa981Stobhe 	if ((osa = sa->sa_previ) != NULL) {
634ca2fa981Stobhe 		if (osa->sa_nexti == sa) {
635ca2fa981Stobhe 			log_debug("%s: resetting: sa %p == osa->sa_nexti %p"
636ca2fa981Stobhe 			    " (osa %p)",
637ca2fa981Stobhe 			    SPI_SA(sa, __func__), osa, sa, osa->sa_nexti);
638ca2fa981Stobhe 			osa->sa_nexti = NULL;
639ca2fa981Stobhe 		} else {
640ca2fa981Stobhe 			log_info("%s: inconsistent: sa %p != osa->sa_nexti %p"
641ca2fa981Stobhe 			    " (osa %p)",
642ca2fa981Stobhe 			    SPI_SA(sa, __func__), osa, sa, osa->sa_nexti);
643ca2fa981Stobhe 		}
644ca2fa981Stobhe 	}
645ca2fa981Stobhe 	if ((osa = sa->sa_prevr) != NULL) {
646ca2fa981Stobhe 		if (osa->sa_nextr == sa) {
647ca2fa981Stobhe 			log_debug("%s: resetting: sa %p == osa->sa_nextr %p"
648ca2fa981Stobhe 			    " (osa %p)",
649ca2fa981Stobhe 			    SPI_SA(sa, __func__), osa, sa, osa->sa_nextr);
650ca2fa981Stobhe 			osa->sa_nextr = NULL;
651ca2fa981Stobhe 		} else {
652ca2fa981Stobhe 			log_info("%s: inconsistent: sa %p != osa->sa_nextr %p"
653ca2fa981Stobhe 			    " (osa %p)",
654ca2fa981Stobhe 			    SPI_SA(sa, __func__), osa, sa, osa->sa_nextr);
655ca2fa981Stobhe 		}
656ca2fa981Stobhe 	}
6574697d2e3Smarkus 	RB_REMOVE(iked_sas, &env->sc_sas, sa);
658729f601bStobhe 	if (sa->sa_dstid_entry_valid)
659729f601bStobhe 		sa_dstid_remove(env, sa);
66045ae9d61Sreyk 	config_free_sa(env, sa);
66145ae9d61Sreyk }
66245ae9d61Sreyk 
6639dbd37b8Sreyk void
sa_free_flows(struct iked * env,struct iked_saflows * head)6649dbd37b8Sreyk sa_free_flows(struct iked *env, struct iked_saflows *head)
6659dbd37b8Sreyk {
6668177f5d5Stobhe 	struct iked_flow	*flow, *flowtmp;
6679dbd37b8Sreyk 
6688177f5d5Stobhe 	TAILQ_FOREACH_SAFE(flow, head, flow_entry, flowtmp) {
6699dbd37b8Sreyk 		log_debug("%s: free %p", __func__, flow);
6709dbd37b8Sreyk 
6719dbd37b8Sreyk 		if (flow->flow_loaded)
6729dbd37b8Sreyk 			RB_REMOVE(iked_flows, &env->sc_activeflows, flow);
6739dbd37b8Sreyk 		TAILQ_REMOVE(head, flow, flow_entry);
6748f6f6c19Stobhe 		(void)pfkey_flow_delete(env, flow);
6759dbd37b8Sreyk 		flow_free(flow);
6769dbd37b8Sreyk 	}
6779dbd37b8Sreyk }
6789dbd37b8Sreyk 
6799dbd37b8Sreyk 
680b3ef9220Sreyk int
sa_address(struct iked_sa * sa,struct iked_addr * addr,struct sockaddr * peer)6812f3a6dc6Stobhe sa_address(struct iked_sa *sa, struct iked_addr *addr, struct sockaddr *peer)
682b3ef9220Sreyk {
683b3ef9220Sreyk 	bzero(addr, sizeof(*addr));
6842f3a6dc6Stobhe 	addr->addr_af = peer->sa_family;
6852f3a6dc6Stobhe 	addr->addr_port = htons(socket_getport(peer));
6862f3a6dc6Stobhe 	memcpy(&addr->addr, peer, peer->sa_len);
687b3ef9220Sreyk 	if (socket_af((struct sockaddr *)&addr->addr, addr->addr_port) == -1) {
688b3ef9220Sreyk 		log_debug("%s: invalid address", __func__);
689b3ef9220Sreyk 		return (-1);
690b3ef9220Sreyk 	}
691b3ef9220Sreyk 	return (0);
692b3ef9220Sreyk }
693b3ef9220Sreyk 
694264f8b22Stobhe int
sa_configure_iface(struct iked * env,struct iked_sa * sa,int add)695264f8b22Stobhe sa_configure_iface(struct iked *env, struct iked_sa *sa, int add)
696264f8b22Stobhe {
697264f8b22Stobhe 	struct iked_flow	*saflow;
698264f8b22Stobhe 	struct sockaddr		*caddr;
699264f8b22Stobhe 	int			 rdomain;
700264f8b22Stobhe 
701ca71a655Stobhe 	if (sa->sa_policy == NULL || sa->sa_policy->pol_iface == 0)
702264f8b22Stobhe 		return (0);
703264f8b22Stobhe 
7049ef39cf4Stobhe 	if (sa->sa_cp_dns) {
7059ef39cf4Stobhe 		if (vroute_setdns(env, add,
7069ef39cf4Stobhe 		    (struct sockaddr *)&sa->sa_cp_dns->addr,
7079ef39cf4Stobhe 		    sa->sa_policy->pol_iface) != 0)
7089ef39cf4Stobhe 			return (-1);
7099ef39cf4Stobhe 	}
7109ef39cf4Stobhe 
711fc2f8a0cStobhe 	if (!sa->sa_cp_addr && !sa->sa_cp_addr6)
712fc2f8a0cStobhe 		return (0);
713fc2f8a0cStobhe 
714264f8b22Stobhe 	if (sa->sa_cp_addr) {
71575f6dc0aStobhe 		if (vroute_setaddr(env, add,
71675f6dc0aStobhe 		    (struct sockaddr *)&sa->sa_cp_addr->addr,
71775f6dc0aStobhe 		    sa->sa_cp_addr->addr_mask, sa->sa_policy->pol_iface) != 0)
718264f8b22Stobhe 			return (-1);
719264f8b22Stobhe 	}
720264f8b22Stobhe 	if (sa->sa_cp_addr6) {
72175f6dc0aStobhe 		if (vroute_setaddr(env, add,
72275f6dc0aStobhe 		    (struct sockaddr *)&sa->sa_cp_addr6->addr,
72375f6dc0aStobhe 		    sa->sa_cp_addr6->addr_mask, sa->sa_policy->pol_iface) != 0)
724264f8b22Stobhe 			return (-1);
725264f8b22Stobhe 	}
726264f8b22Stobhe 
727264f8b22Stobhe 	if (add) {
728264f8b22Stobhe 		/* Add direct route to peer */
729264f8b22Stobhe 		if (vroute_setcloneroute(env, getrtable(),
730264f8b22Stobhe 		    (struct sockaddr *)&sa->sa_peer.addr, 0, NULL))
731264f8b22Stobhe 			return (-1);
732264f8b22Stobhe 	} else {
733264f8b22Stobhe 		if (vroute_setdelroute(env, getrtable(),
734264f8b22Stobhe 		    (struct sockaddr *)&sa->sa_peer.addr,
735264f8b22Stobhe 		    0, NULL))
736264f8b22Stobhe 			return (-1);
737264f8b22Stobhe 	}
738264f8b22Stobhe 
739264f8b22Stobhe 	TAILQ_FOREACH(saflow, &sa->sa_flows, flow_entry) {
740264f8b22Stobhe 		rdomain = saflow->flow_rdomain == -1 ?
741264f8b22Stobhe 		    getrtable() : saflow->flow_rdomain;
742264f8b22Stobhe 
743264f8b22Stobhe 		switch(saflow->flow_src.addr_af) {
744264f8b22Stobhe 		case AF_INET:
74562220633Stobhe 			if (sa->sa_cp_addr == NULL)
74662220633Stobhe 				continue;
747264f8b22Stobhe 			caddr = (struct sockaddr *)&sa->sa_cp_addr->addr;
748264f8b22Stobhe 			break;
749264f8b22Stobhe 		case AF_INET6:
75062220633Stobhe 			if (sa->sa_cp_addr6 == NULL)
75162220633Stobhe 				continue;
752264f8b22Stobhe 			caddr = (struct sockaddr *)&sa->sa_cp_addr6->addr;
753264f8b22Stobhe 			break;
754264f8b22Stobhe 		default:
755264f8b22Stobhe 			return (-1);
756264f8b22Stobhe 		}
757264f8b22Stobhe 		if (sockaddr_cmp((struct sockaddr *)&saflow->flow_src.addr,
758264f8b22Stobhe 		    caddr, -1) != 0)
759264f8b22Stobhe 			continue;
760264f8b22Stobhe 
761264f8b22Stobhe 		if (add) {
762264f8b22Stobhe 			if (vroute_setaddroute(env, rdomain,
763264f8b22Stobhe 			    (struct sockaddr *)&saflow->flow_dst.addr,
764264f8b22Stobhe 			    saflow->flow_dst.addr_mask, caddr))
765264f8b22Stobhe 				return (-1);
766264f8b22Stobhe 		} else {
767264f8b22Stobhe 			if (vroute_setdelroute(env, rdomain,
768264f8b22Stobhe 			    (struct sockaddr *)&saflow->flow_dst.addr,
769264f8b22Stobhe 			    saflow->flow_dst.addr_mask, caddr))
770264f8b22Stobhe 				return (-1);
771264f8b22Stobhe 		}
772264f8b22Stobhe 	}
773264f8b22Stobhe 
774264f8b22Stobhe 	return (0);
775264f8b22Stobhe }
776264f8b22Stobhe 
77745ae9d61Sreyk void
childsa_free(struct iked_childsa * csa)778bb108424Smarkus childsa_free(struct iked_childsa *csa)
77945ae9d61Sreyk {
7801136279cSmikeb 	struct iked_childsa *csb;
7811136279cSmikeb 
7828fdeade2Stobhe 	if (csa == NULL)
7838fdeade2Stobhe 		return;
7848fdeade2Stobhe 
785e7fee6f8Stobhe 	if (csa->csa_loaded)
78638194102Stobhe 		log_info("%s: CHILD SA spi %s is still loaded",
78738194102Stobhe 		    csa->csa_ikesa ? SPI_SA(csa->csa_ikesa, __func__) :
78838194102Stobhe 		    __func__,
78938194102Stobhe 		    print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size));
790e7fee6f8Stobhe 	if ((csb = csa->csa_bundled) != NULL)
791e7fee6f8Stobhe 		csb->csa_bundled = NULL;
7921136279cSmikeb 	if ((csb = csa->csa_peersa) != NULL)
7931136279cSmikeb 		csb->csa_peersa = NULL;
794be2b38f5Sclaudio 	ibuf_free(csa->csa_encrkey);
795be2b38f5Sclaudio 	ibuf_free(csa->csa_integrkey);
796bb108424Smarkus 	free(csa);
79745ae9d61Sreyk }
79845ae9d61Sreyk 
799b0eeedd0Smikeb struct iked_childsa *
childsa_lookup(struct iked_sa * sa,uint64_t spi,uint8_t protoid)800d09d3a7dSreyk childsa_lookup(struct iked_sa *sa, uint64_t spi, uint8_t protoid)
801b0eeedd0Smikeb {
802b0eeedd0Smikeb 	struct iked_childsa	*csa;
803b0eeedd0Smikeb 
804b0eeedd0Smikeb 	if (sa == NULL || spi == 0 || protoid == 0)
805b0eeedd0Smikeb 		return (NULL);
806b0eeedd0Smikeb 
807b0eeedd0Smikeb 	TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) {
808b0eeedd0Smikeb 		if (csa->csa_spi.spi_protoid == protoid &&
809b0eeedd0Smikeb 		    (csa->csa_spi.spi == spi))
810b0eeedd0Smikeb 			break;
811b0eeedd0Smikeb 	}
812b0eeedd0Smikeb 	return (csa);
813b0eeedd0Smikeb }
814b0eeedd0Smikeb 
81545ae9d61Sreyk void
flow_free(struct iked_flow * flow)81645ae9d61Sreyk flow_free(struct iked_flow *flow)
81745ae9d61Sreyk {
81845ae9d61Sreyk 	free(flow);
81945ae9d61Sreyk }
82045ae9d61Sreyk 
82145ae9d61Sreyk struct iked_sa *
sa_lookup(struct iked * env,uint64_t ispi,uint64_t rspi,unsigned int initiator)822d09d3a7dSreyk sa_lookup(struct iked *env, uint64_t ispi, uint64_t rspi,
823d09d3a7dSreyk     unsigned int initiator)
82445ae9d61Sreyk {
82545ae9d61Sreyk 	struct iked_sa	*sa, key;
82645ae9d61Sreyk 
82745ae9d61Sreyk 	key.sa_hdr.sh_ispi = ispi;
82845ae9d61Sreyk 	key.sa_hdr.sh_initiator = initiator;
82945ae9d61Sreyk 
830213d6658Sreyk 	if ((sa = RB_FIND(iked_sas, &env->sc_sas, &key)) != NULL) {
83145ae9d61Sreyk 		gettimeofday(&sa->sa_timeused, NULL);
832ae494144Sreyk 
833213d6658Sreyk 		/* Validate if SPIr matches */
834213d6658Sreyk 		if ((sa->sa_hdr.sh_rspi != 0) &&
835111eebb2Smarkus 		    (rspi != 0) &&
836213d6658Sreyk 		    (sa->sa_hdr.sh_rspi != rspi))
837213d6658Sreyk 			return (NULL);
838213d6658Sreyk 	}
839213d6658Sreyk 
84045ae9d61Sreyk 	return (sa);
84145ae9d61Sreyk }
84245ae9d61Sreyk 
84345ae9d61Sreyk static __inline int
sa_cmp(struct iked_sa * a,struct iked_sa * b)84445ae9d61Sreyk sa_cmp(struct iked_sa *a, struct iked_sa *b)
84545ae9d61Sreyk {
846c4eac93aSmarkus 	if (a->sa_hdr.sh_initiator > b->sa_hdr.sh_initiator)
847c4eac93aSmarkus 		return (-1);
848c4eac93aSmarkus 	if (a->sa_hdr.sh_initiator < b->sa_hdr.sh_initiator)
849c4eac93aSmarkus 		return (1);
85045ae9d61Sreyk 
85145ae9d61Sreyk 	if (a->sa_hdr.sh_ispi > b->sa_hdr.sh_ispi)
85245ae9d61Sreyk 		return (-1);
85345ae9d61Sreyk 	if (a->sa_hdr.sh_ispi < b->sa_hdr.sh_ispi)
85445ae9d61Sreyk 		return (1);
85545ae9d61Sreyk 
85645ae9d61Sreyk 	return (0);
85745ae9d61Sreyk }
85845ae9d61Sreyk 
859729f601bStobhe static struct iked_id *
sa_dstid_checked(struct iked_sa * sa)860729f601bStobhe sa_dstid_checked(struct iked_sa *sa)
861729f601bStobhe {
862729f601bStobhe 	struct iked_id *id;
863729f601bStobhe 
864729f601bStobhe 	id = IKESA_DSTID(sa);
865729f601bStobhe 	if (id == NULL || id->id_buf == NULL ||
866729f601bStobhe 	    ibuf_data(id->id_buf) == NULL)
867729f601bStobhe 		return (NULL);
868729f601bStobhe 	if (ibuf_size(id->id_buf) <= id->id_offset)
869729f601bStobhe 		return (NULL);
870729f601bStobhe 	return (id);
871729f601bStobhe }
872729f601bStobhe 
873729f601bStobhe struct iked_sa *
sa_dstid_lookup(struct iked * env,struct iked_sa * key)874729f601bStobhe sa_dstid_lookup(struct iked *env, struct iked_sa *key)
875729f601bStobhe {
876729f601bStobhe 	struct iked_sa *sa;
877729f601bStobhe 
878729f601bStobhe 	if (sa_dstid_checked(key) == NULL)
879729f601bStobhe 		fatalx("%s: no id for key %p", __func__, key);
880729f601bStobhe 	sa = RB_FIND(iked_dstid_sas, &env->sc_dstid_sas, key);
881729f601bStobhe 	if (sa != NULL && !sa->sa_dstid_entry_valid)
882729f601bStobhe 		fatalx("%s: sa %p not estab (key %p)", __func__, sa, key);
883729f601bStobhe 	return (sa);
884729f601bStobhe }
885729f601bStobhe 
886729f601bStobhe struct iked_sa *
sa_dstid_insert(struct iked * env,struct iked_sa * sa)887729f601bStobhe sa_dstid_insert(struct iked *env, struct iked_sa *sa)
888729f601bStobhe {
889729f601bStobhe 	struct iked_sa *osa;
890729f601bStobhe 
891729f601bStobhe 	if (sa->sa_dstid_entry_valid)
892729f601bStobhe 		fatalx("%s: sa %p is estab", __func__, sa);
893729f601bStobhe 	if (sa_dstid_checked(sa) == NULL)
894729f601bStobhe 		fatalx("%s: no id for sa %p", __func__, sa);
895729f601bStobhe 	osa = RB_FIND(iked_dstid_sas, &env->sc_dstid_sas, sa);
896729f601bStobhe 	if (osa == NULL) {
897729f601bStobhe 		osa = RB_INSERT(iked_dstid_sas, &env->sc_dstid_sas, sa);
898729f601bStobhe 		if (osa && osa != sa) {
899729f601bStobhe 			log_warnx("%s: duplicate IKE SA", SPI_SA(sa, __func__));
900729f601bStobhe 			return (osa);
901729f601bStobhe 		}
902729f601bStobhe 		sa->sa_dstid_entry_valid = 1;
903729f601bStobhe 		return (NULL);
904729f601bStobhe 	}
905729f601bStobhe 	if (!osa->sa_dstid_entry_valid)
906729f601bStobhe 		fatalx("%s: osa %p not estab (sa %p)", __func__, osa, sa);
907729f601bStobhe 	return (osa);
908729f601bStobhe }
909729f601bStobhe 
910729f601bStobhe void
sa_dstid_remove(struct iked * env,struct iked_sa * sa)911729f601bStobhe sa_dstid_remove(struct iked *env, struct iked_sa *sa)
912729f601bStobhe {
913729f601bStobhe 	if (!sa->sa_dstid_entry_valid)
914729f601bStobhe 		fatalx("%s: sa %p is not estab", __func__, sa);
915729f601bStobhe 	if (sa_dstid_checked(sa) == NULL)
916729f601bStobhe 		fatalx("%s: no id for sa %p", __func__, sa);
917729f601bStobhe 	RB_REMOVE(iked_dstid_sas, &env->sc_dstid_sas, sa);
918729f601bStobhe 	sa->sa_dstid_entry_valid = 0;
919729f601bStobhe }
920729f601bStobhe 
921729f601bStobhe static __inline int
sa_dstid_cmp(struct iked_sa * a,struct iked_sa * b)922729f601bStobhe sa_dstid_cmp(struct iked_sa *a, struct iked_sa *b)
923729f601bStobhe {
924729f601bStobhe 	struct iked_id		*aid = NULL, *bid = NULL;
925729f601bStobhe 	size_t			 alen, blen;
926729f601bStobhe 	uint8_t			*aptr, *bptr;
927729f601bStobhe 
928729f601bStobhe 	aid = sa_dstid_checked(a);
929729f601bStobhe 	bid = sa_dstid_checked(b);
930729f601bStobhe 	if (aid == NULL || bid == NULL)
931729f601bStobhe 		fatalx("corrupt IDs");
932729f601bStobhe 	if (aid->id_type > bid->id_type)
933729f601bStobhe 		return (-1);
934729f601bStobhe 	else if (aid->id_type < bid->id_type)
935729f601bStobhe 		return (1);
936729f601bStobhe 	alen = ibuf_size(aid->id_buf);
937729f601bStobhe 	blen = ibuf_size(bid->id_buf);
938729f601bStobhe 	aptr = ibuf_data(aid->id_buf);
939729f601bStobhe 	bptr = ibuf_data(bid->id_buf);
940729f601bStobhe 	if (aptr == NULL || bptr == NULL)
941729f601bStobhe 		fatalx("corrupt ID bufs");
942729f601bStobhe 	if (alen <= aid->id_offset || blen <= bid->id_offset)
943729f601bStobhe 		fatalx("corrupt ID lens");
944729f601bStobhe 	aptr += aid->id_offset;
945729f601bStobhe 	alen -= aid->id_offset;
946729f601bStobhe 	bptr += bid->id_offset;
947729f601bStobhe 	blen -= bid->id_offset;
948729f601bStobhe 	if (alen > blen)
949729f601bStobhe 		return (-1);
950729f601bStobhe 	if (alen < blen)
951729f601bStobhe 		return (1);
952729f601bStobhe 	return (memcmp(aptr, bptr, alen));
953729f601bStobhe }
954729f601bStobhe 
95543be1c05Smarkus static __inline int
sa_addrpool_cmp(struct iked_sa * a,struct iked_sa * b)95643be1c05Smarkus sa_addrpool_cmp(struct iked_sa *a, struct iked_sa *b)
95743be1c05Smarkus {
95843be1c05Smarkus 	return (sockaddr_cmp((struct sockaddr *)&a->sa_addrpool->addr,
95943be1c05Smarkus 	    (struct sockaddr *)&b->sa_addrpool->addr, -1));
96043be1c05Smarkus }
96143be1c05Smarkus 
96219dc8638Spatrick static __inline int
sa_addrpool6_cmp(struct iked_sa * a,struct iked_sa * b)96319dc8638Spatrick sa_addrpool6_cmp(struct iked_sa *a, struct iked_sa *b)
96419dc8638Spatrick {
96519dc8638Spatrick 	return (sockaddr_cmp((struct sockaddr *)&a->sa_addrpool6->addr,
96619dc8638Spatrick 	    (struct sockaddr *)&b->sa_addrpool6->addr, -1));
96719dc8638Spatrick }
96819dc8638Spatrick 
96945ae9d61Sreyk struct iked_user *
user_lookup(struct iked * env,const char * user)97045ae9d61Sreyk user_lookup(struct iked *env, const char *user)
97145ae9d61Sreyk {
97245ae9d61Sreyk 	struct iked_user	 key;
97345ae9d61Sreyk 
97445ae9d61Sreyk 	if (strlcpy(key.usr_name, user,
97545ae9d61Sreyk 	    sizeof(key.usr_name)) >= sizeof(key.usr_name))
97645ae9d61Sreyk 		return (NULL);
97745ae9d61Sreyk 
97845ae9d61Sreyk 	return (RB_FIND(iked_users, &env->sc_users, &key));
97945ae9d61Sreyk }
98045ae9d61Sreyk 
98145ae9d61Sreyk static __inline int
user_cmp(struct iked_user * a,struct iked_user * b)98245ae9d61Sreyk user_cmp(struct iked_user *a, struct iked_user *b)
98345ae9d61Sreyk {
98445ae9d61Sreyk 	return (strcmp(a->usr_name, b->usr_name));
98545ae9d61Sreyk }
98645ae9d61Sreyk 
987b0435d4dStobhe /*
988b0435d4dStobhe  * Find a matching subset of the proposal lists 'local' and 'peer'.
989b0435d4dStobhe  * The resulting proposal is stored in 'result' if 'result' is not NULL.
990b0435d4dStobhe  * The 'rekey' parameter indicates a CREATE_CHILD_SA exchange where
991b0435d4dStobhe  * an extra group is necessary for PFS. For the initial IKE_AUTH exchange
992b0435d4dStobhe  * the ESP SA proposal never includes an explicit DH group.
993b0435d4dStobhe  *
994b0435d4dStobhe  * Return 0 if a matching subset was found and -1 if no subset was found
995b0435d4dStobhe  * or an error occured.
996b0435d4dStobhe  */
997b0435d4dStobhe int
proposals_negotiate(struct iked_proposals * result,struct iked_proposals * local,struct iked_proposals * peer,int rekey,int groupid)998b0435d4dStobhe proposals_negotiate(struct iked_proposals *result, struct iked_proposals *local,
9994ad5fa93Stobhe     struct iked_proposals *peer, int rekey, int groupid)
1000b0435d4dStobhe {
1001b0435d4dStobhe 	struct iked_proposal	*ppeer = NULL, *plocal, *prop, vpeer, vlocal;
1002b0435d4dStobhe 	struct iked_transform	 chosen[IKEV2_XFORMTYPE_MAX];
1003b0435d4dStobhe 	struct iked_transform	*valid[IKEV2_XFORMTYPE_MAX];
1004b0435d4dStobhe 	struct iked_transform	*match[IKEV2_XFORMTYPE_MAX];
1005b0435d4dStobhe 	unsigned int		 i, score, chosen_score = 0;
1006b0435d4dStobhe 	uint8_t			 protoid = 0;
1007b0435d4dStobhe 
1008b0435d4dStobhe 	bzero(valid, sizeof(valid));
1009b0435d4dStobhe 	bzero(&vlocal, sizeof(vlocal));
1010b0435d4dStobhe 	bzero(&vpeer, sizeof(vpeer));
1011b0435d4dStobhe 
1012b0435d4dStobhe 	if (TAILQ_EMPTY(peer)) {
1013b0435d4dStobhe 		log_debug("%s: peer did not send %s proposals", __func__,
1014b0435d4dStobhe 		    print_map(protoid, ikev2_saproto_map));
1015b0435d4dStobhe 		return (-1);
1016b0435d4dStobhe 	}
1017b0435d4dStobhe 
1018b0435d4dStobhe 	TAILQ_FOREACH(plocal, local, prop_entry) {
1019b0435d4dStobhe 		TAILQ_FOREACH(ppeer, peer, prop_entry) {
1020b0435d4dStobhe 			if (ppeer->prop_protoid != plocal->prop_protoid)
1021b0435d4dStobhe 				continue;
1022b0435d4dStobhe 			bzero(match, sizeof(match));
1023b0435d4dStobhe 			score = proposals_match(plocal, ppeer, match,
10244ad5fa93Stobhe 			    rekey, groupid);
1025b0435d4dStobhe 			log_debug("%s: score %d", __func__, score);
1026b0435d4dStobhe 			if (score && (!chosen_score || score < chosen_score)) {
1027b0435d4dStobhe 				chosen_score = score;
1028b0435d4dStobhe 				for (i = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
1029b0435d4dStobhe 					if ((valid[i] = match[i]))
1030b0435d4dStobhe 						memcpy(&chosen[i], match[i],
1031b0435d4dStobhe 						    sizeof(chosen[0]));
1032b0435d4dStobhe 				}
1033b0435d4dStobhe 				memcpy(&vpeer, ppeer, sizeof(vpeer));
1034b0435d4dStobhe 				memcpy(&vlocal, plocal, sizeof(vlocal));
1035b0435d4dStobhe 			}
1036b0435d4dStobhe 		}
1037b0435d4dStobhe 		if (chosen_score != 0)
1038b0435d4dStobhe 			break;
1039b0435d4dStobhe 	}
1040b0435d4dStobhe 
1041b0435d4dStobhe 	if (chosen_score == 0)
1042b0435d4dStobhe 		return (-1);
1043b0435d4dStobhe 	else if (result == NULL)
1044b0435d4dStobhe 		return (0);
1045b0435d4dStobhe 
1046b0435d4dStobhe 	(void)config_free_proposals(result, vpeer.prop_protoid);
1047b0435d4dStobhe 	prop = config_add_proposal(result, vpeer.prop_id, vpeer.prop_protoid);
1048b0435d4dStobhe 
1049b0435d4dStobhe 	if (vpeer.prop_localspi.spi_size) {
1050b0435d4dStobhe 		prop->prop_localspi.spi_size = vpeer.prop_localspi.spi_size;
1051b0435d4dStobhe 		prop->prop_peerspi = vpeer.prop_peerspi;
1052b0435d4dStobhe 	}
1053b0435d4dStobhe 	if (vlocal.prop_localspi.spi_size) {
1054b0435d4dStobhe 		prop->prop_localspi.spi_size = vlocal.prop_localspi.spi_size;
1055b0435d4dStobhe 		prop->prop_localspi.spi = vlocal.prop_localspi.spi;
1056b0435d4dStobhe 	}
1057b0435d4dStobhe 
1058b0435d4dStobhe 	for (i = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
1059b0435d4dStobhe 		if (valid[i] == NULL)
1060b0435d4dStobhe 			continue;
1061b0435d4dStobhe 		print_debug("%s: score %d: %s %s", __func__,
1062b0435d4dStobhe 		    chosen[i].xform_score, print_map(i, ikev2_xformtype_map),
1063b0435d4dStobhe 		    print_map(chosen[i].xform_id, chosen[i].xform_map));
1064b0435d4dStobhe 		if (chosen[i].xform_length)
1065b0435d4dStobhe 			print_debug(" %d", chosen[i].xform_length);
1066b0435d4dStobhe 		print_debug("\n");
1067b0435d4dStobhe 
1068b0435d4dStobhe 		if (config_add_transform(prop, chosen[i].xform_type,
1069b0435d4dStobhe 		    chosen[i].xform_id, chosen[i].xform_length,
1070822b336dStobhe 		    chosen[i].xform_keylength) != 0)
1071b0435d4dStobhe 			break;
1072b0435d4dStobhe 	}
1073b0435d4dStobhe 
1074b0435d4dStobhe 	return (0);
1075b0435d4dStobhe }
1076b0435d4dStobhe 
1077b0435d4dStobhe static int
proposals_match(struct iked_proposal * local,struct iked_proposal * peer,struct iked_transform ** xforms,int rekey,int dhgroup)1078b0435d4dStobhe proposals_match(struct iked_proposal *local, struct iked_proposal *peer,
10794ad5fa93Stobhe     struct iked_transform **xforms, int rekey, int dhgroup)
1080b0435d4dStobhe {
1081b0435d4dStobhe 	struct iked_transform	*tpeer, *tlocal;
1082cca72260Stobhe 	unsigned int		 i, j, type, score, requiredh = 0, nodh = 0, noauth = 0;
10834ad5fa93Stobhe 	unsigned int		 dhforced = 0;
1084b0435d4dStobhe 	uint8_t			 protoid = peer->prop_protoid;
1085b0435d4dStobhe 	uint8_t			 peerxfs[IKEV2_XFORMTYPE_MAX];
1086b0435d4dStobhe 
1087b0435d4dStobhe 	bzero(peerxfs, sizeof(peerxfs));
1088b0435d4dStobhe 
1089b0435d4dStobhe 	for (i = 0; i < peer->prop_nxforms; i++) {
1090b0435d4dStobhe 		tpeer = peer->prop_xforms + i;
109115863c3aStobhe 		/* If any of the ENC transforms is an AEAD, ignore auth */
109215863c3aStobhe 		if (tpeer->xform_type == IKEV2_XFORMTYPE_ENCR &&
109315863c3aStobhe 		    encxf_noauth(tpeer->xform_id))
109415863c3aStobhe 			noauth = 1;
109515863c3aStobhe 	}
109615863c3aStobhe 
109715863c3aStobhe 	for (i = 0; i < peer->prop_nxforms; i++) {
109815863c3aStobhe 		tpeer = peer->prop_xforms + i;
1099aea1f3b7Sjsg 		if (tpeer->xform_type >= IKEV2_XFORMTYPE_MAX)
1100b0435d4dStobhe 			continue;
110115863c3aStobhe 		if (noauth && tpeer->xform_type == IKEV2_XFORMTYPE_INTEGR)
110215863c3aStobhe 			return (0);
1103b0435d4dStobhe 
1104b0435d4dStobhe 		/*
1105b0435d4dStobhe 		 * Record all transform types from the peer's proposal,
1106b0435d4dStobhe 		 * because if we want this proposal we have to select
1107b0435d4dStobhe 		 * a transform for each proposed transform type.
1108b0435d4dStobhe 		 */
1109b0435d4dStobhe 		peerxfs[tpeer->xform_type] = 1;
1110b0435d4dStobhe 
1111b0435d4dStobhe 		for (j = 0; j < local->prop_nxforms; j++) {
1112b0435d4dStobhe 			tlocal = local->prop_xforms + j;
1113b0435d4dStobhe 
1114b0435d4dStobhe 			/*
1115b0435d4dStobhe 			 * We require a DH group for ESP if there is any
1116b0435d4dStobhe 			 * local proposal with DH enabled.
1117b0435d4dStobhe 			 */
1118b0435d4dStobhe 			if (rekey && requiredh == 0 &&
1119b0435d4dStobhe 			    protoid == IKEV2_SAPROTO_ESP &&
1120a60f6266Stobhe 			    tlocal->xform_type == IKEV2_XFORMTYPE_DH &&
1121a60f6266Stobhe 			    tlocal->xform_id != IKEV2_XFORMDH_NONE)
1122b0435d4dStobhe 				requiredh = 1;
1123b0435d4dStobhe 
1124cca72260Stobhe 			/*
1125cca72260Stobhe 			 * If none is an explicit option, don't require
1126cca72260Stobhe 			 * DH group. Overrides requiredh = 1.
1127cca72260Stobhe 			 */
1128cca72260Stobhe 			if (rekey && nodh == 0 &&
1129cca72260Stobhe 			    protoid == IKEV2_SAPROTO_ESP &&
1130cca72260Stobhe 			    tlocal->xform_type == IKEV2_XFORMTYPE_DH &&
1131cca72260Stobhe 			    tlocal->xform_id == IKEV2_XFORMDH_NONE)
1132cca72260Stobhe 				nodh = 1;
1133cca72260Stobhe 
1134b0435d4dStobhe 			/* Compare peer and local proposals */
1135b0435d4dStobhe 			if (tpeer->xform_type != tlocal->xform_type ||
1136b0435d4dStobhe 			    tpeer->xform_id != tlocal->xform_id ||
1137b0435d4dStobhe 			    tpeer->xform_length != tlocal->xform_length)
1138b0435d4dStobhe 				continue;
1139b0435d4dStobhe 			type = tpeer->xform_type;
1140b0435d4dStobhe 
1141203153b6Stobhe 			if (nodh == 0 && dhgroup >= 0 &&
11424ad5fa93Stobhe 			    type == IKEV2_XFORMTYPE_DH) {
11434ad5fa93Stobhe 				if (dhforced)
11444ad5fa93Stobhe 					continue;
11454ad5fa93Stobhe 				/* reset xform, so this xform w/matching group is enforced */
11464ad5fa93Stobhe 				if (tlocal->xform_id == dhgroup) {
11474ad5fa93Stobhe 					xforms[type] = NULL;
11484ad5fa93Stobhe 					dhforced = 1;
11494ad5fa93Stobhe 				}
11504ad5fa93Stobhe 			}
11514ad5fa93Stobhe 
1152b0435d4dStobhe 			if (xforms[type] == NULL || tlocal->xform_score <
1153b0435d4dStobhe 			    xforms[type]->xform_score) {
1154b0435d4dStobhe 				xforms[type] = tlocal;
1155b0435d4dStobhe 			} else
1156b0435d4dStobhe 				continue;
1157b0435d4dStobhe 
1158b0435d4dStobhe 			print_debug("%s: xform %d <-> %d (%d): %s %s "
1159b0435d4dStobhe 			    "(keylength %d <-> %d)", __func__,
1160b0435d4dStobhe 			    peer->prop_id, local->prop_id, tlocal->xform_score,
1161b0435d4dStobhe 			    print_map(type, ikev2_xformtype_map),
1162b0435d4dStobhe 			    print_map(tpeer->xform_id, tpeer->xform_map),
1163b0435d4dStobhe 			    tpeer->xform_keylength, tlocal->xform_keylength);
1164b0435d4dStobhe 			if (tpeer->xform_length)
1165b0435d4dStobhe 				print_debug(" %d", tpeer->xform_length);
1166b0435d4dStobhe 			print_debug("\n");
1167b0435d4dStobhe 		}
1168b0435d4dStobhe 	}
1169b0435d4dStobhe 
1170b0435d4dStobhe 	for (i = score = 0; i < IKEV2_XFORMTYPE_MAX; i++) {
1171b0435d4dStobhe 		if (protoid == IKEV2_SAPROTO_IKE && xforms[i] == NULL &&
1172b0435d4dStobhe 		    (i == IKEV2_XFORMTYPE_ENCR || i == IKEV2_XFORMTYPE_PRF ||
117315863c3aStobhe 		    (!noauth && i == IKEV2_XFORMTYPE_INTEGR) ||
117415863c3aStobhe 		    i == IKEV2_XFORMTYPE_DH)) {
1175b0435d4dStobhe 			score = 0;
1176b0435d4dStobhe 			break;
1177b0435d4dStobhe 		} else if (protoid == IKEV2_SAPROTO_AH && xforms[i] == NULL &&
1178b0435d4dStobhe 		    (i == IKEV2_XFORMTYPE_INTEGR || i == IKEV2_XFORMTYPE_ESN)) {
1179b0435d4dStobhe 			score = 0;
1180b0435d4dStobhe 			break;
1181b0435d4dStobhe 		} else if (protoid == IKEV2_SAPROTO_ESP && xforms[i] == NULL &&
1182b0435d4dStobhe 		    (i == IKEV2_XFORMTYPE_ENCR || i == IKEV2_XFORMTYPE_ESN ||
1183cca72260Stobhe 		    (requiredh && !nodh && i == IKEV2_XFORMTYPE_DH))) {
1184b0435d4dStobhe 			score = 0;
1185b0435d4dStobhe 			break;
1186b0435d4dStobhe 		} else if (peerxfs[i] && xforms[i] == NULL) {
1187b0435d4dStobhe 			score = 0;
1188b0435d4dStobhe 			break;
1189b0435d4dStobhe 		} else if (xforms[i] == NULL)
1190b0435d4dStobhe 			continue;
1191b0435d4dStobhe 
1192b0435d4dStobhe 		score += xforms[i]->xform_score;
1193b0435d4dStobhe 	}
1194b0435d4dStobhe 
1195b0435d4dStobhe 	return (score);
1196b0435d4dStobhe }
1197b0435d4dStobhe 
1198b0eeedd0Smikeb static __inline int
childsa_cmp(struct iked_childsa * a,struct iked_childsa * b)1199b0eeedd0Smikeb childsa_cmp(struct iked_childsa *a, struct iked_childsa *b)
1200b0eeedd0Smikeb {
1201b0eeedd0Smikeb 	if (a->csa_spi.spi > b->csa_spi.spi)
1202b0eeedd0Smikeb 		return (1);
1203b0eeedd0Smikeb 	if (a->csa_spi.spi < b->csa_spi.spi)
1204b0eeedd0Smikeb 		return (-1);
1205b0eeedd0Smikeb 	return (0);
1206b0eeedd0Smikeb }
1207b0eeedd0Smikeb 
12080927abadSmikeb static __inline int
addr_cmp(struct iked_addr * a,struct iked_addr * b,int useports)12090927abadSmikeb addr_cmp(struct iked_addr *a, struct iked_addr *b, int useports)
12100927abadSmikeb {
12110927abadSmikeb 	int		diff = 0;
12120927abadSmikeb 
12130927abadSmikeb 	diff = sockaddr_cmp((struct sockaddr *)&a->addr,
12149165f062Smikeb 	    (struct sockaddr *)&b->addr, 128);
12159165f062Smikeb 	if (!diff)
12169165f062Smikeb 		diff = (int)a->addr_mask - (int)b->addr_mask;
12170927abadSmikeb 	if (!diff && useports)
12180927abadSmikeb 		diff = a->addr_port - b->addr_port;
12190927abadSmikeb 
12200927abadSmikeb 	return (diff);
12210927abadSmikeb }
12220927abadSmikeb 
12230927abadSmikeb static __inline int
flow_cmp(struct iked_flow * a,struct iked_flow * b)12240927abadSmikeb flow_cmp(struct iked_flow *a, struct iked_flow *b)
12250927abadSmikeb {
12260927abadSmikeb 	int		diff = 0;
12270927abadSmikeb 
12282d5ff022Spatrick 	if (!diff)
12290347364bStobhe 		diff = a->flow_rdomain - b->flow_rdomain;
12300347364bStobhe 	if (!diff)
12312d5ff022Spatrick 		diff = (int)a->flow_ipproto - (int)b->flow_ipproto;
12322d5ff022Spatrick 	if (!diff)
12332d5ff022Spatrick 		diff = (int)a->flow_saproto - (int)b->flow_saproto;
12342d5ff022Spatrick 	if (!diff)
12352d5ff022Spatrick 		diff = (int)a->flow_dir - (int)b->flow_dir;
12360927abadSmikeb 	if (!diff)
12370927abadSmikeb 		diff = addr_cmp(&a->flow_dst, &b->flow_dst, 1);
12380927abadSmikeb 	if (!diff)
12390927abadSmikeb 		diff = addr_cmp(&a->flow_src, &b->flow_src, 1);
12405c4cedf2Stobhe 	if (!diff)
12415c4cedf2Stobhe 		diff = addr_cmp(&a->flow_prenat, &b->flow_prenat, 0);
12420927abadSmikeb 
12430927abadSmikeb 	return (diff);
12440927abadSmikeb }
12450927abadSmikeb 
12462d5ff022Spatrick int
flow_equal(struct iked_flow * a,struct iked_flow * b)12472d5ff022Spatrick flow_equal(struct iked_flow *a, struct iked_flow *b)
12482d5ff022Spatrick {
12492d5ff022Spatrick 	return (flow_cmp(a, b) == 0);
12502d5ff022Spatrick }
12512d5ff022Spatrick 
1252ed6203c6Sreyk RB_GENERATE(iked_sas, iked_sa, sa_entry, sa_cmp);
1253729f601bStobhe RB_GENERATE(iked_dstid_sas, iked_sa, sa_dstid_entry, sa_dstid_cmp);
125443be1c05Smarkus RB_GENERATE(iked_addrpool, iked_sa, sa_addrpool_entry, sa_addrpool_cmp);
125519dc8638Spatrick RB_GENERATE(iked_addrpool6, iked_sa, sa_addrpool6_entry, sa_addrpool6_cmp);
1256ed6203c6Sreyk RB_GENERATE(iked_users, iked_user, usr_entry, user_cmp);
1257ed6203c6Sreyk RB_GENERATE(iked_activesas, iked_childsa, csa_node, childsa_cmp);
12589dbd37b8Sreyk RB_GENERATE(iked_flows, iked_flow, flow_node, flow_cmp);
1259