Index: sys/netipsec/key.c =================================================================== --- sys/netipsec/key.c (wersja 202622) +++ sys/netipsec/key.c (kopia robocza) @@ -375,6 +375,8 @@ static struct secasvar *key_do_allocsa_policy __P((struct secashead *, u_int)); static void key_delsp __P((struct secpolicy *)); static struct secpolicy *key_getsp __P((struct secpolicyindex *)); +static struct secpolicy *key_getsp2 __P((struct secpolicyindex *spidx, + struct secpolicy *csp)); static void _key_delsp(struct secpolicy *sp); static struct secpolicy *key_getspbyid __P((u_int32_t)); static u_int32_t key_newreqid __P((void)); @@ -560,7 +562,9 @@ struct secpolicy * key_allocsp(struct secpolicyindex *spidx, u_int dir, const char* where, int tag) { - struct secpolicy *sp; +#define MAX_SPS 16 + struct secpolicy *sp, *sps[MAX_SPS]; + u_int nsps; IPSEC_ASSERT(spidx != NULL, ("null spidx")); IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, @@ -574,6 +578,8 @@ printf("*** objects\n"); kdebug_secpolicyindex(spidx)); + nsps = 0; + SPTREE_LOCK(); LIST_FOREACH(sp, &sptree[dir], chain) { KEYDEBUG(KEYDEBUG_IPSEC_DATA, @@ -582,11 +588,44 @@ if (sp->state == IPSEC_SPSTATE_DEAD) continue; - if (key_cmpspidx_withmask(&sp->spidx, spidx)) - goto found; + if (key_cmpspidx_withmask(&sp->spidx, spidx)) { + sps[nsps++] = sp; + if (nsps >= MAX_SPS) + break; + } } - sp = NULL; -found: + if (nsps == 0) + sp = NULL; + else if (nsps == 1) + sp = sps[0]; + else if (nsps > 1) { + uint32_t ip; + uint16_t port; + u_char hash; + int ii, jj; + + /* + * We build hash based on source and destination IP and ports. + * It is important that we generate the same hash for both + * directions. + */ + hash = 0; + ip = (uint32_t)spidx->src.sin.sin_addr.s_addr; + port = (uint16_t)spidx->src.sin.sin_port; + for (ii = 0; ii <= 1; ii++) { + for (jj = 0; jj <= 3; jj++) { + hash += (ip & 0xff); + ip >>= 8; + } + for (jj = 0; jj <= 1; jj++) { + hash += (port & 0xff); + port >>= 8; + } + ip = (uint32_t)spidx->dst.sin.sin_addr.s_addr; + port = (uint16_t)spidx->dst.sin.sin_port; + } + sp = sps[hash % nsps]; + } if (sp) { /* sanity check */ KEY_CHKSPDIR(sp->spidx.dir, dir, __func__); @@ -1276,6 +1315,36 @@ return sp; } +static struct secpolicy * +key_getsp2(struct secpolicyindex *spidx, struct secpolicy *csp) +{ + struct secpolicy *sp; + + IPSEC_ASSERT(spidx != NULL, ("null spidx")); + IPSEC_ASSERT(csp != NULL, ("null csp")); + + SPTREE_LOCK(); + LIST_FOREACH(sp, &sptree[spidx->dir], chain) { + if (sp->state == IPSEC_SPSTATE_DEAD) + continue; + if (!key_cmpspidx_exactly(spidx, &sp->spidx)) + continue; + if (sp->req == NULL || csp->req == NULL) + continue; + if (key_sockaddrcmp(&sp->req->saidx.src.sa, + &csp->req->saidx.src.sa, 0) != 0) + continue; + if (key_sockaddrcmp(&sp->req->saidx.dst.sa, + &csp->req->saidx.dst.sa, 0) != 0) + continue; + SP_ADDREF(sp); + break; + } + SPTREE_UNLOCK(); + + return sp; +} + /* * get SP by index. * OUT: NULL : not found @@ -1748,7 +1817,7 @@ struct sadb_x_policy *xpl0, *xpl; struct sadb_lifetime *lft = NULL; struct secpolicyindex spidx; - struct secpolicy *newsp; + struct secpolicy *newsp, *chksp; int error; IPSEC_ASSERT(so != NULL, ("null socket")); @@ -1821,32 +1890,31 @@ return key_senderror(so, m, EINVAL); } + /* allocation new SP entry */ + newsp = key_msg2sp(xpl0, PFKEY_EXTLEN(xpl0), &error); + if (newsp == NULL) + return key_senderror(so, m, error); + /* * checking there is SP already or not. * SPDUPDATE doesn't depend on whether there is a SP or not. * If the type is either SPDADD or SPDSETIDX AND a SP is found, * then error. */ - newsp = key_getsp(&spidx); + chksp = key_getsp2(&spidx, newsp); if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) { - if (newsp) { - newsp->state = IPSEC_SPSTATE_DEAD; - KEY_FREESP(&newsp); + if (chksp != NULL) { + chksp->state = IPSEC_SPSTATE_DEAD; + KEY_FREESP(&chksp); } - } else { - if (newsp != NULL) { - KEY_FREESP(&newsp); - ipseclog((LOG_DEBUG, "%s: a SP entry exists already.\n", - __func__)); - return key_senderror(so, m, EEXIST); - } + } else if (chksp != NULL) { + _key_delsp(newsp); + KEY_FREESP(&chksp); + ipseclog((LOG_DEBUG, "%s: a SP entry exists already.\n", + __func__)); + return key_senderror(so, m, EEXIST); } - /* allocation new SP entry */ - if ((newsp = key_msg2sp(xpl0, PFKEY_EXTLEN(xpl0), &error)) == NULL) { - return key_senderror(so, m, error); - } - if ((newsp->id = key_getnewspid()) == 0) { _key_delsp(newsp); return key_senderror(so, m, ENOBUFS);