Projet

Général

Profil

Télécharger (17,6 ko) Statistiques
| Branche: | Révision:

univnautes-tools / patches / stable / 10 / ipsec_improvement.diff @ 4ab3b90b

1
diff --git a/sys/netinet/ip_ipsec.c b/sys/netinet/ip_ipsec.c
2
index 66dc91f..b449f52 100644
3
--- a/sys/netinet/ip_ipsec.c
4
+++ b/sys/netinet/ip_ipsec.c
5
@@ -114,19 +114,11 @@ int
6
 ip_ipsec_fwd(struct mbuf *m)
7
 {
8
 #ifdef IPSEC
9
-	struct m_tag *mtag;
10
-	struct tdb_ident *tdbi;
11
 	struct secpolicy *sp;
12
 	int error;
13
 
14
-	mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
15
-	if (mtag != NULL) {
16
-		tdbi = (struct tdb_ident *)(mtag + 1);
17
-		sp = ipsec_getpolicy(tdbi, IPSEC_DIR_INBOUND);
18
-	} else {
19
-		sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND,
20
-					   IP_FORWARDING, &error);   
21
-	}
22
+	sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND,
23
+				   IP_FORWARDING, &error);   
24
 	if (sp == NULL) {	/* NB: can happen if error */
25
 		/*XXX error stat???*/
26
 		DPRINTF(("ip_input: no SP for forwarding\n"));	/*XXX*/
27
@@ -158,8 +150,6 @@ ip_ipsec_input(struct mbuf *m)
28
 {
29
 #ifdef IPSEC
30
 	struct ip *ip = mtod(m, struct ip *);
31
-	struct m_tag *mtag;
32
-	struct tdb_ident *tdbi;
33
 	struct secpolicy *sp;
34
 	int error;
35
 	/*
36
@@ -174,14 +164,8 @@ ip_ipsec_input(struct mbuf *m)
37
 		 * set during AH, ESP, etc. input handling, before the
38
 		 * packet is returned to the ip input queue for delivery.
39
 		 */ 
40
-		mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_DONE, NULL);
41
-		if (mtag != NULL) {
42
-			tdbi = (struct tdb_ident *)(mtag + 1);
43
-			sp = ipsec_getpolicy(tdbi, IPSEC_DIR_INBOUND);
44
-		} else {
45
-			sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND,
46
-						   IP_FORWARDING, &error);   
47
-		}
48
+		sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND,
49
+					   IP_FORWARDING, &error);   
50
 		if (sp != NULL) {
51
 			/*
52
 			 * Check security policy against packet attributes.
53
diff --git a/sys/netipsec/key.c b/sys/netipsec/key.c
54
index 40a398d..6a4c127 100644
55
--- a/sys/netipsec/key.c
56
+++ b/sys/netipsec/key.c
57
@@ -140,6 +140,18 @@ static VNET_DEFINE(int, key_preferred_oldsa) = 1;
58
 static VNET_DEFINE(u_int32_t, acq_seq) = 0;
59
 #define	V_acq_seq		VNET(acq_seq)
60
 
61
+								/* SPD cache */
62
+struct secpolicycache {
63
+	struct secpolicy *policy;
64
+	struct secpolicyindex spidx;
65
+	u_int32_t genid;
66
+};
67
+#define SPCACHESIZE	8192
68
+static VNET_DEFINE(struct secpolicycache, spcache[2][SPCACHESIZE]);
69
+#define	V_spcache		VNET(spcache)
70
+static VNET_DEFINE(u_int32_t, spcache_genid) = 0;
71
+#define	V_spcache_genid		VNET(spcache_genid)
72
+
73
 								/* SPD */
74
 static VNET_DEFINE(LIST_HEAD(_sptree, secpolicy), sptree[IPSEC_DIR_MAX]);
75
 #define	V_sptree		VNET(sptree)
76
@@ -152,8 +164,13 @@ static struct mtx sptree_lock;
77
 #define	SPTREE_UNLOCK()	mtx_unlock(&sptree_lock)
78
 #define	SPTREE_LOCK_ASSERT()	mtx_assert(&sptree_lock, MA_OWNED)
79
 
80
+#define SPIHASHSIZE	1024
81
+#define	SPIHASH(x)	(((x) + ((x) >> 16)) % SPIHASHSIZE)
82
+
83
 static VNET_DEFINE(LIST_HEAD(_sahtree, secashead), sahtree);	/* SAD */
84
 #define	V_sahtree		VNET(sahtree)
85
+static VNET_DEFINE(LIST_HEAD(_spihash, secasvar), spihash[SPIHASHSIZE]);
86
+#define	V_spihash		VNET(spihash)
87
 static struct mtx sahtree_lock;
88
 #define	SAHTREE_LOCK_INIT() \
89
 	mtx_init(&sahtree_lock, "sahtree", \
90
@@ -330,6 +347,8 @@ SYSCTL_VNET_INT(_net_key, KEYCTL_PREFERED_OLDSA,
91
 
92
 #define __LIST_CHAINED(elm) \
93
 	(!((elm)->chain.le_next == NULL && (elm)->chain.le_prev == NULL))
94
+#define __LIST_SPIHASHED(elm) \
95
+	(!((elm)->spihash.le_next == NULL && (elm)->spihash.le_prev == NULL))
96
 #define LIST_INSERT_TAIL(head, elm, type, field) \
97
 do {\
98
 	struct type *curelm = LIST_FIRST(head); \
99
@@ -438,11 +457,11 @@ static u_int key_getspreqmsglen __P((struct secpolicy *));
100
 static int key_spdexpire __P((struct secpolicy *));
101
 static struct secashead *key_newsah __P((struct secasindex *));
102
 static void key_delsah __P((struct secashead *));
103
-static struct secasvar *key_newsav __P((struct mbuf *,
104
+static struct secasvar *key_newsav __P((u_int32_t, struct mbuf *,
105
 	const struct sadb_msghdr *, struct secashead *, int *,
106
 	const char*, int));
107
-#define	KEY_NEWSAV(m, sadb, sah, e)				\
108
-	key_newsav(m, sadb, sah, e, __FILE__, __LINE__)
109
+#define	KEY_NEWSAV(spi, m, sadb, sah, e)			\
110
+	key_newsav(spi, m, sadb, sah, e, __FILE__, __LINE__)
111
 static void key_delsav __P((struct secasvar *));
112
 static struct secashead *key_getsah __P((struct secasindex *));
113
 static struct secasvar *key_checkspidup __P((struct secasindex *, u_int32_t));
114
@@ -610,15 +629,11 @@ key_havesp(u_int dir)
115
  * OUT:	NULL:	not found
116
  *	others:	found and return the pointer.
117
  */
118
-struct secpolicy *
119
-key_allocsp(struct secpolicyindex *spidx, u_int dir, const char* where, int tag)
120
+static struct secpolicy *
121
+key_allocsp_slow(struct secpolicyindex *spidx, u_int dir, const char* where, int tag)
122
 {
123
 	struct secpolicy *sp;
124
 
125
-	IPSEC_ASSERT(spidx != NULL, ("null spidx"));
126
-	IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND,
127
-		("invalid direction %u", dir));
128
-
129
 	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
130
 		printf("DP %s from %s:%u\n", __func__, where, tag));
131
 
132
@@ -627,7 +642,7 @@ key_allocsp(struct secpolicyindex *spidx, u_int dir, const char* where, int tag)
133
 		printf("*** objects\n");
134
 		kdebug_secpolicyindex(spidx));
135
 
136
-	SPTREE_LOCK();
137
+	SPTREE_LOCK_ASSERT();
138
 	LIST_FOREACH(sp, &V_sptree[dir], chain) {
139
 		KEYDEBUG(KEYDEBUG_IPSEC_DATA,
140
 			printf("*** in SPD\n");
141
@@ -640,6 +655,45 @@ key_allocsp(struct secpolicyindex *spidx, u_int dir, const char* where, int tag)
142
 	}
143
 	sp = NULL;
144
 found:
145
+
146
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
147
+		printf("DP %s return SP:%p (ID=%u) refcnt %u\n", __func__,
148
+			sp, sp ? sp->id : 0, sp ? sp->refcnt : 0));
149
+	return sp;
150
+}
151
+
152
+static Fnv32_t key_hash_spidx(struct secpolicyindex *spidx)
153
+{
154
+	Fnv32_t hash = FNV1_32_INIT;
155
+	hash = fnv_32_buf(&spidx->src.sa, spidx->src.sa.sa_len, hash);
156
+	hash = fnv_32_buf(&spidx->dst.sa, spidx->dst.sa.sa_len, hash);
157
+	hash = fnv_32_buf(&spidx->ul_proto, sizeof(spidx->ul_proto), hash);
158
+	return hash;
159
+}
160
+
161
+struct secpolicy *
162
+key_allocsp(struct secpolicyindex *spidx, u_int dir, const char* where, int tag)
163
+{
164
+	struct secpolicy *sp;
165
+	Fnv32_t hash;
166
+	int hdir;
167
+
168
+	IPSEC_ASSERT(spidx != NULL, ("null spidx"));
169
+	IPSEC_ASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND,
170
+		("invalid direction %u", dir));
171
+
172
+	SPTREE_LOCK();
173
+	hdir = (dir == IPSEC_DIR_INBOUND);
174
+	hash = key_hash_spidx(spidx) % SPCACHESIZE;
175
+	if (V_spcache[hdir][hash].genid == V_spcache_genid &&
176
+	    key_cmpspidx_exactly(&V_spcache[hdir][hash].spidx, spidx)) {
177
+		sp = V_spcache[hdir][hash].policy;
178
+	} else {
179
+		sp = key_allocsp_slow(spidx, dir, where, tag);
180
+		V_spcache[hdir][hash].policy = sp;
181
+		V_spcache[hdir][hash].spidx = *spidx;
182
+		V_spcache[hdir][hash].genid = V_spcache_genid;
183
+	}
184
 	if (sp) {
185
 		/* sanity check */
186
 		KEY_CHKSPDIR(sp->spidx.dir, dir, __func__);
187
@@ -650,9 +704,6 @@ found:
188
 	}
189
 	SPTREE_UNLOCK();
190
 
191
-	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
192
-		printf("DP %s return SP:%p (ID=%u) refcnt %u\n", __func__,
193
-			sp, sp ? sp->id : 0, sp ? sp->refcnt : 0));
194
 	return sp;
195
 }
196
 
197
@@ -1081,10 +1132,7 @@ key_allocsa(
198
 	u_int32_t spi,
199
 	const char* where, int tag)
200
 {
201
-	struct secashead *sah;
202
 	struct secasvar *sav;
203
-	u_int stateidx, arraysize, state;
204
-	const u_int *saorder_state_valid;
205
 	int chkport;
206
 
207
 	IPSEC_ASSERT(dst != NULL, ("null dst address"));
208
@@ -1107,40 +1155,19 @@ key_allocsa(
209
 	 * encrypted so we can't check internal IP header.
210
 	 */
211
 	SAHTREE_LOCK();
212
-	if (V_key_preferred_oldsa) {
213
-		saorder_state_valid = saorder_state_valid_prefer_old;
214
-		arraysize = _ARRAYLEN(saorder_state_valid_prefer_old);
215
-	} else {
216
-		saorder_state_valid = saorder_state_valid_prefer_new;
217
-		arraysize = _ARRAYLEN(saorder_state_valid_prefer_new);
218
-	}
219
-	LIST_FOREACH(sah, &V_sahtree, chain) {
220
-		/* search valid state */
221
-		for (stateidx = 0; stateidx < arraysize; stateidx++) {
222
-			state = saorder_state_valid[stateidx];
223
-			LIST_FOREACH(sav, &sah->savtree[state], chain) {
224
-				/* sanity check */
225
-				KEY_CHKSASTATE(sav->state, state, __func__);
226
-				/* do not return entries w/ unusable state */
227
-				if (sav->state != SADB_SASTATE_MATURE &&
228
-				    sav->state != SADB_SASTATE_DYING)
229
-					continue;
230
-				if (proto != sav->sah->saidx.proto)
231
-					continue;
232
-				if (spi != sav->spi)
233
-					continue;
234
-#if 0	/* don't check src */
235
-				/* check src address */
236
-				if (key_sockaddrcmp(&src->sa, &sav->sah->saidx.src.sa, chkport) != 0)
237
-					continue;
238
-#endif
239
-				/* check dst address */
240
-				if (key_sockaddrcmp(&dst->sa, &sav->sah->saidx.dst.sa, chkport) != 0)
241
-					continue;
242
-				sa_addref(sav);
243
-				goto done;
244
-			}
245
-		}
246
+	LIST_FOREACH(sav, &spihash[SPIHASH(spi)], spihash) {
247
+		if (sav->state != SADB_SASTATE_MATURE &&
248
+			sav->state != SADB_SASTATE_DYING)
249
+			continue;
250
+		if (proto != sav->sah->saidx.proto)
251
+			continue;
252
+		if (spi != sav->spi)
253
+			continue;
254
+		/* check dst address */
255
+		if (key_sockaddrcmp(&dst->sa, &sav->sah->saidx.dst.sa, chkport) != 0)
256
+			continue;
257
+		sa_addref(sav);
258
+		goto done;
259
 	}
260
 	sav = NULL;
261
 done:
262
@@ -1273,6 +1300,8 @@ key_delsp(struct secpolicy *sp)
263
 	IPSEC_ASSERT(sp != NULL, ("null sp"));
264
 	SPTREE_LOCK_ASSERT();
265
 
266
+	if (sp->state != IPSEC_SPSTATE_DEAD)
267
+		V_spcache_genid++;
268
 	sp->state = IPSEC_SPSTATE_DEAD;
269
 
270
 	IPSEC_ASSERT(sp->refcnt == 0,
271
@@ -1936,6 +1965,7 @@ key_spdadd(so, m, mhp)
272
 	newsp->refcnt = 1;	/* do not reclaim until I say I do */
273
 	newsp->state = IPSEC_SPSTATE_ALIVE;
274
 	LIST_INSERT_TAIL(&V_sptree[newsp->spidx.dir], newsp, secpolicy, chain);
275
+	V_spcache_genid++;
276
 
277
 	/* delete the entry in spacqtree */
278
 	if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) {
279
@@ -2383,6 +2413,7 @@ key_spdflush(so, m, mhp)
280
 		SPTREE_LOCK();
281
 		LIST_FOREACH(sp, &V_sptree[dir], chain)
282
 			sp->state = IPSEC_SPSTATE_DEAD;
283
+		V_spcache_genid++;
284
 		SPTREE_UNLOCK();
285
 	}
286
 
287
@@ -2772,7 +2803,8 @@ key_delsah(sah)
288
  * does not modify mbuf.  does not free mbuf on error.
289
  */
290
 static struct secasvar *
291
-key_newsav(m, mhp, sah, errp, where, tag)
292
+key_newsav(spi, m, mhp, sah, errp, where, tag)
293
+	u_int32_t spi;
294
 	struct mbuf *m;
295
 	const struct sadb_msghdr *mhp;
296
 	struct secashead *sah;
297
@@ -2781,12 +2813,12 @@ key_newsav(m, mhp, sah, errp, where, tag)
298
 	int tag;
299
 {
300
 	struct secasvar *newsav;
301
-	const struct sadb_sa *xsa;
302
 
303
 	IPSEC_ASSERT(m != NULL, ("null mbuf"));
304
 	IPSEC_ASSERT(mhp != NULL, ("null msghdr"));
305
 	IPSEC_ASSERT(mhp->msg != NULL, ("null msg"));
306
 	IPSEC_ASSERT(sah != NULL, ("null secashead"));
307
+	SAHTREE_LOCK_ASSERT();
308
 
309
 	newsav = malloc(sizeof(struct secasvar), M_IPSEC_SA, M_NOWAIT|M_ZERO);
310
 	if (newsav == NULL) {
311
@@ -2795,41 +2827,16 @@ key_newsav(m, mhp, sah, errp, where, tag)
312
 		goto done;
313
 	}
314
 
315
-	switch (mhp->msg->sadb_msg_type) {
316
-	case SADB_GETSPI:
317
-		newsav->spi = 0;
318
 
319
 #ifdef IPSEC_DOSEQCHECK
320
-		/* sync sequence number */
321
-		if (mhp->msg->sadb_msg_seq == 0)
322
-			newsav->seq =
323
-				(V_acq_seq = (V_acq_seq == ~0 ? 1 : ++V_acq_seq));
324
-		else
325
+	/* sync sequence number */
326
+	if (mhp->msg->sadb_msg_type == SADB_GETSPI &&
327
+	    mhp->msg->sadb_msg_seq == 0)
328
+		newsav->seq =
329
+			(V_acq_seq = (V_acq_seq == ~0 ? 1 : ++V_acq_seq));
330
+	else
331
 #endif
332
-			newsav->seq = mhp->msg->sadb_msg_seq;
333
-		break;
334
-
335
-	case SADB_ADD:
336
-		/* sanity check */
337
-		if (mhp->ext[SADB_EXT_SA] == NULL) {
338
-			free(newsav, M_IPSEC_SA);
339
-			newsav = NULL;
340
-			ipseclog((LOG_DEBUG, "%s: invalid message is passed.\n",
341
-				__func__));
342
-			*errp = EINVAL;
343
-			goto done;
344
-		}
345
-		xsa = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA];
346
-		newsav->spi = xsa->sadb_sa_spi;
347
 		newsav->seq = mhp->msg->sadb_msg_seq;
348
-		break;
349
-	default:
350
-		free(newsav, M_IPSEC_SA);
351
-		newsav = NULL;
352
-		*errp = EINVAL;
353
-		goto done;
354
-	}
355
-
356
 
357
 	/* copy sav values */
358
 	if (mhp->msg->sadb_msg_type != SADB_GETSPI) {
359
@@ -2841,21 +2848,21 @@ key_newsav(m, mhp, sah, errp, where, tag)
360
 		}
361
 	}
362
 
363
-	SECASVAR_LOCK_INIT(newsav);
364
-
365
 	/* reset created */
366
 	newsav->created = time_second;
367
 	newsav->pid = mhp->msg->sadb_msg_pid;
368
+	newsav->spi = spi;
369
 
370
 	/* add to satree */
371
 	newsav->sah = sah;
372
 	sa_initref(newsav);
373
 	newsav->state = SADB_SASTATE_LARVAL;
374
 
375
-	SAHTREE_LOCK();
376
 	LIST_INSERT_TAIL(&sah->savtree[SADB_SASTATE_LARVAL], newsav,
377
 			secasvar, chain);
378
-	SAHTREE_UNLOCK();
379
+	if (spi)
380
+		LIST_INSERT_HEAD(&spihash[SPIHASH(spi)], newsav, spihash);
381
+
382
 done:
383
 	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
384
 		printf("DP %s from %s:%u return SP:%p\n", __func__,
385
@@ -2932,8 +2939,9 @@ key_delsav(sav)
386
 	/* remove from SA header */
387
 	if (__LIST_CHAINED(sav))
388
 		LIST_REMOVE(sav, chain);
389
+	if (__LIST_SPIHASHED(sav))
390
+		LIST_REMOVE(sav, spihash);
391
 	key_cleansav(sav);
392
-	SECASVAR_LOCK_DESTROY(sav);
393
 	free(sav, M_IPSEC_SA);
394
 }
395
 
396
@@ -2963,7 +2971,6 @@ key_getsah(saidx)
397
 
398
 /*
399
  * check not to be duplicated SPI.
400
- * NOTE: this function is too slow due to searching all SAD.
401
  * OUT:
402
  *	NULL	: not found
403
  *	others	: found, pointer to a SA.
404
@@ -2973,9 +2980,10 @@ key_checkspidup(saidx, spi)
405
 	struct secasindex *saidx;
406
 	u_int32_t spi;
407
 {
408
-	struct secashead *sah;
409
 	struct secasvar *sav;
410
 
411
+	SAHTREE_LOCK_ASSERT();
412
+
413
 	/* check address family */
414
 	if (saidx->src.sa.sa_family != saidx->dst.sa.sa_family) {
415
 		ipseclog((LOG_DEBUG, "%s: address family mismatched.\n",
416
@@ -2984,16 +2992,11 @@ key_checkspidup(saidx, spi)
417
 	}
418
 
419
 	sav = NULL;
420
-	/* check all SAD */
421
-	SAHTREE_LOCK();
422
-	LIST_FOREACH(sah, &V_sahtree, chain) {
423
-		if (!key_ismyaddr((struct sockaddr *)&sah->saidx.dst))
424
-			continue;
425
-		sav = key_getsavbyspi(sah, spi);
426
-		if (sav != NULL)
427
+	LIST_FOREACH(sav, &spihash[SPIHASH(spi)], spihash) {
428
+		if (sav->spi == spi &&
429
+		    key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst))
430
 			break;
431
 	}
432
-	SAHTREE_UNLOCK();
433
 
434
 	return sav;
435
 }
436
@@ -3010,27 +3013,21 @@ key_getsavbyspi(sah, spi)
437
 	u_int32_t spi;
438
 {
439
 	struct secasvar *sav;
440
-	u_int stateidx, state;
441
+	u_int stateidx;
442
 
443
 	sav = NULL;
444
 	SAHTREE_LOCK_ASSERT();
445
-	/* search all status */
446
-	for (stateidx = 0;
447
-	     stateidx < _ARRAYLEN(saorder_state_alive);
448
-	     stateidx++) {
449
-
450
-		state = saorder_state_alive[stateidx];
451
-		LIST_FOREACH(sav, &sah->savtree[state], chain) {
452
 
453
-			/* sanity check */
454
-			if (sav->state != state) {
455
-				ipseclog((LOG_DEBUG, "%s: "
456
-				    "invalid sav->state (queue: %d SA: %d)\n",
457
-				    __func__, state, sav->state));
458
-				continue;
459
-			}
460
+	LIST_FOREACH(sav, &spihash[SPIHASH(spi)], spihash) {
461
+		if (sav->sah != sah)
462
+			continue;
463
+		if (sav->spi != spi)
464
+			continue;
465
 
466
-			if (sav->spi == spi)
467
+		for (stateidx = 0; 
468
+		     stateidx < _ARRAYLEN(saorder_state_alive);
469
+		     stateidx++) {
470
+			if (sav->state == saorder_state_alive[stateidx])
471
 				return sav;
472
 		}
473
 	}
474
@@ -4322,6 +4319,7 @@ restart:
475
 			if ((sp->lifetime && now - sp->created > sp->lifetime)
476
 			 || (sp->validtime && now - sp->lastused > sp->validtime)) {
477
 				sp->state = IPSEC_SPSTATE_DEAD;
478
+				V_spcache_genid++;
479
 				SPTREE_UNLOCK();
480
 				key_spdexpire(sp);
481
 				goto restart;
482
@@ -4758,12 +4756,6 @@ key_getspi(so, m, mhp)
483
 	}
484
 #endif
485
 
486
-	/* SPI allocation */
487
-	spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE],
488
-	                       &saidx);
489
-	if (spi == 0)
490
-		return key_senderror(so, m, EINVAL);
491
-
492
 	/* get a SA index */
493
 	if ((newsah = key_getsah(&saidx)) == NULL) {
494
 		/* create a new SA index */
495
@@ -4773,16 +4765,24 @@ key_getspi(so, m, mhp)
496
 		}
497
 	}
498
 
499
+	/* SPI allocation */
500
+	SAHTREE_LOCK();
501
+	spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE],
502
+	                       &saidx);
503
+	if (spi == 0) {
504
+		SAHTREE_UNLOCK();
505
+		return key_senderror(so, m, EINVAL);
506
+	}
507
+
508
 	/* get a new SA */
509
 	/* XXX rewrite */
510
-	newsav = KEY_NEWSAV(m, mhp, newsah, &error);
511
+	newsav = KEY_NEWSAV(spi, m, mhp, newsah, &error);
512
 	if (newsav == NULL) {
513
 		/* XXX don't free new SA index allocated in above. */
514
+		SAHTREE_UNLOCK();
515
 		return key_senderror(so, m, error);
516
 	}
517
-
518
-	/* set spi */
519
-	newsav->spi = htonl(spi);
520
+	SAHTREE_UNLOCK();
521
 
522
 	/* delete the entry in acqtree */
523
 	if (mhp->msg->sadb_msg_seq != 0) {
524
@@ -4825,7 +4825,7 @@ key_getspi(so, m, mhp)
525
 	m_sa = (struct sadb_sa *)(mtod(n, caddr_t) + off);
526
 	m_sa->sadb_sa_len = PFKEY_UNIT64(sizeof(struct sadb_sa));
527
 	m_sa->sadb_sa_exttype = SADB_EXT_SA;
528
-	m_sa->sadb_sa_spi = htonl(spi);
529
+	m_sa->sadb_sa_spi = spi;
530
 	off += PFKEY_ALIGN8(sizeof(struct sadb_sa));
531
 
532
 	IPSEC_ASSERT(off == len,
533
@@ -4874,6 +4874,8 @@ key_do_getnewspi(spirange, saidx)
534
 	u_int32_t min, max;
535
 	int count = V_key_spi_trycnt;
536
 
537
+	SAHTREE_LOCK_ASSERT();
538
+
539
 	/* set spi range to allocate */
540
 	if (spirange != NULL) {
541
 		min = spirange->sadb_spirange_min;
542
@@ -4895,15 +4897,15 @@ key_do_getnewspi(spirange, saidx)
543
 	}
544
 
545
 	if (min == max) {
546
-		if (key_checkspidup(saidx, min) != NULL) {
547
+		newspi = htonl(min);
548
+
549
+		if (key_checkspidup(saidx, newspi) != NULL) {
550
 			ipseclog((LOG_DEBUG, "%s: SPI %u exists already.\n",
551
 				__func__, min));
552
 			return 0;
553
 		}
554
 
555
 		count--; /* taking one cost. */
556
-		newspi = min;
557
-
558
 	} else {
559
 
560
 		/* init SPI */
561
@@ -4912,7 +4914,7 @@ key_do_getnewspi(spirange, saidx)
562
 		/* when requesting to allocate spi ranged */
563
 		while (count--) {
564
 			/* generate pseudo-random SPI value ranged. */
565
-			newspi = min + (key_random() % (max - min + 1));
566
+			newspi = htonl(min + (key_random() % (max - min + 1)));
567
 
568
 			if (key_checkspidup(saidx, newspi) == NULL)
569
 				break;
570
@@ -5393,12 +5395,14 @@ key_add(so, m, mhp)
571
 	/* We can create new SA only if SPI is differenct. */
572
 	SAHTREE_LOCK();
573
 	newsav = key_getsavbyspi(newsah, sa0->sadb_sa_spi);
574
-	SAHTREE_UNLOCK();
575
 	if (newsav != NULL) {
576
+		SAHTREE_UNLOCK();
577
 		ipseclog((LOG_DEBUG, "%s: SA already exists.\n", __func__));
578
 		return key_senderror(so, m, EEXIST);
579
 	}
580
-	newsav = KEY_NEWSAV(m, mhp, newsah, &error);
581
+	newsav = KEY_NEWSAV(sa0->sadb_sa_spi, m, mhp, newsah, &error);
582
+	SAHTREE_UNLOCK();
583
+
584
 	if (newsav == NULL) {
585
 		return key_senderror(so, m, error);
586
 	}
587
@@ -7733,6 +7737,8 @@ key_init(void)
588
 		LIST_INIT(&V_sptree[i]);
589
 
590
 	LIST_INIT(&V_sahtree);
591
+	for (i = 0; i < SPIHASHSIZE; i++)
592
+		LIST_INIT(&V_spihash[i]);
593
 
594
 	for (i = 0; i <= SADB_SATYPE_MAX; i++)
595
 		LIST_INIT(&V_regtree[i]);
596
diff --git a/sys/netipsec/keydb.h b/sys/netipsec/keydb.h
597
index fb01c0f..793b668 100644
598
--- a/sys/netipsec/keydb.h
599
+++ b/sys/netipsec/keydb.h
600
@@ -119,6 +119,7 @@ struct comp_algo;
601
 /* Security Association */
602
 struct secasvar {
603
 	LIST_ENTRY(secasvar) chain;
604
+	LIST_ENTRY(secasvar) spihash;
605
 	struct mtx lock;		/* update/access lock */
606
 
607
 	u_int refcnt;			/* reference count */
(28-28/67)