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