Network Address Translation(转载)

Network Address Translation

 来源:http://alexanderlaw.blog.hexun.com/9791596_d.html

      地址转换用来改变源/目的地址/端口,是netfilter的一部分,也是通过hook点上注册相应的结构来工作

      Nat注册的hook点和conntrack相同,只是优先级不同,数据包进入netfilter之后先经过conntrack,再经过nat。而在数据包离开netfilter之前先经过nat,再经过conntrack。

 

1  nat模块的初始化

1.1       数据结构    ip_nat_standalone.c

在ip_conntrack结构中有为nat定义的一个nat结构,为什么把这个结构放在ip_conntrack里呢。简单的说,对于非初始化连接的数据包,即后续的数据包,一旦确定它属于某个连接,则可以直接利用连接状态里的nat信息来进行地址转换;而对于初始数据包,必须在nat表里查找相应的规则,确定了地址转换的内容后,将这些信息放到连接跟踪结构的nat参量里面,供后续的数据包使用。

#ifdef CONFIG_IP_NF_NAT_NEEDED

struct {

struct ip_nat_info info;

union ip_conntrack_nat_help help;

#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \

defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)

int masq_index;

#endif

#if defined(CONFIG_IP_NF_RTSP) || defined(CONFIG_IP_NF_RTSP_MODULE)

struct ip_nat_rtsp_info rtsp_info;

#endif

} nat;

#endif /* CONFIG_IP_NF_NAT_NEEDED */

#if defined(CONFIG_IP_NF_CONNTRACK_MARK)

unsigned long mark;

#endif

它包括两个参数,struct ip_nat_info和union ip_conntrack_nat_help,后一个暂时没什么用,只看前一个

struct ip_nat_info

{

/* 用来检测该连接是否已经进行过某类nat初始化了,在新的内核中该参数被去掉了,当然,有其它方法来实现它的作用。 */

int initialized;

unsigned int num_manips;

/* 这个就是用来存储关于如何进行地址转换的相关信息的数据结构,其中IP_NAT_MAX_MANIPS代表某个连接的数据包在经过netfilter一次的过程中最多能进行的地址转换的次数,这里是(2*3)=6 。意思大概是说对于某个连接,如果nat表的每条链上都有一条规则:

NF_IP_PRE_ROUTING==>NF_IP_POST_ROUTING
如果在NF_IP_PRE_ROUTING上做目的转换,要在NF_IP_POST_ROUTING上做反方向上的源转换
NF_IP_POST_ROUTING==>NF_IP_PRE_ROUTING
如果在NF_IP_POST_ROUTING上做源转换,要在NF_IP_PRE_ROUTING上做反方向上的目的转换
NF_IP_LOCAL_OUT==>NF_IP_LOCAL_IN
如果在NF_IP_LOCAL_OUT做源转换,要在NF_IP_LOCAL_IN上做反方向上的目的转换

算下来就是最多进行6次地址转换 */

struct ip_nat_info_manip manips[IP_NAT_MAX_MANIPS];

/* 两个全局hash表,用来将所有需要进行地址转换的连接组织起来 */

struct ip_nat_hash bysource, byipsproto;

/* 做特殊用途,通常是NULL */

struct ip_nat_helper *helper;

struct ip_nat_seq seq[IP_CT_DIR_MAX];

};

ip_nat_info_manip结构定义如下:

struct ip_nat_info_manip

{

/* 方向,初始或应答 */

u_int8_t direction;

/* 转换发生的hook点 */

u_int8_t hooknum;

/* 转换的类型,源还是目的 */

u_int8_t maniptype;

/* Manipulations to occur at each conntrack in this dirn. */

struct ip_conntrack_manip manip;

};

struct ip_conntrack_manip

{

u_int32_t ip;

union ip_conntrack_manip_proto u;

};

ip_nat_hash结构   ip_nat.h

struct ip_nat_hash

{

struct list_head list;

struct ip_conntrack *conntrack;

};

1.2       init()函数    ip_nat_standalone.c

static int __init init(void)

{

return init_or_cleanup(1);

}

init()函数直接调用init_or_cleanup()

static int init_or_cleanup(int init)

{

int ret = 0;

/* nat依赖于conntrack,这个函数是空的 */

need_ip_conntrack();

if (!init) goto cleanup;

/* 初始化nat规则 */

ret = ip_nat_rule_init();

if (ret < 0) {

printk("ip_nat_init: can‘t setup rules.\n");

goto cleanup_nothing;

}

/* 初始化nat */

ret = ip_nat_init();

if (ret < 0) {

printk("ip_nat_init: can‘t setup rules.\n");

goto cleanup_rule_init;

}

/* 注册hook,共在四个hook点上注册了函数,分别是:

NF_IP_PRE_ROUTING   ip_nat_fn

NF_IP_POST_ROUTING  ip_nat_out

NF_IP_LOCAL_OUT   ip_nat_local_fn

NF_IP_LOCAL_IN     ip_nat_fn

NF_IP_LOCAL_OUT和NF_IP_LOCAL_IN需要定义CONFIG_IP_NF_NAT_LOCAL

其中在ip_nat_out和ip_nat_local_fn中都会调用ip_nat_fn

*/

ret = nf_register_hook(&ip_nat_in_ops);

if (ret < 0) {

printk("ip_nat_init: can‘t register in hook.\n");

goto cleanup_nat;

}

ret = nf_register_hook(&ip_nat_out_ops);

if (ret < 0) {

printk("ip_nat_init: can‘t register out hook.\n");

goto cleanup_inops;

}

#ifdef CONFIG_IP_NF_NAT_LOCAL

ret = nf_register_hook(&ip_nat_local_out_ops);

if (ret < 0) {

printk("ip_nat_init: can‘t register local out hook.\n");

goto cleanup_outops;

}

ret = nf_register_hook(&ip_nat_local_in_ops);

if (ret < 0) {

printk("ip_nat_init: can‘t register local in hook.\n");

goto cleanup_localoutops;

}

#endif

return ret;

cleanup:

#ifdef CONFIG_IP_NF_NAT_LOCAL

nf_unregister_hook(&ip_nat_local_in_ops);

cleanup_localoutops:

nf_unregister_hook(&ip_nat_local_out_ops);

cleanup_outops:

#endif

nf_unregister_hook(&ip_nat_out_ops);

cleanup_inops:

nf_unregister_hook(&ip_nat_in_ops);

cleanup_nat:

ip_nat_cleanup();

cleanup_rule_init:

ip_nat_rule_cleanup();

cleanup_nothing:

MUST_BE_READ_WRITE_UNLOCKED(&ip_nat_lock);

return ret;

}

1.3  ip_nat_rule_init()函数  ip_nat_rule.c

int __init ip_nat_rule_init(void)

{

int ret;

/* 注册nat表 */

ret = ipt_register_table(&nat_table);

if (ret != 0)

return ret;

/* 注册了两个target,一个是snat一个是dnat  */

ret = ipt_register_target(&ipt_snat_reg);

if (ret != 0)

goto unregister_table;

ret = ipt_register_target(&ipt_dnat_reg);

if (ret != 0)

goto unregister_snat;

return ret;

unregister_snat:

ipt_unregister_target(&ipt_snat_reg);

unregister_table:

ipt_unregister_table(&nat_table);

return ret;

}

看一下nat表的初始化:

static struct ipt_table nat_table = {

.name        = "nat",

.table         = &nat_initial_table.repl,

.valid_hooks     = NAT_VALID_HOOKS,

.lock          = RW_LOCK_UNLOCKED,

.me      = THIS_MODULE,

};

和filter表的初始化类似,一开始规则都是空的

两个target的初始化:

static struct ipt_target ipt_snat_reg = {

.name        = "SNAT",

.target       = ipt_snat_target,

.checkentry = ipt_snat_checkentry,

};

static struct ipt_target ipt_dnat_reg = {

.name        = "DNAT",

.target       = ipt_dnat_target,

.checkentry = ipt_dnat_checkentry,

};

两个target函数分别是ipt_snat_target和ipt_dnat_target

1.4  ip_nat_init()函数  ipt_nat_core.c

int __init ip_nat_init(void)

{

size_t i;

/* nat的hash表大小和conntrack的hash表相同 */

ip_nat_htable_size = ip_conntrack_htable_size;

/* 初始化了一个叫bysource的全局链表指针 */

bysource = vmalloc(sizeof(struct list_head) * ip_nat_htable_size*2);

if (!bysource) {

return -ENOMEM;

}

/* 全局链表指针byipsproto,在bysource之后。bysource和byipsproto实际上也是两个hash表,每个节点是一个ip_nat_hash结构,包含一个list_head和一个ip_conntrack。有点特别的就是nat用两个hash表来组织地址转换的数据结构,其本质是一样的,只是所使用的hash算法不同,bysource一般用于SNAT的处理,计算bysource的hash值的函数是hash_by_src();byipsproto用于DNAT的处理,计算byipsproto的hash值的函数是hash_by_ipsproto()。*/

byipsproto = bysource + ip_nat_htable_size;

/* 注册一些内建的协议,&protos是用来维护nat模块中用到的协议结构ip_nat_protocol的全局链表 */

WRITE_LOCK(&ip_nat_lock);

list_append(&protos, &ip_nat_protocol_tcp);

list_append(&protos, &ip_nat_protocol_udp);

list_append(&protos, &ip_nat_protocol_icmp);

WRITE_UNLOCK(&ip_nat_lock);

for (i = 0; i < ip_nat_htable_size; i++) {

/* 初始化bysource和byipsproto中的所有链表,两个数组的大小都是ip_nat_htables_size,数组的每个节点是一个链表头 */

INIT_LIST_HEAD(&bysource[i]);

INIT_LIST_HEAD(&byipsproto[i]);

}

IP_NF_ASSERT(ip_conntrack_destroyed == NULL);

/* 初始化一个ip_conntrack_destroyed函数,ip_nat_cleanup_conntrack(struct ip_conntrack *conn) 的作用是在bysource和byipproto链表中删除conn对应的节点 */

ip_conntrack_destroyed = &ip_nat_cleanup_conntrack;

/* Initialize fake conntrack so that NAT will skip it */

ip_conntrack_untracked.nat.info.initialized |=

(1 << IP_NAT_MANIP_SRC) | (1 << IP_NAT_MANIP_DST);

return 0;

}

地址转换的过程

2.1  ip_nat_fn函数  ip_nat_standalone.c

ip_nat_fn()是nat中的主要函数,nat在netfilter中注册了四个hook,最终都会调用该函数

static unsigned int

ip_nat_fn(unsigned int hooknum,

struct sk_buff **pskb,

const struct net_device *in,

const struct net_device *out,

int (*okfn)(struct sk_buff *))

{

struct ip_conntrack *ct;

enum ip_conntrack_info ctinfo;

struct ip_nat_info *info;

/* 根据所在的hook点判断转换类型是源地址转换还是目的地址转换,为0(IP_NAT_MANIP_SRC)表示源地址转换,为1(IP_NAT_MANIP_DST)表示目的地址转换 */

enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum);

/* 前面函数中已经处理过分片的情况,这里应该不会再出现分片包了. */

IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off

& htons(IP_MF|IP_OFFSET)));

/*因为地址转换会修改数据包,所以这里先初始化将其设置为“未修改”标志,后面进行数据包修改时再来重置这个标志*/

(*pskb)->nfcache |= NFC_UNKNOWN;

/* 校验和 */

if ((*pskb)->ip_summed == CHECKSUM_HW)

if (skb_checksum_help(pskb, (out == NULL)))

return NF_DROP;

/*取得数据包的连接状态*/

ct = ip_conntrack_get(*pskb, &ctinfo);

/* 如果找不到对应连接,则应该直接放行它,而不再对其进行转换处理,特别地,ICMP重定向报文将会被丢弃*/

if (!ct) {

/* Exception: ICMP redirect to new connection (not in

hash table yet).  We must not let this through, in

case we‘re doing NAT to the same network. */

if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {

struct icmphdr hdr;

if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4,

&hdr, sizeof(hdr)) == 0

&& hdr.type == ICMP_REDIRECT)

return NF_DROP;

}

return NF_ACCEPT;

}

/* 判断连接状态,调用相应的处理函数*/

switch (ctinfo) {

case IP_CT_RELATED:

case IP_CT_RELATED+IP_CT_IS_REPLY:

if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {

if (!icmp_reply_translation(pskb, ct, hooknum,

CTINFO2DIR(ctinfo)))

return NF_DROP;

else

return NF_ACCEPT;

}

/* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */

/* 如果是一个初始连接的数据包 */

case IP_CT_NEW:

info = &ct->nat.info;

WRITE_LOCK(&ip_nat_lock);

/* 观察这个连接中的nat部分是否已经被初始化过了,如果有则跳过下面的部分,直接进行地址转换,如果没有,进一步判断 */

if (!(info->initialized & (1 << maniptype))

#ifndef CONFIG_IP_NF_NAT_LOCAL

&& !(ct->status & IPS_CONFIRMED)

#endif

) {

unsigned int ret;

/* 如果该连接是由expect创建的,并且有expect函数,则在这里调用 */

if (ct->master

&& master_ct(ct)->nat.info.helper

&& master_ct(ct)->nat.info.helper->expect) {

ret = call_expect(master_ct(ct), pskb,

hooknum, ct, info);

} else {

#ifdef CONFIG_IP_NF_NAT_LOCAL

/* LOCAL_IN hook doesn‘t have a chain!  */

if (hooknum == NF_IP_LOCAL_IN)

ret = alloc_null_binding(ct, info,

hooknum);

else

#endif

/* 既没有被nat修改过,也不是由expect创建,这是一个初始的数据包,开始在nat表中查找规则 */

ret = ip_nat_rule_find(pskb, hooknum, in, out, ct, info);

}

if (ret != NF_ACCEPT) {

WRITE_UNLOCK(&ip_nat_lock);

return ret;

}

} else

/* 如果该连接的nat部分已经被初始化了,打印调试信息 */

DEBUGP("Already setup manip %s for ct %p\n",

maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST",

ct);

WRITE_UNLOCK(&ip_nat_lock);

break;

default:

/* ESTABLISHED */

IP_NF_ASSERT(ctinfo == IP_CT_ESTABLISHED

|| ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY));

info = &ct->nat.info;

}

IP_NF_ASSERT(info);

/* 前面已经修改了连接跟踪表,这里正式修改了数据包里的地址 */

return do_bindings(ct, ctinfo, info, hooknum, pskb);

}

2.2           ip_nat_rule_find函数  ip_nat_rule.c

int ip_nat_rule_find(struct sk_buff **pskb,

unsigned int hooknum,

const struct net_device *in,

const struct net_device *out,

struct ip_conntrack *ct,

struct ip_nat_info *info)

{

int ret;

/* 调用ipt_do_tables函数,第五个参数是&nat_table  */

ret = ipt_do_table(pskb, hooknum, in, out, &nat_table, NULL);

if (ret == NF_ACCEPT) {

if (!(info->initialized & (1 << HOOK2MANIP(hooknum))))

/* NUL mapping */

ret = alloc_null_binding(ct, info, hooknum);

}

return ret;

}

nat表和filter表一样,都是通过调用ipt_do_table函数来工作的

ipt_do_table查找表中的所有entry,如果match全都匹配,则调用target函数

此时的target函数就是在nat初始化时注册的ipt_snat_target和ipt_dnat_target

2.3           ipt_s(d)nat_target函数  ip_nat_rule.c

static unsigned int ipt_snat_target(struct sk_buff **pskb,

const struct net_device *in,

const struct net_device *out,

unsigned int hooknum,

const void *targinfo,

void *userinfo)

{

struct ip_conntrack *ct;

enum ip_conntrack_info ctinfo;

IP_NF_ASSERT(hooknum == NF_IP_POST_ROUTING);

/* 取得数据包的连接状态 */

ct = ip_conntrack_get(*pskb, &ctinfo);

/* Connection must be valid and new. */

IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));

IP_NF_ASSERT(out);

return ip_nat_setup_info(ct, targinfo, hooknum);

}

ipt_dnat_target和ipt_snat_target差不多,都是调用ip_nat_setup_info完成地址转换,这里的targinfo参数来自ipt_entry_target结构的unsigned char data[0]参数,一个长度为0的数组,指向target的末尾

static unsigned int ipt_dnat_target(struct sk_buff **pskb,

const struct net_device *in,

const struct net_device *out,

unsigned int hooknum,

const void *targinfo,

void *userinfo)

{

struct ip_conntrack *ct;

enum ip_conntrack_info ctinfo;

#ifdef CONFIG_IP_NF_NAT_LOCAL

IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING

|| hooknum == NF_IP_LOCAL_OUT);

#else

IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING);

#endif

ct = ip_conntrack_get(*pskb, &ctinfo);

/* Connection must be valid and new. */

IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));

return ip_nat_setup_info(ct, targinfo, hooknum);

}

2.4           ip_nat_setup_info()函数  ip_nat_rule.c

unsigned int

ip_nat_setup_info(struct ip_conntrack *conntrack,   /* 数据包的连接状态 */

const struct ip_nat_multi_range *mr,       /* 转换后的地址池 */

unsigned int hooknum)                    /* hook点 */

{

struct ip_conntrack_tuple new_tuple, inv_tuple, reply;

struct ip_conntrack_tuple orig_tp;

struct ip_nat_info *info = &conntrack->nat.info;

int in_hashes = info->initialized;

MUST_BE_WRITE_LOCKED(&ip_nat_lock);

IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING

|| hooknum == NF_IP_POST_ROUTING

|| hooknum == NF_IP_LOCAL_IN

|| hooknum == NF_IP_LOCAL_OUT);

IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS);

IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));

/* 对当前状态的应答方向的tuple调用invert_tuplepr取反,得到一个orig_tp,如果之前没有进行过地址或端口转换,通常这里得到的orig_tp就等于初始方向的tuple */

invert_tuplepr(&orig_tp, conntrack->tuplehash[IP_CT_DIR_REPLY].tuple);

do {

/* 进行地址转换,new_tuple为转换后的地址的tuple */

if (!get_unique_tuple(&new_tuple,&orig_tp,mr,conntrack,hooknum))

{

DEBUGP("ip_nat_setup_info: Can‘t get unique for %p.\n",

conntrack);

return NF_DROP;

}

/* 对new_tuple取反,得到经过转换后的应答方向的tuple  */

invert_tuplepr(&reply, &new_tuple);

/* 修改conntrack中的应答方向的reply tuple,在这之前还要检查如果该reply tuple已经在hash表里存在了,即被其它连接占用(存在初始方向tuple不同,应答方向tuple相同的连接),则还要回头继续修改 */

} while (!ip_conntrack_alter_reply(conntrack, &reply));

/* 对orig_tp取反,实际上又得到了原conntrack的reply_tuple…… */

invert_tuplepr(&inv_tuple, &orig_tp);

/* 将所作转换的相关信息保存到连接状态conntrack里,这样该连接的后续数据包就可以直接利用这些信息进行地址转换,不用重新查找nat表了 */

/* 如果是源地址改变(SNAT) */

if (!ip_ct_tuple_src_equal(&new_tuple, &orig_tp)) {

/* In this direction, a source manip. */

info->manips[info->num_manips++] =

((struct ip_nat_info_manip)

{ IP_CT_DIR_ORIGINAL, hooknum,

IP_NAT_MANIP_SRC, new_tuple.src });

IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS);

/* 在相对的hook点上必然有对应的目的地址改变(DNAT) */

info->manips[info->num_manips++] =

((struct ip_nat_info_manip)

/* opposite_hook即是求当前hook点的对应hook点 */

{ IP_CT_DIR_REPLY, opposite_hook[hooknum],

IP_NAT_MANIP_DST, orig_tp.src });

IP_NF_ASSERT(info->num_manips <= IP_NAT_MAX_MANIPS);

}

/* 如果是目的地址改变(DNAT) */

if (!ip_ct_tuple_dst_equal(&new_tuple, &orig_tp)) {

/* In this direction, a destination manip */

info->manips[info->num_manips++] =

((struct ip_nat_info_manip)

{ IP_CT_DIR_ORIGINAL, hooknum,

IP_NAT_MANIP_DST, reply.src });

IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS);

/* In the reverse direction, a source manip. */

info->manips[info->num_manips++] =

((struct ip_nat_info_manip)

{ IP_CT_DIR_REPLY, opposite_hook[hooknum],

IP_NAT_MANIP_SRC, inv_tuple.src });

IP_NF_ASSERT(info->num_manips <= IP_NAT_MAX_MANIPS);

}

/* 如果这个连接不是某个连接的预期的连接(子连接),则在全局链表helpers查找对应的ip_nat_helper结构 */

if (!conntrack->master)

info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, &reply);

/* 转换完了,标记一下 */

info->initialized |= (1 << HOOK2MANIP(hooknum));

/* 将所做的地址转换的数据结构加入到全局hash表bysource和byipsproto中,如果该地址转换是某地址转换基础上的再次转换,则用replace_in_hashes替换,反之则用place_in_hashes */

if (in_hashes) {

IP_NF_ASSERT(info->bysource.conntrack);

replace_in_hashes(conntrack, info);

} else {

place_in_hashes(conntrack, info);

}

return NF_ACCEPT;

}

2.5           get_unique_tuple ()函数  ip_nat_core.c

get_unique_tuple,获得一个唯一的tuple,就是说除了要做地址/段口的转换,还要保证转换得到的tuple是唯一的。

很复杂的一个函数。。。

第三个参数是用来替换的地址或端口的范围

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)

{

struct ip_nat_protocol *proto

= find_nat_proto(orig_tuple->dst.protonum);

struct ip_nat_range *rptr;

unsigned int i;

int ret;

struct ip_nat_multi_range *mr = (void *)mrr;

/* 下面这一段比较晕,和p2p,udp打洞等技术有关。 */

if (hooknum == NF_IP_POST_ROUTING) {

/* ip_conntrack_manip结构包含一个ip地址和一个协议端口 */

struct ip_conntrack_manip *manip;

/* find_appropriate_src函数先调用hash_by_src函数计算orig_tuple的hash值,然后去bysource表里查找,如果能找到源地址和端口都匹配的连接,并且如果该连接的地址/端口本身就满足目标地址/端口范围的话,就直接返回查到的这个连接的源ip */

manip = find_appropriate_src(orig_tuple, mr);

if (manip) {

/* Apply same source manipulation. */

*tuple = ((struct ip_conntrack_tuple)

{ *manip, orig_tuple->dst });

DEBUGP("get_unique_tuple: Found current src map\n");

/* 还要保证连接跟踪表里没有这个连接 */

if (!ip_nat_used_tuple(tuple, conntrack))

return 1;

}

}

/* orig_tuple是转换之前的,tuple是转换之后的 */

*tuple = *orig_tuple;

/* 循环 ,尝试mr参数所指定的地址/端口范围,直到能满足其tuple是唯一的 */

while ((rptr = find_best_ips_proto_fast(tuple, mr, conntrack, hooknum))

!= NULL) {

DEBUGP("Found best for "); DUMP_TUPLE(tuple);

/*  IP_NAT_MANIP_SRC, 进行SNAT

IP_NAT_MANIP_DST 进行DNAT

IP_NAT_RANGE_MAP_IPS 在range里指定了IP地址

IP_NAT_RANGE_PROTO_SPECIFIED 在range里指定了port

如果没有指定协议端口范围,或者满足了所指定的范围 */

if ((!(rptr->flags & IP_NAT_RANGE_PROTO_SPECIFIED)

|| proto->in_range(tuple, HOOK2MANIP(hooknum),

&rptr->min, &rptr->max))

&& !ip_nat_used_tuple(tuple, conntrack)) {

ret = 1;

goto clear_fulls;

} else {

if (proto->unique_tuple(tuple, rptr,

HOOK2MANIP(hooknum),

conntrack)) {

/* Must be unique. */

IP_NF_ASSERT(!ip_nat_used_tuple(tuple,

conntrack));

ret = 1;

goto clear_fulls;

} else if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {

/* Try implicit source NAT; protocol

may be able to play with ports to

make it unique. */

struct ip_nat_range r

= { IP_NAT_RANGE_MAP_IPS,

tuple->src.ip, tuple->src.ip,

{ 0 }, { 0 } };

DEBUGP("Trying implicit mapping\n");

if (proto->unique_tuple(tuple, &r,

IP_NAT_MANIP_SRC,

conntrack)) {

/* Must be unique. */

IP_NF_ASSERT(!ip_nat_used_tuple

(tuple, conntrack));

ret = 1;

goto clear_fulls;

}

}

DEBUGP("Protocol can‘t get unique tuple %u.\n",

hooknum);

}

/* Eliminate that from range, and try again. */

rptr->flags |= IP_NAT_RANGE_FULL;

*tuple = *orig_tuple;

}

ret = 0;

clear_fulls:

/* Clear full flags. */

IP_NF_ASSERT(mr->rangesize >= 1);

for (i = 0; i < mr->rangesize; i++)

mr->range[i].flags &= ~IP_NAT_RANGE_FULL;

return ret;

}

Network Address Translation

 

      地址转换用来改变源/目的地址/端口,是netfilter的一部分,也是通过hook点上注册相应的结构来工作

 

      Nat注册的hook点和conntrack相同,只是优先级不同,数据包进入netfilter之后先经过conntrack,再经过nat。而在数据包离开netfilter之前先经过nat,再经过conntrack。

 

1  nat模块的初始化

1.1       数据结构    ip_nat_standalone.c

在ip_conntrack结构中有为nat定义的一个nat结构,为什么把这个结构放在ip_conntrack里呢。简单的说,对于非初始化连接的数据包,即后续的数据包,一旦确定它属于某个连接,则可以直接利用连接状态里的nat信息来进行地址转换;而对于初始数据包,必须在nat表里查找相应的规则,确定了地址转换的内容后,将这些信息放到连接跟踪结构的nat参量里面,供后续的数据包使用。

#ifdef CONFIG_IP_NF_NAT_NEEDED

struct {

struct ip_nat_info info;

union ip_conntrack_nat_help help;

#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \

defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)

int masq_index;

#endif

#if defined(CONFIG_IP_NF_RTSP) || defined(CONFIG_IP_NF_RTSP_MODULE)

struct ip_nat_rtsp_info rtsp_info;

#endif

} nat;

#endif /* CONFIG_IP_NF_NAT_NEEDED */

#if defined(CONFIG_IP_NF_CONNTRACK_MARK)

unsigned long mark;

#endif

它包括两个参数,struct ip_nat_info和union ip_conntrack_nat_help,后一个暂时没什么用,只看前一个

struct ip_nat_info

{

/* 用来检测该连接是否已经进行过某类nat初始化了,在新的内核中该参数被去掉了,当然,有其它方法来实现它的作用。 */

int initialized;

unsigned int num_manips;

/* 这个就是用来存储关于如何进行地址转换的相关信息的数据结构,其中IP_NAT_MAX_MANIPS代表某个连接的数据包在经过netfilter一次的过程中最多能进行的地址转换的次数,这里是(2*3)=6 。意思大概是说对于某个连接,如果nat表的每条链上都有一条规则:

NF_IP_PRE_ROUTING==>NF_IP_POST_ROUTING
如果在NF_IP_PRE_ROUTING上做目的转换,要在NF_IP_POST_ROUTING上做反方向上的源转换
NF_IP_POST_ROUTING==>NF_IP_PRE_ROUTING
如果在NF_IP_POST_ROUTING上做源转换,要在NF_IP_PRE_ROUTING上做反方向上的目的转换
NF_IP_LOCAL_OUT==>NF_IP_LOCAL_IN
如果在NF_IP_LOCAL_OUT做源转换,要在NF_IP_LOCAL_IN上做反方向上的目的转换

算下来就是最多进行6次地址转换 */

struct ip_nat_info_manip manips[IP_NAT_MAX_MANIPS];

/* 两个全局hash表,用来将所有需要进行地址转换的连接组织起来 */

struct ip_nat_hash bysource, byipsproto;

/* 做特殊用途,通常是NULL */

struct ip_nat_helper *helper;

struct ip_nat_seq seq[IP_CT_DIR_MAX];

};

ip_nat_info_manip结构定义如下:

struct ip_nat_info_manip

{

/* 方向,初始或应答 */

u_int8_t direction;

/* 转换发生的hook点 */

u_int8_t hooknum;

/* 转换的类型,源还是目的 */

u_int8_t maniptype;

/* Manipulations to occur at each conntrack in this dirn. */

struct ip_conntrack_manip manip;

};

struct ip_conntrack_manip

{

u_int32_t ip;

union ip_conntrack_manip_proto u;

};

ip_nat_hash结构   ip_nat.h

struct ip_nat_hash

{

struct list_head list;

struct ip_conntrack *conntrack;

};

1.2       init()函数    ip_nat_standalone.c

static int __init init(void)

{

return init_or_cleanup(1);

}

init()函数直接调用init_or_cleanup()

static int init_or_cleanup(int init)

{

int ret = 0;

/* nat依赖于conntrack,这个函数是空的 */

need_ip_conntrack();

if (!init) goto cleanup;

/* 初始化nat规则 */

ret = ip_nat_rule_init();

if (ret < 0) {

printk("ip_nat_init: can‘t setup rules.\n");

goto cleanup_nothing;

}

/* 初始化nat */

ret = ip_nat_init();

if (ret < 0) {

printk("ip_nat_init: can‘t setup rules.\n");

goto cleanup_rule_init;

}

/* 注册hook,共在四个hook点上注册了函数,分别是:

NF_IP_PRE_ROUTING   ip_nat_fn

NF_IP_POST_ROUTING  ip_nat_out

NF_IP_LOCAL_OUT   ip_nat_local_fn

NF_IP_LOCAL_IN     ip_nat_fn

NF_IP_LOCAL_OUT和NF_IP_LOCAL_IN需要定义CONFIG_IP_NF_NAT_LOCAL

其中在ip_nat_out和ip_nat_local_fn中都会调用ip_nat_fn

*/

ret = nf_register_hook(&ip_nat_in_ops);

if (ret < 0) {

printk("ip_nat_init: can‘t register in hook.\n");

goto cleanup_nat;

}

ret = nf_register_hook(&ip_nat_out_ops);

if (ret < 0) {

printk("ip_nat_init: can‘t register out hook.\n");

goto cleanup_inops;

}

#ifdef CONFIG_IP_NF_NAT_LOCAL

ret = nf_register_hook(&ip_nat_local_out_ops);

if (ret < 0) {

printk("ip_nat_init: can‘t register local out hook.\n");

goto cleanup_outops;

}

ret = nf_register_hook(&ip_nat_local_in_ops);

if (ret < 0) {

printk("ip_nat_init: can‘t register local in hook.\n");

goto cleanup_localoutops;

}

#endif

return ret;

cleanup:

#ifdef CONFIG_IP_NF_NAT_LOCAL

nf_unregister_hook(&ip_nat_local_in_ops);

cleanup_localoutops:

nf_unregister_hook(&ip_nat_local_out_ops);

cleanup_outops:

#endif

nf_unregister_hook(&ip_nat_out_ops);

cleanup_inops:

nf_unregister_hook(&ip_nat_in_ops);

cleanup_nat:

ip_nat_cleanup();

cleanup_rule_init:

ip_nat_rule_cleanup();

cleanup_nothing:

MUST_BE_READ_WRITE_UNLOCKED(&ip_nat_lock);

return ret;

}

1.3  ip_nat_rule_init()函数  ip_nat_rule.c

int __init ip_nat_rule_init(void)

{

int ret;

/* 注册nat表 */

ret = ipt_register_table(&nat_table);

if (ret != 0)

return ret;

/* 注册了两个target,一个是snat一个是dnat  */

ret = ipt_register_target(&ipt_snat_reg);

if (ret != 0)

goto unregister_table;

ret = ipt_register_target(&ipt_dnat_reg);

if (ret != 0)

goto unregister_snat;

return ret;

unregister_snat:

ipt_unregister_target(&ipt_snat_reg);

unregister_table:

ipt_unregister_table(&nat_table);

return ret;

}

看一下nat表的初始化:

static struct ipt_table nat_table = {

.name        = "nat",

.table         = &nat_initial_table.repl,

.valid_hooks     = NAT_VALID_HOOKS,

.lock          = RW_LOCK_UNLOCKED,

.me      = THIS_MODULE,

};

和filter表的初始化类似,一开始规则都是空的

两个target的初始化:

static struct ipt_target ipt_snat_reg = {

.name        = "SNAT",

.target       = ipt_snat_target,

.checkentry = ipt_snat_checkentry,

};

static struct ipt_target ipt_dnat_reg = {

.name        = "DNAT",

.target       = ipt_dnat_target,

.checkentry = ipt_dnat_checkentry,

};

两个target函数分别是ipt_snat_target和ipt_dnat_target

1.4  ip_nat_init()函数  ipt_nat_core.c

int __init ip_nat_init(void)

{

size_t i;

/* nat的hash表大小和conntrack的hash表相同 */

ip_nat_htable_size = ip_conntrack_htable_size;

/* 初始化了一个叫bysource的全局链表指针 */

bysource = vmalloc(sizeof(struct list_head) * ip_nat_htable_size*2);

if (!bysource) {

return -ENOMEM;

}

/* 全局链表指针byipsproto,在bysource之后。bysource和byipsproto实际上也是两个hash表,每个节点是一个ip_nat_hash结构,包含一个list_head和一个ip_conntrack。有点特别的就是nat用两个hash表来组织地址转换的数据结构,其本质是一样的,只是所使用的hash算法不同,bysource一般用于SNAT的处理,计算bysource的hash值的函数是hash_by_src();byipsproto用于DNAT的处理,计算byipsproto的hash值的函数是hash_by_ipsproto()。*/

byipsproto = bysource + ip_nat_htable_size;

/* 注册一些内建的协议,&protos是用来维护nat模块中用到的协议结构ip_nat_protocol的全局链表 */

WRITE_LOCK(&ip_nat_lock);

list_append(&protos, &ip_nat_protocol_tcp);

list_append(&protos, &ip_nat_protocol_udp);

list_append(&protos, &ip_nat_protocol_icmp);

WRITE_UNLOCK(&ip_nat_lock);

for (i = 0; i < ip_nat_htable_size; i++) {

/* 初始化bysource和byipsproto中的所有链表,两个数组的大小都是ip_nat_htables_size,数组的每个节点是一个链表头 */

INIT_LIST_HEAD(&bysource[i]);

INIT_LIST_HEAD(&byipsproto[i]);

}

IP_NF_ASSERT(ip_conntrack_destroyed == NULL);

/* 初始化一个ip_conntrack_destroyed函数,ip_nat_cleanup_conntrack(struct ip_conntrack *conn) 的作用是在bysource和byipproto链表中删除conn对应的节点 */

ip_conntrack_destroyed = &ip_nat_cleanup_conntrack;

/* Initialize fake conntrack so that NAT will skip it */

ip_conntrack_untracked.nat.info.initialized |=

(1 << IP_NAT_MANIP_SRC) | (1 << IP_NAT_MANIP_DST);

return 0;

}

地址转换的过程

2.1  ip_nat_fn函数  ip_nat_standalone.c

ip_nat_fn()是nat中的主要函数,nat在netfilter中注册了四个hook,最终都会调用该函数

static unsigned int

ip_nat_fn(unsigned int hooknum,

struct sk_buff **pskb,

const struct net_device *in,

const struct net_device *out,

int (*okfn)(struct sk_buff *))

{

struct ip_conntrack *ct;

enum ip_conntrack_info ctinfo;

struct ip_nat_info *info;

/* 根据所在的hook点判断转换类型是源地址转换还是目的地址转换,为0(IP_NAT_MANIP_SRC)表示源地址转换,为1(IP_NAT_MANIP_DST)表示目的地址转换 */

enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum);

/* 前面函数中已经处理过分片的情况,这里应该不会再出现分片包了. */

IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off

& htons(IP_MF|IP_OFFSET)));

/*因为地址转换会修改数据包,所以这里先初始化将其设置为“未修改”标志,后面进行数据包修改时再来重置这个标志*/

(*pskb)->nfcache |= NFC_UNKNOWN;

/* 校验和 */

if ((*pskb)->ip_summed == CHECKSUM_HW)

if (skb_checksum_help(pskb, (out == NULL)))

return NF_DROP;

/*取得数据包的连接状态*/

ct = ip_conntrack_get(*pskb, &ctinfo);

/* 如果找不到对应连接,则应该直接放行它,而不再对其进行转换处理,特别地,ICMP重定向报文将会被丢弃*/

if (!ct) {

/* Exception: ICMP redirect to new connection (not in

hash table yet).  We must not let this through, in

case we‘re doing NAT to the same network. */

if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {

struct icmphdr hdr;

if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4,

&hdr, sizeof(hdr)) == 0

&& hdr.type == ICMP_REDIRECT)

return NF_DROP;

}

return NF_ACCEPT;

}

/* 判断连接状态,调用相应的处理函数*/

switch (ctinfo) {

case IP_CT_RELATED:

case IP_CT_RELATED+IP_CT_IS_REPLY:

if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {

if (!icmp_reply_translation(pskb, ct, hooknum,

CTINFO2DIR(ctinfo)))

return NF_DROP;

else

return NF_ACCEPT;

}

/* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */

/* 如果是一个初始连接的数据包 */

case IP_CT_NEW:

info = &ct->nat.info;

WRITE_LOCK(&ip_nat_lock);

/* 观察这个连接中的nat部分是否已经被初始化过了,如果有则跳过下面的部分,直接进行地址转换,如果没有,进一步判断 */

if (!(info->initialized & (1 << maniptype))

#ifndef CONFIG_IP_NF_NAT_LOCAL

&& !(ct->status & IPS_CONFIRMED)

#endif

) {

unsigned int ret;

/* 如果该连接是由expect创建的,并且有expect函数,则在这里调用 */

if (ct->master

&& master_ct(ct)->nat.info.helper

&& master_ct(ct)->nat.info.helper->expect) {

ret = call_expect(master_ct(ct), pskb,

hooknum, ct, info);

} else {

#ifdef CONFIG_IP_NF_NAT_LOCAL

/* LOCAL_IN hook doesn‘t have a chain!  */

if (hooknum == NF_IP_LOCAL_IN)

ret = alloc_null_binding(ct, info,

hooknum);

else

#endif

/* 既没有被nat修改过,也不是由expect创建,这是一个初始的数据包,开始在nat表中查找规则 */

ret = ip_nat_rule_find(pskb, hooknum, in, out, ct, info);

}

if (ret != NF_ACCEPT) {

WRITE_UNLOCK(&ip_nat_lock);

return ret;

}

} else

/* 如果该连接的nat部分已经被初始化了,打印调试信息 */

DEBUGP("Already setup manip %s for ct %p\n",

maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST",

ct);

WRITE_UNLOCK(&ip_nat_lock);

break;

default:

/* ESTABLISHED */

IP_NF_ASSERT(ctinfo == IP_CT_ESTABLISHED

|| ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY));

info = &ct->nat.info;

}

IP_NF_ASSERT(info);

/* 前面已经修改了连接跟踪表,这里正式修改了数据包里的地址 */

return do_bindings(ct, ctinfo, info, hooknum, pskb);

}

2.2           ip_nat_rule_find函数  ip_nat_rule.c

int ip_nat_rule_find(struct sk_buff **pskb,

unsigned int hooknum,

const struct net_device *in,

const struct net_device *out,

struct ip_conntrack *ct,

struct ip_nat_info *info)

{

int ret;

/* 调用ipt_do_tables函数,第五个参数是&nat_table  */

ret = ipt_do_table(pskb, hooknum, in, out, &nat_table, NULL);

if (ret == NF_ACCEPT) {

if (!(info->initialized & (1 << HOOK2MANIP(hooknum))))

/* NUL mapping */

ret = alloc_null_binding(ct, info, hooknum);

}

return ret;

}

nat表和filter表一样,都是通过调用ipt_do_table函数来工作的

ipt_do_table查找表中的所有entry,如果match全都匹配,则调用target函数

此时的target函数就是在nat初始化时注册的ipt_snat_target和ipt_dnat_target

2.3           ipt_s(d)nat_target函数  ip_nat_rule.c

static unsigned int ipt_snat_target(struct sk_buff **pskb,

const struct net_device *in,

const struct net_device *out,

unsigned int hooknum,

const void *targinfo,

void *userinfo)

{

struct ip_conntrack *ct;

enum ip_conntrack_info ctinfo;

IP_NF_ASSERT(hooknum == NF_IP_POST_ROUTING);

/* 取得数据包的连接状态 */

ct = ip_conntrack_get(*pskb, &ctinfo);

/* Connection must be valid and new. */

IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));

IP_NF_ASSERT(out);

return ip_nat_setup_info(ct, targinfo, hooknum);

}

ipt_dnat_target和ipt_snat_target差不多,都是调用ip_nat_setup_info完成地址转换,这里的targinfo参数来自ipt_entry_target结构的unsigned char data[0]参数,一个长度为0的数组,指向target的末尾

static unsigned int ipt_dnat_target(struct sk_buff **pskb,

const struct net_device *in,

const struct net_device *out,

unsigned int hooknum,

const void *targinfo,

void *userinfo)

{

struct ip_conntrack *ct;

enum ip_conntrack_info ctinfo;

#ifdef CONFIG_IP_NF_NAT_LOCAL

IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING

|| hooknum == NF_IP_LOCAL_OUT);

#else

IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING);

#endif

ct = ip_conntrack_get(*pskb, &ctinfo);

/* Connection must be valid and new. */

IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));

return ip_nat_setup_info(ct, targinfo, hooknum);

}

2.4           ip_nat_setup_info()函数  ip_nat_rule.c

unsigned int

ip_nat_setup_info(struct ip_conntrack *conntrack,   /* 数据包的连接状态 */

const struct ip_nat_multi_range *mr,       /* 转换后的地址池 */

unsigned int hooknum)                    /* hook点 */

{

struct ip_conntrack_tuple new_tuple, inv_tuple, reply;

struct ip_conntrack_tuple orig_tp;

struct ip_nat_info *info = &conntrack->nat.info;

int in_hashes = info->initialized;

MUST_BE_WRITE_LOCKED(&ip_nat_lock);

IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING

|| hooknum == NF_IP_POST_ROUTING

|| hooknum == NF_IP_LOCAL_IN

|| hooknum == NF_IP_LOCAL_OUT);

IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS);

IP_NF_ASSERT(!(info->initialized & (1 << HOOK2MANIP(hooknum))));

/* 对当前状态的应答方向的tuple调用invert_tuplepr取反,得到一个orig_tp,如果之前没有进行过地址或端口转换,通常这里得到的orig_tp就等于初始方向的tuple */

invert_tuplepr(&orig_tp, conntrack->tuplehash[IP_CT_DIR_REPLY].tuple);

do {

/* 进行地址转换,new_tuple为转换后的地址的tuple */

if (!get_unique_tuple(&new_tuple,&orig_tp,mr,conntrack,hooknum))

{

DEBUGP("ip_nat_setup_info: Can‘t get unique for %p.\n",

conntrack);

return NF_DROP;

}

/* 对new_tuple取反,得到经过转换后的应答方向的tuple  */

invert_tuplepr(&reply, &new_tuple);

/* 修改conntrack中的应答方向的reply tuple,在这之前还要检查如果该reply tuple已经在hash表里存在了,即被其它连接占用(存在初始方向tuple不同,应答方向tuple相同的连接),则还要回头继续修改 */

} while (!ip_conntrack_alter_reply(conntrack, &reply));

/* 对orig_tp取反,实际上又得到了原conntrack的reply_tuple…… */

invert_tuplepr(&inv_tuple, &orig_tp);

/* 将所作转换的相关信息保存到连接状态conntrack里,这样该连接的后续数据包就可以直接利用这些信息进行地址转换,不用重新查找nat表了 */

/* 如果是源地址改变(SNAT) */

if (!ip_ct_tuple_src_equal(&new_tuple, &orig_tp)) {

/* In this direction, a source manip. */

info->manips[info->num_manips++] =

((struct ip_nat_info_manip)

{ IP_CT_DIR_ORIGINAL, hooknum,

IP_NAT_MANIP_SRC, new_tuple.src });

IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS);

/* 在相对的hook点上必然有对应的目的地址改变(DNAT) */

info->manips[info->num_manips++] =

((struct ip_nat_info_manip)

/* opposite_hook即是求当前hook点的对应hook点 */

{ IP_CT_DIR_REPLY, opposite_hook[hooknum],

IP_NAT_MANIP_DST, orig_tp.src });

IP_NF_ASSERT(info->num_manips <= IP_NAT_MAX_MANIPS);

}

/* 如果是目的地址改变(DNAT) */

if (!ip_ct_tuple_dst_equal(&new_tuple, &orig_tp)) {

/* In this direction, a destination manip */

info->manips[info->num_manips++] =

((struct ip_nat_info_manip)

{ IP_CT_DIR_ORIGINAL, hooknum,

IP_NAT_MANIP_DST, reply.src });

IP_NF_ASSERT(info->num_manips < IP_NAT_MAX_MANIPS);

/* In the reverse direction, a source manip. */

info->manips[info->num_manips++] =

((struct ip_nat_info_manip)

{ IP_CT_DIR_REPLY, opposite_hook[hooknum],

IP_NAT_MANIP_SRC, inv_tuple.src });

IP_NF_ASSERT(info->num_manips <= IP_NAT_MAX_MANIPS);

}

/* 如果这个连接不是某个连接的预期的连接(子连接),则在全局链表helpers查找对应的ip_nat_helper结构 */

if (!conntrack->master)

info->helper = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, &reply);

/* 转换完了,标记一下 */

info->initialized |= (1 << HOOK2MANIP(hooknum));

/* 将所做的地址转换的数据结构加入到全局hash表bysource和byipsproto中,如果该地址转换是某地址转换基础上的再次转换,则用replace_in_hashes替换,反之则用place_in_hashes */

if (in_hashes) {

IP_NF_ASSERT(info->bysource.conntrack);

replace_in_hashes(conntrack, info);

} else {

place_in_hashes(conntrack, info);

}

return NF_ACCEPT;

}

2.5           get_unique_tuple ()函数  ip_nat_core.c

get_unique_tuple,获得一个唯一的tuple,就是说除了要做地址/段口的转换,还要保证转换得到的tuple是唯一的。

很复杂的一个函数。。。

第三个参数是用来替换的地址或端口的范围

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)

{

struct ip_nat_protocol *proto

= find_nat_proto(orig_tuple->dst.protonum);

struct ip_nat_range *rptr;

unsigned int i;

int ret;

struct ip_nat_multi_range *mr = (void *)mrr;

/* 下面这一段比较晕,和p2p,udp打洞等技术有关。 */

if (hooknum == NF_IP_POST_ROUTING) {

/* ip_conntrack_manip结构包含一个ip地址和一个协议端口 */

struct ip_conntrack_manip *manip;

/* find_appropriate_src函数先调用hash_by_src函数计算orig_tuple的hash值,然后去bysource表里查找,如果能找到源地址和端口都匹配的连接,并且如果该连接的地址/端口本身就满足目标地址/端口范围的话,就直接返回查到的这个连接的源ip */

manip = find_appropriate_src(orig_tuple, mr);

if (manip) {

/* Apply same source manipulation. */

*tuple = ((struct ip_conntrack_tuple)

{ *manip, orig_tuple->dst });

DEBUGP("get_unique_tuple: Found current src map\n");

/* 还要保证连接跟踪表里没有这个连接 */

if (!ip_nat_used_tuple(tuple, conntrack))

return 1;

}

}

/* orig_tuple是转换之前的,tuple是转换之后的 */

*tuple = *orig_tuple;

/* 循环 ,尝试mr参数所指定的地址/端口范围,直到能满足其tuple是唯一的 */

while ((rptr = find_best_ips_proto_fast(tuple, mr, conntrack, hooknum))

!= NULL) {

DEBUGP("Found best for "); DUMP_TUPLE(tuple);

/*  IP_NAT_MANIP_SRC, 进行SNAT

IP_NAT_MANIP_DST 进行DNAT

IP_NAT_RANGE_MAP_IPS 在range里指定了IP地址

IP_NAT_RANGE_PROTO_SPECIFIED 在range里指定了port

如果没有指定协议端口范围,或者满足了所指定的范围 */

if ((!(rptr->flags & IP_NAT_RANGE_PROTO_SPECIFIED)

|| proto->in_range(tuple, HOOK2MANIP(hooknum),

&rptr->min, &rptr->max))

&& !ip_nat_used_tuple(tuple, conntrack)) {

ret = 1;

goto clear_fulls;

} else {

if (proto->unique_tuple(tuple, rptr,

HOOK2MANIP(hooknum),

conntrack)) {

/* Must be unique. */

IP_NF_ASSERT(!ip_nat_used_tuple(tuple,

conntrack));

ret = 1;

goto clear_fulls;

} else if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {

/* Try implicit source NAT; protocol

may be able to play with ports to

make it unique. */

struct ip_nat_range r

= { IP_NAT_RANGE_MAP_IPS,

tuple->src.ip, tuple->src.ip,

{ 0 }, { 0 } };

DEBUGP("Trying implicit mapping\n");

if (proto->unique_tuple(tuple, &r,

IP_NAT_MANIP_SRC,

conntrack)) {

/* Must be unique. */

IP_NF_ASSERT(!ip_nat_used_tuple

(tuple, conntrack));

ret = 1;

goto clear_fulls;

}

}

DEBUGP("Protocol can‘t get unique tuple %u.\n",

hooknum);

}

/* Eliminate that from range, and try again. */

rptr->flags |= IP_NAT_RANGE_FULL;

*tuple = *orig_tuple;

}

ret = 0;

clear_fulls:

/* Clear full flags. */

IP_NF_ASSERT(mr->rangesize >= 1);

for (i = 0; i < mr->rangesize; i++)

mr->range[i].flags &= ~IP_NAT_RANGE_FULL;

return ret;

}

时间: 2024-11-05 20:47:21

Network Address Translation(转载)的相关文章

NAT (Network Address Translation)

NAT Introduction NAT: Provides the translation of private address to public address. ? NAT has many uses, but its primary use is to conserve public IPv4 addresses. It does this by allowing networks to use private IPv4 addresses internally and providi

NAT(Network Address Translation)网络地址转换

为了缓解ipv4将要枯竭的这种现状 通过使用动态nat可以将私有地址转换为公有地址的方式访问英特网从而节省ipv4 动态nat配置思路 1.配置内网和外网设备的IP地址    pc1ip 192.168.10.1     网关192.168.10.254 pc2  192.168.10.2       网关192.168.10.254 2.配置网关设备上的默认路由 <Huawei>system-view       ----- 进入系统视图 [Huawei]interface GigabitE

NAT(地址转换技术): Network Address Translation Protocol解析

版权声明:本文为@小小呆原创文章,出处! https://blog.csdn.net/gui951753/article/details/79593307 目录 NAT产生背景ip地址基础知识NAT技术的工作原理和特点静态NAT动态NATNAT重载(经常应用到实际中)NAT技术的优缺点优点缺点NAT穿越技术应用层网关(ALG)ALG的实际应用NAT技术的未来参考文献 NAT产生背景 今天,无数快乐的互联网用户在尽情享受Internet带来的乐趣.他们浏览新闻,搜索资料,下载软件,广交新朋,分享信

NTA(Network Address Translation)

本人没有系统的学过计算机网络,但是对计算机网络又特别好奇,可是每每看到那些讲解网络的大部头,就累觉不爱了. 但是,经过零零总总的学习,对网络多多少少还是有点了解的. 应用层     传输层 数据包 gateway 网络层 IP分组 router 数据链路层 帧 bridge 物理层 比特流 repeater 虽然不喜欢看那么多文字描述,但是总是试图把从我这台电脑上到你那台电脑上的流程想清楚. 比如,我在qq上打“I love you”,怎么就在你就知道我爱上你了呢. 首先,这个肯定是经过Sock

iptables 网址转译 (Network Address Translation,NAT)

当封包流经NAT電腦時,其位址/通訊端口會被修改,以達到改变包目的地(或旅程),或是让目的地误以为包是源自NAT电脑的效果.換言之,对封包执行NAT的电脑,可以成为新包的来源或目的地,或是成为真正来源与目的地之间的中继站. ***************************** 警告: NAT需要连线追踪的能力,而连线追踪又需要电脑能看到所有包才有效,所以,如果你的防火墙是有多台电脑构行,请小心避免破坏连线追踪. ***************************** 利用NAT的位址/

Internet protocol security (ipsec) packet processing for multiple clients sharing a single network address

Embodiments of the present invention address deficiencies of the art in respect to secure communications for multiple hosts in an address translation environment and provide a method, system and computer program product for IPsec SA management for mu

[转]Peer-to-Peer Communication Across Network Address Translators

Peer-to-Peer Communication Across Network Address Translators Bryan Ford Massachusetts Institute of Technology baford (at) mit.edu Pyda Srisuresh Caymas Systems, Inc. srisuresh (at) yahoo.com Dan Kegel dank (at) kegel.com J'fais des trous, des petits

【网络补习】Port Address Translation (PAT)

来源:https://searchnetworking.techtarget.com/definition/Port-Address-Translation-PAT PAT is an extension to network address translation (NAT) that permits multiple devices on a local area network (LAN) to be mapped to a single public IP address. The go

PatentTips - DMA address translation between peer-to-peer IO devices

BACKGROUND As processing resources have increased, demands to run multiple software programs and operating systems on a single microprocessor have also increased. To meet these demands virtual environments have been developed to allocate a single com