top

get_unique_tuple()

The function named get_unique_tuple() is a little complex function.

At first, let's look at arguments.
This function is defined in

${linux src}/net/ipv4/netfilter/ip_nat_core.c

This function is called with 5 arguments.

static int
get_unique_tuple(struct ip_conntrack_tuple *tuple,
		 const struct ip_conntrack_tuple *orig_tuple,
		 const struct ip_nat_multi_range *mrr,
		 struct ip_conntrack *conntrack,
		 unsigned int hooknum)

The first argument named tuple is for storing a bland new tuple for
translation. The second one is tuple inversed from tuple stored in
tuplehash[IP_CT_DIR_REPLY]. This may be a really original tuple or
may be already translated tuple.

The third argument is pointer for struct ip_nat_multi_range, which is
set by iptables command to specify the range of ip address and port.

The fourth is a pointer to ip_conntrack of this connection.

The last is a point at which this function is called.
This function is called forNAT, this value is NF_IP_POST_ROUTING,
NF_IP_PRE_ROUTING, or NF_IP_LOCAL_OUT.

{
	struct ip_nat_protocol *proto
		= find_nat_proto(orig_tuple->dst.protonum);
	struct ip_nat_range *rptr;
	unsigned int i;
	int ret;

At first, getting the protocol (tcp, udp, etc) and storing it to proto.
And several declaration of local variables.

	struct ip_nat_multi_range *mr = (void *)mrr;

	if (hooknum == NF_IP_POST_ROUTING) {
		struct ip_conntrack_manip *manip;

manip = find_appropriate_src(orig_tuple, mr); if (manip) { *tuple = ((struct ip_conntrack_tuple) { *manip, orig_tuple->dst }); return 1; } }

"if" statement make sure that this block should be executed for
source adress translation.

struct ip_conntrack_manip is a little complex structure, but in short,
it has a ip address and protocol port.

find_appropriate_src() calculates the hash value from orig_tp (using
orig_tp->src and orig_tp->dst.protonum) and checks wether ip_conntrack
for this connection is linked in list named bysource.

The given tuple as orig_tuple is inversed from hashtuple[IP_CT_DIR_REPLY].tuple
in ip_setup_info().

At this point, if there is not happend a source address change,
the tuple for IP_CT_DIR_REPLY is inversed tuple of IP_CT_DIR_ORIGINAL.

In this case, hash value for bysoruce[] hash list is made from tuple
for IP_CT_DIR_ORIGINAL at first stage of connection tracking.

If so, this source ip and port is unique in the network and if it is
within the range specified with struct ip_nat_muli_range,
this source ip and port is used to distinguish from other translation.

So, find_appropriate_src() returns it directly.

 return &i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src;

No matter where this connection is established, this source address
and orig_tp.dst are able to be used as unipue tuple in the sytesm.

If there is not original tuple in bysource list, it returns NULL.

	*tuple = *orig_tuple;
	while ((rptr = find_best_ips_proto_fast(tuple, mr, conntrack, hooknum))
	       != NULL) {

Next, find_best_ips_proto_fast() function sets ip address in the range
specified in ip_nat_info structure that passed as mr argument.
And go into loop until unique tuple is found.

find_best_ips_proto_fast() function is long but executes a simple code.
Go into the loop.

		if ((!(rptr->flags & IP_NAT_RANGE_PROTO_SPECIFIED)
		     || proto->in_range(tuple, HOOK2MANIP(hooknum),
					&rptr->min, &rptr->max))
		    && !ip_nat_used_tuple(tuple, conntrack)) {

This "if" statement means if this range pointed by rptr has not set
at IPNAT_RANGE_PROTO_SPECIFIED (this means protocol port number
is specified because this flag is set when range for protocol number
is allowed),

or

protocol port number specified in tuple is within
the range between rptr->min and rptr->max,

and in addition

this tuple is unique in the system.
(ip_nat_used_tuple is simple.)

If these conditions are true this block is executed.

			ret = 1;
			goto clear_fulls;

In this case, set ret to 1 and go to the end of this routine labeled
clear_fulls. And return to the loop of ip_nat_setup_info().

		} else {
			if (proto->unique_tuple(tuple, rptr,
						HOOK2MANIP(hooknum),
						conntrack)) {
				IP_NF_ASSERT(!ip_nat_used_tuple(tuple,
								conntrack));
				ret = 1;
				goto clear_fulls;

"else" statement is other situation of "if" condition.
This means that protocol number is specified or not within range
specified by rptr (struct * ip_nat_range).

Then, this code is executed.
Each protocol has its own function to get an unique tuple.
So, leave it to protocol function to make an unique tuple.

IP_NF_ASSERT checks the tuple is not used in the system (if debuged).

if it gets an unique tuple, set ret to 1 and go to the end of routine.

} else if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { struct ip_nat_range r = { IP_NAT_RANGE_MAP_IPS, tuple->src.ip, tuple->src.ip, { 0 }, { 0 } };

if (proto->unique_tuple(tuple, &r, IP_NAT_MANIP_SRC, conntrack)) {

IP_NF_ASSERT(!ip_nat_used_tuple (tuple, conntrack)); ret = 1; goto clear_fulls; } } }

The "else if" conndition in brace indicates that manipulation must
be on destination, but these are exahusited.
(Because proto->unique_tuple failed to get a nuique tuple.)

It tries to make a special ip_nat_range
to manipulate the source protocol ports (IPNAT_MANIP_SRC).

Using this new ip_nat_range, leave a job to protocol specified
unique_tuple() function once more.

This time, "else if" condition tells translation should be done
with destination (IPNAT_MANIP_DST), but they are exhausted.
So try to get an unique tuple by changing source.

If a new tuple is available with source manipuration for destination
translation, set ret to 1 and go to end of rutine.
Return to ip_nat_setup_info().

		rptr->flags |= IP_NAT_RANGE_FULL;
		*tuple = *orig_tuple;
	} /* end of loop */

Mark the flag as this range is already full and restore the tuple
from orig_tuple and again do the loop.
tuple variable is tweaked in the loop.
It should be reset to orig_tuple.

	ret = 0;

If the execution reached this point, there is no available tuple in
the system to distinguish from other tuples.

set ret to 0.

 clear_fulls:
	IP_NF_ASSERT(mr->rangesize >= 1);
	for (i = 0; i < mr->rangesize; i++)
		mr->range[i].flags &= ~IP_NAT_RANGE_FULL;

return ret; }

clea_fulls: label is the point that all of member of ip_nat_multi_range
is cleared IPNAT_RANGE_FULL flag.

and function returns ret.



top
inserted by FC2 system