/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <inet/udp_impl.h>
#include "ilb_impl.h"
#include "ilb_stack.h"
#include "ilb_nat.h"
/*
* NAT source entry garbarge collection timeout. The actual timeout value
* includes a random jitter bounded by the ILB_NAT_SRC_TIMEOUT_JITTER.
*/
/* key1/2 are assumed to be uint32_t. */
{ \
}
/* NAT source port space instance number. */
static void
{
uint32_t i;
if (IN6_IS_ADDR_V4MAPPED(a)) {
ASSERT(i != 0);
return;
}
if (++i != 0) {
return;
}
a->s6_addr32[3] = 0;
if (++i != 0) {
return;
}
a->s6_addr32[2] = 0;
if (++i != 0) {
return;
}
a->s6_addr32[1] = 0;
ASSERT(i != 0);
}
/*
* When ILB does full NAT, it first picks one source address from the rule's
* specified NAT source address list (currently done in round robin fashion).
* Then it needs to allocate a port. This source port must make the tuple
* (source address:source port:destination address:destination port)
* unique. The destination part of the tuple is determined by the back
* end server, and could not be changed.
*
* To handle the above source port number allocation, ILB sets up a table
* of entries identified by source address:back end server address:server port
* tuple. This table is used by all rules for NAT source port allocation.
* Each tuple has an associated vmem arena used for managing the NAT source
* Each back end server (ilb_server_t) has an array of pointers (iser_nat_src)
* to the different entries in this table for NAT source port allocation.
* When ILB needs to allocate a NAT source address and port to talk to a back
* end server, it picks a source address and uses the array pointer to get
* to an entry. Then it calls vmem_alloc() on the associated vmem arena to
* find an unused port.
*
* When a back end server is added, ILB sets up the aforementioned array.
* For each source address specified in the rule, ILB checks if there is any
* existing entry which matches this source address:back end server address:
* port tuple. The server port is either a specific port or 0 (meaning wild
* card port). Normally, a back end server uses the same port as in the rule.
* If a back end server is used to serve two different rules, there will be
* two different ports. Source port allocation for these two rules do not
* conflict, hence we can use two vmem arenas (two different entries in the
* table). But if a server uses port range in one rule, we will treat it as
* a wild card port. Wild card poart matches with any port. If this server
* is used to serve more than one rules and those rules use the same set of
* NAT source addresses, this means that they must share the same set of vmem
* arenas (source port spaces). We do this for simplicity reason. If not,
* we need to partition the port range so that we can identify different forms
* of source port number collision.
*/
/*
* NAT source address initialization routine.
*/
void
{
int i;
for (i = 0; i < ilbs->ilbs_nat_src_hash_size; i++) {
sizeof (ilb_nat_src_entry_t),
}
}
/*
* NAT source address clean up routine.
*/
void
{
int i;
/*
* By setting ilbs_nat_src_tid to 0, the timer handler will not
* restart the timer.
*/
ilbs->ilbs_nat_src_tid = 0;
if (tid != 0)
for (i = 0; i < ilbs->ilbs_nat_src_hash_size; i++) {
!= NULL) {
}
}
}
/* An arena name is "ilb_ns" + "_xxxxxxxxxx" */
/*
* Check if the NAT source and back end server pair ilb_nat_src_entry_t
* exists. If it does, increment the refcnt and return it. If not, create
* one and return it.
*/
static ilb_nat_src_entry_t *
{
break;
}
}
/* Found one, return it. */
tmp->nse_refcnt++;
return (tmp);
}
return (NULL);
}
return (NULL);
}
return (tmp);
}
/*
* Create ilb_nat_src_t struct for a ilb_server_t struct.
*/
int
int num)
{
int i;
return (ENOMEM);
}
for (i = 0; i < num && i < ILB_MAX_NAT_SRC; i++) {
port);
return (ENOMEM);
}
/*
* Increment num_src here so that we can call
* ilb_destroy_nat_src() when we need to do cleanup.
*/
}
return (0);
}
/*
* Timer routine for garbage collecting unneeded NAT source entry. We
* don't use a taskq for this since the table should be relatively small
* and should be OK for a timer to handle.
*/
void
{
int i;
for (i = 0; i < ilbs->ilbs_nat_src_hash_size; i++) {
/*
* When a server is removed, it will release its
* reference on an entry. But there may still be
* conn using some ports. So check the size also.
*/
if (cur->nse_refcnt != 0 ||
continue;
}
}
}
if (ilbs->ilbs_nat_src_tid == 0) {
} else {
}
}
/*
* Destroy a given ilb_nat_src_t struct. It will also release the reference
* hold on all its ilb_nat_src_entry_t.
*/
void
{
int i, size;
return;
/*
* Set each entry to be condemned and the garbarge collector will
* clean them up.
*/
for (i = 0; i < size; i++) {
entry->nse_refcnt--;
}
}
/*
* Given a backend server address and its ilb_nat_src_t, allocate a source
* address and port for NAT usage.
*/
{
in_port_t p;
/* Increment of cur does not need to be atomic. It is just a hint. */
if (nat_src_idx == NULL)
else
i = *nat_src_idx;
if (p != 0)
break;
/*
* If an index is given and we cannot allocate a port using
* that entry, return NULL.
*/
if (nat_src_idx != NULL)
return (NULL);
i = (i + 1) % size;
}
return (NULL);
}
/*
* Use the pre-calculated checksum to adjust the checksum of a packet after
* NAT.
*/
static void
{
while ((adj_sum >> 16) != 0)
}
/* Do full NAT (replace both source and desination info) on a packet. */
void
{
switch (l4) {
case IPPROTO_TCP:
break;
case IPPROTO_UDP:
break;
default:
ASSERT(0);
return;
}
switch (l3) {
case IPPROTO_IP: {
if (c2s) {
} else {
}
break;
}
case IPPROTO_IPV6: {
if (c2s) {
} else {
}
/* No checksum for IPv6 header */
break;
}
default:
ASSERT(0);
break;
}
}
/* Do half NAT (only replace the destination info) on a packet. */
void
{
switch (l4) {
case IPPROTO_TCP:
if (c2s)
else
break;
case IPPROTO_UDP:
if (c2s)
else
break;
default:
ASSERT(0);
return;
}
switch (l3) {
case IPPROTO_IP: {
if (c2s) {
} else {
}
break;
}
case IPPROTO_IPV6: {
if (c2s) {
} else {
}
/* No checksum for IPv6 header */
break;
}
default:
ASSERT(0);
break;
}
}
/* Calculate the IPv6 pseudo checksum, used for ICMPv6 NAT. */
{
}
/* Do NAT on an ICMPv4 packet. */
void
{
if (full_nat) {
}
icmph->icmph_checksum = 0;
}
/* Do NAT on an ICMPv6 packet. */
void
{
int hdr_len;
if (full_nat) {
}
}