Chinaunix首页 | 论坛 | 博客
  • 博客访问: 500493
  • 博文数量: 144
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 508
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-10 13:18
个人简介

Keep looking Donot settle

文章分类

全部博文(144)

文章存档

2019年(1)

2016年(31)

2015年(51)

2014年(61)

分类: 嵌入式

2014-12-22 09:55:18

七、网络地址转换模块(Network Address
Translation


1. 概述


   
网络地址转换的机制一般用于处理IP地址转换,在Netfilter中,可以支持多种NAT类型,而其实现的基础是连接跟踪。


       
NAT
可以分为SNATDNAT,即源NAT和目的NAT,在Netfilter中分别基于以下HOOK




  • NF_IP_PRE_ROUTING:可以在这里定义DNAT的规则,因为路由器进行路由时只检查数据报的目的IP地址,所以为了使数据报得以正确路由,我们必须在路由之前就进行DNAT



  • NF_IP_POST_ROUTING:可以在这里定义SNAT的规则,系统在决定了数据报的路由以后在执行该HOOK上的规则



  • NF_IP_LOCAL_OUT:定义对本地产生的数据报的DNAT规则



  • CONFIG_IP_NF_NAT_LOCAL定义后,NF_IP_LOCAL_IN上也可以定义DNAT规则。


   
同时,
MASQUERADE(伪装)是SNAT的一种特例,它与SNAT几乎一样,只有一点不同:如果连接断开,所有的连接跟踪信息将被丢弃,而去使用重新连接以后的IP地址进行IP伪装;而REDIRECT(重定向)是DNAT的一种特例,这时候就相当于将符合条件的数据报的目的IP地址改为数据报进入系统时的网络接口的IP地址。


2. 基于连接跟踪的相关数据结构


    NAT是基于连接跟踪实现的,NAT中所有的连接都由连接跟踪模块来管理,NAT模块的主要任务是维护nat表和进行实际的地址转换。这样,我们来回头重新审视一下连接跟踪模块中由条件编译决定的部分。


   
首先,是连接的描述
ip_conntrack,在连接跟踪模块部分中提到,这个结构的最后有“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


} nat;


#endif


这是一个叫做nat的子结构,其中有3个成员:



  •  


    • 一个ip_nat_info结构,这个结构下面会具体分析



    • 一个ip_conntrack_nat_help结构,是一个空结构,为扩展功能而设



    • 一个为伪装功能而设的index,从源码中对这个变量的使用看来,是对应所伪装网络接口的ID,也就是net_device中的ifindex成员


 


   
好,下面我们来看一下这个
ip_nat_info结构,这个结构存储了连接中的地址绑定信息,其定义位于include/linux/netfilter_ipv4/ip_nat.hLine98


struct ip_nat_info


{


int initialized;


unsigned int num_manips;


struct ip_nat_info_manip
manips[IP_NAT_MAX_MANIPS];


const struct ip_nat_mapping_type
*mtype;


struct ip_nat_hash bysource,
byipsproto;


struct ip_nat_helper *helper;


struct ip_nat_seq
seq[IP_CT_DIR_MAX];


};




  • `int initialized;`:这是一个位图,表明源地址以及目的地址的地址绑定是否已被初始化



  • `unsigned int num_manips;`:这个成员指定了存放在下面的manip数组中的可执行操作的编号,在不同的HOOK以及不同的方向上,可执行操作是分别进行计数的。



  • `struct ip_nat_info_manip
    manips[IP_NAT_MAX_MANIPS];`
    ip_nat_info_manip结构定义于include/linux/netfilter_ipv4/ip_nat.hLine66,一个ip_nat_info_manip结构对应着一个可执行操作或地址绑定,其成员包括方向(ORIGINAL以及REPLY)、HOOK号、操作类型(由一个枚举类型ip_nat_manip_type定义,有IP_NAT_MANIP_SRCIP_NAT_MANIP_DST两种)以及一个ip_conntrack_manip结构



  • `const struct ip_nat_mapping_type
    *mtype;`
    ip_nat_mapping_type这个结构在整个内核源码中都没有定义,根据注释来看应该也是一个预留的扩展,一般就是NULL



  • `struct ip_nat_hash bysource,
    byipsproto;`
    ip_nat_hash结构定义于include/linux/netfilter_ipv4/ip_nat.hLine89,实际上就是一个带表头的ip_conntrack结构,跟连接跟踪中hash表的实现类似。其中,




    • bysource表是用来管理现有连接的



    • byipsproto表则管理已经完成的转换映射,以保证同一个IP不会同时有两个映射,避免地址转换冲突



  • `struct ip_nat_helper *helper;`:扩展用



  • `struct ip_nat_seq
    seq[IP_CT_DIR_MAX];`
    :这是为每一个方向(其实就两个方向)记录一个序列号。ip_nat_seq结构定义于include/linux/netfilter_ipv4/ip_nat.hLine33,这个结构用得并不多,应该是用于TCP连接的计数和对涉及TCP的修改的定位


 


3. nat表的实现


       
nat
表的初始化和实现与filter极为相似。


   
在初始化上,其初始化位于
net/ipv4/netfilter/ip_nat_rule.cLine104,初始化所用模板则位于net/ipv4/netfilter/ip_nat_rule.cLine50


   
在实现上,其实现函数就是
NAT模块的初始化函数init_or_cleanup()(位于net/ipv4/netfilter/ip_nat_standalone.cLine278)。其工作主要是依次调用ip_nat_rule_init()ip_nat_init()以及nf_register_hook()




  1. 首先ip_nat_rule_init()(位于net/ipv4/netfilter/ip_nat_rule.cLine278)调用ipt_register_table()来初始化并注册nat表,然后利用ipt_register_target()来初始化并注册SNATDNAT,在这个注册过程中,关键的函数是ip_nat_setup_info(位于net/ipv4/netfilter/ip_nat_core.cLine511),其工作是:



  •  


    • 首先调用invert_tupler()net/ipv4/netfilter/ip_conntrack_core.cLine879),将记录反转



    • 然后调用get_unique_tuple()net/ipv4/netfilter/ip_nat_core.cLine393,在指定的地址范围(ip_nat_multi_range结构)中查找空闲的地址),如果没有空闲地址可用则会返回NF_DROP



    • 判断源和目的是否改变,如果改变,则更新ip_nat_info




  1. 然后ip_nat_init()(位于net/ipv4/netfilter/ip_nat_core.cLine953)会给nat所用的两个hash表(bysourcebyipsproto)分配空间并初始化各种协议



  2. 最后会通过nf_register_hook()注册相应HOOK的函数ip_nat_fn()ip_nat_local_fn()ip_nat_out(),并增加连接跟踪的计数器。


   
在具体的
HOOK函数实现上,后两者其实都是基于ip_nat_fn()的,而这其中最重要的处理函数,也就是实际的网络地址转换函数是do_bindings(),下面将对其进行分析:


       
do_bindings()
位于net/ipv4/netfilter/ip_nat_core.cLine747,其主要工作是将ip_nat_info中的地址绑定应用于数据报:




  1. 它首先在ip_nat_info->manip数组中查找能够匹配的绑定



  2. 然后调用manip_pkt()函数(位于net/ipv4/netfilter/ip_nat_core.cLine701)进行相应的地址转换:



  •  


    • manip_pkt()这个函数会根据不同的方向进行转换,并且对校验和进行处理



    • 同时,它是递归调用自己以处理不同协议的情况




  1. 最后调用helper模块进行执行(当然,如果有的话),特别是避免在同一个数据报上执行多次同一个helper模块


 


       
nat
表与filter表还有一个较大的不同:在一个连接中,只有第一个数据报才会经过nat表,而其转换的结果会作用于此连接中的其它所有数据报。


4. 协议的扩展


   
要想扩展
NAT的协议,那么必须写入两个模块,一个用于连接跟踪,一个用于NAT实现。


   
与连接跟踪类似,
nat中协议也由一个列表protos存放,位于include/linux/netfilter_ipv4/ip_nat_core.hLine17。协议的注册和注销分别是由函数ip_nat_protocol_register()ip_nat_protocol_unregister()实现的,位于net/ipv4/netfilter/ip_nat_standalone.cLine242


       
nat
的协议是由结构ip_nat_protocol描述的,其定义位于include/linux/netfilter_ipv4/ip_nat_protocol.hLine10


struct ip_nat_protocol


{


struct list_head list;


const char *name;


unsigned int protonum;


void (*manip_pkt)(struct iphdr *iph, size_t
len,


const struct ip_conntrack_manip
*manip,


enum ip_nat_manip_type
maniptype);


int (*in_range)(const struct ip_conntrack_tuple
*tuple,


enum ip_nat_manip_type
maniptype,


const union ip_conntrack_manip_proto
*min,


const union ip_conntrack_manip_proto
*max);


int (*unique_tuple)(struct ip_conntrack_tuple
*tuple,


const struct ip_nat_range
*range,


enum ip_nat_manip_type
maniptype,


const struct ip_conntrack
*conntrack);


unsigned int (*print)(char
*buffer,


const struct ip_conntrack_tuple
*match,


const struct ip_conntrack_tuple
*mask);


unsigned int (*print_range)(char
*buffer,


const struct ip_nat_range
*range);


};


   
其中重要成员:




  • `void (*manip_pkt)(……)`:其指向的函数会根据ip_nat_info->manip参数进行数据报的转换,即do_bindings()中调用的manip_pkt()函数



  • `int (*in_range)(……)`:其指向的函数检查多元组的协议部分值是否在指定的区间之内



  • `int (*unique_tuple)(……)`:其指向函数的作用是根据manip的类型修改多元组中的协议部分,以获得一个唯一的地址,多元组的协议部分被初始化为ORIGINAL方向


八、数据报修改模块──mangle


1. 概述


   
    mangle
这个词的原意是撕裂、破坏,这里所谓“packet mangling”是指对packet的一些传输特性进行修改。mangle表被用来真正地对数据报进行修改,它可以在所有的5个HOOK上进行操作。


   
从源码看来,
mangle表中所允许修改的传输特性目前有:




  • TOS(服务类型):修改IP数据报头的TOS字段值



  • TTL(生存时间):修改IP数据报头的TTL字段值



  • MARK:修改skbnfmark域设置的nfmark字段值。




    • nfmark是数据报的元数据之一,是一个用户定义的数据报的标记,可以是unsigned long范围内的任何值。该标记值用于基于策略的路由,通知ipqmpd(运行在用户空间的队列分拣器守护进程)将该数据报排队给哪个进程等信息。



  • TCP MSS(最大数据段长度):修改TCP数据报头的MSS字段值


2. mangle表的实现


   
遍历整个源码,没有发现
mangle表的独有数据结构。


       
mangle
表的初始化和实现与filter极为相似。在初始化上,其初始化位于net/ipv4/netfilter/iptable_mangle.cLine117,初始化所用模板则位于net/ipv4/netfilter/iptable_mangle.cLine43


    在实现上,其实现函数就是mangle模块的初始化函数init()(位于net/ipv4/netfilter/iptable_mangle.cLine183)。其工作就是依次注册packet_mangler表以及5HOOK。其中NF_IP_LOCAL_OUT的处理函数为ipt_local_hook(),其余均为ipt_route_hook(),分别位于net/ipv4/netfilter/iptable_mangle.cLine132 & 122,二者的关键实现都是通过调用ipt_do_table()实现的。


3. 数据报的修改


   
对数据报不同位的修改都是通过单独的模块实现的,也就是说由
ipt_tos.oipt_ttl.oipt_mark.oipt_tcpmss.o实现上面所说的四种传输特性的修改。


    以TOS为例,其涉及的文件为net/ipv4/netfilter/ipt_tos.c以及include/linux/netfilter_ipv4/ipt_tos.h


    其专有数据结构为ipt_tos_info,定义于ipt_tos.h中:


struct ipt_tos_info {


u_int8_t tos;


u_int8_t invert;


};


    其模块的添加/卸载函数很简单,其实就是添加/删除TOSMATCHtos_match(定义并初始化于ipt_tos.cLine37):


static struct ipt_match
tos_match


= { { NULL, NULL }, "tos", &match,
&checkentry, NULL, THIS_MODULE };


   
而在tos_match的处理函数match()中,已经完成了对相应位的赋值,这似乎是违反match仅仅匹配而不修改的一个特例。


九、其它高级功能模块


       
Netfilter
中还有一些其它的高级功能模块,基本是为了用户操作方便的,没有对它们进行分析,如:




  • REJECT,丢弃包并通知包的发送者,同时返回给发送者一个可配置的ICMP错误信息,由ipt_REJECT.o完成



  • MIRROR,互换源和目的地址以后并重新发送,由ipt_MIRROR.o完成



  • LOG,将匹配的数据报传递给系统的syslog()进行记录,由ipt_LOG.o完成



  • ULOGUserspace logging,将数据报排队转发到用户空间中,将匹配的数据适用用户空间的log进程进行记录,由ip_ULOG.o完成。这是Netfilter的一个关键技术,可以使用户进程可以进行复杂的数据报操作,从而减轻内核空间中的复杂度



  • Queuing,这是上面ULOG技术的基础,由ip_queue.o完成,提供可靠的异步包处理以及性能两号的libipq库来进行用户空间数据报操作的开发。



  • 等等……

from : http://blog.sina.com.cn/s/blog_a31ff26901013n0l.html

阅读(4001) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~