xref: /onnv-gate/usr/src/uts/common/inet/ilb/ilb_nat.c (revision 11772:8f6362384ccb)
110946SSangeeta.Misra@Sun.COM /*
210946SSangeeta.Misra@Sun.COM  * CDDL HEADER START
310946SSangeeta.Misra@Sun.COM  *
410946SSangeeta.Misra@Sun.COM  * The contents of this file are subject to the terms of the
510946SSangeeta.Misra@Sun.COM  * Common Development and Distribution License (the "License").
610946SSangeeta.Misra@Sun.COM  * You may not use this file except in compliance with the License.
710946SSangeeta.Misra@Sun.COM  *
810946SSangeeta.Misra@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910946SSangeeta.Misra@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010946SSangeeta.Misra@Sun.COM  * See the License for the specific language governing permissions
1110946SSangeeta.Misra@Sun.COM  * and limitations under the License.
1210946SSangeeta.Misra@Sun.COM  *
1310946SSangeeta.Misra@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410946SSangeeta.Misra@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510946SSangeeta.Misra@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610946SSangeeta.Misra@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710946SSangeeta.Misra@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810946SSangeeta.Misra@Sun.COM  *
1910946SSangeeta.Misra@Sun.COM  * CDDL HEADER END
2010946SSangeeta.Misra@Sun.COM  */
2110946SSangeeta.Misra@Sun.COM 
2210946SSangeeta.Misra@Sun.COM /*
23*11772SKacheong.Poon@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2410946SSangeeta.Misra@Sun.COM  * Use is subject to license terms.
2510946SSangeeta.Misra@Sun.COM  */
2610946SSangeeta.Misra@Sun.COM 
2710946SSangeeta.Misra@Sun.COM #include <sys/types.h>
2810946SSangeeta.Misra@Sun.COM #include <sys/cmn_err.h>
2910946SSangeeta.Misra@Sun.COM #include <sys/crc32.h>
3010946SSangeeta.Misra@Sun.COM #include <netinet/in.h>
3110946SSangeeta.Misra@Sun.COM #include <inet/ip.h>
3210946SSangeeta.Misra@Sun.COM #include <inet/ip6.h>
3310946SSangeeta.Misra@Sun.COM #include <inet/tcp.h>
3410946SSangeeta.Misra@Sun.COM #include <inet/udp_impl.h>
3510946SSangeeta.Misra@Sun.COM #include <inet/ilb.h>
3610946SSangeeta.Misra@Sun.COM 
3710946SSangeeta.Misra@Sun.COM #include "ilb_impl.h"
3810946SSangeeta.Misra@Sun.COM #include "ilb_stack.h"
3910946SSangeeta.Misra@Sun.COM #include "ilb_nat.h"
4010946SSangeeta.Misra@Sun.COM 
4110946SSangeeta.Misra@Sun.COM /*
4210946SSangeeta.Misra@Sun.COM  * NAT source entry garbarge collection timeout.  The actual timeout value
4310946SSangeeta.Misra@Sun.COM  * includes a random jitter bounded by the ILB_NAT_SRC_TIMEOUT_JITTER.
4410946SSangeeta.Misra@Sun.COM  */
4510946SSangeeta.Misra@Sun.COM #define	ILB_NAT_SRC_TIMEOUT		30
4610946SSangeeta.Misra@Sun.COM #define	ILB_NAT_SRC_TIMEOUT_JITTER	5
4710946SSangeeta.Misra@Sun.COM 
4810946SSangeeta.Misra@Sun.COM /* key1/2 are assumed to be uint32_t. */
4910946SSangeeta.Misra@Sun.COM #define	ILB_NAT_SRC_HASH(hash, key1, key2, hash_size)			\
5010946SSangeeta.Misra@Sun.COM {									\
5110946SSangeeta.Misra@Sun.COM 	CRC32((hash), (key1), sizeof (uint32_t), -1U, crc32_table);	\
5210946SSangeeta.Misra@Sun.COM 	CRC32((hash), (key2), sizeof (uint32_t), (hash), crc32_table);	\
5310946SSangeeta.Misra@Sun.COM 	(hash) %= (hash_size);						\
5410946SSangeeta.Misra@Sun.COM }
5510946SSangeeta.Misra@Sun.COM 
5610946SSangeeta.Misra@Sun.COM /* NAT source port space instance number.  */
5710946SSangeeta.Misra@Sun.COM static uint32_t ilb_nat_src_instance = 0;
5810946SSangeeta.Misra@Sun.COM 
5910946SSangeeta.Misra@Sun.COM static void
incr_addr(in6_addr_t * a)6010946SSangeeta.Misra@Sun.COM incr_addr(in6_addr_t *a)
6110946SSangeeta.Misra@Sun.COM {
6210946SSangeeta.Misra@Sun.COM 	uint32_t i;
6310946SSangeeta.Misra@Sun.COM 
6410946SSangeeta.Misra@Sun.COM 	i = ntohl(a->s6_addr32[3]);
6510946SSangeeta.Misra@Sun.COM 	if (IN6_IS_ADDR_V4MAPPED(a)) {
6610946SSangeeta.Misra@Sun.COM 		a->s6_addr32[3] = htonl(++i);
6710946SSangeeta.Misra@Sun.COM 		ASSERT(i != 0);
6810946SSangeeta.Misra@Sun.COM 		return;
6910946SSangeeta.Misra@Sun.COM 	}
7010946SSangeeta.Misra@Sun.COM 
7110946SSangeeta.Misra@Sun.COM 	if (++i != 0) {
7210946SSangeeta.Misra@Sun.COM 		a->s6_addr32[3] = htonl(i);
7310946SSangeeta.Misra@Sun.COM 		return;
7410946SSangeeta.Misra@Sun.COM 	}
7510946SSangeeta.Misra@Sun.COM 	a->s6_addr32[3] = 0;
7610946SSangeeta.Misra@Sun.COM 	i = ntohl(a->s6_addr[2]);
7710946SSangeeta.Misra@Sun.COM 	if (++i != 0) {
7810946SSangeeta.Misra@Sun.COM 		a->s6_addr32[2] = htonl(i);
7910946SSangeeta.Misra@Sun.COM 		return;
8010946SSangeeta.Misra@Sun.COM 	}
8110946SSangeeta.Misra@Sun.COM 	a->s6_addr32[2] = 0;
8210946SSangeeta.Misra@Sun.COM 	i = ntohl(a->s6_addr[1]);
8310946SSangeeta.Misra@Sun.COM 	if (++i != 0) {
8410946SSangeeta.Misra@Sun.COM 		a->s6_addr32[1] = htonl(i);
8510946SSangeeta.Misra@Sun.COM 		return;
8610946SSangeeta.Misra@Sun.COM 	}
8710946SSangeeta.Misra@Sun.COM 	a->s6_addr32[1] = 0;
8810946SSangeeta.Misra@Sun.COM 	i = ntohl(a->s6_addr[0]);
8910946SSangeeta.Misra@Sun.COM 	a->s6_addr[0] = htonl(++i);
9010946SSangeeta.Misra@Sun.COM 	ASSERT(i != 0);
9110946SSangeeta.Misra@Sun.COM }
9210946SSangeeta.Misra@Sun.COM 
9310946SSangeeta.Misra@Sun.COM /*
9410946SSangeeta.Misra@Sun.COM  * When ILB does full NAT, it first picks one source address from the rule's
9510946SSangeeta.Misra@Sun.COM  * specified NAT source address list (currently done in round robin fashion).
9610946SSangeeta.Misra@Sun.COM  * Then it needs to allocate a port.  This source port must make the tuple
9710946SSangeeta.Misra@Sun.COM  * (source address:source port:destination address:destination port)
9810946SSangeeta.Misra@Sun.COM  * unique.  The destination part of the tuple is determined by the back
9910946SSangeeta.Misra@Sun.COM  * end server, and could not be changed.
10010946SSangeeta.Misra@Sun.COM  *
10110946SSangeeta.Misra@Sun.COM  * To handle the above source port number allocation, ILB sets up a table
10210946SSangeeta.Misra@Sun.COM  * of entries identified by source address:back end server address:server port
10310946SSangeeta.Misra@Sun.COM  * tuple.  This table is used by all rules for NAT source port allocation.
10410946SSangeeta.Misra@Sun.COM  * Each tuple has an associated vmem arena used for managing the NAT source
10510946SSangeeta.Misra@Sun.COM  * port space between the source address and back end server address/port.
10610946SSangeeta.Misra@Sun.COM  * Each back end server (ilb_server_t) has an array of pointers (iser_nat_src)
10710946SSangeeta.Misra@Sun.COM  * to the different entries in this table for NAT source port allocation.
10810946SSangeeta.Misra@Sun.COM  * When ILB needs to allocate a NAT source address and port to talk to a back
10910946SSangeeta.Misra@Sun.COM  * end server, it picks a source address  and uses the array pointer to get
11010946SSangeeta.Misra@Sun.COM  * to an entry.  Then it calls vmem_alloc() on the associated vmem arena to
11110946SSangeeta.Misra@Sun.COM  * find an unused port.
11210946SSangeeta.Misra@Sun.COM  *
11310946SSangeeta.Misra@Sun.COM  * When a back end server is added, ILB sets up the aforementioned array.
11410946SSangeeta.Misra@Sun.COM  * For each source address specified in the rule, ILB checks if there is any
11510946SSangeeta.Misra@Sun.COM  * existing entry which matches this source address:back end server address:
11610946SSangeeta.Misra@Sun.COM  * port tuple.  The server port is either a specific port or 0 (meaning wild
11710946SSangeeta.Misra@Sun.COM  * card port).  Normally, a back end server uses the same port as in the rule.
11810946SSangeeta.Misra@Sun.COM  * If a back end server is used to serve two different rules, there will be
11910946SSangeeta.Misra@Sun.COM  * two different ports.  Source port allocation for these two rules do not
12010946SSangeeta.Misra@Sun.COM  * conflict, hence we can use two vmem arenas (two different entries in the
12110946SSangeeta.Misra@Sun.COM  * table).  But if a server uses port range in one rule, we will treat it as
12210946SSangeeta.Misra@Sun.COM  * a wild card port.  Wild card poart matches with any port.  If this server
12310946SSangeeta.Misra@Sun.COM  * is used to serve more than one rules and those rules use the same set of
12410946SSangeeta.Misra@Sun.COM  * NAT source addresses, this means that they must share the same set of vmem
12510946SSangeeta.Misra@Sun.COM  * arenas (source port spaces).  We do this for simplicity reason.  If not,
12610946SSangeeta.Misra@Sun.COM  * we need to partition the port range so that we can identify different forms
12710946SSangeeta.Misra@Sun.COM  * of source port number collision.
12810946SSangeeta.Misra@Sun.COM  */
12910946SSangeeta.Misra@Sun.COM 
13010946SSangeeta.Misra@Sun.COM /*
13110946SSangeeta.Misra@Sun.COM  * NAT source address initialization routine.
13210946SSangeeta.Misra@Sun.COM  */
13310946SSangeeta.Misra@Sun.COM void
ilb_nat_src_init(ilb_stack_t * ilbs)13410946SSangeeta.Misra@Sun.COM ilb_nat_src_init(ilb_stack_t *ilbs)
13510946SSangeeta.Misra@Sun.COM {
13610946SSangeeta.Misra@Sun.COM 	int i;
13710946SSangeeta.Misra@Sun.COM 
13810946SSangeeta.Misra@Sun.COM 	ilbs->ilbs_nat_src = kmem_zalloc(sizeof (ilb_nat_src_hash_t) *
13910946SSangeeta.Misra@Sun.COM 	    ilbs->ilbs_nat_src_hash_size, KM_SLEEP);
14010946SSangeeta.Misra@Sun.COM 	for (i = 0; i < ilbs->ilbs_nat_src_hash_size; i++) {
14110946SSangeeta.Misra@Sun.COM 		list_create(&ilbs->ilbs_nat_src[i].nsh_head,
14210946SSangeeta.Misra@Sun.COM 		    sizeof (ilb_nat_src_entry_t),
14310946SSangeeta.Misra@Sun.COM 		    offsetof(ilb_nat_src_entry_t, nse_link));
14410946SSangeeta.Misra@Sun.COM 		mutex_init(&ilbs->ilbs_nat_src[i].nsh_lock, NULL,
14510946SSangeeta.Misra@Sun.COM 		    MUTEX_DEFAULT, NULL);
14610946SSangeeta.Misra@Sun.COM 	}
147*11772SKacheong.Poon@Sun.COM 	ilbs->ilbs_nat_src_tid = timeout(ilb_nat_src_timer, ilbs,
148*11772SKacheong.Poon@Sun.COM 	    SEC_TO_TICK(ILB_NAT_SRC_TIMEOUT +
149*11772SKacheong.Poon@Sun.COM 	    gethrtime() % ILB_NAT_SRC_TIMEOUT_JITTER));
15010946SSangeeta.Misra@Sun.COM }
15110946SSangeeta.Misra@Sun.COM 
15210946SSangeeta.Misra@Sun.COM /*
15310946SSangeeta.Misra@Sun.COM  * NAT source address clean up routine.
15410946SSangeeta.Misra@Sun.COM  */
15510946SSangeeta.Misra@Sun.COM void
ilb_nat_src_fini(ilb_stack_t * ilbs)15610946SSangeeta.Misra@Sun.COM ilb_nat_src_fini(ilb_stack_t *ilbs)
15710946SSangeeta.Misra@Sun.COM {
15810946SSangeeta.Misra@Sun.COM 	ilb_nat_src_entry_t *cur;
15910946SSangeeta.Misra@Sun.COM 	timeout_id_t tid;
16010946SSangeeta.Misra@Sun.COM 	int i;
16110946SSangeeta.Misra@Sun.COM 
16210946SSangeeta.Misra@Sun.COM 	/*
16310946SSangeeta.Misra@Sun.COM 	 * By setting ilbs_nat_src_tid to 0, the timer handler will not
16410946SSangeeta.Misra@Sun.COM 	 * restart the timer.
16510946SSangeeta.Misra@Sun.COM 	 */
16610946SSangeeta.Misra@Sun.COM 	mutex_enter(&ilbs->ilbs_nat_src_lock);
16710946SSangeeta.Misra@Sun.COM 	tid = ilbs->ilbs_nat_src_tid;
16810946SSangeeta.Misra@Sun.COM 	ilbs->ilbs_nat_src_tid = 0;
16910946SSangeeta.Misra@Sun.COM 	mutex_exit(&ilbs->ilbs_nat_src_lock);
17010946SSangeeta.Misra@Sun.COM 	if (tid != 0)
17110946SSangeeta.Misra@Sun.COM 		(void) untimeout(tid);
17210946SSangeeta.Misra@Sun.COM 
17310946SSangeeta.Misra@Sun.COM 	mutex_destroy(&ilbs->ilbs_nat_src_lock);
17410946SSangeeta.Misra@Sun.COM 
17510946SSangeeta.Misra@Sun.COM 	for (i = 0; i < ilbs->ilbs_nat_src_hash_size; i++) {
17610946SSangeeta.Misra@Sun.COM 		while ((cur = list_remove_head(&ilbs->ilbs_nat_src[i].nsh_head))
17710946SSangeeta.Misra@Sun.COM 		    != NULL) {
17810946SSangeeta.Misra@Sun.COM 			vmem_destroy(cur->nse_port_arena);
17910946SSangeeta.Misra@Sun.COM 			kmem_free(cur, sizeof (ilb_nat_src_entry_t));
18010946SSangeeta.Misra@Sun.COM 		}
18110946SSangeeta.Misra@Sun.COM 		mutex_destroy(&ilbs->ilbs_nat_src[i].nsh_lock);
18210946SSangeeta.Misra@Sun.COM 	}
18310946SSangeeta.Misra@Sun.COM 
18410946SSangeeta.Misra@Sun.COM 	kmem_free(ilbs->ilbs_nat_src, sizeof (ilb_nat_src_hash_t) *
18510946SSangeeta.Misra@Sun.COM 	    ilbs->ilbs_nat_src_hash_size);
18610946SSangeeta.Misra@Sun.COM 	ilbs->ilbs_nat_src = NULL;
18710946SSangeeta.Misra@Sun.COM }
18810946SSangeeta.Misra@Sun.COM 
18910946SSangeeta.Misra@Sun.COM /* An arena name is "ilb_ns" + "_xxxxxxxxxx"  */
19010946SSangeeta.Misra@Sun.COM #define	ARENA_NAMESZ	18
19110946SSangeeta.Misra@Sun.COM #define	NAT_PORT_START	4096
19210946SSangeeta.Misra@Sun.COM #define	NAT_PORT_SIZE	65535 - NAT_PORT_START
19310946SSangeeta.Misra@Sun.COM 
19410946SSangeeta.Misra@Sun.COM /*
19510946SSangeeta.Misra@Sun.COM  * Check if the NAT source and back end server pair ilb_nat_src_entry_t
19610946SSangeeta.Misra@Sun.COM  * exists.  If it does, increment the refcnt and return it.  If not, create
19710946SSangeeta.Misra@Sun.COM  * one and return it.
19810946SSangeeta.Misra@Sun.COM  */
19910946SSangeeta.Misra@Sun.COM static ilb_nat_src_entry_t *
ilb_find_nat_src(ilb_stack_t * ilbs,const in6_addr_t * nat_src,const in6_addr_t * serv_addr,in_port_t port)20010946SSangeeta.Misra@Sun.COM ilb_find_nat_src(ilb_stack_t *ilbs, const in6_addr_t *nat_src,
20110946SSangeeta.Misra@Sun.COM     const in6_addr_t *serv_addr, in_port_t port)
20210946SSangeeta.Misra@Sun.COM {
20310946SSangeeta.Misra@Sun.COM 	ilb_nat_src_entry_t *tmp;
20410946SSangeeta.Misra@Sun.COM 	uint32_t idx;
20510946SSangeeta.Misra@Sun.COM 	char arena_name[ARENA_NAMESZ];
20610946SSangeeta.Misra@Sun.COM 	list_t *head;
20710946SSangeeta.Misra@Sun.COM 
20810946SSangeeta.Misra@Sun.COM 	ILB_NAT_SRC_HASH(idx, &nat_src->s6_addr32[3], &serv_addr->s6_addr32[3],
20910946SSangeeta.Misra@Sun.COM 	    ilbs->ilbs_nat_src_hash_size);
21010946SSangeeta.Misra@Sun.COM 	mutex_enter(&ilbs->ilbs_nat_src[idx].nsh_lock);
21110946SSangeeta.Misra@Sun.COM 	head = &ilbs->ilbs_nat_src[idx].nsh_head;
21210946SSangeeta.Misra@Sun.COM 	for (tmp = list_head(head); tmp != NULL; tmp = list_next(head, tmp)) {
21310946SSangeeta.Misra@Sun.COM 		if (IN6_ARE_ADDR_EQUAL(&tmp->nse_src_addr, nat_src) &&
21410946SSangeeta.Misra@Sun.COM 		    IN6_ARE_ADDR_EQUAL(&tmp->nse_serv_addr, serv_addr) &&
21510946SSangeeta.Misra@Sun.COM 		    (port == tmp->nse_port || port == 0 ||
21610946SSangeeta.Misra@Sun.COM 		    tmp->nse_port == 0)) {
21710946SSangeeta.Misra@Sun.COM 			break;
21810946SSangeeta.Misra@Sun.COM 		}
21910946SSangeeta.Misra@Sun.COM 	}
22010946SSangeeta.Misra@Sun.COM 	/* Found one, return it. */
22110946SSangeeta.Misra@Sun.COM 	if (tmp != NULL) {
22210946SSangeeta.Misra@Sun.COM 		tmp->nse_refcnt++;
22310946SSangeeta.Misra@Sun.COM 		mutex_exit(&ilbs->ilbs_nat_src[idx].nsh_lock);
22410946SSangeeta.Misra@Sun.COM 		return (tmp);
22510946SSangeeta.Misra@Sun.COM 	}
22610946SSangeeta.Misra@Sun.COM 
22710946SSangeeta.Misra@Sun.COM 	tmp = kmem_alloc(sizeof (ilb_nat_src_entry_t), KM_NOSLEEP);
22810946SSangeeta.Misra@Sun.COM 	if (tmp == NULL) {
22910946SSangeeta.Misra@Sun.COM 		mutex_exit(&ilbs->ilbs_nat_src[idx].nsh_lock);
23010946SSangeeta.Misra@Sun.COM 		return (NULL);
23110946SSangeeta.Misra@Sun.COM 	}
23210946SSangeeta.Misra@Sun.COM 	tmp->nse_src_addr = *nat_src;
23310946SSangeeta.Misra@Sun.COM 	tmp->nse_serv_addr = *serv_addr;
23410946SSangeeta.Misra@Sun.COM 	tmp->nse_port = port;
23510946SSangeeta.Misra@Sun.COM 	tmp->nse_nsh_lock = &ilbs->ilbs_nat_src[idx].nsh_lock;
23610946SSangeeta.Misra@Sun.COM 	tmp->nse_refcnt = 1;
23710946SSangeeta.Misra@Sun.COM 
23810946SSangeeta.Misra@Sun.COM 	(void) snprintf(arena_name, ARENA_NAMESZ, "ilb_ns_%u",
23910946SSangeeta.Misra@Sun.COM 	    atomic_add_32_nv(&ilb_nat_src_instance, 1));
24010946SSangeeta.Misra@Sun.COM 	if ((tmp->nse_port_arena = vmem_create(arena_name,
24110946SSangeeta.Misra@Sun.COM 	    (void *)NAT_PORT_START, NAT_PORT_SIZE, 1, NULL, NULL, NULL, 1,
24210946SSangeeta.Misra@Sun.COM 	    VM_SLEEP | VMC_IDENTIFIER)) == NULL) {
24310946SSangeeta.Misra@Sun.COM 		kmem_free(tmp, sizeof (*tmp));
24410946SSangeeta.Misra@Sun.COM 		return (NULL);
24510946SSangeeta.Misra@Sun.COM 	}
24610946SSangeeta.Misra@Sun.COM 
24710946SSangeeta.Misra@Sun.COM 	list_insert_tail(head, tmp);
24810946SSangeeta.Misra@Sun.COM 	mutex_exit(&ilbs->ilbs_nat_src[idx].nsh_lock);
24910946SSangeeta.Misra@Sun.COM 
25010946SSangeeta.Misra@Sun.COM 	return (tmp);
25110946SSangeeta.Misra@Sun.COM }
25210946SSangeeta.Misra@Sun.COM 
25310946SSangeeta.Misra@Sun.COM /*
25410946SSangeeta.Misra@Sun.COM  * Create ilb_nat_src_t struct for a ilb_server_t struct.
25510946SSangeeta.Misra@Sun.COM  */
25610946SSangeeta.Misra@Sun.COM int
ilb_create_nat_src(ilb_stack_t * ilbs,ilb_nat_src_t ** nat_src,const in6_addr_t * srv_addr,in_port_t port,const in6_addr_t * start,int num)25710946SSangeeta.Misra@Sun.COM ilb_create_nat_src(ilb_stack_t *ilbs, ilb_nat_src_t **nat_src,
25810946SSangeeta.Misra@Sun.COM     const in6_addr_t *srv_addr, in_port_t port, const in6_addr_t *start,
25910946SSangeeta.Misra@Sun.COM     int num)
26010946SSangeeta.Misra@Sun.COM {
26110946SSangeeta.Misra@Sun.COM 	ilb_nat_src_t *src;
26210946SSangeeta.Misra@Sun.COM 	in6_addr_t cur_addr;
26310946SSangeeta.Misra@Sun.COM 	int i;
26410946SSangeeta.Misra@Sun.COM 
26510946SSangeeta.Misra@Sun.COM 	if ((src = kmem_zalloc(sizeof (ilb_nat_src_t), KM_NOSLEEP)) == NULL) {
26610946SSangeeta.Misra@Sun.COM 		*nat_src = NULL;
26710946SSangeeta.Misra@Sun.COM 		return (ENOMEM);
26810946SSangeeta.Misra@Sun.COM 	}
26910946SSangeeta.Misra@Sun.COM 	cur_addr = *start;
27010946SSangeeta.Misra@Sun.COM 	for (i = 0; i < num && i < ILB_MAX_NAT_SRC; i++) {
27110946SSangeeta.Misra@Sun.COM 		src->src_list[i] = ilb_find_nat_src(ilbs, &cur_addr, srv_addr,
27210946SSangeeta.Misra@Sun.COM 		    port);
27310946SSangeeta.Misra@Sun.COM 		if (src->src_list[i] == NULL) {
27410946SSangeeta.Misra@Sun.COM 			ilb_destroy_nat_src(&src);
27510946SSangeeta.Misra@Sun.COM 			*nat_src = NULL;
27610946SSangeeta.Misra@Sun.COM 			return (ENOMEM);
27710946SSangeeta.Misra@Sun.COM 		}
27810946SSangeeta.Misra@Sun.COM 		incr_addr(&cur_addr);
27910946SSangeeta.Misra@Sun.COM 		/*
28010946SSangeeta.Misra@Sun.COM 		 * Increment num_src here so that we can call
28110946SSangeeta.Misra@Sun.COM 		 * ilb_destroy_nat_src() when we need to do cleanup.
28210946SSangeeta.Misra@Sun.COM 		 */
28310946SSangeeta.Misra@Sun.COM 		src->num_src++;
28410946SSangeeta.Misra@Sun.COM 	}
28510946SSangeeta.Misra@Sun.COM 	*nat_src = src;
28610946SSangeeta.Misra@Sun.COM 	return (0);
28710946SSangeeta.Misra@Sun.COM }
28810946SSangeeta.Misra@Sun.COM 
28910946SSangeeta.Misra@Sun.COM /*
29010946SSangeeta.Misra@Sun.COM  * Timer routine for garbage collecting unneeded NAT source entry.  We
29110946SSangeeta.Misra@Sun.COM  * don't use a taskq for this since the table should be relatively small
29210946SSangeeta.Misra@Sun.COM  * and should be OK for a timer to handle.
29310946SSangeeta.Misra@Sun.COM  */
29410946SSangeeta.Misra@Sun.COM void
ilb_nat_src_timer(void * arg)29510946SSangeeta.Misra@Sun.COM ilb_nat_src_timer(void *arg)
29610946SSangeeta.Misra@Sun.COM {
29710946SSangeeta.Misra@Sun.COM 	ilb_stack_t *ilbs = (ilb_stack_t *)arg;
29810946SSangeeta.Misra@Sun.COM 	ilb_nat_src_entry_t *cur, *tmp;
29910946SSangeeta.Misra@Sun.COM 	list_t *head;
30010946SSangeeta.Misra@Sun.COM 	int i;
30110946SSangeeta.Misra@Sun.COM 
30210946SSangeeta.Misra@Sun.COM 	for (i = 0; i < ilbs->ilbs_nat_src_hash_size; i++) {
30310946SSangeeta.Misra@Sun.COM 		mutex_enter(&ilbs->ilbs_nat_src[i].nsh_lock);
30410946SSangeeta.Misra@Sun.COM 		head = &ilbs->ilbs_nat_src[i].nsh_head;
30510946SSangeeta.Misra@Sun.COM 		cur = list_head(head);
30610946SSangeeta.Misra@Sun.COM 		while (cur != NULL) {
30710946SSangeeta.Misra@Sun.COM 			/*
30810946SSangeeta.Misra@Sun.COM 			 * When a server is removed, it will release its
30910946SSangeeta.Misra@Sun.COM 			 * reference on an entry.  But there may still be
31010946SSangeeta.Misra@Sun.COM 			 * conn using some ports.  So check the size also.
31110946SSangeeta.Misra@Sun.COM 			 */
31210946SSangeeta.Misra@Sun.COM 			if (cur->nse_refcnt != 0 ||
31310946SSangeeta.Misra@Sun.COM 			    vmem_size(cur->nse_port_arena, VMEM_ALLOC) != 0) {
31410946SSangeeta.Misra@Sun.COM 				cur = list_next(head, cur);
31510946SSangeeta.Misra@Sun.COM 				continue;
31610946SSangeeta.Misra@Sun.COM 			}
31710946SSangeeta.Misra@Sun.COM 			tmp = cur;
31810946SSangeeta.Misra@Sun.COM 			cur = list_next(head, cur);
31910946SSangeeta.Misra@Sun.COM 			list_remove(head, tmp);
32010946SSangeeta.Misra@Sun.COM 			vmem_destroy(tmp->nse_port_arena);
32110946SSangeeta.Misra@Sun.COM 			kmem_free(tmp, sizeof (ilb_nat_src_entry_t));
32210946SSangeeta.Misra@Sun.COM 		}
32310946SSangeeta.Misra@Sun.COM 		mutex_exit(&ilbs->ilbs_nat_src[i].nsh_lock);
32410946SSangeeta.Misra@Sun.COM 	}
32510946SSangeeta.Misra@Sun.COM 
32610946SSangeeta.Misra@Sun.COM 	mutex_enter(&ilbs->ilbs_nat_src_lock);
32710946SSangeeta.Misra@Sun.COM 	if (ilbs->ilbs_nat_src_tid == 0) {
32810946SSangeeta.Misra@Sun.COM 		mutex_exit(&ilbs->ilbs_nat_src_lock);
32910946SSangeeta.Misra@Sun.COM 	} else {
33010946SSangeeta.Misra@Sun.COM 		ilbs->ilbs_nat_src_tid = timeout(ilb_nat_src_timer, ilbs,
33110946SSangeeta.Misra@Sun.COM 		    SEC_TO_TICK(ILB_NAT_SRC_TIMEOUT +
33210946SSangeeta.Misra@Sun.COM 		    gethrtime() % ILB_NAT_SRC_TIMEOUT_JITTER));
33310946SSangeeta.Misra@Sun.COM 		mutex_exit(&ilbs->ilbs_nat_src_lock);
33410946SSangeeta.Misra@Sun.COM 	}
33510946SSangeeta.Misra@Sun.COM }
33610946SSangeeta.Misra@Sun.COM 
33710946SSangeeta.Misra@Sun.COM /*
33810946SSangeeta.Misra@Sun.COM  * Destroy a given ilb_nat_src_t struct.  It will also release the reference
33910946SSangeeta.Misra@Sun.COM  * hold on all its ilb_nat_src_entry_t.
34010946SSangeeta.Misra@Sun.COM  */
34110946SSangeeta.Misra@Sun.COM void
ilb_destroy_nat_src(ilb_nat_src_t ** nat_src)34210946SSangeeta.Misra@Sun.COM ilb_destroy_nat_src(ilb_nat_src_t **nat_src)
34310946SSangeeta.Misra@Sun.COM {
34410946SSangeeta.Misra@Sun.COM 	int i, size;
34510946SSangeeta.Misra@Sun.COM 	ilb_nat_src_t *src;
34610946SSangeeta.Misra@Sun.COM 	ilb_nat_src_entry_t *entry;
34710946SSangeeta.Misra@Sun.COM 
34810946SSangeeta.Misra@Sun.COM 	src = *nat_src;
34910946SSangeeta.Misra@Sun.COM 	if (src == NULL)
35010946SSangeeta.Misra@Sun.COM 		return;
35110946SSangeeta.Misra@Sun.COM 	size = src->num_src;
35210946SSangeeta.Misra@Sun.COM 	/*
35310946SSangeeta.Misra@Sun.COM 	 * Set each entry to be condemned and the garbarge collector will
35410946SSangeeta.Misra@Sun.COM 	 * clean them up.
35510946SSangeeta.Misra@Sun.COM 	 */
35610946SSangeeta.Misra@Sun.COM 	for (i = 0; i < size; i++) {
35710946SSangeeta.Misra@Sun.COM 		entry = src->src_list[i];
35810946SSangeeta.Misra@Sun.COM 		mutex_enter(entry->nse_nsh_lock);
35910946SSangeeta.Misra@Sun.COM 		entry->nse_refcnt--;
36010946SSangeeta.Misra@Sun.COM 		mutex_exit(entry->nse_nsh_lock);
36110946SSangeeta.Misra@Sun.COM 	}
36210946SSangeeta.Misra@Sun.COM 	kmem_free(src, sizeof (ilb_nat_src_t));
36310946SSangeeta.Misra@Sun.COM 	*nat_src = NULL;
36410946SSangeeta.Misra@Sun.COM }
36510946SSangeeta.Misra@Sun.COM 
36610946SSangeeta.Misra@Sun.COM /*
36710946SSangeeta.Misra@Sun.COM  * Given a backend server address and its ilb_nat_src_t, allocate a source
36810946SSangeeta.Misra@Sun.COM  * address and port for NAT usage.
36910946SSangeeta.Misra@Sun.COM  */
37010946SSangeeta.Misra@Sun.COM ilb_nat_src_entry_t *
ilb_alloc_nat_addr(ilb_nat_src_t * src,in6_addr_t * addr,in_port_t * port,uint16_t * nat_src_idx)37110946SSangeeta.Misra@Sun.COM ilb_alloc_nat_addr(ilb_nat_src_t *src, in6_addr_t *addr, in_port_t *port,
37210946SSangeeta.Misra@Sun.COM     uint16_t *nat_src_idx)
37310946SSangeeta.Misra@Sun.COM {
37410946SSangeeta.Misra@Sun.COM 	int i, try, size;
37510946SSangeeta.Misra@Sun.COM 	in_port_t p;
37610946SSangeeta.Misra@Sun.COM 
37710946SSangeeta.Misra@Sun.COM 	size = src->num_src;
37810946SSangeeta.Misra@Sun.COM 	/* Increment of cur does not need to be atomic.  It is just a hint. */
37910946SSangeeta.Misra@Sun.COM 	if (nat_src_idx == NULL)
38010946SSangeeta.Misra@Sun.COM 		i = (++src->cur) % size;
38110946SSangeeta.Misra@Sun.COM 	else
38210946SSangeeta.Misra@Sun.COM 		i = *nat_src_idx;
38310946SSangeeta.Misra@Sun.COM 
38410946SSangeeta.Misra@Sun.COM 	for (try = 0; try < size; try++) {
38510946SSangeeta.Misra@Sun.COM 		p = (in_port_t)(uintptr_t)vmem_alloc(
38610946SSangeeta.Misra@Sun.COM 		    src->src_list[i]->nse_port_arena, 1, VM_NOSLEEP);
38710946SSangeeta.Misra@Sun.COM 		if (p != 0)
38810946SSangeeta.Misra@Sun.COM 			break;
38910946SSangeeta.Misra@Sun.COM 		/*
39010946SSangeeta.Misra@Sun.COM 		 * If an index is given and we cannot allocate a port using
39110946SSangeeta.Misra@Sun.COM 		 * that entry, return NULL.
39210946SSangeeta.Misra@Sun.COM 		 */
39310946SSangeeta.Misra@Sun.COM 		if (nat_src_idx != NULL)
39410946SSangeeta.Misra@Sun.COM 			return (NULL);
39510946SSangeeta.Misra@Sun.COM 		i = (i + 1) % size;
39610946SSangeeta.Misra@Sun.COM 	}
39710946SSangeeta.Misra@Sun.COM 	if (try == size)
39810946SSangeeta.Misra@Sun.COM 		return (NULL);
39910946SSangeeta.Misra@Sun.COM 	*addr = src->src_list[i]->nse_src_addr;
40010946SSangeeta.Misra@Sun.COM 	*port = htons(p);
40110946SSangeeta.Misra@Sun.COM 	return (src->src_list[i]);
40210946SSangeeta.Misra@Sun.COM }
40310946SSangeeta.Misra@Sun.COM 
40410946SSangeeta.Misra@Sun.COM /*
40510946SSangeeta.Misra@Sun.COM  * Use the pre-calculated checksum to adjust the checksum of a packet after
40610946SSangeeta.Misra@Sun.COM  * NAT.
40710946SSangeeta.Misra@Sun.COM  */
40810946SSangeeta.Misra@Sun.COM static void
adj_cksum(uint16_t * chksum,uint32_t adj_sum)40910946SSangeeta.Misra@Sun.COM adj_cksum(uint16_t *chksum, uint32_t adj_sum)
41010946SSangeeta.Misra@Sun.COM {
41110946SSangeeta.Misra@Sun.COM 	adj_sum += (uint16_t)~(*chksum);
41210946SSangeeta.Misra@Sun.COM 	while ((adj_sum >> 16) != 0)
41310946SSangeeta.Misra@Sun.COM 		adj_sum = (adj_sum & 0xffff) + (adj_sum >> 16);
41410946SSangeeta.Misra@Sun.COM 	*chksum = (uint16_t)~adj_sum;
41510946SSangeeta.Misra@Sun.COM }
41610946SSangeeta.Misra@Sun.COM 
41710946SSangeeta.Misra@Sun.COM /* Do full NAT (replace both source and desination info) on a packet. */
41810946SSangeeta.Misra@Sun.COM void
ilb_full_nat(int l3,void * iph,int l4,void * tph,ilb_nat_info_t * info,uint32_t adj_ip_sum,uint32_t adj_tp_sum,boolean_t c2s)41910946SSangeeta.Misra@Sun.COM ilb_full_nat(int l3, void *iph, int l4, void *tph, ilb_nat_info_t *info,
42010946SSangeeta.Misra@Sun.COM     uint32_t adj_ip_sum, uint32_t adj_tp_sum, boolean_t c2s)
42110946SSangeeta.Misra@Sun.COM {
42210946SSangeeta.Misra@Sun.COM 	in_port_t *orig_sport, *orig_dport;
42310946SSangeeta.Misra@Sun.COM 	uint16_t *tp_cksum;
42410946SSangeeta.Misra@Sun.COM 
42510946SSangeeta.Misra@Sun.COM 	switch (l4) {
42610946SSangeeta.Misra@Sun.COM 	case IPPROTO_TCP:
42710946SSangeeta.Misra@Sun.COM 		orig_sport = &((tcpha_t *)tph)->tha_lport;
42810946SSangeeta.Misra@Sun.COM 		orig_dport = &((tcpha_t *)tph)->tha_fport;
42910946SSangeeta.Misra@Sun.COM 		tp_cksum = &((tcpha_t *)tph)->tha_sum;
43010946SSangeeta.Misra@Sun.COM 		break;
43110946SSangeeta.Misra@Sun.COM 	case IPPROTO_UDP:
43210946SSangeeta.Misra@Sun.COM 		orig_sport = &((udpha_t *)tph)->uha_src_port;
43310946SSangeeta.Misra@Sun.COM 		orig_dport = &((udpha_t *)tph)->uha_dst_port;
43410946SSangeeta.Misra@Sun.COM 		tp_cksum = &((udpha_t *)tph)->uha_checksum;
43510946SSangeeta.Misra@Sun.COM 		break;
43610946SSangeeta.Misra@Sun.COM 	default:
43710946SSangeeta.Misra@Sun.COM 		ASSERT(0);
43810946SSangeeta.Misra@Sun.COM 		return;
43910946SSangeeta.Misra@Sun.COM 	}
44010946SSangeeta.Misra@Sun.COM 
44110946SSangeeta.Misra@Sun.COM 	switch (l3) {
44210946SSangeeta.Misra@Sun.COM 	case IPPROTO_IP: {
44310946SSangeeta.Misra@Sun.COM 		ipha_t *ipha;
44410946SSangeeta.Misra@Sun.COM 
44510946SSangeeta.Misra@Sun.COM 		ipha = iph;
44610946SSangeeta.Misra@Sun.COM 		if (c2s) {
44710946SSangeeta.Misra@Sun.COM 			IN6_V4MAPPED_TO_IPADDR(&info->nat_src,
44810946SSangeeta.Misra@Sun.COM 			    ipha->ipha_src);
44910946SSangeeta.Misra@Sun.COM 			IN6_V4MAPPED_TO_IPADDR(&info->nat_dst,
45010946SSangeeta.Misra@Sun.COM 			    ipha->ipha_dst);
45110946SSangeeta.Misra@Sun.COM 			*orig_sport = info->nat_sport;
45210946SSangeeta.Misra@Sun.COM 			*orig_dport = info->nat_dport;
45310946SSangeeta.Misra@Sun.COM 		} else {
45410946SSangeeta.Misra@Sun.COM 			IN6_V4MAPPED_TO_IPADDR(&info->vip, ipha->ipha_src);
45510946SSangeeta.Misra@Sun.COM 			IN6_V4MAPPED_TO_IPADDR(&info->src, ipha->ipha_dst);
45610946SSangeeta.Misra@Sun.COM 			*orig_sport = info->dport;
45710946SSangeeta.Misra@Sun.COM 			*orig_dport = info->sport;
45810946SSangeeta.Misra@Sun.COM 		}
45910946SSangeeta.Misra@Sun.COM 		adj_cksum(&ipha->ipha_hdr_checksum, adj_ip_sum);
46010946SSangeeta.Misra@Sun.COM 		adj_cksum(tp_cksum, adj_tp_sum);
46110946SSangeeta.Misra@Sun.COM 		break;
46210946SSangeeta.Misra@Sun.COM 	}
46310946SSangeeta.Misra@Sun.COM 	case IPPROTO_IPV6: {
46410946SSangeeta.Misra@Sun.COM 		ip6_t *ip6h;
46510946SSangeeta.Misra@Sun.COM 
46610946SSangeeta.Misra@Sun.COM 		ip6h = iph;
46710946SSangeeta.Misra@Sun.COM 		if (c2s) {
46810946SSangeeta.Misra@Sun.COM 			ip6h->ip6_src = info->nat_src;
46910946SSangeeta.Misra@Sun.COM 			ip6h->ip6_dst = info->nat_dst;
47010946SSangeeta.Misra@Sun.COM 			*orig_sport = info->nat_sport;
47110946SSangeeta.Misra@Sun.COM 			*orig_dport = info->nat_dport;
47210946SSangeeta.Misra@Sun.COM 		} else {
47310946SSangeeta.Misra@Sun.COM 			ip6h->ip6_src = info->vip;
47410946SSangeeta.Misra@Sun.COM 			ip6h->ip6_dst = info->src;
47510946SSangeeta.Misra@Sun.COM 			*orig_sport = info->dport;
47610946SSangeeta.Misra@Sun.COM 			*orig_dport = info->sport;
47710946SSangeeta.Misra@Sun.COM 		}
47810946SSangeeta.Misra@Sun.COM 		/* No checksum for IPv6 header */
47910946SSangeeta.Misra@Sun.COM 		adj_cksum(tp_cksum, adj_tp_sum);
48010946SSangeeta.Misra@Sun.COM 		break;
48110946SSangeeta.Misra@Sun.COM 	}
48210946SSangeeta.Misra@Sun.COM 	default:
48310946SSangeeta.Misra@Sun.COM 		ASSERT(0);
48410946SSangeeta.Misra@Sun.COM 		break;
48510946SSangeeta.Misra@Sun.COM 	}
48610946SSangeeta.Misra@Sun.COM }
48710946SSangeeta.Misra@Sun.COM 
48810946SSangeeta.Misra@Sun.COM /* Do half NAT (only replace the destination info) on a packet. */
48910946SSangeeta.Misra@Sun.COM void
ilb_half_nat(int l3,void * iph,int l4,void * tph,ilb_nat_info_t * info,uint32_t adj_ip_sum,uint32_t adj_tp_sum,boolean_t c2s)49010946SSangeeta.Misra@Sun.COM ilb_half_nat(int l3, void *iph, int l4, void *tph, ilb_nat_info_t *info,
49110946SSangeeta.Misra@Sun.COM     uint32_t adj_ip_sum, uint32_t adj_tp_sum, boolean_t c2s)
49210946SSangeeta.Misra@Sun.COM {
49310946SSangeeta.Misra@Sun.COM 	in_port_t *orig_port;
49410946SSangeeta.Misra@Sun.COM 	uint16_t *tp_cksum;
49510946SSangeeta.Misra@Sun.COM 
49610946SSangeeta.Misra@Sun.COM 	switch (l4) {
49710946SSangeeta.Misra@Sun.COM 	case IPPROTO_TCP:
49810946SSangeeta.Misra@Sun.COM 		if (c2s)
49910946SSangeeta.Misra@Sun.COM 			orig_port = &((tcpha_t *)tph)->tha_fport;
50010946SSangeeta.Misra@Sun.COM 		else
50110946SSangeeta.Misra@Sun.COM 			orig_port = &((tcpha_t *)tph)->tha_lport;
50210946SSangeeta.Misra@Sun.COM 		tp_cksum = &((tcpha_t *)tph)->tha_sum;
50310946SSangeeta.Misra@Sun.COM 		break;
50410946SSangeeta.Misra@Sun.COM 	case IPPROTO_UDP:
50510946SSangeeta.Misra@Sun.COM 		if (c2s)
50610946SSangeeta.Misra@Sun.COM 			orig_port = &((udpha_t *)tph)->uha_dst_port;
50710946SSangeeta.Misra@Sun.COM 		else
50810946SSangeeta.Misra@Sun.COM 			orig_port = &((udpha_t *)tph)->uha_src_port;
50910946SSangeeta.Misra@Sun.COM 		tp_cksum = &((udpha_t *)tph)->uha_checksum;
51010946SSangeeta.Misra@Sun.COM 		break;
51110946SSangeeta.Misra@Sun.COM 	default:
51210946SSangeeta.Misra@Sun.COM 		ASSERT(0);
51310946SSangeeta.Misra@Sun.COM 		return;
51410946SSangeeta.Misra@Sun.COM 	}
51510946SSangeeta.Misra@Sun.COM 
51610946SSangeeta.Misra@Sun.COM 	switch (l3) {
51710946SSangeeta.Misra@Sun.COM 	case IPPROTO_IP: {
51810946SSangeeta.Misra@Sun.COM 		ipha_t *ipha;
51910946SSangeeta.Misra@Sun.COM 
52010946SSangeeta.Misra@Sun.COM 		ipha = iph;
52110946SSangeeta.Misra@Sun.COM 		if (c2s) {
52210946SSangeeta.Misra@Sun.COM 			IN6_V4MAPPED_TO_IPADDR(&info->nat_dst,
52310946SSangeeta.Misra@Sun.COM 			    ipha->ipha_dst);
52410946SSangeeta.Misra@Sun.COM 			*orig_port = info->nat_dport;
52510946SSangeeta.Misra@Sun.COM 		} else {
52610946SSangeeta.Misra@Sun.COM 			IN6_V4MAPPED_TO_IPADDR(&info->vip, ipha->ipha_src);
52710946SSangeeta.Misra@Sun.COM 			*orig_port = info->dport;
52810946SSangeeta.Misra@Sun.COM 		}
52910946SSangeeta.Misra@Sun.COM 		adj_cksum(&ipha->ipha_hdr_checksum, adj_ip_sum);
53010946SSangeeta.Misra@Sun.COM 		adj_cksum(tp_cksum, adj_tp_sum);
53110946SSangeeta.Misra@Sun.COM 		break;
53210946SSangeeta.Misra@Sun.COM 	}
53310946SSangeeta.Misra@Sun.COM 	case IPPROTO_IPV6: {
53410946SSangeeta.Misra@Sun.COM 		ip6_t *ip6h;
53510946SSangeeta.Misra@Sun.COM 
53610946SSangeeta.Misra@Sun.COM 		ip6h = iph;
53710946SSangeeta.Misra@Sun.COM 		if (c2s) {
53810946SSangeeta.Misra@Sun.COM 			ip6h->ip6_dst = info->nat_dst;
53910946SSangeeta.Misra@Sun.COM 			*orig_port = info->nat_dport;
54010946SSangeeta.Misra@Sun.COM 		} else {
54110946SSangeeta.Misra@Sun.COM 			ip6h->ip6_src = info->vip;
54210946SSangeeta.Misra@Sun.COM 			*orig_port = info->dport;
54310946SSangeeta.Misra@Sun.COM 		}
54410946SSangeeta.Misra@Sun.COM 		/* No checksum for IPv6 header */
54510946SSangeeta.Misra@Sun.COM 		adj_cksum(tp_cksum, adj_tp_sum);
54610946SSangeeta.Misra@Sun.COM 		break;
54710946SSangeeta.Misra@Sun.COM 	}
54810946SSangeeta.Misra@Sun.COM 	default:
54910946SSangeeta.Misra@Sun.COM 		ASSERT(0);
55010946SSangeeta.Misra@Sun.COM 		break;
55110946SSangeeta.Misra@Sun.COM 	}
55210946SSangeeta.Misra@Sun.COM }
55310946SSangeeta.Misra@Sun.COM 
55410946SSangeeta.Misra@Sun.COM /* Calculate the IPv6 pseudo checksum, used for ICMPv6 NAT. */
55510946SSangeeta.Misra@Sun.COM uint32_t
ilb_pseudo_sum_v6(ip6_t * ip6h,uint8_t nxt_hdr)55610946SSangeeta.Misra@Sun.COM ilb_pseudo_sum_v6(ip6_t *ip6h, uint8_t nxt_hdr)
55710946SSangeeta.Misra@Sun.COM {
55810946SSangeeta.Misra@Sun.COM 	uint32_t sum;
55910946SSangeeta.Misra@Sun.COM 	uint16_t *cur;
56010946SSangeeta.Misra@Sun.COM 
56110946SSangeeta.Misra@Sun.COM 	cur = (uint16_t *)&ip6h->ip6_src;
56210946SSangeeta.Misra@Sun.COM 	sum = cur[0] + cur[1] + cur[2] + cur[3] + cur[4] + cur[5] + cur[6] +
56310946SSangeeta.Misra@Sun.COM 	    cur[7] + cur[8] + cur[9] + cur[10] + cur[11] + cur[12] + cur[13] +
56410946SSangeeta.Misra@Sun.COM 	    cur[14] + cur[15] + htons(nxt_hdr);
56510946SSangeeta.Misra@Sun.COM 	return ((sum & 0xffff) + (sum >> 16));
56610946SSangeeta.Misra@Sun.COM }
56710946SSangeeta.Misra@Sun.COM 
56810946SSangeeta.Misra@Sun.COM /* Do NAT on an ICMPv4 packet. */
56910946SSangeeta.Misra@Sun.COM void
ilb_nat_icmpv4(mblk_t * mp,ipha_t * out_iph,icmph_t * icmph,ipha_t * in_iph,in_port_t * sport,in_port_t * dport,ilb_nat_info_t * info,uint32_t sum,boolean_t full_nat)57010946SSangeeta.Misra@Sun.COM ilb_nat_icmpv4(mblk_t *mp, ipha_t *out_iph, icmph_t *icmph, ipha_t *in_iph,
57110946SSangeeta.Misra@Sun.COM     in_port_t *sport, in_port_t *dport, ilb_nat_info_t *info, uint32_t sum,
57210946SSangeeta.Misra@Sun.COM     boolean_t full_nat)
57310946SSangeeta.Misra@Sun.COM {
57410946SSangeeta.Misra@Sun.COM 	if (full_nat) {
57510946SSangeeta.Misra@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(&info->nat_src, out_iph->ipha_src);
57610946SSangeeta.Misra@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(&info->nat_src, in_iph->ipha_dst);
57710946SSangeeta.Misra@Sun.COM 		*dport = info->nat_sport;
57810946SSangeeta.Misra@Sun.COM 	}
57910946SSangeeta.Misra@Sun.COM 	IN6_V4MAPPED_TO_IPADDR(&info->nat_dst, out_iph->ipha_dst);
58010946SSangeeta.Misra@Sun.COM 	adj_cksum(&out_iph->ipha_hdr_checksum, sum);
58110946SSangeeta.Misra@Sun.COM 	IN6_V4MAPPED_TO_IPADDR(&info->nat_dst, in_iph->ipha_src);
58210946SSangeeta.Misra@Sun.COM 	*sport = info->nat_dport;
58310946SSangeeta.Misra@Sun.COM 
58410946SSangeeta.Misra@Sun.COM 	icmph->icmph_checksum = 0;
58510946SSangeeta.Misra@Sun.COM 	icmph->icmph_checksum = IP_CSUM(mp, IPH_HDR_LENGTH(out_iph), 0);
58610946SSangeeta.Misra@Sun.COM }
58710946SSangeeta.Misra@Sun.COM 
58810946SSangeeta.Misra@Sun.COM /* Do NAT on an ICMPv6 packet. */
58910946SSangeeta.Misra@Sun.COM void
ilb_nat_icmpv6(mblk_t * mp,ip6_t * out_ip6h,icmp6_t * icmp6h,ip6_t * in_ip6h,in_port_t * sport,in_port_t * dport,ilb_nat_info_t * info,boolean_t full_nat)59010946SSangeeta.Misra@Sun.COM ilb_nat_icmpv6(mblk_t *mp, ip6_t *out_ip6h, icmp6_t *icmp6h, ip6_t *in_ip6h,
59110946SSangeeta.Misra@Sun.COM     in_port_t *sport, in_port_t *dport, ilb_nat_info_t *info,
59210946SSangeeta.Misra@Sun.COM     boolean_t full_nat)
59310946SSangeeta.Misra@Sun.COM {
59410946SSangeeta.Misra@Sun.COM 	int hdr_len;
59510946SSangeeta.Misra@Sun.COM 
59610946SSangeeta.Misra@Sun.COM 	if (full_nat) {
59710946SSangeeta.Misra@Sun.COM 		out_ip6h->ip6_src = info->nat_src;
59810946SSangeeta.Misra@Sun.COM 		in_ip6h->ip6_dst = info->nat_src;
59910946SSangeeta.Misra@Sun.COM 		*dport = info->nat_sport;
60010946SSangeeta.Misra@Sun.COM 	}
60110946SSangeeta.Misra@Sun.COM 	out_ip6h->ip6_dst = info->nat_dst;
60210946SSangeeta.Misra@Sun.COM 	in_ip6h->ip6_src = info->nat_dst;
60310946SSangeeta.Misra@Sun.COM 	*sport = info->nat_dport;
60410946SSangeeta.Misra@Sun.COM 
60510946SSangeeta.Misra@Sun.COM 	icmp6h->icmp6_cksum = out_ip6h->ip6_plen;
60610946SSangeeta.Misra@Sun.COM 	hdr_len = (char *)icmp6h - (char *)out_ip6h;
60710946SSangeeta.Misra@Sun.COM 	icmp6h->icmp6_cksum = IP_CSUM(mp, hdr_len,
60810946SSangeeta.Misra@Sun.COM 	    ilb_pseudo_sum_v6(out_ip6h, IPPROTO_ICMPV6));
60910946SSangeeta.Misra@Sun.COM }
610