xref: /illumos-gate/usr/src/uts/common/inet/ilb/ilb_conn.c (revision 5422785d352a2bb398daceab3d1898a8aa64d006)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/sysmacros.h>
28 #include <sys/types.h>
29 #include <sys/conf.h>
30 #include <sys/time.h>
31 #include <sys/taskq.h>
32 #include <sys/cmn_err.h>
33 #include <sys/sdt.h>
34 #include <sys/atomic.h>
35 #include <netinet/in.h>
36 #include <inet/ip.h>
37 #include <inet/ip6.h>
38 #include <inet/tcp.h>
39 #include <inet/udp_impl.h>
40 #include <inet/ilb.h>
41 
42 #include "ilb_stack.h"
43 #include "ilb_impl.h"
44 #include "ilb_conn.h"
45 #include "ilb_nat.h"
46 
47 /*
48  * Timer struct for ilb_conn_t and ilb_sticky_t garbage collection
49  *
50  * start: starting index into the hash table to do gc
51  * end: ending index into the hash table to do gc
52  * ilbs: pointer to the ilb_stack_t of the IP stack
53  * tid_lock: mutex to protect the timer id.
54  * tid: timer id of the timer
55  */
56 typedef struct ilb_timer_s {
57 	uint32_t	start;
58 	uint32_t	end;
59 	ilb_stack_t	*ilbs;
60 	kmutex_t	tid_lock;
61 	timeout_id_t	tid;
62 } ilb_timer_t;
63 
64 /* Hash macro for finding the index to the conn hash table */
65 #define	ILB_CONN_HASH(saddr, sport, daddr, dport, hash_size)	\
66 	(((*((saddr) + 3) ^ *((daddr) + 3)) * 50653 +		\
67 	(*((saddr) + 2) ^ *((daddr) + 2)) * 1369 +		\
68 	(*((saddr) + 1) ^ *((daddr) + 1)) * 37 +		\
69 	(*(saddr) ^ *(daddr)) + (sport) * 37 + (dport)) &	\
70 	((hash_size) - 1))
71 
72 /* Kmem cache for the conn hash entry */
73 static struct kmem_cache *ilb_conn_cache = NULL;
74 
75 /*
76  * There are 60 timers running to do conn cache garbage collection.  Each
77  * gc thread is responsible for 1/60 of the conn hash table.
78  */
79 static int ilb_conn_timer_size = 60;
80 
81 /* Each of the above gc timers wake up every 15s to do the gc. */
82 static int ilb_conn_cache_timeout = 15;
83 
84 #define	ILB_STICKY_HASH(saddr, rule, hash_size)			\
85 	(((*((saddr) + 3) ^ ((rule) >> 24)) * 29791 +		\
86 	(*((saddr) + 2) ^ ((rule) >> 16)) * 961 +		\
87 	(*((saddr) + 1) ^ ((rule) >> 8)) * 31 +			\
88 	(*(saddr) ^ (rule))) & ((hash_size) - 1))
89 
90 static struct kmem_cache *ilb_sticky_cache = NULL;
91 
92 /*
93  * There are 60 timers running to do sticky cache garbage collection.  Each
94  * gc thread is responsible for 1/60 of the sticky hash table.
95  */
96 static int ilb_sticky_timer_size = 60;
97 
98 /* Each of the above gc timers wake up every 15s to do the gc. */
99 static int ilb_sticky_timeout = 15;
100 
101 #define	ILB_STICKY_REFRELE(s)			\
102 {						\
103 	mutex_enter(&(s)->hash->sticky_lock);	\
104 	(s)->refcnt--;				\
105 	(s)->atime = ddi_get_lbolt64();		\
106 	mutex_exit(&s->hash->sticky_lock);	\
107 }
108 
109 
110 static void
111 ilb_conn_cache_init(void)
112 {
113 	ilb_conn_cache = kmem_cache_create("ilb_conn_cache",
114 	    sizeof (ilb_conn_t), 0, NULL, NULL, NULL, NULL, NULL,
115 	    ilb_kmem_flags);
116 }
117 
118 void
119 ilb_conn_cache_fini(void)
120 {
121 	if (ilb_conn_cache != NULL) {
122 		kmem_cache_destroy(ilb_conn_cache);
123 		ilb_conn_cache = NULL;
124 	}
125 }
126 
127 static void
128 ilb_conn_remove_common(ilb_conn_t *connp, boolean_t c2s)
129 {
130 	ilb_conn_hash_t *hash;
131 	ilb_conn_t **next, **prev;
132 	ilb_conn_t **next_prev, **prev_next;
133 
134 	if (c2s) {
135 		hash = connp->conn_c2s_hash;
136 		ASSERT(MUTEX_HELD(&hash->ilb_conn_hash_lock));
137 		next = &connp->conn_c2s_next;
138 		prev = &connp->conn_c2s_prev;
139 		if (*next != NULL)
140 			next_prev = &(*next)->conn_c2s_prev;
141 		if (*prev != NULL)
142 			prev_next = &(*prev)->conn_c2s_next;
143 	} else {
144 		hash = connp->conn_s2c_hash;
145 		ASSERT(MUTEX_HELD(&hash->ilb_conn_hash_lock));
146 		next = &connp->conn_s2c_next;
147 		prev = &connp->conn_s2c_prev;
148 		if (*next != NULL)
149 			next_prev = &(*next)->conn_s2c_prev;
150 		if (*prev != NULL)
151 			prev_next = &(*prev)->conn_s2c_next;
152 	}
153 
154 	if (hash->ilb_connp == connp) {
155 		hash->ilb_connp = *next;
156 		if (*next != NULL)
157 			*next_prev = NULL;
158 	} else {
159 		if (*prev != NULL)
160 			*prev_next = *next;
161 		if (*next != NULL)
162 			*next_prev = *prev;
163 	}
164 	ASSERT(hash->ilb_conn_cnt > 0);
165 	hash->ilb_conn_cnt--;
166 
167 	*next = NULL;
168 	*prev = NULL;
169 }
170 
171 static void
172 ilb_conn_remove(ilb_conn_t *connp)
173 {
174 	ASSERT(MUTEX_HELD(&connp->conn_c2s_hash->ilb_conn_hash_lock));
175 	ilb_conn_remove_common(connp, B_TRUE);
176 	ASSERT(MUTEX_HELD(&connp->conn_s2c_hash->ilb_conn_hash_lock));
177 	ilb_conn_remove_common(connp, B_FALSE);
178 
179 	if (connp->conn_rule_cache.topo == ILB_TOPO_IMPL_NAT) {
180 		in_port_t port;
181 
182 		port = ntohs(connp->conn_rule_cache.info.nat_sport);
183 		vmem_free(connp->conn_rule_cache.info.src_ent->nse_port_arena,
184 		    (void *)(uintptr_t)port, 1);
185 	}
186 
187 	if (connp->conn_sticky != NULL)
188 		ILB_STICKY_REFRELE(connp->conn_sticky);
189 	ILB_SERVER_REFRELE(connp->conn_server);
190 	kmem_cache_free(ilb_conn_cache, connp);
191 }
192 
193 /*
194  * Routine to do periodic garbage collection of conn hash entries.  When
195  * a conn hash timer fires, it dispatches a taskq to call this function
196  * to do the gc.  Note that each taskq is responisble for a portion of
197  * the table.  The portion is stored in timer->start, timer->end.
198  */
199 static void
200 ilb_conn_cleanup(void *arg)
201 {
202 	ilb_timer_t *timer = (ilb_timer_t *)arg;
203 	uint32_t i;
204 	ilb_stack_t *ilbs;
205 	ilb_conn_hash_t *c2s_hash, *s2c_hash;
206 	ilb_conn_t *connp, *nxt_connp;
207 	int64_t now;
208 	int64_t expiry;
209 	boolean_t die_now;
210 
211 	ilbs = timer->ilbs;
212 	c2s_hash = ilbs->ilbs_c2s_conn_hash;
213 	ASSERT(c2s_hash != NULL);
214 
215 	now = ddi_get_lbolt64();
216 	for (i = timer->start; i < timer->end; i++) {
217 		mutex_enter(&c2s_hash[i].ilb_conn_hash_lock);
218 		if ((connp = c2s_hash[i].ilb_connp) == NULL) {
219 			ASSERT(c2s_hash[i].ilb_conn_cnt == 0);
220 			mutex_exit(&c2s_hash[i].ilb_conn_hash_lock);
221 			continue;
222 		}
223 		do {
224 			ASSERT(c2s_hash[i].ilb_conn_cnt > 0);
225 			ASSERT(connp->conn_c2s_hash == &c2s_hash[i]);
226 			nxt_connp = connp->conn_c2s_next;
227 			expiry = now - SEC_TO_TICK(connp->conn_expiry);
228 			if (connp->conn_server->iser_die_time != 0 &&
229 			    connp->conn_server->iser_die_time < now)
230 				die_now = B_TRUE;
231 			else
232 				die_now = B_FALSE;
233 			s2c_hash = connp->conn_s2c_hash;
234 			mutex_enter(&s2c_hash->ilb_conn_hash_lock);
235 
236 			if (connp->conn_gc || die_now ||
237 			    (connp->conn_c2s_atime < expiry &&
238 			    connp->conn_s2c_atime < expiry)) {
239 				/* Need to update the nat list cur_connp */
240 				if (connp == ilbs->ilbs_conn_list_connp) {
241 					ilbs->ilbs_conn_list_connp =
242 					    connp->conn_c2s_next;
243 				}
244 				ilb_conn_remove(connp);
245 				goto nxt_connp;
246 			}
247 
248 			if (connp->conn_l4 != IPPROTO_TCP)
249 				goto nxt_connp;
250 
251 			/* Update and check TCP related conn info */
252 			if (connp->conn_c2s_tcp_fin_sent &&
253 			    SEQ_GT(connp->conn_s2c_tcp_ack,
254 			    connp->conn_c2s_tcp_fss)) {
255 				connp->conn_c2s_tcp_fin_acked = B_TRUE;
256 			}
257 			if (connp->conn_s2c_tcp_fin_sent &&
258 			    SEQ_GT(connp->conn_c2s_tcp_ack,
259 			    connp->conn_s2c_tcp_fss)) {
260 				connp->conn_s2c_tcp_fin_acked = B_TRUE;
261 			}
262 			if (connp->conn_c2s_tcp_fin_acked &&
263 			    connp->conn_s2c_tcp_fin_acked) {
264 				ilb_conn_remove(connp);
265 			}
266 nxt_connp:
267 			mutex_exit(&s2c_hash->ilb_conn_hash_lock);
268 			connp = nxt_connp;
269 		} while (connp != NULL);
270 		mutex_exit(&c2s_hash[i].ilb_conn_hash_lock);
271 	}
272 }
273 
274 /* Conn hash timer routine.  It dispatches a taskq and restart the timer */
275 static void
276 ilb_conn_timer(void *arg)
277 {
278 	ilb_timer_t *timer = (ilb_timer_t *)arg;
279 
280 	(void) taskq_dispatch(timer->ilbs->ilbs_conn_taskq, ilb_conn_cleanup,
281 	    arg, TQ_SLEEP);
282 	mutex_enter(&timer->tid_lock);
283 	if (timer->tid == 0) {
284 		mutex_exit(&timer->tid_lock);
285 	} else {
286 		timer->tid = timeout(ilb_conn_timer, arg,
287 		    SEC_TO_TICK(ilb_conn_cache_timeout));
288 		mutex_exit(&timer->tid_lock);
289 	}
290 }
291 
292 void
293 ilb_conn_hash_init(ilb_stack_t *ilbs)
294 {
295 	extern pri_t minclsyspri;
296 	int i, part;
297 	ilb_timer_t *tm;
298 	char tq_name[TASKQ_NAMELEN];
299 
300 	/*
301 	 * If ilbs->ilbs_conn_hash_size is not a power of 2, bump it up to
302 	 * the next power of 2.
303 	 */
304 	if (!ISP2(ilbs->ilbs_conn_hash_size)) {
305 		for (i = 0; i < 31; i++) {
306 			if (ilbs->ilbs_conn_hash_size < (1 << i))
307 				break;
308 		}
309 		ilbs->ilbs_conn_hash_size = 1 << i;
310 	}
311 
312 	/*
313 	 * Can sleep since this should be called when a rule is being added,
314 	 * hence we are not in interrupt context.
315 	 */
316 	ilbs->ilbs_c2s_conn_hash = kmem_zalloc(sizeof (ilb_conn_hash_t) *
317 	    ilbs->ilbs_conn_hash_size, KM_SLEEP);
318 	ilbs->ilbs_s2c_conn_hash = kmem_zalloc(sizeof (ilb_conn_hash_t) *
319 	    ilbs->ilbs_conn_hash_size, KM_SLEEP);
320 
321 	for (i = 0; i < ilbs->ilbs_conn_hash_size; i++) {
322 		mutex_init(&ilbs->ilbs_c2s_conn_hash[i].ilb_conn_hash_lock,
323 		    NULL, MUTEX_DEFAULT, NULL);
324 	}
325 	for (i = 0; i < ilbs->ilbs_conn_hash_size; i++) {
326 		mutex_init(&ilbs->ilbs_s2c_conn_hash[i].ilb_conn_hash_lock,
327 		    NULL, MUTEX_DEFAULT, NULL);
328 	}
329 
330 	if (ilb_conn_cache == NULL)
331 		ilb_conn_cache_init();
332 
333 	(void) snprintf(tq_name, sizeof (tq_name), "ilb_conn_taskq_%p",
334 	    (void *)ilbs->ilbs_netstack);
335 	ASSERT(ilbs->ilbs_conn_taskq == NULL);
336 	ilbs->ilbs_conn_taskq = taskq_create(tq_name,
337 	    ilb_conn_timer_size * 2, minclsyspri, ilb_conn_timer_size,
338 	    ilb_conn_timer_size * 2, TASKQ_PREPOPULATE|TASKQ_DYNAMIC);
339 
340 	ASSERT(ilbs->ilbs_conn_timer_list == NULL);
341 	ilbs->ilbs_conn_timer_list = kmem_zalloc(sizeof (ilb_timer_t) *
342 	    ilb_conn_timer_size, KM_SLEEP);
343 
344 	/*
345 	 * The hash table is divided in equal partition for those timers
346 	 * to do garbage collection.
347 	 */
348 	part = ilbs->ilbs_conn_hash_size / ilb_conn_timer_size + 1;
349 	for (i = 0; i < ilb_conn_timer_size; i++) {
350 		tm = ilbs->ilbs_conn_timer_list + i;
351 		tm->start = i * part;
352 		tm->end = i * part + part;
353 		if (tm->end > ilbs->ilbs_conn_hash_size)
354 			tm->end = ilbs->ilbs_conn_hash_size;
355 		tm->ilbs = ilbs;
356 		mutex_init(&tm->tid_lock, NULL, MUTEX_DEFAULT, NULL);
357 		/* Spread out the starting execution time of all the timers. */
358 		tm->tid = timeout(ilb_conn_timer, tm,
359 		    SEC_TO_TICK(ilb_conn_cache_timeout + i));
360 	}
361 }
362 
363 void
364 ilb_conn_hash_fini(ilb_stack_t *ilbs)
365 {
366 	uint32_t i;
367 	ilb_conn_t *connp;
368 
369 	if (ilbs->ilbs_c2s_conn_hash == NULL) {
370 		ASSERT(ilbs->ilbs_s2c_conn_hash == NULL);
371 		return;
372 	}
373 
374 	/* Stop all the timers first. */
375 	for (i = 0; i < ilb_conn_timer_size; i++) {
376 		timeout_id_t tid;
377 
378 		/* Setting tid to 0 tells the timer handler not to restart. */
379 		mutex_enter(&ilbs->ilbs_conn_timer_list[i].tid_lock);
380 		tid = ilbs->ilbs_conn_timer_list[i].tid;
381 		ilbs->ilbs_conn_timer_list[i].tid = 0;
382 		mutex_exit(&ilbs->ilbs_conn_timer_list[i].tid_lock);
383 		(void) untimeout(tid);
384 	}
385 	kmem_free(ilbs->ilbs_conn_timer_list, sizeof (ilb_timer_t) *
386 	    ilb_conn_timer_size);
387 	taskq_destroy(ilbs->ilbs_conn_taskq);
388 	ilbs->ilbs_conn_taskq = NULL;
389 
390 	/* Then remove all the conns. */
391 	for (i = 0; i < ilbs->ilbs_conn_hash_size; i++) {
392 		while ((connp = ilbs->ilbs_s2c_conn_hash->ilb_connp) != NULL) {
393 			ilbs->ilbs_s2c_conn_hash->ilb_connp =
394 			    connp->conn_s2c_next;
395 			ILB_SERVER_REFRELE(connp->conn_server);
396 			if (connp->conn_rule_cache.topo == ILB_TOPO_IMPL_NAT) {
397 				ilb_nat_src_entry_t *ent;
398 				in_port_t port;
399 
400 				/*
401 				 * src_ent will be freed in ilb_nat_src_fini().
402 				 */
403 				port = ntohs(
404 				    connp->conn_rule_cache.info.nat_sport);
405 				ent = connp->conn_rule_cache.info.src_ent;
406 				vmem_free(ent->nse_port_arena,
407 				    (void *)(uintptr_t)port, 1);
408 			}
409 			kmem_cache_free(ilb_conn_cache, connp);
410 		}
411 	}
412 	kmem_free(ilbs->ilbs_c2s_conn_hash, sizeof (ilb_conn_hash_t) *
413 	    ilbs->ilbs_conn_hash_size);
414 	kmem_free(ilbs->ilbs_s2c_conn_hash, sizeof (ilb_conn_hash_t) *
415 	    ilbs->ilbs_conn_hash_size);
416 }
417 
418 /*
419  * Internet checksum adjustment calculation routines.  We pre-calculate
420  * checksum adjustment so that we don't need to compute the checksum on
421  * the whole packet when we change address/port in the packet.
422  */
423 
424 static void
425 hnat_cksum_v4(uint16_t *oaddr, uint16_t *naddr, in_port_t old_port,
426     in_port_t new_port, uint32_t *adj_sum)
427 {
428 	uint32_t sum;
429 
430 	sum = *oaddr + *(oaddr + 1) + old_port;
431 	while ((sum >> 16) != 0)
432 		sum = (sum & 0xffff) + (sum >> 16);
433 	*adj_sum = (uint16_t)~sum + *naddr + *(naddr + 1) + new_port;
434 }
435 
436 static void
437 hnat_cksum_v6(uint16_t *oaddr, uint16_t *naddr, in_port_t old_port,
438     in_port_t new_port, uint32_t *adj_sum)
439 {
440 	uint32_t sum = 0;
441 
442 	sum = *oaddr + *(oaddr + 1) + *(oaddr + 2) + *(oaddr + 3) +
443 	    *(oaddr + 4) + *(oaddr + 5) + *(oaddr + 6) + *(oaddr + 7) +
444 	    old_port;
445 	while ((sum >> 16) != 0)
446 		sum = (sum & 0xffff) + (sum >> 16);
447 	*adj_sum = (uint16_t)~sum + *naddr + *(naddr + 1) +
448 	    *(naddr + 2) + *(naddr + 3) + *(naddr + 4) + *(naddr + 5) +
449 	    *(naddr + 6) + *(naddr + 7) + new_port;
450 }
451 
452 static void
453 fnat_cksum_v4(uint16_t *oaddr1, uint16_t *oaddr2, uint16_t *naddr1,
454     uint16_t *naddr2, in_port_t old_port1, in_port_t old_port2,
455     in_port_t new_port1, in_port_t new_port2, uint32_t *adj_sum)
456 {
457 	uint32_t sum;
458 
459 	sum = *oaddr1 + *(oaddr1 + 1) + old_port1 + *oaddr2 + *(oaddr2 + 1) +
460 	    old_port2;
461 	while ((sum >> 16) != 0)
462 		sum = (sum & 0xffff) + (sum >> 16);
463 	*adj_sum = (uint16_t)~sum + *naddr1 + *(naddr1 + 1) + new_port1 +
464 	    *naddr2 + *(naddr2 + 1) + new_port2;
465 }
466 
467 static void
468 fnat_cksum_v6(uint16_t *oaddr1, uint16_t *oaddr2, uint16_t *naddr1,
469     uint16_t *naddr2, in_port_t old_port1, in_port_t old_port2,
470     in_port_t new_port1, in_port_t new_port2, uint32_t *adj_sum)
471 {
472 	uint32_t sum = 0;
473 
474 	sum = *oaddr1 + *(oaddr1 + 1) + *(oaddr1 + 2) + *(oaddr1 + 3) +
475 	    *(oaddr1 + 4) + *(oaddr1 + 5) + *(oaddr1 + 6) + *(oaddr1 + 7) +
476 	    old_port1;
477 	sum += *oaddr2 + *(oaddr2 + 1) + *(oaddr2 + 2) + *(oaddr2 + 3) +
478 	    *(oaddr2 + 4) + *(oaddr2 + 5) + *(oaddr2 + 6) + *(oaddr2 + 7) +
479 	    old_port2;
480 	while ((sum >> 16) != 0)
481 		sum = (sum & 0xffff) + (sum >> 16);
482 	sum = (uint16_t)~sum + *naddr1 + *(naddr1 + 1) + *(naddr1 + 2) +
483 	    *(naddr1 + 3) + *(naddr1 + 4) + *(naddr1 + 5) + *(naddr1 + 6) +
484 	    *(naddr1 + 7) + new_port1;
485 	*adj_sum = sum + *naddr2 + *(naddr2 + 1) + *(naddr2 + 2) +
486 	    *(naddr2 + 3) + *(naddr2 + 4) + *(naddr2 + 5) + *(naddr2 + 6) +
487 	    *(naddr2 + 7) + new_port2;
488 }
489 
490 /*
491  * Add a conn hash entry to the tables.  Note that a conn hash entry
492  * (ilb_conn_t) contains info on both directions.  And there are two hash
493  * tables, one for client to server and the other for server to client.
494  * So the same entry is added to both tables and can be ccessed by two
495  * thread simultaneously.  But each thread will only access data on one
496  * direction, so there is no conflict.
497  */
498 int
499 ilb_conn_add(ilb_stack_t *ilbs, ilb_rule_t *rule, ilb_server_t *server,
500     in6_addr_t *src, in_port_t sport, in6_addr_t *dst, in_port_t dport,
501     ilb_nat_info_t *info, uint32_t *ip_sum, uint32_t *tp_sum, ilb_sticky_t *s)
502 {
503 	ilb_conn_t *connp;
504 	ilb_conn_hash_t *hash;
505 	int i;
506 
507 	connp = kmem_cache_alloc(ilb_conn_cache, KM_NOSLEEP);
508 	if (connp == NULL) {
509 		if (s != NULL) {
510 			if (rule->ir_topo == ILB_TOPO_IMPL_NAT) {
511 				ilb_nat_src_entry_t **entry;
512 
513 				entry = s->server->iser_nat_src->src_list;
514 				vmem_free(entry[s->nat_src_idx]->nse_port_arena,
515 				    (void *)(uintptr_t)ntohs(info->nat_sport),
516 				    1);
517 			}
518 			ILB_STICKY_REFRELE(s);
519 		}
520 		return (ENOMEM);
521 	}
522 
523 	connp->conn_l4 = rule->ir_proto;
524 
525 	connp->conn_server = server;
526 	ILB_SERVER_REFHOLD(server);
527 	connp->conn_sticky = s;
528 
529 	connp->conn_rule_cache.topo = rule->ir_topo;
530 	connp->conn_rule_cache.info = *info;
531 
532 	connp->conn_gc = B_FALSE;
533 
534 	connp->conn_expiry = rule->ir_nat_expiry;
535 	connp->conn_cr_time = ddi_get_lbolt64();
536 
537 	/* Client to server info. */
538 	connp->conn_c2s_saddr = *src;
539 	connp->conn_c2s_sport = sport;
540 	connp->conn_c2s_daddr = *dst;
541 	connp->conn_c2s_dport = dport;
542 
543 	connp->conn_c2s_atime = ddi_get_lbolt64();
544 	/* The packet ths triggers this creation should be counted */
545 	connp->conn_c2s_pkt_cnt = 1;
546 	connp->conn_c2s_tcp_fin_sent = B_FALSE;
547 	connp->conn_c2s_tcp_fin_acked = B_FALSE;
548 
549 	/* Server to client info, before NAT */
550 	switch (rule->ir_topo) {
551 	case ILB_TOPO_IMPL_HALF_NAT:
552 		connp->conn_s2c_saddr = info->nat_dst;
553 		connp->conn_s2c_sport = info->nat_dport;
554 		connp->conn_s2c_daddr = *src;
555 		connp->conn_s2c_dport = sport;
556 
557 		/* Pre-calculate checksum changes for both directions */
558 		if (rule->ir_ipver == IPPROTO_IP) {
559 			hnat_cksum_v4((uint16_t *)&dst->s6_addr32[3],
560 			    (uint16_t *)&info->nat_dst.s6_addr32[3], 0, 0,
561 			    &connp->conn_c2s_ip_sum);
562 			hnat_cksum_v4((uint16_t *)&dst->s6_addr32[3],
563 			    (uint16_t *)&info->nat_dst.s6_addr32[3], dport,
564 			    info->nat_dport, &connp->conn_c2s_tp_sum);
565 			*ip_sum = connp->conn_c2s_ip_sum;
566 			*tp_sum = connp->conn_c2s_tp_sum;
567 
568 			hnat_cksum_v4(
569 			    (uint16_t *)&info->nat_dst.s6_addr32[3],
570 			    (uint16_t *)&dst->s6_addr32[3], 0, 0,
571 			    &connp->conn_s2c_ip_sum);
572 			hnat_cksum_v4(
573 			    (uint16_t *)&info->nat_dst.s6_addr32[3],
574 			    (uint16_t *)&dst->s6_addr32[3],
575 			    info->nat_dport, dport,
576 			    &connp->conn_s2c_tp_sum);
577 		} else {
578 			connp->conn_c2s_ip_sum = 0;
579 			hnat_cksum_v6((uint16_t *)dst,
580 			    (uint16_t *)&info->nat_dst, dport,
581 			    info->nat_dport, &connp->conn_c2s_tp_sum);
582 			*ip_sum = 0;
583 			*tp_sum = connp->conn_c2s_tp_sum;
584 
585 			connp->conn_s2c_ip_sum = 0;
586 			hnat_cksum_v6((uint16_t *)&info->nat_dst,
587 			    (uint16_t *)dst, info->nat_dport, dport,
588 			    &connp->conn_s2c_tp_sum);
589 		}
590 		break;
591 	case ILB_TOPO_IMPL_NAT:
592 		connp->conn_s2c_saddr = info->nat_dst;
593 		connp->conn_s2c_sport = info->nat_dport;
594 		connp->conn_s2c_daddr = info->nat_src;
595 		connp->conn_s2c_dport = info->nat_sport;
596 
597 		if (rule->ir_ipver == IPPROTO_IP) {
598 			fnat_cksum_v4((uint16_t *)&src->s6_addr32[3],
599 			    (uint16_t *)&dst->s6_addr32[3],
600 			    (uint16_t *)&info->nat_src.s6_addr32[3],
601 			    (uint16_t *)&info->nat_dst.s6_addr32[3],
602 			    0, 0, 0, 0, &connp->conn_c2s_ip_sum);
603 			fnat_cksum_v4((uint16_t *)&src->s6_addr32[3],
604 			    (uint16_t *)&dst->s6_addr32[3],
605 			    (uint16_t *)&info->nat_src.s6_addr32[3],
606 			    (uint16_t *)&info->nat_dst.s6_addr32[3],
607 			    sport, dport, info->nat_sport,
608 			    info->nat_dport, &connp->conn_c2s_tp_sum);
609 			*ip_sum = connp->conn_c2s_ip_sum;
610 			*tp_sum = connp->conn_c2s_tp_sum;
611 
612 			fnat_cksum_v4(
613 			    (uint16_t *)&info->nat_src.s6_addr32[3],
614 			    (uint16_t *)&info->nat_dst.s6_addr32[3],
615 			    (uint16_t *)&src->s6_addr32[3],
616 			    (uint16_t *)&dst->s6_addr32[3],
617 			    0, 0, 0, 0, &connp->conn_s2c_ip_sum);
618 			fnat_cksum_v4(
619 			    (uint16_t *)&info->nat_src.s6_addr32[3],
620 			    (uint16_t *)&info->nat_dst.s6_addr32[3],
621 			    (uint16_t *)&src->s6_addr32[3],
622 			    (uint16_t *)&dst->s6_addr32[3],
623 			    info->nat_sport, info->nat_dport,
624 			    sport, dport, &connp->conn_s2c_tp_sum);
625 		} else {
626 			fnat_cksum_v6((uint16_t *)src, (uint16_t *)dst,
627 			    (uint16_t *)&info->nat_src,
628 			    (uint16_t *)&info->nat_dst,
629 			    sport, dport, info->nat_sport,
630 			    info->nat_dport, &connp->conn_c2s_tp_sum);
631 			connp->conn_c2s_ip_sum = 0;
632 			*ip_sum = 0;
633 			*tp_sum = connp->conn_c2s_tp_sum;
634 
635 			fnat_cksum_v6((uint16_t *)&info->nat_src,
636 			    (uint16_t *)&info->nat_dst, (uint16_t *)src,
637 			    (uint16_t *)dst, info->nat_sport,
638 			    info->nat_dport, sport, dport,
639 			    &connp->conn_s2c_tp_sum);
640 			connp->conn_s2c_ip_sum = 0;
641 		}
642 		break;
643 	}
644 
645 	connp->conn_s2c_atime = ddi_get_lbolt64();
646 	connp->conn_s2c_pkt_cnt = 1;
647 	connp->conn_s2c_tcp_fin_sent = B_FALSE;
648 	connp->conn_s2c_tcp_fin_acked = B_FALSE;
649 
650 	/* Add it to the s2c hash table. */
651 	hash = ilbs->ilbs_s2c_conn_hash;
652 	i = ILB_CONN_HASH((uint8_t *)&connp->conn_s2c_saddr.s6_addr32[3],
653 	    ntohs(connp->conn_s2c_sport),
654 	    (uint8_t *)&connp->conn_s2c_daddr.s6_addr32[3],
655 	    ntohs(connp->conn_s2c_dport), ilbs->ilbs_conn_hash_size);
656 	connp->conn_s2c_hash = &hash[i];
657 	DTRACE_PROBE2(ilb__conn__hash__add__s2c, ilb_conn_t *, connp, int, i);
658 
659 	mutex_enter(&hash[i].ilb_conn_hash_lock);
660 	hash[i].ilb_conn_cnt++;
661 	connp->conn_s2c_next = hash[i].ilb_connp;
662 	if (hash[i].ilb_connp != NULL)
663 		hash[i].ilb_connp->conn_s2c_prev = connp;
664 	connp->conn_s2c_prev = NULL;
665 	hash[i].ilb_connp = connp;
666 	mutex_exit(&hash[i].ilb_conn_hash_lock);
667 
668 	/* Add it to the c2s hash table. */
669 	hash = ilbs->ilbs_c2s_conn_hash;
670 	i = ILB_CONN_HASH((uint8_t *)&src->s6_addr32[3], ntohs(sport),
671 	    (uint8_t *)&dst->s6_addr32[3], ntohs(dport),
672 	    ilbs->ilbs_conn_hash_size);
673 	connp->conn_c2s_hash = &hash[i];
674 	DTRACE_PROBE2(ilb__conn__hash__add__c2s, ilb_conn_t *, connp, int, i);
675 
676 	mutex_enter(&hash[i].ilb_conn_hash_lock);
677 	hash[i].ilb_conn_cnt++;
678 	connp->conn_c2s_next = hash[i].ilb_connp;
679 	if (hash[i].ilb_connp != NULL)
680 		hash[i].ilb_connp->conn_c2s_prev = connp;
681 	connp->conn_c2s_prev = NULL;
682 	hash[i].ilb_connp = connp;
683 	mutex_exit(&hash[i].ilb_conn_hash_lock);
684 
685 	return (0);
686 }
687 
688 /*
689  * If a connection is using TCP, we keep track of simple TCP state transition
690  * so that we know when to clean up an entry.
691  */
692 static boolean_t
693 update_conn_tcp(ilb_conn_t *connp, void *iph, tcpha_t *tcpha, int32_t pkt_len,
694     boolean_t c2s)
695 {
696 	uint32_t ack, seq;
697 	int32_t seg_len;
698 
699 	if (tcpha->tha_flags & TH_RST)
700 		return (B_FALSE);
701 
702 	seg_len = pkt_len - ((uint8_t *)tcpha - (uint8_t *)iph) -
703 	    TCP_HDR_LENGTH((tcph_t *)tcpha);
704 
705 	if (tcpha->tha_flags & TH_ACK)
706 		ack = ntohl(tcpha->tha_ack);
707 	seq = ntohl(tcpha->tha_seq);
708 	if (c2s) {
709 		ASSERT(MUTEX_HELD(&connp->conn_c2s_hash->ilb_conn_hash_lock));
710 		if (tcpha->tha_flags & TH_FIN) {
711 			connp->conn_c2s_tcp_fss = seq + seg_len;
712 			connp->conn_c2s_tcp_fin_sent = B_TRUE;
713 		}
714 		connp->conn_c2s_tcp_ack = ack;
715 
716 		/* Port reuse by the client, restart the conn. */
717 		if (connp->conn_c2s_tcp_fin_sent &&
718 		    SEQ_GT(seq, connp->conn_c2s_tcp_fss + 1)) {
719 			connp->conn_c2s_tcp_fin_sent = B_FALSE;
720 			connp->conn_c2s_tcp_fin_acked = B_FALSE;
721 		}
722 	} else {
723 		ASSERT(MUTEX_HELD(&connp->conn_s2c_hash->ilb_conn_hash_lock));
724 		if (tcpha->tha_flags & TH_FIN) {
725 			connp->conn_s2c_tcp_fss = seq + seg_len;
726 			connp->conn_s2c_tcp_fin_sent = B_TRUE;
727 		}
728 		connp->conn_s2c_tcp_ack = ack;
729 
730 		/* Port reuse by the client, restart the conn. */
731 		if (connp->conn_s2c_tcp_fin_sent &&
732 		    SEQ_GT(seq, connp->conn_s2c_tcp_fss + 1)) {
733 			connp->conn_s2c_tcp_fin_sent = B_FALSE;
734 			connp->conn_s2c_tcp_fin_acked = B_FALSE;
735 		}
736 	}
737 
738 	return (B_TRUE);
739 }
740 
741 /*
742  * Helper routint to find conn hash entry given some packet information and
743  * the traffic direction (c2s, client to server?)
744  */
745 static boolean_t
746 ilb_find_conn(ilb_stack_t *ilbs, void *iph, void *tph, int l4, in6_addr_t *src,
747     in_port_t sport, in6_addr_t *dst, in_port_t dport,
748     ilb_rule_info_t *rule_cache, uint32_t *ip_sum, uint32_t *tp_sum,
749     int32_t pkt_len, boolean_t c2s)
750 {
751 	ilb_conn_hash_t *hash;
752 	uint_t i;
753 	ilb_conn_t *connp;
754 	boolean_t tcp_alive;
755 	boolean_t ret = B_FALSE;
756 
757 	i = ILB_CONN_HASH((uint8_t *)&src->s6_addr32[3], ntohs(sport),
758 	    (uint8_t *)&dst->s6_addr32[3], ntohs(dport),
759 	    ilbs->ilbs_conn_hash_size);
760 	if (c2s) {
761 		hash = ilbs->ilbs_c2s_conn_hash;
762 		mutex_enter(&hash[i].ilb_conn_hash_lock);
763 		for (connp = hash[i].ilb_connp; connp != NULL;
764 		    connp = connp->conn_c2s_next) {
765 			if (connp->conn_l4 == l4 &&
766 			    connp->conn_c2s_dport == dport &&
767 			    connp->conn_c2s_sport == sport &&
768 			    IN6_ARE_ADDR_EQUAL(src, &connp->conn_c2s_saddr) &&
769 			    IN6_ARE_ADDR_EQUAL(dst, &connp->conn_c2s_daddr)) {
770 				connp->conn_c2s_atime = ddi_get_lbolt64();
771 				connp->conn_c2s_pkt_cnt++;
772 				*rule_cache = connp->conn_rule_cache;
773 				*ip_sum = connp->conn_c2s_ip_sum;
774 				*tp_sum = connp->conn_c2s_tp_sum;
775 				ret = B_TRUE;
776 				break;
777 			}
778 		}
779 	} else {
780 		hash = ilbs->ilbs_s2c_conn_hash;
781 		mutex_enter(&hash[i].ilb_conn_hash_lock);
782 		for (connp = hash[i].ilb_connp; connp != NULL;
783 		    connp = connp->conn_s2c_next) {
784 			if (connp->conn_l4 == l4 &&
785 			    connp->conn_s2c_dport == dport &&
786 			    connp->conn_s2c_sport == sport &&
787 			    IN6_ARE_ADDR_EQUAL(src, &connp->conn_s2c_saddr) &&
788 			    IN6_ARE_ADDR_EQUAL(dst, &connp->conn_s2c_daddr)) {
789 				connp->conn_s2c_atime = ddi_get_lbolt64();
790 				connp->conn_s2c_pkt_cnt++;
791 				*rule_cache = connp->conn_rule_cache;
792 				*ip_sum = connp->conn_s2c_ip_sum;
793 				*tp_sum = connp->conn_s2c_tp_sum;
794 				ret = B_TRUE;
795 				break;
796 			}
797 		}
798 	}
799 	if (ret) {
800 		ILB_S_KSTAT(connp->conn_server, pkt_processed);
801 		ILB_S_KSTAT_UPDATE(connp->conn_server, bytes_processed,
802 		    pkt_len);
803 
804 		switch (l4) {
805 		case (IPPROTO_TCP):
806 			tcp_alive = update_conn_tcp(connp, iph, tph, pkt_len,
807 			    c2s);
808 			if (!tcp_alive) {
809 				connp->conn_gc = B_TRUE;
810 			}
811 			break;
812 		default:
813 			break;
814 		}
815 	}
816 	mutex_exit(&hash[i].ilb_conn_hash_lock);
817 
818 	return (ret);
819 }
820 
821 /*
822  * To check if a give packet matches an existing conn hash entry.  If it
823  * does, return the information about this entry so that the caller can
824  * do the proper NAT.
825  */
826 boolean_t
827 ilb_check_conn(ilb_stack_t *ilbs, int l3, void *iph, int l4, void *tph,
828     in6_addr_t *src, in6_addr_t *dst, in_port_t sport, in_port_t dport,
829     uint32_t pkt_len, in6_addr_t *lb_dst)
830 {
831 	ilb_rule_info_t rule_cache;
832 	uint32_t adj_ip_sum, adj_tp_sum;
833 	boolean_t ret;
834 
835 	/* Check the incoming hash table. */
836 	if (ilb_find_conn(ilbs, iph, tph, l4, src, sport, dst, dport,
837 	    &rule_cache, &adj_ip_sum, &adj_tp_sum, pkt_len, B_TRUE)) {
838 		switch (rule_cache.topo) {
839 		case ILB_TOPO_IMPL_NAT:
840 			*lb_dst = rule_cache.info.nat_dst;
841 			ilb_full_nat(l3, iph, l4, tph, &rule_cache.info,
842 			    adj_ip_sum, adj_tp_sum, B_TRUE);
843 			ret = B_TRUE;
844 			break;
845 		case ILB_TOPO_IMPL_HALF_NAT:
846 			*lb_dst = rule_cache.info.nat_dst;
847 			ilb_half_nat(l3, iph, l4, tph, &rule_cache.info,
848 			    adj_ip_sum, adj_tp_sum, B_TRUE);
849 			ret = B_TRUE;
850 			break;
851 		default:
852 			ret = B_FALSE;
853 			break;
854 		}
855 		return (ret);
856 	}
857 	if (ilb_find_conn(ilbs, iph, tph, l4, src, sport, dst, dport,
858 	    &rule_cache, &adj_ip_sum, &adj_tp_sum, pkt_len, B_FALSE)) {
859 		switch (rule_cache.topo) {
860 		case ILB_TOPO_IMPL_NAT:
861 			*lb_dst = rule_cache.info.src;
862 			ilb_full_nat(l3, iph, l4, tph, &rule_cache.info,
863 			    adj_ip_sum, adj_tp_sum, B_FALSE);
864 			ret = B_TRUE;
865 			break;
866 		case ILB_TOPO_IMPL_HALF_NAT:
867 			*lb_dst = *dst;
868 			ilb_half_nat(l3, iph, l4, tph, &rule_cache.info,
869 			    adj_ip_sum, adj_tp_sum, B_FALSE);
870 			ret = B_TRUE;
871 			break;
872 		default:
873 			ret = B_FALSE;
874 			break;
875 		}
876 		return (ret);
877 	}
878 
879 	return (B_FALSE);
880 }
881 
882 /*
883  * To check if an ICMP packet belongs to a connection in one of the conn
884  * hash entries.
885  */
886 boolean_t
887 ilb_check_icmp_conn(ilb_stack_t *ilbs, mblk_t *mp, int l3, void *out_iph,
888     void *icmph, in6_addr_t *lb_dst)
889 {
890 	ilb_conn_hash_t *hash;
891 	ipha_t *in_iph4;
892 	ip6_t *in_iph6;
893 	icmph_t *icmph4;
894 	icmp6_t *icmph6;
895 	in6_addr_t *in_src_p, *in_dst_p;
896 	in_port_t *sport, *dport;
897 	int l4;
898 	uint_t i;
899 	ilb_conn_t *connp;
900 	ilb_rule_info_t rule_cache;
901 	uint32_t adj_ip_sum;
902 	boolean_t full_nat;
903 
904 	if (l3 == IPPROTO_IP) {
905 		in6_addr_t in_src, in_dst;
906 
907 		icmph4 = (icmph_t *)icmph;
908 		in_iph4 = (ipha_t *)&icmph4[1];
909 
910 		if ((uint8_t *)in_iph4 + IPH_HDR_LENGTH(in_iph4) +
911 		    ICMP_MIN_TP_HDR_LEN > mp->b_wptr) {
912 			return (B_FALSE);
913 		}
914 
915 		IN6_IPADDR_TO_V4MAPPED(in_iph4->ipha_src, &in_src);
916 		in_src_p = &in_src;
917 		IN6_IPADDR_TO_V4MAPPED(in_iph4->ipha_dst, &in_dst);
918 		in_dst_p = &in_dst;
919 
920 		l4 = in_iph4->ipha_protocol;
921 		if (l4 != IPPROTO_TCP && l4 != IPPROTO_UDP)
922 			return (B_FALSE);
923 
924 		sport = (in_port_t *)((char *)in_iph4 +
925 		    IPH_HDR_LENGTH(in_iph4));
926 		dport = sport + 1;
927 
928 		DTRACE_PROBE4(ilb__chk__icmp__conn__v4, uint32_t,
929 		    in_iph4->ipha_src, uint32_t, in_iph4->ipha_dst, uint16_t,
930 		    ntohs(*sport), uint16_t, ntohs(*dport));
931 	} else {
932 		ASSERT(l3 == IPPROTO_IPV6);
933 
934 		icmph6 = (icmp6_t *)icmph;
935 		in_iph6 = (ip6_t *)&icmph6[1];
936 		in_src_p = &in_iph6->ip6_src;
937 		in_dst_p = &in_iph6->ip6_dst;
938 
939 		if ((uint8_t *)in_iph6 + sizeof (ip6_t) +
940 		    ICMP_MIN_TP_HDR_LEN > mp->b_wptr) {
941 			return (B_FALSE);
942 		}
943 
944 		l4 = in_iph6->ip6_nxt;
945 		/* We don't go deep inside an IPv6 packet yet. */
946 		if (l4 != IPPROTO_TCP && l4 != IPPROTO_UDP)
947 			return (B_FALSE);
948 
949 		sport = (in_port_t *)&in_iph6[1];
950 		dport = sport + 1;
951 
952 		DTRACE_PROBE4(ilb__chk__icmp__conn__v6, in6_addr_t *,
953 		    &in_iph6->ip6_src, in6_addr_t *, &in_iph6->ip6_dst,
954 		    uint16_t, ntohs(*sport), uint16_t, ntohs(*dport));
955 	}
956 
957 	i = ILB_CONN_HASH((uint8_t *)&in_dst_p->s6_addr32[3], ntohs(*dport),
958 	    (uint8_t *)&in_src_p->s6_addr32[3], ntohs(*sport),
959 	    ilbs->ilbs_conn_hash_size);
960 	hash = ilbs->ilbs_c2s_conn_hash;
961 
962 	mutex_enter(&hash[i].ilb_conn_hash_lock);
963 	for (connp = hash[i].ilb_connp; connp != NULL;
964 	    connp = connp->conn_c2s_next) {
965 		if (connp->conn_l4 == l4 &&
966 		    connp->conn_c2s_dport == *sport &&
967 		    connp->conn_c2s_sport == *dport &&
968 		    IN6_ARE_ADDR_EQUAL(in_dst_p, &connp->conn_c2s_saddr) &&
969 		    IN6_ARE_ADDR_EQUAL(in_src_p, &connp->conn_c2s_daddr)) {
970 			connp->conn_c2s_atime = ddi_get_lbolt64();
971 			connp->conn_c2s_pkt_cnt++;
972 			rule_cache = connp->conn_rule_cache;
973 			adj_ip_sum = connp->conn_c2s_ip_sum;
974 			break;
975 		}
976 	}
977 	mutex_exit(&hash[i].ilb_conn_hash_lock);
978 
979 	if (connp == NULL) {
980 		DTRACE_PROBE(ilb__chk__icmp__conn__failed);
981 		return (B_FALSE);
982 	}
983 
984 	switch (rule_cache.topo) {
985 	case ILB_TOPO_IMPL_NAT:
986 		full_nat = B_TRUE;
987 		break;
988 	case ILB_TOPO_IMPL_HALF_NAT:
989 		full_nat = B_FALSE;
990 		break;
991 	default:
992 		return (B_FALSE);
993 	}
994 
995 	*lb_dst = rule_cache.info.nat_dst;
996 	if (l3 == IPPROTO_IP) {
997 		ilb_nat_icmpv4(mp, out_iph, icmph4, in_iph4, sport, dport,
998 		    &rule_cache.info, adj_ip_sum, full_nat);
999 	} else {
1000 		ilb_nat_icmpv6(mp, out_iph, icmph6, in_iph6, sport, dport,
1001 		    &rule_cache.info, full_nat);
1002 	}
1003 	return (B_TRUE);
1004 }
1005 
1006 /*
1007  * This routine sends up the conn hash table to user land.  Note that the
1008  * request is an ioctl, hence we cannot really differentiate requests
1009  * from different clients.  There is no context shared between different
1010  * ioctls.  Here we make the assumption that the user land ilbd will
1011  * only allow one client to show the conn hash table at any time.
1012  * Otherwise, the results will be "very" inconsistent.
1013  *
1014  * In each ioctl, a flag (ILB_LIST_BEGIN) indicates whether the client wants
1015  * to read from the beginning of the able.  After a certain entries
1016  * are reported, the kernel remembers the position of the last returned
1017  * entry.  When the next ioctl comes in with the ILB_LIST_BEGIN flag,
1018  * it will return entries starting from where it was left off.  When
1019  * the end of table is reached, a flag (ILB_LIST_END) is set to tell
1020  * the client that there is no more entry.
1021  *
1022  * It is assumed that the caller has checked the size of nat so that it
1023  * can hold num entries.
1024  */
1025 /* ARGSUSED */
1026 int
1027 ilb_list_nat(ilb_stack_t *ilbs, zoneid_t zoneid, ilb_nat_entry_t *nat,
1028     uint32_t *num, uint32_t *flags)
1029 {
1030 	ilb_conn_hash_t *hash;
1031 	ilb_conn_t *cur_connp;
1032 	uint32_t i, j;
1033 	int ret = 0;
1034 
1035 	mutex_enter(&ilbs->ilbs_conn_list_lock);
1036 	while (ilbs->ilbs_conn_list_busy) {
1037 		if (cv_wait_sig(&ilbs->ilbs_conn_list_cv,
1038 		    &ilbs->ilbs_conn_list_lock) == 0) {
1039 			mutex_exit(&ilbs->ilbs_conn_list_lock);
1040 			return (EINTR);
1041 		}
1042 	}
1043 	if ((hash = ilbs->ilbs_c2s_conn_hash) == NULL) {
1044 		ASSERT(ilbs->ilbs_s2c_conn_hash == NULL);
1045 		mutex_exit(&ilbs->ilbs_conn_list_lock);
1046 		*num = 0;
1047 		*flags |= ILB_LIST_END;
1048 		return (0);
1049 	}
1050 	ilbs->ilbs_conn_list_busy = B_TRUE;
1051 	mutex_exit(&ilbs->ilbs_conn_list_lock);
1052 
1053 	if (*flags & ILB_LIST_BEGIN) {
1054 		i = 0;
1055 		mutex_enter(&hash[0].ilb_conn_hash_lock);
1056 		cur_connp = hash[0].ilb_connp;
1057 	} else if (*flags & ILB_LIST_CONT) {
1058 		if (ilbs->ilbs_conn_list_cur == ilbs->ilbs_conn_hash_size) {
1059 			*num = 0;
1060 			*flags |= ILB_LIST_END;
1061 			goto done;
1062 		}
1063 		i = ilbs->ilbs_conn_list_cur;
1064 		mutex_enter(&hash[i].ilb_conn_hash_lock);
1065 		cur_connp = ilbs->ilbs_conn_list_connp;
1066 	} else {
1067 		ret = EINVAL;
1068 		goto done;
1069 	}
1070 
1071 	j = 0;
1072 	while (j < *num) {
1073 		if (cur_connp == NULL) {
1074 			mutex_exit(&hash[i].ilb_conn_hash_lock);
1075 			if (++i == ilbs->ilbs_conn_hash_size) {
1076 				*flags |= ILB_LIST_END;
1077 				break;
1078 			}
1079 			mutex_enter(&hash[i].ilb_conn_hash_lock);
1080 			cur_connp = hash[i].ilb_connp;
1081 			continue;
1082 		}
1083 		nat[j].proto = cur_connp->conn_l4;
1084 
1085 		nat[j].in_global = cur_connp->conn_c2s_daddr;
1086 		nat[j].in_global_port = cur_connp->conn_c2s_dport;
1087 		nat[j].out_global = cur_connp->conn_c2s_saddr;
1088 		nat[j].out_global_port = cur_connp->conn_c2s_sport;
1089 
1090 		nat[j].in_local = cur_connp->conn_s2c_saddr;
1091 		nat[j].in_local_port = cur_connp->conn_s2c_sport;
1092 		nat[j].out_local = cur_connp->conn_s2c_daddr;
1093 		nat[j].out_local_port = cur_connp->conn_s2c_dport;
1094 
1095 		nat[j].create_time = TICK_TO_MSEC(cur_connp->conn_cr_time);
1096 		nat[j].last_access_time =
1097 		    TICK_TO_MSEC(cur_connp->conn_c2s_atime);
1098 
1099 		/*
1100 		 * The conn_s2c_pkt_cnt may not be accurate since we are not
1101 		 * holding the s2c hash lock.
1102 		 */
1103 		nat[j].pkt_cnt = cur_connp->conn_c2s_pkt_cnt +
1104 		    cur_connp->conn_s2c_pkt_cnt;
1105 		j++;
1106 
1107 		cur_connp = cur_connp->conn_c2s_next;
1108 	}
1109 	ilbs->ilbs_conn_list_connp = cur_connp;
1110 	if (j == *num)
1111 		mutex_exit(&hash[i].ilb_conn_hash_lock);
1112 
1113 	ilbs->ilbs_conn_list_cur = i;
1114 
1115 	*num = j;
1116 done:
1117 	mutex_enter(&ilbs->ilbs_conn_list_lock);
1118 	ilbs->ilbs_conn_list_busy = B_FALSE;
1119 	cv_signal(&ilbs->ilbs_conn_list_cv);
1120 	mutex_exit(&ilbs->ilbs_conn_list_lock);
1121 
1122 	return (ret);
1123 }
1124 
1125 
1126 /*
1127  * Stickiness (persistence) handling routines.
1128  */
1129 
1130 
1131 static void
1132 ilb_sticky_cache_init(void)
1133 {
1134 	ilb_sticky_cache = kmem_cache_create("ilb_sticky_cache",
1135 	    sizeof (ilb_sticky_t), 0, NULL, NULL, NULL, NULL, NULL,
1136 	    ilb_kmem_flags);
1137 }
1138 
1139 void
1140 ilb_sticky_cache_fini(void)
1141 {
1142 	if (ilb_sticky_cache != NULL) {
1143 		kmem_cache_destroy(ilb_sticky_cache);
1144 		ilb_sticky_cache = NULL;
1145 	}
1146 }
1147 
1148 void
1149 ilb_sticky_refrele(ilb_sticky_t *s)
1150 {
1151 	ILB_STICKY_REFRELE(s);
1152 }
1153 
1154 static ilb_sticky_t *
1155 ilb_sticky_lookup(ilb_sticky_hash_t *hash, ilb_rule_t *rule, in6_addr_t *src)
1156 {
1157 	ilb_sticky_t *s;
1158 
1159 	ASSERT(mutex_owned(&hash->sticky_lock));
1160 
1161 	for (s = list_head(&hash->sticky_head); s != NULL;
1162 	    s = list_next(&hash->sticky_head, s)) {
1163 		if (s->rule_instance == rule->ir_ks_instance) {
1164 			if (IN6_ARE_ADDR_EQUAL(src, &s->src))
1165 				return (s);
1166 		}
1167 	}
1168 	return (NULL);
1169 }
1170 
1171 static ilb_sticky_t *
1172 ilb_sticky_add(ilb_sticky_hash_t *hash, ilb_rule_t *rule, ilb_server_t *server,
1173     in6_addr_t *src)
1174 {
1175 	ilb_sticky_t *s;
1176 
1177 	ASSERT(mutex_owned(&hash->sticky_lock));
1178 
1179 	if ((s = kmem_cache_alloc(ilb_sticky_cache, KM_NOSLEEP)) == NULL)
1180 		return (NULL);
1181 
1182 	/*
1183 	 * The rule instance is for handling the scenario when the same
1184 	 * client talks to different rules at the same time.  Stickiness
1185 	 * is per rule so we can use the rule instance to differentiate
1186 	 * the client's request.
1187 	 */
1188 	s->rule_instance = rule->ir_ks_instance;
1189 	/*
1190 	 * Copy the rule name for listing all sticky cache entry.  ir_name
1191 	 * is guaranteed to be NULL terminated.
1192 	 */
1193 	(void) strcpy(s->rule_name, rule->ir_name);
1194 	s->server = server;
1195 
1196 	/*
1197 	 * Grab a ref cnt on the server so that it won't go away while
1198 	 * it is still in the sticky table.
1199 	 */
1200 	ILB_SERVER_REFHOLD(server);
1201 	s->src = *src;
1202 	s->expiry = rule->ir_sticky_expiry;
1203 	s->refcnt = 1;
1204 	s->hash = hash;
1205 
1206 	/*
1207 	 * There is no need to set atime here since the refcnt is not
1208 	 * zero.  A sticky entry is removed only when the refcnt is
1209 	 * zero.  But just set it here for debugging purpose.  The
1210 	 * atime is set when a refrele is done on a sticky entry.
1211 	 */
1212 	s->atime = ddi_get_lbolt64();
1213 
1214 	list_insert_head(&hash->sticky_head, s);
1215 	hash->sticky_cnt++;
1216 	return (s);
1217 }
1218 
1219 /*
1220  * This routine checks if there is an existing sticky entry which matches
1221  * a given packet.  If there is one, return it.  If there is not, create
1222  * a sticky entry using the packet's info.
1223  */
1224 ilb_server_t *
1225 ilb_sticky_find_add(ilb_stack_t *ilbs, ilb_rule_t *rule, in6_addr_t *src,
1226     ilb_server_t *server, ilb_sticky_t **res, uint16_t *src_ent_idx)
1227 {
1228 	int i;
1229 	ilb_sticky_hash_t *hash;
1230 	ilb_sticky_t *s;
1231 
1232 	ASSERT(server != NULL);
1233 
1234 	*res = NULL;
1235 
1236 	i = ILB_STICKY_HASH((uint8_t *)&src->s6_addr32[3],
1237 	    (uint32_t)(uintptr_t)rule, ilbs->ilbs_sticky_hash_size);
1238 	hash = &ilbs->ilbs_sticky_hash[i];
1239 
1240 	/* First check if there is already an entry. */
1241 	mutex_enter(&hash->sticky_lock);
1242 	s = ilb_sticky_lookup(hash, rule, src);
1243 
1244 	/* No sticky entry, add one. */
1245 	if (s == NULL) {
1246 add_new_entry:
1247 		s = ilb_sticky_add(hash, rule, server, src);
1248 		if (s == NULL) {
1249 			mutex_exit(&hash->sticky_lock);
1250 			return (NULL);
1251 		}
1252 		/*
1253 		 * Find a source for this server.  All subseqent requests from
1254 		 * the same client matching this sticky entry will use this
1255 		 * source address in doing NAT.  The current algorithm is
1256 		 * simple, rotate the source address.  Note that the
1257 		 * source address array does not change after it's created, so
1258 		 * it is OK to just increment the cur index.
1259 		 */
1260 		if (server->iser_nat_src != NULL) {
1261 			/* It is a hint, does not need to be atomic. */
1262 			*src_ent_idx = (server->iser_nat_src->cur++ %
1263 			    server->iser_nat_src->num_src);
1264 			s->nat_src_idx = *src_ent_idx;
1265 		}
1266 		mutex_exit(&hash->sticky_lock);
1267 		*res = s;
1268 		return (server);
1269 	}
1270 
1271 	/*
1272 	 * We don't hold any lock accessing iser_enabled.  Refer to the
1273 	 * comment in ilb_server_add() about iser_lock.
1274 	 */
1275 	if (!s->server->iser_enabled) {
1276 		/*
1277 		 * s->server == server can only happen if there is a race in
1278 		 * toggling the iser_enabled flag (we don't hold a lock doing
1279 		 * that) so that the load balance algorithm still returns a
1280 		 * disabled server.  In this case, just drop the packet...
1281 		 */
1282 		if (s->server == server) {
1283 			mutex_exit(&hash->sticky_lock);
1284 			return (NULL);
1285 		}
1286 
1287 		/*
1288 		 * The old server is disabled and there is a new server, use
1289 		 * the new one to create a sticky entry.  Since we will
1290 		 * add the entry at the beginning, subsequent lookup will
1291 		 * find this new entry instead of the old one.
1292 		 */
1293 		goto add_new_entry;
1294 	}
1295 
1296 	s->refcnt++;
1297 	*res = s;
1298 	mutex_exit(&hash->sticky_lock);
1299 	if (server->iser_nat_src != NULL)
1300 		*src_ent_idx = s->nat_src_idx;
1301 	return (s->server);
1302 }
1303 
1304 static void
1305 ilb_sticky_cleanup(void *arg)
1306 {
1307 	ilb_timer_t *timer = (ilb_timer_t *)arg;
1308 	uint32_t i;
1309 	ilb_stack_t *ilbs;
1310 	ilb_sticky_hash_t *hash;
1311 	ilb_sticky_t *s, *nxt_s;
1312 	int64_t now, expiry;
1313 
1314 	ilbs = timer->ilbs;
1315 	hash = ilbs->ilbs_sticky_hash;
1316 	ASSERT(hash != NULL);
1317 
1318 	now = ddi_get_lbolt64();
1319 	for (i = timer->start; i < timer->end; i++) {
1320 		mutex_enter(&hash[i].sticky_lock);
1321 		for (s = list_head(&hash[i].sticky_head); s != NULL;
1322 		    s = nxt_s) {
1323 			nxt_s = list_next(&hash[i].sticky_head, s);
1324 			if (s->refcnt != 0)
1325 				continue;
1326 			expiry = now - SEC_TO_TICK(s->expiry);
1327 			if (s->atime < expiry) {
1328 				ILB_SERVER_REFRELE(s->server);
1329 				list_remove(&hash[i].sticky_head, s);
1330 				kmem_cache_free(ilb_sticky_cache, s);
1331 				hash[i].sticky_cnt--;
1332 			}
1333 		}
1334 		mutex_exit(&hash[i].sticky_lock);
1335 	}
1336 }
1337 
1338 static void
1339 ilb_sticky_timer(void *arg)
1340 {
1341 	ilb_timer_t *timer = (ilb_timer_t *)arg;
1342 
1343 	(void) taskq_dispatch(timer->ilbs->ilbs_sticky_taskq,
1344 	    ilb_sticky_cleanup, arg, TQ_SLEEP);
1345 	mutex_enter(&timer->tid_lock);
1346 	if (timer->tid == 0) {
1347 		mutex_exit(&timer->tid_lock);
1348 	} else {
1349 		timer->tid = timeout(ilb_sticky_timer, arg,
1350 		    SEC_TO_TICK(ilb_sticky_timeout));
1351 		mutex_exit(&timer->tid_lock);
1352 	}
1353 }
1354 
1355 void
1356 ilb_sticky_hash_init(ilb_stack_t *ilbs)
1357 {
1358 	extern pri_t minclsyspri;
1359 	int i, part;
1360 	char tq_name[TASKQ_NAMELEN];
1361 	ilb_timer_t *tm;
1362 
1363 	if (!ISP2(ilbs->ilbs_sticky_hash_size)) {
1364 		for (i = 0; i < 31; i++) {
1365 			if (ilbs->ilbs_sticky_hash_size < (1 << i))
1366 				break;
1367 		}
1368 		ilbs->ilbs_sticky_hash_size = 1 << i;
1369 	}
1370 
1371 	ilbs->ilbs_sticky_hash = kmem_zalloc(sizeof (ilb_sticky_hash_t) *
1372 	    ilbs->ilbs_sticky_hash_size, KM_SLEEP);
1373 	for (i = 0; i < ilbs->ilbs_sticky_hash_size; i++) {
1374 		mutex_init(&ilbs->ilbs_sticky_hash[i].sticky_lock, NULL,
1375 		    MUTEX_DEFAULT, NULL);
1376 		list_create(&ilbs->ilbs_sticky_hash[i].sticky_head,
1377 		    sizeof (ilb_sticky_t),
1378 		    offsetof(ilb_sticky_t, list));
1379 	}
1380 
1381 	if (ilb_sticky_cache == NULL)
1382 		ilb_sticky_cache_init();
1383 
1384 	(void) snprintf(tq_name, sizeof (tq_name), "ilb_sticky_taskq_%p",
1385 	    (void *)ilbs->ilbs_netstack);
1386 	ASSERT(ilbs->ilbs_sticky_taskq == NULL);
1387 	ilbs->ilbs_sticky_taskq = taskq_create(tq_name,
1388 	    ilb_sticky_timer_size * 2, minclsyspri, ilb_sticky_timer_size,
1389 	    ilb_sticky_timer_size * 2, TASKQ_PREPOPULATE|TASKQ_DYNAMIC);
1390 
1391 	ASSERT(ilbs->ilbs_sticky_timer_list == NULL);
1392 	ilbs->ilbs_sticky_timer_list = kmem_zalloc(sizeof (ilb_timer_t) *
1393 	    ilb_sticky_timer_size, KM_SLEEP);
1394 	part = ilbs->ilbs_sticky_hash_size / ilb_sticky_timer_size + 1;
1395 	for (i = 0; i < ilb_sticky_timer_size; i++) {
1396 		tm = ilbs->ilbs_sticky_timer_list + i;
1397 		tm->start = i * part;
1398 		tm->end = i * part + part;
1399 		if (tm->end > ilbs->ilbs_sticky_hash_size)
1400 			tm->end = ilbs->ilbs_sticky_hash_size;
1401 		tm->ilbs = ilbs;
1402 		mutex_init(&tm->tid_lock, NULL, MUTEX_DEFAULT, NULL);
1403 		/* Spread out the starting execution time of all the timers. */
1404 		tm->tid = timeout(ilb_sticky_timer, tm,
1405 		    SEC_TO_TICK(ilb_sticky_timeout + i));
1406 	}
1407 }
1408 
1409 void
1410 ilb_sticky_hash_fini(ilb_stack_t *ilbs)
1411 {
1412 	int i;
1413 	ilb_sticky_t *s;
1414 
1415 	if (ilbs->ilbs_sticky_hash == NULL)
1416 		return;
1417 
1418 	/* Stop all the timers first. */
1419 	for (i = 0; i < ilb_sticky_timer_size; i++) {
1420 		timeout_id_t tid;
1421 
1422 		/* Setting tid to 0 tells the timer handler not to restart. */
1423 		mutex_enter(&ilbs->ilbs_sticky_timer_list[i].tid_lock);
1424 		tid = ilbs->ilbs_sticky_timer_list[i].tid;
1425 		ilbs->ilbs_sticky_timer_list[i].tid = 0;
1426 		mutex_exit(&ilbs->ilbs_sticky_timer_list[i].tid_lock);
1427 		(void) untimeout(tid);
1428 	}
1429 	kmem_free(ilbs->ilbs_sticky_timer_list, sizeof (ilb_timer_t) *
1430 	    ilb_sticky_timer_size);
1431 	taskq_destroy(ilbs->ilbs_sticky_taskq);
1432 	ilbs->ilbs_sticky_taskq = NULL;
1433 
1434 	for (i = 0; i < ilbs->ilbs_sticky_hash_size; i++) {
1435 		while ((s = list_head(&ilbs->ilbs_sticky_hash[i].sticky_head))
1436 		    != NULL) {
1437 			list_remove(&ilbs->ilbs_sticky_hash[i].sticky_head, s);
1438 			ILB_SERVER_REFRELE(s->server);
1439 			kmem_free(s, sizeof (ilb_sticky_t));
1440 		}
1441 	}
1442 	kmem_free(ilbs->ilbs_sticky_hash, ilbs->ilbs_sticky_hash_size *
1443 	    sizeof (ilb_sticky_hash_t));
1444 }
1445 
1446 /*
1447  * This routine sends up the sticky hash table to user land.  Refer to
1448  * the comments before ilb_list_nat().  Both routines assume similar
1449  * conditions.
1450  *
1451  * It is assumed that the caller has checked the size of st so that it
1452  * can hold num entries.
1453  */
1454 /* ARGSUSED */
1455 int
1456 ilb_list_sticky(ilb_stack_t *ilbs, zoneid_t zoneid, ilb_sticky_entry_t *st,
1457     uint32_t *num, uint32_t *flags)
1458 {
1459 	ilb_sticky_hash_t *hash;
1460 	ilb_sticky_t *curp;
1461 	uint32_t i, j;
1462 	int ret = 0;
1463 
1464 	mutex_enter(&ilbs->ilbs_sticky_list_lock);
1465 	while (ilbs->ilbs_sticky_list_busy) {
1466 		if (cv_wait_sig(&ilbs->ilbs_sticky_list_cv,
1467 		    &ilbs->ilbs_sticky_list_lock) == 0) {
1468 			mutex_exit(&ilbs->ilbs_sticky_list_lock);
1469 			return (EINTR);
1470 		}
1471 	}
1472 	if ((hash = ilbs->ilbs_sticky_hash) == NULL) {
1473 		mutex_exit(&ilbs->ilbs_sticky_list_lock);
1474 		*num = 0;
1475 		*flags |= ILB_LIST_END;
1476 		return (0);
1477 	}
1478 	ilbs->ilbs_sticky_list_busy = B_TRUE;
1479 	mutex_exit(&ilbs->ilbs_sticky_list_lock);
1480 
1481 	if (*flags & ILB_LIST_BEGIN) {
1482 		i = 0;
1483 		mutex_enter(&hash[0].sticky_lock);
1484 		curp = list_head(&hash[0].sticky_head);
1485 	} else if (*flags & ILB_LIST_CONT) {
1486 		if (ilbs->ilbs_sticky_list_cur == ilbs->ilbs_sticky_hash_size) {
1487 			*num = 0;
1488 			*flags |= ILB_LIST_END;
1489 			goto done;
1490 		}
1491 		i = ilbs->ilbs_sticky_list_cur;
1492 		mutex_enter(&hash[i].sticky_lock);
1493 		curp = ilbs->ilbs_sticky_list_curp;
1494 	} else {
1495 		ret = EINVAL;
1496 		goto done;
1497 	}
1498 
1499 	j = 0;
1500 	while (j < *num) {
1501 		if (curp == NULL) {
1502 			mutex_exit(&hash[i].sticky_lock);
1503 			if (++i == ilbs->ilbs_sticky_hash_size) {
1504 				*flags |= ILB_LIST_END;
1505 				break;
1506 			}
1507 			mutex_enter(&hash[i].sticky_lock);
1508 			curp = list_head(&hash[i].sticky_head);
1509 			continue;
1510 		}
1511 		(void) strcpy(st[j].rule_name, curp->rule_name);
1512 		st[j].req_addr = curp->src;
1513 		st[j].srv_addr = curp->server->iser_addr_v6;
1514 		st[j].expiry_time = TICK_TO_MSEC(curp->expiry);
1515 		j++;
1516 		curp = list_next(&hash[i].sticky_head, curp);
1517 	}
1518 	ilbs->ilbs_sticky_list_curp = curp;
1519 	if (j == *num)
1520 		mutex_exit(&hash[i].sticky_lock);
1521 
1522 	ilbs->ilbs_sticky_list_cur = i;
1523 
1524 	*num = j;
1525 done:
1526 	mutex_enter(&ilbs->ilbs_sticky_list_lock);
1527 	ilbs->ilbs_sticky_list_busy = B_FALSE;
1528 	cv_signal(&ilbs->ilbs_sticky_list_cv);
1529 	mutex_exit(&ilbs->ilbs_sticky_list_lock);
1530 
1531 	return (ret);
1532 }
1533